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