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