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
5 * This file is part of x3.
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.
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.
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.
23 #ifdef WITH_PYTHON /* just disable this file if python doesnt exist */
25 #ifndef WITH_PROTOCOL_P10
26 #error mod-python is only supported with p10 protocol enabled
27 #endif /* WITH_PROTOCOL_P10 */
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.
46 * - modpython.py calls for everything you can reg_ a handler for in x3
47 * - Some kind of system for getting needed binds bound automagicaly to make it easier
48 * to run peoples' scripts and mod-python in general.
49 * - An interface to reading/writing data to x3.db. Maybe generic, or attached to account or channel reg records?
52 static const struct message_entry msgtab
[] = {
53 { "PYMSG_RELOAD_SUCCESS", "Reloaded Python scripts successfully." },
54 { "PYMSG_RELOAD_FAILED", "Error reloading Python scripts." },
55 { "PYMSG_RUN_UNKNOWN_EXCEPTION", "Error running python: unknown exception." },
56 { "PYMSG_RUN_EXCEPTION", "Error running python: %s: %s." },
57 { NULL
, NULL
} /* sentinel */
60 #define MODPYTHON_CONF_NAME "modules/python"
64 char const* scripts_dir
;
65 char const* main_module
;
68 static struct log_type
*PY_LOG
;
69 const char *python_module_deps
[] = { NULL
};
70 static struct module *python_module
;
72 PyObject
*base_module
= NULL
; /* Base python handling library */
73 PyObject
*handler_object
= NULL
; /* instance of handler class */
76 extern struct userNode
*global
, *chanserv
, *opserv
, *nickserv
, *spamserv
;
79 Some hooks you can call from modpython.py to interact with the
80 service. These emb_* functions are available as _svc.* in python. */
82 struct _tuple_dict_extra
{
87 static void pyobj_release_tuple(PyObject
* tuple
, size_t n
) {
93 for (i
= 0; i
< n
; ++i
)
94 Py_XDECREF(PyTuple_GET_ITEM(tuple
, i
));
99 static int _dict_iter_fill_tuple(char const* key
, UNUSED_ARG(void* data
), void* extra
) {
101 struct _tuple_dict_extra
* real_extra
= (struct _tuple_dict_extra
*)extra
;
103 if ((tmp
= PyString_FromString(key
)) == NULL
)
106 if (PyTuple_SetItem(real_extra
->data
, *(int*)real_extra
->extra
, tmp
)) {
111 *real_extra
->extra
= *real_extra
->extra
+ 1;
116 pyobj_from_dict_t(dict_t d
) {
119 struct _tuple_dict_extra extra
;
121 if ((retval
= PyTuple_New(dict_size(d
))) == NULL
)
127 if (dict_foreach(d
, _dict_iter_fill_tuple
, (void*)&extra
) != NULL
) {
128 pyobj_release_tuple(retval
, n
);
135 /* get a tuple with all users in it */
137 emb_get_users(UNUSED_ARG(PyObject
*self
), PyObject
*args
) {
138 if (!PyArg_ParseTuple(args
, ""))
141 return pyobj_from_dict_t(clients
);
144 /* get a tuple with all channels in it */
146 emb_get_channels(UNUSED_ARG(PyObject
* self
), PyObject
* args
) {
147 if (!PyArg_ParseTuple(args
, ""))
150 return pyobj_from_dict_t(channels
);
154 emb_get_servers(UNUSED_ARG(PyObject
* self
), PyObject
* args
) {
155 if (!PyArg_ParseTuple(args
, ""))
158 return pyobj_from_dict_t(servers
);
162 emb_get_accounts(UNUSED_ARG(PyObject
* self
), PyObject
* args
) {
163 if (!PyArg_ParseTuple(args
, ""))
166 return pyobj_from_dict_t(nickserv_handle_dict
);
170 emb_dump(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
172 /* Dump a raw string into the socket
173 usage: _svc.dump(<P10 string>)
177 char linedup
[MAXLEN
];
180 if(!PyArg_ParseTuple(args
, "s:dump", &buf
))
183 safestrncpy(linedup
, buf
, sizeof(linedup
));
185 if(parse_line(linedup
, 1)) {
189 PyErr_SetString(PyExc_Exception
, "invalid protocol message");
193 return Py_BuildValue("i", ret
);
197 emb_send_target_privmsg(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
200 usage: _svc.send_target_privmsg(<servicenick_from>, <nick_to>, <message>)
207 struct service
*service
;
210 if(!PyArg_ParseTuple(args
, "sss:reply", &servicenick
, &channel
, &buf
))
213 if (buf
== NULL
|| strlen(buf
) == 0) {
214 PyErr_SetString(PyExc_Exception
, "invalid empty message");
218 if(!(service
= service_find(servicenick
))) {
219 PyErr_SetString(PyExc_Exception
, "no such service nick");
223 ret
= send_target_message(5, channel
, service
->bot
, "%s", buf
);
224 return Py_BuildValue("i", ret
);
228 emb_send_target_notice(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
231 usage: _svc.send_target_notice(<servicenick_from>, <nick_to>, <message>)
238 struct service
*service
;
240 if(!PyArg_ParseTuple(args
, "sss:reply", &servicenick
, &target
, &buf
))
243 if (buf
== NULL
|| strlen(buf
) == 0) {
244 PyErr_SetString(PyExc_Exception
, "invalid empty message");
248 if(!(service
= service_find(servicenick
))) {
249 PyErr_SetString(PyExc_Exception
, "no such service nick");
253 ret
= send_target_message(4, target
, service
->bot
, "%s", buf
);
255 return Py_BuildValue("i", ret
);
259 pyobj_from_usernode(struct userNode
* user
) {
262 PyObject
* retval
= NULL
;
263 PyObject
* pChanList
= PyTuple_New(user
->channels
.used
);
265 if (pChanList
== NULL
)
268 for (n
=0; n
< user
->channels
.used
; n
++) {
269 mn
= user
->channels
.list
[n
];
270 if (PyTuple_SetItem(pChanList
, n
, Py_BuildValue("s", mn
->channel
->name
)))
274 retval
= Py_BuildValue("{"
278 "s: s, " /* hostname */
280 "s: s, " /* fakehost */
281 "s: s, " /* sethost */
282 "s: s, " /* crypthost */
283 "s: s, " /* cryptip */
284 "s: s, " /* numeric */
286 "s: i, " /* no_notice */
288 "s: s, " /* version_reply */
289 "s: s, " /* account */
290 "s: O}", /* channels */
292 "ident", user
->ident
,
294 "hostname", user
->hostname
,
295 "ip", irc_ntoa(&user
->ip
),
296 "fakehost", user
->fakehost
,
297 "sethost", user
->sethost
,
298 "crypthost", user
->crypthost
,
299 "cryptip", user
->cryptip
,
300 "numeric", user
->numeric
,
302 "no_notice", user
->no_notice
,
304 "version_reply", user
->version_reply
,
305 "account", user
->handle_info
? user
->handle_info
->handle
: NULL
,
306 "channels", pChanList
);
315 pyobj_release_tuple(pChanList
, n
);
321 emb_get_user(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
323 /* Get a python object containing everything x3 knows about a user, by nick.
324 usage: _svc.get_user(<nick>)
327 struct userNode
*user
;
329 if(!PyArg_ParseTuple(args
, "s", &nick
))
332 if(!(user
= GetUserH(nick
))) {
333 PyErr_SetString(PyExc_Exception
, "no such user");
337 return pyobj_from_usernode(user
);
341 pyobj_from_server(struct server
* srv
) {
343 PyObject
* tmp
= NULL
;
344 PyObject
* retval
= NULL
;
345 PyObject
* users
= PyTuple_New(srv
->clients
);
351 for (n
= 0; n
< srv
->num_mask
; ++n
) {
352 if (srv
->users
[n
] == NULL
)
355 tmp
= PyString_FromString(srv
->users
[n
]->nick
);
359 if (PyTuple_SetItem(users
, idx
++, tmp
))
363 retval
= Py_BuildValue("{"
366 "s:l," /* link_time */
367 "s:s," /* description */
369 "s:I," /* num_mask */
372 "s:I," /* max_clients */
374 "s:I," /* self_burst */
381 "link_time", srv
->link_time
,
382 "description", srv
->description
,
383 "numeric", srv
->numeric
,
384 "num_mask", srv
->num_mask
,
386 "clients", srv
->clients
,
387 "max_clients", srv
->max_clients
,
389 "self_burst", srv
->self_burst
,
390 "uplink", srv
->uplink
? srv
->uplink
->name
: NULL
,
401 pyobj_release_tuple(users
, idx
);
407 emb_get_server(UNUSED_ARG(PyObject
* self
), PyObject
* args
) {
411 if (!PyArg_ParseTuple(args
, "s", &name
))
414 if (name
== NULL
|| strlen(name
) == 0) {
415 PyErr_SetString(PyExc_Exception
, "invalid server name");
419 if ((srv
= GetServerH(name
)) == NULL
) {
420 PyErr_SetString(PyExc_Exception
, "unknown server");
424 return pyobj_from_server(srv
);
428 pyobj_from_modelist(struct modeList
* mode
) {
431 PyObject
* retval
= PyTuple_New(mode
->used
);
436 for (n
= 0; n
< mode
->used
; ++n
) {
437 struct modeNode
* mn
= mode
->list
[n
];
438 tmp
= PyString_FromString(mn
->user
->nick
);
440 pyobj_release_tuple(retval
, n
);
444 if (PyTuple_SetItem(retval
, n
, tmp
)) {
445 pyobj_release_tuple(retval
, n
);
454 pyobj_from_banlist(struct banList
* bans
) {
458 PyObject
* retval
= PyTuple_New(bans
->used
);
463 for (n
= 0; n
< bans
->used
; ++n
) {
466 tmp
= Py_BuildValue("{s:s,s:s,s:l}",
467 "ban", bn
->ban
, "who", bn
->who
, "set", bn
->set
);
469 if (tmp
== NULL
|| PyTuple_SetItem(retval
, n
, tmp
)) {
470 pyobj_release_tuple(retval
, n
);
479 pyobj_from_exemptlist(struct exemptList
* exmp
) {
481 struct exemptNode
* en
;
483 PyObject
* retval
= PyTuple_New(exmp
->used
);
488 for (n
= 0; n
< exmp
->used
; ++n
) {
491 tmp
= Py_BuildValue("{s:s,s:s,s:l}",
492 "ban", en
->exempt
, "who", en
->who
, "set", en
->set
);
494 if (tmp
== NULL
|| PyTuple_SetItem(retval
, n
, tmp
)) {
495 pyobj_release_tuple(retval
, n
);
504 emb_get_channel(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
506 /* Returns a python dict object with all sorts of info about a channel.
507 usage: _svc.get_channel(<name>)
510 struct chanNode
*channel
;
511 PyObject
*pChannelMembers
= NULL
;
512 PyObject
*pChannelBans
= NULL
;
513 PyObject
*pChannelExempts
= NULL
;
514 PyObject
*retval
= NULL
;
517 if(!PyArg_ParseTuple(args
, "s", &name
))
520 if(!(channel
= GetChannel(name
))) {
521 PyErr_SetString(PyExc_Exception
, "unknown channel");
525 /* build tuple of nicks in channel */
526 pChannelMembers
= pyobj_from_modelist(&channel
->members
);
527 if (pChannelMembers
== NULL
)
530 /* build tuple of bans */
531 pChannelBans
= pyobj_from_banlist(&channel
->banlist
);
532 if (pChannelBans
== NULL
)
535 /* build tuple of exempts */
536 pChannelExempts
= pyobj_from_exemptlist(&channel
->exemptlist
);
537 if (pChannelExempts
== NULL
)
540 retval
= Py_BuildValue("{s:s,s:s,s:s,s:i"
541 ",s:i,s:i,s:O,s:O,s:O}",
543 "name", channel
->name
,
544 "topic", channel
->topic
,
545 "topic_nick", channel
->topic_nick
,
546 "topic_time", channel
->topic_time
,
548 "timestamp", channel
->timestamp
,
549 "modes", channel
->modes
,
550 "members", pChannelMembers
,
551 "bans", pChannelBans
,
552 "exempts", pChannelExempts
561 pyobj_release_tuple(pChannelExempts
, channel
->exemptlist
.used
);
562 pyobj_release_tuple(pChannelBans
, channel
->banlist
.used
);
563 pyobj_release_tuple(pChannelMembers
, channel
->members
.used
);
569 emb_get_account(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
571 /* Returns a python dict object with all sorts of info about an account.
572 usage: _svc.get_account(<account name>)
575 struct handle_info
*hi
;
578 if(!PyArg_ParseTuple(args
, "s", &name
))
581 hi
= get_handle_info(name
);
584 PyErr_SetString(PyExc_Exception
, "unknown account name");
588 return Py_BuildValue("{s:s,s:i,s:s,s:s,s:s"
591 "account", hi
->handle
,
592 "registered", hi
->registered
,
593 "last_seen", hi
->lastseen
,
594 "infoline", hi
->infoline
? hi
->infoline
: "",
595 "email", hi
->email_addr
? hi
->email_addr
: "",
597 "fakehost", hi
->fakehost
? hi
->fakehost
: "",
598 "last_quit_host", hi
->last_quit_host
601 /* users online authed to this account */
603 /* nicks (nickserv nets only?) */
611 emb_get_info(UNUSED_ARG(PyObject
*self
), UNUSED_ARG(PyObject
*args
))
613 /* return some info about the general setup
614 * of X3, such as what the chanserv's nickname
619 return Py_BuildValue("{s:s,s:s,s:s,s:s,s:s}",
620 "chanserv", chanserv
? chanserv
->nick
: "ChanServ",
621 "nickserv", nickserv
?nickserv
->nick
: "NickServ",
622 "opserv", opserv
?opserv
->nick
: "OpServ",
623 "global", global
?global
->nick
: "Global",
624 "spamserv", spamserv
?spamserv
->nick
: "SpamServ");
628 emb_log_module(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
630 /* a gateway to standard X3 logging subsystem.
631 * level is a value 0 to 9 as defined by the log_severity enum in log.h.
633 * for now, all logs go to the PY_LOG log. In the future this will change.
640 if(!PyArg_ParseTuple(args
, "is", &level
, &message
))
643 log_module(PY_LOG
, level
, "%s", message
);
645 return Py_BuildValue("i", ret
);
649 emb_kill(UNUSED_ARG(PyObject
* self
), PyObject
* args
) {
650 char const* from_nick
, *target_nick
, *message
;
651 struct userNode
*target
;
652 struct service
*service
;
654 if (!PyArg_ParseTuple(args
, "sss", &from_nick
, &target_nick
, &message
))
657 if(!(service
= service_find(from_nick
))) {
658 PyErr_SetString(PyExc_Exception
, "unknown service user specified as from user");
662 if ((target
= GetUserH(target_nick
)) == NULL
) {
663 PyErr_SetString(PyExc_Exception
, "unknown target user");
667 irc_kill(service
->bot
, target
, message
);
672 struct py_timeq_extra
{
678 void py_timeq_callback(void* data
) {
679 struct py_timeq_extra
* extra
= (struct py_timeq_extra
*)data
;
681 PyObject
* retval
= PyObject_Call(extra
->func
, extra
->arg
, NULL
);
684 Py_DECREF(extra
->func
);
685 Py_DECREF(extra
->arg
);
689 emb_timeq_add(UNUSED_ARG(PyObject
* self
), PyObject
* args
) {
691 PyObject
* func
, *arg
;
692 struct py_timeq_extra
* extra
;
694 if (!PyArg_ParseTuple(args
, "lOO", &when
, &func
, &arg
))
697 if (!PyFunction_Check(func
)) {
698 PyErr_SetString(PyExc_Exception
, "first argument must be a function");
702 if (!PyTuple_Check(arg
)) {
703 PyErr_SetString(PyExc_Exception
, "second argument must be a tuple");
707 extra
= malloc(sizeof(struct py_timeq_extra
));
709 PyErr_SetString(PyExc_Exception
, "out of memory");
719 timeq_add(when
, py_timeq_callback
, (void*)extra
);
725 emb_timeq_del(UNUSED_ARG(PyObject
* self
), PyObject
* args
) {
727 * This function will delete all python-added callbacks registered
728 * to run at the given time, regardless of their data. This is due to
729 * the unnecessary extra burden it would require to get the same data for
734 if (!PyArg_ParseTuple(args
, "l", &when
))
737 timeq_del(when
, py_timeq_callback
, NULL
, TIMEQ_IGNORE_DATA
);
742 static PyMethodDef EmbMethods
[] = {
743 /* Communication methods */
744 {"dump", emb_dump
, METH_VARARGS
, "Dump raw P10 line to server"},
745 {"send_target_privmsg", emb_send_target_privmsg
, METH_VARARGS
, "Send a message to somewhere"},
746 {"send_target_notice", emb_send_target_notice
, METH_VARARGS
, "Send a notice to somewhere"},
747 {"log_module", emb_log_module
, METH_VARARGS
, "Log something using the X3 log subsystem"},
748 //TODO: {"exec_cmd", emb_exec_cmd, METH_VARARGS, "execute x3 command provided"},
749 // This should use environment from "python command" call to pass in, if available
750 {"kill", emb_kill
, METH_VARARGS
, "Kill someone"},
753 //TODO: {"gline", emb_gline, METH_VARARGS, "gline a mask"},
754 //TODO: {"ungline", emb_ungline, METH_VARARGS, "remove a gline"},
755 //TODO: {"kick", emb_kick, METH_VARARGS, "kick someone from a channel"},
756 //TODO: {"channel_mode", emb_channel_mode, METH_VARARGS, "set modes on a channel"},
757 //TODO: {"user_mode", emb_user_mode, METH_VARARGS, "Have x3 set usermodes on one of its own nicks"},
759 //TODO: {"get_config", emb_get_config, METH_VARARGS, "get x3.conf settings into a nested dict"},
760 //TODO: {"config_set", emb_config_set, METH_VARARGS, "change a config setting 'on-the-fly'."},
762 {"timeq_add", emb_timeq_add
, METH_VARARGS
, "add function to callback to the event system"},
763 {"timeq_del", emb_timeq_del
, METH_VARARGS
, "remove function to callback from the event system"},
765 /* Information gathering methods */
766 {"get_user", emb_get_user
, METH_VARARGS
, "Get details about a nickname"},
767 {"get_users", emb_get_users
, METH_VARARGS
, "Get all connected users"},
768 {"get_channel", emb_get_channel
, METH_VARARGS
, "Get details about a channel"},
769 {"get_channels", emb_get_channels
, METH_VARARGS
, "Get all channels"},
770 {"get_server", emb_get_server
, METH_VARARGS
, "Get details about a server"},
771 {"get_servers", emb_get_servers
, METH_VARARGS
, "Get all server names"},
772 {"get_account", emb_get_account
, METH_VARARGS
, "Get details about an account"},
773 {"get_accounts", emb_get_accounts
, METH_VARARGS
, "Get all nickserv accounts"},
774 {"get_info", emb_get_info
, METH_VARARGS
, "Get various misc info about x3"},
775 /* null terminator */
776 {NULL
, NULL
, 0, NULL
}
781 These functions set up the embedded environment for us to call out to
782 modpython.py class methods.
785 void python_log_module() {
786 /* Attempt to convert python errors to x3 log system */
787 PyObject
*exc
, *tb
, *value
, *tmp
;
788 char *str_exc
= "NONE";
789 char *str_value
= "NONE";
790 char *str_tb
= "NONE";
792 PyErr_Fetch(&exc
, &value
, &tb
);
795 if((tmp
= PyObject_Str(exc
)))
796 str_exc
= PyString_AsString(tmp
);
799 if((tmp
= PyObject_Str(value
)))
800 str_value
= PyString_AsString(tmp
);
803 if((tmp
= PyObject_Str(tb
)))
804 str_tb
= PyString_AsString(tmp
);
807 /* Now restore it so we can print it (just in case)
808 * (should we do this only when running in debug mode?) */
809 PyErr_Restore(exc
, value
, tb
);
810 PyErr_Print(); /* which of course, clears it again.. */
812 log_module(PY_LOG
, LOG_WARNING
, "PYTHON error: %s, value: %s", str_exc
, str_value
);
814 /* TODO: get the traceback using the traceback module via C api so we can add it to the X3 logs. See
815 * http://mail.python.org/pipermail/python-list/2003-March/192226.html */
816 // (this doesnt work, str_tb is just an object hashid) log_module(PY_LOG, LOG_INFO, "PYTHON error, traceback: %s", str_tb);
820 PyObject
*python_build_handler_args(size_t argc
, char *args
[], PyObject
*pIrcObj
) {
821 /* Sets up a python tuple with passed in arguments, prefixed by the Irc instance
822 which handlers use to interact with C.
823 argc = number of args
825 pIrcObj = instance of the irc class
828 PyObject
*pArgs
= NULL
;
830 pArgs
= PyTuple_New(argc
+ 1);
832 PyTuple_SetItem(pArgs
, i
++, pIrcObj
);
836 for(n
= 0; n
< argc
; ++n
) {
837 pValue
= PyString_FromString(args
[n
]);
840 log_module(PY_LOG
, LOG_INFO
, "Unable to convert '%s' to python string", args
[n
]);
843 PyTuple_SetItem(pArgs
, n
+i
, pValue
);
849 PyObject
*python_build_args(size_t argc
, char *args
[]) {
850 /* Builds the passed in arguments into a python argument tuple.
851 argc = number of args
855 PyObject
*pArgs
= NULL
;
858 pArgs
= PyTuple_New(argc
);
860 for(i
= 0; i
< argc
; ++i
) {
861 pValue
= PyString_FromString(args
[i
]);
864 log_module(PY_LOG
, LOG_INFO
, "Unable to convert '%s' to python string", args
[i
]);
867 PyTuple_SetItem(pArgs
, i
, pValue
);
874 PyObject
*new_irc_object(char *command_service
, char *command_caller
, char *command_target
) {
875 /* Creates a new instance of the irc class (from modpython.py) which is initalized
876 with current environment details like which service were talking to.
877 command_service = which service we are talking to, or empty string if none
878 command_caller = nick of user generating message, or empty string if none
879 command_target = If were reacting to something on a channel, this will
880 be set to the name of the channel. Otherwise empty
882 PyObject
*pIrcArgs
= NULL
;
886 log_module(PY_LOG
, LOG_INFO
, "Attempting to instanciate irc class; %s %s %s", command_service
, command_caller
, command_target
);
887 pIrcClass
= PyObject_GetAttrString(base_module
, "irc");
888 /* pIrcClass is a new reference */
889 if(pIrcClass
&& PyCallable_Check(pIrcClass
)) {
891 char *ircargs
[] = {command_service
, command_caller
, command_target
};
894 pIrcArgs
= python_build_args(3, ircargs
);
895 pIrcObj
= PyObject_CallObject(pIrcClass
, pIrcArgs
);
897 log_module(PY_LOG
, LOG_ERROR
, "IRC Class failed to load");
901 if(pIrcArgs
!= NULL
) {
904 Py_DECREF(pIrcClass
);
908 /* need to free pIrcClass here if it WAS found but was NOT callable? */
909 log_module(PY_LOG
, LOG_ERROR
, "Unable to find irc class");
914 int python_call_handler(char *handler
, char *args
[], size_t argc
, char *command_service
, char *command_caller
, char *command_target
) {
915 /* This is how we talk to modpython.c. First a new instance of the irc class is created using these
916 arguments to setup the current environment. Then the named method of the handler object is called
917 with the givin arguments.
924 log_module(PY_LOG
, LOG_INFO
, "attempting to call handler %s.", handler
);
925 if(base_module
!= NULL
&& handler_object
!= NULL
) {
926 pIrcObj
= new_irc_object(command_service
, command_caller
, command_target
);
928 log_module(PY_LOG
, LOG_INFO
, "Can't get irc object. Bailing.");
932 pArgs
= python_build_handler_args(argc
, args
, pIrcObj
);
933 pMethod
= PyObject_GetAttrString(handler_object
, handler
);
934 if(pMethod
&& PyCallable_Check(pMethod
)) {
935 /* Call the method, with the arguments */
936 pValue
= PyObject_CallObject(pMethod
, pArgs
);
942 ret
= PyInt_AsLong(pValue
);
943 if(ret
== -1 && PyErr_Occurred()) {
945 log_module(PY_LOG
, LOG_INFO
, "error converting return value of handler %s to type long. ", handler
);
949 log_module(PY_LOG
, LOG_INFO
, "handler %s was run successfully, returned %d.", handler
, ret
);
956 /* TODO: instead of print errors, get them as strings
957 * and deal with them with normal x3 log system. */
958 log_module(PY_LOG
, LOG_WARNING
, "call to handler %s failed", handler
);
966 else { /* couldn't find handler methed */
968 /* Free pMethod if it was found but not callable? */
969 log_module(PY_LOG
, LOG_ERROR
, "Cannot find handler %s.", handler
);
974 else { /* No base module.. no python? */
975 log_module(PY_LOG
, LOG_INFO
, "Cannot handle %s, Python is not initialized.", handler
);
980 PyObject
*python_new_handler_object() {
981 /* Create a new instance of the handler class.
982 This is called during python initilization (or reload)
983 and the result is saved and re-used.
985 PyObject
*pHandlerClass
, *pHandlerObj
;
987 log_module(PY_LOG
, LOG_INFO
, "Attempting to instanciate python class handler");
988 pHandlerClass
= PyObject_GetAttrString(base_module
, "handler");
989 /* Class is a new reference */
990 if(pHandlerClass
&& PyCallable_Check(pHandlerClass
)) {
991 /*PyObject *pValue; */
993 pHandlerObj
= PyObject_CallObject(pHandlerClass
, NULL
);
994 if(pHandlerObj
!= NULL
) {
995 log_module(PY_LOG
, LOG_INFO
, "Created new python handler object.");
999 log_module(PY_LOG
, LOG_ERROR
, "Unable to instanciate handler object");
1001 python_log_module();
1006 log_module(PY_LOG
, LOG_ERROR
, "Unable to find handler class");
1008 python_log_module();
1010 Py_DECREF(pHandlerClass
);
1016 /* ------------------------------------------------------------------------------- *
1017 Some gateway functions to convert x3 callbacks into modpython.py callbacks.
1018 Mostly we just build relevant args and call the proper handler method
1020 debate: do we just register these and check them in python
1021 for every one (slow?) or actually work out if a plugin needs
1022 it first? We will start by doing it every time.
1025 python_handle_join(struct modeNode
*mNode
)
1027 /* callback for handle_join events.
1029 struct userNode
*user
= mNode
->user
;
1030 struct chanNode
*channel
= mNode
->channel
;
1033 log_module(PY_LOG
, LOG_INFO
, "python module handle_join");
1034 if(!channel
||!user
) {
1035 log_module(PY_LOG
, LOG_WARNING
, "Python code got join without channel or user!");
1039 char *args
[] = {channel
->name
, user
->nick
};
1040 return python_call_handler("join", args
, 2, "", "", "");
1045 python_handle_server_link(struct server
*server
)
1047 log_module(PY_LOG
, LOG_INFO
, "python module handle_server_link");
1049 log_module(PY_LOG
, LOG_WARNING
, "Python code got server link without server!");
1053 char *args
[] = {server
->name
, server
->description
};
1054 return python_call_handler("server_link", args
, 2, "", "", "");
1059 python_handle_new_user(struct userNode
*user
)
1061 log_module(PY_LOG
, LOG_INFO
, "Python module handle_new_user");
1063 log_module(PY_LOG
, LOG_WARNING
, "Python code got new_user without the user");
1067 char *args
[] = {user
->nick
, user
->ident
, user
->hostname
, user
->info
};
1068 return python_call_handler("new_user", args
, 4, "", "", "");
1073 python_handle_nick_change(struct userNode
*user
, const char *old_nick
)
1075 log_module(PY_LOG
, LOG_INFO
, "Python module handle_nick_change");
1077 log_module(PY_LOG
, LOG_WARNING
, "Python code got nick_change without the user!");
1080 char *args
[] = {user
->nick
, (char *)old_nick
};
1081 python_call_handler("nick_change", args
, 2, "", "", "");
1085 /* ----------------------------------------------------------------------------- */
1089 /* Init the python engine and do init work on modpython.py
1090 This is called during x3 startup, and on a python reload
1094 char* env
= getenv("PYTHONPATH");
1100 setenv("PYTHONPATH", modpython_conf
.scripts_dir
, 1);
1101 else if (!strstr(env
, modpython_conf
.scripts_dir
)) {
1102 buffer
= (char*)malloc(strlen(env
) + strlen(modpython_conf
.scripts_dir
) + 2);
1103 sprintf(buffer
, "%s:%s", modpython_conf
.scripts_dir
, env
);
1104 setenv("PYTHONPATH", buffer
, 1);
1110 Py_InitModule("_svc", EmbMethods
);
1111 pName
= PyString_FromString(modpython_conf
.main_module
);
1112 base_module
= PyImport_Import(pName
);
1115 Py_XDECREF(handler_object
);
1116 handler_object
= NULL
;
1118 if(base_module
!= NULL
) {
1119 handler_object
= python_new_handler_object();
1120 if(handler_object
) {
1121 python_call_handler("init", NULL
, 0, "", "", "");
1125 /* error handler class not found */
1126 log_module(PY_LOG
, LOG_WARNING
, "Failed to create handler object");
1132 python_log_module();
1133 log_module(PY_LOG
, LOG_WARNING
, "Failed to load modpython.py");
1140 python_finalize(void) {
1141 /* Called after X3 is fully up and running.
1142 Code can be put here that needs to run to init things, but
1143 which is sensitive to everything else in x3 being up and ready
1147 PyRun_SimpleString("print 'Hello, World of Python!'");
1148 log_module(PY_LOG
, LOG_INFO
, "python module finalize");
1154 python_cleanup(void) {
1155 /* Called on shutdown of the python module (or before reloading)
1158 log_module(PY_LOG
, LOG_INFO
, "python module cleanup");
1159 if (PyErr_Occurred())
1161 Py_Finalize(); /* Shut down python enterpreter */
1164 /* ---------------------------------------------------------------------------------- *
1165 Python module command handlers.
1167 static MODCMD_FUNC(cmd_reload
) {
1168 /* reload the python system completely
1170 log_module(PY_LOG
, LOG_INFO
, "Shutting python down");
1172 log_module(PY_LOG
, LOG_INFO
, "Loading python stuff");
1174 reply("PYMSG_RELOAD_SUCCESS");
1177 reply("PYMSG_RELOAD_FAILED");
1182 static char* format_python_error(int space_nls
) {
1183 PyObject
* extype
= NULL
, *exvalue
= NULL
, *extraceback
= NULL
;
1184 PyObject
* pextypestr
= NULL
, *pexvaluestr
= NULL
;
1185 char* extypestr
= NULL
, *exvaluestr
= NULL
;
1186 size_t retvallen
= 0;
1187 char* retval
= NULL
, *tmp
;
1189 PyErr_Fetch(&extype
, &exvalue
, &extraceback
);
1193 pextypestr
= PyObject_Str(extype
);
1196 extypestr
= PyString_AsString(pextypestr
);
1200 pexvaluestr
= PyObject_Str(exvalue
);
1202 exvaluestr
= PyString_AsString(pexvaluestr
);
1204 retvallen
= strlen(extypestr
) + (exvaluestr
? strlen(exvaluestr
) + 2 : 0) + 1;
1205 retval
= (char*)malloc(retvallen
);
1207 snprintf(retval
, retvallen
, "%s: %s", extypestr
, exvaluestr
);
1209 strncpy(retval
, extypestr
, retvallen
);
1221 if (PyErr_Occurred())
1222 PyErr_Clear(); /* ignore errors caused by formatting */
1224 Py_XDECREF(exvalue
);
1225 Py_XDECREF(extraceback
);
1226 Py_XDECREF(pextypestr
);
1227 Py_XDECREF(pexvaluestr
);
1232 return strdup("unknown exception");
1235 static MODCMD_FUNC(cmd_run
) {
1236 /* this method allows running arbitrary python commands.
1240 PyObject
* py_main_module
;
1241 PyObject
* py_globals
;
1242 PyObject
* py_locals
;
1243 PyObject
* py_retval
;
1244 PyObject
* extype
, *exvalue
, *extraceback
;
1245 PyObject
* exvaluestr
= NULL
;
1246 char* exmsg
= NULL
, *exmsgptr
;
1248 py_main_module
= PyImport_AddModule("__main__");
1249 py_globals
= py_locals
= PyModule_GetDict(py_main_module
);
1251 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
1253 py_retval
= PyRun_String(msg
, Py_file_input
, py_globals
, py_locals
);
1254 if (py_retval
== NULL
) {
1255 PyErr_Fetch(&extype
, &exvalue
, &extraceback
);
1256 if (exvalue
!= NULL
) {
1257 exvaluestr
= PyObject_Str(exvalue
);
1258 exmsg
= strdup(PyString_AS_STRING(exvaluestr
));
1260 while (exmsgptr
&& *exmsgptr
) {
1261 if (*exmsgptr
== '\n' || *exmsgptr
== '\r' || *exmsgptr
== '\t')
1266 if (extype
!= NULL
&& exvalue
!= NULL
&& PyType_Check(extype
)) {
1267 reply("PYMSG_RUN_EXCEPTION", ((PyTypeObject
*)extype
)->tp_name
, exmsg
);
1269 reply("PYMSG_RUN_UNKNOWN_EXCEPTION");
1273 if (exvalue
!= NULL
)
1275 if (extraceback
!= NULL
)
1276 Py_DECREF(extraceback
);
1277 if (exvaluestr
!= NULL
)
1278 Py_DECREF(exvaluestr
);
1282 Py_DECREF(py_retval
);
1288 #define numstrargs(X) sizeof(X) / sizeof(*X)
1289 static MODCMD_FUNC(cmd_command
) {
1290 char *plugin
= argv
[1];
1291 char *command
= argv
[2];
1292 char *msg
; /* args */
1294 msg
= unsplit_string(argv
+ 3, argc
- 3, NULL
);
1299 char *args
[] = {plugin
, command
, msg
};
1300 python_call_handler("cmd_command", args
, numstrargs(args
), cmd
->parent
->bot
->nick
, user
?user
->nick
:"", channel
?channel
->name
:"");
1304 static void modpython_conf_read(void) {
1308 if (!(conf_node
= conf_get_data(MODPYTHON_CONF_NAME
, RECDB_OBJECT
))) {
1309 log_module(PY_LOG
, LOG_ERROR
, "config node '%s' is missing or has wrong type", MODPYTHON_CONF_NAME
);
1313 str
= database_get_data(conf_node
, "scripts_dir", RECDB_QSTRING
);
1314 modpython_conf
.scripts_dir
= str
? str
: "./";
1316 str
= database_get_data(conf_node
, "main_module", RECDB_QSTRING
);
1317 modpython_conf
.main_module
= str
? str
: "modpython";
1320 int python_init(void) {
1321 /* X3 calls this function on init of the module during startup. We use it to
1322 do all our setup tasks and bindings
1325 PY_LOG
= log_register_type("Python", "file:python.log");
1326 python_module
= module_register("python", PY_LOG
, "mod-python.help", NULL
);
1327 conf_register_reload(modpython_conf_read
);
1329 log_module(PY_LOG
, LOG_INFO
, "python module init");
1330 message_register_table(msgtab
);
1333 reg_auth_func(python_check_messages);
1334 reg_handle_rename_func(python_rename_account);
1335 reg_unreg_func(python_unreg_account);
1336 conf_register_reload(python_conf_read);
1337 saxdb_register("python", python_saxdb_read, python_saxdb_write);
1338 modcmd_register(python_module, "send", cmd_send, 3, MODCMD_REQUIRE_AUTHED, NULL);
1340 modcmd_register(python_module
, "reload", cmd_reload
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+oper", NULL
);
1341 modcmd_register(python_module
, "run", cmd_run
, 2, MODCMD_REQUIRE_AUTHED
, "flags", "+oper", NULL
);
1342 modcmd_register(python_module
, "command", cmd_command
, 3, MODCMD_REQUIRE_STAFF
, NULL
);
1344 // Please help us by implementing any of the callbacks listed as TODO below. They already exist
1345 // in x3, they just need handle_ bridges implemented. (see python_handle_join for an example)
1346 reg_server_link_func(python_handle_server_link
);
1347 reg_new_user_func(python_handle_new_user
);
1348 reg_nick_change_func(python_handle_nick_change
);
1349 //TODO: reg_del_user_func(python_handle_del_user);
1350 //TODO: reg_account_func(python_handle_account); /* stamping of account name to the ircd */
1351 //TODO: reg_handle_rename_func(python_handle_handle_rename); /* handle used to ALSO mean account name */
1352 //TODO: reg_failpw_func(python_handle_failpw);
1353 //TODO: reg_allowauth_func(python_handle_allowauth);
1354 //TODO: reg_handle_merge_func(python_handle_merge);
1356 //TODO: reg_oper_func(python_handle_oper);
1357 //TODO: reg_new_channel_func(python_handle_new_channel);
1358 reg_join_func(python_handle_join
);
1359 //TODO: reg_del_channel_func(python_handle_del_channel);
1360 //TODO: reg_part_func(python_handle_part);
1361 //TODO: reg_kick_func(python_handle_kick);
1362 //TODO: reg_topic_func(python_handle_topic);
1363 //TODO: reg_channel_mode_func(python_handle_channel_mode);
1365 //TODO: reg_privmsg_func(python_handle_privmsg);
1366 //TODO: reg_notice_func
1367 //TODO: reg_svccmd_unbind_func(python_handle_svccmd_unbind);
1368 //TODO: reg_chanmsg_func(python_handle_chanmsg);
1369 //TODO: reg_allchanmsg_func
1370 //TODO: reg_user_mode_func
1372 reg_exit_func(python_cleanup
);
1378 #endif /* WITH_PYTHON */