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 #ifdef ENABLE_PYTHON
29 #include <Python.h>
30 #include <pygobject.h>
31 #include <pygtk/pygtk.h>
32 #endif // ENABLE_PYTHON
33
34 #include <glib.h>
35 #include <glib/gi18n.h>
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 static void *python_dlhandle = NULL;
46
47 #ifdef ENABLE_PYTHON
48 static GString *captured_stdout = NULL;
49 static GString *captured_stderr = NULL;
50
51
52 static PyObject *
capture_stdout(PyObject * self,PyObject * args)53 capture_stdout(PyObject *self, PyObject *args)
54 {
55 char *str = NULL;
56
57 if (!PyArg_ParseTuple(args, "s", &str))
58 return NULL;
59
60 g_string_append(captured_stdout, str);
61
62 Py_INCREF(Py_None);
63 return Py_None;
64 }
65
66 static PyObject *
capture_stderr(PyObject * self,PyObject * args)67 capture_stderr(PyObject *self, PyObject *args)
68 {
69 char *str = NULL;
70
71 if (!PyArg_ParseTuple(args, "s", &str))
72 return NULL;
73
74 g_string_append(captured_stderr, str);
75
76 Py_INCREF(Py_None);
77 return Py_None;
78 }
79
80 static PyObject *
capture_stdin(PyObject * self,PyObject * args)81 capture_stdin(PyObject *self, PyObject *args)
82 {
83 /* Return an empty string.
84 * This is what read() returns when hitting EOF. */
85 return PyString_FromString("");
86 }
87
88 static PyObject *
wrap_gobj(PyObject * self,PyObject * args)89 wrap_gobj(PyObject *self, PyObject *args)
90 {
91 void *addr;
92 GObject *obj;
93
94 if (!PyArg_ParseTuple(args, "l", &addr))
95 return NULL;
96
97 if (!G_IS_OBJECT(addr))
98 return NULL; // XXX
99
100 obj = G_OBJECT(addr);
101
102 if (!obj)
103 return NULL; // XXX
104
105 return pygobject_new(obj);
106 }
107
108 static PyMethodDef parasite_python_methods[] = {
109 {"capture_stdout", capture_stdout, METH_VARARGS, "Captures stdout"},
110 {"capture_stderr", capture_stderr, METH_VARARGS, "Captures stderr"},
111 {"capture_stdin", capture_stdin, METH_VARARGS, "Captures stdin"},
112 {"gobj", wrap_gobj, METH_VARARGS, "Wraps a C GObject"},
113 {NULL, NULL, 0, NULL}
114 };
115
116
117 static gboolean
is_blacklisted(void)118 is_blacklisted(void)
119 {
120 const char *prgname = g_get_prgname();
121
122 return (!strcmp(prgname, "gimp"));
123 }
124 #endif // ENABLE_PYTHON
125
126 int
parasite_python_init(char ** error)127 parasite_python_init(char **error)
128 {
129 #ifdef ENABLE_PYTHON
130 struct sigaction old_sigint;
131 PyObject *pygtk;
132
133 if (is_blacklisted()) {
134 *error = g_strdup("Application is blacklisted");
135 return 0;
136 }
137
138 /* This prevents errors such as "undefined symbol: PyExc_ImportError" */
139 python_dlhandle = dlopen(PYTHON_SHARED_LIB, RTLD_NOW | RTLD_GLOBAL);
140 if (python_dlhandle == NULL)
141 {
142 *error = g_strdup_printf("Parasite: Error on dlopen(): %s\n", dlerror());
143 return 0;
144 }
145
146 captured_stdout = g_string_new("");
147 captured_stderr = g_string_new("");
148
149 /* Back up and later restore SIGINT so Python doesn't steal it from us. */
150 sigaction(SIGINT, NULL, &old_sigint);
151
152 if (!Py_IsInitialized())
153 Py_Initialize();
154
155 sigaction(SIGINT, &old_sigint, NULL);
156
157 Py_InitModule("parasite", parasite_python_methods);
158 if(PyRun_SimpleString(
159 "import parasite\n"
160 "import sys\n"
161 "\n"
162 "class StdoutCatcher:\n"
163 " def write(self, str):\n"
164 " parasite.capture_stdout(str)\n"
165 " def flush(self):\n"
166 " pass\n"
167 "\n"
168 "class StderrCatcher:\n"
169 " def write(self, str):\n"
170 " parasite.capture_stderr(str)\n"
171 " def flush(self):\n"
172 " pass\n"
173 "\n"
174 "class StdinCatcher:\n"
175 " def readline(self, size=-1):\n"
176 " return parasite.capture_stdin(size)\n"
177 " def read(self, size=-1):\n"
178 " return parasite.capture_stdin(size)\n"
179 " def flush(self):\n"
180 " pass\n"
181 "\n"
182 ) == -1) {
183 dlclose(python_dlhandle);
184 python_dlhandle = NULL;
185 return 0;
186 }
187
188 if (!pygobject_init(-1, -1, -1)) {
189 dlclose(python_dlhandle);
190 python_dlhandle = NULL;
191 return 0;
192 }
193
194 pygtk = PyImport_ImportModule("gtk");
195
196 if (pygtk != NULL)
197 {
198 PyObject *module_dict = PyModule_GetDict(pygtk);
199 PyObject *cobject = PyDict_GetItemString(module_dict, "_PyGtk_API");
200
201 /*
202 * This seems to be NULL when we're running a PyGTK program.
203 * We really need to find out why.
204 */
205 if (cobject != NULL)
206 {
207 if (PyCObject_Check(cobject)) {
208 _PyGtk_API = (struct _PyGtk_FunctionStruct*)
209 PyCObject_AsVoidPtr(cobject);
210 }
211 #if PY_VERSION_HEX >= 0x02070000
212 else if (PyCapsule_IsValid(cobject, "gtk._gtk._PyGtk_API")) {
213 _PyGtk_API = (struct _PyGtk_FunctionStruct*)PyCapsule_GetPointer(cobject, "gtk._gtk._PyGtk_API");
214 }
215 #endif
216 else {
217 *error = g_strdup("Parasite: Could not find _PyGtk_API object");
218 return 0;
219 }
220 }
221 } else {
222 *error = g_strdup("Parasite: Could not import gtk");
223 dlclose(python_dlhandle);
224 python_dlhandle = NULL;
225 return 0;
226 }
227
228 python_enabled = TRUE;
229 #endif // ENABLE_PYTHON
230 return !0;
231 }
232
233 void
parasite_python_done(void)234 parasite_python_done(void)
235 {
236 #ifdef ENABLE_PYTHON
237 if(python_dlhandle != NULL) {
238 dlclose(python_dlhandle);
239 python_dlhandle = NULL;
240 }
241 #endif
242 }
243
244 void
parasite_python_run(const char * command,ParasitePythonLogger stdout_logger,ParasitePythonLogger stderr_logger,gpointer user_data)245 parasite_python_run(const char *command,
246 ParasitePythonLogger stdout_logger,
247 ParasitePythonLogger stderr_logger,
248 gpointer user_data)
249 {
250 #ifdef ENABLE_PYTHON
251 PyGILState_STATE gstate;
252 PyObject *module;
253 PyObject *dict;
254 PyObject *obj;
255 const char *cp;
256
257 /* empty string as command is a noop */
258 if(!strcmp(command, ""))
259 return;
260
261 /* if first non-whitespace character is '#', command is also a noop */
262 cp = command;
263 while(cp && (*cp != '\0') && g_ascii_isspace(*cp))
264 cp++;
265 if(cp && *cp == '#')
266 return;
267
268 gstate = PyGILState_Ensure();
269
270 module = PyImport_AddModule("__main__");
271 dict = PyModule_GetDict(module);
272
273 PyRun_SimpleString("old_stdout = sys.stdout\n"
274 "old_stderr = sys.stderr\n"
275 "old_stdin = sys.stdin\n"
276 "sys.stdout = StdoutCatcher()\n"
277 "sys.stderr = StderrCatcher()\n"
278 "sys.stdin = StdinCatcher()\n");
279
280 obj = PyRun_String(command, Py_single_input, dict, dict);
281 if(PyErr_Occurred())
282 PyErr_Print();
283 PyRun_SimpleString("sys.stdout = old_stdout\n"
284 "sys.stderr = old_stderr\n"
285 "sys.stdin = old_stdin\n");
286
287 if (stdout_logger != NULL)
288 stdout_logger(captured_stdout->str, user_data);
289
290 if (stderr_logger != NULL)
291 stderr_logger(captured_stderr->str, user_data);
292
293 // Print any returned object
294 if (obj != NULL && obj != Py_None) {
295 PyObject *repr = PyObject_Repr(obj);
296 if (repr != NULL) {
297 char *string = PyString_AsString(repr);
298
299 if (stdout_logger != NULL) {
300 stdout_logger(string, user_data);
301 stdout_logger("\n", user_data);
302 }
303 }
304
305 Py_XDECREF(repr);
306 }
307 Py_XDECREF(obj);
308
309 PyGILState_Release(gstate);
310 g_string_erase(captured_stdout, 0, -1);
311 g_string_erase(captured_stderr, 0, -1);
312 #endif // ENABLE_PYTHON
313 }
314
315 gboolean
parasite_python_is_enabled(void)316 parasite_python_is_enabled(void)
317 {
318 return python_enabled;
319 }
320
321 // vim: set et sw=4 ts=4:
322