Python plugin: Make some functions static
[claws.git] / src / plugins / python / python-hooks.c
1 /*
2  * Copyright (c) 2008-2009  Christian Hammond
3  * Copyright (c) 2008-2009  David Trowbridge
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21  * THE SOFTWARE.
22  */
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #include "claws-features.h"
26 #endif
27
28 #include <glib.h>
29 #include <glib/gi18n.h>
30
31 #ifdef ENABLE_PYTHON
32 #include <Python.h>
33 #include <pygobject.h>
34 #include <pygtk/pygtk.h>
35 #endif // ENABLE_PYTHON
36
37 #include <dlfcn.h>
38
39 #include <signal.h>
40
41 #include "python-hooks.h"
42
43
44 static gboolean python_enabled = FALSE;
45
46 #ifdef ENABLE_PYTHON
47 static GString *captured_stdout = NULL;
48 static GString *captured_stderr = NULL;
49
50
51 static PyObject *
52 capture_stdout(PyObject *self, PyObject *args)
53 {
54     char *str = NULL;
55
56     if (!PyArg_ParseTuple(args, "s", &str))
57         return NULL;
58
59     g_string_append(captured_stdout, str);
60
61     Py_INCREF(Py_None);
62     return Py_None;
63 }
64
65 static PyObject *
66 capture_stderr(PyObject *self, PyObject *args)
67 {
68     char *str = NULL;
69
70     if (!PyArg_ParseTuple(args, "s", &str))
71         return NULL;
72
73     g_string_append(captured_stderr, str);
74
75     Py_INCREF(Py_None);
76     return Py_None;
77 }
78
79 static PyObject *
80 capture_stdin(PyObject *self, PyObject *args)
81 {
82     /* Return an empty string.
83      * This is what read() returns when hitting EOF. */
84     return PyString_FromString("");
85 }
86
87 static PyObject *
88 wrap_gobj(PyObject *self, PyObject *args)
89 {
90     void *addr;
91     GObject *obj;
92
93     if (!PyArg_ParseTuple(args, "l", &addr))
94         return NULL;
95
96     if (!G_IS_OBJECT(addr))
97         return NULL; // XXX
98
99     obj = G_OBJECT(addr);
100
101     if (!obj)
102         return NULL; // XXX
103
104     return pygobject_new(obj);
105 }
106
107 static PyMethodDef parasite_python_methods[] = {
108     {"capture_stdout", capture_stdout, METH_VARARGS, "Captures stdout"},
109     {"capture_stderr", capture_stderr, METH_VARARGS, "Captures stderr"},
110     {"capture_stdin", capture_stdin, METH_VARARGS, "Captures stdin"},
111     {"gobj", wrap_gobj, METH_VARARGS, "Wraps a C GObject"},
112     {NULL, NULL, 0, NULL}
113 };
114
115
116 static gboolean
117 is_blacklisted(void)
118 {
119     const char *prgname = g_get_prgname();
120
121     return (!strcmp(prgname, "gimp"));
122 }
123 #endif // ENABLE_PYTHON
124
125 void
126 parasite_python_init(void)
127 {
128 #ifdef ENABLE_PYTHON
129     int res;
130     struct sigaction old_sigint;
131     PyObject *pygtk;
132
133     if (is_blacklisted())
134         return;
135
136     /* This prevents errors such as "undefined symbol: PyExc_ImportError" */
137     if (!dlopen(PYTHON_SHARED_LIB, RTLD_NOW | RTLD_GLOBAL))
138     {
139         g_error("%s\n", dlerror());
140         return;
141     }
142
143     captured_stdout = g_string_new("");
144     captured_stderr = g_string_new("");
145
146     /* Back up and later restore SIGINT so Python doesn't steal it from us. */
147     res = sigaction(SIGINT, NULL, &old_sigint);
148
149     if (!Py_IsInitialized())
150         Py_Initialize();
151
152     res = sigaction(SIGINT, &old_sigint, NULL);
153
154     Py_InitModule("parasite", parasite_python_methods);
155     PyRun_SimpleString(
156         "import parasite\n"
157         "import sys\n"
158         "\n"
159         "class StdoutCatcher:\n"
160         "    def write(self, str):\n"
161         "        parasite.capture_stdout(str)\n"
162         "    def flush(self):\n"
163         "        pass\n"
164         "\n"
165         "class StderrCatcher:\n"
166         "    def write(self, str):\n"
167         "        parasite.capture_stderr(str)\n"
168         "    def flush(self):\n"
169         "        pass\n"
170         "\n"
171         "class StdinCatcher:\n"
172         "    def readline(self, size=-1):\n"
173         "        return parasite.capture_stdin(size)\n"
174         "    def read(self, size=-1):\n"
175         "        return parasite.capture_stdin(size)\n"
176         "    def flush(self):\n"
177         "        pass\n"
178         "\n"
179     );
180
181     if (!pygobject_init(-1, -1, -1))
182         return;
183
184     pygtk = PyImport_ImportModule("gtk");
185
186     if (pygtk != NULL)
187     {
188         PyObject *module_dict = PyModule_GetDict(pygtk);
189         PyObject *cobject = PyDict_GetItemString(module_dict, "_PyGtk_API");
190
191         /*
192          * This seems to be NULL when we're running a PyGTK program.
193          * We really need to find out why.
194          */
195         if (cobject != NULL)
196         {
197             if (PyCObject_Check(cobject))
198                 _PyGtk_API = (struct _PyGtk_FunctionStruct*)
199                 PyCObject_AsVoidPtr(cobject);
200             else {
201                 PyErr_SetString(PyExc_RuntimeError,
202                                 "could not find _PyGtk_API object");
203                 return;
204             }
205         }
206     } else {
207         PyErr_SetString(PyExc_ImportError, "could not import gtk");
208         return;
209     }
210
211     python_enabled = TRUE;
212 #endif // ENABLE_PYTHON
213 }
214
215 void
216 parasite_python_run(const char *command,
217                     ParasitePythonLogger stdout_logger,
218                     ParasitePythonLogger stderr_logger,
219                     gpointer user_data)
220 {
221 #ifdef ENABLE_PYTHON
222     PyGILState_STATE gstate;
223     PyObject *module;
224     PyObject *dict;
225     PyObject *obj;
226     const char *cp;
227
228     /* empty string as command is a noop */
229     if(!strcmp(command, ""))
230       return;
231
232     /* if first non-whitespace character is '#', command is also a noop */
233     cp = command;
234     while(cp && (*cp != '\0') && g_ascii_isspace(*cp))
235       cp++;
236     if(cp && *cp == '#')
237       return;
238
239     gstate = PyGILState_Ensure();
240
241     module = PyImport_AddModule("__main__");
242     dict = PyModule_GetDict(module);
243
244     PyRun_SimpleString("old_stdout = sys.stdout\n"
245                        "old_stderr = sys.stderr\n"
246                        "old_stdin  = sys.stdin\n"
247                        "sys.stdout = StdoutCatcher()\n"
248                        "sys.stderr = StderrCatcher()\n"
249                        "sys.stdin  = StdinCatcher()\n");
250
251     obj = PyRun_String(command, Py_single_input, dict, dict);
252     if(PyErr_Occurred())
253       PyErr_Print();
254     PyRun_SimpleString("sys.stdout = old_stdout\n"
255                        "sys.stderr = old_stderr\n"
256                        "sys.stdin = old_stdin\n");
257
258     if (stdout_logger != NULL)
259         stdout_logger(captured_stdout->str, user_data);
260
261     if (stderr_logger != NULL)
262         stderr_logger(captured_stderr->str, user_data);
263
264     // Print any returned object
265     if (obj != NULL && obj != Py_None) {
266        PyObject *repr = PyObject_Repr(obj);
267        if (repr != NULL) {
268            char *string = PyString_AsString(repr);
269
270            stdout_logger(string, user_data);
271            stdout_logger("\n", user_data);
272         }
273
274         Py_XDECREF(repr);
275     }
276     Py_XDECREF(obj);
277
278     PyGILState_Release(gstate);
279     g_string_erase(captured_stdout, 0, -1);
280     g_string_erase(captured_stderr, 0, -1);
281 #endif // ENABLE_PYTHON
282 }
283
284 gboolean
285 parasite_python_is_enabled(void)
286 {
287     return python_enabled;
288 }
289
290 // vim: set et sw=4 ts=4: