]> jfr.im git - irc/evilnet/x3.git/blame - src/mod-python.c
mod-python: refactor modeList conversion into separate 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 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
8d670803 499static PyObject*
039a6658 500emb_get_channel(UNUSED_ARG(PyObject *self), PyObject *args)
8d670803 501{
4c216694 502 /* Returns a python dict object with all sorts of info about a channel.
50d61a79 503 usage: _svc.get_channel(<name>)
4c216694 504 */
8d670803 505 char *name;
506 struct chanNode *channel;
cbfd323c 507 unsigned int n;
8d670803 508 PyObject *pChannelMembers;
509 PyObject *pChannelBans;
510 PyObject *pChannelExempts;
511
039a6658 512
8d670803 513 if(!PyArg_ParseTuple(args, "s", &name))
514 return NULL;
3f24e818 515
8d670803 516 if(!(channel = GetChannel(name))) {
3f24e818 517 PyErr_SetString(PyExc_Exception, "unknown channel");
8d670803 518 return NULL;
519 }
520
521 /* build tuple of nicks in channel */
92fb809b 522 pChannelMembers = pyobj_from_modelist(&channel->members);
8d670803 523
524 /* build tuple of bans */
525 pChannelBans = PyTuple_New(channel->banlist.used);
526 for(n=0; n < channel->banlist.used;n++) {
527 struct banNode *bn = channel->banlist.list[n];
528 PyTuple_SetItem(pChannelBans, n,
529 Py_BuildValue("{s:s,s:s,s:i}",
530 "ban", bn->ban,
531 "who", bn->who,
532 "set", bn->set)
533 );
534 }
535
8d670803 536 /* build tuple of exempts */
537 pChannelExempts = PyTuple_New(channel->exemptlist.used);
538 for(n=0; n < channel->exemptlist.used;n++) {
539 struct exemptNode *en = channel->exemptlist.list[n];
540 PyTuple_SetItem(pChannelExempts, n,
541 Py_BuildValue("{s:s,s:s,s:i}",
d4e0f0c4 542 "ban", en->exempt,
8d670803 543 "who", en->who,
544 "set", en->set)
545 );
546 }
547
8d670803 548 return Py_BuildValue("{s:s,s:s,s:s,s:i"
549 ",s:i,s:i,s:O,s:O,s:O}",
550
551 "name", channel->name,
552 "topic", channel->topic,
553 "topic_nick", channel->topic_nick,
554 "topic_time", channel->topic_time,
555
556 "timestamp", channel->timestamp,
557 "modes", channel->modes,
558 "members", pChannelMembers,
559 "bans", pChannelBans,
560 "exempts", pChannelExempts
561 );
562}
563
8d670803 564static PyObject*
039a6658 565emb_get_account(UNUSED_ARG(PyObject *self), PyObject *args)
8d670803 566{
4c216694 567 /* Returns a python dict object with all sorts of info about an account.
50d61a79 568 usage: _svc.get_account(<account name>)
4c216694 569 */
8d670803 570 char *name;
4c216694 571 struct handle_info *hi;
572
039a6658 573
8d670803 574 if(!PyArg_ParseTuple(args, "s", &name))
575 return NULL;
4c216694 576
577 hi = get_handle_info(name);
5b2b1df2 578
4c216694 579 if(!hi) {
5b2b1df2 580 PyErr_SetString(PyExc_Exception, "unknown account name");
4c216694 581 return NULL;
582 }
5b2b1df2 583
4c216694 584 return Py_BuildValue("{s:s,s:i,s:s,s:s,s:s"
585 ",s:s,s:s}",
586
587 "account", hi->handle,
588 "registered", hi->registered,
589 "last_seen", hi->lastseen,
590 "infoline", hi->infoline ? hi->infoline : "",
591 "email", hi->email_addr ? hi->email_addr : "",
592
593 "fakehost", hi->fakehost ? hi->fakehost : "",
594 "last_quit_host", hi->last_quit_host
595
596 /* TODO: */
597 /* users online authed to this account */
598 /* cookies */
599 /* nicks (nickserv nets only?) */
600 /* masks */
601 /* ignores */
602 /* channels */
603 );
8d670803 604}
8d670803 605
07559983 606static PyObject*
039a6658 607emb_get_info(UNUSED_ARG(PyObject *self), UNUSED_ARG(PyObject *args))
608{
609 /* return some info about the general setup
610 * of X3, such as what the chanserv's nickname
611 * is.
612 */
613
614
615 return Py_BuildValue("{s:s,s:s,s:s,s:s,s:s}",
616 "chanserv", chanserv? chanserv->nick : "ChanServ",
617 "nickserv", nickserv?nickserv->nick : "NickServ",
618 "opserv", opserv?opserv->nick : "OpServ",
619 "global", global?global->nick : "Global",
620 "spamserv", spamserv?spamserv->nick : "SpamServ");
621}
622
623static PyObject*
624emb_log_module(UNUSED_ARG(PyObject *self), PyObject *args)
07559983 625{
626 /* a gateway to standard X3 logging subsystem.
627 * level is a value 0 to 9 as defined by the log_severity enum in log.h.
07559983 628 *
629 * for now, all logs go to the PY_LOG log. In the future this will change.
630 */
631 char *message;
632 int ret = 0;
633 int level;
634
039a6658 635
07559983 636 if(!PyArg_ParseTuple(args, "is", &level, &message))
637 return NULL;
638
639 log_module(PY_LOG, level, "%s", message);
640
641 return Py_BuildValue("i", ret);
642}
8d670803 643
0076604e 644static PyObject*
645emb_kill(UNUSED_ARG(PyObject* self), PyObject* args) {
646 char const* from_nick, *target_nick, *message;
647 struct userNode *target;
648 struct service *service;
649
650 if (!PyArg_ParseTuple(args, "sss", &from_nick, &target_nick, &message))
651 return NULL;
652
653 if(!(service = service_find(from_nick))) {
654 PyErr_SetString(PyExc_Exception, "unknown service user specified as from user");
655 return NULL;
656 }
657
658 if ((target = GetUserH(target_nick)) == NULL) {
659 PyErr_SetString(PyExc_Exception, "unknown target user");
660 return NULL;
661 }
662
663 irc_kill(service->bot, target, message);
664
665 return Py_None;
666}
667
a2c8c575 668static PyMethodDef EmbMethods[] = {
07559983 669 /* Communication methods */
a2c8c575 670 {"dump", emb_dump, METH_VARARGS, "Dump raw P10 line to server"},
671 {"send_target_privmsg", emb_send_target_privmsg, METH_VARARGS, "Send a message to somewhere"},
d4e0f0c4 672 {"send_target_notice", emb_send_target_notice, METH_VARARGS, "Send a notice to somewhere"},
07559983 673 {"log_module", emb_log_module, METH_VARARGS, "Log something using the X3 log subsystem"},
039a6658 674//TODO: {"exec_cmd", emb_exec_cmd, METH_VARARGS, "execute x3 command provided"},
675// This should use environment from "python command" call to pass in, if available
0076604e 676 {"kill", emb_kill, METH_VARARGS, "Kill someone"},
039a6658 677//TODO: {"shun"
678//TODO: {"unshun"
679//TODO: {"gline", emb_gline, METH_VARARGS, "gline a mask"},
680//TODO: {"ungline", emb_ungline, METH_VARARGS, "remove a gline"},
681//TODO: {"kick", emb_kick, METH_VARARGS, "kick someone from a channel"},
682//TODO: {"channel_mode", emb_channel_mode, METH_VARARGS, "set modes on a channel"},
683//TODO: {"user_mode", emb_user_mode, METH_VARARGS, "Have x3 set usermodes on one of its own nicks"},
684//
685//TODO: {"get_config", emb_get_config, METH_VARARGS, "get x3.conf settings into a nested dict"},
686//TODO: {"config_set", emb_config_set, METH_VARARGS, "change a config setting 'on-the-fly'."},
687//
688//TODO: {"timeq_add", emb_timeq_new, METH_VARARGS, "some kind of interface to the timed event system."},
689//TODO: {"timeq_del", emb_timeq_new, METH_VARARGS, "some kind of interface to the timed event system."},
07559983 690 /* Information gathering methods */
8d670803 691 {"get_user", emb_get_user, METH_VARARGS, "Get details about a nickname"},
f0e11521 692 {"get_users", emb_get_users, METH_VARARGS, "Get all connected users"},
8d670803 693 {"get_channel", emb_get_channel, METH_VARARGS, "Get details about a channel"},
f0e11521 694 {"get_channels", emb_get_channels, METH_VARARGS, "Get all channels"},
d8f8d3b6 695 {"get_server", emb_get_server, METH_VARARGS, "Get details about a server"},
dcc1df5e 696 {"get_servers", emb_get_servers, METH_VARARGS, "Get all server names"},
4c216694 697 {"get_account", emb_get_account, METH_VARARGS, "Get details about an account"},
39d37f27 698 {"get_accounts", emb_get_accounts, METH_VARARGS, "Get all nickserv accounts"},
039a6658 699 {"get_info", emb_get_info, METH_VARARGS, "Get various misc info about x3"},
07559983 700 /* null terminator */
a2c8c575 701 {NULL, NULL, 0, NULL}
702};
703
704
f0e11521 705/*
706These functions set up the embedded environment for us to call out to
707modpython.py class methods.
4c216694 708 */
cbfd323c 709
07559983 710void python_log_module() {
711 /* Attempt to convert python errors to x3 log system */
712 PyObject *exc, *tb, *value, *tmp;
713 char *str_exc = "NONE";
714 char *str_value = "NONE";
715 char *str_tb = "NONE";
716
717 PyErr_Fetch(&exc, &value, &tb);
718
719 if(exc) {
720 if((tmp = PyObject_Str(exc)))
721 str_exc = PyString_AsString(tmp);
722 }
723 if(value) {
724 if((tmp = PyObject_Str(value)))
725 str_value = PyString_AsString(tmp);
726 }
727 if(tb) {
728 if((tmp = PyObject_Str(tb)))
729 str_tb = PyString_AsString(tmp);
730 }
731
732 /* Now restore it so we can print it (just in case)
733 * (should we do this only when running in debug mode?) */
734 PyErr_Restore(exc, value, tb);
735 PyErr_Print(); /* which of course, clears it again.. */
736
737 log_module(PY_LOG, LOG_WARNING, "PYTHON error: %s, value: %s", str_exc, str_value);
738
739 /* TODO: get the traceback using the traceback module via C api so we can add it to the X3 logs. See
740 * http://mail.python.org/pipermail/python-list/2003-March/192226.html */
741 // (this doesnt work, str_tb is just an object hashid) log_module(PY_LOG, LOG_INFO, "PYTHON error, traceback: %s", str_tb);
742}
743
744
e0f76584 745PyObject *python_build_handler_args(size_t argc, char *args[], PyObject *pIrcObj) {
4c216694 746 /* Sets up a python tuple with passed in arguments, prefixed by the Irc instance
f0e11521 747 which handlers use to interact with C.
4c216694 748 argc = number of args
749 args = array of args
750 pIrcObj = instance of the irc class
751 */
e0f76584 752 size_t i = 0, n;
753 PyObject *pArgs = NULL;
cbfd323c 754
e0f76584 755 pArgs = PyTuple_New(argc + 1);
756 Py_INCREF(pIrcObj);
757 PyTuple_SetItem(pArgs, i++, pIrcObj);
cbfd323c 758
e0f76584 759 if(args && argc) {
cbfd323c 760 PyObject *pValue;
e0f76584 761 for(n = 0; n < argc; ++n) {
762 pValue = PyString_FromString(args[n]);
cbfd323c 763 if(!pValue) {
e0f76584 764 Py_DECREF(pArgs);
765 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[n]);
766 return NULL;
cbfd323c 767 }
e0f76584 768 PyTuple_SetItem(pArgs, n+i, pValue);
cbfd323c 769 }
cbfd323c 770 }
e0f76584 771 return pArgs;
cbfd323c 772}
773
774PyObject *python_build_args(size_t argc, char *args[]) {
4c216694 775 /* Builds the passed in arguments into a python argument tuple.
776 argc = number of args
777 args = array of args
778 */
cbfd323c 779 size_t i;
780 PyObject *pArgs = NULL;
781
782 if(args && argc) {
783 pArgs = PyTuple_New(argc);
784 PyObject *pValue;
785 for(i = 0; i< argc; ++i) {
786 pValue = PyString_FromString(args[i]);
787 if(!pValue) {
788 Py_DECREF(pArgs);
789 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[i]);
790 return NULL;
791 }
792 PyTuple_SetItem(pArgs, i, pValue);
793 }
794 }
795 return pArgs;
d4e0f0c4 796}
caf97651 797
cbfd323c 798
e0f76584 799PyObject *new_irc_object(char *command_service, char *command_caller, char *command_target) {
4c216694 800 /* Creates a new instance of the irc class (from modpython.py) which is initalized
801 with current environment details like which service were talking to.
802 command_service = which service we are talking to, or empty string if none
803 command_caller = nick of user generating message, or empty string if none
804 command_target = If were reacting to something on a channel, this will
805 be set to the name of the channel. Otherwise empty
806 */
e0f76584 807 PyObject *pIrcArgs = NULL;
808 PyObject *pIrcClass;
809 PyObject *pIrcObj;
810
4c216694 811 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate irc class; %s %s %s", command_service, command_caller, command_target);
e0f76584 812 pIrcClass = PyObject_GetAttrString(base_module, "irc");
813 /* pIrcClass is a new reference */
814 if(pIrcClass && PyCallable_Check(pIrcClass)) {
815 //size_t i;
816 char *ircargs[] = {command_service, command_caller, command_target};
817 //PyObject *pValue;
818
819 pIrcArgs = python_build_args(3, ircargs);
e0f76584 820 pIrcObj = PyObject_CallObject(pIrcClass, pIrcArgs);
821 if(!pIrcObj) {
822 log_module(PY_LOG, LOG_ERROR, "IRC Class failed to load");
07559983 823 python_log_module();
824 //PyErr_Print();
e0f76584 825 }
826 if(pIrcArgs != NULL) {
827 Py_DECREF(pIrcArgs);
828 }
4c216694 829 Py_DECREF(pIrcClass);
e0f76584 830 return pIrcObj;
831 }
832 else {
4c216694 833 /* need to free pIrcClass here if it WAS found but was NOT callable? */
e0f76584 834 log_module(PY_LOG, LOG_ERROR, "Unable to find irc class");
835 return NULL;
836 }
837}
838
cbfd323c 839int python_call_handler(char *handler, char *args[], size_t argc, char *command_service, char *command_caller, char *command_target) {
4c216694 840 /* This is how we talk to modpython.c. First a new instance of the irc class is created using these
841 arguments to setup the current environment. Then the named method of the handler object is called
842 with the givin arguments.
cbfd323c 843 */
844 PyObject *pIrcObj;
845 PyObject *pArgs;
846 PyObject *pMethod;
847 PyObject *pValue;
848
e0f76584 849 log_module(PY_LOG, LOG_INFO, "attempting to call handler %s.", handler);
07559983 850 if(base_module != NULL && handler_object != NULL) {
cbfd323c 851 pIrcObj = new_irc_object(command_service, command_caller, command_target);
e0f76584 852 if(!pIrcObj) {
853 log_module(PY_LOG, LOG_INFO, "Can't get irc object. Bailing.");
854 return 0;
855 }
cbfd323c 856
e0f76584 857 pArgs = python_build_handler_args(argc, args, pIrcObj);
cbfd323c 858 pMethod = PyObject_GetAttrString(handler_object, handler);
859 if(pMethod && PyCallable_Check(pMethod)) {
07559983 860 /* Call the method, with the arguments */
cbfd323c 861 pValue = PyObject_CallObject(pMethod, pArgs);
862 if(pArgs) {
863 Py_DECREF(pArgs);
864 }
865 if(pValue != NULL) {
866 int ret;
867 ret = PyInt_AsLong(pValue);
868 if(ret == -1 && PyErr_Occurred()) {
07559983 869 //PyErr_Print();
cbfd323c 870 log_module(PY_LOG, LOG_INFO, "error converting return value of handler %s to type long. ", handler);
07559983 871 python_log_module();
cbfd323c 872 ret = 0;
873 }
874 log_module(PY_LOG, LOG_INFO, "handler %s was run successfully, returned %d.", handler, ret);
cbfd323c 875 Py_DECREF(pValue);
4c216694 876 Py_DECREF(pIrcObj);
877 Py_DECREF(pMethod);
cbfd323c 878 return ret;
879 }
880 else {
cbfd323c 881 /* TODO: instead of print errors, get them as strings
882 * and deal with them with normal x3 log system. */
cbfd323c 883 log_module(PY_LOG, LOG_WARNING, "call to handler %s failed", handler);
07559983 884 //PyErr_Print();
885 python_log_module();
4c216694 886 Py_DECREF(pIrcObj);
887 Py_DECREF(pMethod);
cbfd323c 888 return 0;
889 }
890 }
891 else { /* couldn't find handler methed */
4c216694 892 Py_DECREF(pArgs);
893 /* Free pMethod if it was found but not callable? */
cbfd323c 894 log_module(PY_LOG, LOG_ERROR, "Cannot find handler %s.", handler);
895 return 0;
896
897 }
898 }
899 else { /* No base module.. no python? */
e0f76584 900 log_module(PY_LOG, LOG_INFO, "Cannot handle %s, Python is not initialized.", handler);
cbfd323c 901 return 0;
902 }
903}
904
905PyObject *python_new_handler_object() {
4c216694 906 /* Create a new instance of the handler class.
907 This is called during python initilization (or reload)
908 and the result is saved and re-used.
909 */
cbfd323c 910 PyObject *pHandlerClass, *pHandlerObj;
911
912 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate python class handler");
913 pHandlerClass = PyObject_GetAttrString(base_module, "handler");
914 /* Class is a new reference */
915 if(pHandlerClass && PyCallable_Check(pHandlerClass)) {
e0f76584 916 /*PyObject *pValue; */
cbfd323c 917
918 pHandlerObj = PyObject_CallObject(pHandlerClass, NULL);
07559983 919 if(pHandlerObj != NULL) {
920 log_module(PY_LOG, LOG_INFO, "Created new python handler object.");
921 return pHandlerObj;
922 }
923 else {
924 log_module(PY_LOG, LOG_ERROR, "Unable to instanciate handler object");
925 //PyErr_Print();
926 python_log_module();
927 return NULL;
928 }
cbfd323c 929 }
930 else {
931 log_module(PY_LOG, LOG_ERROR, "Unable to find handler class");
07559983 932 //PyErr_Print();
933 python_log_module();
934 if(pHandlerClass) {
935 Py_DECREF(pHandlerClass);
936 }
cbfd323c 937 return NULL;
938 }
939}
940
4c216694 941/* ------------------------------------------------------------------------------- *
942 Some gateway functions to convert x3 callbacks into modpython.py callbacks.
943 Mostly we just build relevant args and call the proper handler method
944
945 debate: do we just register these and check them in python
946 for every one (slow?) or actually work out if a plugin needs
947 it first? We will start by doing it every time.
cbfd323c 948 */
0b350353 949static int
950python_handle_join(struct modeNode *mNode)
951{
4c216694 952 /* callback for handle_join events.
953 */
0b350353 954 struct userNode *user = mNode->user;
955 struct chanNode *channel = mNode->channel;
956
a2c8c575 957
caf97651 958 log_module(PY_LOG, LOG_INFO, "python module handle_join");
a2c8c575 959 if(!channel||!user) {
4c216694 960 log_module(PY_LOG, LOG_WARNING, "Python code got join without channel or user!");
a2c8c575 961 return 0;
962 }
963 else {
964 char *args[] = {channel->name, user->nick};
e0f76584 965 return python_call_handler("join", args, 2, "", "", "");
a2c8c575 966 }
0b350353 967}
968
0ab7b4bc 969static int
970python_handle_server_link(struct server *server)
971{
972 log_module(PY_LOG, LOG_INFO, "python module handle_server_link");
973 if(!server) {
974 log_module(PY_LOG, LOG_WARNING, "Python code got server link without server!");
975 return 0;
976 }
977 else {
978 char *args[] = {server->name, server->description};
979 return python_call_handler("server_link", args, 2, "", "", "");
980 }
981}
982
983static int
984python_handle_new_user(struct userNode *user)
985{
986 log_module(PY_LOG, LOG_INFO, "Python module handle_new_user");
987 if(!user) {
988 log_module(PY_LOG, LOG_WARNING, "Python code got new_user without the user");
989 return 0;
990 }
991 else {
992 char *args[] = {user->nick, user->ident, user->hostname, user->info};
993 return python_call_handler("new_user", args, 4, "", "", "");
994 }
995}
996
997static void
998python_handle_nick_change(struct userNode *user, const char *old_nick)
999{
1000 log_module(PY_LOG, LOG_INFO, "Python module handle_nick_change");
1001 if(!user) {
1002 log_module(PY_LOG, LOG_WARNING, "Python code got nick_change without the user!");
1003 }
1004 else {
1005 char *args[] = {user->nick, (char *)old_nick};
1006 python_call_handler("nick_change", args, 2, "", "", "");
1007 }
1008}
1009
4c216694 1010/* ----------------------------------------------------------------------------- */
1011
cbfd323c 1012
caf97651 1013int python_load() {
4c216694 1014 /* Init the python engine and do init work on modpython.py
1015 This is called during x3 startup, and on a python reload
1016 */
caf97651 1017 PyObject *pName;
ef5e0305 1018 char* buffer;
1019 char* env = getenv("PYTHONPATH");
1020
1021 if (env)
1022 env = strdup(env);
1023
1024 if (!env)
1025 setenv("PYTHONPATH", modpython_conf.scripts_dir, 1);
1026 else if (!strstr(env, modpython_conf.scripts_dir)) {
1027 buffer = (char*)malloc(strlen(env) + strlen(modpython_conf.scripts_dir) + 2);
1028 sprintf(buffer, "%s:%s", modpython_conf.scripts_dir, env);
1029 setenv("PYTHONPATH", buffer, 1);
1030 free(buffer);
1031 free(env);
1032 }
caf97651 1033
caf97651 1034 Py_Initialize();
50d61a79 1035 Py_InitModule("_svc", EmbMethods);
ed8d873c 1036 pName = PyString_FromString(modpython_conf.main_module);
caf97651 1037 base_module = PyImport_Import(pName);
1038 Py_DECREF(pName);
bc2f52df 1039
1040 Py_XDECREF(handler_object);
1041 handler_object = NULL;
1042
caf97651 1043 if(base_module != NULL) {
cbfd323c 1044 handler_object = python_new_handler_object();
1045 if(handler_object) {
e0f76584 1046 python_call_handler("init", NULL, 0, "", "", "");
cbfd323c 1047 return 1;
1048 }
1049 else {
1050 /* error handler class not found */
1051 log_module(PY_LOG, LOG_WARNING, "Failed to create handler object");
1052 return 0;
1053 }
caf97651 1054 }
1055 else {
07559983 1056 //PyErr_Print();
1057 python_log_module();
d4e0f0c4 1058 log_module(PY_LOG, LOG_WARNING, "Failed to load modpython.py");
a2c8c575 1059 return 0;
caf97651 1060 }
cbfd323c 1061 return 0;
caf97651 1062}
1063
caf97651 1064int
1065python_finalize(void) {
4c216694 1066 /* Called after X3 is fully up and running.
1067 Code can be put here that needs to run to init things, but
1068 which is sensitive to everything else in x3 being up and ready
1069 to go.
1070 */
caf97651 1071
1072 PyRun_SimpleString("print 'Hello, World of Python!'");
1073 log_module(PY_LOG, LOG_INFO, "python module finalize");
1074
1075 return 1;
1076}
1077
caf97651 1078static void
4c216694 1079python_cleanup(void) {
1080 /* Called on shutdown of the python module (or before reloading)
1081 */
413fd8ea 1082
caf97651 1083 log_module(PY_LOG, LOG_INFO, "python module cleanup");
413fd8ea 1084 if (PyErr_Occurred())
1085 PyErr_Clear();
f0e11521 1086 Py_Finalize(); /* Shut down python enterpreter */
caf97651 1087}
1088
4c216694 1089/* ---------------------------------------------------------------------------------- *
1090 Python module command handlers.
1091*/
caf97651 1092static MODCMD_FUNC(cmd_reload) {
4c216694 1093 /* reload the python system completely
1094 */
caf97651 1095 log_module(PY_LOG, LOG_INFO, "Shutting python down");
1096 python_cleanup();
1097 log_module(PY_LOG, LOG_INFO, "Loading python stuff");
1098 if(python_load()) {
a2c8c575 1099 reply("PYMSG_RELOAD_SUCCESS");
caf97651 1100 }
1101 else {
a2c8c575 1102 reply("PYMSG_RELOAD_FAILED");
caf97651 1103 }
1104 return 1;
1105}
1106
413fd8ea 1107static char* format_python_error(int space_nls) {
1108 PyObject* extype = NULL, *exvalue = NULL, *extraceback = NULL;
1109 PyObject* pextypestr = NULL, *pexvaluestr = NULL;
1110 char* extypestr = NULL, *exvaluestr = NULL;
1111 size_t retvallen = 0;
1112 char* retval = NULL, *tmp;
1113
1114 PyErr_Fetch(&extype, &exvalue, &extraceback);
1115 if (!extype)
1116 goto cleanup;
1117
1118 pextypestr = PyObject_Str(extype);
1119 if (!pextypestr)
1120 goto cleanup;
1121 extypestr = PyString_AsString(pextypestr);
1122 if (!extypestr)
1123 goto cleanup;
1124
1125 pexvaluestr = PyObject_Str(exvalue);
1126 if (pexvaluestr)
1127 exvaluestr = PyString_AsString(pexvaluestr);
1128
1129 retvallen = strlen(extypestr) + (exvaluestr ? strlen(exvaluestr) + 2 : 0) + 1;
1130 retval = (char*)malloc(retvallen);
1131 if (exvaluestr)
1132 snprintf(retval, retvallen, "%s: %s", extypestr, exvaluestr);
1133 else
1134 strncpy(retval, extypestr, retvallen);
1135
1136 if (space_nls) {
1137 tmp = retval;
1138 while (*tmp) {
1139 if (*tmp == '\n')
1140 *tmp = ' ';
1141 ++tmp;
1142 }
1143 }
1144
1145cleanup:
1146 if (PyErr_Occurred())
1147 PyErr_Clear(); /* ignore errors caused by formatting */
1148 Py_XDECREF(extype);
1149 Py_XDECREF(exvalue);
1150 Py_XDECREF(extraceback);
1151 Py_XDECREF(pextypestr);
1152 Py_XDECREF(pexvaluestr);
1153
1154 if (retval)
1155 return retval;
1156
1157 return strdup("unknown exception");
1158}
1159
8d670803 1160static MODCMD_FUNC(cmd_run) {
413fd8ea 1161 /* this method allows running arbitrary python commands.
1162 * use with care.
1163 */
1164 char* msg;
46f628b1 1165 PyObject* py_main_module;
1166 PyObject* py_globals;
1167 PyObject* py_locals;
1168 PyObject* py_retval;
1169 PyObject* extype, *exvalue, *extraceback;
1170 PyObject* exvaluestr = NULL;
1171 char* exmsg = NULL, *exmsgptr;
1172
1173 py_main_module = PyImport_AddModule("__main__");
1174 py_globals = py_locals = PyModule_GetDict(py_main_module);
413fd8ea 1175
a2c8c575 1176 msg = unsplit_string(argv + 1, argc - 1, NULL);
413fd8ea 1177
46f628b1 1178 py_retval = PyRun_String(msg, Py_file_input, py_globals, py_locals);
1179 if (py_retval == NULL) {
1180 PyErr_Fetch(&extype, &exvalue, &extraceback);
1181 if (exvalue != NULL) {
1182 exvaluestr = PyObject_Str(exvalue);
1183 exmsg = strdup(PyString_AS_STRING(exvaluestr));
1184 exmsgptr = exmsg;
1185 while (exmsgptr && *exmsgptr) {
1186 if (*exmsgptr == '\n' || *exmsgptr == '\r' || *exmsgptr == '\t')
1187 *exmsgptr = ' ';
1188 exmsgptr++;
1189 }
1190 }
1191 if (extype != NULL && exvalue != NULL && PyType_Check(extype)) {
1192 reply("PYMSG_RUN_EXCEPTION", ((PyTypeObject*)extype)->tp_name, exmsg);
413fd8ea 1193 } else
1194 reply("PYMSG_RUN_UNKNOWN_EXCEPTION");
46f628b1 1195
1196 if (extype != NULL)
1197 Py_DECREF(extype);
1198 if (exvalue != NULL)
1199 Py_DECREF(exvalue);
1200 if (extraceback != NULL)
1201 Py_DECREF(extraceback);
1202 if (exvaluestr != NULL)
1203 Py_DECREF(exvaluestr);
1204 if (exmsg)
1205 free(exmsg);
1206 } else {
1207 Py_DECREF(py_retval);
413fd8ea 1208 }
1209
a2c8c575 1210 return 1;
1211}
1212
07559983 1213#define numstrargs(X) sizeof(X) / sizeof(*X)
1214static MODCMD_FUNC(cmd_command) {
1215 char *plugin = argv[1];
1216 char *command = argv[2];
1217 char *msg; /* args */
1218 if(argc > 3) {
1219 msg = unsplit_string(argv + 3, argc - 3, NULL);
1220 }
1221 else {
1222 msg = "";
1223 }
1224 char *args[] = {plugin, command, msg};
1225 python_call_handler("cmd_command", args, numstrargs(args), cmd->parent->bot->nick, user?user->nick:"", channel?channel->name:"");
1226 return 1;
1227}
1228
ef5e0305 1229static void modpython_conf_read(void) {
1230 dict_t conf_node;
1231 char const* str;
1232
1233 if (!(conf_node = conf_get_data(MODPYTHON_CONF_NAME, RECDB_OBJECT))) {
1234 log_module(PY_LOG, LOG_ERROR, "config node '%s' is missing or has wrong type", MODPYTHON_CONF_NAME);
1235 return;
1236 }
1237
1238 str = database_get_data(conf_node, "scripts_dir", RECDB_QSTRING);
1239 modpython_conf.scripts_dir = str ? str : "./";
ed8d873c 1240
1241 str = database_get_data(conf_node, "main_module", RECDB_QSTRING);
1242 modpython_conf.main_module = str ? str : "modpython";
ef5e0305 1243}
1244
0b350353 1245int python_init(void) {
4c216694 1246 /* X3 calls this function on init of the module during startup. We use it to
1247 do all our setup tasks and bindings
1248 */
0b350353 1249
0b350353 1250 PY_LOG = log_register_type("Python", "file:python.log");
caf97651 1251 python_module = module_register("python", PY_LOG, "mod-python.help", NULL);
ef5e0305 1252 conf_register_reload(modpython_conf_read);
1253
caf97651 1254 log_module(PY_LOG, LOG_INFO, "python module init");
1255 message_register_table(msgtab);
1256
0b350353 1257/*
1258 reg_auth_func(python_check_messages);
1259 reg_handle_rename_func(python_rename_account);
1260 reg_unreg_func(python_unreg_account);
1261 conf_register_reload(python_conf_read);
0b350353 1262 saxdb_register("python", python_saxdb_read, python_saxdb_write);
0b350353 1263 modcmd_register(python_module, "send", cmd_send, 3, MODCMD_REQUIRE_AUTHED, NULL);
1264*/
caf97651 1265 modcmd_register(python_module, "reload", cmd_reload, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
8d670803 1266 modcmd_register(python_module, "run", cmd_run, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
07559983 1267 modcmd_register(python_module, "command", cmd_command, 3, MODCMD_REQUIRE_STAFF, NULL);
039a6658 1268
f0e11521 1269// Please help us by implementing any of the callbacks listed as TODO below. They already exist
1270// in x3, they just need handle_ bridges implemented. (see python_handle_join for an example)
0ab7b4bc 1271 reg_server_link_func(python_handle_server_link);
1272 reg_new_user_func(python_handle_new_user);
1273 reg_nick_change_func(python_handle_nick_change);
039a6658 1274//TODO: reg_del_user_func(python_handle_del_user);
1275//TODO: reg_account_func(python_handle_account); /* stamping of account name to the ircd */
1276//TODO: reg_handle_rename_func(python_handle_handle_rename); /* handle used to ALSO mean account name */
1277//TODO: reg_failpw_func(python_handle_failpw);
1278//TODO: reg_allowauth_func(python_handle_allowauth);
1279//TODO: reg_handle_merge_func(python_handle_merge);
1280//
1281//TODO: reg_oper_func(python_handle_oper);
1282//TODO: reg_new_channel_func(python_handle_new_channel);
caf97651 1283 reg_join_func(python_handle_join);
039a6658 1284//TODO: reg_del_channel_func(python_handle_del_channel);
1285//TODO: reg_part_func(python_handle_part);
1286//TODO: reg_kick_func(python_handle_kick);
1287//TODO: reg_topic_func(python_handle_topic);
1288//TODO: reg_channel_mode_func(python_handle_channel_mode);
1289
1290//TODO: reg_privmsg_func(python_handle_privmsg);
1291//TODO: reg_notice_func
1292//TODO: reg_svccmd_unbind_func(python_handle_svccmd_unbind);
1293//TODO: reg_chanmsg_func(python_handle_chanmsg);
1294//TODO: reg_allchanmsg_func
1295//TODO: reg_user_mode_func
1296
caf97651 1297 reg_exit_func(python_cleanup);
0b350353 1298
caf97651 1299 python_load();
0b350353 1300 return 1;
1301}
1302
1303#endif /* WITH_PYTHON */