]> jfr.im git - irc/evilnet/x3.git/blob - src/mod-python.c
fixups to new python code so it at least runs and calls hooks successfully now
[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 "sendmail.h"
34 #include "timeq.h"
35
36 /* TODO notes
37 *
38 * - Impliment most of proto-p10 irc_* commands for calling from scripts
39 * - Impliment functions to look up whois, channel, account, and reg-channel info for scripts
40 * - Impliment x3.conf settings for python variables like include path, etc.
41 * - kod-python.py calls for everything you can reg_ a handler for in x3
42 * - Some kind of system for getting needed binds bound automagicaly to make it easier
43 * to run peoples scripts and mod-python in general.
44 */
45
46 static const struct message_entry msgtab[] = {
47 { "PYMSG_RELOAD_SUCCESS", "Reloaded Python scripts successfully." },
48 { "PYMSG_RELOAD_FAILED", "Error reloading Python scripts." },
49 { NULL, NULL } /* sentenal */
50 };
51
52 static struct log_type *PY_LOG;
53 const char *python_module_deps[] = { NULL };
54 static struct module *python_module;
55
56 PyObject *base_module = NULL; /* Base python handling library */
57 PyObject *handler_object = NULL; /* instanciation of handler class */
58
59 static PyObject*
60 emb_dump(PyObject *self, PyObject *args)
61 {
62 char *buf;
63 int ret = 0;
64 char linedup[MAXLEN];
65
66 if(!PyArg_ParseTuple(args, "s:dump", &buf ))
67 return NULL;
68 safestrncpy(linedup, buf, sizeof(linedup));
69 if(parse_line(linedup, 1)) {
70 irc_raw(buf);
71 ret = 1;
72 }
73 return Py_BuildValue("i", ret);
74 }
75
76 static PyObject*
77 emb_send_target_privmsg(PyObject *self, PyObject *args)
78 {
79 int ret = 0;
80 char *servicenick;
81 char *channel;
82 char *buf;
83
84 struct service *service;
85
86 if(!PyArg_ParseTuple(args, "sss:reply", &servicenick, &channel, &buf ))
87 return NULL;
88 if(!(service = service_find(servicenick))) {
89 /* TODO: generate python exception here */
90 return NULL;
91 }
92 send_target_message(5, channel, service->bot, "%s", buf);
93 return Py_BuildValue("i", ret);
94 }
95
96 static PyObject*
97 emb_send_target_notice(PyObject *self, PyObject *args)
98 {
99 int ret = 0;
100 char *servicenick;
101 char *target;
102 char *buf;
103
104 struct service *service;
105
106 if(!PyArg_ParseTuple(args, "sss:reply", &servicenick, &target, &buf ))
107 return NULL;
108 if(!(service = service_find(servicenick))) {
109 /* TODO: generate python exception here */
110 return NULL;
111 }
112 send_target_message(4, target, service->bot, "%s", buf);
113 return Py_BuildValue("i", ret);
114 }
115
116
117 static PyObject*
118 emb_get_user(PyObject *self, PyObject *args)
119 {
120 char *nick;
121 struct userNode *user;
122 struct modeNode *mn;
123 unsigned int n;
124 PyObject* pChanList;
125 if(!PyArg_ParseTuple(args, "s", &nick))
126 return NULL;
127 if(!(user = GetUserH(nick))) {
128 /* TODO: generate python exception here */
129 return NULL;
130 }
131 pChanList = PyTuple_New(user->channels.used);
132 for(n=0;n<user->channels.used;n++) {
133 mn = user->channels.list[n];
134 PyTuple_SetItem(pChanList, n, Py_BuildValue("s", mn->channel->name));
135 }
136 return Py_BuildValue("{s:s,s:s,s:s,s:s,s:s" /* format strings. s=string, i=int */
137 ",s:s,s:s,s:s,s:s,s:s" /* (format is key:value) O=object */
138 ",s:i,s:i,s:s,s:s,s:s" /* blocks of 5 for readability */
139 "s:O}",
140
141 "nick", user->nick,
142 "ident", user->ident,
143 "info", user->info,
144 "hostname", user->hostname,
145 "ip", irc_ntoa(&user->ip),
146
147 "fakehost", user->fakehost,
148 "sethost", user->sethost,
149 "crypthost", user->crypthost,
150 "cryptip", user->cryptip,
151 "numeric", user->numeric, /* TODO: only ifdef WITH_PROTOCOL_P10 */
152
153 "loc", user->loc,
154 "no_notice", user->no_notice,
155 "mark", user->mark,
156 "version_reply", user->version_reply,
157 "account", user->handle_info?user->handle_info->handle:NULL,
158 "channels", pChanList);
159 }
160
161 static PyObject*
162 emb_get_channel(PyObject *self, PyObject *args)
163 {
164 char *name;
165 struct chanNode *channel;
166 unsigned int n;
167 PyObject *pChannelMembers;
168 PyObject *pChannelBans;
169 PyObject *pChannelExempts;
170
171 if(!PyArg_ParseTuple(args, "s", &name))
172 return NULL;
173 if(!(channel = GetChannel(name))) {
174 /* TODO: generate py exception here */
175 return NULL;
176 }
177
178 /* build tuple of nicks in channel */
179 pChannelMembers = PyTuple_New(channel->members.used);
180 for(n=0;n < channel->members.used;n++) {
181 struct modeNode *mn = channel->members.list[n];
182 PyTuple_SetItem(pChannelMembers, n, Py_BuildValue("s", mn->user->nick));
183 }
184
185 /* build tuple of bans */
186 pChannelBans = PyTuple_New(channel->banlist.used);
187 for(n=0; n < channel->banlist.used;n++) {
188 struct banNode *bn = channel->banlist.list[n];
189 PyTuple_SetItem(pChannelBans, n,
190 Py_BuildValue("{s:s,s:s,s:i}",
191 "ban", bn->ban,
192 "who", bn->who,
193 "set", bn->set)
194 );
195 }
196
197
198 /* build tuple of exempts */
199 pChannelExempts = PyTuple_New(channel->exemptlist.used);
200 for(n=0; n < channel->exemptlist.used;n++) {
201 struct exemptNode *en = channel->exemptlist.list[n];
202 PyTuple_SetItem(pChannelExempts, n,
203 Py_BuildValue("{s:s,s:s,s:i}",
204 "ban", en->exempt,
205 "who", en->who,
206 "set", en->set)
207 );
208 }
209
210
211
212 return Py_BuildValue("{s:s,s:s,s:s,s:i"
213 ",s:i,s:i,s:O,s:O,s:O}",
214
215 "name", channel->name,
216 "topic", channel->topic,
217 "topic_nick", channel->topic_nick,
218 "topic_time", channel->topic_time,
219
220 "timestamp", channel->timestamp,
221 "modes", channel->modes,
222 "members", pChannelMembers,
223 "bans", pChannelBans,
224 "exempts", pChannelExempts
225 );
226 }
227
228 /*
229 static PyObject*
230 emb_get_account(PyObject *self, PyObject *args)
231 {
232 char *name;
233 if(!PyArg_ParseTuple(args, "s", &name))
234 return NULL;
235 }
236 */
237
238
239 static PyMethodDef EmbMethods[] = {
240 {"dump", emb_dump, METH_VARARGS, "Dump raw P10 line to server"},
241 {"send_target_privmsg", emb_send_target_privmsg, METH_VARARGS, "Send a message to somewhere"},
242 {"send_target_notice", emb_send_target_notice, METH_VARARGS, "Send a notice to somewhere"},
243 {"get_user", emb_get_user, METH_VARARGS, "Get details about a nickname"},
244 {"get_channel", emb_get_channel, METH_VARARGS, "Get details about a channel"},
245 {NULL, NULL, 0, NULL}
246 };
247
248
249
250 /* This is just a hack-job for testing. It'll go away. */
251 int python_call_func_real(char *function, char *args[], size_t argc) {
252 /* TODO: get arguments, pass through to python function */
253 PyObject *pFunc, *pValue;
254 PyObject *pArgs = NULL;
255 if(base_module != NULL) {
256 log_module(PY_LOG, LOG_INFO, "Attempting to run python function %s", function);
257 pFunc = PyObject_GetAttrString(base_module, function);
258 /* pFunc is a new reference */
259 if(pFunc && PyCallable_Check(pFunc)) {
260 size_t i;
261 if(args && argc) {
262 pArgs = PyTuple_New(argc);
263 for(i = 0; i< argc; ++i) {
264 pValue = PyString_FromString(args[i]);
265 if(!pValue) {
266 Py_DECREF(pArgs);
267 Py_DECREF(pFunc);
268 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[i]);
269 return 0;
270 }
271 PyTuple_SetItem(pArgs, i, pValue);
272 }
273 }
274
275 pValue = PyObject_CallObject(pFunc, pArgs);
276 if(pArgs != NULL) {
277 Py_DECREF(pArgs);
278 }
279 if(pValue != NULL) {
280 int ret;
281 ret = PyInt_AsLong(pValue);
282 if(ret == -1 && PyErr_Occurred()) {
283 PyErr_Print();
284 log_module(PY_LOG, LOG_INFO, "error converting return value of %s to type long. ", function);
285 ret = 0;
286 }
287 log_module(PY_LOG, LOG_INFO, "%s was run successfully, returned %d.", function, ret);
288 /* TODO: convert pValue to c int, return it below */
289 Py_DECREF(pValue);
290 return ret;
291 }
292 else {
293 Py_DECREF(pFunc);
294 /* TODO: instead of print errors, get them as strings
295 * and deal with them with normal x3 log system. */
296 PyErr_Print();
297 log_module(PY_LOG, LOG_WARNING, "call to %s failed", function);
298 return 0;
299 }
300 }
301 else {
302 if(PyErr_Occurred())
303 PyErr_Print();
304 log_module(PY_LOG, LOG_WARNING, "function %s not found or uncallable", function);
305 return 0;
306 }
307 }
308 else {
309 return 0;
310 }
311 }
312
313 /* This is just a hack-job for testing. It will go away */
314 int python_call_func(char *function, char *args[], size_t argc, char *command_caller, char *command_target, char *command_service) {
315 char *setargs[] = {command_caller?command_caller:"",
316 command_target?command_target:"",
317 command_service?command_service:""};
318 python_call_func_real("command_set", setargs, 3);
319 python_call_func_real(function, args, argc);
320 python_call_func_real("command_clear", NULL, 0);
321 return 0;
322 }
323
324 PyObject *python_build_handler_args(size_t argc, char *args[], PyObject *pIrcObj) {
325 size_t i = 0, n;
326 PyObject *pArgs = NULL;
327
328 pArgs = PyTuple_New(argc + 1);
329 Py_INCREF(pIrcObj);
330 PyTuple_SetItem(pArgs, i++, pIrcObj);
331
332 if(args && argc) {
333 PyObject *pValue;
334 for(n = 0; n < argc; ++n) {
335 pValue = PyString_FromString(args[n]);
336 if(!pValue) {
337 Py_DECREF(pArgs);
338 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[n]);
339 return NULL;
340 }
341 PyTuple_SetItem(pArgs, n+i, pValue);
342 }
343 }
344 return pArgs;
345 }
346
347 PyObject *python_build_args(size_t argc, char *args[]) {
348 size_t i;
349 PyObject *pArgs = NULL;
350
351 if(args && argc) {
352 pArgs = PyTuple_New(argc);
353 PyObject *pValue;
354 for(i = 0; i< argc; ++i) {
355 pValue = PyString_FromString(args[i]);
356 if(!pValue) {
357 Py_DECREF(pArgs);
358 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[i]);
359 return NULL;
360 }
361 PyTuple_SetItem(pArgs, i, pValue);
362 }
363 }
364 return pArgs;
365 }
366
367
368 PyObject *new_irc_object(char *command_service, char *command_caller, char *command_target) {
369 PyObject *pIrcArgs = NULL;
370 PyObject *pIrcClass;
371 PyObject *pIrcObj;
372
373 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate irc class");
374 pIrcClass = PyObject_GetAttrString(base_module, "irc");
375 /* pIrcClass is a new reference */
376 if(pIrcClass && PyCallable_Check(pIrcClass)) {
377 //size_t i;
378 char *ircargs[] = {command_service, command_caller, command_target};
379 //PyObject *pValue;
380
381 pIrcArgs = python_build_args(3, ircargs);
382 /*
383 pIrcArgs = PyTuple_New(sizeof(ircargs));
384 for(i = 0; i< ircargc; ++i) {
385 if(ircargs[i]) {
386 pValue = PyString_FromString(ircargs[i]);
387 if(!pValue) {
388 Py_DECREF(pIrcArgs);
389 log_module(PY_LOG, LOG_ERROR, "Unable to convert '%s' to python string", ircargs[i]);
390 return 0;
391 }
392 }
393 else {
394 pValue = Py_None;
395 Py_INCREF(Py_None);
396 }
397 PyTuple_SetItem(pIrcArgs, i, pValue);
398 }
399 */
400 pIrcObj = PyObject_CallObject(pIrcClass, pIrcArgs);
401 if(!pIrcObj) {
402 log_module(PY_LOG, LOG_ERROR, "IRC Class failed to load");
403 PyErr_Print();
404 }
405 if(pIrcArgs != NULL) {
406 Py_DECREF(pIrcArgs);
407 }
408 return pIrcObj;
409 }
410 else {
411 log_module(PY_LOG, LOG_ERROR, "Unable to find irc class");
412 return NULL;
413 }
414 }
415
416
417 int python_call_handler(char *handler, char *args[], size_t argc, char *command_service, char *command_caller, char *command_target) {
418 /* TODO:
419 * - Instanciate class 'irc' with command-* arguments and save it.
420 * - get/find handler class instance
421 * - call handler.<handler> passing in proper args
422 * - destroy irc instance
423 * - return something useful?
424 */
425 PyObject *pIrcObj;
426 PyObject *pArgs;
427 PyObject *pMethod;
428 PyObject *pValue;
429
430 log_module(PY_LOG, LOG_INFO, "attempting to call handler %s.", handler);
431 if(base_module != NULL) {
432 pIrcObj = new_irc_object(command_service, command_caller, command_target);
433 if(!pIrcObj) {
434 log_module(PY_LOG, LOG_INFO, "Can't get irc object. Bailing.");
435 return 0;
436 }
437
438 pArgs = python_build_handler_args(argc, args, pIrcObj);
439 pMethod = PyObject_GetAttrString(handler_object, handler);
440 if(pMethod && PyCallable_Check(pMethod)) {
441 pValue = PyObject_CallObject(pMethod, pArgs);
442 if(pArgs) {
443 Py_DECREF(pArgs);
444 }
445 if(pValue != NULL) {
446 int ret;
447 ret = PyInt_AsLong(pValue);
448 if(ret == -1 && PyErr_Occurred()) {
449 PyErr_Print();
450 log_module(PY_LOG, LOG_INFO, "error converting return value of handler %s to type long. ", handler);
451 ret = 0;
452 }
453 log_module(PY_LOG, LOG_INFO, "handler %s was run successfully, returned %d.", handler, ret);
454 /* TODO: convert pValue to c int, return it below */
455 Py_DECREF(pValue);
456 return ret;
457 }
458 else {
459 Py_DECREF(pIrcObj);
460 Py_DECREF(pMethod);
461 /* TODO: instead of print errors, get them as strings
462 * and deal with them with normal x3 log system. */
463 PyErr_Print();
464 log_module(PY_LOG, LOG_WARNING, "call to handler %s failed", handler);
465 return 0;
466 }
467 }
468 else { /* couldn't find handler methed */
469 log_module(PY_LOG, LOG_ERROR, "Cannot find handler %s.", handler);
470 return 0;
471
472 }
473 }
474 else { /* No base module.. no python? */
475 log_module(PY_LOG, LOG_INFO, "Cannot handle %s, Python is not initialized.", handler);
476 return 0;
477 }
478 }
479
480 PyObject *python_new_handler_object() {
481 PyObject *pHandlerClass, *pHandlerObj;
482
483 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate python class handler");
484 pHandlerClass = PyObject_GetAttrString(base_module, "handler");
485 /* Class is a new reference */
486 if(pHandlerClass && PyCallable_Check(pHandlerClass)) {
487 /*PyObject *pValue; */
488
489 pHandlerObj = PyObject_CallObject(pHandlerClass, NULL);
490 return pHandlerObj;
491 }
492 else {
493 log_module(PY_LOG, LOG_ERROR, "Unable to find handler class");
494 return NULL;
495 }
496 }
497
498 /* debate: do we just register these and check them in python
499 * for every one (slow?) or actually work out if a plugin needs
500 * it first? We will start by doing it every time.
501 */
502 static int
503 python_handle_join(struct modeNode *mNode)
504 {
505 struct userNode *user = mNode->user;
506 struct chanNode *channel = mNode->channel;
507
508
509 log_module(PY_LOG, LOG_INFO, "python module handle_join");
510 if(!channel||!user) {
511 log_module(PY_LOG, LOG_WARNING, "Join without channel or user?");
512 return 0;
513 }
514 else {
515 char *args[] = {channel->name, user->nick};
516 return python_call_handler("join", args, 2, "", "", "");
517 }
518 }
519
520
521 int python_load() {
522 PyObject *pName;
523
524 setenv("PYTHONPATH", "/home/rubin/afternet/services/x3/x3-run/", 1);
525 Py_Initialize();
526 Py_InitModule("svc", EmbMethods);
527 //PyRun_SimpleString("import svc");
528 /* TODO: get "modpython" from x3.conf */
529 pName = PyString_FromString("modpython");
530 base_module = PyImport_Import(pName);
531 Py_DECREF(pName);
532 if(base_module != NULL) {
533 handler_object = python_new_handler_object();
534 if(handler_object) {
535 python_call_handler("init", NULL, 0, "", "", "");
536 return 1;
537 }
538 else {
539 /* error handler class not found */
540 log_module(PY_LOG, LOG_WARNING, "Failed to create handler object");
541 return 0;
542 }
543 }
544 else {
545 PyErr_Print();
546 log_module(PY_LOG, LOG_WARNING, "Failed to load modpython.py");
547 return 0;
548 }
549 return 0;
550 }
551
552 /* Called after X3 is fully up and running */
553 int
554 python_finalize(void) {
555
556 PyRun_SimpleString("print 'Hello, World of Python!'");
557 log_module(PY_LOG, LOG_INFO, "python module finalize");
558
559 return 1;
560 }
561
562 /* Called on shutdown of the module */
563 static void
564 python_cleanup(void)
565 {
566 log_module(PY_LOG, LOG_INFO, "python module cleanup");
567 Py_Finalize(); /* Shut down python enterpriter */
568 return;
569 }
570
571 static MODCMD_FUNC(cmd_reload) {
572 log_module(PY_LOG, LOG_INFO, "Shutting python down");
573 python_cleanup();
574 log_module(PY_LOG, LOG_INFO, "Loading python stuff");
575 if(python_load()) {
576 reply("PYMSG_RELOAD_SUCCESS");
577 }
578 else {
579 reply("PYMSG_RELOAD_FAILED");
580 }
581 return 1;
582 }
583
584 static MODCMD_FUNC(cmd_run) {
585
586 char *msg;
587 msg = unsplit_string(argv + 1, argc - 1, NULL);
588 /* PyRun_SimpleString(msg); */
589 char *args[] = {msg};
590 python_call_func("run", args, 1, user?user->nick:"", channel?channel->name:"", cmd->parent->bot->nick);
591 return 1;
592 }
593
594 /* Called on init of the module during startup */
595 int python_init(void) {
596
597 PY_LOG = log_register_type("Python", "file:python.log");
598 python_module = module_register("python", PY_LOG, "mod-python.help", NULL);
599 log_module(PY_LOG, LOG_INFO, "python module init");
600 message_register_table(msgtab);
601
602 /*
603 reg_auth_func(python_check_messages);
604 reg_handle_rename_func(python_rename_account);
605 reg_unreg_func(python_unreg_account);
606 conf_register_reload(python_conf_read);
607 saxdb_register("python", python_saxdb_read, python_saxdb_write);
608 modcmd_register(python_module, "send", cmd_send, 3, MODCMD_REQUIRE_AUTHED, NULL);
609 */
610 modcmd_register(python_module, "reload", cmd_reload, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
611 modcmd_register(python_module, "run", cmd_run, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
612 reg_join_func(python_handle_join);
613 reg_exit_func(python_cleanup);
614
615 python_load();
616 return 1;
617 }
618
619 #endif /* WITH_PYTHON */