1 /*
2  * e-mail-formatter-utils.h
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 "evolution-config.h"
19 
20 #include "e-mail-formatter-utils.h"
21 #include "e-mail-part-headers.h"
22 
23 #include <string.h>
24 #include <glib/gi18n.h>
25 
26 #include <camel/camel.h>
27 #include <libedataserver/libedataserver.h>
28 
29 #include <e-util/e-util.h>
30 #include <libemail-engine/libemail-engine.h>
31 
32 static const gchar *addrspec_hdrs[] = {
33 	"Sender", "From", "Reply-To", "To", "Cc", "Bcc",
34 	"Resent-Sender", "Resent-From", "Resent-Reply-To",
35 	"Resent-To", "Resent-Cc", "Resent-Bcc", NULL
36 };
37 
38 void
e_mail_formatter_format_text_header(EMailFormatter * formatter,GString * buffer,const gchar * label,const gchar * value,guint32 flags)39 e_mail_formatter_format_text_header (EMailFormatter *formatter,
40                                      GString *buffer,
41                                      const gchar *label,
42                                      const gchar *value,
43                                      guint32 flags)
44 {
45 	GtkTextDirection direction;
46 	const gchar *fmt, *html;
47 	const gchar *display;
48 	gchar *mhtml = NULL, *mfmt = NULL;
49 
50 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
51 	g_return_if_fail (buffer != NULL);
52 	g_return_if_fail (label != NULL);
53 
54 	if (value == NULL)
55 		return;
56 
57 	while (*value == ' ')
58 		value++;
59 
60 	if (!(flags & E_MAIL_FORMATTER_HEADER_FLAG_HTML)) {
61 		CamelMimeFilterToHTMLFlags text_format_flags;
62 
63 		text_format_flags =
64 			e_mail_formatter_get_text_format_flags (formatter);
65 		html = mhtml = camel_text_to_html (
66 			value, text_format_flags, 0);
67 	} else {
68 		html = value;
69 	}
70 
71 	direction = gtk_widget_get_default_direction ();
72 
73 	if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOCOLUMNS) {
74 		if ((flags & E_MAIL_FORMATTER_HEADER_FLAG_BOLD) &&
75 		    !(flags & E_MAIL_FORMATTER_HEADER_FLAG_NO_FORMATTING)) {
76 			fmt = "<tr style=\"display: %s\">"
77 				"<td><b>%s:</b> %s</td></tr>";
78 		} else {
79 			fmt = "<tr style=\"display: %s\">"
80 				"<td>%s: %s</td></tr>";
81 		}
82 	} else {
83 		const gchar *decstr;
84 		const gchar *dirstr;
85 		const gchar *extra_style;
86 
87 		if ((flags & E_MAIL_FORMATTER_HEADER_FLAG_NODEC) != 0)
88 			decstr = "";
89 		else
90 			decstr = ":";
91 
92 		if ((flags & E_MAIL_FORMATTER_HEADER_FLAG_NO_FORMATTING) != 0)
93 			extra_style = " style=\"font-weight: normal;\"";
94 		else
95 			extra_style = "";
96 
97 		if (direction == GTK_TEXT_DIR_RTL)
98 			dirstr = "rtl";
99 		else
100 			dirstr = "ltr";
101 
102 		mfmt = g_strdup_printf (
103 			"<tr class=\"header\" style=\"display: %%s;\">"
104 			"<th class=\"header %s\"%s>%%s%s</th>"
105 			"<td class=\"header %s\">%%s</td>"
106 			"</tr>",
107 			dirstr, extra_style, decstr, dirstr);
108 		fmt = mfmt;
109 	}
110 
111 	if (flags & E_MAIL_FORMATTER_HEADER_FLAG_HIDDEN)
112 		display = "none";
113 	else
114 		display = "table-row";
115 
116 	g_string_append_printf (buffer, fmt, display, label, html);
117 
118 	g_free (mhtml);
119 	g_free (mfmt);
120 }
121 
122 gchar *
e_mail_formatter_format_address(EMailFormatter * formatter,GString * out,struct _camel_header_address * a,const gchar * field,gboolean no_links,gboolean elipsize)123 e_mail_formatter_format_address (EMailFormatter *formatter,
124                                  GString *out,
125                                  struct _camel_header_address *a,
126                                  const gchar *field,
127                                  gboolean no_links,
128                                  gboolean elipsize)
129 {
130 	CamelMimeFilterToHTMLFlags flags;
131 	gchar *name, *mailto, *addr, *sanitized_addr;
132 	gint i = 0;
133 	gchar *str = NULL;
134 	gint limit = mail_config_get_address_count ();
135 	gboolean show_mails = mail_config_get_show_mails_in_preview ();
136 
137 	g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
138 	g_return_val_if_fail (out != NULL, NULL);
139 	g_return_val_if_fail (field != NULL, NULL);
140 
141 	flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES;
142 
143 	while (a != NULL) {
144 		if (a->name)
145 			name = camel_text_to_html (a->name, flags, 0);
146 		else
147 			name = NULL;
148 
149 		switch (a->type) {
150 		case CAMEL_HEADER_ADDRESS_NAME:
151 			sanitized_addr = camel_utils_sanitize_ascii_domain_in_address (a->v.addr, TRUE);
152 			if (name != NULL && *name != '\0') {
153 				gchar *real, *mailaddr;
154 
155 				if (show_mails || no_links) {
156 					if (strchr (a->name, ',') || strchr (a->name, ';') || strchr (a->name, '\"') || strchr (a->name, '<') || strchr (a->name, '>'))
157 						g_string_append_printf (out, "&quot;%s&quot;", name);
158 					else
159 						g_string_append (out, name);
160 
161 					g_string_append (out, " &lt;");
162 				}
163 
164 				/* rfc2368 for mailto syntax and url encoding extras */
165 				if ((real = camel_header_encode_phrase ((guchar *) a->name))) {
166 					mailaddr = g_strdup_printf ("%s <%s>", real, sanitized_addr ? sanitized_addr : a->v.addr);
167 					g_free (real);
168 					mailto = camel_url_encode (mailaddr, "?=&()");
169 					g_free (mailaddr);
170 				} else {
171 					mailto = camel_url_encode (sanitized_addr ? sanitized_addr : a->v.addr, "?=&()");
172 				}
173 			} else {
174 				mailto = camel_url_encode (sanitized_addr ? sanitized_addr : a->v.addr, "?=&()");
175 			}
176 			addr = camel_text_to_html (sanitized_addr ? sanitized_addr : a->v.addr, flags, 0);
177 			if (no_links)
178 				g_string_append_printf (out, "%s", addr);
179 			else if (!show_mails && name && *name)
180 				g_string_append_printf (out, "<a href=\"mailto:%s\">%s</a>", mailto, name);
181 			else
182 				g_string_append_printf (out, "<a href=\"mailto:%s\">%s</a>", mailto, addr);
183 			g_free (sanitized_addr);
184 			g_free (mailto);
185 			g_free (addr);
186 
187 			if (name != NULL && *name != '\0' && (show_mails || no_links))
188 				g_string_append (out, "&gt;");
189 			break;
190 		case CAMEL_HEADER_ADDRESS_GROUP:
191 			g_string_append_printf (out, "%s: ", name);
192 			e_mail_formatter_format_address (
193 				formatter, out, a->v.members, field,
194 				no_links, elipsize);
195 			g_string_append_printf (out, ";");
196 			break;
197 		default:
198 			g_warning ("Invalid address type");
199 			break;
200 		}
201 
202 		g_free (name);
203 
204 		i++;
205 		a = a->next;
206 		if (a != NULL)
207 			g_string_append (out, ", ");
208 
209 		if (!elipsize)
210 			continue;
211 
212 		/* Let us add a '...' if we have more addresses */
213 		if (limit > 0 && i == limit && a != NULL) {
214 			if (strcmp (field, _("To")) == 0 ||
215 			    strcmp (field, _("Cc")) == 0 ||
216 			    strcmp (field, _("Bcc")) == 0) {
217 				gint icon_width, icon_height;
218 
219 				if (!gtk_icon_size_lookup (GTK_ICON_SIZE_BUTTON, &icon_width, &icon_height)) {
220 					icon_width = 16;
221 					icon_height = 16;
222 				}
223 
224 				g_string_append (
225 					out,
226 					"<span id=\"__evo-moreaddr\" "
227 					"style=\"display: none;\">");
228 
229 				str = g_strdup_printf (
230 					"<button type=\"button\" id=\"__evo-moreaddr-button\" class=\"header-collapse\" style=\"display: inline-block;\">"
231 					"<img src=\"gtk-stock://pan-end-symbolic?size=%d\" width=\"%dpx\" height=\"%dpx\"/>"
232 					"</button>",
233 					GTK_ICON_SIZE_BUTTON, icon_width, icon_height);
234 			}
235 		}
236 	}
237 
238 	if (elipsize && str) {
239 		if (strcmp (field, _("To")) == 0 ||
240 		    strcmp (field, _("Cc")) == 0 ||
241 		    strcmp (field, _("Bcc")) == 0) {
242 
243 			g_string_append (
244 				out,
245 				"</span>"
246 				"<span class=\"navigable\" "
247 					"id=\"__evo-moreaddr-ellipsis\" "
248 					"style=\"display: inline;\">...</span>");
249 		}
250 	}
251 
252 	return str;
253 }
254 
255 void
e_mail_formatter_canon_header_name(gchar * name)256 e_mail_formatter_canon_header_name (gchar *name)
257 {
258 	gchar *inptr = name;
259 
260 	g_return_if_fail (name != NULL);
261 
262 	/* canonicalise the header name... first letter is
263 	 * capitalised and any letter following a '-' also gets
264 	 * capitalised */
265 
266 	if (*inptr >= 'a' && *inptr <= 'z')
267 		*inptr -= 0x20;
268 
269 	inptr++;
270 
271 	while (*inptr) {
272 		if (inptr[-1] == '-' && *inptr >= 'a' && *inptr <= 'z')
273 			*inptr -= 0x20;
274 		else if (inptr[-1] != '-' && *inptr >= 'A' && *inptr <= 'Z')
275 			*inptr += 0x20;
276 
277 		inptr++;
278 	}
279 }
280 
281 void
e_mail_formatter_format_header(EMailFormatter * formatter,GString * buffer,const gchar * header_name,const gchar * header_value,guint32 flags,const gchar * charset)282 e_mail_formatter_format_header (EMailFormatter *formatter,
283                                 GString *buffer,
284                                 const gchar *header_name,
285                                 const gchar *header_value,
286                                 guint32 flags,
287                                 const gchar *charset)
288 {
289 	gchar *canon_name, *buf, *value = NULL;
290 	const gchar *label, *txt;
291 	gboolean addrspec = FALSE;
292 	gchar *str_field = NULL;
293 	gint i;
294 
295 	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
296 	g_return_if_fail (buffer != NULL);
297 	g_return_if_fail (header_name != NULL);
298 	g_return_if_fail (header_value != NULL);
299 
300 	canon_name = g_alloca (strlen (header_name) + 1);
301 	strcpy (canon_name, header_name);
302 	e_mail_formatter_canon_header_name (canon_name);
303 
304 	for (i = 0; addrspec_hdrs[i]; i++) {
305 		if (g_ascii_strcasecmp (canon_name, addrspec_hdrs[i]) == 0) {
306 			addrspec = TRUE;
307 			break;
308 		}
309 	}
310 
311 	label = _(canon_name);
312 
313 	if (addrspec) {
314 		struct _camel_header_address *addrs;
315 		GString *html;
316 		gchar *img;
317 		gchar *fmt_charset;
318 
319 		fmt_charset = e_mail_formatter_dup_charset (formatter);
320 		if (fmt_charset == NULL)
321 			fmt_charset = e_mail_formatter_dup_default_charset (formatter);
322 
323 		buf = camel_header_unfold (header_value);
324 		addrs = camel_header_address_decode (buf, fmt_charset);
325 		if (addrs == NULL) {
326 			g_free (fmt_charset);
327 			g_free (buf);
328 			return;
329 		}
330 
331 		g_free (fmt_charset);
332 		g_free (buf);
333 
334 		html = g_string_new ("");
335 		img = e_mail_formatter_format_address (
336 			formatter, html, addrs, label,
337 			(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS),
338 			!(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE));
339 
340 		if (img != NULL) {
341 			str_field = g_strdup_printf ("%s: %s", label, img);
342 			label = str_field;
343 			flags |= E_MAIL_FORMATTER_HEADER_FLAG_NODEC;
344 			g_free (img);
345 		}
346 
347 		camel_header_address_list_clear (&addrs);
348 		txt = value = g_string_free (html, FALSE);
349 
350 		flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML;
351 		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;
352 
353 	} else if (g_str_equal (canon_name, "Subject")) {
354 		buf = camel_header_unfold (header_value);
355 		txt = value = camel_header_decode_string (buf, charset);
356 		g_free (buf);
357 
358 		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;
359 
360 	} else if (g_str_equal (canon_name, "X-Evolution-Mailer")) {
361 		/* pseudo-header */
362 		label = _("Mailer");
363 		buf = camel_header_unfold (header_value);
364 		txt = value = camel_header_format_ctext (buf, charset);
365 		g_free (buf);
366 		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;
367 
368 	} else if (g_str_equal (canon_name, "Date") ||
369 		   g_str_equal (canon_name, "Resent-Date")) {
370 		CamelMimeFilterToHTMLFlags text_format_flags;
371 		gint msg_offset, local_tz;
372 		time_t msg_date;
373 		struct tm local;
374 		gchar *html;
375 		gboolean hide_real_date;
376 
377 		hide_real_date = !e_mail_formatter_get_show_real_date (formatter);
378 
379 		txt = header_value;
380 		while (*txt == ' ' || *txt == '\t')
381 			txt++;
382 
383 		text_format_flags =
384 			e_mail_formatter_get_text_format_flags (formatter);
385 
386 		html = camel_text_to_html (txt, text_format_flags, 0);
387 
388 		msg_date = camel_header_decode_date (txt, &msg_offset);
389 		e_localtime_with_offset (msg_date, &local, &local_tz);
390 
391 		/* Convert message offset to minutes (e.g. -0400 --> -240) */
392 		msg_offset = ((msg_offset / 100) * 60) + (msg_offset % 100);
393 		/* Turn into offset from localtime, not UTC */
394 		msg_offset -= local_tz / 60;
395 
396 		/* value will be freed at the end */
397 		if (!hide_real_date && !msg_offset) {
398 			/* No timezone difference; just
399 			 * show the real Date: header. */
400 			txt = value = html;
401 		} else {
402 			gchar *date_str;
403 
404 			date_str = e_datetime_format_format (
405 				"mail", "header",
406 				DTFormatKindDateTime, msg_date);
407 
408 			if (hide_real_date) {
409 				/* Show only the local-formatted date, losing
410 				 * all timezone information like Outlook does.
411 				 * Should we attempt to show it somehow? */
412 				txt = value = date_str;
413 			} else {
414 				txt = value = g_strdup_printf (
415 					"%s (<I>%s</I>)", html, date_str);
416 				g_free (date_str);
417 			}
418 			g_free (html);
419 		}
420 
421 		flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML;
422 		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;
423 
424 	} else if (g_str_equal (canon_name, "Newsgroups")) {
425 		GSList *ng, *scan;
426 		GString *html;
427 
428 		buf = camel_header_unfold (header_value);
429 
430 		if (!(ng = camel_header_newsgroups_decode (buf))) {
431 			g_free (buf);
432 			return;
433 		}
434 
435 		g_free (buf);
436 
437 		html = g_string_new ("");
438 		scan = ng;
439 		while (scan) {
440 			const gchar *newsgroup = scan->data;
441 
442 			if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS)
443 				g_string_append_printf (
444 					html, "%s", newsgroup);
445 			else
446 				g_string_append_printf (
447 					html, "<a href=\"news:%s\">%s</a>",
448 					newsgroup, newsgroup);
449 			scan = g_slist_next (scan);
450 			if (scan)
451 				g_string_append_printf (html, ", ");
452 		}
453 
454 		g_slist_free_full (ng, g_free);
455 
456 		txt = value = g_string_free (html, FALSE);
457 
458 		flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML;
459 		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;
460 
461 	} else if (g_str_equal (canon_name, "Received") ||
462 		   g_str_has_prefix (canon_name, "X-") ||
463 		   g_str_has_prefix (canon_name, "Dkim-") ||
464 		   g_str_has_prefix (canon_name, "Arc-")) {
465 		/* don't unfold Received nor extension headers */
466 		txt = value = camel_header_decode_string (header_value, charset);
467 	} else {
468 		buf = camel_header_unfold (header_value);
469 		txt = value = camel_header_decode_string (buf, charset);
470 		g_free (buf);
471 	}
472 
473 	e_mail_formatter_format_text_header (
474 		formatter, buffer, label, txt, flags);
475 
476 	g_free (value);
477 	g_free (str_field);
478 }
479 
480 GList *
e_mail_formatter_find_rfc822_end_iter(GList * rfc822_start_iter)481 e_mail_formatter_find_rfc822_end_iter (GList *rfc822_start_iter)
482 {
483 	GList *link = rfc822_start_iter;
484 	EMailPart *part;
485 	const gchar *part_id;
486 	gchar *end;
487 
488 	g_return_val_if_fail (rfc822_start_iter != NULL, NULL);
489 
490 	part = E_MAIL_PART (link->data);
491 
492 	part_id = e_mail_part_get_id (part);
493 	g_return_val_if_fail (part_id != NULL, NULL);
494 
495 	end = g_strconcat (part_id, ".end", NULL);
496 
497 	while (link != NULL) {
498 		part = E_MAIL_PART (link->data);
499 
500 		part_id = e_mail_part_get_id (part);
501 		g_return_val_if_fail (part_id != NULL, NULL);
502 
503 		if (g_strcmp0 (part_id, end) == 0)
504 			break;
505 
506 		link = g_list_next (link);
507 	}
508 
509 	g_free (end);
510 
511 	return link;
512 }
513 
514 gchar *
e_mail_formatter_parse_html_mnemonics(const gchar * label,gchar ** out_access_key)515 e_mail_formatter_parse_html_mnemonics (const gchar *label,
516                                        gchar **out_access_key)
517 {
518 	const gchar *pos = NULL;
519 	GString *html_label = NULL;
520 
521 	g_return_val_if_fail (label != NULL, NULL);
522 
523 	if (out_access_key != NULL)
524 		*out_access_key = NULL;
525 
526 	if (!g_utf8_validate (label, -1, NULL)) {
527 		gchar *res = g_strdup (label);
528 
529 		g_return_val_if_fail (g_utf8_validate (label, -1, NULL), res);
530 
531 		return res;
532 	}
533 
534 	pos = strstr (label, "_");
535 	if (pos != NULL) {
536 		gunichar uk;
537 
538 		html_label = g_string_new ("");
539 		g_string_append_len (html_label, label, pos - label);
540 
541 		pos++;
542 		uk = g_utf8_get_char (pos);
543 
544 		pos = g_utf8_next_char (pos);
545 
546 		g_string_append (html_label, "<u>");
547 		g_string_append_unichar (html_label, uk);
548 		g_string_append (html_label, "</u>");
549 		g_string_append (html_label, pos);
550 
551 		if (out_access_key != NULL && uk != 0) {
552 			gchar ukstr[10];
553 			gint len;
554 
555 			len = g_unichar_to_utf8 (g_unichar_toupper (uk), ukstr);
556 			if (len > 0)
557 				*out_access_key = g_strndup (ukstr, len);
558 		}
559 
560 	} else {
561 		html_label = g_string_new (label);
562 	}
563 
564 	return g_string_free (html_label, FALSE);
565 }
566 
567 void
e_mail_formatter_format_security_header(EMailFormatter * formatter,EMailFormatterContext * context,GString * buffer,EMailPart * part,guint32 flags)568 e_mail_formatter_format_security_header (EMailFormatter *formatter,
569                                          EMailFormatterContext *context,
570                                          GString *buffer,
571                                          EMailPart *part,
572                                          guint32 flags)
573 {
574 	struct _validity_flags {
575 		guint32 flags;
576 		const gchar *description_complete;
577 		const gchar *description_partial;
578 	} validity_flags[] = {
579 		{ E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_SIGNED, N_("GPG signed"), N_("partially GPG signed") },
580 		{ E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_ENCRYPTED, N_("GPG encrypted"), N_("partially GPG encrypted") },
581 		{ E_MAIL_PART_VALIDITY_SMIME | E_MAIL_PART_VALIDITY_SIGNED, N_("S/MIME signed"), N_("partially S/MIME signed") },
582 		{ E_MAIL_PART_VALIDITY_SMIME | E_MAIL_PART_VALIDITY_ENCRYPTED, N_("S/MIME encrypted"), N_("partially S/MIME encrypted") }
583 	};
584 	const gchar *part_id;
585 	gchar *part_id_prefix;
586 	GQueue queue = G_QUEUE_INIT;
587 	GList *head, *link;
588 	guint32 check_valid_flags = 0;
589 	gint part_id_prefix_len;
590 	gboolean is_partial = FALSE;
591 	guint ii;
592 
593 	g_return_if_fail (E_IS_MAIL_PART_HEADERS (part));
594 
595 	/* Get prefix of this PURI */
596 	part_id = e_mail_part_get_id (part);
597 	part_id_prefix = g_strndup (part_id, g_strrstr (part_id, ".") - part_id);
598 	part_id_prefix_len = strlen (part_id_prefix);
599 
600 	e_mail_part_list_queue_parts (context->part_list, NULL, &queue);
601 
602 	head = g_queue_peek_head_link (&queue);
603 
604 	/* Ignore the main message, the headers and the end parts */
605 	#define should_skip_part(_id) \
606 		(g_strcmp0 (_id, part_id_prefix) == 0 || \
607 		(_id && g_str_has_suffix (_id, ".rfc822.end")) || \
608 		(_id && strlen (_id) == part_id_prefix_len + 8 /* strlen (".headers") */ && \
609 		g_strcmp0 (_id + part_id_prefix_len, ".headers") == 0))
610 
611 	/* Check parts for this ID. */
612 	for (link = head; link != NULL; link = g_list_next (link)) {
613 		EMailPart *mail_part = link->data;
614 		const gchar *id = e_mail_part_get_id (mail_part);
615 
616 		if (!e_mail_part_id_has_prefix (mail_part, part_id_prefix))
617 			continue;
618 
619 		if (should_skip_part (id))
620 			continue;
621 
622 		if (!e_mail_part_has_validity (mail_part)) {
623 			/* A part without validity, thus it's partially signed/encrypted */
624 			is_partial = TRUE;
625 		} else {
626 			guint32 validies = 0;
627 			for (ii = 0; ii < G_N_ELEMENTS (validity_flags); ii++) {
628 				if (e_mail_part_get_validity (mail_part, validity_flags[ii].flags))
629 					validies |= validity_flags[ii].flags;
630 			}
631 			check_valid_flags |= validies;
632 		}
633 
634 		/* Do not traverse sub-messages */
635 		if (g_str_has_suffix (e_mail_part_get_id (mail_part), ".rfc822") &&
636 		    !g_str_equal (e_mail_part_get_id (mail_part), part_id_prefix))
637 			link = e_mail_formatter_find_rfc822_end_iter (link);
638 	}
639 
640 	if (check_valid_flags) {
641 		GString *tmp;
642 
643 		if (!is_partial) {
644 			for (link = head; link != NULL && !is_partial; link = g_list_next (link)) {
645 				EMailPart *mail_part = link->data;
646 				const gchar *id = e_mail_part_get_id (mail_part);
647 
648 				if (!e_mail_part_id_has_prefix (mail_part, part_id_prefix))
649 					continue;
650 
651 				if (should_skip_part (id))
652 					continue;
653 
654 				if (!e_mail_part_has_validity (mail_part)) {
655 					/* A part without validity, thus it's partially signed/encrypted */
656 					is_partial = TRUE;
657 					break;
658 				}
659 
660 				is_partial = !e_mail_part_get_validity (mail_part, check_valid_flags);
661 
662 				/* Do not traverse sub-messages */
663 				if (g_str_has_suffix (e_mail_part_get_id (mail_part), ".rfc822") &&
664 				    !g_str_equal (e_mail_part_get_id (mail_part), part_id_prefix))
665 					link = e_mail_formatter_find_rfc822_end_iter (link);
666 			}
667 		}
668 
669 		/* Add encryption/signature header */
670 		tmp = g_string_new ("");
671 
672 		for (link = head; link; link = g_list_next (link)) {
673 			EMailPart *mail_part = link->data;
674 			const gchar *id = e_mail_part_get_id (mail_part);
675 
676 			if (!e_mail_part_has_validity (mail_part) ||
677 			    !e_mail_part_id_has_prefix (mail_part, part_id_prefix))
678 				continue;
679 
680 			if (should_skip_part (id))
681 				continue;
682 
683 			for (ii = 0; ii < G_N_ELEMENTS (validity_flags); ii++) {
684 				if (e_mail_part_get_validity (mail_part, validity_flags[ii].flags)) {
685 					if (tmp->len > 0)
686 						g_string_append (tmp, ", ");
687 					g_string_append (tmp, is_partial ? _(validity_flags[ii].description_partial) : _(validity_flags[ii].description_complete));
688 				}
689 			}
690 
691 			break;
692 		}
693 
694 		if (tmp->len > 0)
695 			e_mail_formatter_format_header (formatter, buffer, _("Security"), tmp->str, flags, "UTF-8");
696 
697 		g_string_free (tmp, TRUE);
698 	}
699 
700 	#undef should_skip_part
701 
702 	while (!g_queue_is_empty (&queue))
703 		g_object_unref (g_queue_pop_head (&queue));
704 
705 	g_free (part_id_prefix);
706 }
707