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