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