#ifdef WITH_PYTHON /* just disable this file if python doesnt exist */
-#include "Python.h"
+#include <Python.h>
#include "chanserv.h"
#include "conf.h"
#include "modcmd.h"
#include "nickserv.h"
#include "opserv.h"
#include "saxdb.h"
-#include "sendmail.h"
+#include "mail.h"
#include "timeq.h"
#include "compat.h"
static const struct message_entry msgtab[] = {
{ "PYMSG_RELOAD_SUCCESS", "Reloaded Python scripts successfully." },
{ "PYMSG_RELOAD_FAILED", "Error reloading Python scripts." },
+ { "PYMSG_RUN_UNKNOWN_EXCEPTION", "Error running python: unknown exception." },
+ { "PYMSG_RUN_EXCEPTION", "Error running python: %s." },
{ NULL, NULL } /* sentenal */
};
+#define MODPYTHON_CONF_NAME "modules/python"
+
+static
+struct {
+ char const* scripts_dir;
+} modpython_conf;
+
static struct log_type *PY_LOG;
const char *python_module_deps[] = { NULL };
static struct module *python_module;
}
}
+static int
+python_handle_server_link(struct server *server)
+{
+ log_module(PY_LOG, LOG_INFO, "python module handle_server_link");
+ if(!server) {
+ log_module(PY_LOG, LOG_WARNING, "Python code got server link without server!");
+ return 0;
+ }
+ else {
+ char *args[] = {server->name, server->description};
+ return python_call_handler("server_link", args, 2, "", "", "");
+ }
+}
+
+static int
+python_handle_new_user(struct userNode *user)
+{
+ log_module(PY_LOG, LOG_INFO, "Python module handle_new_user");
+ if(!user) {
+ log_module(PY_LOG, LOG_WARNING, "Python code got new_user without the user");
+ return 0;
+ }
+ else {
+ char *args[] = {user->nick, user->ident, user->hostname, user->info};
+ return python_call_handler("new_user", args, 4, "", "", "");
+ }
+}
+
+static void
+python_handle_nick_change(struct userNode *user, const char *old_nick)
+{
+ log_module(PY_LOG, LOG_INFO, "Python module handle_nick_change");
+ if(!user) {
+ log_module(PY_LOG, LOG_WARNING, "Python code got nick_change without the user!");
+ }
+ else {
+ char *args[] = {user->nick, (char *)old_nick};
+ python_call_handler("nick_change", args, 2, "", "", "");
+ }
+}
+
/* ----------------------------------------------------------------------------- */
This is called during x3 startup, and on a python reload
*/
PyObject *pName;
+ char* buffer;
+ char* env = getenv("PYTHONPATH");
+
+ if (env)
+ env = strdup(env);
+
+ if (!env)
+ setenv("PYTHONPATH", modpython_conf.scripts_dir, 1);
+ else if (!strstr(env, modpython_conf.scripts_dir)) {
+ buffer = (char*)malloc(strlen(env) + strlen(modpython_conf.scripts_dir) + 2);
+ sprintf(buffer, "%s:%s", modpython_conf.scripts_dir, env);
+ setenv("PYTHONPATH", buffer, 1);
+ free(buffer);
+ free(env);
+ }
- setenv("PYTHONPATH", "/home/rubin/afternet/services/x3/x3-run/", 1);
Py_Initialize();
Py_InitModule("svc", EmbMethods);
/* TODO: get "modpython" from x3.conf */
python_cleanup(void) {
/* Called on shutdown of the python module (or before reloading)
*/
+
log_module(PY_LOG, LOG_INFO, "python module cleanup");
+ if (PyErr_Occurred())
+ PyErr_Clear();
Py_Finalize(); /* Shut down python enterpriter */
return;
}
return 1;
}
+static char* format_python_error(int space_nls) {
+ PyObject* extype = NULL, *exvalue = NULL, *extraceback = NULL;
+ PyObject* pextypestr = NULL, *pexvaluestr = NULL;
+ char* extypestr = NULL, *exvaluestr = NULL;
+ size_t retvallen = 0;
+ char* retval = NULL, *tmp;
+
+ PyErr_Fetch(&extype, &exvalue, &extraceback);
+ if (!extype)
+ goto cleanup;
+
+ pextypestr = PyObject_Str(extype);
+ if (!pextypestr)
+ goto cleanup;
+ extypestr = PyString_AsString(pextypestr);
+ if (!extypestr)
+ goto cleanup;
+
+ pexvaluestr = PyObject_Str(exvalue);
+ if (pexvaluestr)
+ exvaluestr = PyString_AsString(pexvaluestr);
+
+ retvallen = strlen(extypestr) + (exvaluestr ? strlen(exvaluestr) + 2 : 0) + 1;
+ retval = (char*)malloc(retvallen);
+ if (exvaluestr)
+ snprintf(retval, retvallen, "%s: %s", extypestr, exvaluestr);
+ else
+ strncpy(retval, extypestr, retvallen);
+
+ if (space_nls) {
+ tmp = retval;
+ while (*tmp) {
+ if (*tmp == '\n')
+ *tmp = ' ';
+ ++tmp;
+ }
+ }
+
+cleanup:
+ if (PyErr_Occurred())
+ PyErr_Clear(); /* ignore errors caused by formatting */
+ Py_XDECREF(extype);
+ Py_XDECREF(exvalue);
+ Py_XDECREF(extraceback);
+ Py_XDECREF(pextypestr);
+ Py_XDECREF(pexvaluestr);
+
+ if (retval)
+ return retval;
+
+ return strdup("unknown exception");
+}
+
+static int python_run_statements(char const* msg, char** err) {
+ PyObject* o;
+ char* exmsg = NULL;
+ PyObject* py_main_module = NULL;
+ PyObject* py_globals;
+
+ py_main_module = PyImport_AddModule("__main__");
+ if (!py_main_module) {
+ exmsg = format_python_error(1);
+ if (exmsg)
+ *err = exmsg;
+ else
+ *err = strdup("unknown exception");
+ return 1;
+ }
+
+ py_globals = PyModule_GetDict(py_main_module);
+
+ o = PyRun_String(msg, Py_file_input, py_globals, py_globals);
+ if (o == NULL) {
+ exmsg = format_python_error(1);
+ if (exmsg)
+ *err = exmsg;
+ else
+ *err = strdup("unknown exception");
+ return 1;
+ }
+ else {
+ Py_DECREF(o);
+ }
+
+ return 0;
+}
+
static MODCMD_FUNC(cmd_run) {
- /* run an arbitrary python command. This can include shell commands, so should be disabled on
- production, and needs to be handled extremely cautiously as far as access control
- */
- char *msg;
+ /* this method allows running arbitrary python commands.
+ * use with care.
+ */
+ char* msg;
+ char* exmsg = NULL;
+
msg = unsplit_string(argv + 1, argc - 1, NULL);
- char *args[] = {msg};
- python_call_handler("cmd_run", args, 1, cmd->parent->bot->nick, user?user->nick:"", channel?channel->name:"");
+
+ if (python_run_statements(msg, &exmsg)) {
+ if (exmsg) {
+ reply("PYMSG_RUN_EXCEPTION", exmsg);
+ free(exmsg);
+ } else
+ reply("PYMSG_RUN_UNKNOWN_EXCEPTION");
+ }
+
return 1;
}
return 1;
}
+static void modpython_conf_read(void) {
+ dict_t conf_node;
+ char const* str;
+
+ if (!(conf_node = conf_get_data(MODPYTHON_CONF_NAME, RECDB_OBJECT))) {
+ log_module(PY_LOG, LOG_ERROR, "config node '%s' is missing or has wrong type", MODPYTHON_CONF_NAME);
+ return;
+ }
+
+ str = database_get_data(conf_node, "scripts_dir", RECDB_QSTRING);
+ modpython_conf.scripts_dir = str ? str : "./";
+}
+
int python_init(void) {
/* X3 calls this function on init of the module during startup. We use it to
do all our setup tasks and bindings
PY_LOG = log_register_type("Python", "file:python.log");
python_module = module_register("python", PY_LOG, "mod-python.help", NULL);
+ conf_register_reload(modpython_conf_read);
+
log_module(PY_LOG, LOG_INFO, "python module init");
message_register_table(msgtab);
// Please help us by implimenting any of the callbacks listed as TODO below. They already exist
// in x3, they just need handle_ bridges implimented. (see python_handle_join for an example)
-//TODO: reg_server_link_func(python_handle_server_link);
-//TODO: reg_new_user_func(python_handle_new_user);
-//TODO: reg_nick_change_func(python_handle_nick_change);
+ reg_server_link_func(python_handle_server_link);
+ reg_new_user_func(python_handle_new_user);
+ reg_nick_change_func(python_handle_nick_change);
//TODO: reg_del_user_func(python_handle_del_user);
//TODO: reg_account_func(python_handle_account); /* stamping of account name to the ircd */
//TODO: reg_handle_rename_func(python_handle_handle_rename); /* handle used to ALSO mean account name */