]> jfr.im git - irc/evilnet/x3.git/blob - src/mod-python.c
mod-python: make emb_dump raise exception on failure
[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
26 #include <Python.h>
27 #include "chanserv.h"
28 #include "conf.h"
29 #include "modcmd.h"
30 #include "nickserv.h"
31 #include "opserv.h"
32 #include "saxdb.h"
33 #include "mail.h"
34 #include "timeq.h"
35 #include "compat.h"
36
37 /* TODO notes
38 *
39 * - Impliment most of proto-p10 irc_* commands for calling from scripts
40 * - Impliment functions to look up whois, channel, account, and reg-channel info for scripts
41 * - Impliment x3.conf settings for python variables like include path, etc.
42 * - modpython.py calls for everything you can reg_ a handler for in x3
43 * - Some kind of system for getting needed binds bound automagicaly to make it easier
44 * to run peoples scripts and mod-python in general.
45 * - An interface to reading/writing data to x3.db. Maybe generic, or attached to account or channel reg records?
46 */
47
48 static const struct message_entry msgtab[] = {
49 { "PYMSG_RELOAD_SUCCESS", "Reloaded Python scripts successfully." },
50 { "PYMSG_RELOAD_FAILED", "Error reloading Python scripts." },
51 { "PYMSG_RUN_UNKNOWN_EXCEPTION", "Error running python: unknown exception." },
52 { "PYMSG_RUN_EXCEPTION", "Error running python: %s: %s." },
53 { NULL, NULL } /* sentenal */
54 };
55
56 #define MODPYTHON_CONF_NAME "modules/python"
57
58 static
59 struct {
60 char const* scripts_dir;
61 char const* main_module;
62 } modpython_conf;
63
64 static struct log_type *PY_LOG;
65 const char *python_module_deps[] = { NULL };
66 static struct module *python_module;
67
68 PyObject *base_module = NULL; /* Base python handling library */
69 PyObject *handler_object = NULL; /* instanciation of handler class */
70
71
72 extern struct userNode *global, *chanserv, *opserv, *nickserv, *spamserv;
73
74 /* ---------------------------------------------------------------------- *
75 Some hooks you can call from modpython.py to interact with the
76 service, and IRC. These emb_* functions are available as _svc.*
77 in python.
78 */
79
80 static PyObject*
81 emb_dump(UNUSED_ARG(PyObject *self), PyObject *args)
82 {
83 /* Dump a raw string into the socket
84 usage: _svc.dump(<P10 string>)
85 */
86 char *buf;
87 int ret = 0;
88 char linedup[MAXLEN];
89
90
91 if(!PyArg_ParseTuple(args, "s:dump", &buf ))
92 return NULL;
93
94 safestrncpy(linedup, buf, sizeof(linedup));
95
96 if(parse_line(linedup, 1)) {
97 irc_raw(buf);
98 ret = 1;
99 } else {
100 PyErr_SetString(PyExc_Exception, "invalid protocol message");
101 return NULL;
102 }
103
104 return Py_BuildValue("i", ret);
105 }
106
107 static PyObject*
108 emb_send_target_privmsg(UNUSED_ARG(PyObject *self), PyObject *args)
109 {
110 /* Send a privmsg
111 usage: _svc.send_target_privmsg(<servicenick_from>, <nick_to>, <message>)
112 */
113 int ret = 0;
114 char *servicenick;
115 char *channel;
116 char *buf;
117
118 struct service *service;
119
120
121 if(!PyArg_ParseTuple(args, "sss:reply", &servicenick, &channel, &buf ))
122 return NULL;
123 if(!(service = service_find(servicenick))) {
124 /* TODO: generate python exception here */
125 return NULL;
126 }
127 send_target_message(5, channel, service->bot, "%s", buf);
128 return Py_BuildValue("i", ret);
129 }
130
131 static PyObject*
132 emb_send_target_notice(UNUSED_ARG(PyObject *self), PyObject *args)
133 {
134 /* send a notice
135 usage: _svc.send_target_notice(<servicenick_from>, <nick_to>, <message>)
136 */
137 int ret = 0;
138 char *servicenick;
139 char *target;
140 char *buf;
141
142 struct service *service;
143
144
145 if(!PyArg_ParseTuple(args, "sss:reply", &servicenick, &target, &buf ))
146 return NULL;
147 if(!(service = service_find(servicenick))) {
148 /* TODO: generate python exception here */
149 return NULL;
150 }
151 send_target_message(4, target, service->bot, "%s", buf);
152 return Py_BuildValue("i", ret);
153 }
154
155 static PyObject*
156 pyobj_from_usernode(struct userNode* user) {
157 unsigned int n;
158 struct modeNode *mn;
159 PyObject* pChanList = PyTuple_New(user->channels.used);
160
161 for (n=0; n < user->channels.used; n++) {
162 mn = user->channels.list[n];
163 PyTuple_SetItem(pChanList, n, Py_BuildValue("s", mn->channel->name));
164 }
165
166 return Py_BuildValue("{"
167 "s: s, " /* nick */
168 "s: s, " /* ident */
169 "s: s, " /* info */
170 "s: s, " /* hostname */
171 "s: s, " /* ip */
172 "s: s, " /* fakehost */
173 "s: s, " /* sethost */
174 "s: s, " /* crypthost */
175 "s: s, " /* cryptip */
176 #ifdef WITH_PROTOCOL_P10
177 "s: s, " /* numeric */
178 #endif /* WITH_PROTOCOL_P10 */
179 "s: i, " /* loc */
180 "s: i, " /* no_notice */
181 "s: s, " /* mark */
182 "s: s, " /* version_reply */
183 "s: s, " /* account */
184 "s: O}", /* channels */
185 "nick", user->nick,
186 "ident", user->ident,
187 "info", user->info,
188 "hostname", user->hostname,
189 "ip", irc_ntoa(&user->ip),
190 "fakehost", user->fakehost,
191 "sethost", user->sethost,
192 "crypthost", user->crypthost,
193 "cryptip", user->cryptip,
194 #ifdef WITH_PROTOCOL_P10
195 "numeric", user->numeric,
196 #endif /* WITH_PROTOCOL_P10 */
197 "loc", user->loc,
198 "no_notice", user->no_notice,
199 "mark", user->mark,
200 "version_reply", user->version_reply,
201 "account", user->handle_info ? user->handle_info->handle : NULL,
202 "channels", pChanList);
203 }
204
205 static PyObject*
206 emb_get_user(UNUSED_ARG(PyObject *self), PyObject *args)
207 {
208 /* Get a python object containing everything x3 knows about a user, by nick.
209 usage: _svc.get_user(<nick>)
210 */
211 char const* nick;
212 struct userNode *user;
213
214 if(!PyArg_ParseTuple(args, "s", &nick))
215 return NULL;
216
217 if(!(user = GetUserH(nick))) {
218 PyErr_SetString(PyExc_Exception, "no such user");
219 return NULL;
220 }
221
222 return pyobj_from_usernode(user);
223 }
224
225 static PyObject*
226 emb_get_channel(UNUSED_ARG(PyObject *self), PyObject *args)
227 {
228 /* Returns a python dict object with all sorts of info about a channel.
229 usage: _svc.get_channel(<name>)
230 */
231 char *name;
232 struct chanNode *channel;
233 unsigned int n;
234 PyObject *pChannelMembers;
235 PyObject *pChannelBans;
236 PyObject *pChannelExempts;
237
238
239 if(!PyArg_ParseTuple(args, "s", &name))
240 return NULL;
241 if(!(channel = GetChannel(name))) {
242 /* TODO: generate py exception here */
243 return NULL;
244 }
245
246 /* build tuple of nicks in channel */
247 pChannelMembers = PyTuple_New(channel->members.used);
248 for(n=0;n < channel->members.used;n++) {
249 struct modeNode *mn = channel->members.list[n];
250 PyTuple_SetItem(pChannelMembers, n, Py_BuildValue("s", mn->user->nick));
251 }
252
253 /* build tuple of bans */
254 pChannelBans = PyTuple_New(channel->banlist.used);
255 for(n=0; n < channel->banlist.used;n++) {
256 struct banNode *bn = channel->banlist.list[n];
257 PyTuple_SetItem(pChannelBans, n,
258 Py_BuildValue("{s:s,s:s,s:i}",
259 "ban", bn->ban,
260 "who", bn->who,
261 "set", bn->set)
262 );
263 }
264
265 /* build tuple of exempts */
266 pChannelExempts = PyTuple_New(channel->exemptlist.used);
267 for(n=0; n < channel->exemptlist.used;n++) {
268 struct exemptNode *en = channel->exemptlist.list[n];
269 PyTuple_SetItem(pChannelExempts, n,
270 Py_BuildValue("{s:s,s:s,s:i}",
271 "ban", en->exempt,
272 "who", en->who,
273 "set", en->set)
274 );
275 }
276
277 return Py_BuildValue("{s:s,s:s,s:s,s:i"
278 ",s:i,s:i,s:O,s:O,s:O}",
279
280 "name", channel->name,
281 "topic", channel->topic,
282 "topic_nick", channel->topic_nick,
283 "topic_time", channel->topic_time,
284
285 "timestamp", channel->timestamp,
286 "modes", channel->modes,
287 "members", pChannelMembers,
288 "bans", pChannelBans,
289 "exempts", pChannelExempts
290 );
291 }
292
293 static PyObject*
294 emb_get_account(UNUSED_ARG(PyObject *self), PyObject *args)
295 {
296 /* Returns a python dict object with all sorts of info about an account.
297 usage: _svc.get_account(<account name>)
298 */
299 char *name;
300 struct handle_info *hi;
301
302
303 if(!PyArg_ParseTuple(args, "s", &name))
304 return NULL;
305
306 hi = get_handle_info(name);
307 if(!hi) {
308 return NULL;
309 }
310 return Py_BuildValue("{s:s,s:i,s:s,s:s,s:s"
311 ",s:s,s:s}",
312
313 "account", hi->handle,
314 "registered", hi->registered,
315 "last_seen", hi->lastseen,
316 "infoline", hi->infoline ? hi->infoline : "",
317 "email", hi->email_addr ? hi->email_addr : "",
318
319 "fakehost", hi->fakehost ? hi->fakehost : "",
320 "last_quit_host", hi->last_quit_host
321
322 /* TODO: */
323 /* users online authed to this account */
324 /* cookies */
325 /* nicks (nickserv nets only?) */
326 /* masks */
327 /* ignores */
328 /* channels */
329 );
330 }
331
332 static PyObject*
333 emb_get_info(UNUSED_ARG(PyObject *self), UNUSED_ARG(PyObject *args))
334 {
335 /* return some info about the general setup
336 * of X3, such as what the chanserv's nickname
337 * is.
338 */
339
340
341 return Py_BuildValue("{s:s,s:s,s:s,s:s,s:s}",
342 "chanserv", chanserv? chanserv->nick : "ChanServ",
343 "nickserv", nickserv?nickserv->nick : "NickServ",
344 "opserv", opserv?opserv->nick : "OpServ",
345 "global", global?global->nick : "Global",
346 "spamserv", spamserv?spamserv->nick : "SpamServ");
347 }
348
349 static PyObject*
350 emb_log_module(UNUSED_ARG(PyObject *self), PyObject *args)
351 {
352 /* a gateway to standard X3 logging subsystem.
353 * level is a value 0 to 9 as defined by the log_severity enum in log.h.
354 * LOG_INFO is 3, LOG_WARNING is 6, LOG_ERROR is 7.
355 *
356 * for now, all logs go to the PY_LOG log. In the future this will change.
357 */
358 char *message;
359 int ret = 0;
360 int level;
361
362
363 if(!PyArg_ParseTuple(args, "is", &level, &message))
364 return NULL;
365
366 log_module(PY_LOG, level, "%s", message);
367
368 return Py_BuildValue("i", ret);
369 }
370
371 static PyMethodDef EmbMethods[] = {
372 /* Communication methods */
373 {"dump", emb_dump, METH_VARARGS, "Dump raw P10 line to server"},
374 {"send_target_privmsg", emb_send_target_privmsg, METH_VARARGS, "Send a message to somewhere"},
375 {"send_target_notice", emb_send_target_notice, METH_VARARGS, "Send a notice to somewhere"},
376 {"log_module", emb_log_module, METH_VARARGS, "Log something using the X3 log subsystem"},
377 //TODO: {"exec_cmd", emb_exec_cmd, METH_VARARGS, "execute x3 command provided"},
378 // This should use environment from "python command" call to pass in, if available
379 //TODO: {"kill"
380 //TODO: {"shun"
381 //TODO: {"unshun"
382 //TODO: {"gline", emb_gline, METH_VARARGS, "gline a mask"},
383 //TODO: {"ungline", emb_ungline, METH_VARARGS, "remove a gline"},
384 //TODO: {"kick", emb_kick, METH_VARARGS, "kick someone from a channel"},
385 //TODO: {"channel_mode", emb_channel_mode, METH_VARARGS, "set modes on a channel"},
386 //TODO: {"user_mode", emb_user_mode, METH_VARARGS, "Have x3 set usermodes on one of its own nicks"},
387 //
388 //TODO: {"get_config", emb_get_config, METH_VARARGS, "get x3.conf settings into a nested dict"},
389 //TODO: {"config_set", emb_config_set, METH_VARARGS, "change a config setting 'on-the-fly'."},
390 //
391 //TODO: {"timeq_add", emb_timeq_new, METH_VARARGS, "some kind of interface to the timed event system."},
392 //TODO: {"timeq_del", emb_timeq_new, METH_VARARGS, "some kind of interface to the timed event system."},
393 /* Information gathering methods */
394 {"get_user", emb_get_user, METH_VARARGS, "Get details about a nickname"},
395 {"get_channel", emb_get_channel, METH_VARARGS, "Get details about a channel"},
396 {"get_account", emb_get_account, METH_VARARGS, "Get details about an account"},
397 {"get_info", emb_get_info, METH_VARARGS, "Get various misc info about x3"},
398 /* null terminator */
399 {NULL, NULL, 0, NULL}
400 };
401
402
403 /* ------------------------------------------------------------------------------------------------ *
404 Thes functions set up the embedded environment for us to call out to modpython.py class
405 methods.
406 */
407
408 void python_log_module() {
409 /* Attempt to convert python errors to x3 log system */
410 PyObject *exc, *tb, *value, *tmp;
411 char *str_exc = "NONE";
412 char *str_value = "NONE";
413 char *str_tb = "NONE";
414
415 PyErr_Fetch(&exc, &value, &tb);
416
417 if(exc) {
418 if((tmp = PyObject_Str(exc)))
419 str_exc = PyString_AsString(tmp);
420 }
421 if(value) {
422 if((tmp = PyObject_Str(value)))
423 str_value = PyString_AsString(tmp);
424 }
425 if(tb) {
426 if((tmp = PyObject_Str(tb)))
427 str_tb = PyString_AsString(tmp);
428 }
429
430 /* Now restore it so we can print it (just in case)
431 * (should we do this only when running in debug mode?) */
432 PyErr_Restore(exc, value, tb);
433 PyErr_Print(); /* which of course, clears it again.. */
434
435 log_module(PY_LOG, LOG_WARNING, "PYTHON error: %s, value: %s", str_exc, str_value);
436
437 /* TODO: get the traceback using the traceback module via C api so we can add it to the X3 logs. See
438 * http://mail.python.org/pipermail/python-list/2003-March/192226.html */
439 // (this doesnt work, str_tb is just an object hashid) log_module(PY_LOG, LOG_INFO, "PYTHON error, traceback: %s", str_tb);
440 }
441
442
443 PyObject *python_build_handler_args(size_t argc, char *args[], PyObject *pIrcObj) {
444 /* Sets up a python tuple with passed in arguments, prefixed by the Irc instance
445 which handlers use to interact with c.
446 argc = number of args
447 args = array of args
448 pIrcObj = instance of the irc class
449 */
450 size_t i = 0, n;
451 PyObject *pArgs = NULL;
452
453 pArgs = PyTuple_New(argc + 1);
454 Py_INCREF(pIrcObj);
455 PyTuple_SetItem(pArgs, i++, pIrcObj);
456
457 if(args && argc) {
458 PyObject *pValue;
459 for(n = 0; n < argc; ++n) {
460 pValue = PyString_FromString(args[n]);
461 if(!pValue) {
462 Py_DECREF(pArgs);
463 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[n]);
464 return NULL;
465 }
466 PyTuple_SetItem(pArgs, n+i, pValue);
467 }
468 }
469 return pArgs;
470 }
471
472 PyObject *python_build_args(size_t argc, char *args[]) {
473 /* Builds the passed in arguments into a python argument tuple.
474 argc = number of args
475 args = array of args
476 */
477 size_t i;
478 PyObject *pArgs = NULL;
479
480 if(args && argc) {
481 pArgs = PyTuple_New(argc);
482 PyObject *pValue;
483 for(i = 0; i< argc; ++i) {
484 pValue = PyString_FromString(args[i]);
485 if(!pValue) {
486 Py_DECREF(pArgs);
487 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[i]);
488 return NULL;
489 }
490 PyTuple_SetItem(pArgs, i, pValue);
491 }
492 }
493 return pArgs;
494 }
495
496
497 PyObject *new_irc_object(char *command_service, char *command_caller, char *command_target) {
498 /* Creates a new instance of the irc class (from modpython.py) which is initalized
499 with current environment details like which service were talking to.
500 command_service = which service we are talking to, or empty string if none
501 command_caller = nick of user generating message, or empty string if none
502 command_target = If were reacting to something on a channel, this will
503 be set to the name of the channel. Otherwise empty
504 */
505 PyObject *pIrcArgs = NULL;
506 PyObject *pIrcClass;
507 PyObject *pIrcObj;
508
509 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate irc class; %s %s %s", command_service, command_caller, command_target);
510 pIrcClass = PyObject_GetAttrString(base_module, "irc");
511 /* pIrcClass is a new reference */
512 if(pIrcClass && PyCallable_Check(pIrcClass)) {
513 //size_t i;
514 char *ircargs[] = {command_service, command_caller, command_target};
515 //PyObject *pValue;
516
517 pIrcArgs = python_build_args(3, ircargs);
518 pIrcObj = PyObject_CallObject(pIrcClass, pIrcArgs);
519 if(!pIrcObj) {
520 log_module(PY_LOG, LOG_ERROR, "IRC Class failed to load");
521 python_log_module();
522 //PyErr_Print();
523 }
524 if(pIrcArgs != NULL) {
525 Py_DECREF(pIrcArgs);
526 }
527 Py_DECREF(pIrcClass);
528 return pIrcObj;
529 }
530 else {
531 /* need to free pIrcClass here if it WAS found but was NOT callable? */
532 log_module(PY_LOG, LOG_ERROR, "Unable to find irc class");
533 return NULL;
534 }
535 }
536
537 int python_call_handler(char *handler, char *args[], size_t argc, char *command_service, char *command_caller, char *command_target) {
538 /* This is how we talk to modpython.c. First a new instance of the irc class is created using these
539 arguments to setup the current environment. Then the named method of the handler object is called
540 with the givin arguments.
541 */
542 PyObject *pIrcObj;
543 PyObject *pArgs;
544 PyObject *pMethod;
545 PyObject *pValue;
546
547 log_module(PY_LOG, LOG_INFO, "attempting to call handler %s.", handler);
548 if(base_module != NULL && handler_object != NULL) {
549 pIrcObj = new_irc_object(command_service, command_caller, command_target);
550 if(!pIrcObj) {
551 log_module(PY_LOG, LOG_INFO, "Can't get irc object. Bailing.");
552 return 0;
553 }
554
555 pArgs = python_build_handler_args(argc, args, pIrcObj);
556 pMethod = PyObject_GetAttrString(handler_object, handler);
557 if(pMethod && PyCallable_Check(pMethod)) {
558 /* Call the method, with the arguments */
559 pValue = PyObject_CallObject(pMethod, pArgs);
560 if(pArgs) {
561 Py_DECREF(pArgs);
562 }
563 if(pValue != NULL) {
564 int ret;
565 ret = PyInt_AsLong(pValue);
566 if(ret == -1 && PyErr_Occurred()) {
567 //PyErr_Print();
568 log_module(PY_LOG, LOG_INFO, "error converting return value of handler %s to type long. ", handler);
569 python_log_module();
570 ret = 0;
571 }
572 log_module(PY_LOG, LOG_INFO, "handler %s was run successfully, returned %d.", handler, ret);
573 Py_DECREF(pValue);
574 Py_DECREF(pIrcObj);
575 Py_DECREF(pMethod);
576 return ret;
577 }
578 else {
579 /* TODO: instead of print errors, get them as strings
580 * and deal with them with normal x3 log system. */
581 log_module(PY_LOG, LOG_WARNING, "call to handler %s failed", handler);
582 //PyErr_Print();
583 python_log_module();
584 Py_DECREF(pIrcObj);
585 Py_DECREF(pMethod);
586 return 0;
587 }
588 }
589 else { /* couldn't find handler methed */
590 Py_DECREF(pArgs);
591 /* Free pMethod if it was found but not callable? */
592 log_module(PY_LOG, LOG_ERROR, "Cannot find handler %s.", handler);
593 return 0;
594
595 }
596 }
597 else { /* No base module.. no python? */
598 log_module(PY_LOG, LOG_INFO, "Cannot handle %s, Python is not initialized.", handler);
599 return 0;
600 }
601 }
602
603 PyObject *python_new_handler_object() {
604 /* Create a new instance of the handler class.
605 This is called during python initilization (or reload)
606 and the result is saved and re-used.
607 */
608 PyObject *pHandlerClass, *pHandlerObj;
609
610 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate python class handler");
611 pHandlerClass = PyObject_GetAttrString(base_module, "handler");
612 /* Class is a new reference */
613 if(pHandlerClass && PyCallable_Check(pHandlerClass)) {
614 /*PyObject *pValue; */
615
616 pHandlerObj = PyObject_CallObject(pHandlerClass, NULL);
617 if(pHandlerObj != NULL) {
618 log_module(PY_LOG, LOG_INFO, "Created new python handler object.");
619 return pHandlerObj;
620 }
621 else {
622 log_module(PY_LOG, LOG_ERROR, "Unable to instanciate handler object");
623 //PyErr_Print();
624 python_log_module();
625 return NULL;
626 }
627 }
628 else {
629 log_module(PY_LOG, LOG_ERROR, "Unable to find handler class");
630 //PyErr_Print();
631 python_log_module();
632 if(pHandlerClass) {
633 Py_DECREF(pHandlerClass);
634 }
635 return NULL;
636 }
637 }
638
639 /* ------------------------------------------------------------------------------- *
640 Some gateway functions to convert x3 callbacks into modpython.py callbacks.
641 Mostly we just build relevant args and call the proper handler method
642
643 debate: do we just register these and check them in python
644 for every one (slow?) or actually work out if a plugin needs
645 it first? We will start by doing it every time.
646 */
647 static int
648 python_handle_join(struct modeNode *mNode)
649 {
650 /* callback for handle_join events.
651 */
652 struct userNode *user = mNode->user;
653 struct chanNode *channel = mNode->channel;
654
655
656 log_module(PY_LOG, LOG_INFO, "python module handle_join");
657 if(!channel||!user) {
658 log_module(PY_LOG, LOG_WARNING, "Python code got join without channel or user!");
659 return 0;
660 }
661 else {
662 char *args[] = {channel->name, user->nick};
663 return python_call_handler("join", args, 2, "", "", "");
664 }
665 }
666
667 static int
668 python_handle_server_link(struct server *server)
669 {
670 log_module(PY_LOG, LOG_INFO, "python module handle_server_link");
671 if(!server) {
672 log_module(PY_LOG, LOG_WARNING, "Python code got server link without server!");
673 return 0;
674 }
675 else {
676 char *args[] = {server->name, server->description};
677 return python_call_handler("server_link", args, 2, "", "", "");
678 }
679 }
680
681 static int
682 python_handle_new_user(struct userNode *user)
683 {
684 log_module(PY_LOG, LOG_INFO, "Python module handle_new_user");
685 if(!user) {
686 log_module(PY_LOG, LOG_WARNING, "Python code got new_user without the user");
687 return 0;
688 }
689 else {
690 char *args[] = {user->nick, user->ident, user->hostname, user->info};
691 return python_call_handler("new_user", args, 4, "", "", "");
692 }
693 }
694
695 static void
696 python_handle_nick_change(struct userNode *user, const char *old_nick)
697 {
698 log_module(PY_LOG, LOG_INFO, "Python module handle_nick_change");
699 if(!user) {
700 log_module(PY_LOG, LOG_WARNING, "Python code got nick_change without the user!");
701 }
702 else {
703 char *args[] = {user->nick, (char *)old_nick};
704 python_call_handler("nick_change", args, 2, "", "", "");
705 }
706 }
707
708 /* ----------------------------------------------------------------------------- */
709
710
711 int python_load() {
712 /* Init the python engine and do init work on modpython.py
713 This is called during x3 startup, and on a python reload
714 */
715 PyObject *pName;
716 char* buffer;
717 char* env = getenv("PYTHONPATH");
718
719 if (env)
720 env = strdup(env);
721
722 if (!env)
723 setenv("PYTHONPATH", modpython_conf.scripts_dir, 1);
724 else if (!strstr(env, modpython_conf.scripts_dir)) {
725 buffer = (char*)malloc(strlen(env) + strlen(modpython_conf.scripts_dir) + 2);
726 sprintf(buffer, "%s:%s", modpython_conf.scripts_dir, env);
727 setenv("PYTHONPATH", buffer, 1);
728 free(buffer);
729 free(env);
730 }
731
732 Py_Initialize();
733 Py_InitModule("_svc", EmbMethods);
734 pName = PyString_FromString(modpython_conf.main_module);
735 base_module = PyImport_Import(pName);
736 Py_DECREF(pName);
737
738 Py_XDECREF(handler_object);
739 handler_object = NULL;
740
741 if(base_module != NULL) {
742 handler_object = python_new_handler_object();
743 if(handler_object) {
744 python_call_handler("init", NULL, 0, "", "", "");
745 return 1;
746 }
747 else {
748 /* error handler class not found */
749 log_module(PY_LOG, LOG_WARNING, "Failed to create handler object");
750 return 0;
751 }
752 }
753 else {
754 //PyErr_Print();
755 python_log_module();
756 log_module(PY_LOG, LOG_WARNING, "Failed to load modpython.py");
757 return 0;
758 }
759 return 0;
760 }
761
762 int
763 python_finalize(void) {
764 /* Called after X3 is fully up and running.
765 Code can be put here that needs to run to init things, but
766 which is sensitive to everything else in x3 being up and ready
767 to go.
768 */
769
770 PyRun_SimpleString("print 'Hello, World of Python!'");
771 log_module(PY_LOG, LOG_INFO, "python module finalize");
772
773 return 1;
774 }
775
776 static void
777 python_cleanup(void) {
778 /* Called on shutdown of the python module (or before reloading)
779 */
780
781 log_module(PY_LOG, LOG_INFO, "python module cleanup");
782 if (PyErr_Occurred())
783 PyErr_Clear();
784 Py_Finalize(); /* Shut down python enterpriter */
785 }
786
787 /* ---------------------------------------------------------------------------------- *
788 Python module command handlers.
789 */
790 static MODCMD_FUNC(cmd_reload) {
791 /* reload the python system completely
792 */
793 log_module(PY_LOG, LOG_INFO, "Shutting python down");
794 python_cleanup();
795 log_module(PY_LOG, LOG_INFO, "Loading python stuff");
796 if(python_load()) {
797 reply("PYMSG_RELOAD_SUCCESS");
798 }
799 else {
800 reply("PYMSG_RELOAD_FAILED");
801 }
802 return 1;
803 }
804
805 static char* format_python_error(int space_nls) {
806 PyObject* extype = NULL, *exvalue = NULL, *extraceback = NULL;
807 PyObject* pextypestr = NULL, *pexvaluestr = NULL;
808 char* extypestr = NULL, *exvaluestr = NULL;
809 size_t retvallen = 0;
810 char* retval = NULL, *tmp;
811
812 PyErr_Fetch(&extype, &exvalue, &extraceback);
813 if (!extype)
814 goto cleanup;
815
816 pextypestr = PyObject_Str(extype);
817 if (!pextypestr)
818 goto cleanup;
819 extypestr = PyString_AsString(pextypestr);
820 if (!extypestr)
821 goto cleanup;
822
823 pexvaluestr = PyObject_Str(exvalue);
824 if (pexvaluestr)
825 exvaluestr = PyString_AsString(pexvaluestr);
826
827 retvallen = strlen(extypestr) + (exvaluestr ? strlen(exvaluestr) + 2 : 0) + 1;
828 retval = (char*)malloc(retvallen);
829 if (exvaluestr)
830 snprintf(retval, retvallen, "%s: %s", extypestr, exvaluestr);
831 else
832 strncpy(retval, extypestr, retvallen);
833
834 if (space_nls) {
835 tmp = retval;
836 while (*tmp) {
837 if (*tmp == '\n')
838 *tmp = ' ';
839 ++tmp;
840 }
841 }
842
843 cleanup:
844 if (PyErr_Occurred())
845 PyErr_Clear(); /* ignore errors caused by formatting */
846 Py_XDECREF(extype);
847 Py_XDECREF(exvalue);
848 Py_XDECREF(extraceback);
849 Py_XDECREF(pextypestr);
850 Py_XDECREF(pexvaluestr);
851
852 if (retval)
853 return retval;
854
855 return strdup("unknown exception");
856 }
857
858 static MODCMD_FUNC(cmd_run) {
859 /* this method allows running arbitrary python commands.
860 * use with care.
861 */
862 char* msg;
863 PyObject* py_main_module;
864 PyObject* py_globals;
865 PyObject* py_locals;
866 PyObject* py_retval;
867 PyObject* extype, *exvalue, *extraceback;
868 PyObject* exvaluestr = NULL;
869 char* exmsg = NULL, *exmsgptr;
870
871 py_main_module = PyImport_AddModule("__main__");
872 py_globals = py_locals = PyModule_GetDict(py_main_module);
873
874 msg = unsplit_string(argv + 1, argc - 1, NULL);
875
876 py_retval = PyRun_String(msg, Py_file_input, py_globals, py_locals);
877 if (py_retval == NULL) {
878 PyErr_Fetch(&extype, &exvalue, &extraceback);
879 if (exvalue != NULL) {
880 exvaluestr = PyObject_Str(exvalue);
881 exmsg = strdup(PyString_AS_STRING(exvaluestr));
882 exmsgptr = exmsg;
883 while (exmsgptr && *exmsgptr) {
884 if (*exmsgptr == '\n' || *exmsgptr == '\r' || *exmsgptr == '\t')
885 *exmsgptr = ' ';
886 exmsgptr++;
887 }
888 }
889 if (extype != NULL && exvalue != NULL && PyType_Check(extype)) {
890 reply("PYMSG_RUN_EXCEPTION", ((PyTypeObject*)extype)->tp_name, exmsg);
891 } else
892 reply("PYMSG_RUN_UNKNOWN_EXCEPTION");
893
894 if (extype != NULL)
895 Py_DECREF(extype);
896 if (exvalue != NULL)
897 Py_DECREF(exvalue);
898 if (extraceback != NULL)
899 Py_DECREF(extraceback);
900 if (exvaluestr != NULL)
901 Py_DECREF(exvaluestr);
902 if (exmsg)
903 free(exmsg);
904 } else {
905 Py_DECREF(py_retval);
906 }
907
908 return 1;
909 }
910
911 #define numstrargs(X) sizeof(X) / sizeof(*X)
912 static MODCMD_FUNC(cmd_command) {
913 char *plugin = argv[1];
914 char *command = argv[2];
915 char *msg; /* args */
916 if(argc > 3) {
917 msg = unsplit_string(argv + 3, argc - 3, NULL);
918 }
919 else {
920 msg = "";
921 }
922 char *args[] = {plugin, command, msg};
923 python_call_handler("cmd_command", args, numstrargs(args), cmd->parent->bot->nick, user?user->nick:"", channel?channel->name:"");
924 return 1;
925 }
926
927 static void modpython_conf_read(void) {
928 dict_t conf_node;
929 char const* str;
930
931 if (!(conf_node = conf_get_data(MODPYTHON_CONF_NAME, RECDB_OBJECT))) {
932 log_module(PY_LOG, LOG_ERROR, "config node '%s' is missing or has wrong type", MODPYTHON_CONF_NAME);
933 return;
934 }
935
936 str = database_get_data(conf_node, "scripts_dir", RECDB_QSTRING);
937 modpython_conf.scripts_dir = str ? str : "./";
938
939 str = database_get_data(conf_node, "main_module", RECDB_QSTRING);
940 modpython_conf.main_module = str ? str : "modpython";
941 }
942
943 int python_init(void) {
944 /* X3 calls this function on init of the module during startup. We use it to
945 do all our setup tasks and bindings
946 */
947
948 PY_LOG = log_register_type("Python", "file:python.log");
949 python_module = module_register("python", PY_LOG, "mod-python.help", NULL);
950 conf_register_reload(modpython_conf_read);
951
952 log_module(PY_LOG, LOG_INFO, "python module init");
953 message_register_table(msgtab);
954
955 /*
956 reg_auth_func(python_check_messages);
957 reg_handle_rename_func(python_rename_account);
958 reg_unreg_func(python_unreg_account);
959 conf_register_reload(python_conf_read);
960 saxdb_register("python", python_saxdb_read, python_saxdb_write);
961 modcmd_register(python_module, "send", cmd_send, 3, MODCMD_REQUIRE_AUTHED, NULL);
962 */
963 modcmd_register(python_module, "reload", cmd_reload, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
964 modcmd_register(python_module, "run", cmd_run, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
965 modcmd_register(python_module, "command", cmd_command, 3, MODCMD_REQUIRE_STAFF, NULL);
966
967 // Please help us by implimenting any of the callbacks listed as TODO below. They already exist
968 // in x3, they just need handle_ bridges implimented. (see python_handle_join for an example)
969 reg_server_link_func(python_handle_server_link);
970 reg_new_user_func(python_handle_new_user);
971 reg_nick_change_func(python_handle_nick_change);
972 //TODO: reg_del_user_func(python_handle_del_user);
973 //TODO: reg_account_func(python_handle_account); /* stamping of account name to the ircd */
974 //TODO: reg_handle_rename_func(python_handle_handle_rename); /* handle used to ALSO mean account name */
975 //TODO: reg_failpw_func(python_handle_failpw);
976 //TODO: reg_allowauth_func(python_handle_allowauth);
977 //TODO: reg_handle_merge_func(python_handle_merge);
978 //
979 //TODO: reg_oper_func(python_handle_oper);
980 //TODO: reg_new_channel_func(python_handle_new_channel);
981 reg_join_func(python_handle_join);
982 //TODO: reg_del_channel_func(python_handle_del_channel);
983 //TODO: reg_part_func(python_handle_part);
984 //TODO: reg_kick_func(python_handle_kick);
985 //TODO: reg_topic_func(python_handle_topic);
986 //TODO: reg_channel_mode_func(python_handle_channel_mode);
987
988 //TODO: reg_privmsg_func(python_handle_privmsg);
989 //TODO: reg_notice_func
990 //TODO: reg_svccmd_unbind_func(python_handle_svccmd_unbind);
991 //TODO: reg_chanmsg_func(python_handle_chanmsg);
992 //TODO: reg_allchanmsg_func
993 //TODO: reg_user_mode_func
994
995 reg_exit_func(python_cleanup);
996
997 python_load();
998 return 1;
999 }
1000
1001 #endif /* WITH_PYTHON */