+ /* a gateway to standard X3 logging subsystem.
+ * level is a value 0 to 9 as defined by the log_severity enum in log.h.
+ *
+ * for now, all logs go to the PY_LOG log. In the future this will change.
+ */
+ char *message;
+ int level;
+
+ if(!PyArg_ParseTuple(args, "is", &level, &message))
+ return NULL;
+
+ log_module(PY_LOG, level, "%s", message);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(emb_kill__doc__,
+ "kill(servicenick, target, message)\n\n"
+ "Kills a given user.");
+
+static PyObject*
+emb_kill(UNUSED_ARG(PyObject* self), PyObject* args) {
+ char const* from_nick, *target_nick, *message;
+ struct userNode *target;
+ struct service *service;
+
+ if (!PyArg_ParseTuple(args, "sss", &from_nick, &target_nick, &message))
+ return NULL;
+
+ if(!(service = service_find(from_nick))) {
+ PyErr_SetString(PyExc_Exception, "unknown service user specified as from user");
+ return NULL;
+ }
+
+ if ((target = GetUserH(target_nick)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "unknown target user");
+ return NULL;
+ }
+
+ irc_kill(service->bot, target, message);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+struct py_timeq_extra {
+ PyObject* func;
+ PyObject* arg;
+};
+
+static
+void py_timeq_callback(void* data) {
+ struct py_timeq_extra* extra = (struct py_timeq_extra*)data;
+
+ PyObject* retval = PyObject_Call(extra->func, extra->arg, NULL);
+ Py_XDECREF(retval);
+
+ Py_DECREF(extra->func);
+ Py_DECREF(extra->arg);
+}
+
+PyDoc_STRVAR(emb_timeq_add__doc__,
+ "timeq_add(when, function, args)\n\n"
+ "Adds a callback to the service timer system.\n\n"
+ "The specific function must be callable, and the specified arguments must be\n"
+ "a tuple with the arguments that the function expects.");
+
+static PyObject*
+emb_timeq_add(UNUSED_ARG(PyObject* self), PyObject* args) {
+ time_t when;
+ PyObject* func, *arg;
+ struct py_timeq_extra* extra;
+
+ if (!PyArg_ParseTuple(args, "lOO", &when, &func, &arg))
+ return NULL;
+
+ if (!PyFunction_Check(func)) {
+ PyErr_SetString(PyExc_Exception, "first argument must be a function");
+ return NULL;
+ }
+
+ if (!PyTuple_Check(arg)) {
+ PyErr_SetString(PyExc_Exception, "second argument must be a tuple");
+ return NULL;
+ }
+
+ extra = malloc(sizeof(struct py_timeq_extra));
+ if (extra == NULL) {
+ PyErr_SetString(PyExc_Exception, "out of memory");
+ return NULL;
+ }
+
+ Py_INCREF(func);
+ Py_INCREF(arg);
+
+ extra->func = func;
+ extra->arg = arg;
+
+ timeq_add(when, py_timeq_callback, (void*)extra);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(emb_timeq_del__doc__,
+ "timeq_del(when)\n\n"
+ "This function deletes all python-added callbacks registered to run at the\n"
+ "given time, regardless of their data. This is due to the unnecessary extra\n"
+ "burden it would require to get the same data for multiple runs.");
+
+static PyObject*
+emb_timeq_del(UNUSED_ARG(PyObject* self), PyObject* args) {
+ time_t when;
+
+ if (!PyArg_ParseTuple(args, "l", &when))
+ return NULL;
+
+ timeq_del(when, py_timeq_callback, NULL, TIMEQ_IGNORE_DATA);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static int pyobj_config_make_dict(char const* key, void* data_, void* extra) {
+ struct record_data* data = (struct record_data*)data_;
+ PyObject* dict = (PyObject*)extra;
+ PyObject* value = NULL, *tmp;
+ size_t n, idx;
+ int success;
+
+ switch (data->type) {
+ case RECDB_QSTRING:
+ value = PyString_FromString(data->d.qstring);
+ break;
+
+ case RECDB_STRING_LIST:
+ value = PyList_New(data->d.slist->used);
+ if (value == NULL)
+ break;
+
+ success = 1;
+ for (n = 0; n < data->d.slist->used; ++n) {
+ tmp = PyString_FromString(data->d.slist->list[n]);
+ if (tmp == NULL) {
+ success = 0;
+ break;
+ }
+
+ if (PyList_SetItem(value, n, tmp)) {
+ Py_DECREF(tmp);
+ success = 0;
+ break;
+ }
+ }
+ if (!success) {
+ for (idx = 0; idx < n; ++idx) {
+ tmp = PyList_GET_ITEM(value, idx);
+ Py_DECREF(tmp);
+ PyList_SET_ITEM(value, idx, NULL);
+ }
+ Py_DECREF(value);
+ value = NULL;
+ }
+ break;
+
+ case RECDB_OBJECT:
+ value = PyDict_New();
+ if (value == NULL)
+ break;
+
+ if (dict_foreach(data->d.object, pyobj_config_make_dict, (void*)value) != NULL) {
+ PyDict_Clear(value);
+ value = NULL;
+ break;
+ }
+
+ break;
+
+ default:
+ Py_INCREF(Py_None);
+ value = Py_None;
+ }
+
+ if (value == NULL)
+ return 1;
+
+ if (PyDict_SetItemString(dict, key, value))
+ return 1;
+
+ return 0;
+}
+
+PyDoc_STRVAR(emb_get_config__doc__,
+ "get_config() -> dict with config elements and values\n\n"
+ "Updates to the returned dictionary will not reflect in the service's\n"
+ "configuration.");
+
+static PyObject*
+emb_get_config(UNUSED_ARG(PyObject* self), PyObject* args) {
+ PyObject* dict;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ dict = PyDict_New();
+ if (dict == NULL)
+ return NULL;
+
+ if (conf_enum_root(pyobj_config_make_dict, (void*)dict) != NULL) {
+ PyDict_Clear(dict);
+ PyErr_SetString(PyExc_Exception, "unable to iterate config");
+ return NULL;
+ }
+
+ return dict;
+}
+
+PyDoc_STRVAR(emb_kick__doc__,
+ "kick(who, target, message)\n\n"
+ "Kicks a given target as if the who user kicked them using the given message.");
+
+static PyObject* emb_kick(UNUSED_ARG(PyObject* self), PyObject* args) {
+ struct userNode* who, *target;
+ struct chanNode* channel;
+ char const* msg;
+
+ char const* who_s, *target_s, *channel_s;
+
+ if (!PyArg_ParseTuple(args, "ssss", &who_s, &target_s, &channel_s, &msg))
+ return NULL;
+
+ if ((who = GetUserH(who_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "no such user");
+ return NULL;
+ }
+
+ if ((target = GetUserH(target_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "no such target");
+ return NULL;
+ }
+
+ if ((channel = GetChannel(channel_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "no such channel");
+ return NULL;
+ }
+
+ irc_kick(who, target, channel, msg);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(emb_channel_mode__doc__,
+ "channel_mode(who, channel, modes)\n\n"
+ "Lets a current server's user set a specified channel's modes as specified.");
+
+static PyObject* emb_channel_mode(UNUSED_ARG(PyObject* self_), PyObject* args) {
+ struct userNode* who;
+ struct chanNode* channel;
+ char const* modes;
+
+ char const* who_s, *channel_s;
+
+ if (!PyArg_ParseTuple(args, "sss", &who_s, &channel_s, &modes))
+ return NULL;
+
+ if ((who = GetUserH(who_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "unknown user");
+ return NULL;
+ }
+
+ if (who->uplink != self) {
+ PyErr_SetString(PyExc_Exception, "user not on current server");
+ return NULL;
+ }
+
+ if ((channel = GetChannel(channel_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "unknown channel");
+ return NULL;
+ }
+
+ irc_mode(who, channel, modes);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(emb_user_mode__doc__,
+ "user_mode(target, modes)\n\n"
+ "Sets target's modes as specified. The modes are in normal +f-n syntax.");
+
+static PyObject* emb_user_mode(UNUSED_ARG(PyObject* self), PyObject* args) {
+ struct userNode* target;
+ char const* modes;
+
+ char const* target_s;
+
+ if (!PyArg_ParseTuple(args, "ss", &target_s, &modes))
+ return NULL;
+
+ if ((target = GetUserH(target_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "unknown user");
+ return NULL;
+ }
+
+ irc_umode(target, modes);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(emb_fakehost__doc__,
+ "fakehost(target, host)\n\n"
+ "Sets the fakehost of a given user to the specified host.");
+
+static PyObject* emb_fakehost(UNUSED_ARG(PyObject* self), PyObject* args) {
+ struct userNode* target;
+ char const* host;
+
+ char const* target_s;
+
+ if (!PyArg_ParseTuple(args, "ss", &target_s, &host))
+ return NULL;
+
+ if ((target = GetUserH(target_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "unknown user");
+ return NULL;
+ }
+
+ irc_fakehost(target, host);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(emb_svsnick__doc__,
+ "svsnick(from, target, newnick)\n\n"
+ "The from nick must be on the service server.");
+
+static PyObject*
+emb_svsnick(UNUSED_ARG(PyObject* self_), PyObject* args) {
+ struct userNode* from, *target;
+ const char* newnick;
+
+ const char* from_s, *target_s;
+
+ if (!PyArg_ParseTuple(args, "sss", &from_s, &target_s, &newnick))
+ return NULL;
+
+ if ((from = GetUserH(from_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "unknown from user");
+ return NULL;
+ }
+
+ if ((target = GetUserH(target_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "unknown target user");
+ return NULL;
+ }
+
+ if (from->uplink != self) {
+ PyErr_SetString(PyExc_Exception, "from user is not on service server");
+ return NULL;
+ }
+
+ irc_svsnick(from, target, newnick);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(emb_svsquit__doc__,
+ "svsquit(from, target, reason)\n\n"
+ "The from user must be on the service server.");
+
+static PyObject*
+emb_svsquit(UNUSED_ARG(PyObject* self_), PyObject* args) {
+ struct userNode* from, *target;
+ char const* reason;
+
+ char const* from_s, *target_s;
+
+ if (!PyArg_ParseTuple(args, "sss", &from_s, &target_s, &reason))
+ return NULL;
+
+ if ((from = GetUserH(from_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "unknown from user");
+ return NULL;
+ }
+
+ if (from->uplink != self) {
+ PyErr_SetString(PyExc_Exception, "from user is not on service server");
+ return NULL;
+ }
+
+ if ((target = GetUserH(target_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "unknown target user");
+ return NULL;
+ }
+
+ irc_svsquit(from, target, reason);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(emb_svsjoin__doc__,
+ "svsjoin(from, target, to)\n\n"
+ "From user from must a user on the service server.\n"
+ "To must be an existing channel name.");
+
+static PyObject*
+emb_svsjoin(UNUSED_ARG(PyObject* self_), PyObject* args) {
+ struct userNode* from, *target;
+ struct chanNode* to;
+
+ const char* from_s, *target_s, *to_s;
+
+ if (!PyArg_ParseTuple(args, "sss", &from_s, &target_s, &to_s))
+ return NULL;
+
+ if ((from = GetUserH(from_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "unknown from user");
+ return NULL;
+ }
+
+ if (from->uplink != self) {
+ PyErr_SetString(PyExc_Exception, "from user is not on service server");
+ return NULL;
+ }
+
+ if ((target = GetUserH(target_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "unknown target user");
+ return NULL;
+ }
+
+ if ((to = GetChannel(to_s)) == NULL)
+ to = AddChannel(to_s, now, NULL, NULL, NULL);
+
+ irc_svsjoin(from, target, to);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(emb_adduser__doc__,
+ "adduser(nick, ident, hostname, description, modes) -> dict with user information\n\n"
+ "Adds a new local user with the given information.");
+
+static PyObject*
+emb_adduser(UNUSED_ARG(PyObject* self_), PyObject* args) {
+ char const* nick, *ident, *hostname, *desc, *modes;
+ struct userNode* user;
+ PyObject* retval;
+
+ if (!PyArg_ParseTuple(args, "sssss", &nick, &ident, &hostname, &desc, &modes))
+ return NULL;
+
+ user = AddLocalUser(nick, ident, hostname, desc, modes);
+
+ retval = pyobj_from_usernode(user);
+
+ return retval;
+}
+
+/* TODO: Add the rest of the service members to the dict */
+static PyObject*
+pyobj_from_service(struct service* serv) {
+ PyObject* bot, *retval;
+
+ bot = pyobj_from_usernode(serv->bot);
+ if (bot == NULL)
+ goto cleanup;
+
+ retval = Py_BuildValue("{s:O,s:c,s:I}",
+ "bot", bot,
+ "trigger", serv->trigger,
+ "privileged", serv->privileged);
+ if (retval == NULL)
+ goto cleanup;
+
+ return retval;
+
+cleanup:
+ Py_XDECREF(bot);
+ return NULL;
+}
+
+PyDoc_STRVAR(emb_service_register__doc__,
+ "service_register(nick)\n\n"
+ "Registers nick as a service. The specified nick must be on the local server.");
+
+static PyObject*
+emb_service_register(UNUSED_ARG(PyObject* self_), PyObject* args) {
+ struct userNode* user;
+ char const* user_s;
+
+ if (!PyArg_ParseTuple(args, "s", &user_s))
+ return NULL;
+
+ if ((user = GetUserH(user_s)) == NULL) {
+ PyErr_SetString(PyExc_Exception, "unknown user");
+ return NULL;
+ }
+
+ if (user->uplink != self) {
+ PyErr_SetString(PyExc_Exception, "user is not on service server");
+ return NULL;
+ }
+
+ return pyobj_from_service(service_register(user));
+}
+
+size_t logs_size = 0;
+static struct log_type **logs_list = NULL;
+
+PyDoc_STRVAR(emb_log_register_type__doc__,
+ "registers a log source to write event data to.");
+static PyObject* emb_log_register_type(UNUSED_ARG(PyObject *self), PyObject* args) {
+ const char* logName;
+ const char* defaultLog;
+ struct log_type* log;
+ struct log_type** newlogs;
+
+ if (!PyArg_ParseTuple(args, "ss", &logName, &defaultLog))
+ return NULL;
+
+ newlogs = realloc(logs_list, (logs_size+1)*sizeof(struct log_type*));
+ if (newlogs == NULL) {
+ PyErr_SetString(PyExc_Exception, "unable to allocate memory for log structures. aborting.");
+ return NULL;
+ }
+ logs_list = newlogs;
+
+ log = log_register_type(logName, defaultLog);
+ if (log == NULL) {
+ PyErr_SetString(PyExc_Exception, "unable to register log");
+ return NULL;
+ }
+
+ logs_list[logs_size++] = log;
+
+ return Py_BuildValue("O", PyCObject_FromVoidPtr(log, NULL));
+}
+
+PyDoc_STRVAR(emb_module_register__doc__, "registers a module");
+PyObject* emb_module_register(UNUSED_ARG(PyObject* self), PyObject* args) {
+ PyObject* pylog;
+ char const *name, *helpfile;
+ struct log_type* log;
+ struct module* mod;
+
+ if (!PyArg_ParseTuple(args, "sOs", &name, &pylog, &helpfile))
+ return NULL;
+
+ log = PyCObject_AsVoidPtr(pylog);
+
+ mod = module_register(name, log, helpfile, NULL);
+
+ if (mod == NULL) {
+ PyErr_SetString(PyExc_Exception, "unable to register module");
+ return NULL;
+ }
+
+ return Py_BuildValue("O", PyCObject_FromVoidPtr(mod, NULL));
+}
+
+static PyMethodDef EmbMethods[] = {
+ /* Communication methods */
+ {"dump", emb_dump, METH_VARARGS, emb_dump__doc__},
+ {"send_target_privmsg", emb_send_target_privmsg, METH_VARARGS, emb_send_target_privmsg__doc__},
+ {"send_target_notice", emb_send_target_notice, METH_VARARGS, emb_send_target_notice__doc__},
+ {"log_module", emb_log_module, METH_VARARGS, emb_log_module__doc__},
+//TODO: {"exec_cmd", emb_exec_cmd, METH_VARARGS, "execute x3 command provided"},
+// This should use environment from "python command" call to pass in, if available
+ {"kill", emb_kill, METH_VARARGS, emb_kill__doc__},
+ {"fakehost", emb_fakehost, METH_VARARGS, emb_fakehost__doc__},
+ {"svsnick", emb_svsnick, METH_VARARGS, emb_svsnick__doc__},
+ {"svsquit", emb_svsquit, METH_VARARGS, emb_svsquit__doc__},
+ {"svsjoin", emb_svsjoin, METH_VARARGS, emb_svsjoin__doc__},
+ {"adduser", emb_adduser, METH_VARARGS, emb_adduser__doc__},
+ {"service_register", emb_service_register, METH_VARARGS, emb_service_register__doc__},
+//TODO: svsmode, svsident, nick, quit, join, part, ident, vhost
+//TODO: {"shun"
+//TODO: {"unshun"
+//TODO: {"gline", emb_gline, METH_VARARGS, "gline a mask"},
+//TODO: {"ungline", emb_ungline, METH_VARARGS, "remove a gline"},
+ {"kick", emb_kick, METH_VARARGS, emb_kick__doc__},
+ {"channel_mode", emb_channel_mode, METH_VARARGS, emb_channel_mode__doc__},
+ {"user_mode", emb_user_mode, METH_VARARGS, emb_user_mode__doc__},
+//
+ {"get_config", emb_get_config, METH_VARARGS, emb_get_config__doc__},
+//TODO: {"config_set", emb_config_set, METH_VARARGS, "change a config setting 'on-the-fly'."},
+//
+ {"timeq_add", emb_timeq_add, METH_VARARGS, emb_timeq_add__doc__},
+ {"timeq_del", emb_timeq_del, METH_VARARGS, emb_timeq_del__doc__},
+
+ /* module registration methods */
+ {"log_register_type", emb_log_register_type, METH_VARARGS,
+ emb_log_register_type__doc__},
+ {"module_register", emb_module_register, METH_VARARGS,
+ emb_module_register__doc__},
+
+ /* Information gathering methods */
+ {"get_user", emb_get_user, METH_VARARGS, emb_get_user__doc__},
+ {"get_users", emb_get_users, METH_VARARGS, emb_get_users__doc__},
+ {"get_channel", emb_get_channel, METH_VARARGS, emb_get_channel__doc__},
+ {"get_channels", emb_get_channels, METH_VARARGS, emb_get_channels__doc__},
+ {"get_server", emb_get_server, METH_VARARGS, emb_get_server__doc__},
+ {"get_servers", emb_get_servers, METH_VARARGS, emb_get_servers__doc__},
+ {"get_account", emb_get_account, METH_VARARGS, emb_get_account__doc__},
+ {"get_accounts", emb_get_accounts, METH_VARARGS, emb_get_accounts__doc__},
+ {"get_info", emb_get_info, METH_VARARGS, emb_get_info__doc__},
+ /* null terminator */
+ {NULL, NULL, 0, NULL}
+};
+
+
+/*
+These functions set up the embedded environment for us to call out to
+modpython.py class methods.
+ */
+
+void python_log_module() {
+ /* Attempt to convert python errors to x3 log system */
+ PyObject *exc, *tb, *value, *tmp;
+ char *str_exc = "NONE";
+ char *str_value = "NONE";
+ char *str_tb = "NONE";
+
+ PyErr_Fetch(&exc, &value, &tb);
+
+ if(exc) {
+ if((tmp = PyObject_Str(exc)))
+ str_exc = PyString_AsString(tmp);
+ }
+ if(value) {
+ if((tmp = PyObject_Str(value)))
+ str_value = PyString_AsString(tmp);
+ }
+ if(tb) {
+ if((tmp = PyObject_Str(tb)))
+ str_tb = PyString_AsString(tmp);
+ }
+
+ /* Now restore it so we can print it (just in case)
+ * (should we do this only when running in debug mode?) */
+ PyErr_Restore(exc, value, tb);
+ PyErr_Print(); /* which of course, clears it again.. */
+
+ log_module(PY_LOG, LOG_WARNING, "PYTHON error: %s, value: %s", str_exc, str_value);
+
+ /* TODO: get the traceback using the traceback module via C api so we can add it to the X3 logs. See
+ * http://mail.python.org/pipermail/python-list/2003-March/192226.html */
+ // (this doesnt work, str_tb is just an object hashid) log_module(PY_LOG, LOG_INFO, "PYTHON error, traceback: %s", str_tb);
+}
+
+
+PyObject *python_build_handler_args(size_t argc, char *args[], PyObject *pIrcObj) {
+ /* Sets up a python tuple with passed in arguments, prefixed by the Irc instance
+ which handlers use to interact with C.
+ argc = number of args
+ args = array of args
+ pIrcObj = instance of the irc class
+ */
+ size_t i = 0, n;
+ PyObject *pArgs = NULL;
+
+ pArgs = PyTuple_New(argc + 1);
+ Py_INCREF(pIrcObj);
+ PyTuple_SetItem(pArgs, i++, pIrcObj);
+
+ if(args && argc) {
+ PyObject *pValue;
+ for(n = 0; n < argc; ++n) {
+ pValue = PyString_FromString(args[n]);
+ if(!pValue) {
+ Py_DECREF(pArgs);
+ log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[n]);
+ return NULL;
+ }
+ PyTuple_SetItem(pArgs, n+i, pValue);
+ }
+ }
+ return pArgs;
+}
+
+PyObject *python_build_args(size_t argc, char *args[]) {
+ /* Builds the passed in arguments into a python argument tuple.
+ argc = number of args
+ args = array of args
+ */
+ size_t i;
+ PyObject *pArgs = NULL;
+
+ if(args && argc) {
+ pArgs = PyTuple_New(argc);
+ PyObject *pValue;
+ for(i = 0; i< argc; ++i) {
+ pValue = PyString_FromString(args[i]);
+ if(!pValue) {
+ Py_DECREF(pArgs);
+ log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[i]);
+ return NULL;
+ }
+ PyTuple_SetItem(pArgs, i, pValue);
+ }
+ }
+ return pArgs;
+}
+
+
+PyObject *new_irc_object(char *command_service, char *command_caller, char *command_target) {
+ /* Creates a new instance of the irc class (from modpython.py) which is initalized
+ with current environment details like which service were talking to.
+ command_service = which service we are talking to, or empty string if none
+ command_caller = nick of user generating message, or empty string if none
+ command_target = If were reacting to something on a channel, this will
+ be set to the name of the channel. Otherwise empty
+ */
+ PyObject *pIrcArgs = NULL;
+ PyObject *pIrcClass;
+ PyObject *pIrcObj;
+
+ log_module(PY_LOG, LOG_INFO, "Attempting to instanciate irc class; %s %s %s", command_service, command_caller, command_target);
+ pIrcClass = PyObject_GetAttrString(base_module, "irc");
+ /* pIrcClass is a new reference */
+ if(pIrcClass && PyCallable_Check(pIrcClass)) {
+ //size_t i;
+ char *ircargs[] = {command_service, command_caller, command_target};
+ //PyObject *pValue;
+
+ pIrcArgs = python_build_args(3, ircargs);
+ pIrcObj = PyObject_CallObject(pIrcClass, pIrcArgs);
+ if(!pIrcObj) {
+ log_module(PY_LOG, LOG_ERROR, "IRC Class failed to load");
+ python_log_module();
+ //PyErr_Print();
+ }
+ if(pIrcArgs != NULL) {
+ Py_DECREF(pIrcArgs);
+ }
+ Py_DECREF(pIrcClass);
+ return pIrcObj;
+ }
+ else {
+ /* need to free pIrcClass here if it WAS found but was NOT callable? */
+ log_module(PY_LOG, LOG_ERROR, "Unable to find irc class");
+ return NULL;
+ }
+}
+
+int python_call_handler(char *handler, char *args[], size_t argc, char *command_service, char *command_caller, char *command_target) {
+ /* This is how we talk to modpython.c. First a new instance of the irc class is created using these
+ arguments to setup the current environment. Then the named method of the handler object is called
+ with the givin arguments.
+ */
+ PyObject *pIrcObj;
+ PyObject *pArgs;
+ PyObject *pMethod;
+ PyObject *pValue;
+
+ log_module(PY_LOG, LOG_INFO, "attempting to call handler %s.", handler);
+ if(base_module != NULL && handler_object != NULL) {
+ pIrcObj = new_irc_object(command_service, command_caller, command_target);
+ if(!pIrcObj) {
+ log_module(PY_LOG, LOG_INFO, "Can't get irc object. Bailing.");
+ return 0;
+ }
+
+ pArgs = python_build_handler_args(argc, args, pIrcObj);
+ pMethod = PyObject_GetAttrString(handler_object, handler);
+ if(pMethod && PyCallable_Check(pMethod)) {
+ /* Call the method, with the arguments */
+ pValue = PyObject_CallObject(pMethod, pArgs);
+ if(pArgs) {
+ Py_DECREF(pArgs);
+ }
+ if(pValue != NULL) {
+ int ret;
+ ret = PyInt_AsLong(pValue);
+ if(ret == -1 && PyErr_Occurred()) {
+ //PyErr_Print();
+ log_module(PY_LOG, LOG_INFO, "error converting return value of handler %s to type long. ", handler);
+ python_log_module();
+ ret = 0;
+ }
+ log_module(PY_LOG, LOG_INFO, "handler %s was run successfully, returned %d.", handler, ret);
+ Py_DECREF(pValue);
+ Py_DECREF(pIrcObj);
+ Py_DECREF(pMethod);
+ return ret;
+ }
+ else {
+ /* TODO: instead of print errors, get them as strings
+ * and deal with them with normal x3 log system. */
+ log_module(PY_LOG, LOG_WARNING, "call to handler %s failed", handler);
+ //PyErr_Print();
+ python_log_module();
+ Py_DECREF(pIrcObj);
+ Py_DECREF(pMethod);
+ return 0;
+ }
+ }
+ else { /* couldn't find handler methed */
+ Py_DECREF(pArgs);
+ /* Free pMethod if it was found but not callable? */
+ log_module(PY_LOG, LOG_ERROR, "Cannot find handler %s.", handler);
+ return 0;
+
+ }
+ }
+ else { /* No base module.. no python? */
+ log_module(PY_LOG, LOG_INFO, "Cannot handle %s, Python is not initialized.", handler);
+ return 0;
+ }
+}
+
+PyObject *python_new_handler_object() {
+ /* Create a new instance of the handler class.
+ This is called during python initilization (or reload)
+ and the result is saved and re-used.
+ */
+ PyObject *pHandlerClass, *pHandlerObj;
+
+ log_module(PY_LOG, LOG_INFO, "Attempting to instanciate python class handler");
+ pHandlerClass = PyObject_GetAttrString(base_module, "handler");
+ /* Class is a new reference */
+ if(pHandlerClass && PyCallable_Check(pHandlerClass)) {
+ /*PyObject *pValue; */
+
+ pHandlerObj = PyObject_CallObject(pHandlerClass, NULL);
+ if(pHandlerObj != NULL) {
+ log_module(PY_LOG, LOG_INFO, "Created new python handler object.");
+ return pHandlerObj;
+ }
+ else {
+ log_module(PY_LOG, LOG_ERROR, "Unable to instanciate handler object");
+ //PyErr_Print();
+ python_log_module();
+ return NULL;
+ }
+ }
+ else {
+ log_module(PY_LOG, LOG_ERROR, "Unable to find handler class");
+ //PyErr_Print();
+ python_log_module();
+ if(pHandlerClass) {
+ Py_DECREF(pHandlerClass);
+ }
+ return NULL;
+ }