]> jfr.im git - irc/evilnet/x3.git/blame - src/mod-python.c
mod-python: add support for module_register
[irc/evilnet/x3.git] / src / mod-python.c
CommitLineData
0b350353 1/* mod-python.c - Script module for x3
2 * Copyright 2003-2004 Martijn Smit and srvx Development Team
3 * Copyright 2005-2006 X3 Development Team
4 *
5 * This file is part of x3.
6 *
7 * x3 is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with srvx; if not, write to the Free Software Foundation,
19 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20 */
21
22#include "config.h"
23#ifdef WITH_PYTHON /* just disable this file if python doesnt exist */
24
d8f8d3b6 25#ifndef WITH_PROTOCOL_P10
26#error mod-python is only supported with p10 protocol enabled
27#endif /* WITH_PROTOCOL_P10 */
0b350353 28
413fd8ea 29#include <Python.h>
0b350353 30#include "chanserv.h"
31#include "conf.h"
32#include "modcmd.h"
33#include "nickserv.h"
34#include "opserv.h"
35#include "saxdb.h"
1136f709 36#include "mail.h"
0b350353 37#include "timeq.h"
039a6658 38#include "compat.h"
39d37f27 39#include "nickserv.h"
0b350353 40
a2c8c575 41/* TODO notes
42 *
f0e11521 43 * - Implement most of proto-p10 irc_* commands for calling from scripts
44 * - Implement functions to look up whois, channel, account, and reg-channel info for scripts
45 * - Implement x3.conf settings for python variables like include path, etc.
4c216694 46 * - modpython.py calls for everything you can reg_ a handler for in x3
a2c8c575 47 * - Some kind of system for getting needed binds bound automagicaly to make it easier
f0e11521 48 * to run peoples' scripts and mod-python in general.
039a6658 49 * - An interface to reading/writing data to x3.db. Maybe generic, or attached to account or channel reg records?
a2c8c575 50 */
0b350353 51
52static const struct message_entry msgtab[] = {
caf97651 53 { "PYMSG_RELOAD_SUCCESS", "Reloaded Python scripts successfully." },
54 { "PYMSG_RELOAD_FAILED", "Error reloading Python scripts." },
413fd8ea 55 { "PYMSG_RUN_UNKNOWN_EXCEPTION", "Error running python: unknown exception." },
46f628b1 56 { "PYMSG_RUN_EXCEPTION", "Error running python: %s: %s." },
f0e11521 57 { NULL, NULL } /* sentinel */
0b350353 58};
59
ef5e0305 60#define MODPYTHON_CONF_NAME "modules/python"
61
62static
63struct {
64 char const* scripts_dir;
ed8d873c 65 char const* main_module;
ef5e0305 66} modpython_conf;
67
0b350353 68static struct log_type *PY_LOG;
69const char *python_module_deps[] = { NULL };
70static struct module *python_module;
71
caf97651 72PyObject *base_module = NULL; /* Base python handling library */
f0e11521 73PyObject *handler_object = NULL; /* instance of handler class */
caf97651 74
4c216694 75
039a6658 76extern struct userNode *global, *chanserv, *opserv, *nickserv, *spamserv;
77
f0e11521 78/*
79Some hooks you can call from modpython.py to interact with the
80service. These emb_* functions are available as _svc.* in python. */
4c216694 81
6d94ce8b 82struct _tuple_dict_extra {
83 PyObject* data;
84 size_t* extra;
85};
86
ee6f1c82 87static void pyobj_release_tuple(PyObject* tuple, size_t n) {
88 size_t i;
89
90 if (tuple == NULL)
91 return;
92
93 for (i = 0; i < n; ++i)
94 Py_XDECREF(PyTuple_GET_ITEM(tuple, i));
95
96 Py_XDECREF(tuple);
97}
98
39d37f27 99static int _dict_iter_fill_tuple(char const* key, UNUSED_ARG(void* data), void* extra) {
318ec177 100 PyObject* tmp;
dcc1df5e 101 struct _tuple_dict_extra* real_extra = (struct _tuple_dict_extra*)extra;
102
318ec177 103 if ((tmp = PyString_FromString(key)) == NULL)
104 return 1;
105
106 if (PyTuple_SetItem(real_extra->data, *(int*)real_extra->extra, tmp)) {
107 Py_DECREF(tmp);
108 return 1;
109 }
110
dcc1df5e 111 *real_extra->extra = *real_extra->extra + 1;
112 return 0;
113}
114
6d94ce8b 115static PyObject*
b39754f7 116pyobj_from_dict_t(dict_t d) {
6d94ce8b 117 PyObject* retval;
b39754f7 118 size_t n = 0;
6d94ce8b 119 struct _tuple_dict_extra extra;
120
b39754f7 121 if ((retval = PyTuple_New(dict_size(d))) == NULL)
5345ea76 122 return NULL;
6d94ce8b 123
124 extra.extra = &n;
125 extra.data = retval;
126
b39754f7 127 if (dict_foreach(d, _dict_iter_fill_tuple, (void*)&extra) != NULL) {
ee6f1c82 128 pyobj_release_tuple(retval, n);
5345ea76 129 return NULL;
130 }
6d94ce8b 131
132 return retval;
133}
134
5661f28e 135PyDoc_STRVAR(emb_get_users__doc__,
136 "get_users() -> tuple with user nicks");
137
cc0b2b7f 138static PyObject*
b39754f7 139emb_get_users(UNUSED_ARG(PyObject *self), PyObject *args) {
cc0b2b7f 140 if (!PyArg_ParseTuple(args, ""))
141 return NULL;
142
b39754f7 143 return pyobj_from_dict_t(clients);
144}
cc0b2b7f 145
5661f28e 146PyDoc_STRVAR(emb_get_channels__doc__,
147 "get_channels() -> tuple with channel names");
148
b39754f7 149static PyObject*
150emb_get_channels(UNUSED_ARG(PyObject* self), PyObject* args) {
151 if (!PyArg_ParseTuple(args, ""))
d12756d7 152 return NULL;
cc0b2b7f 153
b39754f7 154 return pyobj_from_dict_t(channels);
cc0b2b7f 155}
156
5661f28e 157PyDoc_STRVAR(emb_get_servers__doc__,
158 "get_servers() -> tuple with server names");
159
dcc1df5e 160static PyObject*
161emb_get_servers(UNUSED_ARG(PyObject* self), PyObject* args) {
dcc1df5e 162 if (!PyArg_ParseTuple(args, ""))
163 return NULL;
164
b39754f7 165 return pyobj_from_dict_t(servers);
39d37f27 166}
167
5661f28e 168PyDoc_STRVAR(emb_get_accounts__doc__,
169 "get_accounts() -> tuple with all nickserv account names");
170
39d37f27 171static PyObject*
172emb_get_accounts(UNUSED_ARG(PyObject* self), PyObject* args) {
39d37f27 173 if (!PyArg_ParseTuple(args, ""))
174 return NULL;
175
b39754f7 176 return pyobj_from_dict_t(nickserv_handle_dict);
dcc1df5e 177}
178
5661f28e 179PyDoc_STRVAR(emb_dump__doc__,
180 "dump(dump) -> an integer detailing success\n\n"
181 "Dumps a string to the server socket for propagation to other servers.\n\n"
182 "Return value is 1 on success and 0 on failure.\n");
183
a2c8c575 184static PyObject*
039a6658 185emb_dump(UNUSED_ARG(PyObject *self), PyObject *args)
a2c8c575 186{
4c216694 187 /* Dump a raw string into the socket
50d61a79 188 usage: _svc.dump(<P10 string>)
4c216694 189 */
a2c8c575 190 char *buf;
191 int ret = 0;
192 char linedup[MAXLEN];
193
039a6658 194
a2c8c575 195 if(!PyArg_ParseTuple(args, "s:dump", &buf ))
196 return NULL;
a957511b 197
a2c8c575 198 safestrncpy(linedup, buf, sizeof(linedup));
a957511b 199
a2c8c575 200 if(parse_line(linedup, 1)) {
201 irc_raw(buf);
202 ret = 1;
a957511b 203 } else {
204 PyErr_SetString(PyExc_Exception, "invalid protocol message");
205 return NULL;
a2c8c575 206 }
a957511b 207
a2c8c575 208 return Py_BuildValue("i", ret);
209}
210
5661f28e 211PyDoc_STRVAR(emb_send_target_privmsg__doc__,
212 "send_target_privmsg(servicenick, target, message) -> amount of message sent");
213
a2c8c575 214static PyObject*
039a6658 215emb_send_target_privmsg(UNUSED_ARG(PyObject *self), PyObject *args)
a2c8c575 216{
4c216694 217 /* Send a privmsg
50d61a79 218 usage: _svc.send_target_privmsg(<servicenick_from>, <nick_to>, <message>)
4c216694 219 */
a2c8c575 220 int ret = 0;
221 char *servicenick;
222 char *channel;
223 char *buf;
224
225 struct service *service;
226
039a6658 227
a2c8c575 228 if(!PyArg_ParseTuple(args, "sss:reply", &servicenick, &channel, &buf ))
229 return NULL;
e7af1e12 230
231 if (buf == NULL || strlen(buf) == 0) {
232 PyErr_SetString(PyExc_Exception, "invalid empty message");
233 return NULL;
234 }
235
a2c8c575 236 if(!(service = service_find(servicenick))) {
e7af1e12 237 PyErr_SetString(PyExc_Exception, "no such service nick");
8d670803 238 return NULL;
a2c8c575 239 }
e7af1e12 240
241 ret = send_target_message(5, channel, service->bot, "%s", buf);
a2c8c575 242 return Py_BuildValue("i", ret);
243}
244
5661f28e 245PyDoc_STRVAR(emb_send_target_notice__doc__,
246 "send_target_notice(servicenick, target, message) -> amount of message sent");
247
d4e0f0c4 248static PyObject*
039a6658 249emb_send_target_notice(UNUSED_ARG(PyObject *self), PyObject *args)
d4e0f0c4 250{
4c216694 251 /* send a notice
50d61a79 252 usage: _svc.send_target_notice(<servicenick_from>, <nick_to>, <message>)
4c216694 253 */
d4e0f0c4 254 int ret = 0;
255 char *servicenick;
256 char *target;
257 char *buf;
258
259 struct service *service;
260
261 if(!PyArg_ParseTuple(args, "sss:reply", &servicenick, &target, &buf ))
262 return NULL;
66f68f65 263
264 if (buf == NULL || strlen(buf) == 0) {
265 PyErr_SetString(PyExc_Exception, "invalid empty message");
266 return NULL;
267 }
268
d4e0f0c4 269 if(!(service = service_find(servicenick))) {
66f68f65 270 PyErr_SetString(PyExc_Exception, "no such service nick");
d4e0f0c4 271 return NULL;
272 }
66f68f65 273
274 ret = send_target_message(4, target, service->bot, "%s", buf);
275
d4e0f0c4 276 return Py_BuildValue("i", ret);
277}
278
bfdfd1c3 279static PyObject*
280pyobj_from_usernode(struct userNode* user) {
ee6f1c82 281 unsigned int n;
bfdfd1c3 282 struct modeNode *mn;
82089e3f 283 PyObject* retval = NULL;
bfdfd1c3 284 PyObject* pChanList = PyTuple_New(user->channels.used);
285
82089e3f 286 if (pChanList == NULL)
287 return NULL;
288
bfdfd1c3 289 for (n=0; n < user->channels.used; n++) {
290 mn = user->channels.list[n];
82089e3f 291 if (PyTuple_SetItem(pChanList, n, Py_BuildValue("s", mn->channel->name)))
292 goto cleanup;
bfdfd1c3 293 }
294
82089e3f 295 retval = Py_BuildValue("{"
bfdfd1c3 296 "s: s, " /* nick */
297 "s: s, " /* ident */
298 "s: s, " /* info */
299 "s: s, " /* hostname */
300 "s: s, " /* ip */
301 "s: s, " /* fakehost */
302 "s: s, " /* sethost */
303 "s: s, " /* crypthost */
304 "s: s, " /* cryptip */
bfdfd1c3 305 "s: s, " /* numeric */
bfdfd1c3 306 "s: i, " /* loc */
307 "s: i, " /* no_notice */
308 "s: s, " /* mark */
309 "s: s, " /* version_reply */
310 "s: s, " /* account */
311 "s: O}", /* channels */
312 "nick", user->nick,
313 "ident", user->ident,
314 "info", user->info,
315 "hostname", user->hostname,
316 "ip", irc_ntoa(&user->ip),
317 "fakehost", user->fakehost,
318 "sethost", user->sethost,
319 "crypthost", user->crypthost,
320 "cryptip", user->cryptip,
bfdfd1c3 321 "numeric", user->numeric,
bfdfd1c3 322 "loc", user->loc,
323 "no_notice", user->no_notice,
324 "mark", user->mark,
325 "version_reply", user->version_reply,
326 "account", user->handle_info ? user->handle_info->handle : NULL,
327 "channels", pChanList);
82089e3f 328
329 if (retval == NULL)
330 goto cleanup;
331
332 return retval;
333
334cleanup:
335 Py_XDECREF(retval);
ee6f1c82 336 pyobj_release_tuple(pChanList, n);
82089e3f 337
338 return NULL;
bfdfd1c3 339}
340
5661f28e 341PyDoc_STRVAR(emb_get_user__doc__,
342 "get_user(nick) -> dict with user information\n\n"
343 "Updating the returned dictionary will not be reflected in the user's\n"
344 "information.");
345
8d670803 346static PyObject*
039a6658 347emb_get_user(UNUSED_ARG(PyObject *self), PyObject *args)
8d670803 348{
4c216694 349 /* Get a python object containing everything x3 knows about a user, by nick.
50d61a79 350 usage: _svc.get_user(<nick>)
4c216694 351 */
54d2fd3d 352 char const* nick;
8d670803 353 struct userNode *user;
039a6658 354
8d670803 355 if(!PyArg_ParseTuple(args, "s", &nick))
356 return NULL;
bfdfd1c3 357
8d670803 358 if(!(user = GetUserH(nick))) {
54d2fd3d 359 PyErr_SetString(PyExc_Exception, "no such user");
8d670803 360 return NULL;
361 }
bfdfd1c3 362
363 return pyobj_from_usernode(user);
8d670803 364}
365
d8f8d3b6 366static PyObject*
367pyobj_from_server(struct server* srv) {
368 size_t n, idx;
369 PyObject* tmp = NULL;
370 PyObject* retval = NULL;
371 PyObject* users = PyTuple_New(srv->clients);
372
373 if (users == NULL)
374 return NULL;
375
376 idx = 0;
377 for (n = 0; n < srv->num_mask; ++n) {
378 if (srv->users[n] == NULL)
379 continue;
380
381 tmp = PyString_FromString(srv->users[n]->nick);
382 if (tmp == NULL)
383 goto cleanup;
384
385 if (PyTuple_SetItem(users, idx++, tmp))
386 goto cleanup;
387 }
388
389 retval = Py_BuildValue("{"
390 "s:s," /* name */
391 "s:l," /* boot */
392 "s:l," /* link_time */
393 "s:s," /* description */
394 "s:s," /* numeric */
395 "s:I," /* num_mask */
396 "s:I," /* hops */
397 "s:I," /* clients */
398 "s:I," /* max_clients */
399 "s:I," /* burst */
400 "s:I," /* self_burst */
401 "s:s" /* uplink */
402 "s:O" /* users */
403 /* TODO: Children */
404 "}",
405 "name", srv->name,
406 "boot", srv->boot,
407 "link_time", srv->link_time,
408 "description", srv->description,
409 "numeric", srv->numeric,
410 "num_mask", srv->num_mask,
411 "hops", srv->hops,
412 "clients", srv->clients,
413 "max_clients", srv->max_clients,
414 "burst", srv->burst,
415 "self_burst", srv->self_burst,
416 "uplink", srv->uplink ? srv->uplink->name : NULL,
417 "users", users
418 );
419
420 if (retval == NULL)
421 goto cleanup;
422
423 return retval;
424
425cleanup:
426 Py_XDECREF(retval);
ee6f1c82 427 pyobj_release_tuple(users, idx);
d8f8d3b6 428
429 return NULL;
430}
431
5661f28e 432PyDoc_STRVAR(emb_get_server__doc__,
433 "get_server(name) -> dict with information\n\n"
434 "Changes made to the returned dictionary will not reflect in the server's\n"
435 "information.");
436
d8f8d3b6 437static PyObject*
438emb_get_server(UNUSED_ARG(PyObject* self), PyObject* args) {
439 struct server* srv;
440 char const* name;
441
442 if (!PyArg_ParseTuple(args, "s", &name))
443 return NULL;
444
445 if (name == NULL || strlen(name) == 0) {
446 PyErr_SetString(PyExc_Exception, "invalid server name");
447 return NULL;
448 }
449
450 if ((srv = GetServerH(name)) == NULL) {
451 PyErr_SetString(PyExc_Exception, "unknown server");
452 return NULL;
453 }
454
455 return pyobj_from_server(srv);
456}
457
92fb809b 458static PyObject*
459pyobj_from_modelist(struct modeList* mode) {
460 size_t n;
461 PyObject* tmp;
462 PyObject* retval = PyTuple_New(mode->used);
463
464 if (retval == NULL)
465 return NULL;
466
467 for (n = 0; n < mode->used; ++n) {
468 struct modeNode* mn = mode->list[n];
469 tmp = PyString_FromString(mn->user->nick);
470 if (tmp == NULL) {
471 pyobj_release_tuple(retval, n);
472 return NULL;
473 }
474
475 if (PyTuple_SetItem(retval, n, tmp)) {
476 pyobj_release_tuple(retval, n);
477 return NULL;
478 }
479 }
480
481 return retval;
482}
483
c9f7b679 484static PyObject*
485pyobj_from_banlist(struct banList* bans) {
486 size_t n;
487 struct banNode* bn;
488 PyObject* tmp;
489 PyObject* retval = PyTuple_New(bans->used);
490
491 if (retval == NULL)
492 return NULL;
493
494 for (n = 0; n < bans->used; ++n) {
495 bn = bans->list[n];
496
497 tmp = Py_BuildValue("{s:s,s:s,s:l}",
498 "ban", bn->ban, "who", bn->who, "set", bn->set);
499
500 if (tmp == NULL || PyTuple_SetItem(retval, n, tmp)) {
501 pyobj_release_tuple(retval, n);
502 return NULL;
503 }
504 }
505
506 return retval;
507}
508
509static PyObject*
510pyobj_from_exemptlist(struct exemptList* exmp) {
511 size_t n;
512 struct exemptNode* en;
513 PyObject* tmp;
514 PyObject* retval = PyTuple_New(exmp->used);
515
516 if (retval == NULL)
517 return NULL;
518
519 for (n = 0; n < exmp->used; ++n) {
520 en = exmp->list[n];
521
522 tmp = Py_BuildValue("{s:s,s:s,s:l}",
523 "ban", en->exempt, "who", en->who, "set", en->set);
524
525 if (tmp == NULL || PyTuple_SetItem(retval, n, tmp)) {
526 pyobj_release_tuple(retval, n);
527 return NULL;
528 }
529 }
530
531 return retval;
532}
533
8d670803 534static PyObject*
43b43d56 535pyobj_from_channode(struct chanNode* channel) {
c9f7b679 536 PyObject *pChannelMembers = NULL;
537 PyObject *pChannelBans = NULL;
538 PyObject *pChannelExempts = NULL;
539 PyObject *retval = NULL;
8d670803 540
8d670803 541 /* build tuple of nicks in channel */
92fb809b 542 pChannelMembers = pyobj_from_modelist(&channel->members);
c9f7b679 543 if (pChannelMembers == NULL)
544 goto cleanup;
8d670803 545
546 /* build tuple of bans */
c9f7b679 547 pChannelBans = pyobj_from_banlist(&channel->banlist);
548 if (pChannelBans == NULL)
549 goto cleanup;
8d670803 550
8d670803 551 /* build tuple of exempts */
c9f7b679 552 pChannelExempts = pyobj_from_exemptlist(&channel->exemptlist);
553 if (pChannelExempts == NULL)
554 goto cleanup;
8d670803 555
c9f7b679 556 retval = Py_BuildValue("{s:s,s:s,s:s,s:i"
8d670803 557 ",s:i,s:i,s:O,s:O,s:O}",
558
559 "name", channel->name,
560 "topic", channel->topic,
561 "topic_nick", channel->topic_nick,
562 "topic_time", channel->topic_time,
563
564 "timestamp", channel->timestamp,
565 "modes", channel->modes,
566 "members", pChannelMembers,
567 "bans", pChannelBans,
568 "exempts", pChannelExempts
569 );
c9f7b679 570 if (retval == NULL)
571 goto cleanup;
572
573 return retval;
574
575cleanup:
576 Py_XDECREF(retval);
577 pyobj_release_tuple(pChannelExempts, channel->exemptlist.used);
578 pyobj_release_tuple(pChannelBans, channel->banlist.used);
579 pyobj_release_tuple(pChannelMembers, channel->members.used);
580
581 return NULL;
8d670803 582}
583
43b43d56 584PyDoc_STRVAR(emb_get_channel__doc__,
585 "get_channel(channel) -> dict with channel information\n\n"
586 "Updates made to the returned dictionary does not reflect in the channel\n"
587 "information.");
588
589static PyObject*
590emb_get_channel(UNUSED_ARG(PyObject *self), PyObject *args)
591{
592 /* Returns a python dict object with all sorts of info about a channel.
593 usage: _svc.get_channel(<name>)
594 */
595 char *name;
596 struct chanNode *channel;
597
598 if(!PyArg_ParseTuple(args, "s", &name))
599 return NULL;
600
601 if(!(channel = GetChannel(name))) {
602 PyErr_SetString(PyExc_Exception, "unknown channel");
603 return NULL;
604 }
605
606 return pyobj_from_channode(channel);
607}
608
5661f28e 609PyDoc_STRVAR(emb_get_account__doc__,
610 "get_account(account) -> dict with account information\n\n"
611 "Changes made to the returned dictionary will not be reflected in the\n"
612 "account's information.");
613
8d670803 614static PyObject*
039a6658 615emb_get_account(UNUSED_ARG(PyObject *self), PyObject *args)
8d670803 616{
4c216694 617 /* Returns a python dict object with all sorts of info about an account.
50d61a79 618 usage: _svc.get_account(<account name>)
4c216694 619 */
8d670803 620 char *name;
4c216694 621 struct handle_info *hi;
622
039a6658 623
8d670803 624 if(!PyArg_ParseTuple(args, "s", &name))
625 return NULL;
4c216694 626
627 hi = get_handle_info(name);
5b2b1df2 628
4c216694 629 if(!hi) {
5b2b1df2 630 PyErr_SetString(PyExc_Exception, "unknown account name");
4c216694 631 return NULL;
632 }
5b2b1df2 633
4c216694 634 return Py_BuildValue("{s:s,s:i,s:s,s:s,s:s"
635 ",s:s,s:s}",
636
637 "account", hi->handle,
638 "registered", hi->registered,
639 "last_seen", hi->lastseen,
640 "infoline", hi->infoline ? hi->infoline : "",
641 "email", hi->email_addr ? hi->email_addr : "",
642
643 "fakehost", hi->fakehost ? hi->fakehost : "",
644 "last_quit_host", hi->last_quit_host
645
646 /* TODO: */
647 /* users online authed to this account */
648 /* cookies */
649 /* nicks (nickserv nets only?) */
650 /* masks */
651 /* ignores */
652 /* channels */
653 );
8d670803 654}
8d670803 655
5661f28e 656PyDoc_STRVAR(emb_get_info__doc__,
657 "get_info() -> dict with general service setup information\n\n"
658 "The dictionary contains the nicks of the different services.");
659
07559983 660static PyObject*
039a6658 661emb_get_info(UNUSED_ARG(PyObject *self), UNUSED_ARG(PyObject *args))
662{
663 /* return some info about the general setup
664 * of X3, such as what the chanserv's nickname
665 * is.
666 */
667
668
669 return Py_BuildValue("{s:s,s:s,s:s,s:s,s:s}",
670 "chanserv", chanserv? chanserv->nick : "ChanServ",
671 "nickserv", nickserv?nickserv->nick : "NickServ",
672 "opserv", opserv?opserv->nick : "OpServ",
673 "global", global?global->nick : "Global",
674 "spamserv", spamserv?spamserv->nick : "SpamServ");
675}
676
5661f28e 677PyDoc_STRVAR(emb_log_module__doc__,
678 "log_module(level, message)\n\n"
679 "Logs a message in the PY_LOG subsystem given a severity level and a message.");
680
039a6658 681static PyObject*
682emb_log_module(UNUSED_ARG(PyObject *self), PyObject *args)
07559983 683{
684 /* a gateway to standard X3 logging subsystem.
685 * level is a value 0 to 9 as defined by the log_severity enum in log.h.
07559983 686 *
687 * for now, all logs go to the PY_LOG log. In the future this will change.
688 */
689 char *message;
07559983 690 int level;
691
692 if(!PyArg_ParseTuple(args, "is", &level, &message))
693 return NULL;
694
695 log_module(PY_LOG, level, "%s", message);
696
5661f28e 697 Py_INCREF(Py_None);
698 return Py_None;
07559983 699}
8d670803 700
5661f28e 701PyDoc_STRVAR(emb_kill__doc__,
702 "kill(servicenick, target, message)\n\n"
703 "Kills a given user.");
704
0076604e 705static PyObject*
706emb_kill(UNUSED_ARG(PyObject* self), PyObject* args) {
707 char const* from_nick, *target_nick, *message;
708 struct userNode *target;
709 struct service *service;
710
711 if (!PyArg_ParseTuple(args, "sss", &from_nick, &target_nick, &message))
712 return NULL;
713
714 if(!(service = service_find(from_nick))) {
715 PyErr_SetString(PyExc_Exception, "unknown service user specified as from user");
716 return NULL;
717 }
718
719 if ((target = GetUserH(target_nick)) == NULL) {
720 PyErr_SetString(PyExc_Exception, "unknown target user");
721 return NULL;
722 }
723
724 irc_kill(service->bot, target, message);
725
8d455e8b 726 Py_INCREF(Py_None);
0076604e 727 return Py_None;
728}
729
8f206d22 730struct py_timeq_extra {
731 PyObject* func;
732 PyObject* arg;
733};
734
735static
736void py_timeq_callback(void* data) {
737 struct py_timeq_extra* extra = (struct py_timeq_extra*)data;
738
739 PyObject* retval = PyObject_Call(extra->func, extra->arg, NULL);
740 Py_XDECREF(retval);
741
742 Py_DECREF(extra->func);
743 Py_DECREF(extra->arg);
744}
745
5661f28e 746PyDoc_STRVAR(emb_timeq_add__doc__,
747 "timeq_add(when, function, args)\n\n"
748 "Adds a callback to the service timer system.\n\n"
749 "The specific function must be callable, and the specified arguments must be\n"
750 "a tuple with the arguments that the function expects.");
751
8f206d22 752static PyObject*
753emb_timeq_add(UNUSED_ARG(PyObject* self), PyObject* args) {
754 time_t when;
755 PyObject* func, *arg;
756 struct py_timeq_extra* extra;
757
758 if (!PyArg_ParseTuple(args, "lOO", &when, &func, &arg))
759 return NULL;
760
761 if (!PyFunction_Check(func)) {
762 PyErr_SetString(PyExc_Exception, "first argument must be a function");
763 return NULL;
764 }
765
766 if (!PyTuple_Check(arg)) {
767 PyErr_SetString(PyExc_Exception, "second argument must be a tuple");
768 return NULL;
769 }
770
771 extra = malloc(sizeof(struct py_timeq_extra));
772 if (extra == NULL) {
773 PyErr_SetString(PyExc_Exception, "out of memory");
774 return NULL;
775 }
776
777 Py_INCREF(func);
778 Py_INCREF(arg);
779
780 extra->func = func;
781 extra->arg = arg;
782
783 timeq_add(when, py_timeq_callback, (void*)extra);
784
8d455e8b 785 Py_INCREF(Py_None);
8f206d22 786 return Py_None;
787}
788
5661f28e 789PyDoc_STRVAR(emb_timeq_del__doc__,
790 "timeq_del(when)\n\n"
791 "This function deletes all python-added callbacks registered to run at the\n"
792 "given time, regardless of their data. This is due to the unnecessary extra\n"
793 "burden it would require to get the same data for multiple runs.");
794
3f218269 795static PyObject*
796emb_timeq_del(UNUSED_ARG(PyObject* self), PyObject* args) {
3f218269 797 time_t when;
798
799 if (!PyArg_ParseTuple(args, "l", &when))
800 return NULL;
801
802 timeq_del(when, py_timeq_callback, NULL, TIMEQ_IGNORE_DATA);
803
8d455e8b 804 Py_INCREF(Py_None);
3f218269 805 return Py_None;
806}
807
2bee6a6e 808static int pyobj_config_make_dict(char const* key, void* data_, void* extra) {
809 struct record_data* data = (struct record_data*)data_;
810 PyObject* dict = (PyObject*)extra;
811 PyObject* value = NULL, *tmp;
812 size_t n, idx;
813 int success;
814
815 switch (data->type) {
816 case RECDB_QSTRING:
817 value = PyString_FromString(data->d.qstring);
818 break;
819
820 case RECDB_STRING_LIST:
821 value = PyList_New(data->d.slist->used);
822 if (value == NULL)
823 break;
824
825 success = 1;
826 for (n = 0; n < data->d.slist->used; ++n) {
827 tmp = PyString_FromString(data->d.slist->list[n]);
828 if (tmp == NULL) {
829 success = 0;
830 break;
831 }
832
833 if (PyList_SetItem(value, n, tmp)) {
834 Py_DECREF(tmp);
835 success = 0;
836 break;
837 }
838 }
839 if (!success) {
840 for (idx = 0; idx < n; ++idx) {
841 tmp = PyList_GET_ITEM(value, idx);
842 Py_DECREF(tmp);
843 PyList_SET_ITEM(value, idx, NULL);
844 }
845 Py_DECREF(value);
846 value = NULL;
847 }
848 break;
849
850 case RECDB_OBJECT:
851 value = PyDict_New();
852 if (value == NULL)
853 break;
854
855 if (dict_foreach(data->d.object, pyobj_config_make_dict, (void*)value) != NULL) {
856 PyDict_Clear(value);
857 value = NULL;
858 break;
859 }
860
861 break;
862
863 default:
8d455e8b 864 Py_INCREF(Py_None);
2bee6a6e 865 value = Py_None;
866 }
867
868 if (value == NULL)
869 return 1;
870
871 if (PyDict_SetItemString(dict, key, value))
872 return 1;
873
874 return 0;
875}
876
5661f28e 877PyDoc_STRVAR(emb_get_config__doc__,
878 "get_config() -> dict with config elements and values\n\n"
879 "Updates to the returned dictionary will not reflect in the service's\n"
880 "configuration.");
881
2bee6a6e 882static PyObject*
883emb_get_config(UNUSED_ARG(PyObject* self), PyObject* args) {
884 PyObject* dict;
885
886 if (!PyArg_ParseTuple(args, ""))
887 return NULL;
888
889 dict = PyDict_New();
890 if (dict == NULL)
891 return NULL;
892
893 if (conf_enum_root(pyobj_config_make_dict, (void*)dict) != NULL) {
894 PyDict_Clear(dict);
895 PyErr_SetString(PyExc_Exception, "unable to iterate config");
896 return NULL;
897 }
898
899 return dict;
900}
901
5661f28e 902PyDoc_STRVAR(emb_kick__doc__,
903 "kick(who, target, message)\n\n"
904 "Kicks a given target as if the who user kicked them using the given message.");
905
749f0565 906static PyObject* emb_kick(UNUSED_ARG(PyObject* self), PyObject* args) {
907 struct userNode* who, *target;
908 struct chanNode* channel;
909 char const* msg;
910
911 char const* who_s, *target_s, *channel_s;
912
913 if (!PyArg_ParseTuple(args, "ssss", &who_s, &target_s, &channel_s, &msg))
914 return NULL;
915
916 if ((who = GetUserH(who_s)) == NULL) {
917 PyErr_SetString(PyExc_Exception, "no such user");
918 return NULL;
919 }
920
921 if ((target = GetUserH(target_s)) == NULL) {
922 PyErr_SetString(PyExc_Exception, "no such target");
923 return NULL;
924 }
925
926 if ((channel = GetChannel(channel_s)) == NULL) {
927 PyErr_SetString(PyExc_Exception, "no such channel");
928 return NULL;
929 }
930
931 irc_kick(who, target, channel, msg);
932
8d455e8b 933 Py_INCREF(Py_None);
749f0565 934 return Py_None;
935}
936
5661f28e 937PyDoc_STRVAR(emb_channel_mode__doc__,
938 "channel_mode(who, channel, modes)\n\n"
939 "Lets a current server's user set a specified channel's modes as specified.");
940
b778caa0 941static PyObject* emb_channel_mode(UNUSED_ARG(PyObject* self_), PyObject* args) {
942 struct userNode* who;
943 struct chanNode* channel;
944 char const* modes;
945
946 char const* who_s, *channel_s;
947
948 if (!PyArg_ParseTuple(args, "sss", &who_s, &channel_s, &modes))
949 return NULL;
950
951 if ((who = GetUserH(who_s)) == NULL) {
952 PyErr_SetString(PyExc_Exception, "unknown user");
953 return NULL;
954 }
955
956 if (who->uplink != self) {
957 PyErr_SetString(PyExc_Exception, "user not on current server");
958 return NULL;
959 }
960
961 if ((channel = GetChannel(channel_s)) == NULL) {
962 PyErr_SetString(PyExc_Exception, "unknown channel");
963 return NULL;
964 }
965
966 irc_mode(who, channel, modes);
967
968 Py_INCREF(Py_None);
969 return Py_None;
970}
971
5661f28e 972PyDoc_STRVAR(emb_user_mode__doc__,
973 "user_mode(target, modes)\n\n"
974 "Sets target's modes as specified. The modes are in normal +f-n syntax.");
975
26d745b2 976static PyObject* emb_user_mode(UNUSED_ARG(PyObject* self), PyObject* args) {
977 struct userNode* target;
978 char const* modes;
979
980 char const* target_s;
981
982 if (!PyArg_ParseTuple(args, "ss", &target_s, &modes))
983 return NULL;
984
985 if ((target = GetUserH(target_s)) == NULL) {
986 PyErr_SetString(PyExc_Exception, "unknown user");
987 return NULL;
988 }
989
990 irc_umode(target, modes);
991
992 Py_INCREF(Py_None);
993 return Py_None;
994}
995
5661f28e 996PyDoc_STRVAR(emb_fakehost__doc__,
997 "fakehost(target, host)\n\n"
998 "Sets the fakehost of a given user to the specified host.");
999
86d0ebc4 1000static PyObject* emb_fakehost(UNUSED_ARG(PyObject* self), PyObject* args) {
1001 struct userNode* target;
1002 char const* host;
1003
1004 char const* target_s;
1005
1006 if (!PyArg_ParseTuple(args, "ss", &target_s, &host))
1007 return NULL;
1008
1009 if ((target = GetUserH(target_s)) == NULL) {
1010 PyErr_SetString(PyExc_Exception, "unknown user");
1011 return NULL;
1012 }
1013
1014 irc_fakehost(target, host);
1015
1016 Py_INCREF(Py_None);
1017 return Py_None;
1018}
1019
15df606d 1020PyDoc_STRVAR(emb_svsnick__doc__,
1021 "svsnick(from, target, newnick)\n\n"
1022 "The from nick must be on the service server.");
1023
1024static PyObject*
1025emb_svsnick(UNUSED_ARG(PyObject* self_), PyObject* args) {
1026 struct userNode* from, *target;
1027 const char* newnick;
1028
1029 const char* from_s, *target_s;
1030
1031 if (!PyArg_ParseTuple(args, "sss", &from_s, &target_s, &newnick))
1032 return NULL;
1033
1034 if ((from = GetUserH(from_s)) == NULL) {
1035 PyErr_SetString(PyExc_Exception, "unknown from user");
1036 return NULL;
1037 }
1038
1039 if ((target = GetUserH(target_s)) == NULL) {
1040 PyErr_SetString(PyExc_Exception, "unknown target user");
1041 return NULL;
1042 }
1043
1044 if (from->uplink != self) {
1045 PyErr_SetString(PyExc_Exception, "from user is not on service server");
1046 return NULL;
1047 }
1048
1049 irc_svsnick(from, target, newnick);
1050
1051 Py_INCREF(Py_None);
1052 return Py_None;
1053}
1054
f20f0ab5 1055PyDoc_STRVAR(emb_svsquit__doc__,
1056 "svsquit(from, target, reason)\n\n"
1057 "The from user must be on the service server.");
1058
1059static PyObject*
1060emb_svsquit(UNUSED_ARG(PyObject* self_), PyObject* args) {
1061 struct userNode* from, *target;
1062 char const* reason;
1063
1064 char const* from_s, *target_s;
1065
1066 if (!PyArg_ParseTuple(args, "sss", &from_s, &target_s, &reason))
1067 return NULL;
1068
1069 if ((from = GetUserH(from_s)) == NULL) {
1070 PyErr_SetString(PyExc_Exception, "unknown from user");
1071 return NULL;
1072 }
1073
1074 if (from->uplink != self) {
1075 PyErr_SetString(PyExc_Exception, "from user is not on service server");
1076 return NULL;
1077 }
1078
1079 if ((target = GetUserH(target_s)) == NULL) {
1080 PyErr_SetString(PyExc_Exception, "unknown target user");
1081 return NULL;
1082 }
1083
1084 irc_svsquit(from, target, reason);
1085
1086 Py_INCREF(Py_None);
1087 return Py_None;
1088}
1089
7304cfdc 1090PyDoc_STRVAR(emb_svsjoin__doc__,
1091 "svsjoin(from, target, to)\n\n"
1092 "From user from must a user on the service server.\n"
1093 "To must be an existing channel name.");
1094
1095static PyObject*
1096emb_svsjoin(UNUSED_ARG(PyObject* self_), PyObject* args) {
1097 struct userNode* from, *target;
1098 struct chanNode* to;
1099
1100 const char* from_s, *target_s, *to_s;
1101
1102 if (!PyArg_ParseTuple(args, "sss", &from_s, &target_s, &to_s))
1103 return NULL;
1104
1105 if ((from = GetUserH(from_s)) == NULL) {
1106 PyErr_SetString(PyExc_Exception, "unknown from user");
1107 return NULL;
1108 }
1109
1110 if (from->uplink != self) {
1111 PyErr_SetString(PyExc_Exception, "from user is not on service server");
1112 return NULL;
1113 }
1114
1115 if ((target = GetUserH(target_s)) == NULL) {
1116 PyErr_SetString(PyExc_Exception, "unknown target user");
1117 return NULL;
1118 }
1119
1120 if ((to = GetChannel(to_s)) == NULL)
1121 to = AddChannel(to_s, now, NULL, NULL, NULL);
1122
1123 irc_svsjoin(from, target, to);
1124
1125 Py_INCREF(Py_None);
1126 return Py_None;
1127}
1128
1e7b511d 1129PyDoc_STRVAR(emb_adduser__doc__,
1130 "adduser(nick, ident, hostname, description, modes) -> dict with user information\n\n"
1131 "Adds a new local user with the given information.");
1132
1133static PyObject*
1134emb_adduser(UNUSED_ARG(PyObject* self_), PyObject* args) {
1135 char const* nick, *ident, *hostname, *desc, *modes;
1136 struct userNode* user;
1137 PyObject* retval;
1138
1139 if (!PyArg_ParseTuple(args, "sssss", &nick, &ident, &hostname, &desc, &modes))
1140 return NULL;
1141
1142 user = AddLocalUser(nick, ident, hostname, desc, modes);
1143
1144 retval = pyobj_from_usernode(user);
1145
1146 return retval;
1147}
1148
94a73df0 1149/* TODO: Add the rest of the service members to the dict */
1150static PyObject*
1151pyobj_from_service(struct service* serv) {
1152 PyObject* bot, *retval;
1153
1154 bot = pyobj_from_usernode(serv->bot);
1155 if (bot == NULL)
1156 goto cleanup;
1157
1158 retval = Py_BuildValue("{s:O,s:c,s:I}",
1159 "bot", bot,
1160 "trigger", serv->trigger,
1161 "privileged", serv->privileged);
1162 if (retval == NULL)
1163 goto cleanup;
1164
1165 return retval;
1166
1167cleanup:
1168 Py_XDECREF(bot);
1169 return NULL;
1170}
1171
1172PyDoc_STRVAR(emb_service_register__doc__,
1173 "service_register(nick)\n\n"
1174 "Registers nick as a service. The specified nick must be on the local server.");
1175
1176static PyObject*
1177emb_service_register(UNUSED_ARG(PyObject* self_), PyObject* args) {
1178 struct userNode* user;
1179 char const* user_s;
1180
1181 if (!PyArg_ParseTuple(args, "s", &user_s))
1182 return NULL;
1183
1184 if ((user = GetUserH(user_s)) == NULL) {
1185 PyErr_SetString(PyExc_Exception, "unknown user");
1186 return NULL;
1187 }
1188
1189 if (user->uplink != self) {
1190 PyErr_SetString(PyExc_Exception, "user is not on service server");
1191 return NULL;
1192 }
1193
1194 return pyobj_from_service(service_register(user));
1195}
1196
07021002 1197size_t logs_size = 0;
1198static struct log_type **logs_list = NULL;
1199
1200PyDoc_STRVAR(emb_log_register_type__doc__,
1201 "registers a log source to write event data to.");
1202static PyObject* emb_log_register_type(UNUSED_ARG(PyObject *self), PyObject* args) {
1203 const char* logName;
1204 const char* defaultLog;
1205 struct log_type* log;
1206 struct log_type** newlogs;
1207
1208 if (!PyArg_ParseTuple(args, "ss", &logName, &defaultLog))
1209 return NULL;
1210
1211 newlogs = realloc(logs_list, (logs_size+1)*sizeof(struct log_type*));
1212 if (newlogs == NULL) {
1213 PyErr_SetString(PyExc_Exception, "unable to allocate memory for log structures. aborting.");
1214 return NULL;
1215 }
1216 logs_list = newlogs;
1217
1218 log = log_register_type(logName, defaultLog);
1219 if (log == NULL) {
1220 PyErr_SetString(PyExc_Exception, "unable to register log");
1221 return NULL;
1222 }
1223
1224 logs_list[logs_size++] = log;
1225
1226 return Py_BuildValue("O", PyCObject_FromVoidPtr(log, NULL));
1227}
1228
af40e58b 1229PyDoc_STRVAR(emb_module_register__doc__, "registers a module");
1230PyObject* emb_module_register(UNUSED_ARG(PyObject* self), PyObject* args) {
1231 PyObject* pylog;
1232 char const *name, *helpfile;
1233 struct log_type* log;
1234 struct module* mod;
1235
1236 if (!PyArg_ParseTuple(args, "sOs", &name, &pylog, &helpfile))
1237 return NULL;
1238
1239 log = PyCObject_AsVoidPtr(pylog);
1240
1241 mod = module_register(name, log, helpfile, NULL);
1242
1243 if (mod == NULL) {
1244 PyErr_SetString(PyExc_Exception, "unable to register module");
1245 return NULL;
1246 }
1247
1248 return Py_BuildValue("O", PyCObject_FromVoidPtr(mod, NULL));
1249}
1250
a2c8c575 1251static PyMethodDef EmbMethods[] = {
07559983 1252 /* Communication methods */
5661f28e 1253 {"dump", emb_dump, METH_VARARGS, emb_dump__doc__},
1254 {"send_target_privmsg", emb_send_target_privmsg, METH_VARARGS, emb_send_target_privmsg__doc__},
1255 {"send_target_notice", emb_send_target_notice, METH_VARARGS, emb_send_target_notice__doc__},
1256 {"log_module", emb_log_module, METH_VARARGS, emb_log_module__doc__},
039a6658 1257//TODO: {"exec_cmd", emb_exec_cmd, METH_VARARGS, "execute x3 command provided"},
1258// This should use environment from "python command" call to pass in, if available
5661f28e 1259 {"kill", emb_kill, METH_VARARGS, emb_kill__doc__},
1260 {"fakehost", emb_fakehost, METH_VARARGS, emb_fakehost__doc__},
15df606d 1261 {"svsnick", emb_svsnick, METH_VARARGS, emb_svsnick__doc__},
f20f0ab5 1262 {"svsquit", emb_svsquit, METH_VARARGS, emb_svsquit__doc__},
7304cfdc 1263 {"svsjoin", emb_svsjoin, METH_VARARGS, emb_svsjoin__doc__},
1e7b511d 1264 {"adduser", emb_adduser, METH_VARARGS, emb_adduser__doc__},
94a73df0 1265 {"service_register", emb_service_register, METH_VARARGS, emb_service_register__doc__},
7304cfdc 1266//TODO: svsmode, svsident, nick, quit, join, part, ident, vhost
039a6658 1267//TODO: {"shun"
1268//TODO: {"unshun"
1269//TODO: {"gline", emb_gline, METH_VARARGS, "gline a mask"},
1270//TODO: {"ungline", emb_ungline, METH_VARARGS, "remove a gline"},
5661f28e 1271 {"kick", emb_kick, METH_VARARGS, emb_kick__doc__},
1272 {"channel_mode", emb_channel_mode, METH_VARARGS, emb_channel_mode__doc__},
1273 {"user_mode", emb_user_mode, METH_VARARGS, emb_user_mode__doc__},
039a6658 1274//
5661f28e 1275 {"get_config", emb_get_config, METH_VARARGS, emb_get_config__doc__},
039a6658 1276//TODO: {"config_set", emb_config_set, METH_VARARGS, "change a config setting 'on-the-fly'."},
1277//
5661f28e 1278 {"timeq_add", emb_timeq_add, METH_VARARGS, emb_timeq_add__doc__},
1279 {"timeq_del", emb_timeq_del, METH_VARARGS, emb_timeq_del__doc__},
3f218269 1280
07021002 1281 /* module registration methods */
1282 {"log_register_type", emb_log_register_type, METH_VARARGS,
1283 emb_log_register_type__doc__},
af40e58b 1284 {"module_register", emb_module_register, METH_VARARGS,
1285 emb_module_register__doc__},
07021002 1286
07559983 1287 /* Information gathering methods */
5661f28e 1288 {"get_user", emb_get_user, METH_VARARGS, emb_get_user__doc__},
1289 {"get_users", emb_get_users, METH_VARARGS, emb_get_users__doc__},
1290 {"get_channel", emb_get_channel, METH_VARARGS, emb_get_channel__doc__},
1291 {"get_channels", emb_get_channels, METH_VARARGS, emb_get_channels__doc__},
1292 {"get_server", emb_get_server, METH_VARARGS, emb_get_server__doc__},
1293 {"get_servers", emb_get_servers, METH_VARARGS, emb_get_servers__doc__},
1294 {"get_account", emb_get_account, METH_VARARGS, emb_get_account__doc__},
1295 {"get_accounts", emb_get_accounts, METH_VARARGS, emb_get_accounts__doc__},
1296 {"get_info", emb_get_info, METH_VARARGS, emb_get_info__doc__},
07559983 1297 /* null terminator */
a2c8c575 1298 {NULL, NULL, 0, NULL}
1299};
1300
1301
f0e11521 1302/*
1303These functions set up the embedded environment for us to call out to
1304modpython.py class methods.
4c216694 1305 */
cbfd323c 1306
07559983 1307void python_log_module() {
1308 /* Attempt to convert python errors to x3 log system */
1309 PyObject *exc, *tb, *value, *tmp;
1310 char *str_exc = "NONE";
1311 char *str_value = "NONE";
1312 char *str_tb = "NONE";
1313
1314 PyErr_Fetch(&exc, &value, &tb);
1315
1316 if(exc) {
1317 if((tmp = PyObject_Str(exc)))
1318 str_exc = PyString_AsString(tmp);
1319 }
1320 if(value) {
1321 if((tmp = PyObject_Str(value)))
1322 str_value = PyString_AsString(tmp);
1323 }
1324 if(tb) {
1325 if((tmp = PyObject_Str(tb)))
1326 str_tb = PyString_AsString(tmp);
1327 }
1328
1329 /* Now restore it so we can print it (just in case)
1330 * (should we do this only when running in debug mode?) */
1331 PyErr_Restore(exc, value, tb);
1332 PyErr_Print(); /* which of course, clears it again.. */
1333
1334 log_module(PY_LOG, LOG_WARNING, "PYTHON error: %s, value: %s", str_exc, str_value);
1335
1336 /* TODO: get the traceback using the traceback module via C api so we can add it to the X3 logs. See
1337 * http://mail.python.org/pipermail/python-list/2003-March/192226.html */
1338 // (this doesnt work, str_tb is just an object hashid) log_module(PY_LOG, LOG_INFO, "PYTHON error, traceback: %s", str_tb);
1339}
1340
1341
e0f76584 1342PyObject *python_build_handler_args(size_t argc, char *args[], PyObject *pIrcObj) {
4c216694 1343 /* Sets up a python tuple with passed in arguments, prefixed by the Irc instance
f0e11521 1344 which handlers use to interact with C.
4c216694 1345 argc = number of args
1346 args = array of args
1347 pIrcObj = instance of the irc class
1348 */
e0f76584 1349 size_t i = 0, n;
1350 PyObject *pArgs = NULL;
cbfd323c 1351
e0f76584 1352 pArgs = PyTuple_New(argc + 1);
1353 Py_INCREF(pIrcObj);
1354 PyTuple_SetItem(pArgs, i++, pIrcObj);
cbfd323c 1355
e0f76584 1356 if(args && argc) {
cbfd323c 1357 PyObject *pValue;
e0f76584 1358 for(n = 0; n < argc; ++n) {
1359 pValue = PyString_FromString(args[n]);
cbfd323c 1360 if(!pValue) {
e0f76584 1361 Py_DECREF(pArgs);
1362 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[n]);
1363 return NULL;
cbfd323c 1364 }
e0f76584 1365 PyTuple_SetItem(pArgs, n+i, pValue);
cbfd323c 1366 }
cbfd323c 1367 }
e0f76584 1368 return pArgs;
cbfd323c 1369}
1370
1371PyObject *python_build_args(size_t argc, char *args[]) {
4c216694 1372 /* Builds the passed in arguments into a python argument tuple.
1373 argc = number of args
1374 args = array of args
1375 */
cbfd323c 1376 size_t i;
1377 PyObject *pArgs = NULL;
1378
1379 if(args && argc) {
1380 pArgs = PyTuple_New(argc);
1381 PyObject *pValue;
1382 for(i = 0; i< argc; ++i) {
1383 pValue = PyString_FromString(args[i]);
1384 if(!pValue) {
1385 Py_DECREF(pArgs);
1386 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[i]);
1387 return NULL;
1388 }
1389 PyTuple_SetItem(pArgs, i, pValue);
1390 }
1391 }
1392 return pArgs;
d4e0f0c4 1393}
caf97651 1394
cbfd323c 1395
e0f76584 1396PyObject *new_irc_object(char *command_service, char *command_caller, char *command_target) {
4c216694 1397 /* Creates a new instance of the irc class (from modpython.py) which is initalized
1398 with current environment details like which service were talking to.
1399 command_service = which service we are talking to, or empty string if none
1400 command_caller = nick of user generating message, or empty string if none
1401 command_target = If were reacting to something on a channel, this will
1402 be set to the name of the channel. Otherwise empty
1403 */
e0f76584 1404 PyObject *pIrcArgs = NULL;
1405 PyObject *pIrcClass;
1406 PyObject *pIrcObj;
1407
4c216694 1408 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate irc class; %s %s %s", command_service, command_caller, command_target);
e0f76584 1409 pIrcClass = PyObject_GetAttrString(base_module, "irc");
1410 /* pIrcClass is a new reference */
1411 if(pIrcClass && PyCallable_Check(pIrcClass)) {
1412 //size_t i;
1413 char *ircargs[] = {command_service, command_caller, command_target};
1414 //PyObject *pValue;
1415
1416 pIrcArgs = python_build_args(3, ircargs);
e0f76584 1417 pIrcObj = PyObject_CallObject(pIrcClass, pIrcArgs);
1418 if(!pIrcObj) {
1419 log_module(PY_LOG, LOG_ERROR, "IRC Class failed to load");
07559983 1420 python_log_module();
1421 //PyErr_Print();
e0f76584 1422 }
1423 if(pIrcArgs != NULL) {
1424 Py_DECREF(pIrcArgs);
1425 }
4c216694 1426 Py_DECREF(pIrcClass);
e0f76584 1427 return pIrcObj;
1428 }
1429 else {
4c216694 1430 /* need to free pIrcClass here if it WAS found but was NOT callable? */
e0f76584 1431 log_module(PY_LOG, LOG_ERROR, "Unable to find irc class");
1432 return NULL;
1433 }
1434}
1435
cbfd323c 1436int python_call_handler(char *handler, char *args[], size_t argc, char *command_service, char *command_caller, char *command_target) {
4c216694 1437 /* This is how we talk to modpython.c. First a new instance of the irc class is created using these
1438 arguments to setup the current environment. Then the named method of the handler object is called
1439 with the givin arguments.
cbfd323c 1440 */
1441 PyObject *pIrcObj;
1442 PyObject *pArgs;
1443 PyObject *pMethod;
1444 PyObject *pValue;
1445
e0f76584 1446 log_module(PY_LOG, LOG_INFO, "attempting to call handler %s.", handler);
07559983 1447 if(base_module != NULL && handler_object != NULL) {
cbfd323c 1448 pIrcObj = new_irc_object(command_service, command_caller, command_target);
e0f76584 1449 if(!pIrcObj) {
1450 log_module(PY_LOG, LOG_INFO, "Can't get irc object. Bailing.");
1451 return 0;
1452 }
cbfd323c 1453
e0f76584 1454 pArgs = python_build_handler_args(argc, args, pIrcObj);
cbfd323c 1455 pMethod = PyObject_GetAttrString(handler_object, handler);
1456 if(pMethod && PyCallable_Check(pMethod)) {
07559983 1457 /* Call the method, with the arguments */
cbfd323c 1458 pValue = PyObject_CallObject(pMethod, pArgs);
1459 if(pArgs) {
1460 Py_DECREF(pArgs);
1461 }
1462 if(pValue != NULL) {
1463 int ret;
1464 ret = PyInt_AsLong(pValue);
1465 if(ret == -1 && PyErr_Occurred()) {
07559983 1466 //PyErr_Print();
cbfd323c 1467 log_module(PY_LOG, LOG_INFO, "error converting return value of handler %s to type long. ", handler);
07559983 1468 python_log_module();
cbfd323c 1469 ret = 0;
1470 }
1471 log_module(PY_LOG, LOG_INFO, "handler %s was run successfully, returned %d.", handler, ret);
cbfd323c 1472 Py_DECREF(pValue);
4c216694 1473 Py_DECREF(pIrcObj);
1474 Py_DECREF(pMethod);
cbfd323c 1475 return ret;
1476 }
1477 else {
cbfd323c 1478 /* TODO: instead of print errors, get them as strings
1479 * and deal with them with normal x3 log system. */
cbfd323c 1480 log_module(PY_LOG, LOG_WARNING, "call to handler %s failed", handler);
07559983 1481 //PyErr_Print();
1482 python_log_module();
4c216694 1483 Py_DECREF(pIrcObj);
1484 Py_DECREF(pMethod);
cbfd323c 1485 return 0;
1486 }
1487 }
1488 else { /* couldn't find handler methed */
4c216694 1489 Py_DECREF(pArgs);
1490 /* Free pMethod if it was found but not callable? */
cbfd323c 1491 log_module(PY_LOG, LOG_ERROR, "Cannot find handler %s.", handler);
1492 return 0;
1493
1494 }
1495 }
1496 else { /* No base module.. no python? */
e0f76584 1497 log_module(PY_LOG, LOG_INFO, "Cannot handle %s, Python is not initialized.", handler);
cbfd323c 1498 return 0;
1499 }
1500}
1501
1502PyObject *python_new_handler_object() {
4c216694 1503 /* Create a new instance of the handler class.
1504 This is called during python initilization (or reload)
1505 and the result is saved and re-used.
1506 */
cbfd323c 1507 PyObject *pHandlerClass, *pHandlerObj;
1508
1509 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate python class handler");
1510 pHandlerClass = PyObject_GetAttrString(base_module, "handler");
1511 /* Class is a new reference */
1512 if(pHandlerClass && PyCallable_Check(pHandlerClass)) {
e0f76584 1513 /*PyObject *pValue; */
cbfd323c 1514
1515 pHandlerObj = PyObject_CallObject(pHandlerClass, NULL);
07559983 1516 if(pHandlerObj != NULL) {
1517 log_module(PY_LOG, LOG_INFO, "Created new python handler object.");
1518 return pHandlerObj;
1519 }
1520 else {
1521 log_module(PY_LOG, LOG_ERROR, "Unable to instanciate handler object");
1522 //PyErr_Print();
1523 python_log_module();
1524 return NULL;
1525 }
cbfd323c 1526 }
1527 else {
1528 log_module(PY_LOG, LOG_ERROR, "Unable to find handler class");
07559983 1529 //PyErr_Print();
1530 python_log_module();
1531 if(pHandlerClass) {
1532 Py_DECREF(pHandlerClass);
1533 }
cbfd323c 1534 return NULL;
1535 }
1536}
1537
4c216694 1538/* ------------------------------------------------------------------------------- *
1539 Some gateway functions to convert x3 callbacks into modpython.py callbacks.
1540 Mostly we just build relevant args and call the proper handler method
1541
1542 debate: do we just register these and check them in python
1543 for every one (slow?) or actually work out if a plugin needs
1544 it first? We will start by doing it every time.
cbfd323c 1545 */
0b350353 1546static int
fb38e2bc 1547python_handle_join(struct modeNode *mNode, UNUSED_ARG(void *extra))
0b350353 1548{
4c216694 1549 /* callback for handle_join events.
1550 */
0b350353 1551 struct userNode *user = mNode->user;
1552 struct chanNode *channel = mNode->channel;
1553
a2c8c575 1554
caf97651 1555 log_module(PY_LOG, LOG_INFO, "python module handle_join");
a2c8c575 1556 if(!channel||!user) {
4c216694 1557 log_module(PY_LOG, LOG_WARNING, "Python code got join without channel or user!");
a2c8c575 1558 return 0;
1559 }
1560 else {
1561 char *args[] = {channel->name, user->nick};
e0f76584 1562 return python_call_handler("join", args, 2, "", "", "");
a2c8c575 1563 }
0b350353 1564}
1565
0ab7b4bc 1566static int
9d59f196 1567python_handle_server_link(struct server *server, UNUSED_ARG(void *extra))
0ab7b4bc 1568{
1ad8c8df 1569 PyObject* srv = NULL;
1570 PyObject* funcname = NULL;
1571 PyObject* retval = NULL;
1572 char const* err = NULL;
1573 int i = 0;
1574
1575 if (handler_object == NULL) {
1576 err = "No Python handler is allocated. Ignoring python_handle_server_link.";
1577 goto cleanup;
0ab7b4bc 1578 }
1ad8c8df 1579
1580 if (server == NULL) {
1581 err = "Python code got server link without server!";
1582 goto cleanup;
1583 }
1584
1585 if ((srv = pyobj_from_server(server)) == NULL) {
1586 err = "Python code unable to get PyObject with server!";
1587 goto cleanup;
1588 }
1589
1590 funcname = PyString_FromString("server_link");
1591 if (funcname == NULL) {
1592 err = "Unable to allocate memory";
1593 goto cleanup;
0ab7b4bc 1594 }
1ad8c8df 1595
1596 retval = PyObject_CallMethodObjArgs(handler_object, funcname, srv, NULL);
1597 if (retval == NULL) {
1598 err = "Error calling server_link handler";
1599 goto cleanup;
1600 }
1601
1602cleanup:
1603 Py_XDECREF(srv);
1604 Py_XDECREF(funcname);
1605
1606 if (retval != NULL && PyInt_Check(retval))
1607 i = (int)PyInt_AsLong(retval);
1608
1609 Py_XDECREF(retval);
1610
1611 if (err != NULL)
1612 log_module(PY_LOG, LOG_WARNING, "%s", err);
1613
1614 return i;
0ab7b4bc 1615}
1616
1617static int
f0fb2e2d 1618python_handle_new_user(struct userNode *user, UNUSED_ARG(void *extra))
0ab7b4bc 1619{
c9b009fe 1620 PyObject* name = NULL;
1621 PyObject* usr = NULL;
1622 PyObject* retval = NULL;
1623 int i = 0;
1624 const char* err = NULL;
1625
9c7f11c2 1626 if (handler_object == NULL) {
1627 err = "No Python handler is allocated. Ignoring python_handle_server_link.";
1628 goto cleanup;
1629 }
1630
0ab7b4bc 1631 if(!user) {
1632 log_module(PY_LOG, LOG_WARNING, "Python code got new_user without the user");
1633 return 0;
1634 }
c9b009fe 1635
1636 if ((usr = pyobj_from_usernode(user)) == NULL) {
1637 err = "unable to allocate python user information";
1638 goto cleanup;
1639 }
1640
1641 name = PyString_FromString("new_user");
1642 if (name == NULL) {
1643 err = "unable to allocate memory for handler function name";
1644 goto cleanup;
1645 }
1646
1647 if ((retval = PyObject_CallMethodObjArgs(handler_object, name, usr, NULL)) == NULL) {
1648 err = "error calling new_user handler";
1649 goto cleanup;
0ab7b4bc 1650 }
c9b009fe 1651
1652cleanup:
1653 Py_XDECREF(usr);
1654 Py_XDECREF(name);
1655
1656 if (retval != NULL && PyInt_Check(retval))
1657 i = (int)PyInt_AsLong(retval);
1658
1659 Py_XDECREF(retval);
1660
1661 if (err != NULL)
1662 log_module(PY_LOG, LOG_WARNING, "%s", err);
1663
1664 return i;
0ab7b4bc 1665}
1666
1667static void
63189c10 1668python_handle_nick_change(struct userNode *user, const char *old_nick, UNUSED_ARG(void *extra))
0ab7b4bc 1669{
0a585d7e 1670 PyObject* usr = NULL;
1671 PyObject* name = NULL;
1672 PyObject* oldnick = NULL;
1673 PyObject* retval = NULL;
1674 char const* err = NULL;
1675
9c7f11c2 1676 if (handler_object == NULL) {
1677 err = "No Python handler is allocated. Ignoring python_handle_server_link.";
1678 goto cleanup;
1679 }
1680
0a585d7e 1681 if (user == NULL) {
1682 err = "Python code got nick_change without the user!";
1683 goto cleanup;
0ab7b4bc 1684 }
0a585d7e 1685
1686 if ((usr = pyobj_from_usernode(user)) == NULL) {
1687 err = "unable to allocate Python usernode";
1688 goto cleanup;
0ab7b4bc 1689 }
0a585d7e 1690
1691 name = PyString_FromString("nick_change");
1692 if (name == NULL) {
1693 err = "unable to allocate memory for handler function name";
1694 goto cleanup;
1695 }
1696
1697 oldnick = PyString_FromString(old_nick);
1698
1699 retval = PyObject_CallMethodObjArgs(handler_object, name, usr, oldnick, NULL);
1700 if (retval == NULL) {
1701 err = "error calling nick_change handler";
1702 goto cleanup;
1703 }
1704
1705cleanup:
1706 Py_XDECREF(usr);
1707 Py_XDECREF(name);
1708 Py_XDECREF(oldnick);
1709 Py_XDECREF(retval);
1710
1711 if (err != NULL)
1712 log_module(PY_LOG, LOG_WARNING, "%s", err);
0ab7b4bc 1713}
1714
a6bcc929 1715void python_handle_del_user(struct userNode *user, struct userNode *killer, const char *why, UNUSED_ARG(void *extra)) {
9c7f11c2 1716 PyObject *usr = NULL, *killr = NULL, *name = NULL;
1717 PyObject *reason = NULL, *retval = NULL;
1718 char const* err = NULL;
1719
1720 if (handler_object == NULL) {
1721 err = "No Python handler is allocated. Ignoring python_handle_server_link.";
1722 goto cleanup;
1723 }
1724
1725 if (user == NULL) {
1726 Py_INCREF(Py_None);
1727 usr = Py_None;
1728 } else {
1729 usr = pyobj_from_usernode(user);
1730 if (usr == NULL) {
1731 err = "unable to allocate usernode for user";
1732 goto cleanup;
1733 }
1734 }
1735
1736 if (killer == NULL) {
1737 Py_INCREF(Py_None);
1738 killr = Py_None;
1739 } else {
1740 killr = pyobj_from_usernode(killer);
1741 if (killr == NULL) {
1742 err = "unable to allocate usernode for killer";
1743 goto cleanup;
1744 }
1745 }
1746
1747 if (why == NULL) {
1748 Py_INCREF(Py_None);
1749 reason = Py_None;
1750 } else {
1751 reason = PyString_FromString(why);
1752 if (reason == NULL) {
1753 err = "unable to allocate memory for reason";
1754 goto cleanup;
1755 }
1756 }
1757
1758 name = PyString_FromString("del_user");
1759 if (name == NULL) {
1760 err = "unable to allocate memory for handler function name";
1761 goto cleanup;
1762 }
1763
1764 retval = PyObject_CallMethodObjArgs(handler_object, name, usr, killr, reason, NULL);
1765 if (retval == NULL) {
1766 err = "error calling del_user handler";
1767 goto cleanup;
1768 }
1769
1770cleanup:
1771 Py_XDECREF(usr);
1772 Py_XDECREF(killr);
1773 Py_XDECREF(name);
1774 Py_XDECREF(reason);
1775 Py_XDECREF(retval);
1776
1777 if (err != NULL)
1778 log_module(PY_LOG, LOG_WARNING, "%s", err);
1779}
1780
5a2c7cf6 1781int python_handle_topic(struct userNode *who, struct chanNode *chan, const char *old_topic, UNUSED_ARG(void *extra)) {
43b43d56 1782 PyObject* pwho = NULL, *pchan = NULL, *oldtopic = NULL;
1783 PyObject* name = NULL, *retval = NULL;
1784 const char* err = NULL;
1785 int i = 0;
1786
1787 if (who == NULL) {
1788 Py_INCREF(Py_None);
1789 pwho = Py_None;
1790 } else {
1791 if ((pwho = pyobj_from_usernode(who)) == NULL) {
1792 err = "unable to allocate usernode";
1793 goto cleanup;
1794 }
1795 }
1796
1797 if ((pchan = pyobj_from_channode(chan)) == NULL) {
1798 err = "unable to allocate channode";
1799 goto cleanup;
1800 }
1801
1802 if (old_topic == NULL) {
1803 Py_INCREF(Py_None);
1804 oldtopic = Py_None;
1805 } else {
1806 oldtopic = PyString_FromString(old_topic);
1807 if (oldtopic == NULL) {
1808 err = "unable to allocate memory for old topic string";
1809 goto cleanup;
1810 }
1811 }
1812
1813 name = PyString_FromString("topic");
1814 if (name == NULL) {
1815 err = "unable to allocate memory for topic handler function name";
1816 goto cleanup;
1817 }
1818
1819 retval = PyObject_CallMethodObjArgs(handler_object, name, pwho, pchan, oldtopic, NULL);
1820 if (retval == NULL) {
1821 err = "error calling topic handler";
1822 goto cleanup;
1823 }
1824
1825cleanup:
1826 Py_XDECREF(pwho);
1827 Py_XDECREF(pchan);
1828 Py_XDECREF(oldtopic);
1829 Py_XDECREF(name);
1830
1831 if (retval != NULL && PyInt_Check(retval))
1832 i = (int)PyInt_AsLong(retval);
1833
1834 Py_XDECREF(retval);
1835
1836 if (err != NULL)
1837 log_module(PY_LOG, LOG_WARNING, "%s", err);
1838
1839 return i;
1840}
4c216694 1841/* ----------------------------------------------------------------------------- */
1842
cbfd323c 1843
caf97651 1844int python_load() {
4c216694 1845 /* Init the python engine and do init work on modpython.py
1846 This is called during x3 startup, and on a python reload
1847 */
caf97651 1848 PyObject *pName;
ef5e0305 1849 char* buffer;
1850 char* env = getenv("PYTHONPATH");
1851
1852 if (env)
1853 env = strdup(env);
1854
1855 if (!env)
1856 setenv("PYTHONPATH", modpython_conf.scripts_dir, 1);
1857 else if (!strstr(env, modpython_conf.scripts_dir)) {
1858 buffer = (char*)malloc(strlen(env) + strlen(modpython_conf.scripts_dir) + 2);
1859 sprintf(buffer, "%s:%s", modpython_conf.scripts_dir, env);
1860 setenv("PYTHONPATH", buffer, 1);
1861 free(buffer);
1862 free(env);
1863 }
caf97651 1864
caf97651 1865 Py_Initialize();
50d61a79 1866 Py_InitModule("_svc", EmbMethods);
ed8d873c 1867 pName = PyString_FromString(modpython_conf.main_module);
caf97651 1868 base_module = PyImport_Import(pName);
1869 Py_DECREF(pName);
bc2f52df 1870
1871 Py_XDECREF(handler_object);
1872 handler_object = NULL;
1873
caf97651 1874 if(base_module != NULL) {
cbfd323c 1875 handler_object = python_new_handler_object();
1876 if(handler_object) {
e0f76584 1877 python_call_handler("init", NULL, 0, "", "", "");
cbfd323c 1878 return 1;
1879 }
1880 else {
1881 /* error handler class not found */
1882 log_module(PY_LOG, LOG_WARNING, "Failed to create handler object");
1883 return 0;
1884 }
caf97651 1885 }
1886 else {
07559983 1887 //PyErr_Print();
1888 python_log_module();
d4e0f0c4 1889 log_module(PY_LOG, LOG_WARNING, "Failed to load modpython.py");
a2c8c575 1890 return 0;
caf97651 1891 }
cbfd323c 1892 return 0;
caf97651 1893}
1894
caf97651 1895int
1896python_finalize(void) {
4c216694 1897 /* Called after X3 is fully up and running.
1898 Code can be put here that needs to run to init things, but
1899 which is sensitive to everything else in x3 being up and ready
1900 to go.
1901 */
caf97651 1902
1903 PyRun_SimpleString("print 'Hello, World of Python!'");
1904 log_module(PY_LOG, LOG_INFO, "python module finalize");
1905
1906 return 1;
1907}
1908
caf97651 1909static void
30874d66 1910python_cleanup(UNUSED_ARG(void *extra)) {
4c216694 1911 /* Called on shutdown of the python module (or before reloading)
1912 */
413fd8ea 1913
caf97651 1914 log_module(PY_LOG, LOG_INFO, "python module cleanup");
9c7f11c2 1915
1916 Py_XDECREF(handler_object);
1917 handler_object = NULL;
1918
413fd8ea 1919 if (PyErr_Occurred())
1920 PyErr_Clear();
f0e11521 1921 Py_Finalize(); /* Shut down python enterpreter */
9c7f11c2 1922
1923 log_module(PY_LOG, LOG_INFO, "python module cleanup done");
caf97651 1924}
1925
4c216694 1926/* ---------------------------------------------------------------------------------- *
1927 Python module command handlers.
1928*/
caf97651 1929static MODCMD_FUNC(cmd_reload) {
4c216694 1930 /* reload the python system completely
1931 */
caf97651 1932 log_module(PY_LOG, LOG_INFO, "Shutting python down");
30874d66 1933 python_cleanup(NULL);
caf97651 1934 log_module(PY_LOG, LOG_INFO, "Loading python stuff");
1935 if(python_load()) {
a2c8c575 1936 reply("PYMSG_RELOAD_SUCCESS");
caf97651 1937 }
1938 else {
a2c8c575 1939 reply("PYMSG_RELOAD_FAILED");
caf97651 1940 }
1941 return 1;
1942}
1943
8d670803 1944static MODCMD_FUNC(cmd_run) {
413fd8ea 1945 /* this method allows running arbitrary python commands.
1946 * use with care.
1947 */
1948 char* msg;
46f628b1 1949 PyObject* py_main_module;
1950 PyObject* py_globals;
1951 PyObject* py_locals;
1952 PyObject* py_retval;
1953 PyObject* extype, *exvalue, *extraceback;
1954 PyObject* exvaluestr = NULL;
1955 char* exmsg = NULL, *exmsgptr;
1956
1957 py_main_module = PyImport_AddModule("__main__");
1958 py_globals = py_locals = PyModule_GetDict(py_main_module);
413fd8ea 1959
a2c8c575 1960 msg = unsplit_string(argv + 1, argc - 1, NULL);
413fd8ea 1961
46f628b1 1962 py_retval = PyRun_String(msg, Py_file_input, py_globals, py_locals);
1963 if (py_retval == NULL) {
1964 PyErr_Fetch(&extype, &exvalue, &extraceback);
1965 if (exvalue != NULL) {
1966 exvaluestr = PyObject_Str(exvalue);
1967 exmsg = strdup(PyString_AS_STRING(exvaluestr));
1968 exmsgptr = exmsg;
1969 while (exmsgptr && *exmsgptr) {
1970 if (*exmsgptr == '\n' || *exmsgptr == '\r' || *exmsgptr == '\t')
1971 *exmsgptr = ' ';
1972 exmsgptr++;
1973 }
1974 }
1975 if (extype != NULL && exvalue != NULL && PyType_Check(extype)) {
1976 reply("PYMSG_RUN_EXCEPTION", ((PyTypeObject*)extype)->tp_name, exmsg);
413fd8ea 1977 } else
1978 reply("PYMSG_RUN_UNKNOWN_EXCEPTION");
46f628b1 1979
1980 if (extype != NULL)
1981 Py_DECREF(extype);
1982 if (exvalue != NULL)
1983 Py_DECREF(exvalue);
1984 if (extraceback != NULL)
1985 Py_DECREF(extraceback);
1986 if (exvaluestr != NULL)
1987 Py_DECREF(exvaluestr);
1988 if (exmsg)
1989 free(exmsg);
1990 } else {
1991 Py_DECREF(py_retval);
413fd8ea 1992 }
1993
a2c8c575 1994 return 1;
1995}
1996
07559983 1997#define numstrargs(X) sizeof(X) / sizeof(*X)
1998static MODCMD_FUNC(cmd_command) {
1999 char *plugin = argv[1];
2000 char *command = argv[2];
2001 char *msg; /* args */
2002 if(argc > 3) {
2003 msg = unsplit_string(argv + 3, argc - 3, NULL);
2004 }
2005 else {
2006 msg = "";
2007 }
2008 char *args[] = {plugin, command, msg};
2009 python_call_handler("cmd_command", args, numstrargs(args), cmd->parent->bot->nick, user?user->nick:"", channel?channel->name:"");
2010 return 1;
2011}
2012
ef5e0305 2013static void modpython_conf_read(void) {
2014 dict_t conf_node;
2015 char const* str;
2016
2017 if (!(conf_node = conf_get_data(MODPYTHON_CONF_NAME, RECDB_OBJECT))) {
2018 log_module(PY_LOG, LOG_ERROR, "config node '%s' is missing or has wrong type", MODPYTHON_CONF_NAME);
2019 return;
2020 }
2021
2022 str = database_get_data(conf_node, "scripts_dir", RECDB_QSTRING);
2023 modpython_conf.scripts_dir = str ? str : "./";
ed8d873c 2024
2025 str = database_get_data(conf_node, "main_module", RECDB_QSTRING);
2026 modpython_conf.main_module = str ? str : "modpython";
ef5e0305 2027}
2028
0b350353 2029int python_init(void) {
4c216694 2030 /* X3 calls this function on init of the module during startup. We use it to
2031 do all our setup tasks and bindings
2032 */
0b350353 2033
0b350353 2034 PY_LOG = log_register_type("Python", "file:python.log");
caf97651 2035 python_module = module_register("python", PY_LOG, "mod-python.help", NULL);
ef5e0305 2036 conf_register_reload(modpython_conf_read);
2037
caf97651 2038 log_module(PY_LOG, LOG_INFO, "python module init");
2039 message_register_table(msgtab);
2040
0b350353 2041/*
2042 reg_auth_func(python_check_messages);
2043 reg_handle_rename_func(python_rename_account);
2044 reg_unreg_func(python_unreg_account);
2045 conf_register_reload(python_conf_read);
0b350353 2046 saxdb_register("python", python_saxdb_read, python_saxdb_write);
0b350353 2047 modcmd_register(python_module, "send", cmd_send, 3, MODCMD_REQUIRE_AUTHED, NULL);
2048*/
caf97651 2049 modcmd_register(python_module, "reload", cmd_reload, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
8d670803 2050 modcmd_register(python_module, "run", cmd_run, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
07559983 2051 modcmd_register(python_module, "command", cmd_command, 3, MODCMD_REQUIRE_STAFF, NULL);
039a6658 2052
f0e11521 2053// Please help us by implementing any of the callbacks listed as TODO below. They already exist
2054// in x3, they just need handle_ bridges implemented. (see python_handle_join for an example)
9d59f196 2055 reg_server_link_func(python_handle_server_link, NULL);
f0fb2e2d 2056 reg_new_user_func(python_handle_new_user, NULL);
63189c10 2057 reg_nick_change_func(python_handle_nick_change, NULL);
a6bcc929 2058 reg_del_user_func(python_handle_del_user, NULL);
039a6658 2059//TODO: reg_account_func(python_handle_account); /* stamping of account name to the ircd */
2060//TODO: reg_handle_rename_func(python_handle_handle_rename); /* handle used to ALSO mean account name */
2061//TODO: reg_failpw_func(python_handle_failpw);
2062//TODO: reg_allowauth_func(python_handle_allowauth);
2063//TODO: reg_handle_merge_func(python_handle_merge);
2064//
2065//TODO: reg_oper_func(python_handle_oper);
2066//TODO: reg_new_channel_func(python_handle_new_channel);
fb38e2bc 2067 reg_join_func(python_handle_join, NULL);
039a6658 2068//TODO: reg_del_channel_func(python_handle_del_channel);
2069//TODO: reg_part_func(python_handle_part);
2070//TODO: reg_kick_func(python_handle_kick);
5a2c7cf6 2071 reg_topic_func(python_handle_topic, NULL);
039a6658 2072//TODO: reg_channel_mode_func(python_handle_channel_mode);
2073
2074//TODO: reg_privmsg_func(python_handle_privmsg);
2075//TODO: reg_notice_func
2076//TODO: reg_svccmd_unbind_func(python_handle_svccmd_unbind);
2077//TODO: reg_chanmsg_func(python_handle_chanmsg);
2078//TODO: reg_allchanmsg_func
2079//TODO: reg_user_mode_func
2080
30874d66 2081 reg_exit_func(python_cleanup, NULL);
0b350353 2082
caf97651 2083 python_load();
0b350353 2084 return 1;
2085}
2086
2087#endif /* WITH_PYTHON */