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