Python plugin: Make some functions static
[claws.git] / src / plugins / python / messageinfotype.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 #ifdef HAVE_CONFIG_H
19 #  include "config.h"
20 #include "claws-features.h"
21 #endif
22
23 #include <glib.h>
24 #include <glib/gi18n.h>
25
26 #include "messageinfotype.h"
27
28 #include "common/tags.h"
29 #include "common/defs.h"
30 #include "mainwindow.h"
31 #include "summaryview.h"
32 #include "procheader.h"
33
34 #include <structmember.h>
35
36 #include <string.h>
37
38 #define HEADER_CONTENT_SIZE BUFFSIZE
39
40 typedef struct {
41     PyObject_HEAD
42     PyObject *from;
43     PyObject *to;
44     PyObject *cc;
45     PyObject *subject;
46     PyObject *msgid;
47     PyObject *filepath;
48     MsgInfo *msginfo;
49 } clawsmail_MessageInfoObject;
50
51
52 static void MessageInfo_dealloc(clawsmail_MessageInfoObject* self)
53 {
54   Py_XDECREF(self->from);
55   Py_XDECREF(self->to);
56   Py_XDECREF(self->cc);
57   Py_XDECREF(self->subject);
58   Py_XDECREF(self->msgid);
59   self->ob_type->tp_free((PyObject*)self);
60 }
61
62 static int MessageInfo_init(clawsmail_MessageInfoObject *self, PyObject *args, PyObject *kwds)
63 {
64   Py_INCREF(Py_None);
65   self->from = Py_None;
66
67   Py_INCREF(Py_None);
68   self->to = Py_None;
69
70   Py_INCREF(Py_None);
71   self->cc = Py_None;
72
73   Py_INCREF(Py_None);
74   self->subject = Py_None;
75
76   Py_INCREF(Py_None);
77   self->msgid = Py_None;
78
79   return 0;
80 }
81
82 static PyObject* MessageInfo_str(PyObject *self)
83 {
84   PyObject *str;
85   str = PyString_FromString("MessageInfo: ");
86   PyString_ConcatAndDel(&str, PyObject_GetAttrString(self, "From"));
87   PyString_ConcatAndDel(&str, PyString_FromString(" / "));
88   PyString_ConcatAndDel(&str, PyObject_GetAttrString(self, "Subject"));
89   return str;
90 }
91
92 static PyObject *py_boolean_return_value(gboolean val)
93 {
94   if(val) {
95     Py_INCREF(Py_True);
96     return Py_True;
97   }
98   else {
99     Py_INCREF(Py_False);
100     return Py_False;
101   }
102 }
103
104 static PyObject *is_new(PyObject *self, PyObject *args)
105 {
106   return py_boolean_return_value(MSG_IS_NEW(((clawsmail_MessageInfoObject*)self)->msginfo->flags));
107 }
108
109 static PyObject *is_unread(PyObject *self, PyObject *args)
110 {
111   return py_boolean_return_value(MSG_IS_UNREAD(((clawsmail_MessageInfoObject*)self)->msginfo->flags));
112 }
113
114 static PyObject *is_marked(PyObject *self, PyObject *args)
115 {
116   return py_boolean_return_value(MSG_IS_MARKED(((clawsmail_MessageInfoObject*)self)->msginfo->flags));
117 }
118
119 static PyObject *is_replied(PyObject *self, PyObject *args)
120 {
121   return py_boolean_return_value(MSG_IS_REPLIED(((clawsmail_MessageInfoObject*)self)->msginfo->flags));
122 }
123
124 static PyObject *is_locked(PyObject *self, PyObject *args)
125 {
126   return py_boolean_return_value(MSG_IS_LOCKED(((clawsmail_MessageInfoObject*)self)->msginfo->flags));
127 }
128
129 static PyObject *is_forwarded(PyObject *self, PyObject *args)
130 {
131   return py_boolean_return_value(MSG_IS_FORWARDED(((clawsmail_MessageInfoObject*)self)->msginfo->flags));
132 }
133
134 static PyObject* get_tags(PyObject *self, PyObject *args)
135 {
136   GSList *tags_list;
137   Py_ssize_t num_tags;
138   PyObject *tags_tuple;
139
140   tags_list = ((clawsmail_MessageInfoObject*)self)->msginfo->tags;
141   num_tags = g_slist_length(tags_list);
142
143   tags_tuple = PyTuple_New(num_tags);
144   if(tags_tuple != NULL) {
145     Py_ssize_t iTag;
146     PyObject *tag_object;
147     GSList *walk;
148
149     iTag = 0;
150     for(walk = tags_list; walk; walk = walk->next) {
151       tag_object = Py_BuildValue("s", tags_get_tag(GPOINTER_TO_INT(walk->data)));
152       if(tag_object == NULL) {
153         Py_DECREF(tags_tuple);
154         return NULL;
155       }
156       PyTuple_SET_ITEM(tags_tuple, iTag++, tag_object);
157     }
158   }
159
160   return tags_tuple;
161 }
162
163
164 static PyObject* add_or_remove_tag(PyObject *self, PyObject *args, gboolean add)
165 {
166   int retval;
167   const char *tag_str;
168   gint tag_id;
169   MsgInfo *msginfo;
170   MainWindow *mainwin;
171
172   retval = PyArg_ParseTuple(args, "s", &tag_str);
173   if(!retval)
174     return NULL;
175
176   tag_id = tags_get_id_for_str(tag_str);
177   if(tag_id == -1) {
178     PyErr_SetString(PyExc_ValueError, "Tag does not exist");
179     return NULL;
180   }
181
182   msginfo = ((clawsmail_MessageInfoObject*)self)->msginfo;
183
184   if(!add) {
185     /* raise KeyError if tag is not set */
186     if(!g_slist_find(msginfo->tags, GINT_TO_POINTER(tag_id))) {
187       PyErr_SetString(PyExc_KeyError, "Tag is not set on this message");
188       return NULL;
189     }
190   }
191
192   procmsg_msginfo_update_tags(msginfo, add, tag_id);
193
194   /* update display */
195   mainwin = mainwindow_get_mainwindow();
196   if(mainwin)
197     summary_redisplay_msg(mainwin->summaryview);
198
199   Py_RETURN_NONE;
200 }
201
202
203
204 static PyObject* add_tag(PyObject *self, PyObject *args)
205 {
206   return add_or_remove_tag(self, args, TRUE);
207 }
208
209
210 static PyObject* remove_tag(PyObject *self, PyObject *args)
211 {
212   return add_or_remove_tag(self, args, FALSE);
213 }
214
215 static PyObject* get_header(PyObject *self, PyObject *args)
216 {
217   int retval;
218   const char *header_str;
219   char *header_str_dup;
220   MsgInfo *msginfo;
221   gchar header_content[HEADER_CONTENT_SIZE];
222
223   retval = PyArg_ParseTuple(args, "s", &header_str);
224   if(!retval)
225     return NULL;
226
227   msginfo = ((clawsmail_MessageInfoObject*)self)->msginfo;
228
229   header_str_dup = g_strdup(header_str);
230   retval = procheader_get_header_from_msginfo(msginfo, header_content, HEADER_CONTENT_SIZE, header_str);
231   g_free(header_str_dup);
232   if(retval == 0) {
233     PyObject *header_content_object;
234     gchar *content_start;
235
236     /* the string is now Header: Value. Strip the Header: part */
237     content_start = strstr(header_content, ":");
238     if(content_start == NULL)
239       content_start = header_content;
240     else
241       content_start++;
242     /* strip leading spaces */
243     while(*content_start == ' ')
244       content_start++;
245     header_content_object = Py_BuildValue("s", content_start);
246     return header_content_object;
247   }
248   else {
249     Py_RETURN_NONE;
250   }
251 }
252
253
254 static PyMethodDef MessageInfo_methods[] = {
255   {"is_new",  is_new, METH_NOARGS,
256    "is_new() - checks if the message is new\n"
257    "\n"
258    "Returns True if the new flag of the message is set."},
259
260   {"is_unread",  is_unread, METH_NOARGS,
261    "is_unread() - checks if the message is unread\n"
262    "\n"
263    "Returns True if the unread flag of the message is set."},
264
265   {"is_marked",  is_marked, METH_NOARGS,
266    "is_marked() - checks if the message is marked\n"
267    "\n"
268    "Returns True if the marked flag of the message is set."},
269
270   {"is_replied",  is_replied, METH_NOARGS,
271    "is_replied() - checks if the message has been replied to\n"
272    "\n"
273    "Returns True if the replied flag of the message is set."},
274
275   {"is_locked",  is_locked, METH_NOARGS,
276    "is_locked() - checks if the message has been locked\n"
277    "\n"
278    "Returns True if the locked flag of the message is set."},
279
280   {"is_forwarded",  is_forwarded, METH_NOARGS,
281    "is_forwarded() - checks if the message has been forwarded\n"
282    "\n"
283    "Returns True if the forwarded flag of the message is set."},
284
285   {"get_tags",  get_tags, METH_NOARGS,
286    "get_tags() - get message tags\n"
287    "\n"
288    "Returns a tuple of tags that apply to this message."},
289
290   {"add_tag",  add_tag, METH_VARARGS,
291    "add_tag(tag) - add a tag to this message\n"
292    "\n"
293    "Add a tag to this message. If the tag is already set, nothing is done.\n"
294    "If the tag does not exist, a ValueError exception is raised."},
295
296   {"remove_tag",  remove_tag, METH_VARARGS,
297    "remove_tag(tag) - remove a tag from this message\n"
298    "\n"
299    "Remove a tag from this message. If the tag is not set, a KeyError exception is raised.\n"
300    "If the tag does not exist, a ValueError exception is raised."},
301
302    {"get_header",  get_header, METH_VARARGS,
303     "get_header(name) - get a message header with a given name\n"
304     "\n"
305     "Get a message header content with a given name. If the header does not exist,\n"
306     "the value 'None' is returned. If multiple headers with the same name exist,\n"
307     "the first one is returned."},
308
309   {NULL}
310 };
311
312 static PyMemberDef MessageInfo_members[] = {
313     { "From", T_OBJECT_EX, offsetof(clawsmail_MessageInfoObject, from), 0,
314         "From - the From header of the message" },
315
316     { "To", T_OBJECT_EX, offsetof(clawsmail_MessageInfoObject, to), 0,
317         "To - the To header of the message" },
318
319     { "Cc", T_OBJECT_EX, offsetof(clawsmail_MessageInfoObject, cc), 0,
320         "Cc - the Cc header of the message" },
321
322     {"Subject", T_OBJECT_EX, offsetof(clawsmail_MessageInfoObject, subject), 0,
323      "Subject - the subject header of the message"},
324
325     {"MessageID", T_OBJECT_EX, offsetof(clawsmail_MessageInfoObject, msgid), 0,
326      "MessageID - the Message-ID header of the message"},
327
328     {"FilePath", T_OBJECT_EX, offsetof(clawsmail_MessageInfoObject, filepath), 0,
329      "FilePath - path and filename of the message"},
330
331     {NULL}
332 };
333
334 static PyTypeObject clawsmail_MessageInfoType = {
335     PyObject_HEAD_INIT(NULL)
336     0,                         /* ob_size*/
337     "clawsmail.MessageInfo",   /* tp_name*/
338     sizeof(clawsmail_MessageInfoObject), /* tp_basicsize*/
339     0,                         /* tp_itemsize*/
340     (destructor)MessageInfo_dealloc, /* tp_dealloc*/
341     0,                         /* tp_print*/
342     0,                         /* tp_getattr*/
343     0,                         /* tp_setattr*/
344     0,                         /* tp_compare*/
345     0,                         /* tp_repr*/
346     0,                         /* tp_as_number*/
347     0,                         /* tp_as_sequence*/
348     0,                         /* tp_as_mapping*/
349     0,                         /* tp_hash */
350     0,                         /* tp_call*/
351     MessageInfo_str,           /* tp_str*/
352     0,                         /* tp_getattro*/
353     0,                         /* tp_setattro*/
354     0,                         /* tp_as_buffer*/
355     Py_TPFLAGS_DEFAULT,        /* tp_flags*/
356     "A MessageInfo represents" /* tp_doc */
357     "a single message.",
358     0,                         /* tp_traverse */
359     0,                         /* tp_clear */
360     0,                         /* tp_richcompare */
361     0,                         /* tp_weaklistoffset */
362     0,                         /* tp_iter */
363     0,                         /* tp_iternext */
364     MessageInfo_methods,       /* tp_methods */
365     MessageInfo_members,       /* tp_members */
366     0,                         /* tp_getset */
367     0,                         /* tp_base */
368     0,                         /* tp_dict */
369     0,                         /* tp_descr_get */
370     0,                         /* tp_descr_set */
371     0,                         /* tp_dictoffset */
372     (initproc)MessageInfo_init,/* tp_init */
373     0,                         /* tp_alloc */
374     0,                         /* tp_new */
375 };
376
377 PyMODINIT_FUNC initmessageinfo(PyObject *module)
378 {
379     clawsmail_MessageInfoType.tp_new = PyType_GenericNew;
380     if(PyType_Ready(&clawsmail_MessageInfoType) < 0)
381         return;
382
383     Py_INCREF(&clawsmail_MessageInfoType);
384     PyModule_AddObject(module, "MessageInfo", (PyObject*)&clawsmail_MessageInfoType);
385 }
386
387 #define MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(fis, pms)     \
388   do {                                                            \
389     if(fis) {                                                     \
390       PyObject *str;                                              \
391       str = PyString_FromString(fis);                             \
392       if(str) {                                                   \
393         int retval;                                               \
394         retval = PyObject_SetAttrString((PyObject*)ff, pms, str); \
395         Py_DECREF(str);                                           \
396         if(retval == -1)                                          \
397           goto err;                                               \
398       }                                                           \
399     }                                                             \
400   } while(0)
401
402 static gboolean update_members(clawsmail_MessageInfoObject *ff, MsgInfo *msginfo)
403 {
404   gchar *filepath;
405
406   MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(msginfo->from, "From");
407   MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(msginfo->to, "To");
408   MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(msginfo->cc, "Cc");
409   MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(msginfo->subject, "Subject");
410   MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(msginfo->msgid, "MessageID");
411
412   filepath = procmsg_get_message_file_path(msginfo);
413   if(filepath) {
414     MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(filepath, "FilePath");
415     g_free(filepath);
416   }
417   else {
418     MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER("", "FilePath");
419   }
420
421   return TRUE;
422 err:
423   return FALSE;
424 }
425
426 PyObject* clawsmail_messageinfo_new(MsgInfo *msginfo)
427 {
428   clawsmail_MessageInfoObject *ff;
429
430   if(!msginfo)
431     return NULL;
432
433   ff = (clawsmail_MessageInfoObject*) PyObject_CallObject((PyObject*) &clawsmail_MessageInfoType, NULL);
434   if(!ff)
435     return NULL;
436
437   ff->msginfo = msginfo;
438
439   if(update_members(ff, msginfo))
440     return (PyObject*)ff;
441   else {
442     Py_XDECREF(ff);
443     return NULL;
444   }
445 }
446
447 PyTypeObject* clawsmail_messageinfo_get_type_object()
448 {
449   return &clawsmail_MessageInfoType;
450 }
451
452 MsgInfo* clawsmail_messageinfo_get_msginfo(PyObject *self)
453 {
454   return ((clawsmail_MessageInfoObject*)self)->msginfo;
455 }