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