From fbe30a6da13729c16770608bb5a7f11d79b22fb2 Mon Sep 17 00:00:00 2001 From: fredrik Date: Wed, 22 Nov 2006 13:03:26 +0000 Subject: [PATCH] Add python module (note: must be compiled seperately) git-svn-id: svn+ssh://svn.dolda2000.com/srv/svn/repos/src/doldaconnect@746 959494ce-11ee-0310-bf91-de5d638817bd --- lib/python/dolcon/__init__.py | 41 +++++ lib/python/dolmod.c | 409 ++++++++++++++++++++++++++++++++++++++++++ lib/python/setup.py | 13 ++ 3 files changed, 463 insertions(+) create mode 100644 lib/python/dolcon/__init__.py create mode 100644 lib/python/dolmod.c create mode 100755 lib/python/setup.py diff --git a/lib/python/dolcon/__init__.py b/lib/python/dolcon/__init__.py new file mode 100644 index 0000000..e7adb29 --- /dev/null +++ b/lib/python/dolcon/__init__.py @@ -0,0 +1,41 @@ +from dolmod import * + +def login(useauthless = True, **kw): + result = [None] + def mycb(*v): + result[0] = v + loginasync(mycb, useauthless, **kw) + while result[0] is None: + select() + return result[0] + +def mustconnect(host, port = -1): + connect(host, port) + while True: + resp = getresp() + if resp is not None and resp.getcmd() == u".connect": + break + select() + if resp.getcode() != 200: + raise RuntimeError, resp.intresp()[0][0] + +def cnl(host, port = -1, useauthless = True, **kw): + mustconnect(host, port) + err, reason = login(useauthless, **kw) + if err != "success": + raise RuntimeError, (err, reason) + +def ecmd(*args): + tag = qcmd(*args) + while True: + resp = getresp(tag) + if resp is not None: + break; + select() + return resp + +def ecmda(code, *args): + resp = ecmd(*args) + if resp.getcode() != code: + raise ValueError, resp.getcode() + return resp diff --git a/lib/python/dolmod.c b/lib/python/dolmod.c new file mode 100644 index 0000000..edae7ae --- /dev/null +++ b/lib/python/dolmod.c @@ -0,0 +1,409 @@ +/* + * Dolda Connect - Modular multiuser Direct Connect-style client + * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include +#include +#include + +struct respobj { + PyObject_HEAD + struct dc_response *resp; +}; + +static int fd = -1; + +static void resptype_del(struct respobj *self) +{ + dc_freeresp(self->resp); + self->ob_type->tp_free((PyObject *)self); +} + +static PyObject *resp_getcode(struct respobj *self) +{ + return(Py_BuildValue("i", self->resp->code)); +} + +static PyObject *resp_gettag(struct respobj *self) +{ + return(Py_BuildValue("i", self->resp->tag)); +} + +static PyObject *resp_getcmd(struct respobj *self) +{ + return(PyUnicode_FromWideChar(self->resp->cmdname, wcslen(self->resp->cmdname))); +} + +static PyObject *resp_extract(struct respobj *self) +{ + int i, o; + PyObject *ret, *sl; + wchar_t *c; + + ret = PyList_New(self->resp->numlines); + for(i = 0; i < self->resp->numlines; i++) { + sl = PyList_New(self->resp->rlines[i].argc); + for(o = 0; o < self->resp->rlines[i].argc; o++) { + c = self->resp->rlines[i].argv[o]; + PyList_SetItem(sl, o, PyUnicode_FromWideChar(c, wcslen(c))); + } + PyList_SetItem(ret, i, sl); + } + return(ret); +} + +static PyObject *resp_intresp(struct respobj *self) +{ + int i; + PyObject *ret, *sl; + struct dc_intresp *ires; + + ret = PyList_New(0); + self->resp->curline = 0; + while((ires = dc_interpret(self->resp)) != NULL) { + sl = PyList_New(ires->argc); + for(i = 0; i < ires->argc; i++) { + switch(ires->argv[i].type) { + case 1: + PyList_SetItem(sl, i, PyUnicode_FromWideChar(ires->argv[i].val.str, wcslen(ires->argv[i].val.str))); + break; + case 2: + PyList_SetItem(sl, i, PyInt_FromLong(ires->argv[i].val.num)); + break; + case 3: + PyList_SetItem(sl, i, PyFloat_FromDouble(ires->argv[i].val.flnum)); + break; + } + } + dc_freeires(ires); + PyList_Append(ret, sl); + } + return(ret); +} + +static PyMethodDef respmethods[] = { + {"getcode", (PyCFunction)resp_getcode, METH_NOARGS, + "Get the numerical code from the response"}, + {"gettag", (PyCFunction)resp_gettag, METH_NOARGS, + "Get the tag from the response"}, + {"getcmd", (PyCFunction)resp_getcmd, METH_NOARGS, + "Get the command name from the response"}, + {"extract", (PyCFunction)resp_extract, METH_NOARGS, + "Extract the lines and columns from the response"}, + {"intresp", (PyCFunction)resp_intresp, METH_NOARGS, + "Interpret all the lines from the response with native datatypes"}, + {NULL, NULL, 0, NULL} +}; + +static PyTypeObject resptype = { + PyObject_HEAD_INIT(NULL) + .tp_name = "dolcon.Response", + .tp_basicsize = sizeof(struct respobj), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "Dolda Connect response objects", + .tp_dealloc = (destructor)resptype_del, + .tp_methods = respmethods, +}; + +static struct respobj *makeresp(struct dc_response *resp) +{ + struct respobj *new; + + new = (struct respobj *)resptype.tp_alloc(&resptype, 0); + new->resp = resp; + return(new); +} + +static PyObject *mod_connect(PyObject *self, PyObject *args) +{ + char *host; + int port; + + port = -1; + if(!PyArg_ParseTuple(args, "s|i", &host, &port)) + return(NULL); + if(fd >= 0) + dc_disconnect(); + if((fd = dc_connect(host, port)) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return(NULL); + } + return(Py_BuildValue("i", fd)); +} + +static PyObject *mod_disconnect(PyObject *self, PyObject *args) +{ + if(fd != -1) { + dc_disconnect(); + fd = -1; + } + Py_RETURN_NONE; +} + +static PyObject *mod_connected(PyObject *self, PyObject *args) +{ + if(fd == -1) + Py_RETURN_FALSE; + else + Py_RETURN_TRUE; +} + +static PyObject *mod_select(PyObject *self, PyObject *args) +{ + struct pollfd pfd; + int timeout, ret; + + timeout = -1; + if(!PyArg_ParseTuple(args, "|i", &timeout)) + return(NULL); + if(fd < 0) { + PyErr_SetString(PyExc_RuntimeError, "Not connected"); + return(NULL); + } + pfd.fd = fd; + pfd.events = POLLIN; + if(dc_wantwrite()) + pfd.events |= POLLOUT; + if((ret = poll(&pfd, 1, timeout)) < 0) { + if(errno == EINTR) + Py_RETURN_FALSE; + dc_disconnect(); + fd = -1; + PyErr_SetFromErrno(PyExc_OSError); + return(NULL); + } + if(((pfd.revents & POLLIN) && dc_handleread()) || ((pfd.revents & POLLOUT) && dc_handlewrite())) { + if(errno == 0) { + fd = -1; + Py_RETURN_FALSE; + } + PyErr_SetFromErrno(PyExc_OSError); + } + if(ret > 0) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + +static PyObject *mod_getresp(PyObject *self, PyObject *args) +{ + int tag; + struct dc_response *resp; + + tag = -1; + if(!PyArg_ParseTuple(args, "|i", &tag)) + return(NULL); + if(tag == -1) + resp = dc_getresp(); + else + resp = dc_gettaggedresp(tag); + if(resp == NULL) + Py_RETURN_NONE; + return((PyObject *)makeresp(resp)); +} + +static int qcmd_cb(struct dc_response *resp) +{ + PyObject *pycb, *args, *ret; + + pycb = resp->data; + args = Py_BuildValue("(N)", makeresp(resp)); + ret = PyObject_Call(pycb, args, NULL); + Py_DECREF(args); + Py_DECREF(ret); + Py_DECREF(pycb); + return(2); +} + +static PyObject *mod_qcmd(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int i, tag; + wchar_t **toks, *tok, *cmd; + size_t tokssize, toksdata, toksize; + PyObject *c, *n, *cb, *ret; + + toks = NULL; + tokssize = toksdata = 0; + cmd = NULL; + for(i = 0; i < PySequence_Size(args); i++) { + c = PySequence_GetItem(args, i); + if(!PyUnicode_Check(c)) { + n = PyUnicode_FromObject(c); + Py_DECREF(c); + c = n; + } + tok = smalloc((toksize = (PyUnicode_GetSize(c) + 1)) * sizeof(*tok)); + tok[PyUnicode_AsWideChar((PyUnicodeObject *)c, tok, toksize)] = L'\0'; + Py_DECREF(c); + if(cmd == NULL) + cmd = tok; + else + addtobuf(toks, tok); + } + if(cmd == NULL) { + PyErr_SetString(PyExc_TypeError, "qcmd needs at least 1 argument"); + return(NULL); + } + addtobuf(toks, NULL); + ret = NULL; + if(PyMapping_HasKeyString(kwargs, "cb")) { + cb = PyMapping_GetItemString(kwargs, "cb"); + if(PyCallable_Check(cb)) { + ret = PyInt_FromLong(dc_queuecmd(qcmd_cb, cb, cmd, L"%%a", toks, NULL)); + } else { + PyErr_SetString(PyExc_TypeError, "Callback must be callable"); + Py_DECREF(cb); + } + } else { + ret = PyInt_FromLong(dc_queuecmd(NULL, NULL, cmd, L"%%a", toks, NULL)); + } + dc_freewcsarr(toks); + free(cmd); + return(ret); +} + +static void login_cb(int err, wchar_t *reason, PyObject *cb) +{ + char *errstr; + PyObject *args, *pyerr, *pyreason, *ret; + + switch(err) { + case DC_LOGIN_ERR_SUCCESS: + errstr = "success"; + break; + case DC_LOGIN_ERR_NOLOGIN: + errstr = "nologin"; + break; + case DC_LOGIN_ERR_SERVER: + errstr = "server"; + break; + case DC_LOGIN_ERR_USER: + errstr = "user"; + break; + case DC_LOGIN_ERR_CONV: + errstr = "conv"; + break; + case DC_LOGIN_ERR_AUTHFAIL: + errstr = "authfail"; + break; + } + pyerr = PyString_FromString(errstr); + if(reason == NULL) + Py_INCREF(pyreason = Py_None); + else + pyreason = PyUnicode_FromWideChar(reason, wcslen(reason)); + args = PyTuple_Pack(2, pyerr, pyreason); + Py_DECREF(pyerr); Py_DECREF(pyreason); + ret = PyObject_Call(cb, args, NULL); + Py_DECREF(cb); + Py_DECREF(args); + Py_DECREF(ret); +} + +static PyObject *mod_loginasync(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int useauthless; + char *username; + PyObject *o, *cb, *conv; + + username = NULL; + conv = NULL; + useauthless = 1; + if(!PyArg_ParseTuple(args, "O|i", &cb, &useauthless)) + return(NULL); + if(!PyCallable_Check(cb)) { + PyErr_SetString(PyExc_TypeError, "Callback must be callable"); + return(NULL); + } + if(PyMapping_HasKeyString(kwargs, "username")) { + o = PyMapping_GetItemString(kwargs, "username"); + username = PyString_AsString(o); + Py_DECREF(o); + if(username == NULL) + return(NULL); + } + if(PyMapping_HasKeyString(kwargs, "conv")) { + conv = PyMapping_GetItemString(kwargs, "conv"); + if(!PyCallable_Check(conv)) { + PyErr_SetString(PyExc_TypeError, "Conv callback must be callable"); + return(NULL); + } + PyErr_SetString(PyExc_NotImplementedError, "Custom conv functions are not yet supported by the Python interface"); + return(NULL); + } + Py_INCREF(cb); + dc_loginasync(username, useauthless, NULL, (void (*)(int, wchar_t *, void *))login_cb, cb); + Py_RETURN_NONE; +} + +static PyObject *mod_lexsexpr(PyObject *self, PyObject *args) +{ + PyObject *arg, *se, *ret; + wchar_t **arr, **ap, *buf; + size_t bufsize; + + if(!PyArg_ParseTuple(args, "O", &arg)) + return(NULL); + if((se = PyUnicode_FromObject(arg)) == NULL) + return(NULL); + buf = smalloc((bufsize = (PyUnicode_GetSize(se) + 1)) * sizeof(*buf)); + buf[PyUnicode_AsWideChar((PyUnicodeObject *)se, buf, bufsize)] = L'\0'; + arr = dc_lexsexpr(buf); + free(buf); + Py_DECREF(se); + ret = PyList_New(0); + if(arr != NULL) { + for(ap = arr; *ap; ap++) + PyList_Append(ret, PyUnicode_FromWideChar(*ap, wcslen(*ap))); + dc_freewcsarr(arr); + } + return(ret); +} + +static PyMethodDef methods[] = { + {"connect", mod_connect, METH_VARARGS, + "Connect to a Dolda Connect server"}, + {"disconnect", mod_disconnect, METH_VARARGS, + "Disconnect from the server"}, + {"connected", mod_connected, METH_VARARGS, + "Return a boolean indicated whether there currently is a connection to a server"}, + {"select", mod_select, METH_VARARGS, + "Handle data from the server connection, optionally blocking until something happens"}, + {"getresp", mod_getresp, METH_VARARGS, + "Get a queued response object"}, + {"qcmd", (PyCFunction)mod_qcmd, METH_VARARGS | METH_KEYWORDS, + "Queue a command to be sent to the server"}, + {"loginasync", (PyCFunction)mod_loginasync, METH_VARARGS | METH_KEYWORDS, + "Perform an asynchronous login procedure"}, + {"lexsexpr", mod_lexsexpr, METH_VARARGS, + "Use a standard algorithm to lex a search expression"}, + {NULL, NULL, 0, NULL} +}; + +PyMODINIT_FUNC initdolmod(void) +{ + PyObject *m; + + if(PyType_Ready(&resptype) < 0) + return; + m = Py_InitModule("dolmod", methods); + Py_INCREF(&resptype); + PyModule_AddObject(m, "Response", (PyObject *)&resptype); + dc_init(); +} diff --git a/lib/python/setup.py b/lib/python/setup.py new file mode 100755 index 0000000..e2e20d0 --- /dev/null +++ b/lib/python/setup.py @@ -0,0 +1,13 @@ +#!/usr/bin/python + +from distutils.core import setup, Extension + +dolmod = Extension("dolmod", ["dolmod.c"], + libraries = ["dcui"]) + +setup(name = "dolcon", + version = "0.3", + description = "Python glue module for libdolcon", + ext_modules = [dolmod], + packages = ["dolcon"], + license = "GPL-2") -- 2.11.0