]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/mod-python.c
Return an error if an extended ban is invalid
[irc/evilnet/x3.git] / src / mod-python.c
index 9bd193cb7f66205fb0b2bb85eec6e3878cbf073a..f563cfad2022692067a10de315555a2d81b6df66 100644 (file)
 #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;
@@ -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 */