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