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