2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <doldaconnect/uilib.h>
22 #include <doldaconnect/uimisc.h>
23 #include <doldaconnect/utils.h>
28 struct dc_response *resp;
33 static void resptype_del(struct respobj *self)
35 dc_freeresp(self->resp);
36 self->ob_type->tp_free((PyObject *)self);
39 static PyObject *resp_getcode(struct respobj *self)
41 return(Py_BuildValue("i", self->resp->code));
44 static PyObject *resp_gettag(struct respobj *self)
46 return(Py_BuildValue("i", self->resp->tag));
49 static PyObject *resp_getcmd(struct respobj *self)
51 return(PyUnicode_FromWideChar(self->resp->cmdname, wcslen(self->resp->cmdname)));
54 static PyObject *resp_extract(struct respobj *self)
60 ret = PyList_New(self->resp->numlines);
61 for(i = 0; i < self->resp->numlines; i++) {
62 sl = PyList_New(self->resp->rlines[i].argc);
63 for(o = 0; o < self->resp->rlines[i].argc; o++) {
64 c = self->resp->rlines[i].argv[o];
65 PyList_SetItem(sl, o, PyUnicode_FromWideChar(c, wcslen(c)));
67 PyList_SetItem(ret, i, sl);
72 static PyObject *resp_intresp(struct respobj *self)
76 struct dc_intresp *ires;
79 self->resp->curline = 0;
80 while((ires = dc_interpret(self->resp)) != NULL) {
81 sl = PyList_New(ires->argc);
82 for(i = 0; i < ires->argc; i++) {
83 switch(ires->argv[i].type) {
85 PyList_SetItem(sl, i, PyUnicode_FromWideChar(ires->argv[i].val.str, wcslen(ires->argv[i].val.str)));
88 PyList_SetItem(sl, i, PyInt_FromLong(ires->argv[i].val.num));
91 PyList_SetItem(sl, i, PyFloat_FromDouble(ires->argv[i].val.flnum));
96 PyList_Append(ret, sl);
101 static PyMethodDef respmethods[] = {
102 {"getcode", (PyCFunction)resp_getcode, METH_NOARGS,
103 "Get the numerical code from the response"},
104 {"gettag", (PyCFunction)resp_gettag, METH_NOARGS,
105 "Get the tag from the response"},
106 {"getcmd", (PyCFunction)resp_getcmd, METH_NOARGS,
107 "Get the command name from the response"},
108 {"extract", (PyCFunction)resp_extract, METH_NOARGS,
109 "Extract the lines and columns from the response"},
110 {"intresp", (PyCFunction)resp_intresp, METH_NOARGS,
111 "Interpret all the lines from the response with native datatypes"},
112 {NULL, NULL, 0, NULL}
115 static PyTypeObject resptype = {
116 PyObject_HEAD_INIT(NULL)
117 .tp_name = "dolcon.Response",
118 .tp_basicsize = sizeof(struct respobj),
119 .tp_flags = Py_TPFLAGS_DEFAULT,
120 .tp_doc = "Dolda Connect response objects",
121 .tp_dealloc = (destructor)resptype_del,
122 .tp_methods = respmethods,
125 static struct respobj *makeresp(struct dc_response *resp)
129 new = (struct respobj *)resptype.tp_alloc(&resptype, 0);
134 static PyObject *mod_connect(PyObject *self, PyObject *args)
139 if(!PyArg_ParseTuple(args, "|s", &host))
143 if((fd = dc_connect(host)) < 0) {
144 PyErr_SetFromErrno(PyExc_OSError);
147 return(Py_BuildValue("i", fd));
150 static PyObject *mod_disconnect(PyObject *self, PyObject *args)
159 static PyObject *mod_connected(PyObject *self, PyObject *args)
167 static PyObject *mod_select(PyObject *self, PyObject *args)
173 if(!PyArg_ParseTuple(args, "|i", &timeout))
176 PyErr_SetString(PyExc_RuntimeError, "Not connected");
182 pfd.events |= POLLOUT;
183 if((ret = poll(&pfd, 1, timeout)) < 0) {
188 PyErr_SetFromErrno(PyExc_OSError);
191 if(((pfd.revents & POLLIN) && dc_handleread()) || ((pfd.revents & POLLOUT) && dc_handlewrite())) {
196 PyErr_SetFromErrno(PyExc_OSError);
203 static PyObject *mod_getresp(PyObject *self, PyObject *args)
206 struct dc_response *resp;
209 if(!PyArg_ParseTuple(args, "|i", &tag))
214 resp = dc_gettaggedresp(tag);
217 return((PyObject *)makeresp(resp));
220 static int qcmd_cb(struct dc_response *resp)
222 PyObject *pycb, *args, *ret;
225 args = Py_BuildValue("(N)", makeresp(resp));
226 ret = PyObject_Call(pycb, args, NULL);
233 static PyObject *mod_qcmd(PyObject *self, PyObject *args, PyObject *kwargs)
236 wchar_t **toks, *tok, *cmd;
237 size_t tokssize, toksdata, toksize;
238 PyObject *c, *n, *cb, *ret;
241 tokssize = toksdata = 0;
244 for(i = 0; i < PySequence_Size(args); i++) {
245 if((c = PySequence_GetItem(args, i)) == NULL)
247 if(!PyUnicode_Check(c)) {
248 n = PyUnicode_FromObject(c);
253 tok = smalloc((toksize = (PyUnicode_GetSize(c) + 1)) * sizeof(*tok));
254 tok[PyUnicode_AsWideChar((PyUnicodeObject *)c, tok, toksize)] = L'\0';
262 PyErr_SetString(PyExc_TypeError, "qcmd needs at least 1 argument");
265 addtobuf(toks, NULL);
267 if(PyMapping_HasKeyString(kwargs, "cb")) {
268 cb = PyMapping_GetItemString(kwargs, "cb");
269 if(PyCallable_Check(cb)) {
270 ret = PyInt_FromLong(dc_queuecmd(qcmd_cb, cb, cmd, L"%a", toks, NULL));
272 PyErr_SetString(PyExc_TypeError, "Callback must be callable");
276 ret = PyInt_FromLong(dc_queuecmd(NULL, NULL, cmd, L"%a", toks, NULL));
286 static void login_cb(int err, wchar_t *reason, PyObject *cb)
289 PyObject *args, *pyerr, *pyreason, *ret;
292 case DC_LOGIN_ERR_SUCCESS:
295 case DC_LOGIN_ERR_NOLOGIN:
298 case DC_LOGIN_ERR_SERVER:
301 case DC_LOGIN_ERR_USER:
304 case DC_LOGIN_ERR_CONV:
307 case DC_LOGIN_ERR_AUTHFAIL:
311 pyerr = PyString_FromString(errstr);
313 Py_INCREF(pyreason = Py_None);
315 pyreason = PyUnicode_FromWideChar(reason, wcslen(reason));
316 args = PyTuple_Pack(2, pyerr, pyreason);
317 Py_DECREF(pyerr); Py_DECREF(pyreason);
318 ret = PyObject_Call(cb, args, NULL);
324 static PyObject *mod_loginasync(PyObject *self, PyObject *args, PyObject *kwargs)
328 PyObject *o, *cb, *conv;
333 if(!PyArg_ParseTuple(args, "O|i", &cb, &useauthless))
335 if(!PyCallable_Check(cb)) {
336 PyErr_SetString(PyExc_TypeError, "Callback must be callable");
339 if(PyMapping_HasKeyString(kwargs, "username")) {
340 o = PyMapping_GetItemString(kwargs, "username");
341 username = PyString_AsString(o);
346 if(PyMapping_HasKeyString(kwargs, "conv")) {
347 conv = PyMapping_GetItemString(kwargs, "conv");
348 if(!PyCallable_Check(conv)) {
349 PyErr_SetString(PyExc_TypeError, "Conv callback must be callable");
352 PyErr_SetString(PyExc_NotImplementedError, "Custom conv functions are not yet supported by the Python interface");
356 dc_loginasync(username, useauthless, NULL, (void (*)(int, wchar_t *, void *))login_cb, cb);
360 static PyObject *mod_lexsexpr(PyObject *self, PyObject *args)
362 PyObject *arg, *se, *ret;
363 wchar_t **arr, **ap, *buf;
366 if(!PyArg_ParseTuple(args, "O", &arg))
368 if((se = PyUnicode_FromObject(arg)) == NULL)
370 buf = smalloc((bufsize = (PyUnicode_GetSize(se) + 1)) * sizeof(*buf));
371 buf[PyUnicode_AsWideChar((PyUnicodeObject *)se, buf, bufsize)] = L'\0';
372 arr = dc_lexsexpr(buf);
377 for(ap = arr; *ap; ap++)
378 PyList_Append(ret, PyUnicode_FromWideChar(*ap, wcslen(*ap)));
384 static PyObject *mod_wantwrite(PyObject *self)
392 static PyObject *mod_checkproto(PyObject *self, PyObject *args)
395 struct respobj *resp;
399 if(!PyArg_ParseTuple(args, "O|i", &tmp, &version))
401 if(!PyObject_TypeCheck(tmp, &resptype)) {
402 PyErr_SetString(PyExc_TypeError, "first argument must be a response object");
406 if(dc_checkprotocol(resp->resp, version))
412 static PyMethodDef methods[] = {
413 {"connect", mod_connect, METH_VARARGS,
414 "Connect to a Dolda Connect server"},
415 {"disconnect", mod_disconnect, METH_VARARGS,
416 "Disconnect from the server"},
417 {"connected", mod_connected, METH_VARARGS,
418 "Return a boolean indicated whether there currently is a connection to a server"},
419 {"select", mod_select, METH_VARARGS,
420 "Handle data from the server connection, optionally blocking until something happens"},
421 {"getresp", mod_getresp, METH_VARARGS,
422 "Get a queued response object"},
423 {"qcmd", (PyCFunction)mod_qcmd, METH_VARARGS | METH_KEYWORDS,
424 "Queue a command to be sent to the server"},
425 {"loginasync", (PyCFunction)mod_loginasync, METH_VARARGS | METH_KEYWORDS,
426 "Perform an asynchronous login procedure"},
427 {"lexsexpr", mod_lexsexpr, METH_VARARGS,
428 "Use a standard algorithm to lex a search expression"},
429 {"wantwrite", (PyCFunction)mod_wantwrite, METH_NOARGS,
430 "Return a boolean indicating whether there is output to be fed to the server"},
431 {"checkproto", (PyCFunction)mod_checkproto, METH_VARARGS,
432 "Check so that the connect stanza returned by the server indicates support for the correct revision of the protocol"},
433 {NULL, NULL, 0, NULL}
436 PyMODINIT_FUNC initdolmod(void)
440 if(PyType_Ready(&resptype) < 0)
442 m = Py_InitModule("dolmod", methods);
443 Py_INCREF(&resptype);
444 PyModule_AddObject(m, "Response", (PyObject *)&resptype);
445 PyModule_AddObject(m, "latest", Py_BuildValue("i", DC_LATEST));