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