]> jfr.im git - irc/evilnet/x3.git/blame - src/mod-python.c
mod-python: remember to increase reference count of Py_None
[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
b39754f7 135/* get a tuple with all users in it */
cc0b2b7f 136static PyObject*
b39754f7 137emb_get_users(UNUSED_ARG(PyObject *self), PyObject *args) {
cc0b2b7f 138 if (!PyArg_ParseTuple(args, ""))
139 return NULL;
140
b39754f7 141 return pyobj_from_dict_t(clients);
142}
cc0b2b7f 143
b39754f7 144/* get a tuple with all channels in it */
145static PyObject*
146emb_get_channels(UNUSED_ARG(PyObject* self), PyObject* args) {
147 if (!PyArg_ParseTuple(args, ""))
d12756d7 148 return NULL;
cc0b2b7f 149
b39754f7 150 return pyobj_from_dict_t(channels);
cc0b2b7f 151}
152
dcc1df5e 153static PyObject*
154emb_get_servers(UNUSED_ARG(PyObject* self), PyObject* args) {
dcc1df5e 155 if (!PyArg_ParseTuple(args, ""))
156 return NULL;
157
b39754f7 158 return pyobj_from_dict_t(servers);
39d37f27 159}
160
161static PyObject*
162emb_get_accounts(UNUSED_ARG(PyObject* self), PyObject* args) {
39d37f27 163 if (!PyArg_ParseTuple(args, ""))
164 return NULL;
165
b39754f7 166 return pyobj_from_dict_t(nickserv_handle_dict);
dcc1df5e 167}
168
a2c8c575 169static PyObject*
039a6658 170emb_dump(UNUSED_ARG(PyObject *self), PyObject *args)
a2c8c575 171{
4c216694 172 /* Dump a raw string into the socket
50d61a79 173 usage: _svc.dump(<P10 string>)
4c216694 174 */
a2c8c575 175 char *buf;
176 int ret = 0;
177 char linedup[MAXLEN];
178
039a6658 179
a2c8c575 180 if(!PyArg_ParseTuple(args, "s:dump", &buf ))
181 return NULL;
a957511b 182
a2c8c575 183 safestrncpy(linedup, buf, sizeof(linedup));
a957511b 184
a2c8c575 185 if(parse_line(linedup, 1)) {
186 irc_raw(buf);
187 ret = 1;
a957511b 188 } else {
189 PyErr_SetString(PyExc_Exception, "invalid protocol message");
190 return NULL;
a2c8c575 191 }
a957511b 192
a2c8c575 193 return Py_BuildValue("i", ret);
194}
195
196static PyObject*
039a6658 197emb_send_target_privmsg(UNUSED_ARG(PyObject *self), PyObject *args)
a2c8c575 198{
4c216694 199 /* Send a privmsg
50d61a79 200 usage: _svc.send_target_privmsg(<servicenick_from>, <nick_to>, <message>)
4c216694 201 */
a2c8c575 202 int ret = 0;
203 char *servicenick;
204 char *channel;
205 char *buf;
206
207 struct service *service;
208
039a6658 209
a2c8c575 210 if(!PyArg_ParseTuple(args, "sss:reply", &servicenick, &channel, &buf ))
211 return NULL;
e7af1e12 212
213 if (buf == NULL || strlen(buf) == 0) {
214 PyErr_SetString(PyExc_Exception, "invalid empty message");
215 return NULL;
216 }
217
a2c8c575 218 if(!(service = service_find(servicenick))) {
e7af1e12 219 PyErr_SetString(PyExc_Exception, "no such service nick");
8d670803 220 return NULL;
a2c8c575 221 }
e7af1e12 222
223 ret = send_target_message(5, channel, service->bot, "%s", buf);
a2c8c575 224 return Py_BuildValue("i", ret);
225}
226
d4e0f0c4 227static PyObject*
039a6658 228emb_send_target_notice(UNUSED_ARG(PyObject *self), PyObject *args)
d4e0f0c4 229{
4c216694 230 /* send a notice
50d61a79 231 usage: _svc.send_target_notice(<servicenick_from>, <nick_to>, <message>)
4c216694 232 */
d4e0f0c4 233 int ret = 0;
234 char *servicenick;
235 char *target;
236 char *buf;
237
238 struct service *service;
239
240 if(!PyArg_ParseTuple(args, "sss:reply", &servicenick, &target, &buf ))
241 return NULL;
66f68f65 242
243 if (buf == NULL || strlen(buf) == 0) {
244 PyErr_SetString(PyExc_Exception, "invalid empty message");
245 return NULL;
246 }
247
d4e0f0c4 248 if(!(service = service_find(servicenick))) {
66f68f65 249 PyErr_SetString(PyExc_Exception, "no such service nick");
d4e0f0c4 250 return NULL;
251 }
66f68f65 252
253 ret = send_target_message(4, target, service->bot, "%s", buf);
254
d4e0f0c4 255 return Py_BuildValue("i", ret);
256}
257
bfdfd1c3 258static PyObject*
259pyobj_from_usernode(struct userNode* user) {
ee6f1c82 260 unsigned int n;
bfdfd1c3 261 struct modeNode *mn;
82089e3f 262 PyObject* retval = NULL;
bfdfd1c3 263 PyObject* pChanList = PyTuple_New(user->channels.used);
264
82089e3f 265 if (pChanList == NULL)
266 return NULL;
267
bfdfd1c3 268 for (n=0; n < user->channels.used; n++) {
269 mn = user->channels.list[n];
82089e3f 270 if (PyTuple_SetItem(pChanList, n, Py_BuildValue("s", mn->channel->name)))
271 goto cleanup;
bfdfd1c3 272 }
273
82089e3f 274 retval = Py_BuildValue("{"
bfdfd1c3 275 "s: s, " /* nick */
276 "s: s, " /* ident */
277 "s: s, " /* info */
278 "s: s, " /* hostname */
279 "s: s, " /* ip */
280 "s: s, " /* fakehost */
281 "s: s, " /* sethost */
282 "s: s, " /* crypthost */
283 "s: s, " /* cryptip */
bfdfd1c3 284 "s: s, " /* numeric */
bfdfd1c3 285 "s: i, " /* loc */
286 "s: i, " /* no_notice */
287 "s: s, " /* mark */
288 "s: s, " /* version_reply */
289 "s: s, " /* account */
290 "s: O}", /* channels */
291 "nick", user->nick,
292 "ident", user->ident,
293 "info", user->info,
294 "hostname", user->hostname,
295 "ip", irc_ntoa(&user->ip),
296 "fakehost", user->fakehost,
297 "sethost", user->sethost,
298 "crypthost", user->crypthost,
299 "cryptip", user->cryptip,
bfdfd1c3 300 "numeric", user->numeric,
bfdfd1c3 301 "loc", user->loc,
302 "no_notice", user->no_notice,
303 "mark", user->mark,
304 "version_reply", user->version_reply,
305 "account", user->handle_info ? user->handle_info->handle : NULL,
306 "channels", pChanList);
82089e3f 307
308 if (retval == NULL)
309 goto cleanup;
310
311 return retval;
312
313cleanup:
314 Py_XDECREF(retval);
ee6f1c82 315 pyobj_release_tuple(pChanList, n);
82089e3f 316
317 return NULL;
bfdfd1c3 318}
319
8d670803 320static PyObject*
039a6658 321emb_get_user(UNUSED_ARG(PyObject *self), PyObject *args)
8d670803 322{
4c216694 323 /* Get a python object containing everything x3 knows about a user, by nick.
50d61a79 324 usage: _svc.get_user(<nick>)
4c216694 325 */
54d2fd3d 326 char const* nick;
8d670803 327 struct userNode *user;
039a6658 328
8d670803 329 if(!PyArg_ParseTuple(args, "s", &nick))
330 return NULL;
bfdfd1c3 331
8d670803 332 if(!(user = GetUserH(nick))) {
54d2fd3d 333 PyErr_SetString(PyExc_Exception, "no such user");
8d670803 334 return NULL;
335 }
bfdfd1c3 336
337 return pyobj_from_usernode(user);
8d670803 338}
339
d8f8d3b6 340static PyObject*
341pyobj_from_server(struct server* srv) {
342 size_t n, idx;
343 PyObject* tmp = NULL;
344 PyObject* retval = NULL;
345 PyObject* users = PyTuple_New(srv->clients);
346
347 if (users == NULL)
348 return NULL;
349
350 idx = 0;
351 for (n = 0; n < srv->num_mask; ++n) {
352 if (srv->users[n] == NULL)
353 continue;
354
355 tmp = PyString_FromString(srv->users[n]->nick);
356 if (tmp == NULL)
357 goto cleanup;
358
359 if (PyTuple_SetItem(users, idx++, tmp))
360 goto cleanup;
361 }
362
363 retval = Py_BuildValue("{"
364 "s:s," /* name */
365 "s:l," /* boot */
366 "s:l," /* link_time */
367 "s:s," /* description */
368 "s:s," /* numeric */
369 "s:I," /* num_mask */
370 "s:I," /* hops */
371 "s:I," /* clients */
372 "s:I," /* max_clients */
373 "s:I," /* burst */
374 "s:I," /* self_burst */
375 "s:s" /* uplink */
376 "s:O" /* users */
377 /* TODO: Children */
378 "}",
379 "name", srv->name,
380 "boot", srv->boot,
381 "link_time", srv->link_time,
382 "description", srv->description,
383 "numeric", srv->numeric,
384 "num_mask", srv->num_mask,
385 "hops", srv->hops,
386 "clients", srv->clients,
387 "max_clients", srv->max_clients,
388 "burst", srv->burst,
389 "self_burst", srv->self_burst,
390 "uplink", srv->uplink ? srv->uplink->name : NULL,
391 "users", users
392 );
393
394 if (retval == NULL)
395 goto cleanup;
396
397 return retval;
398
399cleanup:
400 Py_XDECREF(retval);
ee6f1c82 401 pyobj_release_tuple(users, idx);
d8f8d3b6 402
403 return NULL;
404}
405
406static PyObject*
407emb_get_server(UNUSED_ARG(PyObject* self), PyObject* args) {
408 struct server* srv;
409 char const* name;
410
411 if (!PyArg_ParseTuple(args, "s", &name))
412 return NULL;
413
414 if (name == NULL || strlen(name) == 0) {
415 PyErr_SetString(PyExc_Exception, "invalid server name");
416 return NULL;
417 }
418
419 if ((srv = GetServerH(name)) == NULL) {
420 PyErr_SetString(PyExc_Exception, "unknown server");
421 return NULL;
422 }
423
424 return pyobj_from_server(srv);
425}
426
92fb809b 427static PyObject*
428pyobj_from_modelist(struct modeList* mode) {
429 size_t n;
430 PyObject* tmp;
431 PyObject* retval = PyTuple_New(mode->used);
432
433 if (retval == NULL)
434 return NULL;
435
436 for (n = 0; n < mode->used; ++n) {
437 struct modeNode* mn = mode->list[n];
438 tmp = PyString_FromString(mn->user->nick);
439 if (tmp == NULL) {
440 pyobj_release_tuple(retval, n);
441 return NULL;
442 }
443
444 if (PyTuple_SetItem(retval, n, tmp)) {
445 pyobj_release_tuple(retval, n);
446 return NULL;
447 }
448 }
449
450 return retval;
451}
452
c9f7b679 453static PyObject*
454pyobj_from_banlist(struct banList* bans) {
455 size_t n;
456 struct banNode* bn;
457 PyObject* tmp;
458 PyObject* retval = PyTuple_New(bans->used);
459
460 if (retval == NULL)
461 return NULL;
462
463 for (n = 0; n < bans->used; ++n) {
464 bn = bans->list[n];
465
466 tmp = Py_BuildValue("{s:s,s:s,s:l}",
467 "ban", bn->ban, "who", bn->who, "set", bn->set);
468
469 if (tmp == NULL || PyTuple_SetItem(retval, n, tmp)) {
470 pyobj_release_tuple(retval, n);
471 return NULL;
472 }
473 }
474
475 return retval;
476}
477
478static PyObject*
479pyobj_from_exemptlist(struct exemptList* exmp) {
480 size_t n;
481 struct exemptNode* en;
482 PyObject* tmp;
483 PyObject* retval = PyTuple_New(exmp->used);
484
485 if (retval == NULL)
486 return NULL;
487
488 for (n = 0; n < exmp->used; ++n) {
489 en = exmp->list[n];
490
491 tmp = Py_BuildValue("{s:s,s:s,s:l}",
492 "ban", en->exempt, "who", en->who, "set", en->set);
493
494 if (tmp == NULL || PyTuple_SetItem(retval, n, tmp)) {
495 pyobj_release_tuple(retval, n);
496 return NULL;
497 }
498 }
499
500 return retval;
501}
502
8d670803 503static PyObject*
039a6658 504emb_get_channel(UNUSED_ARG(PyObject *self), PyObject *args)
8d670803 505{
4c216694 506 /* Returns a python dict object with all sorts of info about a channel.
50d61a79 507 usage: _svc.get_channel(<name>)
4c216694 508 */
8d670803 509 char *name;
510 struct chanNode *channel;
c9f7b679 511 PyObject *pChannelMembers = NULL;
512 PyObject *pChannelBans = NULL;
513 PyObject *pChannelExempts = NULL;
514 PyObject *retval = NULL;
8d670803 515
039a6658 516
8d670803 517 if(!PyArg_ParseTuple(args, "s", &name))
518 return NULL;
3f24e818 519
8d670803 520 if(!(channel = GetChannel(name))) {
3f24e818 521 PyErr_SetString(PyExc_Exception, "unknown channel");
8d670803 522 return NULL;
523 }
524
525 /* build tuple of nicks in channel */
92fb809b 526 pChannelMembers = pyobj_from_modelist(&channel->members);
c9f7b679 527 if (pChannelMembers == NULL)
528 goto cleanup;
8d670803 529
530 /* build tuple of bans */
c9f7b679 531 pChannelBans = pyobj_from_banlist(&channel->banlist);
532 if (pChannelBans == NULL)
533 goto cleanup;
8d670803 534
8d670803 535 /* build tuple of exempts */
c9f7b679 536 pChannelExempts = pyobj_from_exemptlist(&channel->exemptlist);
537 if (pChannelExempts == NULL)
538 goto cleanup;
8d670803 539
c9f7b679 540 retval = Py_BuildValue("{s:s,s:s,s:s,s:i"
8d670803 541 ",s:i,s:i,s:O,s:O,s:O}",
542
543 "name", channel->name,
544 "topic", channel->topic,
545 "topic_nick", channel->topic_nick,
546 "topic_time", channel->topic_time,
547
548 "timestamp", channel->timestamp,
549 "modes", channel->modes,
550 "members", pChannelMembers,
551 "bans", pChannelBans,
552 "exempts", pChannelExempts
553 );
c9f7b679 554 if (retval == NULL)
555 goto cleanup;
556
557 return retval;
558
559cleanup:
560 Py_XDECREF(retval);
561 pyobj_release_tuple(pChannelExempts, channel->exemptlist.used);
562 pyobj_release_tuple(pChannelBans, channel->banlist.used);
563 pyobj_release_tuple(pChannelMembers, channel->members.used);
564
565 return NULL;
8d670803 566}
567
8d670803 568static PyObject*
039a6658 569emb_get_account(UNUSED_ARG(PyObject *self), PyObject *args)
8d670803 570{
4c216694 571 /* Returns a python dict object with all sorts of info about an account.
50d61a79 572 usage: _svc.get_account(<account name>)
4c216694 573 */
8d670803 574 char *name;
4c216694 575 struct handle_info *hi;
576
039a6658 577
8d670803 578 if(!PyArg_ParseTuple(args, "s", &name))
579 return NULL;
4c216694 580
581 hi = get_handle_info(name);
5b2b1df2 582
4c216694 583 if(!hi) {
5b2b1df2 584 PyErr_SetString(PyExc_Exception, "unknown account name");
4c216694 585 return NULL;
586 }
5b2b1df2 587
4c216694 588 return Py_BuildValue("{s:s,s:i,s:s,s:s,s:s"
589 ",s:s,s:s}",
590
591 "account", hi->handle,
592 "registered", hi->registered,
593 "last_seen", hi->lastseen,
594 "infoline", hi->infoline ? hi->infoline : "",
595 "email", hi->email_addr ? hi->email_addr : "",
596
597 "fakehost", hi->fakehost ? hi->fakehost : "",
598 "last_quit_host", hi->last_quit_host
599
600 /* TODO: */
601 /* users online authed to this account */
602 /* cookies */
603 /* nicks (nickserv nets only?) */
604 /* masks */
605 /* ignores */
606 /* channels */
607 );
8d670803 608}
8d670803 609
07559983 610static PyObject*
039a6658 611emb_get_info(UNUSED_ARG(PyObject *self), UNUSED_ARG(PyObject *args))
612{
613 /* return some info about the general setup
614 * of X3, such as what the chanserv's nickname
615 * is.
616 */
617
618
619 return Py_BuildValue("{s:s,s:s,s:s,s:s,s:s}",
620 "chanserv", chanserv? chanserv->nick : "ChanServ",
621 "nickserv", nickserv?nickserv->nick : "NickServ",
622 "opserv", opserv?opserv->nick : "OpServ",
623 "global", global?global->nick : "Global",
624 "spamserv", spamserv?spamserv->nick : "SpamServ");
625}
626
627static PyObject*
628emb_log_module(UNUSED_ARG(PyObject *self), PyObject *args)
07559983 629{
630 /* a gateway to standard X3 logging subsystem.
631 * level is a value 0 to 9 as defined by the log_severity enum in log.h.
07559983 632 *
633 * for now, all logs go to the PY_LOG log. In the future this will change.
634 */
635 char *message;
636 int ret = 0;
637 int level;
638
039a6658 639
07559983 640 if(!PyArg_ParseTuple(args, "is", &level, &message))
641 return NULL;
642
643 log_module(PY_LOG, level, "%s", message);
644
645 return Py_BuildValue("i", ret);
646}
8d670803 647
0076604e 648static PyObject*
649emb_kill(UNUSED_ARG(PyObject* self), PyObject* args) {
650 char const* from_nick, *target_nick, *message;
651 struct userNode *target;
652 struct service *service;
653
654 if (!PyArg_ParseTuple(args, "sss", &from_nick, &target_nick, &message))
655 return NULL;
656
657 if(!(service = service_find(from_nick))) {
658 PyErr_SetString(PyExc_Exception, "unknown service user specified as from user");
659 return NULL;
660 }
661
662 if ((target = GetUserH(target_nick)) == NULL) {
663 PyErr_SetString(PyExc_Exception, "unknown target user");
664 return NULL;
665 }
666
667 irc_kill(service->bot, target, message);
668
8d455e8b 669 Py_INCREF(Py_None);
0076604e 670 return Py_None;
671}
672
8f206d22 673struct py_timeq_extra {
674 PyObject* func;
675 PyObject* arg;
676};
677
678static
679void py_timeq_callback(void* data) {
680 struct py_timeq_extra* extra = (struct py_timeq_extra*)data;
681
682 PyObject* retval = PyObject_Call(extra->func, extra->arg, NULL);
683 Py_XDECREF(retval);
684
685 Py_DECREF(extra->func);
686 Py_DECREF(extra->arg);
687}
688
689static PyObject*
690emb_timeq_add(UNUSED_ARG(PyObject* self), PyObject* args) {
691 time_t when;
692 PyObject* func, *arg;
693 struct py_timeq_extra* extra;
694
695 if (!PyArg_ParseTuple(args, "lOO", &when, &func, &arg))
696 return NULL;
697
698 if (!PyFunction_Check(func)) {
699 PyErr_SetString(PyExc_Exception, "first argument must be a function");
700 return NULL;
701 }
702
703 if (!PyTuple_Check(arg)) {
704 PyErr_SetString(PyExc_Exception, "second argument must be a tuple");
705 return NULL;
706 }
707
708 extra = malloc(sizeof(struct py_timeq_extra));
709 if (extra == NULL) {
710 PyErr_SetString(PyExc_Exception, "out of memory");
711 return NULL;
712 }
713
714 Py_INCREF(func);
715 Py_INCREF(arg);
716
717 extra->func = func;
718 extra->arg = arg;
719
720 timeq_add(when, py_timeq_callback, (void*)extra);
721
8d455e8b 722 Py_INCREF(Py_None);
8f206d22 723 return Py_None;
724}
725
3f218269 726static PyObject*
727emb_timeq_del(UNUSED_ARG(PyObject* self), PyObject* args) {
728 /* NOTE:
729 * This function will delete all python-added callbacks registered
730 * to run at the given time, regardless of their data. This is due to
731 * the unnecessary extra burden it would require to get the same data for
732 * multiple runs.
733 */
734 time_t when;
735
736 if (!PyArg_ParseTuple(args, "l", &when))
737 return NULL;
738
739 timeq_del(when, py_timeq_callback, NULL, TIMEQ_IGNORE_DATA);
740
8d455e8b 741 Py_INCREF(Py_None);
3f218269 742 return Py_None;
743}
744
2bee6a6e 745static int pyobj_config_make_dict(char const* key, void* data_, void* extra) {
746 struct record_data* data = (struct record_data*)data_;
747 PyObject* dict = (PyObject*)extra;
748 PyObject* value = NULL, *tmp;
749 size_t n, idx;
750 int success;
751
752 switch (data->type) {
753 case RECDB_QSTRING:
754 value = PyString_FromString(data->d.qstring);
755 break;
756
757 case RECDB_STRING_LIST:
758 value = PyList_New(data->d.slist->used);
759 if (value == NULL)
760 break;
761
762 success = 1;
763 for (n = 0; n < data->d.slist->used; ++n) {
764 tmp = PyString_FromString(data->d.slist->list[n]);
765 if (tmp == NULL) {
766 success = 0;
767 break;
768 }
769
770 if (PyList_SetItem(value, n, tmp)) {
771 Py_DECREF(tmp);
772 success = 0;
773 break;
774 }
775 }
776 if (!success) {
777 for (idx = 0; idx < n; ++idx) {
778 tmp = PyList_GET_ITEM(value, idx);
779 Py_DECREF(tmp);
780 PyList_SET_ITEM(value, idx, NULL);
781 }
782 Py_DECREF(value);
783 value = NULL;
784 }
785 break;
786
787 case RECDB_OBJECT:
788 value = PyDict_New();
789 if (value == NULL)
790 break;
791
792 if (dict_foreach(data->d.object, pyobj_config_make_dict, (void*)value) != NULL) {
793 PyDict_Clear(value);
794 value = NULL;
795 break;
796 }
797
798 break;
799
800 default:
8d455e8b 801 Py_INCREF(Py_None);
2bee6a6e 802 value = Py_None;
803 }
804
805 if (value == NULL)
806 return 1;
807
808 if (PyDict_SetItemString(dict, key, value))
809 return 1;
810
811 return 0;
812}
813
814static PyObject*
815emb_get_config(UNUSED_ARG(PyObject* self), PyObject* args) {
816 PyObject* dict;
817
818 if (!PyArg_ParseTuple(args, ""))
819 return NULL;
820
821 dict = PyDict_New();
822 if (dict == NULL)
823 return NULL;
824
825 if (conf_enum_root(pyobj_config_make_dict, (void*)dict) != NULL) {
826 PyDict_Clear(dict);
827 PyErr_SetString(PyExc_Exception, "unable to iterate config");
828 return NULL;
829 }
830
831 return dict;
832}
833
749f0565 834static PyObject* emb_kick(UNUSED_ARG(PyObject* self), PyObject* args) {
835 struct userNode* who, *target;
836 struct chanNode* channel;
837 char const* msg;
838
839 char const* who_s, *target_s, *channel_s;
840
841 if (!PyArg_ParseTuple(args, "ssss", &who_s, &target_s, &channel_s, &msg))
842 return NULL;
843
844 if ((who = GetUserH(who_s)) == NULL) {
845 PyErr_SetString(PyExc_Exception, "no such user");
846 return NULL;
847 }
848
849 if ((target = GetUserH(target_s)) == NULL) {
850 PyErr_SetString(PyExc_Exception, "no such target");
851 return NULL;
852 }
853
854 if ((channel = GetChannel(channel_s)) == NULL) {
855 PyErr_SetString(PyExc_Exception, "no such channel");
856 return NULL;
857 }
858
859 irc_kick(who, target, channel, msg);
860
8d455e8b 861 Py_INCREF(Py_None);
749f0565 862 return Py_None;
863}
864
a2c8c575 865static PyMethodDef EmbMethods[] = {
07559983 866 /* Communication methods */
a2c8c575 867 {"dump", emb_dump, METH_VARARGS, "Dump raw P10 line to server"},
868 {"send_target_privmsg", emb_send_target_privmsg, METH_VARARGS, "Send a message to somewhere"},
d4e0f0c4 869 {"send_target_notice", emb_send_target_notice, METH_VARARGS, "Send a notice to somewhere"},
07559983 870 {"log_module", emb_log_module, METH_VARARGS, "Log something using the X3 log subsystem"},
039a6658 871//TODO: {"exec_cmd", emb_exec_cmd, METH_VARARGS, "execute x3 command provided"},
872// This should use environment from "python command" call to pass in, if available
0076604e 873 {"kill", emb_kill, METH_VARARGS, "Kill someone"},
039a6658 874//TODO: {"shun"
875//TODO: {"unshun"
876//TODO: {"gline", emb_gline, METH_VARARGS, "gline a mask"},
877//TODO: {"ungline", emb_ungline, METH_VARARGS, "remove a gline"},
749f0565 878 {"kick", emb_kick, METH_VARARGS, "kick someone from a channel"},
039a6658 879//TODO: {"channel_mode", emb_channel_mode, METH_VARARGS, "set modes on a channel"},
880//TODO: {"user_mode", emb_user_mode, METH_VARARGS, "Have x3 set usermodes on one of its own nicks"},
881//
2bee6a6e 882 {"get_config", emb_get_config, METH_VARARGS, "get x3.conf settings into a nested dict"},
039a6658 883//TODO: {"config_set", emb_config_set, METH_VARARGS, "change a config setting 'on-the-fly'."},
884//
8f206d22 885 {"timeq_add", emb_timeq_add, METH_VARARGS, "add function to callback to the event system"},
3f218269 886 {"timeq_del", emb_timeq_del, METH_VARARGS, "remove function to callback from the event system"},
887
07559983 888 /* Information gathering methods */
8d670803 889 {"get_user", emb_get_user, METH_VARARGS, "Get details about a nickname"},
f0e11521 890 {"get_users", emb_get_users, METH_VARARGS, "Get all connected users"},
8d670803 891 {"get_channel", emb_get_channel, METH_VARARGS, "Get details about a channel"},
f0e11521 892 {"get_channels", emb_get_channels, METH_VARARGS, "Get all channels"},
d8f8d3b6 893 {"get_server", emb_get_server, METH_VARARGS, "Get details about a server"},
dcc1df5e 894 {"get_servers", emb_get_servers, METH_VARARGS, "Get all server names"},
4c216694 895 {"get_account", emb_get_account, METH_VARARGS, "Get details about an account"},
39d37f27 896 {"get_accounts", emb_get_accounts, METH_VARARGS, "Get all nickserv accounts"},
039a6658 897 {"get_info", emb_get_info, METH_VARARGS, "Get various misc info about x3"},
07559983 898 /* null terminator */
a2c8c575 899 {NULL, NULL, 0, NULL}
900};
901
902
f0e11521 903/*
904These functions set up the embedded environment for us to call out to
905modpython.py class methods.
4c216694 906 */
cbfd323c 907
07559983 908void python_log_module() {
909 /* Attempt to convert python errors to x3 log system */
910 PyObject *exc, *tb, *value, *tmp;
911 char *str_exc = "NONE";
912 char *str_value = "NONE";
913 char *str_tb = "NONE";
914
915 PyErr_Fetch(&exc, &value, &tb);
916
917 if(exc) {
918 if((tmp = PyObject_Str(exc)))
919 str_exc = PyString_AsString(tmp);
920 }
921 if(value) {
922 if((tmp = PyObject_Str(value)))
923 str_value = PyString_AsString(tmp);
924 }
925 if(tb) {
926 if((tmp = PyObject_Str(tb)))
927 str_tb = PyString_AsString(tmp);
928 }
929
930 /* Now restore it so we can print it (just in case)
931 * (should we do this only when running in debug mode?) */
932 PyErr_Restore(exc, value, tb);
933 PyErr_Print(); /* which of course, clears it again.. */
934
935 log_module(PY_LOG, LOG_WARNING, "PYTHON error: %s, value: %s", str_exc, str_value);
936
937 /* TODO: get the traceback using the traceback module via C api so we can add it to the X3 logs. See
938 * http://mail.python.org/pipermail/python-list/2003-March/192226.html */
939 // (this doesnt work, str_tb is just an object hashid) log_module(PY_LOG, LOG_INFO, "PYTHON error, traceback: %s", str_tb);
940}
941
942
e0f76584 943PyObject *python_build_handler_args(size_t argc, char *args[], PyObject *pIrcObj) {
4c216694 944 /* Sets up a python tuple with passed in arguments, prefixed by the Irc instance
f0e11521 945 which handlers use to interact with C.
4c216694 946 argc = number of args
947 args = array of args
948 pIrcObj = instance of the irc class
949 */
e0f76584 950 size_t i = 0, n;
951 PyObject *pArgs = NULL;
cbfd323c 952
e0f76584 953 pArgs = PyTuple_New(argc + 1);
954 Py_INCREF(pIrcObj);
955 PyTuple_SetItem(pArgs, i++, pIrcObj);
cbfd323c 956
e0f76584 957 if(args && argc) {
cbfd323c 958 PyObject *pValue;
e0f76584 959 for(n = 0; n < argc; ++n) {
960 pValue = PyString_FromString(args[n]);
cbfd323c 961 if(!pValue) {
e0f76584 962 Py_DECREF(pArgs);
963 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[n]);
964 return NULL;
cbfd323c 965 }
e0f76584 966 PyTuple_SetItem(pArgs, n+i, pValue);
cbfd323c 967 }
cbfd323c 968 }
e0f76584 969 return pArgs;
cbfd323c 970}
971
972PyObject *python_build_args(size_t argc, char *args[]) {
4c216694 973 /* Builds the passed in arguments into a python argument tuple.
974 argc = number of args
975 args = array of args
976 */
cbfd323c 977 size_t i;
978 PyObject *pArgs = NULL;
979
980 if(args && argc) {
981 pArgs = PyTuple_New(argc);
982 PyObject *pValue;
983 for(i = 0; i< argc; ++i) {
984 pValue = PyString_FromString(args[i]);
985 if(!pValue) {
986 Py_DECREF(pArgs);
987 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[i]);
988 return NULL;
989 }
990 PyTuple_SetItem(pArgs, i, pValue);
991 }
992 }
993 return pArgs;
d4e0f0c4 994}
caf97651 995
cbfd323c 996
e0f76584 997PyObject *new_irc_object(char *command_service, char *command_caller, char *command_target) {
4c216694 998 /* Creates a new instance of the irc class (from modpython.py) which is initalized
999 with current environment details like which service were talking to.
1000 command_service = which service we are talking to, or empty string if none
1001 command_caller = nick of user generating message, or empty string if none
1002 command_target = If were reacting to something on a channel, this will
1003 be set to the name of the channel. Otherwise empty
1004 */
e0f76584 1005 PyObject *pIrcArgs = NULL;
1006 PyObject *pIrcClass;
1007 PyObject *pIrcObj;
1008
4c216694 1009 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate irc class; %s %s %s", command_service, command_caller, command_target);
e0f76584 1010 pIrcClass = PyObject_GetAttrString(base_module, "irc");
1011 /* pIrcClass is a new reference */
1012 if(pIrcClass && PyCallable_Check(pIrcClass)) {
1013 //size_t i;
1014 char *ircargs[] = {command_service, command_caller, command_target};
1015 //PyObject *pValue;
1016
1017 pIrcArgs = python_build_args(3, ircargs);
e0f76584 1018 pIrcObj = PyObject_CallObject(pIrcClass, pIrcArgs);
1019 if(!pIrcObj) {
1020 log_module(PY_LOG, LOG_ERROR, "IRC Class failed to load");
07559983 1021 python_log_module();
1022 //PyErr_Print();
e0f76584 1023 }
1024 if(pIrcArgs != NULL) {
1025 Py_DECREF(pIrcArgs);
1026 }
4c216694 1027 Py_DECREF(pIrcClass);
e0f76584 1028 return pIrcObj;
1029 }
1030 else {
4c216694 1031 /* need to free pIrcClass here if it WAS found but was NOT callable? */
e0f76584 1032 log_module(PY_LOG, LOG_ERROR, "Unable to find irc class");
1033 return NULL;
1034 }
1035}
1036
cbfd323c 1037int python_call_handler(char *handler, char *args[], size_t argc, char *command_service, char *command_caller, char *command_target) {
4c216694 1038 /* This is how we talk to modpython.c. First a new instance of the irc class is created using these
1039 arguments to setup the current environment. Then the named method of the handler object is called
1040 with the givin arguments.
cbfd323c 1041 */
1042 PyObject *pIrcObj;
1043 PyObject *pArgs;
1044 PyObject *pMethod;
1045 PyObject *pValue;
1046
e0f76584 1047 log_module(PY_LOG, LOG_INFO, "attempting to call handler %s.", handler);
07559983 1048 if(base_module != NULL && handler_object != NULL) {
cbfd323c 1049 pIrcObj = new_irc_object(command_service, command_caller, command_target);
e0f76584 1050 if(!pIrcObj) {
1051 log_module(PY_LOG, LOG_INFO, "Can't get irc object. Bailing.");
1052 return 0;
1053 }
cbfd323c 1054
e0f76584 1055 pArgs = python_build_handler_args(argc, args, pIrcObj);
cbfd323c 1056 pMethod = PyObject_GetAttrString(handler_object, handler);
1057 if(pMethod && PyCallable_Check(pMethod)) {
07559983 1058 /* Call the method, with the arguments */
cbfd323c 1059 pValue = PyObject_CallObject(pMethod, pArgs);
1060 if(pArgs) {
1061 Py_DECREF(pArgs);
1062 }
1063 if(pValue != NULL) {
1064 int ret;
1065 ret = PyInt_AsLong(pValue);
1066 if(ret == -1 && PyErr_Occurred()) {
07559983 1067 //PyErr_Print();
cbfd323c 1068 log_module(PY_LOG, LOG_INFO, "error converting return value of handler %s to type long. ", handler);
07559983 1069 python_log_module();
cbfd323c 1070 ret = 0;
1071 }
1072 log_module(PY_LOG, LOG_INFO, "handler %s was run successfully, returned %d.", handler, ret);
cbfd323c 1073 Py_DECREF(pValue);
4c216694 1074 Py_DECREF(pIrcObj);
1075 Py_DECREF(pMethod);
cbfd323c 1076 return ret;
1077 }
1078 else {
cbfd323c 1079 /* TODO: instead of print errors, get them as strings
1080 * and deal with them with normal x3 log system. */
cbfd323c 1081 log_module(PY_LOG, LOG_WARNING, "call to handler %s failed", handler);
07559983 1082 //PyErr_Print();
1083 python_log_module();
4c216694 1084 Py_DECREF(pIrcObj);
1085 Py_DECREF(pMethod);
cbfd323c 1086 return 0;
1087 }
1088 }
1089 else { /* couldn't find handler methed */
4c216694 1090 Py_DECREF(pArgs);
1091 /* Free pMethod if it was found but not callable? */
cbfd323c 1092 log_module(PY_LOG, LOG_ERROR, "Cannot find handler %s.", handler);
1093 return 0;
1094
1095 }
1096 }
1097 else { /* No base module.. no python? */
e0f76584 1098 log_module(PY_LOG, LOG_INFO, "Cannot handle %s, Python is not initialized.", handler);
cbfd323c 1099 return 0;
1100 }
1101}
1102
1103PyObject *python_new_handler_object() {
4c216694 1104 /* Create a new instance of the handler class.
1105 This is called during python initilization (or reload)
1106 and the result is saved and re-used.
1107 */
cbfd323c 1108 PyObject *pHandlerClass, *pHandlerObj;
1109
1110 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate python class handler");
1111 pHandlerClass = PyObject_GetAttrString(base_module, "handler");
1112 /* Class is a new reference */
1113 if(pHandlerClass && PyCallable_Check(pHandlerClass)) {
e0f76584 1114 /*PyObject *pValue; */
cbfd323c 1115
1116 pHandlerObj = PyObject_CallObject(pHandlerClass, NULL);
07559983 1117 if(pHandlerObj != NULL) {
1118 log_module(PY_LOG, LOG_INFO, "Created new python handler object.");
1119 return pHandlerObj;
1120 }
1121 else {
1122 log_module(PY_LOG, LOG_ERROR, "Unable to instanciate handler object");
1123 //PyErr_Print();
1124 python_log_module();
1125 return NULL;
1126 }
cbfd323c 1127 }
1128 else {
1129 log_module(PY_LOG, LOG_ERROR, "Unable to find handler class");
07559983 1130 //PyErr_Print();
1131 python_log_module();
1132 if(pHandlerClass) {
1133 Py_DECREF(pHandlerClass);
1134 }
cbfd323c 1135 return NULL;
1136 }
1137}
1138
4c216694 1139/* ------------------------------------------------------------------------------- *
1140 Some gateway functions to convert x3 callbacks into modpython.py callbacks.
1141 Mostly we just build relevant args and call the proper handler method
1142
1143 debate: do we just register these and check them in python
1144 for every one (slow?) or actually work out if a plugin needs
1145 it first? We will start by doing it every time.
cbfd323c 1146 */
0b350353 1147static int
1148python_handle_join(struct modeNode *mNode)
1149{
4c216694 1150 /* callback for handle_join events.
1151 */
0b350353 1152 struct userNode *user = mNode->user;
1153 struct chanNode *channel = mNode->channel;
1154
a2c8c575 1155
caf97651 1156 log_module(PY_LOG, LOG_INFO, "python module handle_join");
a2c8c575 1157 if(!channel||!user) {
4c216694 1158 log_module(PY_LOG, LOG_WARNING, "Python code got join without channel or user!");
a2c8c575 1159 return 0;
1160 }
1161 else {
1162 char *args[] = {channel->name, user->nick};
e0f76584 1163 return python_call_handler("join", args, 2, "", "", "");
a2c8c575 1164 }
0b350353 1165}
1166
0ab7b4bc 1167static int
1168python_handle_server_link(struct server *server)
1169{
1170 log_module(PY_LOG, LOG_INFO, "python module handle_server_link");
1171 if(!server) {
1172 log_module(PY_LOG, LOG_WARNING, "Python code got server link without server!");
1173 return 0;
1174 }
1175 else {
1176 char *args[] = {server->name, server->description};
1177 return python_call_handler("server_link", args, 2, "", "", "");
1178 }
1179}
1180
1181static int
1182python_handle_new_user(struct userNode *user)
1183{
1184 log_module(PY_LOG, LOG_INFO, "Python module handle_new_user");
1185 if(!user) {
1186 log_module(PY_LOG, LOG_WARNING, "Python code got new_user without the user");
1187 return 0;
1188 }
1189 else {
1190 char *args[] = {user->nick, user->ident, user->hostname, user->info};
1191 return python_call_handler("new_user", args, 4, "", "", "");
1192 }
1193}
1194
1195static void
1196python_handle_nick_change(struct userNode *user, const char *old_nick)
1197{
1198 log_module(PY_LOG, LOG_INFO, "Python module handle_nick_change");
1199 if(!user) {
1200 log_module(PY_LOG, LOG_WARNING, "Python code got nick_change without the user!");
1201 }
1202 else {
1203 char *args[] = {user->nick, (char *)old_nick};
1204 python_call_handler("nick_change", args, 2, "", "", "");
1205 }
1206}
1207
4c216694 1208/* ----------------------------------------------------------------------------- */
1209
cbfd323c 1210
caf97651 1211int python_load() {
4c216694 1212 /* Init the python engine and do init work on modpython.py
1213 This is called during x3 startup, and on a python reload
1214 */
caf97651 1215 PyObject *pName;
ef5e0305 1216 char* buffer;
1217 char* env = getenv("PYTHONPATH");
1218
1219 if (env)
1220 env = strdup(env);
1221
1222 if (!env)
1223 setenv("PYTHONPATH", modpython_conf.scripts_dir, 1);
1224 else if (!strstr(env, modpython_conf.scripts_dir)) {
1225 buffer = (char*)malloc(strlen(env) + strlen(modpython_conf.scripts_dir) + 2);
1226 sprintf(buffer, "%s:%s", modpython_conf.scripts_dir, env);
1227 setenv("PYTHONPATH", buffer, 1);
1228 free(buffer);
1229 free(env);
1230 }
caf97651 1231
caf97651 1232 Py_Initialize();
50d61a79 1233 Py_InitModule("_svc", EmbMethods);
ed8d873c 1234 pName = PyString_FromString(modpython_conf.main_module);
caf97651 1235 base_module = PyImport_Import(pName);
1236 Py_DECREF(pName);
bc2f52df 1237
1238 Py_XDECREF(handler_object);
1239 handler_object = NULL;
1240
caf97651 1241 if(base_module != NULL) {
cbfd323c 1242 handler_object = python_new_handler_object();
1243 if(handler_object) {
e0f76584 1244 python_call_handler("init", NULL, 0, "", "", "");
cbfd323c 1245 return 1;
1246 }
1247 else {
1248 /* error handler class not found */
1249 log_module(PY_LOG, LOG_WARNING, "Failed to create handler object");
1250 return 0;
1251 }
caf97651 1252 }
1253 else {
07559983 1254 //PyErr_Print();
1255 python_log_module();
d4e0f0c4 1256 log_module(PY_LOG, LOG_WARNING, "Failed to load modpython.py");
a2c8c575 1257 return 0;
caf97651 1258 }
cbfd323c 1259 return 0;
caf97651 1260}
1261
caf97651 1262int
1263python_finalize(void) {
4c216694 1264 /* Called after X3 is fully up and running.
1265 Code can be put here that needs to run to init things, but
1266 which is sensitive to everything else in x3 being up and ready
1267 to go.
1268 */
caf97651 1269
1270 PyRun_SimpleString("print 'Hello, World of Python!'");
1271 log_module(PY_LOG, LOG_INFO, "python module finalize");
1272
1273 return 1;
1274}
1275
caf97651 1276static void
4c216694 1277python_cleanup(void) {
1278 /* Called on shutdown of the python module (or before reloading)
1279 */
413fd8ea 1280
caf97651 1281 log_module(PY_LOG, LOG_INFO, "python module cleanup");
413fd8ea 1282 if (PyErr_Occurred())
1283 PyErr_Clear();
f0e11521 1284 Py_Finalize(); /* Shut down python enterpreter */
caf97651 1285}
1286
4c216694 1287/* ---------------------------------------------------------------------------------- *
1288 Python module command handlers.
1289*/
caf97651 1290static MODCMD_FUNC(cmd_reload) {
4c216694 1291 /* reload the python system completely
1292 */
caf97651 1293 log_module(PY_LOG, LOG_INFO, "Shutting python down");
1294 python_cleanup();
1295 log_module(PY_LOG, LOG_INFO, "Loading python stuff");
1296 if(python_load()) {
a2c8c575 1297 reply("PYMSG_RELOAD_SUCCESS");
caf97651 1298 }
1299 else {
a2c8c575 1300 reply("PYMSG_RELOAD_FAILED");
caf97651 1301 }
1302 return 1;
1303}
1304
413fd8ea 1305static char* format_python_error(int space_nls) {
1306 PyObject* extype = NULL, *exvalue = NULL, *extraceback = NULL;
1307 PyObject* pextypestr = NULL, *pexvaluestr = NULL;
1308 char* extypestr = NULL, *exvaluestr = NULL;
1309 size_t retvallen = 0;
1310 char* retval = NULL, *tmp;
1311
1312 PyErr_Fetch(&extype, &exvalue, &extraceback);
1313 if (!extype)
1314 goto cleanup;
1315
1316 pextypestr = PyObject_Str(extype);
1317 if (!pextypestr)
1318 goto cleanup;
1319 extypestr = PyString_AsString(pextypestr);
1320 if (!extypestr)
1321 goto cleanup;
1322
1323 pexvaluestr = PyObject_Str(exvalue);
1324 if (pexvaluestr)
1325 exvaluestr = PyString_AsString(pexvaluestr);
1326
1327 retvallen = strlen(extypestr) + (exvaluestr ? strlen(exvaluestr) + 2 : 0) + 1;
1328 retval = (char*)malloc(retvallen);
1329 if (exvaluestr)
1330 snprintf(retval, retvallen, "%s: %s", extypestr, exvaluestr);
1331 else
1332 strncpy(retval, extypestr, retvallen);
1333
1334 if (space_nls) {
1335 tmp = retval;
1336 while (*tmp) {
1337 if (*tmp == '\n')
1338 *tmp = ' ';
1339 ++tmp;
1340 }
1341 }
1342
1343cleanup:
1344 if (PyErr_Occurred())
1345 PyErr_Clear(); /* ignore errors caused by formatting */
1346 Py_XDECREF(extype);
1347 Py_XDECREF(exvalue);
1348 Py_XDECREF(extraceback);
1349 Py_XDECREF(pextypestr);
1350 Py_XDECREF(pexvaluestr);
1351
1352 if (retval)
1353 return retval;
1354
1355 return strdup("unknown exception");
1356}
1357
8d670803 1358static MODCMD_FUNC(cmd_run) {
413fd8ea 1359 /* this method allows running arbitrary python commands.
1360 * use with care.
1361 */
1362 char* msg;
46f628b1 1363 PyObject* py_main_module;
1364 PyObject* py_globals;
1365 PyObject* py_locals;
1366 PyObject* py_retval;
1367 PyObject* extype, *exvalue, *extraceback;
1368 PyObject* exvaluestr = NULL;
1369 char* exmsg = NULL, *exmsgptr;
1370
1371 py_main_module = PyImport_AddModule("__main__");
1372 py_globals = py_locals = PyModule_GetDict(py_main_module);
413fd8ea 1373
a2c8c575 1374 msg = unsplit_string(argv + 1, argc - 1, NULL);
413fd8ea 1375
46f628b1 1376 py_retval = PyRun_String(msg, Py_file_input, py_globals, py_locals);
1377 if (py_retval == NULL) {
1378 PyErr_Fetch(&extype, &exvalue, &extraceback);
1379 if (exvalue != NULL) {
1380 exvaluestr = PyObject_Str(exvalue);
1381 exmsg = strdup(PyString_AS_STRING(exvaluestr));
1382 exmsgptr = exmsg;
1383 while (exmsgptr && *exmsgptr) {
1384 if (*exmsgptr == '\n' || *exmsgptr == '\r' || *exmsgptr == '\t')
1385 *exmsgptr = ' ';
1386 exmsgptr++;
1387 }
1388 }
1389 if (extype != NULL && exvalue != NULL && PyType_Check(extype)) {
1390 reply("PYMSG_RUN_EXCEPTION", ((PyTypeObject*)extype)->tp_name, exmsg);
413fd8ea 1391 } else
1392 reply("PYMSG_RUN_UNKNOWN_EXCEPTION");
46f628b1 1393
1394 if (extype != NULL)
1395 Py_DECREF(extype);
1396 if (exvalue != NULL)
1397 Py_DECREF(exvalue);
1398 if (extraceback != NULL)
1399 Py_DECREF(extraceback);
1400 if (exvaluestr != NULL)
1401 Py_DECREF(exvaluestr);
1402 if (exmsg)
1403 free(exmsg);
1404 } else {
1405 Py_DECREF(py_retval);
413fd8ea 1406 }
1407
a2c8c575 1408 return 1;
1409}
1410
07559983 1411#define numstrargs(X) sizeof(X) / sizeof(*X)
1412static MODCMD_FUNC(cmd_command) {
1413 char *plugin = argv[1];
1414 char *command = argv[2];
1415 char *msg; /* args */
1416 if(argc > 3) {
1417 msg = unsplit_string(argv + 3, argc - 3, NULL);
1418 }
1419 else {
1420 msg = "";
1421 }
1422 char *args[] = {plugin, command, msg};
1423 python_call_handler("cmd_command", args, numstrargs(args), cmd->parent->bot->nick, user?user->nick:"", channel?channel->name:"");
1424 return 1;
1425}
1426
ef5e0305 1427static void modpython_conf_read(void) {
1428 dict_t conf_node;
1429 char const* str;
1430
1431 if (!(conf_node = conf_get_data(MODPYTHON_CONF_NAME, RECDB_OBJECT))) {
1432 log_module(PY_LOG, LOG_ERROR, "config node '%s' is missing or has wrong type", MODPYTHON_CONF_NAME);
1433 return;
1434 }
1435
1436 str = database_get_data(conf_node, "scripts_dir", RECDB_QSTRING);
1437 modpython_conf.scripts_dir = str ? str : "./";
ed8d873c 1438
1439 str = database_get_data(conf_node, "main_module", RECDB_QSTRING);
1440 modpython_conf.main_module = str ? str : "modpython";
ef5e0305 1441}
1442
0b350353 1443int python_init(void) {
4c216694 1444 /* X3 calls this function on init of the module during startup. We use it to
1445 do all our setup tasks and bindings
1446 */
0b350353 1447
0b350353 1448 PY_LOG = log_register_type("Python", "file:python.log");
caf97651 1449 python_module = module_register("python", PY_LOG, "mod-python.help", NULL);
ef5e0305 1450 conf_register_reload(modpython_conf_read);
1451
caf97651 1452 log_module(PY_LOG, LOG_INFO, "python module init");
1453 message_register_table(msgtab);
1454
0b350353 1455/*
1456 reg_auth_func(python_check_messages);
1457 reg_handle_rename_func(python_rename_account);
1458 reg_unreg_func(python_unreg_account);
1459 conf_register_reload(python_conf_read);
0b350353 1460 saxdb_register("python", python_saxdb_read, python_saxdb_write);
0b350353 1461 modcmd_register(python_module, "send", cmd_send, 3, MODCMD_REQUIRE_AUTHED, NULL);
1462*/
caf97651 1463 modcmd_register(python_module, "reload", cmd_reload, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
8d670803 1464 modcmd_register(python_module, "run", cmd_run, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
07559983 1465 modcmd_register(python_module, "command", cmd_command, 3, MODCMD_REQUIRE_STAFF, NULL);
039a6658 1466
f0e11521 1467// Please help us by implementing any of the callbacks listed as TODO below. They already exist
1468// in x3, they just need handle_ bridges implemented. (see python_handle_join for an example)
0ab7b4bc 1469 reg_server_link_func(python_handle_server_link);
1470 reg_new_user_func(python_handle_new_user);
1471 reg_nick_change_func(python_handle_nick_change);
039a6658 1472//TODO: reg_del_user_func(python_handle_del_user);
1473//TODO: reg_account_func(python_handle_account); /* stamping of account name to the ircd */
1474//TODO: reg_handle_rename_func(python_handle_handle_rename); /* handle used to ALSO mean account name */
1475//TODO: reg_failpw_func(python_handle_failpw);
1476//TODO: reg_allowauth_func(python_handle_allowauth);
1477//TODO: reg_handle_merge_func(python_handle_merge);
1478//
1479//TODO: reg_oper_func(python_handle_oper);
1480//TODO: reg_new_channel_func(python_handle_new_channel);
caf97651 1481 reg_join_func(python_handle_join);
039a6658 1482//TODO: reg_del_channel_func(python_handle_del_channel);
1483//TODO: reg_part_func(python_handle_part);
1484//TODO: reg_kick_func(python_handle_kick);
1485//TODO: reg_topic_func(python_handle_topic);
1486//TODO: reg_channel_mode_func(python_handle_channel_mode);
1487
1488//TODO: reg_privmsg_func(python_handle_privmsg);
1489//TODO: reg_notice_func
1490//TODO: reg_svccmd_unbind_func(python_handle_svccmd_unbind);
1491//TODO: reg_chanmsg_func(python_handle_chanmsg);
1492//TODO: reg_allchanmsg_func
1493//TODO: reg_user_mode_func
1494
caf97651 1495 reg_exit_func(python_cleanup);
0b350353 1496
caf97651 1497 python_load();
0b350353 1498 return 1;
1499}
1500
1501#endif /* WITH_PYTHON */