X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/82089e3fd2b8c69de9286ff4d6cc9d845670ab7e..3f21826930f08150016306f0ef5b34a459405aa5:/src/mod-python.c diff --git a/src/mod-python.c b/src/mod-python.c index 17c2c59..5548cc6 100644 --- a/src/mod-python.c +++ b/src/mod-python.c @@ -36,6 +36,7 @@ #include "mail.h" #include "timeq.h" #include "compat.h" +#include "nickserv.h" /* TODO notes * @@ -83,24 +84,19 @@ struct _tuple_dict_extra { size_t* extra; }; -static int _dict_iter_get_users(char const* key, UNUSED_ARG(void* data), void* extra) { - PyObject* tmp; - struct _tuple_dict_extra* real_extra = (struct _tuple_dict_extra*)extra; - - if ((tmp = PyString_FromString(key)) == NULL) - return 1; +static void pyobj_release_tuple(PyObject* tuple, size_t n) { + size_t i; - if (PyTuple_SetItem(real_extra->data, *(int*)real_extra->extra, tmp)) { - Py_DECREF(tmp); - return 1; - } + if (tuple == NULL) + return; - *real_extra->extra = *real_extra->extra + 1; + for (i = 0; i < n; ++i) + Py_XDECREF(PyTuple_GET_ITEM(tuple, i)); - return 0; + Py_XDECREF(tuple); } -static int _dict_iter_get_channels(char const* key, UNUSED_ARG(void* data), void* extra) { +static int _dict_iter_fill_tuple(char const* key, UNUSED_ARG(void* data), void* extra) { PyObject* tmp; struct _tuple_dict_extra* real_extra = (struct _tuple_dict_extra*)extra; @@ -116,112 +112,58 @@ static int _dict_iter_get_channels(char const* key, UNUSED_ARG(void* data), void return 0; } -static int _dict_iter_get_servers(char const* key, UNUSED_ARG(void* data), void* extra) { - PyObject* tmp; - struct _tuple_dict_extra* real_extra = (struct _tuple_dict_extra*)extra; - - if ((tmp = PyString_FromString(key)) == NULL) - return 1; - - if (PyTuple_SetItem(real_extra->data, *(int*)real_extra->extra, tmp)) { - Py_DECREF(tmp); - return 1; - } - - *real_extra->extra = *real_extra->extra + 1; - return 0; -} - -/* get a tuple with all users in it */ static PyObject* -emb_get_users(UNUSED_ARG(PyObject *self), PyObject *args) { +pyobj_from_dict_t(dict_t d) { PyObject* retval; - PyObject* tmp; - size_t num_clients, n = 0, i; + size_t n = 0; struct _tuple_dict_extra extra; - if (!PyArg_ParseTuple(args, "")) - return NULL; - - num_clients = dict_size(clients); - retval = PyTuple_New(num_clients); - if (retval == NULL) + if ((retval = PyTuple_New(dict_size(d))) == NULL) return NULL; extra.extra = &n; extra.data = retval; - if (dict_foreach(clients, _dict_iter_get_users, (void*)&extra) != NULL) { - for (i = 0; i < n; ++i) { - tmp = PyTuple_GetItem(retval, i); - PyTuple_SET_ITEM(retval, i, NULL); - Py_DECREF(tmp); - } - Py_DECREF(retval); + if (dict_foreach(d, _dict_iter_fill_tuple, (void*)&extra) != NULL) { + pyobj_release_tuple(retval, n); return NULL; } return retval; } -/* get a tuple with all channels in it */ +/* get a tuple with all users in it */ static PyObject* -emb_get_channels(UNUSED_ARG(PyObject* self), PyObject* args) { - PyObject* retval; - PyObject* tmp; - size_t num_channels, n = 0, i; - struct _tuple_dict_extra extra; - +emb_get_users(UNUSED_ARG(PyObject *self), PyObject *args) { if (!PyArg_ParseTuple(args, "")) return NULL; - num_channels = dict_size(channels); - retval = PyTuple_New(num_channels); - if (retval == NULL) - return NULL; - - extra.extra = &n; - extra.data = retval; + return pyobj_from_dict_t(clients); +} - if (dict_foreach(channels, _dict_iter_get_channels, (void*)&extra) != NULL) { - for (i = 0; i < n; ++i) { - tmp = PyTuple_GetItem(retval, i); - PyTuple_SET_ITEM(retval, i, NULL); - Py_DECREF(tmp); - } - Py_DECREF(retval); +/* get a tuple with all channels in it */ +static PyObject* +emb_get_channels(UNUSED_ARG(PyObject* self), PyObject* args) { + if (!PyArg_ParseTuple(args, "")) return NULL; - } - return retval; + return pyobj_from_dict_t(channels); } static PyObject* emb_get_servers(UNUSED_ARG(PyObject* self), PyObject* args) { - PyObject* retval; - PyObject* tmp; - size_t n = 0, i; - struct _tuple_dict_extra extra; - if (!PyArg_ParseTuple(args, "")) return NULL; - retval = PyTuple_New(dict_size(servers)); - - extra.extra = &n; - extra.data = retval; + return pyobj_from_dict_t(servers); +} - if (dict_foreach(servers, _dict_iter_get_servers, (void*)&extra) != NULL) { - for (i = 0; i < n; ++i) { - tmp = PyTuple_GetItem(retval, i); - PyTuple_SET_ITEM(retval, i, NULL); - Py_DECREF(tmp); - } - Py_DECREF(retval); +static PyObject* +emb_get_accounts(UNUSED_ARG(PyObject* self), PyObject* args) { + if (!PyArg_ParseTuple(args, "")) return NULL; - } - return retval; + return pyobj_from_dict_t(nickserv_handle_dict); } static PyObject* @@ -315,7 +257,7 @@ emb_send_target_notice(UNUSED_ARG(PyObject *self), PyObject *args) static PyObject* pyobj_from_usernode(struct userNode* user) { - unsigned int n, i; + unsigned int n; struct modeNode *mn; PyObject* retval = NULL; PyObject* pChanList = PyTuple_New(user->channels.used); @@ -370,12 +312,7 @@ pyobj_from_usernode(struct userNode* user) { cleanup: Py_XDECREF(retval); - for (i = 0; i < n; ++i) { - retval = PyTuple_GetItem(pChanList, i); - PyTuple_SET_ITEM(pChanList, i, NULL); - Py_XDECREF(retval); - } - Py_DECREF(pChanList); + pyobj_release_tuple(pChanList, n); return NULL; } @@ -461,13 +398,7 @@ pyobj_from_server(struct server* srv) { cleanup: Py_XDECREF(retval); - - for (n = 0; n < idx; ++n) { - tmp = PyTuple_GetItem(users, n); - PyTuple_SetItem(users, n, NULL); - Py_DECREF(tmp); - } - Py_DECREF(users); + pyobj_release_tuple(users, idx); return NULL; } @@ -493,6 +424,82 @@ emb_get_server(UNUSED_ARG(PyObject* self), PyObject* args) { return pyobj_from_server(srv); } +static PyObject* +pyobj_from_modelist(struct modeList* mode) { + size_t n; + PyObject* tmp; + PyObject* retval = PyTuple_New(mode->used); + + if (retval == NULL) + return NULL; + + for (n = 0; n < mode->used; ++n) { + struct modeNode* mn = mode->list[n]; + tmp = PyString_FromString(mn->user->nick); + if (tmp == NULL) { + pyobj_release_tuple(retval, n); + return NULL; + } + + if (PyTuple_SetItem(retval, n, tmp)) { + pyobj_release_tuple(retval, n); + return NULL; + } + } + + return retval; +} + +static PyObject* +pyobj_from_banlist(struct banList* bans) { + size_t n; + struct banNode* bn; + PyObject* tmp; + PyObject* retval = PyTuple_New(bans->used); + + if (retval == NULL) + return NULL; + + for (n = 0; n < bans->used; ++n) { + bn = bans->list[n]; + + tmp = Py_BuildValue("{s:s,s:s,s:l}", + "ban", bn->ban, "who", bn->who, "set", bn->set); + + if (tmp == NULL || PyTuple_SetItem(retval, n, tmp)) { + pyobj_release_tuple(retval, n); + return NULL; + } + } + + return retval; +} + +static PyObject* +pyobj_from_exemptlist(struct exemptList* exmp) { + size_t n; + struct exemptNode* en; + PyObject* tmp; + PyObject* retval = PyTuple_New(exmp->used); + + if (retval == NULL) + return NULL; + + for (n = 0; n < exmp->used; ++n) { + en = exmp->list[n]; + + tmp = Py_BuildValue("{s:s,s:s,s:l}", + "ban", en->exempt, "who", en->who, "set", en->set); + + if (tmp == NULL || PyTuple_SetItem(retval, n, tmp)) { + pyobj_release_tuple(retval, n); + return NULL; + } + } + + return retval; +} + static PyObject* emb_get_channel(UNUSED_ARG(PyObject *self), PyObject *args) { @@ -501,10 +508,10 @@ emb_get_channel(UNUSED_ARG(PyObject *self), PyObject *args) */ char *name; struct chanNode *channel; - unsigned int n; - PyObject *pChannelMembers; - PyObject *pChannelBans; - PyObject *pChannelExempts; + PyObject *pChannelMembers = NULL; + PyObject *pChannelBans = NULL; + PyObject *pChannelExempts = NULL; + PyObject *retval = NULL; if(!PyArg_ParseTuple(args, "s", &name)) @@ -516,37 +523,21 @@ emb_get_channel(UNUSED_ARG(PyObject *self), PyObject *args) } /* build tuple of nicks in channel */ - pChannelMembers = PyTuple_New(channel->members.used); - for(n=0;n < channel->members.used;n++) { - struct modeNode *mn = channel->members.list[n]; - PyTuple_SetItem(pChannelMembers, n, Py_BuildValue("s", mn->user->nick)); - } + pChannelMembers = pyobj_from_modelist(&channel->members); + if (pChannelMembers == NULL) + goto cleanup; /* build tuple of bans */ - pChannelBans = PyTuple_New(channel->banlist.used); - for(n=0; n < channel->banlist.used;n++) { - struct banNode *bn = channel->banlist.list[n]; - PyTuple_SetItem(pChannelBans, n, - Py_BuildValue("{s:s,s:s,s:i}", - "ban", bn->ban, - "who", bn->who, - "set", bn->set) - ); - } + pChannelBans = pyobj_from_banlist(&channel->banlist); + if (pChannelBans == NULL) + goto cleanup; /* build tuple of exempts */ - pChannelExempts = PyTuple_New(channel->exemptlist.used); - for(n=0; n < channel->exemptlist.used;n++) { - struct exemptNode *en = channel->exemptlist.list[n]; - PyTuple_SetItem(pChannelExempts, n, - Py_BuildValue("{s:s,s:s,s:i}", - "ban", en->exempt, - "who", en->who, - "set", en->set) - ); - } + pChannelExempts = pyobj_from_exemptlist(&channel->exemptlist); + if (pChannelExempts == NULL) + goto cleanup; - return Py_BuildValue("{s:s,s:s,s:s,s:i" + retval = Py_BuildValue("{s:s,s:s,s:s,s:i" ",s:i,s:i,s:O,s:O,s:O}", "name", channel->name, @@ -560,6 +551,18 @@ emb_get_channel(UNUSED_ARG(PyObject *self), PyObject *args) "bans", pChannelBans, "exempts", pChannelExempts ); + if (retval == NULL) + goto cleanup; + + return retval; + +cleanup: + Py_XDECREF(retval); + pyobj_release_tuple(pChannelExempts, channel->exemptlist.used); + pyobj_release_tuple(pChannelBans, channel->banlist.used); + pyobj_release_tuple(pChannelMembers, channel->members.used); + + return NULL; } static PyObject* @@ -642,6 +645,100 @@ emb_log_module(UNUSED_ARG(PyObject *self), PyObject *args) return Py_BuildValue("i", ret); } +static PyObject* +emb_kill(UNUSED_ARG(PyObject* self), PyObject* args) { + char const* from_nick, *target_nick, *message; + struct userNode *target; + struct service *service; + + if (!PyArg_ParseTuple(args, "sss", &from_nick, &target_nick, &message)) + return NULL; + + if(!(service = service_find(from_nick))) { + PyErr_SetString(PyExc_Exception, "unknown service user specified as from user"); + return NULL; + } + + if ((target = GetUserH(target_nick)) == NULL) { + PyErr_SetString(PyExc_Exception, "unknown target user"); + return NULL; + } + + irc_kill(service->bot, target, message); + + return Py_None; +} + +struct py_timeq_extra { + PyObject* func; + PyObject* arg; +}; + +static +void py_timeq_callback(void* data) { + struct py_timeq_extra* extra = (struct py_timeq_extra*)data; + + PyObject* retval = PyObject_Call(extra->func, extra->arg, NULL); + Py_XDECREF(retval); + + Py_DECREF(extra->func); + Py_DECREF(extra->arg); +} + +static PyObject* +emb_timeq_add(UNUSED_ARG(PyObject* self), PyObject* args) { + time_t when; + PyObject* func, *arg; + struct py_timeq_extra* extra; + + if (!PyArg_ParseTuple(args, "lOO", &when, &func, &arg)) + return NULL; + + if (!PyFunction_Check(func)) { + PyErr_SetString(PyExc_Exception, "first argument must be a function"); + return NULL; + } + + if (!PyTuple_Check(arg)) { + PyErr_SetString(PyExc_Exception, "second argument must be a tuple"); + return NULL; + } + + extra = malloc(sizeof(struct py_timeq_extra)); + if (extra == NULL) { + PyErr_SetString(PyExc_Exception, "out of memory"); + return NULL; + } + + Py_INCREF(func); + Py_INCREF(arg); + + extra->func = func; + extra->arg = arg; + + timeq_add(when, py_timeq_callback, (void*)extra); + + return Py_None; +} + +static PyObject* +emb_timeq_del(UNUSED_ARG(PyObject* self), PyObject* args) { + /* NOTE: + * This function will delete all python-added callbacks registered + * to run at the given time, regardless of their data. This is due to + * the unnecessary extra burden it would require to get the same data for + * multiple runs. + */ + time_t when; + + if (!PyArg_ParseTuple(args, "l", &when)) + return NULL; + + timeq_del(when, py_timeq_callback, NULL, TIMEQ_IGNORE_DATA); + + return Py_None; +} + static PyMethodDef EmbMethods[] = { /* Communication methods */ {"dump", emb_dump, METH_VARARGS, "Dump raw P10 line to server"}, @@ -650,7 +747,7 @@ static PyMethodDef EmbMethods[] = { {"log_module", emb_log_module, METH_VARARGS, "Log something using the X3 log subsystem"}, //TODO: {"exec_cmd", emb_exec_cmd, METH_VARARGS, "execute x3 command provided"}, // This should use environment from "python command" call to pass in, if available -//TODO: {"kill" + {"kill", emb_kill, METH_VARARGS, "Kill someone"}, //TODO: {"shun" //TODO: {"unshun" //TODO: {"gline", emb_gline, METH_VARARGS, "gline a mask"}, @@ -662,8 +759,9 @@ static PyMethodDef EmbMethods[] = { //TODO: {"get_config", emb_get_config, METH_VARARGS, "get x3.conf settings into a nested dict"}, //TODO: {"config_set", emb_config_set, METH_VARARGS, "change a config setting 'on-the-fly'."}, // -//TODO: {"timeq_add", emb_timeq_new, METH_VARARGS, "some kind of interface to the timed event system."}, -//TODO: {"timeq_del", emb_timeq_new, METH_VARARGS, "some kind of interface to the timed event system."}, + {"timeq_add", emb_timeq_add, METH_VARARGS, "add function to callback to the event system"}, + {"timeq_del", emb_timeq_del, METH_VARARGS, "remove function to callback from the event system"}, + /* Information gathering methods */ {"get_user", emb_get_user, METH_VARARGS, "Get details about a nickname"}, {"get_users", emb_get_users, METH_VARARGS, "Get all connected users"}, @@ -672,6 +770,7 @@ static PyMethodDef EmbMethods[] = { {"get_server", emb_get_server, METH_VARARGS, "Get details about a server"}, {"get_servers", emb_get_servers, METH_VARARGS, "Get all server names"}, {"get_account", emb_get_account, METH_VARARGS, "Get details about an account"}, + {"get_accounts", emb_get_accounts, METH_VARARGS, "Get all nickserv accounts"}, {"get_info", emb_get_info, METH_VARARGS, "Get various misc info about x3"}, /* null terminator */ {NULL, NULL, 0, NULL}