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