]> jfr.im git - irc/evilnet/x3.git/blame - src/mod-python.c
python nick/channel lookup support
[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
25
26#include "Python.h"
27#include "chanserv.h"
28#include "conf.h"
29#include "modcmd.h"
30#include "nickserv.h"
31#include "opserv.h"
32#include "saxdb.h"
33#include "sendmail.h"
34#include "timeq.h"
35
a2c8c575 36/* TODO notes
37 *
38 * - Impliment most of proto-p10 irc_* commands for calling from scripts
39 * - Impliment functions to look up whois, channel, account, and reg-channel info for scripts
40 * - Impliment x3.conf settings for python variables like include path, etc.
41 * - kod-python.py calls for everything you can reg_ a handler for in x3
42 * - Some kind of system for getting needed binds bound automagicaly to make it easier
43 * to run peoples scripts and mod-python in general.
44 */
0b350353 45
46static const struct message_entry msgtab[] = {
caf97651 47 { "PYMSG_RELOAD_SUCCESS", "Reloaded Python scripts successfully." },
48 { "PYMSG_RELOAD_FAILED", "Error reloading Python scripts." },
0b350353 49 { NULL, NULL } /* sentenal */
50};
51
52static struct log_type *PY_LOG;
53const char *python_module_deps[] = { NULL };
54static struct module *python_module;
55
caf97651 56PyObject *base_module = NULL; /* Base python handling library */
57
a2c8c575 58static PyObject*
59emb_dump(PyObject *self, PyObject *args)
60{
61 char *buf;
62 int ret = 0;
63 char linedup[MAXLEN];
64
65 if(!PyArg_ParseTuple(args, "s:dump", &buf ))
66 return NULL;
67 safestrncpy(linedup, buf, sizeof(linedup));
68 if(parse_line(linedup, 1)) {
69 irc_raw(buf);
70 ret = 1;
71 }
72 return Py_BuildValue("i", ret);
73}
74
75static PyObject*
76emb_send_target_privmsg(PyObject *self, PyObject *args)
77{
78 int ret = 0;
79 char *servicenick;
80 char *channel;
81 char *buf;
82
83 struct service *service;
84
85 if(!PyArg_ParseTuple(args, "sss:reply", &servicenick, &channel, &buf ))
86 return NULL;
87 if(!(service = service_find(servicenick))) {
88 /* TODO: generate python exception here */
8d670803 89 return NULL;
a2c8c575 90 }
91 send_target_message(5, channel, service->bot, "%s", buf);
92 return Py_BuildValue("i", ret);
93}
94
8d670803 95static PyObject*
96emb_get_user(PyObject *self, PyObject *args)
97{
98 char *nick;
99 struct userNode *user;
100 struct modeNode *mn;
101 int n;
102 PyObject* pChanList;
103 if(!PyArg_ParseTuple(args, "s", &nick))
104 return NULL;
105 if(!(user = GetUserH(nick))) {
106 /* TODO: generate python exception here */
107 return NULL;
108 }
109 pChanList = PyTuple_New(user->channels.used);
110 for(n=0;n<user->channels.used;n++) {
111 mn = user->channels.list[n];
112 PyTuple_SetItem(pChanList, n, Py_BuildValue("s", mn->channel->name));
113 }
114 return Py_BuildValue("{s:s,s:s,s:s,s:s,s:s" /* format strings. s=string, i=int */
115 ",s:s,s:s,s:s,s:s,s:s" /* (format is key:value) O=object */
116 ",s:i,s:i,s:s,s:s,s:s" /* blocks of 5 for readability */
117 "s:O}",
118
119 "nick", user->nick,
120 "ident", user->ident,
121 "info", user->info,
122 "hostname", user->hostname,
123 "ip", irc_ntoa(&user->ip),
124
125 "fakehost", user->fakehost,
126 "sethost", user->sethost,
127 "crypthost", user->crypthost,
128 "cryptip", user->cryptip,
129 "numeric", user->numeric, /* TODO: only ifdef WITH_PROTOCOL_P10 */
130
131 "loc", user->loc,
132 "no_notice", user->no_notice,
133 "mark", user->mark,
134 "version_reply", user->version_reply,
135 "account", user->handle_info?user->handle_info->handle:NULL,
136 "channels", pChanList);
137}
138
139static PyObject*
140emb_get_channel(PyObject *self, PyObject *args)
141{
142 char *name;
143 struct chanNode *channel;
144 int n;
145 PyObject *pChannelMembers;
146 PyObject *pChannelBans;
147 PyObject *pChannelExempts;
148
149 if(!PyArg_ParseTuple(args, "s", &name))
150 return NULL;
151 if(!(channel = GetChannel(name))) {
152 /* TODO: generate py exception here */
153 return NULL;
154 }
155
156 /* build tuple of nicks in channel */
157 pChannelMembers = PyTuple_New(channel->members.used);
158 for(n=0;n < channel->members.used;n++) {
159 struct modeNode *mn = channel->members.list[n];
160 PyTuple_SetItem(pChannelMembers, n, Py_BuildValue("s", mn->user->nick));
161 }
162
163 /* build tuple of bans */
164 pChannelBans = PyTuple_New(channel->banlist.used);
165 for(n=0; n < channel->banlist.used;n++) {
166 struct banNode *bn = channel->banlist.list[n];
167 PyTuple_SetItem(pChannelBans, n,
168 Py_BuildValue("{s:s,s:s,s:i}",
169 "ban", bn->ban,
170 "who", bn->who,
171 "set", bn->set)
172 );
173 }
174
175
176 /* build tuple of exempts */
177 pChannelExempts = PyTuple_New(channel->exemptlist.used);
178 for(n=0; n < channel->exemptlist.used;n++) {
179 struct exemptNode *en = channel->exemptlist.list[n];
180 PyTuple_SetItem(pChannelExempts, n,
181 Py_BuildValue("{s:s,s:s,s:i}",
182 "ban", en->ban,
183 "who", en->who,
184 "set", en->set)
185 );
186 }
187
188
189
190 return Py_BuildValue("{s:s,s:s,s:s,s:i"
191 ",s:i,s:i,s:O,s:O,s:O}",
192
193 "name", channel->name,
194 "topic", channel->topic,
195 "topic_nick", channel->topic_nick,
196 "topic_time", channel->topic_time,
197
198 "timestamp", channel->timestamp,
199 "modes", channel->modes,
200 "members", pChannelMembers,
201 "bans", pChannelBans,
202 "exempts", pChannelExempts
203 );
204}
205
206/*
207static PyObject*
208emb_get_account(PyObject *self, PyObject *args)
209{
210 char *name;
211 if(!PyArg_ParseTuple(args, "s", &name))
212 return NULL;
213}
214*/
215
216
a2c8c575 217static PyMethodDef EmbMethods[] = {
218 {"dump", emb_dump, METH_VARARGS, "Dump raw P10 line to server"},
219 {"send_target_privmsg", emb_send_target_privmsg, METH_VARARGS, "Send a message to somewhere"},
8d670803 220 {"get_user", emb_get_user, METH_VARARGS, "Get details about a nickname"},
221 {"get_channel", emb_get_channel, METH_VARARGS, "Get details about a channel"},
a2c8c575 222 {NULL, NULL, 0, NULL}
223};
224
225
226int python_call_func(char *function, char *args[], size_t argc) {
caf97651 227 /* TODO: get arguments, pass through to python function */
228 PyObject *pFunc, *pValue;
a2c8c575 229 PyObject *pArgs = NULL;
caf97651 230 if(base_module != NULL) {
a2c8c575 231 log_module(PY_LOG, LOG_INFO, "Attempting to run python function %s", function);
232 pFunc = PyObject_GetAttrString(base_module, function);
233 /* pFunc is a new reference */
234 if(pFunc && PyCallable_Check(pFunc)) {
235 size_t i;
236 if(args && argc) {
237 pArgs = PyTuple_New(argc);
238 for(i = 0; i< argc; ++i) {
239 pValue = PyString_FromString(args[i]);
240 if(!pValue) {
241 Py_DECREF(pArgs);
242 Py_DECREF(pFunc);
243 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[i]);
244 return 0;
245 }
246 PyTuple_SetItem(pArgs, i, pValue);
247 }
248 }
249 pValue = PyObject_CallObject(pFunc, pArgs);
250 if(pArgs != NULL) {
251 Py_DECREF(pArgs);
252 }
253 if(pValue != NULL) {
254 int ret;
255 ret = PyInt_AsLong(pValue);
256 if(ret == -1 && PyErr_Occurred()) {
257 PyErr_Print();
258 log_module(PY_LOG, LOG_INFO, "error converting return value of %s to type long. ", function);
259 ret = 0;
260 }
261 log_module(PY_LOG, LOG_INFO, "%s was run successfully, returned %d.", function, ret);
262 /* TODO: convert pValue to c int, return it below */
263 Py_DECREF(pValue);
264 return ret;
265 }
266 else {
267 Py_DECREF(pFunc);
268 /* TODO: instead of print errors, get them as strings
269 * and deal with them with normal x3 log system. */
270 PyErr_Print();
271 log_module(PY_LOG, LOG_WARNING, "call to %s failed", function);
272 return 0;
273 }
274 }
275 else {
276 if(PyErr_Occurred())
277 PyErr_Print();
278 log_module(PY_LOG, LOG_WARNING, "function %s not found or uncallable", function);
279 return 0;
280 }
caf97651 281 }
282 else {
a2c8c575 283 return 0;
caf97651 284 }
0b350353 285}
286
caf97651 287
0b350353 288static int
289python_handle_join(struct modeNode *mNode)
290{
291 struct userNode *user = mNode->user;
292 struct chanNode *channel = mNode->channel;
293
a2c8c575 294
caf97651 295 log_module(PY_LOG, LOG_INFO, "python module handle_join");
a2c8c575 296 if(!channel||!user) {
297 return 0;
298 }
299 else {
300 char *args[] = {channel->name, user->nick};
301 return python_call_func("handle_join", args, 2);
302 }
0b350353 303}
304
caf97651 305int python_load() {
306 PyObject *pName;
307
308 setenv("PYTHONPATH", "/home/rubin/afternet/services/x3/x3-run/", 1);
309 Py_Initialize();
a2c8c575 310 Py_InitModule("svc", EmbMethods);
311 PyRun_SimpleString("import svc");
caf97651 312 /* TODO: get "mod-python" from x3.conf */
313 pName = PyString_FromString("mod-python");
314 base_module = PyImport_Import(pName);
315 Py_DECREF(pName);
316 if(base_module != NULL) {
a2c8c575 317 python_call_func("handle_init", NULL, 0);
318 return 1;
caf97651 319 }
320 else {
a2c8c575 321 PyErr_Print();
322 log_module(PY_LOG, LOG_WARNING, "Failed to load mod-python.py");
323 return 0;
caf97651 324 }
325}
326
327/* Called after X3 is fully up and running */
328int
329python_finalize(void) {
330
331 PyRun_SimpleString("print 'Hello, World of Python!'");
332 log_module(PY_LOG, LOG_INFO, "python module finalize");
333
334 return 1;
335}
336
337/* Called on shutdown of the module */
338static void
339python_cleanup(void)
340{
341 log_module(PY_LOG, LOG_INFO, "python module cleanup");
342 Py_Finalize(); /* Shut down python enterpriter */
343 return;
344}
345
346static MODCMD_FUNC(cmd_reload) {
347 log_module(PY_LOG, LOG_INFO, "Shutting python down");
348 python_cleanup();
349 log_module(PY_LOG, LOG_INFO, "Loading python stuff");
350 if(python_load()) {
a2c8c575 351 reply("PYMSG_RELOAD_SUCCESS");
caf97651 352 }
353 else {
a2c8c575 354 reply("PYMSG_RELOAD_FAILED");
caf97651 355 }
356 return 1;
357}
358
8d670803 359static MODCMD_FUNC(cmd_run) {
a2c8c575 360
361 char *msg;
362 msg = unsplit_string(argv + 1, argc - 1, NULL);
363 PyRun_SimpleString(msg);
364 return 1;
365}
366
0b350353 367/* Called on init of the module during startup */
368int python_init(void) {
369
0b350353 370 PY_LOG = log_register_type("Python", "file:python.log");
caf97651 371 python_module = module_register("python", PY_LOG, "mod-python.help", NULL);
372 log_module(PY_LOG, LOG_INFO, "python module init");
373 message_register_table(msgtab);
374
0b350353 375/*
376 reg_auth_func(python_check_messages);
377 reg_handle_rename_func(python_rename_account);
378 reg_unreg_func(python_unreg_account);
379 conf_register_reload(python_conf_read);
0b350353 380 saxdb_register("python", python_saxdb_read, python_saxdb_write);
0b350353 381 modcmd_register(python_module, "send", cmd_send, 3, MODCMD_REQUIRE_AUTHED, NULL);
382*/
caf97651 383 modcmd_register(python_module, "reload", cmd_reload, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
8d670803 384 modcmd_register(python_module, "run", cmd_run, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
caf97651 385 reg_join_func(python_handle_join);
386 reg_exit_func(python_cleanup);
0b350353 387
caf97651 388 python_load();
0b350353 389 return 1;
390}
391
392#endif /* WITH_PYTHON */