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