diff options
Diffstat (limited to 'src/plugins/scripts/python/weechat-python.c')
-rw-r--r-- | src/plugins/scripts/python/weechat-python.c | 792 |
1 files changed, 792 insertions, 0 deletions
diff --git a/src/plugins/scripts/python/weechat-python.c b/src/plugins/scripts/python/weechat-python.c new file mode 100644 index 000000000..ec7590f79 --- /dev/null +++ b/src/plugins/scripts/python/weechat-python.c @@ -0,0 +1,792 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* weechat-python.c: Python plugin for WeeChat */ + +#undef _ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <Python.h> +//#include <stdio.h> +//#include <stdlib.h> +//#include <string.h> +//#include <stdarg.h> +//#include <time.h> +//#include <sys/socket.h> +//#include <netinet/in.h> +//#include <arpa/inet.h> + +#include "../../weechat-plugin.h" +#include "../script.h" +#include "weechat-python.h" +#include "weechat-python-api.h" + + +WEECHAT_PLUGIN_NAME("python"); +WEECHAT_PLUGIN_DESCRIPTION("Python plugin for WeeChat"); +WEECHAT_PLUGIN_AUTHOR("FlashCode <flashcode@flashtux.org>"); +WEECHAT_PLUGIN_VERSION("0.1"); +WEECHAT_PLUGIN_LICENSE("GPL"); + +struct t_weechat_plugin *weechat_python_plugin; + +struct t_plugin_script *python_scripts = NULL; +struct t_plugin_script *python_current_script = NULL; +char *python_current_script_filename = NULL; +PyThreadState *python_mainThreadState = NULL; + +char python_buffer_output[128]; + + +/* + * weechat_python_exec: execute a Python script + */ + +void * +weechat_python_exec (struct t_plugin_script *script, + int ret_type, char *function, char **argv) +{ + PyObject *evMain; + PyObject *evDict; + PyObject *evFunc; + PyObject *rc; + void *ret_value; + int *ret_i; + + /* PyEval_AcquireLock (); */ + PyThreadState_Swap (script->interpreter); + + evMain = PyImport_AddModule ((char *) "__main__"); + evDict = PyModule_GetDict (evMain); + evFunc = PyDict_GetItemString (evDict, function); + + if ( !(evFunc && PyCallable_Check (evFunc)) ) + { + weechat_printf (NULL, + weechat_gettext ("%s%s unable to run function \"%s\""), + weechat_prefix ("error"), "python", function); + /* PyEval_ReleaseThread (python_current_script->interpreter); */ + return NULL; + } + + python_current_script = script; + + if (argv && argv[0]) + { + if (argv[1]) + { + if (argv[2]) + { + if (argv[3]) + { + if (argv[4]) + { + rc = PyObject_CallFunction (evFunc, "sssss", argv[0], + argv[1], argv[2], argv[3], + argv[4]); + } + else + rc = PyObject_CallFunction (evFunc, "ssss", argv[0], + argv[1], argv[2], argv[3]); + } + else + rc = PyObject_CallFunction (evFunc, "sss", argv[0], + argv[1], argv[2]); + } + else + rc = PyObject_CallFunction (evFunc, "ss", argv[0], argv[1]); + } + else + rc = PyObject_CallFunction (evFunc, "s", argv[0]); + } + else + rc = PyObject_CallFunction (evFunc, NULL); + + ret_value = NULL; + + /* + ugly hack : rc = NULL while 'return weechat.PLUGIN_RC_OK .... + because of '#define PLUGIN_RC_OK 0' + */ + if (rc == NULL) + rc = PyInt_FromLong (0); + + if (PyString_Check (rc) && (ret_type == WEECHAT_SCRIPT_EXEC_STRING)) + { + if (PyString_AsString (rc)) + ret_value = strdup (PyString_AsString(rc)); + else + ret_value = NULL; + + Py_XDECREF(rc); + } + else if (PyInt_Check (rc) && (ret_type == WEECHAT_SCRIPT_EXEC_INT)) + { + + ret_i = (int *)malloc (sizeof(int)); + if (ret_i) + *ret_i = (int) PyInt_AsLong(rc); + ret_value = ret_i; + + Py_XDECREF(rc); + } + else + { + weechat_printf (NULL, + weechat_gettext ("%s%s: function \"%s\" must return " + "a valid value"), + weechat_prefix ("error"), "python", function); + /* PyEval_ReleaseThread (python_current_script->interpreter); */ + return NULL; + } + + if (ret_value == NULL) + { + weechat_printf (NULL, + weechat_gettext ("%s%s: not enough memory for " + "function \"%s\""), + weechat_prefix ("error"), "python", function); + /* PyEval_ReleaseThread (python_current_script->interpreter); */ + return NULL; + } + + if (PyErr_Occurred ()) PyErr_Print (); + + /* PyEval_ReleaseThread (python_current_script->interpreter); */ + + return ret_value; +} + +/* + * weechat_python_output : redirection for stdout and stderr + */ + +static PyObject * +weechat_python_output (PyObject *self, PyObject *args) +{ + char *msg, *m, *p; + + /* make C compiler happy */ + (void) self; + + msg = NULL; + + if (!PyArg_ParseTuple (args, "s", &msg)) + { + if (strlen(python_buffer_output) > 0) + { + weechat_printf (NULL, + weechat_gettext ("%s: stdout/stderr: %s%s"), + "python", python_buffer_output, ""); + python_buffer_output[0] = '\0'; + } + } + else + { + m = msg; + while ((p = strchr (m, '\n')) != NULL) + { + *p = '\0'; + if (strlen (m) + strlen (python_buffer_output) > 0) + { + weechat_printf (NULL, + weechat_gettext ("%s: stdout/stderr: %s%s"), + "python", python_buffer_output, m); + } + *p = '\n'; + python_buffer_output[0] = '\0'; + m = ++p; + } + + if (strlen(m) + strlen(python_buffer_output) > sizeof(python_buffer_output)) + { + weechat_printf (NULL, + weechat_gettext ("%s: stdout/stderr : %s%s"), + "python", python_buffer_output, m); + python_buffer_output[0] = '\0'; + } + else + strcat (python_buffer_output, m); + } + + Py_INCREF(Py_None); + return Py_None; +} + +/* + * Outputs subroutines + */ + +static +PyMethodDef weechat_python_output_funcs[] = { + { "write", weechat_python_output, METH_VARARGS, "" }, + { NULL, NULL, 0, NULL } +}; + +/* + * weechat_python_load: load a Python script + */ + +int +weechat_python_load (char *filename) +{ + char *argv[] = { "__weechat_plugin__" , NULL }; + FILE *fp; + PyThreadState *python_current_interpreter; + PyObject *weechat_module, *weechat_outputs, *weechat_dict; + PyObject *python_path, *path; + char *w_home, *p_home; + int len; + + weechat_printf (NULL, + weechat_gettext ("%s%s: loading script \"%s\""), + weechat_prefix ("info"), "python", filename); + if ((fp = fopen (filename, "r")) == NULL) + { + weechat_printf (NULL, + weechat_gettext ("%s%s: script \"%s\" not found"), + weechat_prefix ("error"), "python", filename); + return 0; + } + + python_current_script = NULL; + + /* PyEval_AcquireLock (); */ + python_current_interpreter = Py_NewInterpreter (); + PySys_SetArgv(1, argv); + + if (python_current_interpreter == NULL) + { + weechat_printf (NULL, + weechat_gettext ("%s%s: unable to create new " + "sub-interpreter"), + weechat_prefix ("error"), "python"); + fclose (fp); + /* PyEval_ReleaseLock (); */ + return 0; + } + + /* PyThreadState_Swap (python_current_interpreter); */ + + weechat_module = Py_InitModule ("weechat", weechat_python_funcs); + + if ( weechat_module == NULL) + { + weechat_printf (NULL, + weechat_gettext ("%s%s: unable to initialize WeeChat " + "module"), + weechat_prefix ("error"), "python"); + fclose (fp); + + Py_EndInterpreter (python_current_interpreter); + /* PyEval_ReleaseLock (); */ + + return 0; + } + + /* adding $weechat_dir/python in $PYTHONPATH */ + python_path = PySys_GetObject ("path"); + w_home = weechat_info_get ("weechat_dir"); + if (w_home) + { + len = strlen (w_home) + 1 + strlen("python") + 1; + p_home = (char *)malloc (len * sizeof(char)); + if (p_home) + { + snprintf (p_home, len, "%s/python", w_home); + path = PyString_FromString (p_home); + if (path != NULL) + { + PyList_Insert (python_path, 0, path); + Py_DECREF (path); + } + free (p_home); + } + } + + /* define some constants */ + weechat_dict = PyModule_GetDict(weechat_module); + PyDict_SetItemString(weechat_dict, "WEECHAT_RC_OK", PyInt_FromLong((long) WEECHAT_RC_OK)); + PyDict_SetItemString(weechat_dict, "WEECHAT_RC_ERROR", PyInt_FromLong((long) WEECHAT_RC_ERROR)); + PyDict_SetItemString(weechat_dict, "WEECHAT_RC_OK_IGNORE_WEECHAT", PyInt_FromLong((long) WEECHAT_RC_OK_IGNORE_WEECHAT)); + PyDict_SetItemString(weechat_dict, "WEECHAT_RC_OK_IGNORE_PLUGINS", PyInt_FromLong((long) WEECHAT_RC_OK_IGNORE_PLUGINS)); + PyDict_SetItemString(weechat_dict, "WEECHAT_RC_OK_IGNORE_ALL", PyInt_FromLong((long) WEECHAT_RC_OK_IGNORE_ALL)); + PyDict_SetItemString(weechat_dict, "WEECHAT_RC_OK_WITH_HIGHLIGHT", PyInt_FromLong((long) WEECHAT_RC_OK_WITH_HIGHLIGHT)); + PyDict_SetItemString(weechat_dict, "WEECHAT_LIST_POS_SORT", PyString_FromString(WEECHAT_LIST_POS_SORT)); + PyDict_SetItemString(weechat_dict, "WEECHAT_LIST_POS_BEGINNING", PyString_FromString(WEECHAT_LIST_POS_BEGINNING)); + PyDict_SetItemString(weechat_dict, "WEECHAT_LIST_POS_END", PyString_FromString(WEECHAT_LIST_POS_END)); + PyDict_SetItemString(weechat_dict, "WEECHAT_HOTLIST_LOW", PyString_FromString(WEECHAT_HOTLIST_LOW)); + PyDict_SetItemString(weechat_dict, "WEECHAT_HOTLIST_MESSAGE", PyString_FromString(WEECHAT_HOTLIST_MESSAGE)); + PyDict_SetItemString(weechat_dict, "WEECHAT_HOTLIST_PRIVATE", PyString_FromString(WEECHAT_HOTLIST_PRIVATE)); + PyDict_SetItemString(weechat_dict, "WEECHAT_HOTLIST_HIGHLIGHT", PyString_FromString(WEECHAT_HOTLIST_HIGHLIGHT)); + PyDict_SetItemString(weechat_dict, "WEECHAT_HOOK_SIGNAL_STRING", PyString_FromString(WEECHAT_HOOK_SIGNAL_STRING)); + PyDict_SetItemString(weechat_dict, "WEECHAT_HOOK_SIGNAL_INT", PyString_FromString(WEECHAT_HOOK_SIGNAL_INT)); + PyDict_SetItemString(weechat_dict, "WEECHAT_HOOK_SIGNAL_POINTER", PyString_FromString(WEECHAT_HOOK_SIGNAL_POINTER)); + + weechat_outputs = Py_InitModule("weechatOutputs", weechat_python_output_funcs); + if (weechat_outputs == NULL) + { + weechat_printf (NULL, + weechat_gettext ("%s%s: unable to redirect stdout and " + "stderr"), + weechat_prefix ("error"), "python"); + } + else + { + if (PySys_SetObject("stdout", weechat_outputs) == -1) + { + weechat_printf (NULL, + weechat_gettext ("%s%s: unable to redirect stdout"), + weechat_prefix ("error"), "python"); + } + if (PySys_SetObject("stderr", weechat_outputs) == -1) + { + weechat_printf (NULL, + weechat_gettext ("%s%s: unable to redirect stderr"), + weechat_prefix ("error"), "python"); + } + } + + python_current_script_filename = filename; + + if (PyRun_SimpleFile (fp, filename) != 0) + { + weechat_printf (NULL, + weechat_gettext ("%s%s: unable to parse file \"%s\""), + weechat_prefix ("error"), "python", filename); + fclose (fp); + + if (PyErr_Occurred ()) PyErr_Print (); + Py_EndInterpreter (python_current_interpreter); + /* PyEval_ReleaseLock (); */ + + /* if script was registered, removing from list */ + if (python_current_script != NULL) + { + script_remove (weechat_python_plugin, + &python_scripts, python_current_script); + } + return 0; + } + + if (PyErr_Occurred ()) PyErr_Print (); + + fclose (fp); + + if (python_current_script == NULL) + { + weechat_printf (NULL, + weechat_gettext ("%s%s: function \"register\" not " + "found (or failed) in file \"%s\""), + weechat_prefix ("error"), "python", filename); + + if (PyErr_Occurred ()) PyErr_Print (); + Py_EndInterpreter (python_current_interpreter); + /* PyEval_ReleaseLock (); */ + + return 0; + } + + python_current_script->interpreter = (PyThreadState *) python_current_interpreter; + /* PyEval_ReleaseThread (python_current_script->interpreter); */ + + return 1; +} + +/* + * weechat_python_load_cb: callback for script_auto_load() function + */ + +int +weechat_python_load_cb (void *data, char *filename) +{ + /* make C compiler happy */ + (void) data; + + return weechat_python_load (filename); +} + +/* + * weechat_python_unload: unload a Python script + */ + +void +weechat_python_unload (struct t_plugin_script *script) +{ + int *r; + + weechat_printf (NULL, + weechat_gettext ("%s%s: unloading script \"%s\""), + weechat_prefix ("info"), "python", script->name); + + if (script->shutdown_func && script->shutdown_func[0]) + { + r = (int *) weechat_python_exec (script, WEECHAT_SCRIPT_EXEC_INT, + script->shutdown_func, NULL); + if (r) + free (r); + } + + PyThreadState_Swap (script->interpreter); + Py_EndInterpreter (script->interpreter); + + script_remove (weechat_python_plugin, &python_scripts, script); +} + +/* + * weechat_python_unload_name: unload a Python script by name + */ + +void +weechat_python_unload_name (char *name) +{ + struct t_plugin_script *ptr_script; + + ptr_script = script_search (weechat_python_plugin, &python_scripts, name); + if (ptr_script) + { + weechat_python_unload (ptr_script); + weechat_printf (NULL, + weechat_gettext ("%s%s: script \"%s\" unloaded"), + weechat_prefix ("info"), "python", name); + } + else + { + weechat_printf (NULL, + weechat_gettext ("%s%s: script \"%s\" not loaded"), + weechat_prefix ("error"), "python", name); + } +} + +/* + * weechat_python_unload_all: unload all Python scripts + */ + +void +weechat_python_unload_all () +{ + while (python_scripts) + { + weechat_python_unload (python_scripts); + } +} + +/* + * weechat_python_cmd: /python command handler + */ + +int +weechat_python_command_cb (void *data, struct t_gui_buffer *buffer, + int argc, char **argv, char **argv_eol) +{ + //int handler_found, modifier_found; + char *path_script; + struct t_plugin_script *ptr_script; + //struct t_plugin_handler *ptr_handler; + //struct t_plugin_modifier *ptr_modifier; + + /* make C compiler happy */ + (void) data; + (void) buffer; + + if (argc == 1) + { + /* list registered Python scripts */ + weechat_printf (NULL, ""); + weechat_printf (NULL, + weechat_gettext ("Registered %s scripts:"), + "python"); + if (python_scripts) + { + for (ptr_script = python_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + weechat_printf (NULL, + weechat_gettext (" %s v%s (%s), by %s, " + "license %s"), + ptr_script->name, + ptr_script->version, + ptr_script->description, + ptr_script->author, + ptr_script->license); + } + } + else + weechat_printf (NULL, weechat_gettext (" (none)")); + + /* + // List Python message handlers + plugin->print_server (plugin, ""); + plugin->print_server (plugin, "Python message handlers:"); + handler_found = 0; + for (ptr_handler = plugin->handlers; + ptr_handler; ptr_handler = ptr_handler->next_handler) + { + if ((ptr_handler->type == PLUGIN_HANDLER_MESSAGE) + && (ptr_handler->handler_args)) + { + handler_found = 1; + plugin->print_server (plugin, " IRC(%s) => Python(%s)", + ptr_handler->irc_command, + ptr_handler->handler_args); + } + } + if (!handler_found) + plugin->print_server (plugin, " (none)"); + + // List Python command handlers + plugin->print_server (plugin, ""); + plugin->print_server (plugin, "Python command handlers:"); + handler_found = 0; + for (ptr_handler = plugin->handlers; + ptr_handler; ptr_handler = ptr_handler->next_handler) + { + if ((ptr_handler->type == PLUGIN_HANDLER_COMMAND) + && (ptr_handler->handler_args)) + { + handler_found = 1; + plugin->print_server (plugin, " /%s => Python(%s)", + ptr_handler->command, + ptr_handler->handler_args); + } + } + if (!handler_found) + plugin->print_server (plugin, " (none)"); + + // List Python timer handlers + plugin->print_server (plugin, ""); + plugin->print_server (plugin, "Python timer handlers:"); + handler_found = 0; + for (ptr_handler = plugin->handlers; + ptr_handler; ptr_handler = ptr_handler->next_handler) + { + if ((ptr_handler->type == PLUGIN_HANDLER_TIMER) + && (ptr_handler->handler_args)) + { + handler_found = 1; + plugin->print_server (plugin, " %d seconds => Python(%s)", + ptr_handler->interval, + ptr_handler->handler_args); + } + } + if (!handler_found) + plugin->print_server (plugin, " (none)"); + + // List Python keyboard handlers + plugin->print_server (plugin, ""); + plugin->print_server (plugin, "Python keyboard handlers:"); + handler_found = 0; + for (ptr_handler = plugin->handlers; + ptr_handler; ptr_handler = ptr_handler->next_handler) + { + if ((ptr_handler->type == PLUGIN_HANDLER_KEYBOARD) + && (ptr_handler->handler_args)) + { + handler_found = 1; + plugin->print_server (plugin, " Python(%s)", + ptr_handler->handler_args); + } + } + if (!handler_found) + plugin->print_server (plugin, " (none)"); + + // List Python event handlers + plugin->print_server (plugin, ""); + plugin->print_server (plugin, "Python event handlers:"); + handler_found = 0; + for (ptr_handler = plugin->handlers; + ptr_handler; ptr_handler = ptr_handler->next_handler) + { + if ((ptr_handler->type == PLUGIN_HANDLER_EVENT) + && (ptr_handler->handler_args)) + { + handler_found = 1; + plugin->print_server (plugin, " %s => Python(%s)", + ptr_handler->event, + ptr_handler->handler_args); + } + } + if (!handler_found) + plugin->print_server (plugin, " (none)"); + + // List Python modifiers + plugin->print_server (plugin, ""); + plugin->print_server (plugin, "Python modifiers:"); + modifier_found = 0; + for (ptr_modifier = plugin->modifiers; + ptr_modifier; ptr_modifier = ptr_modifier->next_modifier) + { + modifier_found = 1; + if (ptr_modifier->type == PLUGIN_MODIFIER_IRC_IN) + plugin->print_server (plugin, " IRC(%s, %s) => Python(%s)", + ptr_modifier->command, + PLUGIN_MODIFIER_IRC_IN_STR, + ptr_modifier->modifier_args); + else if (ptr_modifier->type == PLUGIN_MODIFIER_IRC_USER) + plugin->print_server (plugin, " IRC(%s, %s) => Python(%s)", + ptr_modifier->command, + PLUGIN_MODIFIER_IRC_USER_STR, + ptr_modifier->modifier_args); + else if (ptr_modifier->type == PLUGIN_MODIFIER_IRC_OUT) + plugin->print_server (plugin, " IRC(%s, %s) => Python(%s)", + ptr_modifier->command, + PLUGIN_MODIFIER_IRC_OUT_STR, + ptr_modifier->modifier_args); + } + if (!modifier_found) + plugin->print_server (plugin, " (none)"); + */ + } + else if (argc == 2) + { + if (weechat_strcasecmp (argv[1], "autoload") == 0) + { + script_auto_load (weechat_python_plugin, "python", + &weechat_python_load_cb); + } + else if (weechat_strcasecmp (argv[1], "reload") == 0) + { + weechat_python_unload_all (); + script_auto_load (weechat_python_plugin, "python", + &weechat_python_load_cb); + } + else if (weechat_strcasecmp (argv[1], "unload") == 0) + { + weechat_python_unload_all (); + } + } + else + { + if (weechat_strcasecmp (argv[1], "load") == 0) + { + /* load Python script */ + path_script = script_search_full_name (weechat_python_plugin, + "python", argv_eol[2]); + weechat_python_load ((path_script) ? path_script : argv_eol[2]); + if (path_script) + free (path_script); + } + else if (weechat_strcasecmp (argv[1], "unload") == 0) + { + /* unload Python script */ + weechat_python_unload_name (argv_eol[2]); + } + else + { + weechat_printf (NULL, + weechat_gettext ("%s%s: unknown option for " + "command \"%s\""), + weechat_prefix ("error"), "python", "python"); + } + } + + return WEECHAT_RC_OK; +} + +/* + * weechat_plugin_init: initialize Python plugin + */ + +int +weechat_plugin_init (struct t_weechat_plugin *plugin) +{ + weechat_python_plugin = plugin; + + /* init stdout/stderr buffer */ + python_buffer_output[0] = '\0'; + + Py_Initialize (); + if (Py_IsInitialized () == 0) + { + weechat_printf (NULL, + weechat_gettext ("%s%s: unable to launch global " + "interpreter"), + weechat_prefix ("error"), "python"); + return WEECHAT_RC_ERROR; + } + + PyEval_InitThreads(); + /* python_mainThreadState = PyThreadState_Swap(NULL); */ + python_mainThreadState = PyEval_SaveThread(); + /* PyEval_ReleaseLock (); */ + + if (python_mainThreadState == NULL) + { + weechat_printf (NULL, + weechat_gettext ("%s%s: unable to get current " + "interpreter state"), + weechat_prefix ("error"), "python"); + return WEECHAT_RC_ERROR; + } + + weechat_hook_command ("python", + weechat_gettext ("list/load/unload Python scripts"), + weechat_gettext ("[load filename] | [autoload] | " + "[reload] | [unload [script]]"), + weechat_gettext ("filename: Python script (file) to " + "load\n" + "script: script name to unload\n\n" + "Without argument, /python command " + "lists all loaded Python scripts."), + "load|autoload|reload|unload %f", + &weechat_python_command_cb, NULL); + + weechat_mkdir_home ("python", 0644); + weechat_mkdir_home ("python/autoload", 0644); + + script_init (weechat_python_plugin); + script_auto_load (weechat_python_plugin, "python", + &weechat_python_load_cb); + + /* init ok */ + return WEECHAT_RC_OK; +} + +/* + * weechat_plugin_end: shutdown Python interface + */ + +void +weechat_plugin_end (struct t_weechat_plugin *plugin) +{ + /* make C compiler happy */ + (void) plugin; + + /* unload all scripts */ + weechat_python_unload_all (); + + /* free Python interpreter */ + if (python_mainThreadState != NULL) + { + /* PyEval_AcquireLock (); */ + PyThreadState_Swap (python_mainThreadState); + /* PyEval_ReleaseLock (); */ + python_mainThreadState = NULL; + } + + Py_Finalize (); + if (Py_IsInitialized () != 0) + { + weechat_printf (NULL, + weechat_gettext ("%s%s: unable to free interpreter"), + weechat_prefix ("error"), "python"); + } +} |