NEWS update for 3.27.91
[evolution.git] / src / em-format / e-mail-part.c
blob48d8be9c0f98afa76952e90cc60d55ad60ae1b34
1 /*
2 * e-mail-part.c
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.
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.
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/>.
18 /**
19 * EMailPart:
21 * The #EMailPart is a wrapper around #CamelMimePart which holds additional
22 * information about the mime part, like it's ID, encryption type etc.
24 * Each #EMailPart must have a unique ID. The ID is a dot-separated
25 * hierarchical description of the location of the part within the email
26 * message.
29 #include "evolution-config.h"
31 #include "e-mail-part.h"
33 #include <string.h>
35 #include "e-mail-part-attachment.h"
36 #include "e-mail-part-list.h"
38 #define E_MAIL_PART_GET_PRIVATE(obj) \
39 (G_TYPE_INSTANCE_GET_PRIVATE \
40 ((obj), E_TYPE_MAIL_PART, EMailPartPrivate))
42 struct _EMailPartPrivate {
43 GWeakRef part_list;
44 CamelMimePart *mime_part;
46 gchar *id;
47 gchar *cid;
48 gchar *mime_type;
50 gboolean is_attachment;
51 gboolean converted_to_utf8;
54 enum {
55 PROP_0,
56 PROP_CID,
57 PROP_CONVERTED_TO_UTF8,
58 PROP_ID,
59 PROP_IS_ATTACHMENT,
60 PROP_MIME_PART,
61 PROP_MIME_TYPE,
62 PROP_PART_LIST
65 G_DEFINE_TYPE_WITH_CODE (
66 EMailPart,
67 e_mail_part,
68 G_TYPE_OBJECT,
69 G_IMPLEMENT_INTERFACE (
70 E_TYPE_EXTENSIBLE, NULL))
72 static void
73 mail_part_validity_pair_free (gpointer ptr)
75 EMailPartValidityPair *pair = ptr;
77 if (!pair)
78 return;
80 camel_cipher_validity_free (pair->validity);
81 g_free (pair);
84 static void
85 mail_part_set_id (EMailPart *part,
86 const gchar *id)
88 g_return_if_fail (part->priv->id == NULL);
90 part->priv->id = g_strdup (id);
93 static void
94 mail_part_set_mime_part (EMailPart *part,
95 CamelMimePart *mime_part)
97 g_return_if_fail (part->priv->mime_part == NULL);
99 /* The CamelMimePart is optional. */
100 if (mime_part != NULL)
101 part->priv->mime_part = g_object_ref (mime_part);
104 static void
105 mail_part_set_property (GObject *object,
106 guint property_id,
107 const GValue *value,
108 GParamSpec *pspec)
110 switch (property_id) {
111 case PROP_CID:
112 e_mail_part_set_cid (
113 E_MAIL_PART (object),
114 g_value_get_string (value));
115 return;
117 case PROP_CONVERTED_TO_UTF8:
118 e_mail_part_set_converted_to_utf8 (
119 E_MAIL_PART (object),
120 g_value_get_boolean (value));
121 return;
123 case PROP_ID:
124 mail_part_set_id (
125 E_MAIL_PART (object),
126 g_value_get_string (value));
127 return;
129 case PROP_IS_ATTACHMENT:
130 e_mail_part_set_is_attachment (
131 E_MAIL_PART (object),
132 g_value_get_boolean (value));
133 return;
135 case PROP_MIME_PART:
136 mail_part_set_mime_part (
137 E_MAIL_PART (object),
138 g_value_get_object (value));
139 return;
141 case PROP_MIME_TYPE:
142 e_mail_part_set_mime_type (
143 E_MAIL_PART (object),
144 g_value_get_string (value));
145 return;
147 case PROP_PART_LIST:
148 e_mail_part_set_part_list (
149 E_MAIL_PART (object),
150 g_value_get_object (value));
151 return;
154 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
157 static void
158 mail_part_get_property (GObject *object,
159 guint property_id,
160 GValue *value,
161 GParamSpec *pspec)
163 switch (property_id) {
164 case PROP_CID:
165 g_value_set_string (
166 value,
167 e_mail_part_get_cid (
168 E_MAIL_PART (object)));
169 return;
171 case PROP_CONVERTED_TO_UTF8:
172 g_value_set_boolean (
173 value,
174 e_mail_part_get_converted_to_utf8 (
175 E_MAIL_PART (object)));
176 return;
178 case PROP_ID:
179 g_value_set_string (
180 value,
181 e_mail_part_get_id (
182 E_MAIL_PART (object)));
183 return;
185 case PROP_IS_ATTACHMENT:
186 g_value_set_boolean (
187 value,
188 e_mail_part_get_is_attachment (
189 E_MAIL_PART (object)));
190 return;
192 case PROP_MIME_PART:
193 g_value_take_object (
194 value,
195 e_mail_part_ref_mime_part (
196 E_MAIL_PART (object)));
197 return;
199 case PROP_MIME_TYPE:
200 g_value_set_string (
201 value,
202 e_mail_part_get_mime_type (
203 E_MAIL_PART (object)));
204 return;
206 case PROP_PART_LIST:
207 g_value_take_object (
208 value,
209 e_mail_part_ref_part_list (
210 E_MAIL_PART (object)));
211 return;
214 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
217 static void
218 mail_part_dispose (GObject *object)
220 EMailPartPrivate *priv;
222 priv = E_MAIL_PART_GET_PRIVATE (object);
224 g_weak_ref_set (&priv->part_list, NULL);
226 g_clear_object (&priv->mime_part);
228 /* Chain up to parent's dispose() method. */
229 G_OBJECT_CLASS (e_mail_part_parent_class)->dispose (object);
232 static void
233 mail_part_finalize (GObject *object)
235 EMailPart *part = E_MAIL_PART (object);
236 EMailPartValidityPair *pair;
238 g_free (part->priv->id);
239 g_free (part->priv->cid);
240 g_free (part->priv->mime_type);
242 while ((pair = g_queue_pop_head (&part->validities)) != NULL)
243 mail_part_validity_pair_free (pair);
245 /* Chain up to parent's finalize() method. */
246 G_OBJECT_CLASS (e_mail_part_parent_class)->finalize (object);
249 static void
250 mail_part_constructed (GObject *object)
252 /* Chain up to parent's constructed() method. */
253 G_OBJECT_CLASS (e_mail_part_parent_class)->constructed (object);
255 e_extensible_load_extensions (E_EXTENSIBLE (object));
258 static void
259 e_mail_part_class_init (EMailPartClass *class)
261 GObjectClass *object_class;
263 g_type_class_add_private (class, sizeof (EMailPartPrivate));
265 object_class = G_OBJECT_CLASS (class);
266 object_class->set_property = mail_part_set_property;
267 object_class->get_property = mail_part_get_property;
268 object_class->dispose = mail_part_dispose;
269 object_class->finalize = mail_part_finalize;
270 object_class->constructed = mail_part_constructed;
272 g_object_class_install_property (
273 object_class,
274 PROP_CID,
275 g_param_spec_string (
276 "cid",
277 "Content ID",
278 "The MIME Content-ID",
279 NULL,
280 G_PARAM_READWRITE |
281 G_PARAM_STATIC_STRINGS));
283 g_object_class_install_property (
284 object_class,
285 PROP_CONVERTED_TO_UTF8,
286 g_param_spec_boolean (
287 "converted-to-utf8",
288 "Converted To UTF8",
289 "Whether the part content was already converted to UTF-8",
290 FALSE,
291 G_PARAM_READWRITE |
292 G_PARAM_STATIC_STRINGS));
294 g_object_class_install_property (
295 object_class,
296 PROP_ID,
297 g_param_spec_string (
298 "id",
299 "Part ID",
300 "The part ID",
301 NULL,
302 G_PARAM_READWRITE |
303 G_PARAM_CONSTRUCT_ONLY |
304 G_PARAM_STATIC_STRINGS));
306 g_object_class_install_property (
307 object_class,
308 PROP_IS_ATTACHMENT,
309 g_param_spec_boolean (
310 "is-attachment",
311 "Is Attachment",
312 "Format the part as an attachment",
313 FALSE,
314 G_PARAM_READWRITE |
315 G_PARAM_CONSTRUCT |
316 G_PARAM_STATIC_STRINGS));
318 g_object_class_install_property (
319 object_class,
320 PROP_MIME_PART,
321 g_param_spec_object (
322 "mime-part",
323 "MIME Part",
324 "The MIME part",
325 CAMEL_TYPE_MIME_PART,
326 G_PARAM_READWRITE |
327 G_PARAM_CONSTRUCT_ONLY |
328 G_PARAM_STATIC_STRINGS));
330 g_object_class_install_property (
331 object_class,
332 PROP_MIME_TYPE,
333 g_param_spec_string (
334 "mime-type",
335 "MIME Type",
336 "The MIME type",
337 NULL,
338 G_PARAM_READWRITE |
339 G_PARAM_STATIC_STRINGS));
341 g_object_class_install_property (
342 object_class,
343 PROP_PART_LIST,
344 g_param_spec_object (
345 "part-list",
346 "Part List",
347 "The part list that owns the part",
348 E_TYPE_MAIL_PART_LIST,
349 G_PARAM_READWRITE |
350 G_PARAM_STATIC_STRINGS));
353 static void
354 e_mail_part_init (EMailPart *part)
356 part->priv = E_MAIL_PART_GET_PRIVATE (part);
360 * e_mail_part_new:
361 * @mime_part: (allow-none) a #CamelMimePart or %NULL
362 * @id: part ID
364 * Creates a new #EMailPart for the given @mime_part.
366 * Return value: a new #EMailPart
368 EMailPart *
369 e_mail_part_new (CamelMimePart *mime_part,
370 const gchar *id)
372 g_return_val_if_fail (id != NULL, NULL);
374 return g_object_new (
375 E_TYPE_MAIL_PART,
376 "id", id, "mime-part", mime_part, NULL);
379 const gchar *
380 e_mail_part_get_id (EMailPart *part)
382 g_return_val_if_fail (E_IS_MAIL_PART (part), NULL);
384 return part->priv->id;
387 const gchar *
388 e_mail_part_get_cid (EMailPart *part)
390 g_return_val_if_fail (E_IS_MAIL_PART (part), NULL);
392 return part->priv->cid;
395 void
396 e_mail_part_set_cid (EMailPart *part,
397 const gchar *cid)
399 g_return_if_fail (E_IS_MAIL_PART (part));
401 g_free (part->priv->cid);
402 part->priv->cid = g_strdup (cid);
404 g_object_notify (G_OBJECT (part), "cid");
407 gboolean
408 e_mail_part_id_has_prefix (EMailPart *part,
409 const gchar *prefix)
411 g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE);
412 g_return_val_if_fail (prefix != NULL, FALSE);
414 return g_str_has_prefix (part->priv->id, prefix);
417 gboolean
418 e_mail_part_id_has_suffix (EMailPart *part,
419 const gchar *suffix)
421 g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE);
422 g_return_val_if_fail (suffix != NULL, FALSE);
424 return g_str_has_suffix (part->priv->id, suffix);
427 gboolean
428 e_mail_part_id_has_substr (EMailPart *part,
429 const gchar *substr)
431 g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE);
432 g_return_val_if_fail (substr != NULL, FALSE);
434 return (strstr (part->priv->id, substr) != NULL);
437 CamelMimePart *
438 e_mail_part_ref_mime_part (EMailPart *part)
440 CamelMimePart *mime_part = NULL;
442 g_return_val_if_fail (E_IS_MAIL_PART (part), NULL);
444 if (part->priv->mime_part != NULL)
445 mime_part = g_object_ref (part->priv->mime_part);
447 return mime_part;
450 const gchar *
451 e_mail_part_get_mime_type (EMailPart *part)
453 g_return_val_if_fail (E_IS_MAIL_PART (part), NULL);
455 return part->priv->mime_type;
458 void
459 e_mail_part_set_mime_type (EMailPart *part,
460 const gchar *mime_type)
462 g_return_if_fail (E_IS_MAIL_PART (part));
464 if (g_strcmp0 (mime_type, part->priv->mime_type) == 0)
465 return;
467 g_free (part->priv->mime_type);
468 part->priv->mime_type = g_strdup (mime_type);
470 g_object_notify (G_OBJECT (part), "mime-type");
473 gboolean
474 e_mail_part_get_converted_to_utf8 (EMailPart *part)
476 g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE);
478 return part->priv->converted_to_utf8;
481 void
482 e_mail_part_set_converted_to_utf8 (EMailPart *part,
483 gboolean converted_to_utf8)
485 g_return_if_fail (E_IS_MAIL_PART (part));
487 if (converted_to_utf8 == part->priv->converted_to_utf8)
488 return;
490 part->priv->converted_to_utf8 = converted_to_utf8;
492 g_object_notify (G_OBJECT (part), "converted-to-utf8");
495 gboolean
496 e_mail_part_should_show_inline (EMailPart *part)
498 CamelMimePart *mime_part;
499 const CamelContentDisposition *disposition;
500 gboolean res = FALSE;
502 g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE);
504 /* Automatically expand attachments that have inline
505 * disposition or the EMailParts have specific
506 * force_inline flag set. */
508 if (part->force_collapse)
509 return FALSE;
511 if (part->force_inline)
512 return TRUE;
514 if (E_IS_MAIL_PART_ATTACHMENT (part)) {
515 EMailPartAttachment *empa = E_MAIL_PART_ATTACHMENT (part);
517 if (g_strcmp0 (empa->snoop_mime_type, "message/rfc822") == 0)
518 return TRUE;
521 mime_part = e_mail_part_ref_mime_part (part);
522 if (!mime_part)
523 return FALSE;
525 disposition = camel_mime_part_get_content_disposition (mime_part);
526 if (disposition && disposition->disposition &&
527 g_ascii_strncasecmp (disposition->disposition, "inline", 6) == 0) {
528 GSettings *settings;
530 settings = e_util_ref_settings ("org.gnome.evolution.mail");
531 res = g_settings_get_boolean (settings, "display-content-disposition-inline");
532 g_clear_object (&settings);
535 g_object_unref (mime_part);
537 return res;
540 EMailPartList *
541 e_mail_part_ref_part_list (EMailPart *part)
543 g_return_val_if_fail (E_IS_MAIL_PART (part), NULL);
545 return g_weak_ref_get (&part->priv->part_list);
548 void
549 e_mail_part_set_part_list (EMailPart *part,
550 EMailPartList *part_list)
552 g_return_if_fail (E_IS_MAIL_PART (part));
554 if (part_list != NULL)
555 g_return_if_fail (E_IS_MAIL_PART_LIST (part_list));
557 g_weak_ref_set (&part->priv->part_list, part_list);
559 g_object_notify (G_OBJECT (part), "part-list");
562 gboolean
563 e_mail_part_get_is_attachment (EMailPart *part)
565 g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE);
567 return part->priv->is_attachment;
570 void
571 e_mail_part_set_is_attachment (EMailPart *part,
572 gboolean is_attachment)
574 g_return_if_fail (E_IS_MAIL_PART (part));
576 if (is_attachment == part->priv->is_attachment)
577 return;
579 part->priv->is_attachment = is_attachment;
581 g_object_notify (G_OBJECT (part), "is-attachment");
584 void
585 e_mail_part_bind_dom_element (EMailPart *part,
586 EWebView *web_view,
587 guint64 page_id,
588 const gchar *element_id)
590 EMailPartClass *class;
592 g_return_if_fail (E_IS_MAIL_PART (part));
593 g_return_if_fail (E_IS_WEB_VIEW (web_view));
594 g_return_if_fail (page_id != 0);
595 g_return_if_fail (element_id && *element_id);
597 class = E_MAIL_PART_GET_CLASS (part);
599 if (class->bind_dom_element != NULL)
600 class->bind_dom_element (part, web_view, page_id, element_id);
603 void
604 e_mail_part_web_view_loaded (EMailPart *part,
605 EWebView *web_view)
607 EMailPartClass *klass;
609 g_return_if_fail (E_IS_MAIL_PART (part));
610 g_return_if_fail (E_IS_WEB_VIEW (web_view));
612 klass = E_MAIL_PART_GET_CLASS (part);
614 if (klass->web_view_loaded)
615 klass->web_view_loaded (part, web_view);
618 static EMailPartValidityPair *
619 mail_part_find_validity_pair (EMailPart *part,
620 EMailPartValidityFlags validity_type)
622 GList *head, *link;
624 head = g_queue_peek_head_link (&part->validities);
626 for (link = head; link != NULL; link = g_list_next (link)) {
627 EMailPartValidityPair *pair = link->data;
629 if (pair == NULL)
630 continue;
632 if ((pair->validity_type & validity_type) == validity_type)
633 return pair;
636 return NULL;
640 * e_mail_part_update_validity:
641 * @part: An #EMailPart
642 * @validity: a #CamelCipherValidity
643 * @validity_type: E_MAIL_PART_VALIDITY_* flags
645 * Updates validity of the @part. When the part already has some validity
646 * set, the new @validity and @validity_type are just appended, preserving
647 * the original validity. Validities of the same type (PGP or S/MIME) are
648 * merged together.
650 void
651 e_mail_part_update_validity (EMailPart *part,
652 CamelCipherValidity *validity,
653 EMailPartValidityFlags validity_type)
655 EMailPartValidityPair *pair;
656 EMailPartValidityFlags mask;
658 g_return_if_fail (E_IS_MAIL_PART (part));
659 g_return_if_fail (validity != NULL);
661 mask = E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_SMIME;
663 pair = mail_part_find_validity_pair (part, validity_type & mask);
664 if (pair != NULL) {
665 pair->validity_type |= validity_type;
666 camel_cipher_validity_envelope (pair->validity, validity);
667 } else {
668 pair = g_new0 (EMailPartValidityPair, 1);
669 pair->validity_type = validity_type;
670 pair->validity = camel_cipher_validity_clone (validity);
672 g_queue_push_tail (&part->validities, pair);
677 * e_mail_part_get_validity:
678 * @part: An #EMailPart
679 * @validity_type: E_MAIL_PART_VALIDITY_* flags
681 * Returns, validity of @part contains any validity with the same bits
682 * as @validity_type set. It should contain all bits of it.
684 * Returns: a #CamelCipherValidity of the given type, %NULL if not found
686 * Since: 3.8
688 CamelCipherValidity *
689 e_mail_part_get_validity (EMailPart *part,
690 EMailPartValidityFlags validity_type)
692 EMailPartValidityPair *pair;
694 g_return_val_if_fail (E_IS_MAIL_PART (part), NULL);
696 pair = mail_part_find_validity_pair (part, validity_type);
698 return (pair != NULL) ? pair->validity : NULL;
701 gboolean
702 e_mail_part_has_validity (EMailPart *part)
704 g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE);
706 return !g_queue_is_empty (&part->validities);
709 EMailPartValidityFlags
710 e_mail_part_get_validity_flags (EMailPart *part)
712 EMailPartValidityFlags flags = 0;
713 GList *head, *link;
715 g_return_val_if_fail (E_IS_MAIL_PART (part), 0);
717 head = g_queue_peek_head_link (&part->validities);
719 for (link = head; link != NULL; link = g_list_next (link)) {
720 EMailPartValidityPair *pair = link->data;
722 if (pair != NULL)
723 flags |= pair->validity_type;
726 return flags;
729 static gboolean
730 from_matches_signers_alt_emails (CamelInternetAddress *from_address,
731 CamelCipherCertInfo *cinfo)
733 GSList *props_link;
734 gboolean matches = FALSE;
736 for (props_link = cinfo->properties; props_link && !matches; props_link = g_slist_next (props_link)) {
737 const CamelCipherCertInfoProperty *prop = props_link->data;
739 if (prop && g_strcmp0 (prop->name, CAMEL_CIPHER_CERT_INFO_PROPERTY_SIGNERS_ALT_EMAILS) == 0 && prop->value) {
740 CamelInternetAddress *address;
741 gint count, ii;
743 address = camel_internet_address_new ();
744 count = camel_address_unformat (CAMEL_ADDRESS (address), prop->value);
745 for (ii = 0; ii < count && !matches; ii++) {
746 const gchar *email = NULL;
748 if (camel_internet_address_get (address, ii, NULL, &email) && email && *email) {
749 matches = camel_internet_address_find_address (from_address, email, NULL) >= 0;
752 g_object_unref (address);
753 break;
757 return matches;
760 void
761 e_mail_part_verify_validity_sender (EMailPart *part,
762 CamelInternetAddress *from_address)
764 GList *link;
766 g_return_if_fail (E_IS_MAIL_PART (part));
768 if (!from_address)
769 return;
771 for (link = g_queue_peek_head_link (&part->validities); link; link = g_list_next (link)) {
772 EMailPartValidityPair *pair = link->data;
774 if (pair && pair->validity && !(pair->validity_type & E_MAIL_PART_VALIDITY_VERIFIED)) {
775 pair->validity_type |= E_MAIL_PART_VALIDITY_VERIFIED;
777 if (pair->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) {
778 GList *link2;
779 gboolean from_matches_signer = FALSE;
781 for (link2 = g_queue_peek_head_link (&pair->validity->sign.signers); link2 && !from_matches_signer; link2 = g_list_next (link2)) {
782 CamelCipherCertInfo *cinfo = link2->data;
784 if (cinfo->email && *cinfo->email) {
785 from_matches_signer = from_matches_signer ||
786 (from_address && camel_internet_address_find_address (from_address, cinfo->email, NULL) >= 0) ||
787 (from_address && from_matches_signers_alt_emails (from_address, cinfo));
791 if (!from_matches_signer)
792 pair->validity_type |= E_MAIL_PART_VALIDITY_SENDER_SIGNER_MISMATCH;