]>
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 | ||
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 | |
46 | static 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 | ||
52 | static struct log_type *PY_LOG; | |
53 | const char *python_module_deps[] = { NULL }; | |
54 | static struct module *python_module; | |
55 | ||
caf97651 | 56 | PyObject *base_module = NULL; /* Base python handling library */ |
57 | ||
a2c8c575 | 58 | static PyObject* |
59 | emb_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 | ||
75 | static PyObject* | |
76 | emb_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 | 95 | static PyObject* |
96 | emb_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 | ||
139 | static PyObject* | |
140 | emb_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 | /* | |
207 | static PyObject* | |
208 | emb_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 | 217 | static 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 | ||
226 | int 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 | 288 | static int |
289 | python_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 | 305 | int 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 */ | |
328 | int | |
329 | python_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 */ | |
338 | static void | |
339 | python_cleanup(void) | |
340 | { | |
341 | log_module(PY_LOG, LOG_INFO, "python module cleanup"); | |
342 | Py_Finalize(); /* Shut down python enterpriter */ | |
343 | return; | |
344 | } | |
345 | ||
346 | static 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 | 359 | static 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 */ |
368 | int 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 */ |