X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/039a6658781c4a1e429b5b048861fdeb8fc0474e..3b7fa78b1de8f9ee8718cba3da3b2db522b70620:/src/mod-python.c diff --git a/src/mod-python.c b/src/mod-python.c index 9bd193c..f563cfa 100644 --- a/src/mod-python.c +++ b/src/mod-python.c @@ -23,14 +23,14 @@ #ifdef WITH_PYTHON /* just disable this file if python doesnt exist */ -#include "Python.h" +#include #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" @@ -48,9 +48,18 @@ 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; @@ -627,6 +636,47 @@ python_handle_join(struct modeNode *mNode) } } +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, "", "", ""); + } +} + /* ----------------------------------------------------------------------------- */ @@ -635,8 +685,22 @@ int python_load() { 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 */ @@ -682,7 +746,10 @@ static void 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; } @@ -705,14 +772,110 @@ static MODCMD_FUNC(cmd_reload) { 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; } @@ -732,6 +895,19 @@ static MODCMD_FUNC(cmd_command) { 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 @@ -739,6 +915,8 @@ int python_init(void) { 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); @@ -756,9 +934,9 @@ int python_init(void) { // 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 */