Clean all ‘… warning: "_POSIX_C_SOURCE" redefined’
[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 "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 PyMethodDef MessageInfo_methods[] = {
279   {"is_new",  is_new, METH_NOARGS,
280    "is_new() - checks if the message is new\n"
281    "\n"
282    "Returns True if the new flag of the message is set."},
283
284   {"is_unread",  is_unread, METH_NOARGS,
285    "is_unread() - checks if the message is unread\n"
286    "\n"
287    "Returns True if the unread flag of the message is set."},
288
289   {"is_marked",  is_marked, METH_NOARGS,
290    "is_marked() - checks if the message is marked\n"
291    "\n"
292    "Returns True if the marked flag of the message is set."},
293
294   {"is_replied",  is_replied, METH_NOARGS,
295    "is_replied() - checks if the message has been replied to\n"
296    "\n"
297    "Returns True if the replied flag of the message is set."},
298
299   {"is_locked",  is_locked, METH_NOARGS,
300    "is_locked() - checks if the message has been locked\n"
301    "\n"
302    "Returns True if the locked flag of the message is set."},
303
304   {"is_forwarded",  is_forwarded, METH_NOARGS,
305    "is_forwarded() - checks if the message has been forwarded\n"
306    "\n"
307    "Returns True if the forwarded flag of the message is set."},
308
309   {"get_tags",  get_tags, METH_NOARGS,
310    "get_tags() - get message tags\n"
311    "\n"
312    "Returns a tuple of tags that apply to this message."},
313
314   {"add_tag",  add_tag, METH_VARARGS,
315    "add_tag(tag) - add a tag to this message\n"
316    "\n"
317    "Add a tag to this message. If the tag is already set, nothing is done.\n"
318    "If the tag does not exist, a ValueError exception is raised."},
319
320   {"remove_tag",  remove_tag, METH_VARARGS,
321    "remove_tag(tag) - remove a tag from this message\n"
322    "\n"
323    "Remove a tag from this message. If the tag is not set, a KeyError exception is raised.\n"
324    "If the tag does not exist, a ValueError exception is raised."},
325
326    {"get_header",  get_header, METH_VARARGS,
327     "get_header(name) - get a message header with a given name\n"
328     "\n"
329     "Get a message header content with a given name. If the header does not exist,\n"
330     "the value 'None' is returned. If multiple headers with the same name exist,\n"
331     "the first one is returned."},
332
333   {NULL}
334 };
335
336 static PyGetSetDef MessageInfo_getset[] = {
337     { "From", (getter)get_From, (setter)NULL,
338       "From - the From header of the message", NULL},
339
340     { "To", (getter)get_To, (setter)NULL,
341       "To - the To header of the message", NULL },
342
343     { "Cc", (getter)get_Cc, (setter)NULL,
344       "Cc - the Cc header of the message", NULL },
345
346     {"Subject", (getter)get_Subject, (setter)NULL,
347      "Subject - the subject header of the message", NULL},
348
349     {"MessageID", (getter)get_MessageID, (setter)NULL,
350      "MessageID - the Message-ID header of the message", NULL},
351
352     {"FilePath", (getter)get_FilePath, (setter)NULL,
353      "FilePath - path and filename of the message", NULL},
354
355     {NULL}
356 };
357
358
359 static PyTypeObject clawsmail_MessageInfoType = {
360     PyObject_HEAD_INIT(NULL)
361     0,                         /* ob_size*/
362     "clawsmail.MessageInfo",   /* tp_name*/
363     sizeof(clawsmail_MessageInfoObject), /* tp_basicsize*/
364     0,                         /* tp_itemsize*/
365     (destructor)MessageInfo_dealloc, /* tp_dealloc*/
366     0,                         /* tp_print*/
367     0,                         /* tp_getattr*/
368     0,                         /* tp_setattr*/
369     0,                         /* tp_compare*/
370     0,                         /* tp_repr*/
371     0,                         /* tp_as_number*/
372     0,                         /* tp_as_sequence*/
373     0,                         /* tp_as_mapping*/
374     0,                         /* tp_hash */
375     0,                         /* tp_call*/
376     (reprfunc)MessageInfo_str, /* tp_str*/
377     0,                         /* tp_getattro*/
378     0,                         /* tp_setattro*/
379     0,                         /* tp_as_buffer*/
380     Py_TPFLAGS_DEFAULT,        /* tp_flags*/
381     "A MessageInfo represents" /* tp_doc */
382     " a single message.\n\n"
383     "Do not construct objects of this type yourself.",
384     0,                         /* tp_traverse */
385     0,                         /* tp_clear */
386     0,                         /* tp_richcompare */
387     0,                         /* tp_weaklistoffset */
388     0,                         /* tp_iter */
389     0,                         /* tp_iternext */
390     MessageInfo_methods,       /* tp_methods */
391     0,                         /* tp_members */
392     MessageInfo_getset,        /* tp_getset */
393     0,                         /* tp_base */
394     0,                         /* tp_dict */
395     0,                         /* tp_descr_get */
396     0,                         /* tp_descr_set */
397     0,                         /* tp_dictoffset */
398     (initproc)MessageInfo_init,/* tp_init */
399     0,                         /* tp_alloc */
400     0,                         /* tp_new */
401 };
402
403 gboolean cmpy_add_messageinfo(PyObject *module)
404 {
405   clawsmail_MessageInfoType.tp_new = PyType_GenericNew;
406   if(PyType_Ready(&clawsmail_MessageInfoType) < 0)
407     return FALSE;
408
409   Py_INCREF(&clawsmail_MessageInfoType);
410   return (PyModule_AddObject(module, "MessageInfo", (PyObject*)&clawsmail_MessageInfoType) == 0);
411 }
412
413 PyObject* clawsmail_messageinfo_new(MsgInfo *msginfo)
414 {
415   clawsmail_MessageInfoObject *ff;
416
417   if(!msginfo)
418     return NULL;
419
420   ff = (clawsmail_MessageInfoObject*) PyObject_CallObject((PyObject*) &clawsmail_MessageInfoType, NULL);
421   if(!ff)
422     return NULL;
423
424   ff->msginfo = msginfo;
425   return (PyObject*)ff;
426 }
427
428 PyTypeObject* clawsmail_messageinfo_get_type_object()
429 {
430   return &clawsmail_MessageInfoType;
431 }
432
433 MsgInfo* clawsmail_messageinfo_get_msginfo(PyObject *self)
434 {
435   return ((clawsmail_MessageInfoObject*)self)->msginfo;
436 }