Exemplo n.º 1
0
static int
envelope_decode_nstring (CamelIMAP4Engine *engine, char **nstring, gboolean rfc2047, CamelException *ex)
{
	camel_imap4_token_t token;
	unsigned char *literal;
	size_t n;

	if (camel_imap4_engine_next_token (engine, &token, ex) == -1)
		return -1;

	switch (token.token) {
	case CAMEL_IMAP4_TOKEN_NIL:
		*nstring = NULL;
		break;
	case CAMEL_IMAP4_TOKEN_ATOM:
		if (rfc2047)
			*nstring = camel_header_decode_string (token.v.atom, NULL);
		else
			*nstring = g_strdup (token.v.atom);
		break;
	case CAMEL_IMAP4_TOKEN_QSTRING:
		if (rfc2047)
			*nstring = camel_header_decode_string (token.v.qstring, NULL);
		else
			*nstring = g_strdup (token.v.qstring);
		break;
	case CAMEL_IMAP4_TOKEN_LITERAL:
		if (camel_imap4_engine_literal (engine, &literal, &n, ex) == -1)
			return -1;

		if (rfc2047) {
			*nstring = camel_header_decode_string (literal, NULL);
			g_free (literal);
		} else
			*nstring = literal;

		break;
	default:
		camel_imap4_utils_set_unexpected_token_error (ex, engine, &token);
		return -1;
	}

	return 0;
}
Exemplo n.º 2
0
static int
envelope_decode_address (CamelIMAP4Engine *engine, GString *addrs, CamelException *ex)
{
	char *addr, *name = NULL, *user = NULL;
	struct _camel_header_address *cia;
	unsigned char *literal = NULL;
	camel_imap4_token_t token;
	const char *domain = NULL;
	int part = 0;
	size_t n;

	if (camel_imap4_engine_next_token (engine, &token, ex) == -1)
		return -1;

	if (token.token == CAMEL_IMAP4_TOKEN_NIL) {
		return 0;
	} else if (token.token != '(') {
		camel_imap4_utils_set_unexpected_token_error (ex, engine, &token);
		return -1;
	}

	if (addrs->len > 0)
		g_string_append (addrs, ", ");

	do {
		if (camel_imap4_engine_next_token (engine, &token, ex) == -1)
			goto exception;

		literal = NULL;
		switch (token.token) {
		case CAMEL_IMAP4_TOKEN_NIL:
			break;
		case CAMEL_IMAP4_TOKEN_ATOM:
		case CAMEL_IMAP4_TOKEN_QSTRING:
			switch (part) {
			case 0:
				name = camel_header_decode_string (token.v.qstring, NULL);
				break;
			case 2:
				user = g_strdup (token.v.qstring);
				break;
			case 3:
				domain = token.v.qstring;
				break;
			}
			break;
		case CAMEL_IMAP4_TOKEN_LITERAL:
			if (camel_imap4_engine_literal (engine, &literal, &n, ex) == -1)
				goto exception;

			switch (part) {
			case 0:
				name = camel_header_decode_string (literal, NULL);
				g_free (literal);
				break;
			case 2:
				user = literal;
				break;
			case 3:
				domain = literal;
				break;
			}
			break;
		default:
			camel_imap4_utils_set_unexpected_token_error (ex, engine, &token);
			goto exception;
		}

		part++;
	} while (part < 4);

	addr = g_strdup_printf ("%s@%s", user, domain);
	g_free (literal);
	g_free (user);

	cia = camel_header_address_new_name (name, addr);
	g_free (name);
	g_free (addr);

	addr = camel_header_address_list_format (cia);
	camel_header_address_unref (cia);

	g_string_append (addrs, addr);
	g_free (addr);

	if (camel_imap4_engine_next_token (engine, &token, ex) == -1)
		return -1;

	if (token.token != ')') {
		camel_imap4_utils_set_unexpected_token_error (ex, engine, &token);
		return -1;
	}

	return 0;

 exception:

	g_free (name);
	g_free (user);

	return -1;
}
Exemplo n.º 3
0
static void
emfqe_format_header (EMailFormatter *formatter,
		     EMailFormatterContext *context,
                     GString *buffer,
                     EMailPart *part,
                     const gchar *header_name,
                     const gchar *charset)
{
	CamelMimePart *mime_part;
	EMailFormatterHeaderFlags flags;
	gchar *canon_name, *buf, *value = NULL;
	const gchar *txt, *label;
	gboolean addrspec = FALSE;
	gint is_html = FALSE;
	gint i;

	/* Skip Face header in prints, which includes also message forward */
	if (context->mode == E_MAIL_FORMATTER_MODE_PRINTING &&
	    g_ascii_strcasecmp (header_name, "Face") == 0)
		return;

	flags = E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE;

	canon_name = g_alloca (strlen (header_name) + 1);
	strcpy (canon_name, header_name);
	e_mail_formatter_canon_header_name (canon_name);

	/* Never quote Bcc/Resent-Bcc headers. */
	if (g_str_equal (canon_name, "Bcc"))
		return;
	if (g_str_equal (canon_name, "Resent-Bcc"))
		return;

	mime_part = e_mail_part_ref_mime_part (part);

	for (i = 0; addrspec_hdrs[i]; i++) {
		if (g_str_equal (canon_name, addrspec_hdrs[i])) {
			addrspec = TRUE;
			break;
		}
	}

	label = _(canon_name);

	if (addrspec) {
		CamelMedium *medium;
		struct _camel_header_address *addrs;
		GString *html;
		gchar *charset;

		medium = CAMEL_MEDIUM (mime_part);
		txt = camel_medium_get_header (medium, canon_name);
		if (txt == NULL)
			return;

		charset = e_mail_formatter_dup_charset (formatter);
		if (!charset)
			charset = e_mail_formatter_dup_default_charset (formatter);

		buf = camel_header_unfold (txt);
		addrs = camel_header_address_decode (txt, charset);
		g_free (charset);

		if (addrs == NULL) {
			g_free (buf);
			return;
		}

		g_free (buf);

		html = g_string_new ("");
		e_mail_formatter_format_address (formatter, html,
			addrs, canon_name, FALSE, FALSE);
		camel_header_address_unref (addrs);
		txt = value = html->str;
		g_string_free (html, FALSE);
		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;
		is_html = TRUE;

	} else if (g_str_equal (canon_name, "Subject")) {
		CamelMimeMessage *message;

		message = CAMEL_MIME_MESSAGE (mime_part);
		txt = camel_mime_message_get_subject (message);
		label = _("Subject");
		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;

	} else if (g_str_equal (canon_name, "X-Evolution-Mailer")) {
		CamelMedium *medium;

		medium = CAMEL_MEDIUM (mime_part);
		txt = camel_medium_get_header (medium, "x-mailer");
		if (txt == NULL)
			txt = camel_medium_get_header (medium, "user-agent");
		if (txt == NULL)
			txt = camel_medium_get_header (medium, "x-newsreader");
		if (txt == NULL)
			txt = camel_medium_get_header (medium, "x-mimeole");
		if (txt == NULL)
			return;

		txt = value = camel_header_format_ctext (txt, charset);

		label = _("Mailer");
		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;

	} else if (g_str_equal (canon_name, "Date") ||
		   g_str_equal (canon_name, "Resent-Date")) {
		CamelMedium *medium;

		medium = CAMEL_MEDIUM (mime_part);
		txt = camel_medium_get_header (medium, canon_name);
		if (txt == NULL)
			return;

		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;

	} else {
		CamelMedium *medium;

		medium = CAMEL_MEDIUM (mime_part);
		txt = camel_medium_get_header (medium, canon_name);
		buf = camel_header_unfold (txt);
		txt = value = camel_header_decode_string (txt, charset);
		g_free (buf);
	}

	emfqe_format_text_header (formatter, buffer, label, txt, flags, is_html);

	g_free (value);

	g_object_unref (mime_part);
}
void
e_mail_formatter_format_header (EMailFormatter *formatter,
                                GString *buffer,
                                const gchar *header_name,
                                const gchar *header_value,
                                guint32 flags,
                                const gchar *charset)
{
	gchar *canon_name, *buf, *value = NULL;
	const gchar *label, *txt;
	gboolean addrspec = FALSE;
	gchar *str_field = NULL;
	gint i;

	g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
	g_return_if_fail (buffer != NULL);
	g_return_if_fail (header_name != NULL);
	g_return_if_fail (header_value != NULL);

	canon_name = g_alloca (strlen (header_name) + 1);
	strcpy (canon_name, header_name);
	e_mail_formatter_canon_header_name (canon_name);

	for (i = 0; addrspec_hdrs[i]; i++) {
		if (g_ascii_strcasecmp (canon_name, addrspec_hdrs[i]) == 0) {
			addrspec = TRUE;
			break;
		}
	}

	label = _(canon_name);

	if (addrspec) {
		struct _camel_header_address *addrs;
		GString *html;
		gchar *img;
		gchar *charset;

		charset = e_mail_formatter_dup_charset (formatter);
		if (charset == NULL)
			charset = e_mail_formatter_dup_default_charset (formatter);

		buf = camel_header_unfold (header_value);
		addrs = camel_header_address_decode (buf, charset);
		if (addrs == NULL) {
			g_free (charset);
			g_free (buf);
			return;
		}

		g_free (charset);
		g_free (buf);

		html = g_string_new ("");
		img = e_mail_formatter_format_address (
			formatter, html, addrs, label,
			(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS),
			!(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE));

		if (img != NULL) {
			str_field = g_strdup_printf ("%s: %s", label, img);
			label = str_field;
			flags |= E_MAIL_FORMATTER_HEADER_FLAG_NODEC;
			g_free (img);
		}

		camel_header_address_list_clear (&addrs);
		txt = value = html->str;
		g_string_free (html, FALSE);

		flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML;
		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;

	} else if (g_str_equal (canon_name, "Subject")) {
		buf = camel_header_unfold (header_value);
		txt = value = camel_header_decode_string (buf, charset);
		g_free (buf);

		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;

	} else if (g_str_equal (canon_name, "X-Evolution-Mailer")) {
		/* pseudo-header */
		label = _("Mailer");
		txt = value = camel_header_format_ctext (header_value, charset);
		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;

	} else if (g_str_equal (canon_name, "Date") ||
		   g_str_equal (canon_name, "Resent-Date")) {
		CamelMimeFilterToHTMLFlags text_format_flags;
		gint msg_offset, local_tz;
		time_t msg_date;
		struct tm local;
		gchar *html;
		gboolean hide_real_date;

		hide_real_date = !e_mail_formatter_get_show_real_date (formatter);

		txt = header_value;
		while (*txt == ' ' || *txt == '\t')
			txt++;

		text_format_flags =
			e_mail_formatter_get_text_format_flags (formatter);

		html = camel_text_to_html (txt, text_format_flags, 0);

		msg_date = camel_header_decode_date (txt, &msg_offset);
		e_localtime_with_offset (msg_date, &local, &local_tz);

		/* Convert message offset to minutes (e.g. -0400 --> -240) */
		msg_offset = ((msg_offset / 100) * 60) + (msg_offset % 100);
		/* Turn into offset from localtime, not UTC */
		msg_offset -= local_tz / 60;

		/* value will be freed at the end */
		if (!hide_real_date && !msg_offset) {
			/* No timezone difference; just
			 * show the real Date: header. */
			txt = value = html;
		} else {
			gchar *date_str;

			date_str = e_datetime_format_format (
				"mail", "header",
				DTFormatKindDateTime, msg_date);

			if (hide_real_date) {
				/* Show only the local-formatted date, losing
				 * all timezone information like Outlook does.
				 * Should we attempt to show it somehow? */
				txt = value = date_str;
			} else {
				txt = value = g_strdup_printf (
					"%s (<I>%s</I>)", html, date_str);
				g_free (date_str);
			}
			g_free (html);
		}

		flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML;
		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;

	} else if (g_str_equal (canon_name, "Newsgroups")) {
		GSList *ng, *scan;
		GString *html;

		buf = camel_header_unfold (header_value);

		if (!(ng = camel_header_newsgroups_decode (buf))) {
			g_free (buf);
			return;
		}

		g_free (buf);

		html = g_string_new ("");
		scan = ng;
		while (scan) {
			const gchar *newsgroup = scan->data;

			if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS)
				g_string_append_printf (
					html, "%s", newsgroup);
			else
				g_string_append_printf (
					html, "<a href=\"news:%s\">%s</a>",
					newsgroup, newsgroup);
			scan = g_slist_next (scan);
			if (scan)
				g_string_append_printf (html, ", ");
		}

		g_slist_free_full (ng, g_free);

		txt = html->str;
		value = g_string_free (html, FALSE);

		flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML;
		flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD;

	} else if (g_str_equal (canon_name, "Received") ||
		   g_str_has_prefix (canon_name, "X-")) {
		/* don't unfold Received nor extension headers */
		txt = value = camel_header_decode_string (header_value, charset);

	} else {
		/* don't unfold Received nor extension headers */
		buf = camel_header_unfold (header_value);
		txt = value = camel_header_decode_string (buf, charset);
		g_free (buf);
	}

	e_mail_formatter_format_text_header (
		formatter, buffer, label, txt, flags);

	g_free (value);
	g_free (str_field);
}
/* FIXME: check format of fields. */
static gboolean
process_header (CamelMedium *medium,
                const gchar *name,
                const gchar *value)
{
	CamelHeaderType header_type;
	CamelMimeMessage *message = CAMEL_MIME_MESSAGE (medium);
	CamelInternetAddress *addr;
	const gchar *charset;
	gchar *unfolded;

	header_type = (CamelHeaderType) g_hash_table_lookup (header_name_table, name);
	switch (header_type) {
	case HEADER_FROM:
		addr = camel_internet_address_new ();
		unfolded = camel_header_unfold (value);
		if (camel_address_decode ((CamelAddress *) addr, unfolded) <= 0) {
			g_object_unref (addr);
		} else {
			if (message->from)
				g_object_unref (message->from);
			message->from = addr;
		}
		g_free (unfolded);
		break;
	case HEADER_REPLY_TO:
		addr = camel_internet_address_new ();
		unfolded = camel_header_unfold (value);
		if (camel_address_decode ((CamelAddress *) addr, unfolded) <= 0) {
			g_object_unref (addr);
		} else {
			if (message->reply_to)
				g_object_unref (message->reply_to);
			message->reply_to = addr;
		}
		g_free (unfolded);
		break;
	case HEADER_SUBJECT:
		g_free (message->subject);
		if (((CamelDataWrapper *) message)->mime_type) {
			charset = camel_content_type_param (((CamelDataWrapper *) message)->mime_type, "charset");
			charset = camel_iconv_charset_name (charset);
		} else
			charset = NULL;

		unfolded = camel_header_unfold (value);
		message->subject = g_strstrip (camel_header_decode_string (unfolded, charset));
		g_free (unfolded);
		break;
	case HEADER_TO:
	case HEADER_CC:
	case HEADER_BCC:
	case HEADER_RESENT_TO:
	case HEADER_RESENT_CC:
	case HEADER_RESENT_BCC:
		addr = g_hash_table_lookup (message->recipients, name);
		if (value) {
			unfolded = camel_header_unfold (value);
			camel_address_decode (CAMEL_ADDRESS (addr), unfolded);
			g_free (unfolded);
		} else {
			camel_address_remove (CAMEL_ADDRESS (addr), -1);
		}
		return FALSE;
	case HEADER_DATE:
		if (value) {
			message->date = camel_header_decode_date (value, &message->date_offset);
		} else {
			message->date = CAMEL_MESSAGE_DATE_CURRENT;
			message->date_offset = 0;
		}
		break;
	case HEADER_MESSAGE_ID:
		g_free (message->message_id);
		if (value)
			message->message_id = camel_header_msgid_decode (value);
		else
			message->message_id = NULL;
		break;
	default:
		return FALSE;
	}

	return TRUE;
}
/* searhces for match inside value, if match is mixed case, hten use case-sensitive,
   else insensitive */
gboolean
camel_search_header_match (const gchar *value, const gchar *match, camel_search_match_t how, camel_search_t type, const gchar *default_charset)
{
	const gchar *name, *addr;
	const guchar *ptr;
	gint truth = FALSE, i;
	CamelInternetAddress *cia;
	gchar *v, *vdom, *mdom;
	gunichar c;

	ptr = (const guchar *)value;
	while ((c = camel_utf8_getc (&ptr)) && g_unichar_isspace (c))
		value = (const gchar *)ptr;

	switch (type) {
	case CAMEL_SEARCH_TYPE_ENCODED:
		v = camel_header_decode_string (value, default_charset); /* FIXME: Find header charset */
		truth = header_match (v, match, how);
		g_free (v);
		break;
	case CAMEL_SEARCH_TYPE_MLIST:
		/* Special mailing list old-version domain hack
		   If one of the mailing list names doesn't have an @ in it, its old-style, so
		   only match against the pre-domain part, which should be common */

		vdom = strchr (value, '@');
		mdom = strchr (match, '@');
		if (mdom == NULL && vdom != NULL) {
			v = g_alloca (vdom-value+1);
			memcpy (v, value, vdom-value);
			v[vdom-value] = 0;
			value = (gchar *)v;
		} else if (mdom != NULL && vdom == NULL) {
			v = g_alloca (mdom-match+1);
			memcpy (v, match, mdom-match);
			v[mdom-match] = 0;
			match = (gchar *)v;
		}
		/* Falls through */
	case CAMEL_SEARCH_TYPE_ASIS:
		truth = header_match (value, match, how);
		break;
	case CAMEL_SEARCH_TYPE_ADDRESS_ENCODED:
	case CAMEL_SEARCH_TYPE_ADDRESS:
		/* possible simple case to save some work if we can */
		if (header_match (value, match, how))
			return TRUE;

		/* Now we decode any addresses, and try asis matches on name and address parts */
		cia = camel_internet_address_new ();
		if (type == CAMEL_SEARCH_TYPE_ADDRESS_ENCODED)
			camel_address_decode ((CamelAddress *)cia, value);
		else
			camel_address_unformat ((CamelAddress *)cia, value);

		for (i=0; !truth && camel_internet_address_get (cia, i, &name, &addr);i++)
			truth = (name && header_match (name, match, how)) || (addr && header_match (addr, match, how));

		g_object_unref (cia);
		break;
	}

	return truth;
}
static gboolean
emfpe_headers_format (EMailFormatterExtension *extension,
                      EMailFormatter *formatter,
                      EMailFormatterContext *context,
                      EMailPart *part,
                      GOutputStream *stream,
                      GCancellable *cancellable)
{
	EMailPartHeaders *headers_part;
	GtkTreeModel *tree_model;
	GtkTreeIter iter;
	gboolean iter_valid;
	GString *str, *tmp;
	gchar *subject;
	const gchar *buf;
	gint attachments_count;
	gchar *part_id_prefix;
	CamelMimePart *mime_part;
	GQueue queue = G_QUEUE_INIT;
	GList *head, *link;
	const gchar *part_id;

	g_return_val_if_fail (E_IS_MAIL_PART_HEADERS (part), FALSE);

	mime_part = e_mail_part_ref_mime_part (part);

	buf = camel_medium_get_header (CAMEL_MEDIUM (mime_part), "subject");
	subject = camel_header_decode_string (buf, "UTF-8");
	str = g_string_new ("");
	g_string_append_printf (str, "<h1>%s</h1>\n", subject);
	g_free (subject);

	g_string_append (
		str,
		"<table border=\"0\" cellspacing=\"5\" "
		"cellpadding=\"0\" class=\"printing-header\">\n");

	headers_part = E_MAIL_PART_HEADERS (part);
	tree_model = e_mail_part_headers_ref_print_model (headers_part);
	iter_valid = gtk_tree_model_get_iter_first (tree_model, &iter);

	while (iter_valid) {
		gchar *header_name = NULL;
		gchar *header_value = NULL;
		gboolean include = FALSE;

		gtk_tree_model_get (
			tree_model, &iter,
			E_MAIL_PART_HEADERS_PRINT_MODEL_COLUMN_INCLUDE,
			&include,
			E_MAIL_PART_HEADERS_PRINT_MODEL_COLUMN_HEADER_NAME,
			&header_name,
			E_MAIL_PART_HEADERS_PRINT_MODEL_COLUMN_HEADER_VALUE,
			&header_value,
			-1);

		if (include)
			e_mail_formatter_format_header (
				formatter, str,
				header_name, header_value,
				E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS,
				"UTF-8");

		g_free (header_name);
		g_free (header_value);

		iter_valid = gtk_tree_model_iter_next (tree_model, &iter);
	}

	g_object_unref (tree_model);

	/* Get prefix of this PURI */
	part_id = e_mail_part_get_id (part);
	part_id_prefix = g_strndup (part_id, g_strrstr (part_id, ".") - part_id);

	/* Add encryption/signature header */
	tmp = g_string_new ("");

	e_mail_part_list_queue_parts (context->part_list, NULL, &queue);

	head = g_queue_peek_head_link (&queue);

	/* Find first secured part. */
	for (link = head; link != NULL; link = g_list_next (link)) {
		EMailPart *mail_part = link->data;

		if (!e_mail_part_has_validity (mail_part))
			continue;

		if (!e_mail_part_id_has_prefix (mail_part, part_id_prefix))
			continue;

		if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_SIGNED)) {
			g_string_append (tmp, _("GPG signed"));
		}

		if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_ENCRYPTED)) {
			if (tmp->len > 0)
				g_string_append (tmp, ", ");
			g_string_append (tmp, _("GPG encrpyted"));
		}

		if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_SMIME | E_MAIL_PART_VALIDITY_SIGNED)) {
			if (tmp->len > 0)
				g_string_append (tmp, ", ");
			g_string_append (tmp, _("S/MIME signed"));
		}

		if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_SMIME | E_MAIL_PART_VALIDITY_ENCRYPTED)) {
			if (tmp->len > 0)
				g_string_append (tmp, ", ");
			g_string_append (tmp, _("S/MIME encrpyted"));
		}

		break;
	}

	if (tmp->len > 0) {
		e_mail_formatter_format_header (
			formatter, str,
			_("Security"), tmp->str,
			E_MAIL_FORMATTER_HEADER_FLAG_BOLD |
			E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS, "UTF-8");
	}
	g_string_free (tmp, TRUE);

	/* Count attachments and display the number as a header */
	attachments_count = 0;

	for (link = head; link != NULL; link = g_list_next (link)) {
		EMailPart *mail_part = E_MAIL_PART (link->data);

		if (!e_mail_part_id_has_prefix (mail_part, part_id_prefix))
			continue;

		if (!e_mail_part_get_is_attachment (mail_part))
			continue;

		if (mail_part->is_hidden)
			continue;

		if (e_mail_part_get_cid (mail_part) != NULL)
			continue;

		attachments_count++;
	}

	if (attachments_count > 0) {
		gchar *header_value;

		header_value = g_strdup_printf ("%d", attachments_count);
		e_mail_formatter_format_header (
			formatter, str,
			_("Attachments"), header_value,
			E_MAIL_FORMATTER_HEADER_FLAG_BOLD |
			E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS, "UTF-8");
		g_free (header_value);
	}

	while (!g_queue_is_empty (&queue))
		g_object_unref (g_queue_pop_head (&queue));

	g_string_append (str, "</table>");

	g_output_stream_write_all (
		stream, str->str, str->len, NULL, cancellable, NULL);

	g_string_free (str, TRUE);
	g_free (part_id_prefix);

	g_object_unref (mime_part);

	return TRUE;
}