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