Python plugin: Add Mailbox type
[claws.git] / src / plugins / python / clawsmailmodule.c
1 /* Python plugin for Claws-Mail
2  * Copyright (C) 2009-2012 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 #include "clawsmailmodule.h"
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include <glib.h>
26 #include <glib/gi18n.h>
27
28 #include "nodetype.h"
29 #include "composewindowtype.h"
30 #include "folderpropertiestype.h"
31 #include "foldertype.h"
32 #include "messageinfotype.h"
33 #include "accounttype.h"
34 #include "mailboxtype.h"
35
36 #include <pygobject.h>
37 #include <pygtk/pygtk.h>
38
39 #include "main.h"
40 #include "mainwindow.h"
41 #include "summaryview.h"
42 #include "quicksearch.h"
43 #include "toolbar.h"
44 #include "prefs_common.h"
45 #include "common/tags.h"
46 #include "account.h"
47
48 #include <glib.h>
49
50 static PyObject *cm_module = NULL;
51
52 PyObject* get_gobj_from_address(gpointer addr)
53 {
54   GObject *obj;
55
56   if (!G_IS_OBJECT(addr))
57       return NULL;
58
59   obj = G_OBJECT(addr);
60
61   if (!obj)
62       return NULL;
63
64   return pygobject_new(obj);
65 }
66
67 static PyObject* private_wrap_gobj(PyObject *self, PyObject *args)
68 {
69     void *addr;
70
71     if (!PyArg_ParseTuple(args, "l", &addr))
72         return NULL;
73
74     return get_gobj_from_address(addr);
75 }
76
77 static PyObject *get_mainwindow_action_group(PyObject *self, PyObject *args)
78 {
79   MainWindow *mainwin;
80
81   mainwin =  mainwindow_get_mainwindow();
82   if(mainwin)
83     return get_gobj_from_address(mainwin->action_group);
84   else
85     return NULL;
86 }
87
88 static PyObject *get_mainwindow_ui_manager(PyObject *self, PyObject *args)
89 {
90   MainWindow *mainwin;
91
92   mainwin =  mainwindow_get_mainwindow();
93   if(mainwin)
94     return get_gobj_from_address(mainwin->ui_manager);
95   else
96     return NULL;
97 }
98
99 static PyObject *get_folderview_selected_folder(PyObject *self, PyObject *args)
100 {
101   MainWindow *mainwin;
102
103   mainwin =  mainwindow_get_mainwindow();
104   if(mainwin && mainwin->folderview) {
105     FolderItem *item;
106     item = folderview_get_selected_item(mainwin->folderview);
107     if(item)
108       return clawsmail_folder_new(item);
109   }
110   Py_RETURN_NONE;
111 }
112
113 static PyObject *get_folderview_selected_mailbox(PyObject *self, PyObject *args)
114 {
115   MainWindow *mainwin;
116
117   mainwin =  mainwindow_get_mainwindow();
118   if(mainwin && mainwin->folderview) {
119     FolderItem *item;
120     item = folderview_get_selected_item(mainwin->folderview);
121     if(item) {
122       gchar *id;
123       id = folder_item_get_identifier(item);
124       /* If there is an id, it's a folder, not a mailbox */
125       if(id) {
126         g_free(id);
127         Py_RETURN_NONE;
128       }
129       else
130         return clawsmail_mailbox_new(item->folder);
131     }
132   }
133   Py_RETURN_NONE;
134 }
135
136
137 static PyObject *folderview_select_folder(PyObject *self, PyObject *args)
138 {
139   MainWindow *mainwin;
140
141   mainwin =  mainwindow_get_mainwindow();
142   if(mainwin && mainwin->folderview) {
143     FolderItem *item;
144     PyObject *folder;
145     folder = PyTuple_GetItem(args, 0);
146     if(!folder)
147       return NULL;
148     Py_INCREF(folder);
149     item = clawsmail_folder_get_item(folder);
150     Py_DECREF(folder);
151     if(item)
152       folderview_select(mainwin->folderview, item);
153   }
154   Py_INCREF(Py_None);
155   return Py_None;
156 }
157
158 static gboolean setup_folderitem_node(GNode *item_node, GNode *item_parent, PyObject **pyparent)
159 {
160   PyObject *pynode, *children;
161   int retval, n_children, i_child;
162   PyObject *folder;
163
164   /* create a python node for the folderitem node */
165   pynode = clawsmail_node_new(cm_module);
166   if(!pynode)
167     return FALSE;
168
169   /* store Folder in pynode */
170   folder = clawsmail_folder_new(item_node->data);
171   retval = PyObject_SetAttrString(pynode, "data", folder);
172   Py_DECREF(folder);
173   if(retval == -1) {
174     Py_DECREF(pynode);
175     return FALSE;
176   }
177
178   if(pyparent && *pyparent) {
179     /* add this node to the parent's childs */
180     children = PyObject_GetAttrString(*pyparent, "children");
181     retval = PyList_Append(children, pynode);
182     Py_DECREF(children);
183
184     if(retval == -1) {
185       Py_DECREF(pynode);
186       return FALSE;
187     }
188   }
189   else if(pyparent) {
190     *pyparent = pynode;
191     Py_INCREF(pynode);
192   }
193
194   /* call this function recursively for all children of the new node */
195   n_children = g_node_n_children(item_node);
196   for(i_child = 0; i_child < n_children; i_child++) {
197     if(!setup_folderitem_node(g_node_nth_child(item_node, i_child), item_node, &pynode)) {
198       Py_DECREF(pynode);
199       return FALSE;
200     }
201   }
202
203   Py_DECREF(pynode);
204   return TRUE;
205 }
206
207 static PyObject* get_folder_tree_from_account_name(const char *str)
208 {
209   PyObject *result;
210   GList *walk;
211
212   result = Py_BuildValue("[]");
213   if(!result)
214     return NULL;
215
216   for(walk = folder_get_list(); walk; walk = walk->next) {
217     Folder *folder = walk->data;
218     if((!str || !g_strcmp0(str, folder->name)) && folder->node) {
219       PyObject *root;
220       int n_children, i_child, retval;
221
222       /* create root nodes */
223       root = clawsmail_node_new(cm_module);
224       if(!root) {
225         Py_DECREF(result);
226         return NULL;
227       }
228
229       n_children = g_node_n_children(folder->node);
230       for(i_child = 0; i_child < n_children; i_child++) {
231         if(!setup_folderitem_node(g_node_nth_child(folder->node, i_child), folder->node, &root)) {
232           Py_DECREF(root);
233           Py_DECREF(result);
234           return NULL;
235         }
236       }
237       retval = PyList_Append(result, root);
238       Py_DECREF(root);
239       if(retval == -1) {
240         Py_DECREF(result);
241         return NULL;
242       }
243     }
244   }
245   return result;
246 }
247
248 static PyObject* get_folder_tree_from_folderitem(FolderItem *item)
249 {
250   PyObject *result;
251   GList *walk;
252
253   for(walk = folder_get_list(); walk; walk = walk->next) {
254     Folder *folder = walk->data;
255     if(folder->node) {
256       GNode *root_node;
257
258       root_node = g_node_find(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
259       if(!root_node)
260         continue;
261
262       result = NULL;
263       if(!setup_folderitem_node(root_node, NULL, &result))
264         return NULL;
265       else
266         return result;
267     }
268   }
269
270   PyErr_SetString(PyExc_LookupError, "Folder not found");
271   return NULL;
272 }
273
274 static PyObject* get_folder_tree(PyObject *self, PyObject *args)
275 {
276   PyObject *arg;
277   PyObject *result;
278   int retval;
279
280   Py_INCREF(Py_None);
281   arg = Py_None;
282   retval = PyArg_ParseTuple(args, "|O", &arg);
283   Py_DECREF(Py_None);
284   if(!retval)
285     return NULL;
286
287   /* calling possibilities:
288    * nothing, the mailbox name in a string, a Folder object */
289
290   /* no arguments: build up a list of folder trees */
291   if(PyTuple_Size(args) == 0) {
292     result = get_folder_tree_from_account_name(NULL);
293   }
294   else if(PyString_Check(arg)){
295     const char *str;
296     str = PyString_AsString(arg);
297     if(!str)
298       return NULL;
299
300     result = get_folder_tree_from_account_name(str);
301   }
302   else if(PyObject_TypeCheck(arg, clawsmail_folder_get_type_object())) {
303     result = get_folder_tree_from_folderitem(clawsmail_folder_get_item(arg));
304   }
305   else {
306     PyErr_SetString(PyExc_TypeError, "Parameter must be nothing, a mailbox string or a Folder object.");
307     return NULL;
308   }
309
310   return result;
311 }
312
313 static PyObject* quicksearch_search(PyObject *self, PyObject *args)
314 {
315   const char *string;
316   int searchtype;
317   QuickSearch *qs;
318   MainWindow *mainwin;
319
320   /* must be given exactly one argument, which is a string */
321   searchtype = prefs_common.summary_quicksearch_type;
322   if(!PyArg_ParseTuple(args, "s|i", &string, &searchtype))
323     return NULL;
324
325   mainwin = mainwindow_get_mainwindow();
326   if(!mainwin || !mainwin->summaryview || !mainwin->summaryview->quicksearch) {
327     PyErr_SetString(PyExc_LookupError, "Quicksearch not found");
328     return NULL;
329   }
330
331   qs = mainwin->summaryview->quicksearch;
332   quicksearch_set(qs, searchtype, string);
333
334   Py_INCREF(Py_None);
335   return Py_None;
336 }
337
338 static PyObject* quicksearch_clear(PyObject *self, PyObject *args)
339 {
340   QuickSearch *qs;
341   MainWindow *mainwin;
342
343   mainwin = mainwindow_get_mainwindow();
344   if(!mainwin || !mainwin->summaryview || !mainwin->summaryview->quicksearch) {
345     PyErr_SetString(PyExc_LookupError, "Quicksearch not found");
346     return NULL;
347   }
348
349   qs = mainwin->summaryview->quicksearch;
350   quicksearch_set(qs, prefs_common.summary_quicksearch_type, "");
351
352   Py_INCREF(Py_None);
353   return Py_None;
354 }
355
356 static PyObject* summaryview_select_messages(PyObject *self, PyObject *args)
357 {
358   PyObject *olist;
359   MainWindow *mainwin;
360   Py_ssize_t size, iEl;
361   GSList *msginfos;
362
363   mainwin = mainwindow_get_mainwindow();
364   if(!mainwin || !mainwin->summaryview) {
365     PyErr_SetString(PyExc_LookupError, "SummaryView not found");
366     return NULL;
367   }
368
369   if(!PyArg_ParseTuple(args, "O!", &PyList_Type, &olist)) {
370     PyErr_SetString(PyExc_LookupError, "Argument must be a list of MessageInfo objects.");
371     return NULL;
372   }
373
374   msginfos = NULL;
375   size = PyList_Size(olist);
376   for(iEl = 0; iEl < size; iEl++) {
377     PyObject *element = PyList_GET_ITEM(olist, iEl);
378
379     if(!element || !PyObject_TypeCheck(element, clawsmail_messageinfo_get_type_object())) {
380       PyErr_SetString(PyExc_LookupError, "Argument must be a list of MessageInfo objects.");
381       return NULL;
382     }
383
384     msginfos = g_slist_prepend(msginfos, clawsmail_messageinfo_get_msginfo(element));
385   }
386
387   summary_unselect_all(mainwin->summaryview);
388   summary_select_by_msg_list(mainwin->summaryview, msginfos);
389   g_slist_free(msginfos);
390
391   Py_INCREF(Py_None);
392   return Py_None;
393 }
394
395 static PyObject* get_summaryview_selected_message_list(PyObject *self, PyObject *args)
396 {
397   MainWindow *mainwin;
398   GSList *list, *walk;
399   PyObject *result;
400
401   mainwin = mainwindow_get_mainwindow();
402   if(!mainwin || !mainwin->summaryview) {
403     PyErr_SetString(PyExc_LookupError, "SummaryView not found");
404     return NULL;
405   }
406
407   result = Py_BuildValue("[]");
408   if(!result)
409     return NULL;
410
411   list = summary_get_selected_msg_list(mainwin->summaryview);
412   for(walk = list; walk; walk = walk->next) {
413     PyObject *msg;
414     msg = clawsmail_messageinfo_new(walk->data);
415     if(PyList_Append(result, msg) == -1) {
416       Py_DECREF(result);
417       return NULL;
418     }
419   }
420   g_slist_free(list);
421
422   return result;
423 }
424
425 static PyObject* is_exiting(PyObject *self, PyObject *args)
426 {
427   if(claws_is_exiting())
428     Py_RETURN_TRUE;
429   else
430     Py_RETURN_FALSE;
431 }
432
433 static PyObject* get_tags(PyObject *self, PyObject *args)
434 {
435   Py_ssize_t num_tags;
436   PyObject *tags_tuple;
437   GSList *tags_list;
438
439   tags_list = tags_get_list();
440   num_tags = g_slist_length(tags_list);
441
442   tags_tuple = PyTuple_New(num_tags);
443   if(tags_tuple != NULL) {
444     Py_ssize_t iTag;
445     PyObject *tag_object;
446     GSList *walk;
447
448     iTag = 0;
449     for(walk = tags_list; walk; walk = walk->next) {
450       tag_object = Py_BuildValue("s", tags_get_tag(GPOINTER_TO_INT(walk->data)));
451       if(tag_object == NULL) {
452         Py_DECREF(tags_tuple);
453         return NULL;
454       }
455       PyTuple_SET_ITEM(tags_tuple, iTag++, tag_object);
456     }
457   }
458
459   g_slist_free(tags_list);
460
461   return tags_tuple;
462 }
463
464 static PyObject* get_accounts(PyObject *self, PyObject *args)
465 {
466   PyObject *accounts_tuple;
467   GList *accounts_list;
468   GList *walk;
469
470   accounts_list = account_get_list();
471
472   accounts_tuple = PyTuple_New(g_list_length(accounts_list));
473   if(accounts_tuple) {
474     PyObject *account_object;
475     Py_ssize_t iAccount;
476
477     iAccount = 0;
478     for(walk = accounts_list; walk; walk = walk->next) {
479       account_object = clawsmail_account_new(walk->data);
480       if(account_object == NULL) {
481         Py_DECREF(accounts_tuple);
482         return NULL;
483       }
484       PyTuple_SET_ITEM(accounts_tuple, iAccount++, account_object);
485     }
486   }
487
488   return accounts_tuple;
489 }
490
491 static PyObject* get_mailboxes(PyObject *self, PyObject *args)
492 {
493   PyObject *mailboxes_tuple;
494   GList *mailboxes_list;
495   GList *walk;
496
497   mailboxes_list = folder_get_list();
498
499   mailboxes_tuple = PyTuple_New(g_list_length(mailboxes_list));
500   if(mailboxes_tuple) {
501     PyObject *mailbox_object;
502     Py_ssize_t iMailbox;
503
504     iMailbox = 0;
505     for(walk = mailboxes_list; walk; walk = walk->next) {
506       mailbox_object = clawsmail_mailbox_new(walk->data);
507       if(mailbox_object == NULL) {
508         Py_DECREF(mailboxes_tuple);
509         return NULL;
510       }
511       PyTuple_SET_ITEM(mailboxes_tuple, iMailbox++, mailbox_object);
512     }
513   }
514
515   return mailboxes_tuple;
516 }
517
518
519 static PyObject* make_sure_tag_exists(PyObject *self, PyObject *args)
520 {
521   int retval;
522   const char *tag_str;
523
524   retval = PyArg_ParseTuple(args, "s", &tag_str);
525   if(!retval)
526     return NULL;
527
528   if(IS_NOT_RESERVED_TAG(tag_str) == FALSE) {
529     /* tag name is reserved, raise KeyError */
530     PyErr_SetString(PyExc_ValueError, "Tag name is reserved");
531     return NULL;
532   }
533
534   tags_add_tag(tag_str);
535
536   Py_RETURN_NONE;
537 }
538
539 static PyObject* delete_tag(PyObject *self, PyObject *args)
540 {
541   int retval;
542   const char *tag_str;
543   gint tag_id;
544   MainWindow *mainwin;
545
546   retval = PyArg_ParseTuple(args, "s", &tag_str);
547   if(!retval)
548     return NULL;
549
550   tag_id = tags_get_id_for_str(tag_str);
551   if(tag_id == -1) {
552     PyErr_SetString(PyExc_KeyError, "Tag does not exist");
553     return NULL;
554   }
555
556   tags_remove_tag(tag_id);
557
558   /* update display */
559   mainwin = mainwindow_get_mainwindow();
560   if(mainwin)
561     summary_redisplay_msg(mainwin->summaryview);
562
563   Py_RETURN_NONE;
564 }
565
566
567 static PyObject* rename_tag(PyObject *self, PyObject *args)
568 {
569   int retval;
570   const char *old_tag_str;
571   const char *new_tag_str;
572   gint tag_id;
573   MainWindow *mainwin;
574
575   retval = PyArg_ParseTuple(args, "ss", &old_tag_str, &new_tag_str);
576   if(!retval)
577     return NULL;
578
579   if((IS_NOT_RESERVED_TAG(new_tag_str) == FALSE) ||(IS_NOT_RESERVED_TAG(old_tag_str) == FALSE)) {
580     PyErr_SetString(PyExc_ValueError, "Tag name is reserved");
581     return NULL;
582   }
583
584   tag_id = tags_get_id_for_str(old_tag_str);
585   if(tag_id == -1) {
586     PyErr_SetString(PyExc_KeyError, "Tag does not exist");
587     return NULL;
588   }
589
590   tags_update_tag(tag_id, new_tag_str);
591
592   /* update display */
593   mainwin = mainwindow_get_mainwindow();
594   if(mainwin)
595     summary_redisplay_msg(mainwin->summaryview);
596
597   Py_RETURN_NONE;
598 }
599
600 static gboolean get_message_list_for_move_or_copy(PyObject *messagelist, PyObject *folder, GSList **list)
601 {
602   Py_ssize_t size, iEl;
603   FolderItem *folderitem;
604
605   *list = NULL;
606
607   folderitem = clawsmail_folder_get_item(folder);
608   if(!folderitem) {
609     PyErr_SetString(PyExc_LookupError, "Brokern Folder object.");
610     return FALSE;
611   }
612
613   size = PyList_Size(messagelist);
614   for(iEl = 0; iEl < size; iEl++) {
615     PyObject *element = PyList_GET_ITEM(messagelist, iEl);
616     MsgInfo *msginfo;
617
618     if(!element || !PyObject_TypeCheck(element, clawsmail_messageinfo_get_type_object())) {
619       PyErr_SetString(PyExc_TypeError, "Argument must be a list of MessageInfo objects.");
620       return FALSE;
621     }
622
623     msginfo = clawsmail_messageinfo_get_msginfo(element);
624     if(!msginfo) {
625       PyErr_SetString(PyExc_LookupError, "Broken MessageInfo object.");
626       return FALSE;
627     }
628
629     procmsg_msginfo_set_to_folder(msginfo, folderitem);
630     *list = g_slist_prepend(*list, msginfo);
631   }
632
633   return TRUE;
634 }
635
636 static PyObject* move_or_copy_messages(PyObject *self, PyObject *args, gboolean move)
637 {
638   PyObject *messagelist;
639   PyObject *folder;
640   int retval;
641   GSList *list = NULL;
642
643   retval = PyArg_ParseTuple(args, "O!O!",
644     &PyList_Type, &messagelist,
645     clawsmail_folder_get_type_object(), &folder);
646   if(!retval )
647     return NULL;
648
649   folder_item_update_freeze();
650
651   if(!get_message_list_for_move_or_copy(messagelist, folder, &list))
652     goto err;
653
654   if(move)
655     procmsg_move_messages(list);
656   else
657     procmsg_copy_messages(list);
658
659   folder_item_update_thaw();
660   g_slist_free(list);
661   Py_RETURN_NONE;
662
663 err:
664   folder_item_update_thaw();
665   g_slist_free(list);
666   return NULL;
667 }
668
669 static PyObject* move_messages(PyObject *self, PyObject *args)
670 {
671   return move_or_copy_messages(self, args, TRUE);
672 }
673
674
675 static PyObject* copy_messages(PyObject *self, PyObject *args)
676 {
677   return move_or_copy_messages(self, args, FALSE);
678 }
679
680 static PyObject* get_current_account(PyObject *self, PyObject *args)
681 {
682   PrefsAccount *account;
683   account = account_get_cur_account();
684   if(account) {
685     return clawsmail_account_new(account);
686   }
687   else
688     Py_RETURN_NONE;
689 }
690
691 static PyObject* get_default_account(PyObject *self, PyObject *args)
692 {
693   PrefsAccount *account;
694   account = account_get_default();
695   if(account) {
696     return clawsmail_account_new(account);
697   }
698   else
699     Py_RETURN_NONE;
700 }
701
702
703 static PyMethodDef ClawsMailMethods[] = {
704     /* public */
705     {"get_mainwindow_action_group",  get_mainwindow_action_group, METH_NOARGS,
706      "get_mainwindow_action_group() - get action group of main window menu\n"
707      "\n"
708      "Returns the gtk.ActionGroup for the main window."},
709
710     {"get_mainwindow_ui_manager",  get_mainwindow_ui_manager, METH_NOARGS,
711      "get_mainwindow_ui_manager() - get ui manager of main window\n"
712      "\n"
713      "Returns the gtk.UIManager for the main window."},
714
715     {"get_folder_tree",  get_folder_tree, METH_VARARGS,
716      "get_folder_tree([root]) - get a folder tree\n"
717      "\n"
718      "Without arguments, get a list of folder trees for all mailboxes.\n"
719      "\n"
720      "If the optional root argument is a string, it is supposed to be a\n"
721      "mailbox name. The function then returns a tree of folders of that mailbox.\n"
722      "\n"
723      "If the optional root argument is a clawsmail.Folder, the function\n"
724      "returns a tree of subfolders with the given folder as root element.\n"
725      "\n"
726      "In any case, a tree consists of elements of the type clawsmail.Node."},
727
728     {"get_folderview_selected_folder",  get_folderview_selected_folder, METH_NOARGS,
729      "get_folderview_selected_folder() - get selected folder in folderview\n"
730      "\n"
731      "Returns the currently selected folder as a clawsmail.Folder or None if no folder is selected."},
732     {"folderview_select_folder",  folderview_select_folder, METH_VARARGS,
733      "folderview_select_folder(folder) - select folder in folderview\n"
734      "\n"
735      "Takes an argument of type clawsmail.Folder, and selects the corresponding folder."},
736
737     {"get_folderview_selected_mailbox",  get_folderview_selected_mailbox, METH_NOARGS,
738      "get_folderview_selected_mailbox() - get selected mailbox in folderview\n"
739      "\n"
740      "Returns the currently selected mailbox as a clawsmail.Mailbox or None if no mailbox is selected."},
741
742     {"quicksearch_search", quicksearch_search, METH_VARARGS,
743      "quicksearch_search(string [, type]) - perform a quicksearch\n"
744      "\n"
745      "Perform a quicksearch of the given string. The optional type argument can be\n"
746      "one of clawsmail.QUICK_SEARCH_SUBJECT, clawsmail.QUICK_SEARCH_FROM, clawsmail.QUICK_SEARCH_TO,\n"
747      "clawsmail.QUICK_SEARCH_EXTENDED, clawsmail.QUICK_SEARCH_MIXED, or clawsmail.QUICK_SEARCH_TAG.\n"
748      "If it is omitted, the current selection is used. The string argument has to be a valid search\n"
749      "string corresponding to the type."},
750
751     {"quicksearch_clear", quicksearch_clear, METH_NOARGS,
752      "quicksearch_clear() - clear the quicksearch"},
753
754     {"get_summaryview_selected_message_list", get_summaryview_selected_message_list, METH_NOARGS,
755      "get_summaryview_selected_message_list() - get selected message list\n"
756      "\n"
757      "Get a list of clawsmail.MessageInfo objects of the current selection."},
758
759     {"summaryview_select_messages", summaryview_select_messages, METH_VARARGS,
760      "summaryview_select_messages(message_list) - select a list of messages in the summary view\n"
761      "\n"
762      "Select a list of clawsmail.MessageInfo objects in the summary view."},
763
764     {"is_exiting", is_exiting, METH_NOARGS,
765      "is_exiting() - test whether Claws Mail is currently exiting\n"
766      "\n"
767      "Returns True if Claws Mail is currently exiting. The most common usage for this is to skip\n"
768      "unnecessary cleanup tasks in a shutdown script when Claws Mail is exiting anyways. If the Python\n"
769      "plugin is explicitly unloaded, the shutdown script will still be called, but this function will\n"
770      "return False."},
771
772     {"move_messages", move_messages, METH_VARARGS,
773      "move_messages(message_list, target_folder) - move a list of messages to a target folder\n"
774      "\n"
775      "Move a list of clawsmail.MessageInfo objects to a target folder.\n"
776      "The target_folder argument has to be a clawsmail.Folder object."},
777
778     {"copy_messages", copy_messages, METH_VARARGS,
779      "copy_messages(message_list, target_folder) - copy a list of messages to a target folder\n"
780      "\n"
781      "Copy a list of clawsmail.MessageInfo objects to a target folder.\n"
782      "The target_folder argument has to be a clawsmail.Folder object."},
783
784     {"get_tags", get_tags, METH_NOARGS,
785      "get_tags() - get a tuple of all tags that Claws Mail knows about\n"
786      "\n"
787      "Get a tuple of strings representing all tags that are defined in Claws Mail."},
788
789     {"make_sure_tag_exists", make_sure_tag_exists, METH_VARARGS,
790      "make_sure_tag_exists(tag) - make sure that the specified tag exists\n"
791      "\n"
792      "This function creates the given tag if it does not exist yet.\n"
793      "It is not an error if the tag already exists. In this case, this function does nothing.\n"
794      "However, if a reserved tag name is chosen, a ValueError exception is raised."},
795
796     {"delete_tag", delete_tag, METH_VARARGS,
797      "delete_tag(tag) - delete a tag\n"
798      "\n"
799      "This function deletes an existing tag.\n"
800      "Raises a KeyError exception if the tag does not exist."},
801
802     {"rename_tag", rename_tag, METH_VARARGS,
803      "rename_tag(old_tag, new_tag) - rename tag old_tag to new_tag\n"
804      "\n"
805      "This function renames an existing tag.\n"
806      "Raises a KeyError exception if the tag does not exist.\n"
807      "Raises a ValueError exception if the old or new tag name is a reserved name."},
808
809      {"get_accounts", get_accounts, METH_NOARGS,
810       "get_accounts() - get a tuple of all accounts that Claws Mail knows about\n"
811       "\n"
812       "Get a tuple of Account objects representing all accounts that are defined in Claws Mail."},
813
814       {"get_current_account", get_current_account, METH_NOARGS,
815        "get_current_account() - get the current account\n"
816        "\n"
817        "Return the object representing the currently selected account."},
818
819      {"get_default_account", get_default_account, METH_NOARGS,
820       "get_default_account() - get the default account\n"
821       "\n"
822       "Return the object representing the default account."},
823
824      {"get_mailboxes", get_mailboxes, METH_NOARGS,
825       "get_mailboxes() - get a tuple of all mailboxes that Claws Mail knows about\n"
826       "\n"
827       "Get a tuple of Mailbox objects representing all mailboxes that are defined in Claws Mail."},
828
829      /* private */
830     {"__gobj", private_wrap_gobj, METH_VARARGS,
831      "__gobj(ptr) - transforms a C GObject pointer into a PyGObject\n"
832      "\n"
833      "For internal usage only."},
834
835     {NULL, NULL, 0, NULL}
836 };
837
838 static gboolean add_miscstuff(PyObject *module)
839 {
840   gboolean retval;
841   PyObject *dict;
842   PyObject *res;
843   const char *cmd =
844       "QUICK_SEARCH_SUBJECT = 0\n"
845       "QUICK_SEARCH_FROM = 1\n"
846       "QUICK_SEARCH_TO = 2\n"
847       "QUICK_SEARCH_EXTENDED = 3\n"
848       "QUICK_SEARCH_MIXED = 4\n"
849       "QUICK_SEARCH_TAG = 5\n"
850       "\n";
851   dict = PyModule_GetDict(module);
852   res = PyRun_String(cmd, Py_file_input, dict, dict);
853   retval = (res != NULL);
854   Py_XDECREF(res);
855   return retval;
856 }
857
858
859 PyMODINIT_FUNC initclawsmail(void)
860 {
861   gboolean ok = TRUE;
862
863   /* create module */
864   cm_module = Py_InitModule3("clawsmail", ClawsMailMethods,
865       "This module can be used to access some of Claws Mail's data structures\n"
866       "in order to extend or modify the user interface or automate repetitive tasks.\n"
867       "\n"
868       "Whenever possible, the interface works with standard GTK+ widgets\n"
869       "via the PyGTK bindings, so you can refer to the GTK+ / PyGTK documentation\n"
870       "to find out about all possible options.\n"
871       "\n"
872       "The interface to Claws Mail in this module is extended on a 'as-needed' basis.\n"
873       "If you're missing something specific, try contacting the author.");
874
875   /* add module member "compose_window" set to None */
876   Py_INCREF(Py_None);
877   PyModule_AddObject(cm_module, "compose_window", Py_None);
878
879   /* initialize classes */
880   ok = ok && cmpy_add_node(cm_module);
881   ok = ok && cmpy_add_composewindow(cm_module);
882   ok = ok && cmpy_add_folder(cm_module);
883   ok = ok && cmpy_add_messageinfo(cm_module);
884   ok = ok && cmpy_add_account(cm_module);
885   ok = ok && cmpy_add_folderproperties(cm_module);
886   ok = ok && cmpy_add_mailbox(cm_module);
887
888   /* initialize misc things */
889   if(ok)
890     add_miscstuff(cm_module);
891 }
892
893
894 void put_composewindow_into_module(Compose *compose)
895 {
896   PyObject *pycompose;
897
898   pycompose = clawsmail_compose_new(cm_module, compose);
899   PyObject_SetAttrString(cm_module, "compose_window", pycompose);
900   Py_DECREF(pycompose);
901 }