1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  *  Copyright (C) 2004,2005 Johan Dahlin
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 
24 #include <Python.h>
25 #define NO_IMPORT_PYGOBJECT //To avoid a multiple definition, nautilus-python-object.c also includes and does the import.
26 #include <pygobject.h>
27 #include <gmodule.h>
28 #include <gtk/gtk.h>
29 
30 #include "nautilus-python.h"
31 #include "nautilus-python-object.h"
32 
33 #include <nautilus-extension.h>
34 
35 static const GDebugKey nautilus_python_debug_keys[] = {
36     {"misc", NAUTILUS_PYTHON_DEBUG_MISC},
37 };
38 static const guint nautilus_python_ndebug_keys = sizeof (nautilus_python_debug_keys) / sizeof (GDebugKey);
39 NautilusPythonDebug nautilus_python_debug;
40 
41 static gboolean nautilus_python_init_python(void);
42 
43 static GArray *all_types = NULL;
44 
45 PyTypeObject *_PyGtkWidget_Type;
46 PyTypeObject *_PyNautilusColumn_Type;
47 PyTypeObject *_PyNautilusColumnProvider_Type;
48 PyTypeObject *_PyNautilusInfoProvider_Type;
49 PyTypeObject *_PyNautilusLocationWidgetProvider_Type;
50 PyTypeObject *_PyNautilusMenu_Type;
51 PyTypeObject *_PyNautilusMenuItem_Type;
52 PyTypeObject *_PyNautilusMenuProvider_Type;
53 PyTypeObject *_PyNautilusPropertyPage_Type;
54 PyTypeObject *_PyNautilusPropertyPageProvider_Type;
55 PyTypeObject *_PyNautilusOperationHandle_Type;
56 
57 static inline gboolean
np_init_pygobject(void)58 np_init_pygobject(void) {
59     PyObject *gobject = pygobject_init (PYGOBJECT_MAJOR_VERSION, PYGOBJECT_MINOR_VERSION, PYGOBJECT_MICRO_VERSION);
60 
61     if (gobject == NULL) {
62         PyErr_Print ();
63         return FALSE;
64     }
65 
66     return TRUE;
67 }
68 
69 static void
nautilus_python_load_file(GTypeModule * type_module,const gchar * filename)70 nautilus_python_load_file(GTypeModule *type_module,
71                           const gchar *filename) {
72     PyObject *main_module, *main_locals, *locals, *key, *value;
73     PyObject *module;
74     GType gtype;
75     Py_ssize_t pos = 0;
76 
77     debug_enter_args("filename=%s", filename);
78 
79     main_module = PyImport_AddModule("__main__");
80     if (main_module == NULL) {
81         g_warning("Could not get __main__.");
82         return;
83     }
84 
85     main_locals = PyModule_GetDict(main_module);
86     module = PyImport_ImportModuleEx((char *) filename, main_locals, main_locals, NULL);
87     if (!module) {
88         PyErr_Print();
89         return;
90     }
91 
92     locals = PyModule_GetDict(module);
93 
94     while (PyDict_Next(locals, &pos, &key, &value)) {
95         if (!PyType_Check(value))
96             continue;
97 
98         if (PyObject_IsSubclass(value, (PyObject*)&PyNautilusColumnProvider_Type) ||
99 				PyObject_IsSubclass(value, (PyObject*)&PyNautilusInfoProvider_Type) ||
100 				PyObject_IsSubclass(value, (PyObject*)&PyNautilusLocationWidgetProvider_Type) ||
101 				PyObject_IsSubclass(value, (PyObject*)&PyNautilusMenuProvider_Type) ||
102 				PyObject_IsSubclass(value, (PyObject*)&PyNautilusPropertyPageProvider_Type)) {
103             gtype = nautilus_python_object_get_type(type_module, value);
104             g_array_append_val(all_types, gtype);
105         }
106     }
107 
108     debug("Loaded python modules");
109 }
110 
111 static void
nautilus_python_load_dir(GTypeModule * module,const char * dirname)112 nautilus_python_load_dir (GTypeModule *module,
113                           const char  *dirname) {
114     GDir *dir;
115     const char *name;
116     gboolean initialized = FALSE;
117 
118     debug_enter_args("dirname=%s", dirname);
119 
120     dir = g_dir_open(dirname, 0, NULL);
121     if (!dir)
122         return;
123 
124     while ((name = g_dir_read_name(dir))) {
125         if (g_str_has_suffix(name, ".py")) {
126             char *modulename;
127             int len;
128 
129             len = strlen(name) - 3;
130             modulename = g_new0(char, len + 1 );
131             strncpy(modulename, name, len);
132 
133             if (!initialized) {
134                 PyObject *sys_path, *py_path;
135 
136                 /* n-p python part is initialized on demand (or not
137                 * at all if no extensions are found) */
138                 if (!nautilus_python_init_python()) {
139                     g_warning("nautilus_python_init_python failed");
140                     g_dir_close(dir);
141                     break;
142                 }
143 
144                 /* sys.path.insert(0, dirname) */
145                 sys_path = PySys_GetObject("path");
146 #if PY_MAJOR_VERSION >= 3
147                 py_path = PyUnicode_FromString(dirname);
148 #else
149                 py_path = PyString_FromString(dirname);
150 #endif
151                 PyList_Insert(sys_path, 0, py_path);
152                 Py_DECREF(py_path);
153             }
154 
155             nautilus_python_load_file(module, modulename);
156         }
157     }
158 }
159 
160 static gboolean
nautilus_python_init_python(void)161 nautilus_python_init_python (void) {
162     PyObject *nautilus;
163     GModule *libpython;
164 
165     if (Py_IsInitialized())
166         return TRUE;
167 
168     debug("g_module_open " PY_LIB_LOC "/lib" PY_LIB_NAME "." G_MODULE_SUFFIX ".1.0");
169     libpython = g_module_open (PY_LIB_LOC "/lib" PY_LIB_NAME "." G_MODULE_SUFFIX ".1.0", 0);
170     if (!libpython)
171         g_warning("g_module_open libpython failed: %s", g_module_error());
172 
173     debug("Py_Initialize");
174     Py_Initialize();
175     if (PyErr_Occurred()) {
176         PyErr_Print();
177         return FALSE;
178     }
179 
180     debug("PySys_SetArgv");
181 #if PY_MAJOR_VERSION >= 3
182     wchar_t *argv[] = { L"nautilus", NULL };
183 #else
184     char *argv[] = { "nautilus", NULL };
185 #endif
186     PySys_SetArgv(1, argv);
187     if (PyErr_Occurred()) {
188         PyErr_Print();
189         return FALSE;
190     }
191 
192     debug("Sanitize the python search path");
193     PyRun_SimpleString("import sys; sys.path = [path for path in sys.path if path]");
194     if (PyErr_Occurred()) {
195         PyErr_Print();
196         return FALSE;
197     }
198 
199     /* import gobject */
200     debug("init_pygobject");
201     if (!np_init_pygobject()) {
202         g_warning("pygobject initialization failed");
203         return FALSE;
204     }
205 
206     /* import nautilus */
207     g_setenv("INSIDE_NAUTILUS_PYTHON", "", FALSE);
208     debug("import nautilus");
209     PyRun_SimpleString("import gi; gi.require_version('Nautilus', '3.0')");
210     nautilus = PyImport_ImportModule("gi.repository.Nautilus");
211     if (!nautilus) {
212         PyErr_Print();
213         return FALSE;
214     }
215 
216     _PyGtkWidget_Type = pygobject_lookup_class(GTK_TYPE_WIDGET);
217     g_assert(_PyGtkWidget_Type != NULL);
218 
219 #define IMPORT(x, y) \
220     _PyNautilus##x##_Type = (PyTypeObject *)PyObject_GetAttrString(nautilus, y); \
221     if (_PyNautilus##x##_Type == NULL) { \
222         PyErr_Print(); \
223         return FALSE; \
224     }
225 
226     IMPORT(Column, "Column");
227     IMPORT(ColumnProvider, "ColumnProvider");
228     IMPORT(InfoProvider, "InfoProvider");
229     IMPORT(LocationWidgetProvider, "LocationWidgetProvider");
230     IMPORT(Menu, "Menu");
231     IMPORT(MenuItem, "MenuItem");
232     IMPORT(MenuProvider, "MenuProvider");
233     IMPORT(PropertyPage, "PropertyPage");
234     IMPORT(PropertyPageProvider, "PropertyPageProvider");
235     IMPORT(OperationHandle, "OperationHandle");
236 
237 #undef IMPORT
238 
239     return TRUE;
240 }
241 
242 
243 static void
nautilus_python_check_all_directories(GTypeModule * module)244 nautilus_python_check_all_directories(GTypeModule *module) {
245     gchar *extensions_dir = NULL;
246 
247     GList *dirs = NULL;
248 
249     // Check ~/.local/share first
250     dirs = g_list_append(dirs, g_build_filename(g_get_user_data_dir(),
251         "nautilus-python", "extensions", NULL));
252 
253     // If nautilus is built in a non-standard prefix
254     // Check nautilus prefix's DATADIR
255     gchar *prefix_extension_dir = DATADIR "/nautilus-python/extensions";
256     dirs = g_list_append(dirs, prefix_extension_dir);
257 
258     // Check all system data dirs
259     const gchar *const *temp = g_get_system_data_dirs();
260     while (*temp != NULL) {
261         gchar *dir = g_build_filename(*temp,
262             "nautilus-python", "extensions", NULL);
263         if (g_strcmp0(dir, prefix_extension_dir) != 0) {
264             dirs = g_list_append(dirs, dir);
265         }
266 
267         temp++;
268     }
269 
270     dirs = g_list_first(dirs);
271     while (dirs != NULL) {
272         gchar *dir = dirs->data;
273         nautilus_python_load_dir(module, dir);
274         dirs = dirs->next;
275     }
276 
277     g_list_free(dirs);
278 }
279 
280 void
nautilus_module_initialize(GTypeModule * module)281 nautilus_module_initialize(GTypeModule *module) {
282     gchar *user_extensions_dir;
283     const gchar *env_string;
284 
285     env_string = g_getenv("NAUTILUS_PYTHON_DEBUG");
286     if (env_string != NULL) {
287         nautilus_python_debug = g_parse_debug_string(env_string,
288                                                      nautilus_python_debug_keys,
289                                                      nautilus_python_ndebug_keys);
290         env_string = NULL;
291     }
292 
293     debug_enter();
294 
295     all_types = g_array_new(FALSE, FALSE, sizeof(GType));
296 
297 	nautilus_python_check_all_directories(module);
298 }
299 
300 void
nautilus_module_shutdown(void)301 nautilus_module_shutdown(void) {
302     debug_enter();
303 
304     if (Py_IsInitialized())
305         Py_Finalize();
306 
307     g_array_free(all_types, TRUE);
308 }
309 
310 void
nautilus_module_list_types(const GType ** types,int * num_types)311 nautilus_module_list_types(const GType **types,
312                            int          *num_types) {
313     debug_enter();
314 
315     *types = (GType*)all_types->data;
316     *num_types = all_types->len;
317 }
318