1 /*
2 * e-mail-part-list.c
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 #include <camel/camel.h>
19
20 #include "e-mail-part-list.h"
21
22 #define E_MAIL_PART_LIST_GET_PRIVATE(obj) \
23 (G_TYPE_INSTANCE_GET_PRIVATE \
24 ((obj), E_TYPE_MAIL_PART_LIST, EMailPartListPrivate))
25
26 struct _EMailPartListPrivate {
27 CamelFolder *folder;
28 CamelMimeMessage *message;
29 gchar *message_uid;
30
31 GQueue queue;
32 GMutex queue_lock;
33 };
34
35 enum {
36 PROP_0,
37 PROP_FOLDER,
38 PROP_MESSAGE,
39 PROP_MESSAGE_UID
40 };
41
42 G_DEFINE_TYPE (EMailPartList, e_mail_part_list, G_TYPE_OBJECT)
43
44 static CamelObjectBag *registry = NULL;
45 G_LOCK_DEFINE_STATIC (registry);
46
47 static void
mail_part_list_set_folder(EMailPartList * part_list,CamelFolder * folder)48 mail_part_list_set_folder (EMailPartList *part_list,
49 CamelFolder *folder)
50 {
51 g_return_if_fail (part_list->priv->folder == NULL);
52
53 /* The folder property is optional. */
54 if (folder != NULL) {
55 g_return_if_fail (CAMEL_IS_FOLDER (folder));
56 part_list->priv->folder = g_object_ref (folder);
57 }
58 }
59
60 static void
mail_part_list_set_message(EMailPartList * part_list,CamelMimeMessage * message)61 mail_part_list_set_message (EMailPartList *part_list,
62 CamelMimeMessage *message)
63 {
64 g_return_if_fail (part_list->priv->message == NULL);
65
66 /* The message property is optional. */
67 if (message != NULL) {
68 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
69 part_list->priv->message = g_object_ref (message);
70 }
71 }
72
73 static void
mail_part_list_set_message_uid(EMailPartList * part_list,const gchar * message_uid)74 mail_part_list_set_message_uid (EMailPartList *part_list,
75 const gchar *message_uid)
76 {
77 g_return_if_fail (part_list->priv->message_uid == NULL);
78
79 /* The message_uid property is optional. */
80 part_list->priv->message_uid = g_strdup (message_uid);
81 }
82
83 static void
mail_part_list_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)84 mail_part_list_set_property (GObject *object,
85 guint property_id,
86 const GValue *value,
87 GParamSpec *pspec)
88 {
89 switch (property_id) {
90 case PROP_FOLDER:
91 mail_part_list_set_folder (
92 E_MAIL_PART_LIST (object),
93 g_value_get_object (value));
94 return;
95
96 case PROP_MESSAGE:
97 mail_part_list_set_message (
98 E_MAIL_PART_LIST (object),
99 g_value_get_object (value));
100 return;
101
102 case PROP_MESSAGE_UID:
103 mail_part_list_set_message_uid (
104 E_MAIL_PART_LIST (object),
105 g_value_get_string (value));
106 return;
107 }
108
109 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
110 }
111
112 static void
mail_part_list_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)113 mail_part_list_get_property (GObject *object,
114 guint property_id,
115 GValue *value,
116 GParamSpec *pspec)
117 {
118 switch (property_id) {
119 case PROP_FOLDER:
120 g_value_set_object (
121 value,
122 e_mail_part_list_get_folder (
123 E_MAIL_PART_LIST (object)));
124 return;
125
126 case PROP_MESSAGE:
127 g_value_set_object (
128 value,
129 e_mail_part_list_get_message (
130 E_MAIL_PART_LIST (object)));
131 return;
132
133 case PROP_MESSAGE_UID:
134 g_value_set_string (
135 value,
136 e_mail_part_list_get_message_uid (
137 E_MAIL_PART_LIST (object)));
138 return;
139 }
140
141 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
142 }
143
144 static void
mail_part_list_dispose(GObject * object)145 mail_part_list_dispose (GObject *object)
146 {
147 EMailPartListPrivate *priv;
148
149 priv = E_MAIL_PART_LIST_GET_PRIVATE (object);
150 g_clear_object (&priv->folder);
151 g_clear_object (&priv->message);
152
153 g_mutex_lock (&priv->queue_lock);
154 while (!g_queue_is_empty (&priv->queue))
155 g_object_unref (g_queue_pop_head (&priv->queue));
156 g_mutex_unlock (&priv->queue_lock);
157
158 /* Chain up to parent's dispose() method. */
159 G_OBJECT_CLASS (e_mail_part_list_parent_class)->dispose (object);
160 }
161
162 static void
mail_part_list_finalize(GObject * object)163 mail_part_list_finalize (GObject *object)
164 {
165 EMailPartListPrivate *priv;
166
167 priv = E_MAIL_PART_LIST_GET_PRIVATE (object);
168
169 g_free (priv->message_uid);
170
171 g_warn_if_fail (g_queue_is_empty (&priv->queue));
172 g_mutex_clear (&priv->queue_lock);
173
174 /* Chain up to parent's finalize() method. */
175 G_OBJECT_CLASS (e_mail_part_list_parent_class)->finalize (object);
176 }
177
178 static void
e_mail_part_list_class_init(EMailPartListClass * class)179 e_mail_part_list_class_init (EMailPartListClass *class)
180 {
181 GObjectClass *object_class;
182
183 g_type_class_add_private (class, sizeof (EMailPartListPrivate));
184
185 object_class = G_OBJECT_CLASS (class);
186 object_class->set_property = mail_part_list_set_property;
187 object_class->get_property = mail_part_list_get_property;
188 object_class->dispose = mail_part_list_dispose;
189 object_class->finalize = mail_part_list_finalize;
190
191 g_object_class_install_property (
192 object_class,
193 PROP_FOLDER,
194 g_param_spec_object (
195 "folder",
196 "Folder",
197 NULL,
198 CAMEL_TYPE_FOLDER,
199 G_PARAM_READWRITE |
200 G_PARAM_CONSTRUCT_ONLY |
201 G_PARAM_STATIC_STRINGS));
202
203 g_object_class_install_property (
204 object_class,
205 PROP_MESSAGE,
206 g_param_spec_object (
207 "message",
208 "Message",
209 NULL,
210 CAMEL_TYPE_MIME_MESSAGE,
211 G_PARAM_READWRITE |
212 G_PARAM_CONSTRUCT_ONLY |
213 G_PARAM_STATIC_STRINGS));
214
215 g_object_class_install_property (
216 object_class,
217 PROP_MESSAGE_UID,
218 g_param_spec_string (
219 "message-uid",
220 "Message UID",
221 NULL,
222 NULL,
223 G_PARAM_READWRITE |
224 G_PARAM_CONSTRUCT_ONLY |
225 G_PARAM_STATIC_STRINGS));
226 }
227
228 static void
e_mail_part_list_init(EMailPartList * part_list)229 e_mail_part_list_init (EMailPartList *part_list)
230 {
231 part_list->priv = E_MAIL_PART_LIST_GET_PRIVATE (part_list);
232
233 g_mutex_init (&part_list->priv->queue_lock);
234 }
235
236 EMailPartList *
e_mail_part_list_new(CamelMimeMessage * message,const gchar * message_uid,CamelFolder * folder)237 e_mail_part_list_new (CamelMimeMessage *message,
238 const gchar *message_uid,
239 CamelFolder *folder)
240 {
241 if (message != NULL)
242 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
243
244 if (folder != NULL)
245 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
246
247 return g_object_new (
248 E_TYPE_MAIL_PART_LIST,
249 "message", message,
250 "message-uid", message_uid,
251 "folder", folder, NULL);
252 }
253
254 CamelFolder *
e_mail_part_list_get_folder(EMailPartList * part_list)255 e_mail_part_list_get_folder (EMailPartList *part_list)
256 {
257 g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), NULL);
258
259 return part_list->priv->folder;
260 }
261
262 CamelMimeMessage *
e_mail_part_list_get_message(EMailPartList * part_list)263 e_mail_part_list_get_message (EMailPartList *part_list)
264 {
265 g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), NULL);
266
267 return part_list->priv->message;
268 }
269
270 const gchar *
e_mail_part_list_get_message_uid(EMailPartList * part_list)271 e_mail_part_list_get_message_uid (EMailPartList *part_list)
272 {
273 g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), NULL);
274
275 return part_list->priv->message_uid;
276 }
277
278 void
e_mail_part_list_add_part(EMailPartList * part_list,EMailPart * part)279 e_mail_part_list_add_part (EMailPartList *part_list,
280 EMailPart *part)
281 {
282 g_return_if_fail (E_IS_MAIL_PART_LIST (part_list));
283 g_return_if_fail (E_IS_MAIL_PART (part));
284
285 g_mutex_lock (&part_list->priv->queue_lock);
286
287 g_queue_push_tail (
288 &part_list->priv->queue,
289 g_object_ref (part));
290
291 g_mutex_unlock (&part_list->priv->queue_lock);
292
293 e_mail_part_set_part_list (part, part_list);
294 }
295
296 EMailPart *
e_mail_part_list_ref_part(EMailPartList * part_list,const gchar * part_id)297 e_mail_part_list_ref_part (EMailPartList *part_list,
298 const gchar *part_id)
299 {
300 EMailPart *match = NULL;
301 GList *head, *link;
302 gboolean by_cid;
303
304 g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), NULL);
305 g_return_val_if_fail (part_id != NULL, NULL);
306
307 by_cid = (g_ascii_strncasecmp (part_id, "cid:", 4) == 0);
308
309 g_mutex_lock (&part_list->priv->queue_lock);
310
311 head = g_queue_peek_head_link (&part_list->priv->queue);
312
313 for (link = head; link != NULL; link = g_list_next (link)) {
314 EMailPart *candidate = E_MAIL_PART (link->data);
315 const gchar *candidate_id;
316
317 if (by_cid)
318 candidate_id = e_mail_part_get_cid (candidate);
319 else
320 candidate_id = e_mail_part_get_id (candidate);
321
322 if (g_strcmp0 (candidate_id, part_id) == 0) {
323 match = g_object_ref (candidate);
324 break;
325 }
326 }
327
328 g_mutex_unlock (&part_list->priv->queue_lock);
329
330 return match;
331 }
332
333 /**
334 * e_mail_part_list_queue_parts:
335 * @part_list: an #EMailPartList
336 * @part_id: the #EMailPart ID to begin queueing from, or %NULL
337 * @result_queue: a #GQueue in which to deposit #EMailPart instances
338 *
339 * Populates @result_queue with a sequence of #EMailPart instances beginning
340 * with the part having @part_id. If @part_id is %NULL, the entire sequence
341 * of #EMailPart instances is queued.
342 *
343 * Each #EMailPart is referenced for thread-safety and should be unreferenced
344 * with g_object_unref().
345 *
346 * Returns: the number of parts added to @result_queue
347 **/
348 guint
e_mail_part_list_queue_parts(EMailPartList * part_list,const gchar * part_id,GQueue * result_queue)349 e_mail_part_list_queue_parts (EMailPartList *part_list,
350 const gchar *part_id,
351 GQueue *result_queue)
352 {
353 GList *link;
354 guint parts_queued = 0;
355
356 g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), FALSE);
357 g_return_val_if_fail (result_queue != NULL, FALSE);
358
359 g_mutex_lock (&part_list->priv->queue_lock);
360
361 link = g_queue_peek_head_link (&part_list->priv->queue);
362
363 if (part_id != NULL) {
364 for (; link != NULL; link = g_list_next (link)) {
365 EMailPart *candidate = E_MAIL_PART (link->data);
366 const gchar *candidate_id;
367
368 candidate_id = e_mail_part_get_id (candidate);
369
370 if (g_strcmp0 (candidate_id, part_id) == 0)
371 break;
372 }
373 }
374
375 /* We skip the loop entirely if link is NULL. */
376 for (; link != NULL; link = g_list_next (link)) {
377 EMailPart *part = link->data;
378
379 if (part == NULL)
380 continue;
381
382 g_queue_push_tail (result_queue, g_object_ref (part));
383 parts_queued++;
384 }
385
386 g_mutex_unlock (&part_list->priv->queue_lock);
387
388 return parts_queued;
389 }
390
391 /**
392 * e_mail_part_list_is_empty:
393 * @part_list: an #EMailPartList
394 *
395 * Returns: whether the part list is empty (it doesn't contain any #EMailpart).
396 **/
397 gboolean
e_mail_part_list_is_empty(EMailPartList * part_list)398 e_mail_part_list_is_empty (EMailPartList *part_list)
399 {
400 gboolean is_empty;
401
402 g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), TRUE);
403
404 g_mutex_lock (&part_list->priv->queue_lock);
405 is_empty = g_queue_is_empty (&part_list->priv->queue);
406 g_mutex_unlock (&part_list->priv->queue_lock);
407
408 return is_empty;
409 }
410
411 void
e_mail_part_list_sum_validity(EMailPartList * part_list,EMailPartValidityFlags * out_validity_pgp_sum,EMailPartValidityFlags * out_validity_smime_sum)412 e_mail_part_list_sum_validity (EMailPartList *part_list,
413 EMailPartValidityFlags *out_validity_pgp_sum,
414 EMailPartValidityFlags *out_validity_smime_sum)
415 {
416 EMailPartValidityFlags validity_pgp_sum = 0;
417 EMailPartValidityFlags validity_smime_sum = 0;
418 GQueue queue = G_QUEUE_INIT;
419
420 g_return_if_fail (E_IS_MAIL_PART_LIST (part_list));
421
422 e_mail_part_list_queue_parts (part_list, NULL, &queue);
423
424 while (!g_queue_is_empty (&queue)) {
425 EMailPart *part = g_queue_pop_head (&queue);
426 GList *head, *link;
427
428 head = g_queue_peek_head_link (&part->validities);
429
430 for (link = head; link != NULL; link = g_list_next (link)) {
431 EMailPartValidityPair *vpair = link->data;
432
433 if (vpair == NULL)
434 continue;
435
436 if ((vpair->validity_type & E_MAIL_PART_VALIDITY_PGP) != 0)
437 validity_pgp_sum |= vpair->validity_type;
438 if ((vpair->validity_type & E_MAIL_PART_VALIDITY_SMIME) != 0)
439 validity_smime_sum |= vpair->validity_type;
440 }
441
442 g_object_unref (part);
443 }
444
445 if (out_validity_pgp_sum)
446 *out_validity_pgp_sum = validity_pgp_sum;
447
448 if (out_validity_smime_sum)
449 *out_validity_smime_sum = validity_smime_sum;
450 }
451
452 /**
453 * e_mail_part_list_get_registry:
454 *
455 * Returns a #CamelObjectBag where parsed #EMailPartLists can be stored.
456 */
457 CamelObjectBag *
e_mail_part_list_get_registry(void)458 e_mail_part_list_get_registry (void)
459 {
460 G_LOCK (registry);
461 if (registry == NULL) {
462 registry = camel_object_bag_new (
463 g_str_hash, g_str_equal,
464 (CamelCopyFunc) g_strdup, g_free);
465 }
466 G_UNLOCK (registry);
467
468 return registry;
469 }
470