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