]> jfr.im git - irc/evilnet/x3.git/blame - src/mod-python.c
mod-python: improve error logic for emb_get_channel
[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 115/* get a tuple with all users in it */
116static PyObject*
117emb_get_users(UNUSED_ARG(PyObject *self), PyObject *args) {
118 PyObject* retval;
ee6f1c82 119 size_t num_clients, n = 0;
6d94ce8b 120 struct _tuple_dict_extra extra;
121
122 if (!PyArg_ParseTuple(args, ""))
123 return NULL;
124
125 num_clients = dict_size(clients);
126 retval = PyTuple_New(num_clients);
5345ea76 127 if (retval == NULL)
128 return NULL;
6d94ce8b 129
130 extra.extra = &n;
131 extra.data = retval;
132
39d37f27 133 if (dict_foreach(clients, _dict_iter_fill_tuple, (void*)&extra) != NULL) {
ee6f1c82 134 pyobj_release_tuple(retval, n);
5345ea76 135 return NULL;
136 }
6d94ce8b 137
138 return retval;
139}
140
cc0b2b7f 141/* get a tuple with all channels in it */
142static PyObject*
143emb_get_channels(UNUSED_ARG(PyObject* self), PyObject* args) {
144 PyObject* retval;
ee6f1c82 145 size_t num_channels, n = 0;
cc0b2b7f 146 struct _tuple_dict_extra extra;
147
148 if (!PyArg_ParseTuple(args, ""))
149 return NULL;
150
151 num_channels = dict_size(channels);
152 retval = PyTuple_New(num_channels);
d12756d7 153 if (retval == NULL)
154 return NULL;
cc0b2b7f 155
156 extra.extra = &n;
157 extra.data = retval;
158
39d37f27 159 if (dict_foreach(channels, _dict_iter_fill_tuple, (void*)&extra) != NULL) {
ee6f1c82 160 pyobj_release_tuple(retval, n);
d12756d7 161 return NULL;
162 }
cc0b2b7f 163
164 return retval;
165}
166
dcc1df5e 167static PyObject*
168emb_get_servers(UNUSED_ARG(PyObject* self), PyObject* args) {
169 PyObject* retval;
ee6f1c82 170 size_t n = 0;
dcc1df5e 171 struct _tuple_dict_extra extra;
172
173 if (!PyArg_ParseTuple(args, ""))
174 return NULL;
175
176 retval = PyTuple_New(dict_size(servers));
39d37f27 177 if (retval == NULL)
178 return NULL;
179
180 extra.extra = &n;
181 extra.data = retval;
182
183 if (dict_foreach(servers, _dict_iter_fill_tuple, (void*)&extra) != NULL) {
ee6f1c82 184 pyobj_release_tuple(retval, n);
39d37f27 185 return NULL;
186 }
187
188 return retval;
189}
190
191static PyObject*
192emb_get_accounts(UNUSED_ARG(PyObject* self), PyObject* args) {
193 PyObject* retval;
ee6f1c82 194 size_t n = 0;
39d37f27 195 struct _tuple_dict_extra extra;
196
197 if (!PyArg_ParseTuple(args, ""))
198 return NULL;
199
200 retval = PyTuple_New(dict_size(nickserv_handle_dict));
201 if (retval == NULL)
202 return NULL;
dcc1df5e 203
204 extra.extra = &n;
205 extra.data = retval;
206
39d37f27 207 if (dict_foreach(nickserv_handle_dict, _dict_iter_fill_tuple, (void*)&extra) != NULL) {
ee6f1c82 208 pyobj_release_tuple(retval, n);
318ec177 209 return NULL;
210 }
dcc1df5e 211
212 return retval;
213}
214
a2c8c575 215static PyObject*
039a6658 216emb_dump(UNUSED_ARG(PyObject *self), PyObject *args)
a2c8c575 217{
4c216694 218 /* Dump a raw string into the socket
50d61a79 219 usage: _svc.dump(<P10 string>)
4c216694 220 */
a2c8c575 221 char *buf;
222 int ret = 0;
223 char linedup[MAXLEN];
224
039a6658 225
a2c8c575 226 if(!PyArg_ParseTuple(args, "s:dump", &buf ))
227 return NULL;
a957511b 228
a2c8c575 229 safestrncpy(linedup, buf, sizeof(linedup));
a957511b 230
a2c8c575 231 if(parse_line(linedup, 1)) {
232 irc_raw(buf);
233 ret = 1;
a957511b 234 } else {
235 PyErr_SetString(PyExc_Exception, "invalid protocol message");
236 return NULL;
a2c8c575 237 }
a957511b 238
a2c8c575 239 return Py_BuildValue("i", ret);
240}
241
242static PyObject*
039a6658 243emb_send_target_privmsg(UNUSED_ARG(PyObject *self), PyObject *args)
a2c8c575 244{
4c216694 245 /* Send a privmsg
50d61a79 246 usage: _svc.send_target_privmsg(<servicenick_from>, <nick_to>, <message>)
4c216694 247 */
a2c8c575 248 int ret = 0;
249 char *servicenick;
250 char *channel;
251 char *buf;
252
253 struct service *service;
254
039a6658 255
a2c8c575 256 if(!PyArg_ParseTuple(args, "sss:reply", &servicenick, &channel, &buf ))
257 return NULL;
e7af1e12 258
259 if (buf == NULL || strlen(buf) == 0) {
260 PyErr_SetString(PyExc_Exception, "invalid empty message");
261 return NULL;
262 }
263
a2c8c575 264 if(!(service = service_find(servicenick))) {
e7af1e12 265 PyErr_SetString(PyExc_Exception, "no such service nick");
8d670803 266 return NULL;
a2c8c575 267 }
e7af1e12 268
269 ret = send_target_message(5, channel, service->bot, "%s", buf);
a2c8c575 270 return Py_BuildValue("i", ret);
271}
272
d4e0f0c4 273static PyObject*
039a6658 274emb_send_target_notice(UNUSED_ARG(PyObject *self), PyObject *args)
d4e0f0c4 275{
4c216694 276 /* send a notice
50d61a79 277 usage: _svc.send_target_notice(<servicenick_from>, <nick_to>, <message>)
4c216694 278 */
d4e0f0c4 279 int ret = 0;
280 char *servicenick;
281 char *target;
282 char *buf;
283
284 struct service *service;
285
286 if(!PyArg_ParseTuple(args, "sss:reply", &servicenick, &target, &buf ))
287 return NULL;
66f68f65 288
289 if (buf == NULL || strlen(buf) == 0) {
290 PyErr_SetString(PyExc_Exception, "invalid empty message");
291 return NULL;
292 }
293
d4e0f0c4 294 if(!(service = service_find(servicenick))) {
66f68f65 295 PyErr_SetString(PyExc_Exception, "no such service nick");
d4e0f0c4 296 return NULL;
297 }
66f68f65 298
299 ret = send_target_message(4, target, service->bot, "%s", buf);
300
d4e0f0c4 301 return Py_BuildValue("i", ret);
302}
303
bfdfd1c3 304static PyObject*
305pyobj_from_usernode(struct userNode* user) {
ee6f1c82 306 unsigned int n;
bfdfd1c3 307 struct modeNode *mn;
82089e3f 308 PyObject* retval = NULL;
bfdfd1c3 309 PyObject* pChanList = PyTuple_New(user->channels.used);
310
82089e3f 311 if (pChanList == NULL)
312 return NULL;
313
bfdfd1c3 314 for (n=0; n < user->channels.used; n++) {
315 mn = user->channels.list[n];
82089e3f 316 if (PyTuple_SetItem(pChanList, n, Py_BuildValue("s", mn->channel->name)))
317 goto cleanup;
bfdfd1c3 318 }
319
82089e3f 320 retval = Py_BuildValue("{"
bfdfd1c3 321 "s: s, " /* nick */
322 "s: s, " /* ident */
323 "s: s, " /* info */
324 "s: s, " /* hostname */
325 "s: s, " /* ip */
326 "s: s, " /* fakehost */
327 "s: s, " /* sethost */
328 "s: s, " /* crypthost */
329 "s: s, " /* cryptip */
bfdfd1c3 330 "s: s, " /* numeric */
bfdfd1c3 331 "s: i, " /* loc */
332 "s: i, " /* no_notice */
333 "s: s, " /* mark */
334 "s: s, " /* version_reply */
335 "s: s, " /* account */
336 "s: O}", /* channels */
337 "nick", user->nick,
338 "ident", user->ident,
339 "info", user->info,
340 "hostname", user->hostname,
341 "ip", irc_ntoa(&user->ip),
342 "fakehost", user->fakehost,
343 "sethost", user->sethost,
344 "crypthost", user->crypthost,
345 "cryptip", user->cryptip,
bfdfd1c3 346 "numeric", user->numeric,
bfdfd1c3 347 "loc", user->loc,
348 "no_notice", user->no_notice,
349 "mark", user->mark,
350 "version_reply", user->version_reply,
351 "account", user->handle_info ? user->handle_info->handle : NULL,
352 "channels", pChanList);
82089e3f 353
354 if (retval == NULL)
355 goto cleanup;
356
357 return retval;
358
359cleanup:
360 Py_XDECREF(retval);
ee6f1c82 361 pyobj_release_tuple(pChanList, n);
82089e3f 362
363 return NULL;
bfdfd1c3 364}
365
8d670803 366static PyObject*
039a6658 367emb_get_user(UNUSED_ARG(PyObject *self), PyObject *args)
8d670803 368{
4c216694 369 /* Get a python object containing everything x3 knows about a user, by nick.
50d61a79 370 usage: _svc.get_user(<nick>)
4c216694 371 */
54d2fd3d 372 char const* nick;
8d670803 373 struct userNode *user;
039a6658 374
8d670803 375 if(!PyArg_ParseTuple(args, "s", &nick))
376 return NULL;
bfdfd1c3 377
8d670803 378 if(!(user = GetUserH(nick))) {
54d2fd3d 379 PyErr_SetString(PyExc_Exception, "no such user");
8d670803 380 return NULL;
381 }
bfdfd1c3 382
383 return pyobj_from_usernode(user);
8d670803 384}
385
d8f8d3b6 386static PyObject*
387pyobj_from_server(struct server* srv) {
388 size_t n, idx;
389 PyObject* tmp = NULL;
390 PyObject* retval = NULL;
391 PyObject* users = PyTuple_New(srv->clients);
392
393 if (users == NULL)
394 return NULL;
395
396 idx = 0;
397 for (n = 0; n < srv->num_mask; ++n) {
398 if (srv->users[n] == NULL)
399 continue;
400
401 tmp = PyString_FromString(srv->users[n]->nick);
402 if (tmp == NULL)
403 goto cleanup;
404
405 if (PyTuple_SetItem(users, idx++, tmp))
406 goto cleanup;
407 }
408
409 retval = Py_BuildValue("{"
410 "s:s," /* name */
411 "s:l," /* boot */
412 "s:l," /* link_time */
413 "s:s," /* description */
414 "s:s," /* numeric */
415 "s:I," /* num_mask */
416 "s:I," /* hops */
417 "s:I," /* clients */
418 "s:I," /* max_clients */
419 "s:I," /* burst */
420 "s:I," /* self_burst */
421 "s:s" /* uplink */
422 "s:O" /* users */
423 /* TODO: Children */
424 "}",
425 "name", srv->name,
426 "boot", srv->boot,
427 "link_time", srv->link_time,
428 "description", srv->description,
429 "numeric", srv->numeric,
430 "num_mask", srv->num_mask,
431 "hops", srv->hops,
432 "clients", srv->clients,
433 "max_clients", srv->max_clients,
434 "burst", srv->burst,
435 "self_burst", srv->self_burst,
436 "uplink", srv->uplink ? srv->uplink->name : NULL,
437 "users", users
438 );
439
440 if (retval == NULL)
441 goto cleanup;
442
443 return retval;
444
445cleanup:
446 Py_XDECREF(retval);
ee6f1c82 447 pyobj_release_tuple(users, idx);
d8f8d3b6 448
449 return NULL;
450}
451
452static PyObject*
453emb_get_server(UNUSED_ARG(PyObject* self), PyObject* args) {
454 struct server* srv;
455 char const* name;
456
457 if (!PyArg_ParseTuple(args, "s", &name))
458 return NULL;
459
460 if (name == NULL || strlen(name) == 0) {
461 PyErr_SetString(PyExc_Exception, "invalid server name");
462 return NULL;
463 }
464
465 if ((srv = GetServerH(name)) == NULL) {
466 PyErr_SetString(PyExc_Exception, "unknown server");
467 return NULL;
468 }
469
470 return pyobj_from_server(srv);
471}
472
92fb809b 473static PyObject*
474pyobj_from_modelist(struct modeList* mode) {
475 size_t n;
476 PyObject* tmp;
477 PyObject* retval = PyTuple_New(mode->used);
478
479 if (retval == NULL)
480 return NULL;
481
482 for (n = 0; n < mode->used; ++n) {
483 struct modeNode* mn = mode->list[n];
484 tmp = PyString_FromString(mn->user->nick);
485 if (tmp == NULL) {
486 pyobj_release_tuple(retval, n);
487 return NULL;
488 }
489
490 if (PyTuple_SetItem(retval, n, tmp)) {
491 pyobj_release_tuple(retval, n);
492 return NULL;
493 }
494 }
495
496 return retval;
497}
498
c9f7b679 499static PyObject*
500pyobj_from_banlist(struct banList* bans) {
501 size_t n;
502 struct banNode* bn;
503 PyObject* tmp;
504 PyObject* retval = PyTuple_New(bans->used);
505
506 if (retval == NULL)
507 return NULL;
508
509 for (n = 0; n < bans->used; ++n) {
510 bn = bans->list[n];
511
512 tmp = Py_BuildValue("{s:s,s:s,s:l}",
513 "ban", bn->ban, "who", bn->who, "set", bn->set);
514
515 if (tmp == NULL || PyTuple_SetItem(retval, n, tmp)) {
516 pyobj_release_tuple(retval, n);
517 return NULL;
518 }
519 }
520
521 return retval;
522}
523
524static PyObject*
525pyobj_from_exemptlist(struct exemptList* exmp) {
526 size_t n;
527 struct exemptNode* en;
528 PyObject* tmp;
529 PyObject* retval = PyTuple_New(exmp->used);
530
531 if (retval == NULL)
532 return NULL;
533
534 for (n = 0; n < exmp->used; ++n) {
535 en = exmp->list[n];
536
537 tmp = Py_BuildValue("{s:s,s:s,s:l}",
538 "ban", en->exempt, "who", en->who, "set", en->set);
539
540 if (tmp == NULL || PyTuple_SetItem(retval, n, tmp)) {
541 pyobj_release_tuple(retval, n);
542 return NULL;
543 }
544 }
545
546 return retval;
547}
548
8d670803 549static PyObject*
039a6658 550emb_get_channel(UNUSED_ARG(PyObject *self), PyObject *args)
8d670803 551{
4c216694 552 /* Returns a python dict object with all sorts of info about a channel.
50d61a79 553 usage: _svc.get_channel(<name>)
4c216694 554 */
8d670803 555 char *name;
556 struct chanNode *channel;
c9f7b679 557 PyObject *pChannelMembers = NULL;
558 PyObject *pChannelBans = NULL;
559 PyObject *pChannelExempts = NULL;
560 PyObject *retval = NULL;
8d670803 561
039a6658 562
8d670803 563 if(!PyArg_ParseTuple(args, "s", &name))
564 return NULL;
3f24e818 565
8d670803 566 if(!(channel = GetChannel(name))) {
3f24e818 567 PyErr_SetString(PyExc_Exception, "unknown channel");
8d670803 568 return NULL;
569 }
570
571 /* build tuple of nicks in channel */
92fb809b 572 pChannelMembers = pyobj_from_modelist(&channel->members);
c9f7b679 573 if (pChannelMembers == NULL)
574 goto cleanup;
8d670803 575
576 /* build tuple of bans */
c9f7b679 577 pChannelBans = pyobj_from_banlist(&channel->banlist);
578 if (pChannelBans == NULL)
579 goto cleanup;
8d670803 580
8d670803 581 /* build tuple of exempts */
c9f7b679 582 pChannelExempts = pyobj_from_exemptlist(&channel->exemptlist);
583 if (pChannelExempts == NULL)
584 goto cleanup;
8d670803 585
c9f7b679 586 retval = Py_BuildValue("{s:s,s:s,s:s,s:i"
8d670803 587 ",s:i,s:i,s:O,s:O,s:O}",
588
589 "name", channel->name,
590 "topic", channel->topic,
591 "topic_nick", channel->topic_nick,
592 "topic_time", channel->topic_time,
593
594 "timestamp", channel->timestamp,
595 "modes", channel->modes,
596 "members", pChannelMembers,
597 "bans", pChannelBans,
598 "exempts", pChannelExempts
599 );
c9f7b679 600 if (retval == NULL)
601 goto cleanup;
602
603 return retval;
604
605cleanup:
606 Py_XDECREF(retval);
607 pyobj_release_tuple(pChannelExempts, channel->exemptlist.used);
608 pyobj_release_tuple(pChannelBans, channel->banlist.used);
609 pyobj_release_tuple(pChannelMembers, channel->members.used);
610
611 return NULL;
8d670803 612}
613
8d670803 614static PyObject*
039a6658 615emb_get_account(UNUSED_ARG(PyObject *self), PyObject *args)
8d670803 616{
4c216694 617 /* Returns a python dict object with all sorts of info about an account.
50d61a79 618 usage: _svc.get_account(<account name>)
4c216694 619 */
8d670803 620 char *name;
4c216694 621 struct handle_info *hi;
622
039a6658 623
8d670803 624 if(!PyArg_ParseTuple(args, "s", &name))
625 return NULL;
4c216694 626
627 hi = get_handle_info(name);
5b2b1df2 628
4c216694 629 if(!hi) {
5b2b1df2 630 PyErr_SetString(PyExc_Exception, "unknown account name");
4c216694 631 return NULL;
632 }
5b2b1df2 633
4c216694 634 return Py_BuildValue("{s:s,s:i,s:s,s:s,s:s"
635 ",s:s,s:s}",
636
637 "account", hi->handle,
638 "registered", hi->registered,
639 "last_seen", hi->lastseen,
640 "infoline", hi->infoline ? hi->infoline : "",
641 "email", hi->email_addr ? hi->email_addr : "",
642
643 "fakehost", hi->fakehost ? hi->fakehost : "",
644 "last_quit_host", hi->last_quit_host
645
646 /* TODO: */
647 /* users online authed to this account */
648 /* cookies */
649 /* nicks (nickserv nets only?) */
650 /* masks */
651 /* ignores */
652 /* channels */
653 );
8d670803 654}
8d670803 655
07559983 656static PyObject*
039a6658 657emb_get_info(UNUSED_ARG(PyObject *self), UNUSED_ARG(PyObject *args))
658{
659 /* return some info about the general setup
660 * of X3, such as what the chanserv's nickname
661 * is.
662 */
663
664
665 return Py_BuildValue("{s:s,s:s,s:s,s:s,s:s}",
666 "chanserv", chanserv? chanserv->nick : "ChanServ",
667 "nickserv", nickserv?nickserv->nick : "NickServ",
668 "opserv", opserv?opserv->nick : "OpServ",
669 "global", global?global->nick : "Global",
670 "spamserv", spamserv?spamserv->nick : "SpamServ");
671}
672
673static PyObject*
674emb_log_module(UNUSED_ARG(PyObject *self), PyObject *args)
07559983 675{
676 /* a gateway to standard X3 logging subsystem.
677 * level is a value 0 to 9 as defined by the log_severity enum in log.h.
07559983 678 *
679 * for now, all logs go to the PY_LOG log. In the future this will change.
680 */
681 char *message;
682 int ret = 0;
683 int level;
684
039a6658 685
07559983 686 if(!PyArg_ParseTuple(args, "is", &level, &message))
687 return NULL;
688
689 log_module(PY_LOG, level, "%s", message);
690
691 return Py_BuildValue("i", ret);
692}
8d670803 693
0076604e 694static PyObject*
695emb_kill(UNUSED_ARG(PyObject* self), PyObject* args) {
696 char const* from_nick, *target_nick, *message;
697 struct userNode *target;
698 struct service *service;
699
700 if (!PyArg_ParseTuple(args, "sss", &from_nick, &target_nick, &message))
701 return NULL;
702
703 if(!(service = service_find(from_nick))) {
704 PyErr_SetString(PyExc_Exception, "unknown service user specified as from user");
705 return NULL;
706 }
707
708 if ((target = GetUserH(target_nick)) == NULL) {
709 PyErr_SetString(PyExc_Exception, "unknown target user");
710 return NULL;
711 }
712
713 irc_kill(service->bot, target, message);
714
715 return Py_None;
716}
717
a2c8c575 718static PyMethodDef EmbMethods[] = {
07559983 719 /* Communication methods */
a2c8c575 720 {"dump", emb_dump, METH_VARARGS, "Dump raw P10 line to server"},
721 {"send_target_privmsg", emb_send_target_privmsg, METH_VARARGS, "Send a message to somewhere"},
d4e0f0c4 722 {"send_target_notice", emb_send_target_notice, METH_VARARGS, "Send a notice to somewhere"},
07559983 723 {"log_module", emb_log_module, METH_VARARGS, "Log something using the X3 log subsystem"},
039a6658 724//TODO: {"exec_cmd", emb_exec_cmd, METH_VARARGS, "execute x3 command provided"},
725// This should use environment from "python command" call to pass in, if available
0076604e 726 {"kill", emb_kill, METH_VARARGS, "Kill someone"},
039a6658 727//TODO: {"shun"
728//TODO: {"unshun"
729//TODO: {"gline", emb_gline, METH_VARARGS, "gline a mask"},
730//TODO: {"ungline", emb_ungline, METH_VARARGS, "remove a gline"},
731//TODO: {"kick", emb_kick, METH_VARARGS, "kick someone from a channel"},
732//TODO: {"channel_mode", emb_channel_mode, METH_VARARGS, "set modes on a channel"},
733//TODO: {"user_mode", emb_user_mode, METH_VARARGS, "Have x3 set usermodes on one of its own nicks"},
734//
735//TODO: {"get_config", emb_get_config, METH_VARARGS, "get x3.conf settings into a nested dict"},
736//TODO: {"config_set", emb_config_set, METH_VARARGS, "change a config setting 'on-the-fly'."},
737//
738//TODO: {"timeq_add", emb_timeq_new, METH_VARARGS, "some kind of interface to the timed event system."},
739//TODO: {"timeq_del", emb_timeq_new, METH_VARARGS, "some kind of interface to the timed event system."},
07559983 740 /* Information gathering methods */
8d670803 741 {"get_user", emb_get_user, METH_VARARGS, "Get details about a nickname"},
f0e11521 742 {"get_users", emb_get_users, METH_VARARGS, "Get all connected users"},
8d670803 743 {"get_channel", emb_get_channel, METH_VARARGS, "Get details about a channel"},
f0e11521 744 {"get_channels", emb_get_channels, METH_VARARGS, "Get all channels"},
d8f8d3b6 745 {"get_server", emb_get_server, METH_VARARGS, "Get details about a server"},
dcc1df5e 746 {"get_servers", emb_get_servers, METH_VARARGS, "Get all server names"},
4c216694 747 {"get_account", emb_get_account, METH_VARARGS, "Get details about an account"},
39d37f27 748 {"get_accounts", emb_get_accounts, METH_VARARGS, "Get all nickserv accounts"},
039a6658 749 {"get_info", emb_get_info, METH_VARARGS, "Get various misc info about x3"},
07559983 750 /* null terminator */
a2c8c575 751 {NULL, NULL, 0, NULL}
752};
753
754
f0e11521 755/*
756These functions set up the embedded environment for us to call out to
757modpython.py class methods.
4c216694 758 */
cbfd323c 759
07559983 760void python_log_module() {
761 /* Attempt to convert python errors to x3 log system */
762 PyObject *exc, *tb, *value, *tmp;
763 char *str_exc = "NONE";
764 char *str_value = "NONE";
765 char *str_tb = "NONE";
766
767 PyErr_Fetch(&exc, &value, &tb);
768
769 if(exc) {
770 if((tmp = PyObject_Str(exc)))
771 str_exc = PyString_AsString(tmp);
772 }
773 if(value) {
774 if((tmp = PyObject_Str(value)))
775 str_value = PyString_AsString(tmp);
776 }
777 if(tb) {
778 if((tmp = PyObject_Str(tb)))
779 str_tb = PyString_AsString(tmp);
780 }
781
782 /* Now restore it so we can print it (just in case)
783 * (should we do this only when running in debug mode?) */
784 PyErr_Restore(exc, value, tb);
785 PyErr_Print(); /* which of course, clears it again.. */
786
787 log_module(PY_LOG, LOG_WARNING, "PYTHON error: %s, value: %s", str_exc, str_value);
788
789 /* TODO: get the traceback using the traceback module via C api so we can add it to the X3 logs. See
790 * http://mail.python.org/pipermail/python-list/2003-March/192226.html */
791 // (this doesnt work, str_tb is just an object hashid) log_module(PY_LOG, LOG_INFO, "PYTHON error, traceback: %s", str_tb);
792}
793
794
e0f76584 795PyObject *python_build_handler_args(size_t argc, char *args[], PyObject *pIrcObj) {
4c216694 796 /* Sets up a python tuple with passed in arguments, prefixed by the Irc instance
f0e11521 797 which handlers use to interact with C.
4c216694 798 argc = number of args
799 args = array of args
800 pIrcObj = instance of the irc class
801 */
e0f76584 802 size_t i = 0, n;
803 PyObject *pArgs = NULL;
cbfd323c 804
e0f76584 805 pArgs = PyTuple_New(argc + 1);
806 Py_INCREF(pIrcObj);
807 PyTuple_SetItem(pArgs, i++, pIrcObj);
cbfd323c 808
e0f76584 809 if(args && argc) {
cbfd323c 810 PyObject *pValue;
e0f76584 811 for(n = 0; n < argc; ++n) {
812 pValue = PyString_FromString(args[n]);
cbfd323c 813 if(!pValue) {
e0f76584 814 Py_DECREF(pArgs);
815 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[n]);
816 return NULL;
cbfd323c 817 }
e0f76584 818 PyTuple_SetItem(pArgs, n+i, pValue);
cbfd323c 819 }
cbfd323c 820 }
e0f76584 821 return pArgs;
cbfd323c 822}
823
824PyObject *python_build_args(size_t argc, char *args[]) {
4c216694 825 /* Builds the passed in arguments into a python argument tuple.
826 argc = number of args
827 args = array of args
828 */
cbfd323c 829 size_t i;
830 PyObject *pArgs = NULL;
831
832 if(args && argc) {
833 pArgs = PyTuple_New(argc);
834 PyObject *pValue;
835 for(i = 0; i< argc; ++i) {
836 pValue = PyString_FromString(args[i]);
837 if(!pValue) {
838 Py_DECREF(pArgs);
839 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[i]);
840 return NULL;
841 }
842 PyTuple_SetItem(pArgs, i, pValue);
843 }
844 }
845 return pArgs;
d4e0f0c4 846}
caf97651 847
cbfd323c 848
e0f76584 849PyObject *new_irc_object(char *command_service, char *command_caller, char *command_target) {
4c216694 850 /* Creates a new instance of the irc class (from modpython.py) which is initalized
851 with current environment details like which service were talking to.
852 command_service = which service we are talking to, or empty string if none
853 command_caller = nick of user generating message, or empty string if none
854 command_target = If were reacting to something on a channel, this will
855 be set to the name of the channel. Otherwise empty
856 */
e0f76584 857 PyObject *pIrcArgs = NULL;
858 PyObject *pIrcClass;
859 PyObject *pIrcObj;
860
4c216694 861 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate irc class; %s %s %s", command_service, command_caller, command_target);
e0f76584 862 pIrcClass = PyObject_GetAttrString(base_module, "irc");
863 /* pIrcClass is a new reference */
864 if(pIrcClass && PyCallable_Check(pIrcClass)) {
865 //size_t i;
866 char *ircargs[] = {command_service, command_caller, command_target};
867 //PyObject *pValue;
868
869 pIrcArgs = python_build_args(3, ircargs);
e0f76584 870 pIrcObj = PyObject_CallObject(pIrcClass, pIrcArgs);
871 if(!pIrcObj) {
872 log_module(PY_LOG, LOG_ERROR, "IRC Class failed to load");
07559983 873 python_log_module();
874 //PyErr_Print();
e0f76584 875 }
876 if(pIrcArgs != NULL) {
877 Py_DECREF(pIrcArgs);
878 }
4c216694 879 Py_DECREF(pIrcClass);
e0f76584 880 return pIrcObj;
881 }
882 else {
4c216694 883 /* need to free pIrcClass here if it WAS found but was NOT callable? */
e0f76584 884 log_module(PY_LOG, LOG_ERROR, "Unable to find irc class");
885 return NULL;
886 }
887}
888
cbfd323c 889int python_call_handler(char *handler, char *args[], size_t argc, char *command_service, char *command_caller, char *command_target) {
4c216694 890 /* This is how we talk to modpython.c. First a new instance of the irc class is created using these
891 arguments to setup the current environment. Then the named method of the handler object is called
892 with the givin arguments.
cbfd323c 893 */
894 PyObject *pIrcObj;
895 PyObject *pArgs;
896 PyObject *pMethod;
897 PyObject *pValue;
898
e0f76584 899 log_module(PY_LOG, LOG_INFO, "attempting to call handler %s.", handler);
07559983 900 if(base_module != NULL && handler_object != NULL) {
cbfd323c 901 pIrcObj = new_irc_object(command_service, command_caller, command_target);
e0f76584 902 if(!pIrcObj) {
903 log_module(PY_LOG, LOG_INFO, "Can't get irc object. Bailing.");
904 return 0;
905 }
cbfd323c 906
e0f76584 907 pArgs = python_build_handler_args(argc, args, pIrcObj);
cbfd323c 908 pMethod = PyObject_GetAttrString(handler_object, handler);
909 if(pMethod && PyCallable_Check(pMethod)) {
07559983 910 /* Call the method, with the arguments */
cbfd323c 911 pValue = PyObject_CallObject(pMethod, pArgs);
912 if(pArgs) {
913 Py_DECREF(pArgs);
914 }
915 if(pValue != NULL) {
916 int ret;
917 ret = PyInt_AsLong(pValue);
918 if(ret == -1 && PyErr_Occurred()) {
07559983 919 //PyErr_Print();
cbfd323c 920 log_module(PY_LOG, LOG_INFO, "error converting return value of handler %s to type long. ", handler);
07559983 921 python_log_module();
cbfd323c 922 ret = 0;
923 }
924 log_module(PY_LOG, LOG_INFO, "handler %s was run successfully, returned %d.", handler, ret);
cbfd323c 925 Py_DECREF(pValue);
4c216694 926 Py_DECREF(pIrcObj);
927 Py_DECREF(pMethod);
cbfd323c 928 return ret;
929 }
930 else {
cbfd323c 931 /* TODO: instead of print errors, get them as strings
932 * and deal with them with normal x3 log system. */
cbfd323c 933 log_module(PY_LOG, LOG_WARNING, "call to handler %s failed", handler);
07559983 934 //PyErr_Print();
935 python_log_module();
4c216694 936 Py_DECREF(pIrcObj);
937 Py_DECREF(pMethod);
cbfd323c 938 return 0;
939 }
940 }
941 else { /* couldn't find handler methed */
4c216694 942 Py_DECREF(pArgs);
943 /* Free pMethod if it was found but not callable? */
cbfd323c 944 log_module(PY_LOG, LOG_ERROR, "Cannot find handler %s.", handler);
945 return 0;
946
947 }
948 }
949 else { /* No base module.. no python? */
e0f76584 950 log_module(PY_LOG, LOG_INFO, "Cannot handle %s, Python is not initialized.", handler);
cbfd323c 951 return 0;
952 }
953}
954
955PyObject *python_new_handler_object() {
4c216694 956 /* Create a new instance of the handler class.
957 This is called during python initilization (or reload)
958 and the result is saved and re-used.
959 */
cbfd323c 960 PyObject *pHandlerClass, *pHandlerObj;
961
962 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate python class handler");
963 pHandlerClass = PyObject_GetAttrString(base_module, "handler");
964 /* Class is a new reference */
965 if(pHandlerClass && PyCallable_Check(pHandlerClass)) {
e0f76584 966 /*PyObject *pValue; */
cbfd323c 967
968 pHandlerObj = PyObject_CallObject(pHandlerClass, NULL);
07559983 969 if(pHandlerObj != NULL) {
970 log_module(PY_LOG, LOG_INFO, "Created new python handler object.");
971 return pHandlerObj;
972 }
973 else {
974 log_module(PY_LOG, LOG_ERROR, "Unable to instanciate handler object");
975 //PyErr_Print();
976 python_log_module();
977 return NULL;
978 }
cbfd323c 979 }
980 else {
981 log_module(PY_LOG, LOG_ERROR, "Unable to find handler class");
07559983 982 //PyErr_Print();
983 python_log_module();
984 if(pHandlerClass) {
985 Py_DECREF(pHandlerClass);
986 }
cbfd323c 987 return NULL;
988 }
989}
990
4c216694 991/* ------------------------------------------------------------------------------- *
992 Some gateway functions to convert x3 callbacks into modpython.py callbacks.
993 Mostly we just build relevant args and call the proper handler method
994
995 debate: do we just register these and check them in python
996 for every one (slow?) or actually work out if a plugin needs
997 it first? We will start by doing it every time.
cbfd323c 998 */
0b350353 999static int
1000python_handle_join(struct modeNode *mNode)
1001{
4c216694 1002 /* callback for handle_join events.
1003 */
0b350353 1004 struct userNode *user = mNode->user;
1005 struct chanNode *channel = mNode->channel;
1006
a2c8c575 1007
caf97651 1008 log_module(PY_LOG, LOG_INFO, "python module handle_join");
a2c8c575 1009 if(!channel||!user) {
4c216694 1010 log_module(PY_LOG, LOG_WARNING, "Python code got join without channel or user!");
a2c8c575 1011 return 0;
1012 }
1013 else {
1014 char *args[] = {channel->name, user->nick};
e0f76584 1015 return python_call_handler("join", args, 2, "", "", "");
a2c8c575 1016 }
0b350353 1017}
1018
0ab7b4bc 1019static int
1020python_handle_server_link(struct server *server)
1021{
1022 log_module(PY_LOG, LOG_INFO, "python module handle_server_link");
1023 if(!server) {
1024 log_module(PY_LOG, LOG_WARNING, "Python code got server link without server!");
1025 return 0;
1026 }
1027 else {
1028 char *args[] = {server->name, server->description};
1029 return python_call_handler("server_link", args, 2, "", "", "");
1030 }
1031}
1032
1033static int
1034python_handle_new_user(struct userNode *user)
1035{
1036 log_module(PY_LOG, LOG_INFO, "Python module handle_new_user");
1037 if(!user) {
1038 log_module(PY_LOG, LOG_WARNING, "Python code got new_user without the user");
1039 return 0;
1040 }
1041 else {
1042 char *args[] = {user->nick, user->ident, user->hostname, user->info};
1043 return python_call_handler("new_user", args, 4, "", "", "");
1044 }
1045}
1046
1047static void
1048python_handle_nick_change(struct userNode *user, const char *old_nick)
1049{
1050 log_module(PY_LOG, LOG_INFO, "Python module handle_nick_change");
1051 if(!user) {
1052 log_module(PY_LOG, LOG_WARNING, "Python code got nick_change without the user!");
1053 }
1054 else {
1055 char *args[] = {user->nick, (char *)old_nick};
1056 python_call_handler("nick_change", args, 2, "", "", "");
1057 }
1058}
1059
4c216694 1060/* ----------------------------------------------------------------------------- */
1061
cbfd323c 1062
caf97651 1063int python_load() {
4c216694 1064 /* Init the python engine and do init work on modpython.py
1065 This is called during x3 startup, and on a python reload
1066 */
caf97651 1067 PyObject *pName;
ef5e0305 1068 char* buffer;
1069 char* env = getenv("PYTHONPATH");
1070
1071 if (env)
1072 env = strdup(env);
1073
1074 if (!env)
1075 setenv("PYTHONPATH", modpython_conf.scripts_dir, 1);
1076 else if (!strstr(env, modpython_conf.scripts_dir)) {
1077 buffer = (char*)malloc(strlen(env) + strlen(modpython_conf.scripts_dir) + 2);
1078 sprintf(buffer, "%s:%s", modpython_conf.scripts_dir, env);
1079 setenv("PYTHONPATH", buffer, 1);
1080 free(buffer);
1081 free(env);
1082 }
caf97651 1083
caf97651 1084 Py_Initialize();
50d61a79 1085 Py_InitModule("_svc", EmbMethods);
ed8d873c 1086 pName = PyString_FromString(modpython_conf.main_module);
caf97651 1087 base_module = PyImport_Import(pName);
1088 Py_DECREF(pName);
bc2f52df 1089
1090 Py_XDECREF(handler_object);
1091 handler_object = NULL;
1092
caf97651 1093 if(base_module != NULL) {
cbfd323c 1094 handler_object = python_new_handler_object();
1095 if(handler_object) {
e0f76584 1096 python_call_handler("init", NULL, 0, "", "", "");
cbfd323c 1097 return 1;
1098 }
1099 else {
1100 /* error handler class not found */
1101 log_module(PY_LOG, LOG_WARNING, "Failed to create handler object");
1102 return 0;
1103 }
caf97651 1104 }
1105 else {
07559983 1106 //PyErr_Print();
1107 python_log_module();
d4e0f0c4 1108 log_module(PY_LOG, LOG_WARNING, "Failed to load modpython.py");
a2c8c575 1109 return 0;
caf97651 1110 }
cbfd323c 1111 return 0;
caf97651 1112}
1113
caf97651 1114int
1115python_finalize(void) {
4c216694 1116 /* Called after X3 is fully up and running.
1117 Code can be put here that needs to run to init things, but
1118 which is sensitive to everything else in x3 being up and ready
1119 to go.
1120 */
caf97651 1121
1122 PyRun_SimpleString("print 'Hello, World of Python!'");
1123 log_module(PY_LOG, LOG_INFO, "python module finalize");
1124
1125 return 1;
1126}
1127
caf97651 1128static void
4c216694 1129python_cleanup(void) {
1130 /* Called on shutdown of the python module (or before reloading)
1131 */
413fd8ea 1132
caf97651 1133 log_module(PY_LOG, LOG_INFO, "python module cleanup");
413fd8ea 1134 if (PyErr_Occurred())
1135 PyErr_Clear();
f0e11521 1136 Py_Finalize(); /* Shut down python enterpreter */
caf97651 1137}
1138
4c216694 1139/* ---------------------------------------------------------------------------------- *
1140 Python module command handlers.
1141*/
caf97651 1142static MODCMD_FUNC(cmd_reload) {
4c216694 1143 /* reload the python system completely
1144 */
caf97651 1145 log_module(PY_LOG, LOG_INFO, "Shutting python down");
1146 python_cleanup();
1147 log_module(PY_LOG, LOG_INFO, "Loading python stuff");
1148 if(python_load()) {
a2c8c575 1149 reply("PYMSG_RELOAD_SUCCESS");
caf97651 1150 }
1151 else {
a2c8c575 1152 reply("PYMSG_RELOAD_FAILED");
caf97651 1153 }
1154 return 1;
1155}
1156
413fd8ea 1157static char* format_python_error(int space_nls) {
1158 PyObject* extype = NULL, *exvalue = NULL, *extraceback = NULL;
1159 PyObject* pextypestr = NULL, *pexvaluestr = NULL;
1160 char* extypestr = NULL, *exvaluestr = NULL;
1161 size_t retvallen = 0;
1162 char* retval = NULL, *tmp;
1163
1164 PyErr_Fetch(&extype, &exvalue, &extraceback);
1165 if (!extype)
1166 goto cleanup;
1167
1168 pextypestr = PyObject_Str(extype);
1169 if (!pextypestr)
1170 goto cleanup;
1171 extypestr = PyString_AsString(pextypestr);
1172 if (!extypestr)
1173 goto cleanup;
1174
1175 pexvaluestr = PyObject_Str(exvalue);
1176 if (pexvaluestr)
1177 exvaluestr = PyString_AsString(pexvaluestr);
1178
1179 retvallen = strlen(extypestr) + (exvaluestr ? strlen(exvaluestr) + 2 : 0) + 1;
1180 retval = (char*)malloc(retvallen);
1181 if (exvaluestr)
1182 snprintf(retval, retvallen, "%s: %s", extypestr, exvaluestr);
1183 else
1184 strncpy(retval, extypestr, retvallen);
1185
1186 if (space_nls) {
1187 tmp = retval;
1188 while (*tmp) {
1189 if (*tmp == '\n')
1190 *tmp = ' ';
1191 ++tmp;
1192 }
1193 }
1194
1195cleanup:
1196 if (PyErr_Occurred())
1197 PyErr_Clear(); /* ignore errors caused by formatting */
1198 Py_XDECREF(extype);
1199 Py_XDECREF(exvalue);
1200 Py_XDECREF(extraceback);
1201 Py_XDECREF(pextypestr);
1202 Py_XDECREF(pexvaluestr);
1203
1204 if (retval)
1205 return retval;
1206
1207 return strdup("unknown exception");
1208}
1209
8d670803 1210static MODCMD_FUNC(cmd_run) {
413fd8ea 1211 /* this method allows running arbitrary python commands.
1212 * use with care.
1213 */
1214 char* msg;
46f628b1 1215 PyObject* py_main_module;
1216 PyObject* py_globals;
1217 PyObject* py_locals;
1218 PyObject* py_retval;
1219 PyObject* extype, *exvalue, *extraceback;
1220 PyObject* exvaluestr = NULL;
1221 char* exmsg = NULL, *exmsgptr;
1222
1223 py_main_module = PyImport_AddModule("__main__");
1224 py_globals = py_locals = PyModule_GetDict(py_main_module);
413fd8ea 1225
a2c8c575 1226 msg = unsplit_string(argv + 1, argc - 1, NULL);
413fd8ea 1227
46f628b1 1228 py_retval = PyRun_String(msg, Py_file_input, py_globals, py_locals);
1229 if (py_retval == NULL) {
1230 PyErr_Fetch(&extype, &exvalue, &extraceback);
1231 if (exvalue != NULL) {
1232 exvaluestr = PyObject_Str(exvalue);
1233 exmsg = strdup(PyString_AS_STRING(exvaluestr));
1234 exmsgptr = exmsg;
1235 while (exmsgptr && *exmsgptr) {
1236 if (*exmsgptr == '\n' || *exmsgptr == '\r' || *exmsgptr == '\t')
1237 *exmsgptr = ' ';
1238 exmsgptr++;
1239 }
1240 }
1241 if (extype != NULL && exvalue != NULL && PyType_Check(extype)) {
1242 reply("PYMSG_RUN_EXCEPTION", ((PyTypeObject*)extype)->tp_name, exmsg);
413fd8ea 1243 } else
1244 reply("PYMSG_RUN_UNKNOWN_EXCEPTION");
46f628b1 1245
1246 if (extype != NULL)
1247 Py_DECREF(extype);
1248 if (exvalue != NULL)
1249 Py_DECREF(exvalue);
1250 if (extraceback != NULL)
1251 Py_DECREF(extraceback);
1252 if (exvaluestr != NULL)
1253 Py_DECREF(exvaluestr);
1254 if (exmsg)
1255 free(exmsg);
1256 } else {
1257 Py_DECREF(py_retval);
413fd8ea 1258 }
1259
a2c8c575 1260 return 1;
1261}
1262
07559983 1263#define numstrargs(X) sizeof(X) / sizeof(*X)
1264static MODCMD_FUNC(cmd_command) {
1265 char *plugin = argv[1];
1266 char *command = argv[2];
1267 char *msg; /* args */
1268 if(argc > 3) {
1269 msg = unsplit_string(argv + 3, argc - 3, NULL);
1270 }
1271 else {
1272 msg = "";
1273 }
1274 char *args[] = {plugin, command, msg};
1275 python_call_handler("cmd_command", args, numstrargs(args), cmd->parent->bot->nick, user?user->nick:"", channel?channel->name:"");
1276 return 1;
1277}
1278
ef5e0305 1279static void modpython_conf_read(void) {
1280 dict_t conf_node;
1281 char const* str;
1282
1283 if (!(conf_node = conf_get_data(MODPYTHON_CONF_NAME, RECDB_OBJECT))) {
1284 log_module(PY_LOG, LOG_ERROR, "config node '%s' is missing or has wrong type", MODPYTHON_CONF_NAME);
1285 return;
1286 }
1287
1288 str = database_get_data(conf_node, "scripts_dir", RECDB_QSTRING);
1289 modpython_conf.scripts_dir = str ? str : "./";
ed8d873c 1290
1291 str = database_get_data(conf_node, "main_module", RECDB_QSTRING);
1292 modpython_conf.main_module = str ? str : "modpython";
ef5e0305 1293}
1294
0b350353 1295int python_init(void) {
4c216694 1296 /* X3 calls this function on init of the module during startup. We use it to
1297 do all our setup tasks and bindings
1298 */
0b350353 1299
0b350353 1300 PY_LOG = log_register_type("Python", "file:python.log");
caf97651 1301 python_module = module_register("python", PY_LOG, "mod-python.help", NULL);
ef5e0305 1302 conf_register_reload(modpython_conf_read);
1303
caf97651 1304 log_module(PY_LOG, LOG_INFO, "python module init");
1305 message_register_table(msgtab);
1306
0b350353 1307/*
1308 reg_auth_func(python_check_messages);
1309 reg_handle_rename_func(python_rename_account);
1310 reg_unreg_func(python_unreg_account);
1311 conf_register_reload(python_conf_read);
0b350353 1312 saxdb_register("python", python_saxdb_read, python_saxdb_write);
0b350353 1313 modcmd_register(python_module, "send", cmd_send, 3, MODCMD_REQUIRE_AUTHED, NULL);
1314*/
caf97651 1315 modcmd_register(python_module, "reload", cmd_reload, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
8d670803 1316 modcmd_register(python_module, "run", cmd_run, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
07559983 1317 modcmd_register(python_module, "command", cmd_command, 3, MODCMD_REQUIRE_STAFF, NULL);
039a6658 1318
f0e11521 1319// Please help us by implementing any of the callbacks listed as TODO below. They already exist
1320// in x3, they just need handle_ bridges implemented. (see python_handle_join for an example)
0ab7b4bc 1321 reg_server_link_func(python_handle_server_link);
1322 reg_new_user_func(python_handle_new_user);
1323 reg_nick_change_func(python_handle_nick_change);
039a6658 1324//TODO: reg_del_user_func(python_handle_del_user);
1325//TODO: reg_account_func(python_handle_account); /* stamping of account name to the ircd */
1326//TODO: reg_handle_rename_func(python_handle_handle_rename); /* handle used to ALSO mean account name */
1327//TODO: reg_failpw_func(python_handle_failpw);
1328//TODO: reg_allowauth_func(python_handle_allowauth);
1329//TODO: reg_handle_merge_func(python_handle_merge);
1330//
1331//TODO: reg_oper_func(python_handle_oper);
1332//TODO: reg_new_channel_func(python_handle_new_channel);
caf97651 1333 reg_join_func(python_handle_join);
039a6658 1334//TODO: reg_del_channel_func(python_handle_del_channel);
1335//TODO: reg_part_func(python_handle_part);
1336//TODO: reg_kick_func(python_handle_kick);
1337//TODO: reg_topic_func(python_handle_topic);
1338//TODO: reg_channel_mode_func(python_handle_channel_mode);
1339
1340//TODO: reg_privmsg_func(python_handle_privmsg);
1341//TODO: reg_notice_func
1342//TODO: reg_svccmd_unbind_func(python_handle_svccmd_unbind);
1343//TODO: reg_chanmsg_func(python_handle_chanmsg);
1344//TODO: reg_allchanmsg_func
1345//TODO: reg_user_mode_func
1346
caf97651 1347 reg_exit_func(python_cleanup);
0b350353 1348
caf97651 1349 python_load();
0b350353 1350 return 1;
1351}
1352
1353#endif /* WITH_PYTHON */