]> jfr.im git - irc/evilnet/x3.git/blame - src/mod-python.c
mod-python: add emb_user_mode function
[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
b778caa0 865static PyObject* emb_channel_mode(UNUSED_ARG(PyObject* self_), PyObject* args) {
866 struct userNode* who;
867 struct chanNode* channel;
868 char const* modes;
869
870 char const* who_s, *channel_s;
871
872 if (!PyArg_ParseTuple(args, "sss", &who_s, &channel_s, &modes))
873 return NULL;
874
875 if ((who = GetUserH(who_s)) == NULL) {
876 PyErr_SetString(PyExc_Exception, "unknown user");
877 return NULL;
878 }
879
880 if (who->uplink != self) {
881 PyErr_SetString(PyExc_Exception, "user not on current server");
882 return NULL;
883 }
884
885 if ((channel = GetChannel(channel_s)) == NULL) {
886 PyErr_SetString(PyExc_Exception, "unknown channel");
887 return NULL;
888 }
889
890 irc_mode(who, channel, modes);
891
892 Py_INCREF(Py_None);
893 return Py_None;
894}
895
26d745b2 896static PyObject* emb_user_mode(UNUSED_ARG(PyObject* self), PyObject* args) {
897 struct userNode* target;
898 char const* modes;
899
900 char const* target_s;
901
902 if (!PyArg_ParseTuple(args, "ss", &target_s, &modes))
903 return NULL;
904
905 if ((target = GetUserH(target_s)) == NULL) {
906 PyErr_SetString(PyExc_Exception, "unknown user");
907 return NULL;
908 }
909
910 irc_umode(target, modes);
911
912 Py_INCREF(Py_None);
913 return Py_None;
914}
915
a2c8c575 916static PyMethodDef EmbMethods[] = {
07559983 917 /* Communication methods */
a2c8c575 918 {"dump", emb_dump, METH_VARARGS, "Dump raw P10 line to server"},
919 {"send_target_privmsg", emb_send_target_privmsg, METH_VARARGS, "Send a message to somewhere"},
d4e0f0c4 920 {"send_target_notice", emb_send_target_notice, METH_VARARGS, "Send a notice to somewhere"},
07559983 921 {"log_module", emb_log_module, METH_VARARGS, "Log something using the X3 log subsystem"},
039a6658 922//TODO: {"exec_cmd", emb_exec_cmd, METH_VARARGS, "execute x3 command provided"},
923// This should use environment from "python command" call to pass in, if available
0076604e 924 {"kill", emb_kill, METH_VARARGS, "Kill someone"},
039a6658 925//TODO: {"shun"
926//TODO: {"unshun"
927//TODO: {"gline", emb_gline, METH_VARARGS, "gline a mask"},
928//TODO: {"ungline", emb_ungline, METH_VARARGS, "remove a gline"},
749f0565 929 {"kick", emb_kick, METH_VARARGS, "kick someone from a channel"},
b778caa0 930 {"channel_mode", emb_channel_mode, METH_VARARGS, "set modes on a channel"},
26d745b2 931 {"user_mode", emb_user_mode, METH_VARARGS, "Have x3 set usermodes on one of its own nicks"},
039a6658 932//
2bee6a6e 933 {"get_config", emb_get_config, METH_VARARGS, "get x3.conf settings into a nested dict"},
039a6658 934//TODO: {"config_set", emb_config_set, METH_VARARGS, "change a config setting 'on-the-fly'."},
935//
8f206d22 936 {"timeq_add", emb_timeq_add, METH_VARARGS, "add function to callback to the event system"},
3f218269 937 {"timeq_del", emb_timeq_del, METH_VARARGS, "remove function to callback from the event system"},
938
07559983 939 /* Information gathering methods */
8d670803 940 {"get_user", emb_get_user, METH_VARARGS, "Get details about a nickname"},
f0e11521 941 {"get_users", emb_get_users, METH_VARARGS, "Get all connected users"},
8d670803 942 {"get_channel", emb_get_channel, METH_VARARGS, "Get details about a channel"},
f0e11521 943 {"get_channels", emb_get_channels, METH_VARARGS, "Get all channels"},
d8f8d3b6 944 {"get_server", emb_get_server, METH_VARARGS, "Get details about a server"},
dcc1df5e 945 {"get_servers", emb_get_servers, METH_VARARGS, "Get all server names"},
4c216694 946 {"get_account", emb_get_account, METH_VARARGS, "Get details about an account"},
39d37f27 947 {"get_accounts", emb_get_accounts, METH_VARARGS, "Get all nickserv accounts"},
039a6658 948 {"get_info", emb_get_info, METH_VARARGS, "Get various misc info about x3"},
07559983 949 /* null terminator */
a2c8c575 950 {NULL, NULL, 0, NULL}
951};
952
953
f0e11521 954/*
955These functions set up the embedded environment for us to call out to
956modpython.py class methods.
4c216694 957 */
cbfd323c 958
07559983 959void python_log_module() {
960 /* Attempt to convert python errors to x3 log system */
961 PyObject *exc, *tb, *value, *tmp;
962 char *str_exc = "NONE";
963 char *str_value = "NONE";
964 char *str_tb = "NONE";
965
966 PyErr_Fetch(&exc, &value, &tb);
967
968 if(exc) {
969 if((tmp = PyObject_Str(exc)))
970 str_exc = PyString_AsString(tmp);
971 }
972 if(value) {
973 if((tmp = PyObject_Str(value)))
974 str_value = PyString_AsString(tmp);
975 }
976 if(tb) {
977 if((tmp = PyObject_Str(tb)))
978 str_tb = PyString_AsString(tmp);
979 }
980
981 /* Now restore it so we can print it (just in case)
982 * (should we do this only when running in debug mode?) */
983 PyErr_Restore(exc, value, tb);
984 PyErr_Print(); /* which of course, clears it again.. */
985
986 log_module(PY_LOG, LOG_WARNING, "PYTHON error: %s, value: %s", str_exc, str_value);
987
988 /* TODO: get the traceback using the traceback module via C api so we can add it to the X3 logs. See
989 * http://mail.python.org/pipermail/python-list/2003-March/192226.html */
990 // (this doesnt work, str_tb is just an object hashid) log_module(PY_LOG, LOG_INFO, "PYTHON error, traceback: %s", str_tb);
991}
992
993
e0f76584 994PyObject *python_build_handler_args(size_t argc, char *args[], PyObject *pIrcObj) {
4c216694 995 /* Sets up a python tuple with passed in arguments, prefixed by the Irc instance
f0e11521 996 which handlers use to interact with C.
4c216694 997 argc = number of args
998 args = array of args
999 pIrcObj = instance of the irc class
1000 */
e0f76584 1001 size_t i = 0, n;
1002 PyObject *pArgs = NULL;
cbfd323c 1003
e0f76584 1004 pArgs = PyTuple_New(argc + 1);
1005 Py_INCREF(pIrcObj);
1006 PyTuple_SetItem(pArgs, i++, pIrcObj);
cbfd323c 1007
e0f76584 1008 if(args && argc) {
cbfd323c 1009 PyObject *pValue;
e0f76584 1010 for(n = 0; n < argc; ++n) {
1011 pValue = PyString_FromString(args[n]);
cbfd323c 1012 if(!pValue) {
e0f76584 1013 Py_DECREF(pArgs);
1014 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[n]);
1015 return NULL;
cbfd323c 1016 }
e0f76584 1017 PyTuple_SetItem(pArgs, n+i, pValue);
cbfd323c 1018 }
cbfd323c 1019 }
e0f76584 1020 return pArgs;
cbfd323c 1021}
1022
1023PyObject *python_build_args(size_t argc, char *args[]) {
4c216694 1024 /* Builds the passed in arguments into a python argument tuple.
1025 argc = number of args
1026 args = array of args
1027 */
cbfd323c 1028 size_t i;
1029 PyObject *pArgs = NULL;
1030
1031 if(args && argc) {
1032 pArgs = PyTuple_New(argc);
1033 PyObject *pValue;
1034 for(i = 0; i< argc; ++i) {
1035 pValue = PyString_FromString(args[i]);
1036 if(!pValue) {
1037 Py_DECREF(pArgs);
1038 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[i]);
1039 return NULL;
1040 }
1041 PyTuple_SetItem(pArgs, i, pValue);
1042 }
1043 }
1044 return pArgs;
d4e0f0c4 1045}
caf97651 1046
cbfd323c 1047
e0f76584 1048PyObject *new_irc_object(char *command_service, char *command_caller, char *command_target) {
4c216694 1049 /* Creates a new instance of the irc class (from modpython.py) which is initalized
1050 with current environment details like which service were talking to.
1051 command_service = which service we are talking to, or empty string if none
1052 command_caller = nick of user generating message, or empty string if none
1053 command_target = If were reacting to something on a channel, this will
1054 be set to the name of the channel. Otherwise empty
1055 */
e0f76584 1056 PyObject *pIrcArgs = NULL;
1057 PyObject *pIrcClass;
1058 PyObject *pIrcObj;
1059
4c216694 1060 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate irc class; %s %s %s", command_service, command_caller, command_target);
e0f76584 1061 pIrcClass = PyObject_GetAttrString(base_module, "irc");
1062 /* pIrcClass is a new reference */
1063 if(pIrcClass && PyCallable_Check(pIrcClass)) {
1064 //size_t i;
1065 char *ircargs[] = {command_service, command_caller, command_target};
1066 //PyObject *pValue;
1067
1068 pIrcArgs = python_build_args(3, ircargs);
e0f76584 1069 pIrcObj = PyObject_CallObject(pIrcClass, pIrcArgs);
1070 if(!pIrcObj) {
1071 log_module(PY_LOG, LOG_ERROR, "IRC Class failed to load");
07559983 1072 python_log_module();
1073 //PyErr_Print();
e0f76584 1074 }
1075 if(pIrcArgs != NULL) {
1076 Py_DECREF(pIrcArgs);
1077 }
4c216694 1078 Py_DECREF(pIrcClass);
e0f76584 1079 return pIrcObj;
1080 }
1081 else {
4c216694 1082 /* need to free pIrcClass here if it WAS found but was NOT callable? */
e0f76584 1083 log_module(PY_LOG, LOG_ERROR, "Unable to find irc class");
1084 return NULL;
1085 }
1086}
1087
cbfd323c 1088int python_call_handler(char *handler, char *args[], size_t argc, char *command_service, char *command_caller, char *command_target) {
4c216694 1089 /* This is how we talk to modpython.c. First a new instance of the irc class is created using these
1090 arguments to setup the current environment. Then the named method of the handler object is called
1091 with the givin arguments.
cbfd323c 1092 */
1093 PyObject *pIrcObj;
1094 PyObject *pArgs;
1095 PyObject *pMethod;
1096 PyObject *pValue;
1097
e0f76584 1098 log_module(PY_LOG, LOG_INFO, "attempting to call handler %s.", handler);
07559983 1099 if(base_module != NULL && handler_object != NULL) {
cbfd323c 1100 pIrcObj = new_irc_object(command_service, command_caller, command_target);
e0f76584 1101 if(!pIrcObj) {
1102 log_module(PY_LOG, LOG_INFO, "Can't get irc object. Bailing.");
1103 return 0;
1104 }
cbfd323c 1105
e0f76584 1106 pArgs = python_build_handler_args(argc, args, pIrcObj);
cbfd323c 1107 pMethod = PyObject_GetAttrString(handler_object, handler);
1108 if(pMethod && PyCallable_Check(pMethod)) {
07559983 1109 /* Call the method, with the arguments */
cbfd323c 1110 pValue = PyObject_CallObject(pMethod, pArgs);
1111 if(pArgs) {
1112 Py_DECREF(pArgs);
1113 }
1114 if(pValue != NULL) {
1115 int ret;
1116 ret = PyInt_AsLong(pValue);
1117 if(ret == -1 && PyErr_Occurred()) {
07559983 1118 //PyErr_Print();
cbfd323c 1119 log_module(PY_LOG, LOG_INFO, "error converting return value of handler %s to type long. ", handler);
07559983 1120 python_log_module();
cbfd323c 1121 ret = 0;
1122 }
1123 log_module(PY_LOG, LOG_INFO, "handler %s was run successfully, returned %d.", handler, ret);
cbfd323c 1124 Py_DECREF(pValue);
4c216694 1125 Py_DECREF(pIrcObj);
1126 Py_DECREF(pMethod);
cbfd323c 1127 return ret;
1128 }
1129 else {
cbfd323c 1130 /* TODO: instead of print errors, get them as strings
1131 * and deal with them with normal x3 log system. */
cbfd323c 1132 log_module(PY_LOG, LOG_WARNING, "call to handler %s failed", handler);
07559983 1133 //PyErr_Print();
1134 python_log_module();
4c216694 1135 Py_DECREF(pIrcObj);
1136 Py_DECREF(pMethod);
cbfd323c 1137 return 0;
1138 }
1139 }
1140 else { /* couldn't find handler methed */
4c216694 1141 Py_DECREF(pArgs);
1142 /* Free pMethod if it was found but not callable? */
cbfd323c 1143 log_module(PY_LOG, LOG_ERROR, "Cannot find handler %s.", handler);
1144 return 0;
1145
1146 }
1147 }
1148 else { /* No base module.. no python? */
e0f76584 1149 log_module(PY_LOG, LOG_INFO, "Cannot handle %s, Python is not initialized.", handler);
cbfd323c 1150 return 0;
1151 }
1152}
1153
1154PyObject *python_new_handler_object() {
4c216694 1155 /* Create a new instance of the handler class.
1156 This is called during python initilization (or reload)
1157 and the result is saved and re-used.
1158 */
cbfd323c 1159 PyObject *pHandlerClass, *pHandlerObj;
1160
1161 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate python class handler");
1162 pHandlerClass = PyObject_GetAttrString(base_module, "handler");
1163 /* Class is a new reference */
1164 if(pHandlerClass && PyCallable_Check(pHandlerClass)) {
e0f76584 1165 /*PyObject *pValue; */
cbfd323c 1166
1167 pHandlerObj = PyObject_CallObject(pHandlerClass, NULL);
07559983 1168 if(pHandlerObj != NULL) {
1169 log_module(PY_LOG, LOG_INFO, "Created new python handler object.");
1170 return pHandlerObj;
1171 }
1172 else {
1173 log_module(PY_LOG, LOG_ERROR, "Unable to instanciate handler object");
1174 //PyErr_Print();
1175 python_log_module();
1176 return NULL;
1177 }
cbfd323c 1178 }
1179 else {
1180 log_module(PY_LOG, LOG_ERROR, "Unable to find handler class");
07559983 1181 //PyErr_Print();
1182 python_log_module();
1183 if(pHandlerClass) {
1184 Py_DECREF(pHandlerClass);
1185 }
cbfd323c 1186 return NULL;
1187 }
1188}
1189
4c216694 1190/* ------------------------------------------------------------------------------- *
1191 Some gateway functions to convert x3 callbacks into modpython.py callbacks.
1192 Mostly we just build relevant args and call the proper handler method
1193
1194 debate: do we just register these and check them in python
1195 for every one (slow?) or actually work out if a plugin needs
1196 it first? We will start by doing it every time.
cbfd323c 1197 */
0b350353 1198static int
1199python_handle_join(struct modeNode *mNode)
1200{
4c216694 1201 /* callback for handle_join events.
1202 */
0b350353 1203 struct userNode *user = mNode->user;
1204 struct chanNode *channel = mNode->channel;
1205
a2c8c575 1206
caf97651 1207 log_module(PY_LOG, LOG_INFO, "python module handle_join");
a2c8c575 1208 if(!channel||!user) {
4c216694 1209 log_module(PY_LOG, LOG_WARNING, "Python code got join without channel or user!");
a2c8c575 1210 return 0;
1211 }
1212 else {
1213 char *args[] = {channel->name, user->nick};
e0f76584 1214 return python_call_handler("join", args, 2, "", "", "");
a2c8c575 1215 }
0b350353 1216}
1217
0ab7b4bc 1218static int
1219python_handle_server_link(struct server *server)
1220{
1221 log_module(PY_LOG, LOG_INFO, "python module handle_server_link");
1222 if(!server) {
1223 log_module(PY_LOG, LOG_WARNING, "Python code got server link without server!");
1224 return 0;
1225 }
1226 else {
1227 char *args[] = {server->name, server->description};
1228 return python_call_handler("server_link", args, 2, "", "", "");
1229 }
1230}
1231
1232static int
1233python_handle_new_user(struct userNode *user)
1234{
1235 log_module(PY_LOG, LOG_INFO, "Python module handle_new_user");
1236 if(!user) {
1237 log_module(PY_LOG, LOG_WARNING, "Python code got new_user without the user");
1238 return 0;
1239 }
1240 else {
1241 char *args[] = {user->nick, user->ident, user->hostname, user->info};
1242 return python_call_handler("new_user", args, 4, "", "", "");
1243 }
1244}
1245
1246static void
1247python_handle_nick_change(struct userNode *user, const char *old_nick)
1248{
1249 log_module(PY_LOG, LOG_INFO, "Python module handle_nick_change");
1250 if(!user) {
1251 log_module(PY_LOG, LOG_WARNING, "Python code got nick_change without the user!");
1252 }
1253 else {
1254 char *args[] = {user->nick, (char *)old_nick};
1255 python_call_handler("nick_change", args, 2, "", "", "");
1256 }
1257}
1258
4c216694 1259/* ----------------------------------------------------------------------------- */
1260
cbfd323c 1261
caf97651 1262int python_load() {
4c216694 1263 /* Init the python engine and do init work on modpython.py
1264 This is called during x3 startup, and on a python reload
1265 */
caf97651 1266 PyObject *pName;
ef5e0305 1267 char* buffer;
1268 char* env = getenv("PYTHONPATH");
1269
1270 if (env)
1271 env = strdup(env);
1272
1273 if (!env)
1274 setenv("PYTHONPATH", modpython_conf.scripts_dir, 1);
1275 else if (!strstr(env, modpython_conf.scripts_dir)) {
1276 buffer = (char*)malloc(strlen(env) + strlen(modpython_conf.scripts_dir) + 2);
1277 sprintf(buffer, "%s:%s", modpython_conf.scripts_dir, env);
1278 setenv("PYTHONPATH", buffer, 1);
1279 free(buffer);
1280 free(env);
1281 }
caf97651 1282
caf97651 1283 Py_Initialize();
50d61a79 1284 Py_InitModule("_svc", EmbMethods);
ed8d873c 1285 pName = PyString_FromString(modpython_conf.main_module);
caf97651 1286 base_module = PyImport_Import(pName);
1287 Py_DECREF(pName);
bc2f52df 1288
1289 Py_XDECREF(handler_object);
1290 handler_object = NULL;
1291
caf97651 1292 if(base_module != NULL) {
cbfd323c 1293 handler_object = python_new_handler_object();
1294 if(handler_object) {
e0f76584 1295 python_call_handler("init", NULL, 0, "", "", "");
cbfd323c 1296 return 1;
1297 }
1298 else {
1299 /* error handler class not found */
1300 log_module(PY_LOG, LOG_WARNING, "Failed to create handler object");
1301 return 0;
1302 }
caf97651 1303 }
1304 else {
07559983 1305 //PyErr_Print();
1306 python_log_module();
d4e0f0c4 1307 log_module(PY_LOG, LOG_WARNING, "Failed to load modpython.py");
a2c8c575 1308 return 0;
caf97651 1309 }
cbfd323c 1310 return 0;
caf97651 1311}
1312
caf97651 1313int
1314python_finalize(void) {
4c216694 1315 /* Called after X3 is fully up and running.
1316 Code can be put here that needs to run to init things, but
1317 which is sensitive to everything else in x3 being up and ready
1318 to go.
1319 */
caf97651 1320
1321 PyRun_SimpleString("print 'Hello, World of Python!'");
1322 log_module(PY_LOG, LOG_INFO, "python module finalize");
1323
1324 return 1;
1325}
1326
caf97651 1327static void
4c216694 1328python_cleanup(void) {
1329 /* Called on shutdown of the python module (or before reloading)
1330 */
413fd8ea 1331
caf97651 1332 log_module(PY_LOG, LOG_INFO, "python module cleanup");
413fd8ea 1333 if (PyErr_Occurred())
1334 PyErr_Clear();
f0e11521 1335 Py_Finalize(); /* Shut down python enterpreter */
caf97651 1336}
1337
4c216694 1338/* ---------------------------------------------------------------------------------- *
1339 Python module command handlers.
1340*/
caf97651 1341static MODCMD_FUNC(cmd_reload) {
4c216694 1342 /* reload the python system completely
1343 */
caf97651 1344 log_module(PY_LOG, LOG_INFO, "Shutting python down");
1345 python_cleanup();
1346 log_module(PY_LOG, LOG_INFO, "Loading python stuff");
1347 if(python_load()) {
a2c8c575 1348 reply("PYMSG_RELOAD_SUCCESS");
caf97651 1349 }
1350 else {
a2c8c575 1351 reply("PYMSG_RELOAD_FAILED");
caf97651 1352 }
1353 return 1;
1354}
1355
413fd8ea 1356static char* format_python_error(int space_nls) {
1357 PyObject* extype = NULL, *exvalue = NULL, *extraceback = NULL;
1358 PyObject* pextypestr = NULL, *pexvaluestr = NULL;
1359 char* extypestr = NULL, *exvaluestr = NULL;
1360 size_t retvallen = 0;
1361 char* retval = NULL, *tmp;
1362
1363 PyErr_Fetch(&extype, &exvalue, &extraceback);
1364 if (!extype)
1365 goto cleanup;
1366
1367 pextypestr = PyObject_Str(extype);
1368 if (!pextypestr)
1369 goto cleanup;
1370 extypestr = PyString_AsString(pextypestr);
1371 if (!extypestr)
1372 goto cleanup;
1373
1374 pexvaluestr = PyObject_Str(exvalue);
1375 if (pexvaluestr)
1376 exvaluestr = PyString_AsString(pexvaluestr);
1377
1378 retvallen = strlen(extypestr) + (exvaluestr ? strlen(exvaluestr) + 2 : 0) + 1;
1379 retval = (char*)malloc(retvallen);
1380 if (exvaluestr)
1381 snprintf(retval, retvallen, "%s: %s", extypestr, exvaluestr);
1382 else
1383 strncpy(retval, extypestr, retvallen);
1384
1385 if (space_nls) {
1386 tmp = retval;
1387 while (*tmp) {
1388 if (*tmp == '\n')
1389 *tmp = ' ';
1390 ++tmp;
1391 }
1392 }
1393
1394cleanup:
1395 if (PyErr_Occurred())
1396 PyErr_Clear(); /* ignore errors caused by formatting */
1397 Py_XDECREF(extype);
1398 Py_XDECREF(exvalue);
1399 Py_XDECREF(extraceback);
1400 Py_XDECREF(pextypestr);
1401 Py_XDECREF(pexvaluestr);
1402
1403 if (retval)
1404 return retval;
1405
1406 return strdup("unknown exception");
1407}
1408
8d670803 1409static MODCMD_FUNC(cmd_run) {
413fd8ea 1410 /* this method allows running arbitrary python commands.
1411 * use with care.
1412 */
1413 char* msg;
46f628b1 1414 PyObject* py_main_module;
1415 PyObject* py_globals;
1416 PyObject* py_locals;
1417 PyObject* py_retval;
1418 PyObject* extype, *exvalue, *extraceback;
1419 PyObject* exvaluestr = NULL;
1420 char* exmsg = NULL, *exmsgptr;
1421
1422 py_main_module = PyImport_AddModule("__main__");
1423 py_globals = py_locals = PyModule_GetDict(py_main_module);
413fd8ea 1424
a2c8c575 1425 msg = unsplit_string(argv + 1, argc - 1, NULL);
413fd8ea 1426
46f628b1 1427 py_retval = PyRun_String(msg, Py_file_input, py_globals, py_locals);
1428 if (py_retval == NULL) {
1429 PyErr_Fetch(&extype, &exvalue, &extraceback);
1430 if (exvalue != NULL) {
1431 exvaluestr = PyObject_Str(exvalue);
1432 exmsg = strdup(PyString_AS_STRING(exvaluestr));
1433 exmsgptr = exmsg;
1434 while (exmsgptr && *exmsgptr) {
1435 if (*exmsgptr == '\n' || *exmsgptr == '\r' || *exmsgptr == '\t')
1436 *exmsgptr = ' ';
1437 exmsgptr++;
1438 }
1439 }
1440 if (extype != NULL && exvalue != NULL && PyType_Check(extype)) {
1441 reply("PYMSG_RUN_EXCEPTION", ((PyTypeObject*)extype)->tp_name, exmsg);
413fd8ea 1442 } else
1443 reply("PYMSG_RUN_UNKNOWN_EXCEPTION");
46f628b1 1444
1445 if (extype != NULL)
1446 Py_DECREF(extype);
1447 if (exvalue != NULL)
1448 Py_DECREF(exvalue);
1449 if (extraceback != NULL)
1450 Py_DECREF(extraceback);
1451 if (exvaluestr != NULL)
1452 Py_DECREF(exvaluestr);
1453 if (exmsg)
1454 free(exmsg);
1455 } else {
1456 Py_DECREF(py_retval);
413fd8ea 1457 }
1458
a2c8c575 1459 return 1;
1460}
1461
07559983 1462#define numstrargs(X) sizeof(X) / sizeof(*X)
1463static MODCMD_FUNC(cmd_command) {
1464 char *plugin = argv[1];
1465 char *command = argv[2];
1466 char *msg; /* args */
1467 if(argc > 3) {
1468 msg = unsplit_string(argv + 3, argc - 3, NULL);
1469 }
1470 else {
1471 msg = "";
1472 }
1473 char *args[] = {plugin, command, msg};
1474 python_call_handler("cmd_command", args, numstrargs(args), cmd->parent->bot->nick, user?user->nick:"", channel?channel->name:"");
1475 return 1;
1476}
1477
ef5e0305 1478static void modpython_conf_read(void) {
1479 dict_t conf_node;
1480 char const* str;
1481
1482 if (!(conf_node = conf_get_data(MODPYTHON_CONF_NAME, RECDB_OBJECT))) {
1483 log_module(PY_LOG, LOG_ERROR, "config node '%s' is missing or has wrong type", MODPYTHON_CONF_NAME);
1484 return;
1485 }
1486
1487 str = database_get_data(conf_node, "scripts_dir", RECDB_QSTRING);
1488 modpython_conf.scripts_dir = str ? str : "./";
ed8d873c 1489
1490 str = database_get_data(conf_node, "main_module", RECDB_QSTRING);
1491 modpython_conf.main_module = str ? str : "modpython";
ef5e0305 1492}
1493
0b350353 1494int python_init(void) {
4c216694 1495 /* X3 calls this function on init of the module during startup. We use it to
1496 do all our setup tasks and bindings
1497 */
0b350353 1498
0b350353 1499 PY_LOG = log_register_type("Python", "file:python.log");
caf97651 1500 python_module = module_register("python", PY_LOG, "mod-python.help", NULL);
ef5e0305 1501 conf_register_reload(modpython_conf_read);
1502
caf97651 1503 log_module(PY_LOG, LOG_INFO, "python module init");
1504 message_register_table(msgtab);
1505
0b350353 1506/*
1507 reg_auth_func(python_check_messages);
1508 reg_handle_rename_func(python_rename_account);
1509 reg_unreg_func(python_unreg_account);
1510 conf_register_reload(python_conf_read);
0b350353 1511 saxdb_register("python", python_saxdb_read, python_saxdb_write);
0b350353 1512 modcmd_register(python_module, "send", cmd_send, 3, MODCMD_REQUIRE_AUTHED, NULL);
1513*/
caf97651 1514 modcmd_register(python_module, "reload", cmd_reload, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
8d670803 1515 modcmd_register(python_module, "run", cmd_run, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
07559983 1516 modcmd_register(python_module, "command", cmd_command, 3, MODCMD_REQUIRE_STAFF, NULL);
039a6658 1517
f0e11521 1518// Please help us by implementing any of the callbacks listed as TODO below. They already exist
1519// in x3, they just need handle_ bridges implemented. (see python_handle_join for an example)
0ab7b4bc 1520 reg_server_link_func(python_handle_server_link);
1521 reg_new_user_func(python_handle_new_user);
1522 reg_nick_change_func(python_handle_nick_change);
039a6658 1523//TODO: reg_del_user_func(python_handle_del_user);
1524//TODO: reg_account_func(python_handle_account); /* stamping of account name to the ircd */
1525//TODO: reg_handle_rename_func(python_handle_handle_rename); /* handle used to ALSO mean account name */
1526//TODO: reg_failpw_func(python_handle_failpw);
1527//TODO: reg_allowauth_func(python_handle_allowauth);
1528//TODO: reg_handle_merge_func(python_handle_merge);
1529//
1530//TODO: reg_oper_func(python_handle_oper);
1531//TODO: reg_new_channel_func(python_handle_new_channel);
caf97651 1532 reg_join_func(python_handle_join);
039a6658 1533//TODO: reg_del_channel_func(python_handle_del_channel);
1534//TODO: reg_part_func(python_handle_part);
1535//TODO: reg_kick_func(python_handle_kick);
1536//TODO: reg_topic_func(python_handle_topic);
1537//TODO: reg_channel_mode_func(python_handle_channel_mode);
1538
1539//TODO: reg_privmsg_func(python_handle_privmsg);
1540//TODO: reg_notice_func
1541//TODO: reg_svccmd_unbind_func(python_handle_svccmd_unbind);
1542//TODO: reg_chanmsg_func(python_handle_chanmsg);
1543//TODO: reg_allchanmsg_func
1544//TODO: reg_user_mode_func
1545
caf97651 1546 reg_exit_func(python_cleanup);
0b350353 1547
caf97651 1548 python_load();
0b350353 1549 return 1;
1550}
1551
1552#endif /* WITH_PYTHON */