Python plugin: Better error reporting during directory creation
[claws.git] / src / plugins / python / python_plugin.c
1 /* Python plugin for Claws-Mail
2  * Copyright (C) 2009 Holger Berndt
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #ifdef HAVE_CONFIG_H
19 #  include "config.h"
20 #include "claws-features.h"
21 #endif
22
23 #include <glib.h>
24 #include <glib/gi18n.h>
25
26 #include <Python.h>
27
28 #include <errno.h>
29
30 #include "common/hooks.h"
31 #include "common/plugin.h"
32 #include "common/version.h"
33 #include "common/utils.h"
34 #include "gtk/menu.h"
35 #include "main.h"
36 #include "mainwindow.h"
37 #include "prefs_toolbar.h"
38
39 #include "python-shell.h"
40 #include "python-hooks.h"
41 #include "clawsmailmodule.h"
42
43 #define PYTHON_SCRIPTS_BASE_DIR "python-scripts"
44 #define PYTHON_SCRIPTS_MAIN_DIR "main"
45 #define PYTHON_SCRIPTS_COMPOSE_DIR "compose"
46 #define PYTHON_SCRIPTS_AUTO_DIR "auto"
47 #define PYTHON_SCRIPTS_AUTO_STARTUP "startup"
48 #define PYTHON_SCRIPTS_AUTO_SHUTDOWN "shutdown"
49 #define PYTHON_SCRIPTS_AUTO_COMPOSE "compose_any"
50 #define PYTHON_SCRIPTS_ACTION_PREFIX "Tools/PythonScripts/"
51
52 static GSList *menu_id_list = NULL;
53 static GSList *python_mainwin_scripts_id_list = NULL;
54 static GSList *python_mainwin_scripts_names = NULL;
55 static GSList *python_compose_scripts_names = NULL;
56
57 static GtkWidget *python_console = NULL;
58
59 static guint hook_compose_create;
60
61 static gboolean python_console_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
62 {
63   MainWindow *mainwin;
64   GtkToggleAction *action;
65
66   mainwin =  mainwindow_get_mainwindow();
67   action = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mainwin->action_group, "Tools/ShowPythonConsole"));
68   gtk_toggle_action_set_active(action, FALSE);
69   return TRUE;
70 }
71
72 static void setup_python_console(void)
73 {
74   GtkWidget *vbox;
75   GtkWidget *console;
76
77   python_console = gtk_window_new(GTK_WINDOW_TOPLEVEL);
78   gtk_widget_set_size_request(python_console, 600, 400);
79
80   vbox = gtk_vbox_new(FALSE, 0);
81   gtk_container_add(GTK_CONTAINER(python_console), vbox);
82
83   console = parasite_python_shell_new();
84   gtk_box_pack_start(GTK_BOX(vbox), console, TRUE, TRUE, 0);
85
86   g_signal_connect(python_console, "delete-event", G_CALLBACK(python_console_delete_event), NULL);
87
88   gtk_widget_show_all(python_console);
89
90   parasite_python_shell_focus(PARASITE_PYTHON_SHELL(console));
91 }
92
93 static void show_hide_python_console(GtkToggleAction *action, gpointer callback_data)
94 {
95   if(gtk_toggle_action_get_active(action)) {
96     if(!python_console)
97       setup_python_console();
98     gtk_widget_show(python_console);
99   }
100   else {
101     gtk_widget_hide(python_console);
102   }
103 }
104
105 static void remove_python_scripts_menus(void)
106 {
107   GSList *walk;
108   MainWindow *mainwin;
109
110   mainwin =  mainwindow_get_mainwindow();
111
112   /* toolbar */
113   for(walk = python_mainwin_scripts_names; walk; walk = walk->next)
114     prefs_toolbar_unregister_plugin_item(TOOLBAR_MAIN, "Python", walk->data);
115
116   /* ui */
117   for(walk = python_mainwin_scripts_id_list; walk; walk = walk->next)
118       gtk_ui_manager_remove_ui(mainwin->ui_manager, GPOINTER_TO_UINT(walk->data));
119   g_slist_free(python_mainwin_scripts_id_list);
120   python_mainwin_scripts_id_list = NULL;
121
122   /* actions */
123   for(walk = python_mainwin_scripts_names; walk; walk = walk->next) {
124     GtkAction *action;
125     gchar *entry;
126     entry = g_strconcat(PYTHON_SCRIPTS_ACTION_PREFIX, walk->data, NULL);
127     action = gtk_action_group_get_action(mainwin->action_group, entry);
128     g_free(entry);
129     if(action)
130       gtk_action_group_remove_action(mainwin->action_group, action);
131     g_free(walk->data);
132   }
133   g_slist_free(python_mainwin_scripts_names);
134   python_mainwin_scripts_names = NULL;
135
136   /* compose scripts */
137   for(walk = python_compose_scripts_names; walk; walk = walk->next) {
138     prefs_toolbar_unregister_plugin_item(TOOLBAR_COMPOSE, "Python", walk->data);
139     g_free(walk->data);
140   }
141   g_slist_free(python_compose_scripts_names);
142   python_compose_scripts_names = NULL;
143 }
144
145 static gchar* extract_filename(const gchar *str)
146 {
147   gchar *filename;
148
149   filename = g_strrstr(str, "/");
150   if(!filename || *(filename+1) == '\0') {
151     debug_print("Error: Could not extract filename from %s\n", str);
152     return NULL;
153   }
154   filename++;
155   return filename;
156 }
157
158 static void run_script_file(const gchar *filename, Compose *compose)
159 {
160   FILE *fp;
161   fp = fopen(filename, "r");
162   if(!fp) {
163     g_print("Error: Could not open file '%s'\n", filename);
164     return;
165   }
166   put_composewindow_into_module(compose);
167   PyRun_SimpleFile(fp, filename);
168   fclose(fp);
169 }
170
171 static void run_auto_script_file_if_it_exists(const gchar *autofilename, Compose *compose)
172 {
173   gchar *auto_filepath;
174
175   /* execute auto/autofilename, if it exists */
176   auto_filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
177       PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S,
178       PYTHON_SCRIPTS_AUTO_DIR, G_DIR_SEPARATOR_S, autofilename, NULL);
179   if(file_exist(auto_filepath, FALSE))
180     run_script_file(auto_filepath, compose);
181   g_free(auto_filepath);
182 }
183
184 static void python_mainwin_script_callback(GtkAction *action, gpointer data)
185 {
186   char *filename;
187
188   filename = extract_filename(data);
189   if(!filename)
190     return;
191   filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_MAIN_DIR, G_DIR_SEPARATOR_S, filename, NULL);
192   run_script_file(filename, NULL);
193   g_free(filename);
194 }
195
196 typedef struct _ComposeActionData ComposeActionData;
197 struct _ComposeActionData {
198   gchar *name;
199   Compose *compose;
200 };
201
202 static void python_compose_script_callback(GtkAction *action, gpointer data)
203 {
204   char *filename;
205   ComposeActionData *dat = (ComposeActionData*)data;
206
207   filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_COMPOSE_DIR, G_DIR_SEPARATOR_S, dat->name, NULL);
208   run_script_file(filename, dat->compose);
209
210   g_free(filename);
211 }
212
213 static void mainwin_toolbar_callback(gpointer parent, const gchar *item_name, gpointer data)
214 {
215         gchar *script;
216         script = g_strconcat(PYTHON_SCRIPTS_ACTION_PREFIX, item_name, NULL);
217         python_mainwin_script_callback(NULL, script);
218         g_free(script);
219 }
220
221 static void compose_toolbar_callback(gpointer parent, const gchar *item_name, gpointer data)
222 {
223   gchar *filename;
224
225   filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
226       PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S,
227       PYTHON_SCRIPTS_COMPOSE_DIR, G_DIR_SEPARATOR_S,
228       item_name, NULL);
229   run_script_file(filename, (Compose*)parent);
230   g_free(filename);
231 }
232
233 static char* make_sure_script_directory_exists(const gchar *subdir)
234 {
235   char *dir;
236   char *retval = NULL;
237   dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S, subdir, NULL);
238   if(!g_file_test(dir, G_FILE_TEST_IS_DIR)) {
239     if(g_mkdir(dir, 0777) != 0)
240       retval = g_strdup_printf("Could not create directory '%s': %s", dir, g_strerror(errno));
241   }
242   g_free(dir);
243   return retval;
244 }
245
246 static int make_sure_directories_exist(char **error)
247 {
248   const char* dirs[] = {
249       ""
250       , PYTHON_SCRIPTS_MAIN_DIR
251       , PYTHON_SCRIPTS_COMPOSE_DIR
252       , PYTHON_SCRIPTS_AUTO_DIR
253       , NULL
254   };
255   const char **dir = dirs;
256
257   *error = NULL;
258
259   while(*dir) {
260     *error = make_sure_script_directory_exists(*dir);
261     if(*error)
262       break;
263     dir++;
264   }
265
266   return (*error == NULL);
267 }
268
269 static void migrate_scripts_out_of_base_dir(void)
270 {
271   char *base_dir;
272   GDir *dir;
273   const char *filename;
274   gchar *dest_dir;
275
276   base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR, NULL);
277   dir = g_dir_open(base_dir, 0, NULL);
278   g_free(base_dir);
279   if(!dir)
280     return;
281
282   dest_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
283       PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S,
284       PYTHON_SCRIPTS_MAIN_DIR, NULL);
285   if(!g_file_test(dest_dir, G_FILE_TEST_IS_DIR)) {
286     if(g_mkdir(dest_dir, 0777) != 0) {
287       g_free(dest_dir);
288       g_dir_close(dir);
289       return;
290     }
291   }
292
293   while((filename = g_dir_read_name(dir)) != NULL) {
294     gchar *filepath;
295     filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S, filename, NULL);
296     if(g_file_test(filepath, G_FILE_TEST_IS_REGULAR)) {
297       gchar *dest_file;
298       dest_file = g_strconcat(dest_dir, G_DIR_SEPARATOR_S, filename, NULL);
299       if(move_file(filepath, dest_file, FALSE) == 0)
300         g_print("Python plugin: Moved file '%s' to %s subdir\n", filename, PYTHON_SCRIPTS_MAIN_DIR);
301       else
302         g_print("Python plugin: Warning: Could not move file '%s' to %s subdir\n", filename, PYTHON_SCRIPTS_MAIN_DIR);
303       g_free(dest_file);
304     }
305     g_free(filepath);
306   }
307   g_dir_close(dir);
308   g_free(dest_dir);
309 }
310
311
312 static void create_mainwindow_menus_and_items(GSList *filenames, gint num_entries)
313 {
314   MainWindow *mainwin;
315   gint ii;
316   GSList *walk;
317   GtkActionEntry *entries;
318
319   /* create menu items */
320   entries = g_new0(GtkActionEntry, num_entries);
321   ii = 0;
322   mainwin =  mainwindow_get_mainwindow();
323   for(walk = filenames; walk; walk = walk->next) {
324     entries[ii].name = g_strconcat(PYTHON_SCRIPTS_ACTION_PREFIX, walk->data, NULL);
325     entries[ii].label = walk->data;
326     entries[ii].callback = G_CALLBACK(python_mainwin_script_callback);
327     gtk_action_group_add_actions(mainwin->action_group, &(entries[ii]), 1, (gpointer)entries[ii].name);
328     ii++;
329   }
330   for(ii = 0; ii < num_entries; ii++) {
331     guint id;
332
333     python_mainwin_scripts_names = g_slist_prepend(python_mainwin_scripts_names, g_strdup(entries[ii].label));
334     MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/" PYTHON_SCRIPTS_ACTION_PREFIX, entries[ii].label,
335         entries[ii].name, GTK_UI_MANAGER_MENUITEM, id)
336     python_mainwin_scripts_id_list = g_slist_prepend(python_mainwin_scripts_id_list, GUINT_TO_POINTER(id));
337
338     prefs_toolbar_register_plugin_item(TOOLBAR_MAIN, "Python", entries[ii].label, mainwin_toolbar_callback, NULL);
339   }
340
341   g_free(entries);
342 }
343
344
345 /* this function doesn't really create menu items, but prepares a list that can be used
346  * in the compose create hook. It does however register the scripts for the toolbar editor */
347 static void create_compose_menus_and_items(GSList *filenames)
348 {
349   GSList *walk;
350   for(walk = filenames; walk; walk = walk->next) {
351     python_compose_scripts_names = g_slist_prepend(python_compose_scripts_names, g_strdup((gchar*)walk->data));
352     prefs_toolbar_register_plugin_item(TOOLBAR_COMPOSE, "Python", (gchar*)walk->data, compose_toolbar_callback, NULL);
353   }
354 }
355
356 static GtkActionEntry compose_tools_python_actions[] = {
357     {"Tools/PythonScripts", NULL, N_("Python scripts") },
358 };
359
360 static void ComposeActionData_destroy_cb(gpointer data)
361 {
362   ComposeActionData *dat = (ComposeActionData*)data;
363   g_free(dat->name);
364   g_free(dat);
365 }
366
367 static gboolean my_compose_create_hook(gpointer cw, gpointer data)
368 {
369   gint ii;
370   GSList *walk;
371   GtkActionEntry *entries;
372   GtkActionGroup *action_group;
373   Compose *compose = (Compose*)cw;
374   guint num_entries = g_slist_length(python_compose_scripts_names);
375
376   action_group = gtk_action_group_new("PythonPlugin");
377   gtk_action_group_add_actions(action_group, compose_tools_python_actions, 1, NULL);
378   entries = g_new0(GtkActionEntry, num_entries);
379   ii = 0;
380   for(walk = python_compose_scripts_names; walk; walk = walk->next) {
381     ComposeActionData *dat;
382
383     entries[ii].name = walk->data;
384     entries[ii].label = walk->data;
385     entries[ii].callback = G_CALLBACK(python_compose_script_callback);
386
387     dat = g_new0(ComposeActionData, 1);
388     dat->name = g_strdup(walk->data);
389     dat->compose = compose;
390
391     gtk_action_group_add_actions_full(action_group, &(entries[ii]), 1, dat, ComposeActionData_destroy_cb);
392     ii++;
393   }
394   gtk_ui_manager_insert_action_group(compose->ui_manager, action_group, 0);
395
396   MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "PythonScripts",
397       "Tools/PythonScripts", GTK_UI_MANAGER_MENU)
398
399   for(ii = 0; ii < num_entries; ii++) {
400     MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/" PYTHON_SCRIPTS_ACTION_PREFIX, entries[ii].label,
401         entries[ii].name, GTK_UI_MANAGER_MENUITEM)
402   }
403
404   g_free(entries);
405
406   run_auto_script_file_if_it_exists(PYTHON_SCRIPTS_AUTO_COMPOSE, compose);
407
408   return FALSE;
409 }
410
411
412 static void refresh_scripts_in_dir(const gchar *subdir, ToolbarType toolbar_type)
413 {
414   char *scripts_dir;
415   GDir *dir;
416   GError *error = NULL;
417   const char *filename;
418   GSList *filenames = NULL;
419   GSList *walk;
420   gint num_entries;
421
422   scripts_dir = g_strconcat(get_rc_dir(),
423       G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR,
424       G_DIR_SEPARATOR_S, subdir,
425       NULL);
426   debug_print("Refreshing: %s\n", scripts_dir);
427
428   dir = g_dir_open(scripts_dir, 0, &error);
429   g_free(scripts_dir);
430
431   if(!dir) {
432     g_print("Could not open directory '%s': %s\n", subdir, error->message);
433     g_error_free(error);
434     return;
435   }
436
437   /* get filenames */
438   num_entries = 0;
439   while((filename = g_dir_read_name(dir)) != NULL) {
440     char *fn;
441
442     fn = g_strdup(filename);
443     filenames = g_slist_prepend(filenames, fn);
444     num_entries++;
445   }
446   g_dir_close(dir);
447
448   if(toolbar_type == TOOLBAR_MAIN)
449     create_mainwindow_menus_and_items(filenames, num_entries);
450   else if(toolbar_type == TOOLBAR_COMPOSE)
451     create_compose_menus_and_items(filenames);
452
453   /* cleanup */
454   for(walk = filenames; walk; walk = walk->next)
455     g_free(walk->data);
456   g_slist_free(filenames);
457 }
458
459 static void browse_python_scripts_dir(GtkAction *action, gpointer data)
460 {
461   gchar *uri;
462   GdkAppLaunchContext *launch_context;
463   GError *error = NULL;
464   MainWindow *mainwin;
465
466   mainwin =  mainwindow_get_mainwindow();
467   if(!mainwin) {
468       debug_print("Browse Python scripts: Problems getting the mainwindow\n");
469       return;
470   }
471   launch_context = gdk_app_launch_context_new();
472   gdk_app_launch_context_set_screen(launch_context, gtk_widget_get_screen(mainwin->window));
473   uri = g_strconcat("file://", get_rc_dir(), G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S, NULL);
474   g_app_info_launch_default_for_uri(uri, G_APP_LAUNCH_CONTEXT(launch_context), &error);
475
476   if(error) {
477       debug_print("Could not open scripts dir browser: '%s'\n", error->message);
478       g_error_free(error);
479   }
480
481   g_object_unref(launch_context);
482   g_free(uri);
483 }
484
485 static void refresh_python_scripts_menus(GtkAction *action, gpointer data)
486 {
487   remove_python_scripts_menus();
488
489   migrate_scripts_out_of_base_dir();
490
491   refresh_scripts_in_dir(PYTHON_SCRIPTS_MAIN_DIR, TOOLBAR_MAIN);
492   refresh_scripts_in_dir(PYTHON_SCRIPTS_COMPOSE_DIR, TOOLBAR_COMPOSE);
493 }
494
495 static GtkToggleActionEntry mainwindow_tools_python_toggle[] = {
496     {"Tools/ShowPythonConsole", NULL, N_("Show Python console..."),
497         NULL, NULL, G_CALLBACK(show_hide_python_console), FALSE},
498 };
499
500 static GtkActionEntry mainwindow_tools_python_actions[] = {
501     {"Tools/PythonScripts", NULL, N_("Python scripts") },
502     {"Tools/PythonScripts/Refresh", NULL, N_("Refresh"),
503         NULL, NULL, G_CALLBACK(refresh_python_scripts_menus) },
504     {"Tools/PythonScripts/Browse", NULL, N_("Browse"),
505         NULL, NULL, G_CALLBACK(browse_python_scripts_dir) },
506     {"Tools/PythonScripts/---", NULL, "---" },
507 };
508
509 void python_menu_init(void)
510 {
511   MainWindow *mainwin;
512   guint id;
513
514   mainwin =  mainwindow_get_mainwindow();
515
516   gtk_action_group_add_toggle_actions(mainwin->action_group, mainwindow_tools_python_toggle, 1, mainwin);
517   gtk_action_group_add_actions(mainwin->action_group, mainwindow_tools_python_actions, 3, mainwin);
518
519   MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/Tools", "ShowPythonConsole",
520       "Tools/ShowPythonConsole", GTK_UI_MANAGER_MENUITEM, id)
521   menu_id_list = g_slist_prepend(menu_id_list, GUINT_TO_POINTER(id));
522
523   MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/Tools", "PythonScripts",
524       "Tools/PythonScripts", GTK_UI_MANAGER_MENU, id)
525   menu_id_list = g_slist_prepend(menu_id_list, GUINT_TO_POINTER(id));
526
527   MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/Tools/PythonScripts", "Refresh",
528       "Tools/PythonScripts/Refresh", GTK_UI_MANAGER_MENUITEM, id)
529   menu_id_list = g_slist_prepend(menu_id_list, GUINT_TO_POINTER(id));
530
531   MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/Tools/PythonScripts", "Browse",
532       "Tools/PythonScripts/Browse", GTK_UI_MANAGER_MENUITEM, id)
533   menu_id_list = g_slist_prepend(menu_id_list, GUINT_TO_POINTER(id));
534
535   MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/Tools/PythonScripts", "Separator1",
536       "Tools/PythonScripts/---", GTK_UI_MANAGER_SEPARATOR, id)
537   menu_id_list = g_slist_prepend(menu_id_list, GUINT_TO_POINTER(id));
538
539   refresh_python_scripts_menus(NULL, NULL);
540 }
541
542 void python_menu_done(void)
543 {
544   MainWindow *mainwin;
545
546   mainwin = mainwindow_get_mainwindow();
547
548   if(mainwin && !claws_is_exiting()) {
549     GSList *walk;
550
551     remove_python_scripts_menus();
552
553     for(walk = menu_id_list; walk; walk = walk->next)
554       gtk_ui_manager_remove_ui(mainwin->ui_manager, GPOINTER_TO_UINT(walk->data));
555     MENUITEM_REMUI_MANAGER(mainwin->ui_manager, mainwin->action_group, "Tools/ShowPythonConsole", 0);
556     MENUITEM_REMUI_MANAGER(mainwin->ui_manager, mainwin->action_group, "Tools/PythonScripts", 0);
557     MENUITEM_REMUI_MANAGER(mainwin->ui_manager, mainwin->action_group, "Tools/PythonScripts/Refresh", 0);
558     MENUITEM_REMUI_MANAGER(mainwin->ui_manager, mainwin->action_group, "Tools/PythonScripts/Browse", 0);
559     MENUITEM_REMUI_MANAGER(mainwin->ui_manager, mainwin->action_group, "Tools/PythonScripts/---", 0);
560   }
561 }
562
563 gint plugin_init(gchar **error)
564 {
565   /* Version check */
566   if(!check_plugin_version(MAKE_NUMERIC_VERSION(3,7,6,9), VERSION_NUMERIC, _("Python"), error))
567     return -1;
568
569   /* load hooks */
570   hook_compose_create = hooks_register_hook(COMPOSE_CREATED_HOOKLIST, my_compose_create_hook, NULL);
571   if(hook_compose_create == (guint)-1) {
572     *error = g_strdup(_("Failed to register \"compose create hook\" in the Python plugin"));
573     return -1;
574   }
575
576   /* script directories */
577   if(!make_sure_directories_exist(error))
578     return -1;
579
580   /* initialize python interpreter */
581   Py_Initialize();
582
583   /* initialize python interactive shell */
584   parasite_python_init();
585
586   /* initialize Claws Mail Python module */
587   claws_mail_python_init();
588
589   /* load menu options */
590   python_menu_init();
591
592   run_auto_script_file_if_it_exists(PYTHON_SCRIPTS_AUTO_STARTUP, NULL);
593
594   debug_print("Python plugin loaded\n");
595
596   return 0;
597 }
598
599 gboolean plugin_done(void)
600 {
601   hooks_unregister_hook(COMPOSE_CREATED_HOOKLIST, hook_compose_create);
602
603   run_auto_script_file_if_it_exists(PYTHON_SCRIPTS_AUTO_SHUTDOWN, NULL);
604
605   python_menu_done();
606
607   if(python_console) {
608     gtk_widget_destroy(python_console);
609     python_console = NULL;
610   }
611
612   /* finialize python interpreter */
613   Py_Finalize();
614
615   debug_print("Python plugin done and unloaded.\n");
616   return FALSE;
617 }
618
619 const gchar *plugin_name(void)
620 {
621   return _("Python");
622 }
623
624 const gchar *plugin_desc(void)
625 {
626   return _("This plugin provides Python integration features.\n"
627            "\nFor the most up-to-date API documentation, type\n"
628            "\n help(clawsmail)\n"
629            "\nin the interactive Python console under Tools -> Show Python console.\n"
630            "\nThe source distribution of this plugin comes with various example scripts "
631            "in the \"examples\" subdirectory. If you wrote a script that you would be "
632            "interested in sharing, feel free to send it to me to have it considered "
633            "for inclusion in the examples.\n"
634            "\nFeedback to <berndth@gmx.de> is welcome.");
635 }
636
637 const gchar *plugin_type(void)
638 {
639   return "GTK2";
640 }
641
642 const gchar *plugin_licence(void)
643 {
644   return "GPL3+";
645 }
646
647 const gchar *plugin_version(void)
648 {
649   return VERSION;
650 }
651
652 struct PluginFeature *plugin_provides(void)
653 {
654   static struct PluginFeature features[] =
655     { {PLUGIN_UTILITY, N_("Python integration")},
656       {PLUGIN_NOTHING, NULL}};
657   return features;
658 }