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