Exemplo n.º 1
0
/* This is only wrapper to parse_qp_mail, it decodes string and if returned TRUE,
 * then makes one string and returns it, otherwise returns NULL.
 * Returned string is usable to place directly into GtkHtml stream.
 * Returned value should be freed with g_free. */
gchar *
eab_parse_qp_email_to_html (const gchar *string)
{
	gchar *name = NULL, *mail = NULL;
	gchar *html_name, *html_mail;
	gchar *value;

	if (!eab_parse_qp_email (string, &name, &mail))
		return NULL;

	html_name = e_text_to_html (name, 0);
	html_mail = e_text_to_html (mail, E_TEXT_TO_HTML_CONVERT_ADDRESSES);

	value = g_strdup_printf ("%s <%s>", html_name, html_mail);

	g_free (html_name);
	g_free (html_mail);
	g_free (name);
	g_free (mail);

	return value;
}
Exemplo n.º 2
0
static void
accum_attribute_multival (GString *buffer,
                          EContact *contact,
                          const gchar *html_label,
                          EContactField field,
                          const gchar *icon,
                          guint html_flags)
{
	GList *val_list, *l;
	GString *val = g_string_new ("");
	const gchar *str;
	gchar *tmp;

	val_list = e_contact_get (contact, field);

	for (l = val_list; l; l = l->next) {
		str = l->data;

		if (l != val_list)
			g_string_append (val, "<br>");

		tmp = maybe_create_url (str, html_flags);
		if (tmp)
			str = tmp;

		if ((html_flags & E_TEXT_TO_HTML_CONVERT_URLS) != 0) {
			gchar *value = e_text_to_html (str, html_flags);

			if (value && *value)
				g_string_append (val, value);

			g_free (value);
		} else {
			g_string_append (val, str);
		}

		g_free (tmp);
	}

	if (val->str && *val->str) {
		if ((html_flags & E_TEXT_TO_HTML_CONVERT_URLS) != 0)
			html_flags = 0;

		render_table_row (buffer, html_label, val->str, icon, html_flags);
	}

	g_string_free (val, TRUE);
	g_list_foreach (val_list, (GFunc) g_free, NULL);
	g_list_free (val_list);
}
Exemplo n.º 3
0
static void
render_table_row (GString *buffer,
                  const gchar *label,
                  const gchar *str,
                  const gchar *icon,
                  guint html_flags)
{
	const gchar *icon_html;
	gchar *value;

	if (html_flags)
		value = e_text_to_html (str, html_flags);
	else
		value = (gchar *) str;

	if (icon && icon_available (icon)) {
		icon_html = g_strdup_printf ("<img src=\"gtk-stock://%s\" width=\"16\" height=\"16\" />", icon);
	} else {
		icon_html = "";
	}

	if (TEXT_IS_RIGHT_TO_LEFT) {
		g_string_append_printf (
			buffer, "<tr>"
			"<td valign=\"top\" align=\"right\">%s</td>"
			"<th align=\"right\" valign=\"top\" width=\"100\" nowrap>:%s</th>"
			"<td valign=\"top\" width=\"" IMAGE_COL_WIDTH "\">%s</td>"
			"</tr>",
			value, label, icon_html);
	} else {
		g_string_append_printf (
			buffer, "<tr>"
			"<td valign=\"top\" width=\"" IMAGE_COL_WIDTH "\">%s</td>"
			"<th valign=\"top\" width=\"100\" nowrap>%s:</th>"
			"<td valign=\"top\">%s</td>"
			"</tr>",
			icon_html, label, value);
	}

	if (html_flags)
		g_free (value);
}
Exemplo n.º 4
0
gint
main (gint argc,
      gchar **argv)
{
	gint i, errors = 0;
	gchar *html, *url, *p;

	for (i = 0; i < num_url_tests; i++) {
		html = e_text_to_html (
			url_tests[i].text,
			E_TEXT_TO_HTML_CONVERT_URLS |
			E_TEXT_TO_HTML_CONVERT_ADDRESSES);

		url = strstr (html, "href=\"");
		if (url) {
			url += 6;
			p = strchr (url, '"');
			if (p)
				*p = '\0';

			while ((p = strstr (url, "&amp;")))
				memmove (p + 1, p + 5, strlen (p + 5) + 1);
		}

		if ((url && (!url_tests[i].url || strcmp (url, url_tests[i].url) != 0)) ||
		    (!url && url_tests[i].url)) {
			printf ("FAILED on \"%s\" -> %s\n  (got %s)\n\n",
				url_tests[i].text,
				url_tests[i].url ? url_tests[i].url : "(nothing)",
				url ? url : "(nothing)");
			errors++;
		}

		g_free (html);
	}

	printf ("\n%d errors\n", errors);
	return errors;
}
Exemplo n.º 5
0
static void
render_compact (EABContactFormatter *formatter,
                EContact *contact,
                GString *buffer)
{
	const gchar *str;
	gchar *html;
	EContactPhoto *photo;

	g_string_append (buffer, HTML_HEADER);
	g_string_append (buffer,"<body class=\"-e-web-view-background-color -e-web-view-text-color\">");

	if (contact == NULL) {
		g_string_append (buffer, "</body></html>");
		return;
	}

	g_string_append_printf (
		buffer,
		"<table><tr><td valign=\"top\">");

	photo = e_contact_get (contact, E_CONTACT_PHOTO);

	if (photo == NULL)
		photo = e_contact_get (contact, E_CONTACT_LOGO);

	if (photo != NULL) {
		gint calced_width = MAX_COMPACT_IMAGE_DIMENSION;
		gint calced_height = MAX_COMPACT_IMAGE_DIMENSION;
		GdkPixbufLoader *loader = gdk_pixbuf_loader_new ();
		GdkPixbuf *pixbuf;

		/* figure out if we need to downscale the
		 * image here.  we don't scale the pixbuf
		 * itself, just insert width/height tags in
		 * the html */
		if (photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
			gdk_pixbuf_loader_write (
				loader, photo->data.inlined.data,
				photo->data.inlined.length, NULL);
		} else if (photo->type == E_CONTACT_PHOTO_TYPE_URI &&
				photo->data.uri &&
				g_ascii_strncasecmp (photo->data.uri, "file://", 7) == 0) {
			gchar *filename, *contents = NULL;
			gsize length;

			filename = g_filename_from_uri (photo->data.uri, NULL, NULL);

			if (filename) {
				if (g_file_get_contents (filename, &contents, &length, NULL)) {
					gdk_pixbuf_loader_write (loader, (const guchar *) contents, length, NULL);
					g_free (contents);
				}

				g_free (filename);
			}
		}

		gdk_pixbuf_loader_close (loader, NULL);
		pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);

		if (pixbuf)
			g_object_ref (pixbuf);

		g_object_unref (loader);

		if (pixbuf) {
			gint max_dimension;

			calced_width = gdk_pixbuf_get_width (pixbuf);
			calced_height = gdk_pixbuf_get_height (pixbuf);

			max_dimension = calced_width;

			if (max_dimension < calced_height)
				max_dimension = calced_height;

			if (max_dimension > MAX_COMPACT_IMAGE_DIMENSION) {
				calced_width *= ((gfloat) MAX_COMPACT_IMAGE_DIMENSION / max_dimension);
				calced_height *= ((gfloat) MAX_COMPACT_IMAGE_DIMENSION / max_dimension);
			}

			g_object_unref (pixbuf);
		}

		if (photo->type == E_CONTACT_PHOTO_TYPE_URI &&
			photo->data.uri && *photo->data.uri) {
			gboolean is_local = g_str_has_prefix (photo->data.uri, "file://");
			const gchar *uri = photo->data.uri;
			/* WebKit 2.2.x doesn't re-escape URIs, thus do this for versions before and after this */
			#if !(WEBKIT_MAJOR_VERSION == 2 && WEBKIT_MINOR_VERSION == 2)
			gchar *unescaped = g_uri_unescape_string (uri, NULL);
			uri = unescaped;
			#endif
			g_string_append_printf (
				buffer,
				"<img id=\"__evo-contact-photo\" width=\"%d\" height=\"%d\" src=\"%s%s\">",
				calced_width, calced_height,
				is_local ? "evo-" : "", uri);
			#if !(WEBKIT_MAJOR_VERSION == 2 && WEBKIT_MINOR_VERSION == 2)
			g_free (unescaped);
			#endif
		} else {
			gchar *photo_data;

			photo_data = g_base64_encode (
					photo->data.inlined.data,
					photo->data.inlined.length);
			g_string_append_printf (
				buffer,
				"<img id=\"__evo-contact-photo\" border=\"1\" src=\"data:%s;base64,%s\" "
					"width=\"%d\" height=\"%d\">",
				photo->data.inlined.mime_type,
				photo_data,
				calced_width, calced_height);
				g_free (photo_data);
		}

		e_contact_photo_free (photo);
	}

	g_string_append (buffer, "</td><td width=\"5\"></td><td valign=\"top\">\n");

	str = e_contact_get_const (contact, E_CONTACT_FILE_AS);

	if (str) {
		html = e_text_to_html (str, 0);
		g_string_append_printf (buffer, "<b>%s</b>", html);
		g_free (html);
	} else {
		str = e_contact_get_const (contact, E_CONTACT_FULL_NAME);

		if (str) {
			html = e_text_to_html (str, 0);
			g_string_append_printf (buffer, "<b>%s</b>", html);
			g_free (html);
		}
	}

	g_string_append (buffer, "<hr>");

	if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
		GList *email_list;
		GList *l;

		g_string_append (
			buffer,
			"<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">"
			"<tr><td valign=\"top\">");
		g_string_append_printf (
			buffer,
			"<b>%s:</b>&nbsp;<td>", _ ("List Members"));

		email_list = e_contact_get (contact, E_CONTACT_EMAIL);

		for (l = email_list; l; l = l->next) {
			if (l->data) {
				html = e_text_to_html (l->data, 0);
				g_string_append_printf (buffer, "%s, ", html);
				g_free (html);
			}
		}

		g_string_append (buffer, "</td></tr></table>");

	} else {

		gboolean comma = FALSE;
		str = e_contact_get_const (contact, E_CONTACT_TITLE);

		if (str) {
			html = e_text_to_html (str, 0);
			g_string_append_printf (buffer, "<b>%s:</b> %s<br>", _ ("Job Title"), str);
			g_free (html);
		}

		#define print_email() { \
			html = eab_parse_qp_email_to_html (str); \
 \
			if (!html) \
				html = e_text_to_html (str, 0); \
 \
			g_string_append_printf (buffer, "%s%s", comma ? ", " : "", html); \
			g_free (html); \
			comma = TRUE; \
		}

		g_string_append_printf (buffer, "<b>%s:</b> ", _ ("Email"));
		str = e_contact_get_const (contact, E_CONTACT_EMAIL_1);

		if (str)
			print_email ();

		str = e_contact_get_const (contact, E_CONTACT_EMAIL_2);

		if (str)
			print_email ();

		str = e_contact_get_const (contact, E_CONTACT_EMAIL_3);

		if (str)
			print_email ();

		g_string_append (buffer, "<br>");

		#undef print_email

		str = e_contact_get_const (contact, E_CONTACT_HOMEPAGE_URL);

		if (str) {
			html = e_text_to_html (str, E_TEXT_TO_HTML_CONVERT_URLS);
			g_string_append_printf (
				buffer, "<b>%s:</b> %s<br>",
				_ ("Home page"), html);
			g_free (html);
		}

		str = e_contact_get_const (contact, E_CONTACT_BLOG_URL);

		if (str) {
			html = e_text_to_html (str, E_TEXT_TO_HTML_CONVERT_URLS);
			g_string_append_printf (
				buffer, "<b>%s:</b> %s<br>",
				_ ("Blog"), html);
		}
	}

	g_string_append (buffer, "</td></tr></table>\n");

	g_string_append (buffer, "</body></html>\n");
}
Exemplo n.º 6
0
static void
render_contact_column (EABContactFormatter *formatter,
                       EContact *contact,
                       GString *buffer)
{
	GString *accum, *email;
	GList *email_list, *l, *email_attr_list, *al;
	gint email_num = 0;
	const gchar *nl;
	guint32 sip_flags = 0;

	if (formatter->priv->supports_sip)
		sip_flags = E_TEXT_TO_HTML_CONVERT_URLS |
			    E_TEXT_TO_HTML_HIDE_URL_SCHEME |
			    E_TEXT_TO_HTML_URL_IS_WHOLE_TEXT |
			    E_CREATE_SIP_URL;

	email = g_string_new ("");
	nl = "";

	email_list = e_contact_get (contact, E_CONTACT_EMAIL);
	email_attr_list = e_contact_get_attributes (contact, E_CONTACT_EMAIL);

	for (l = email_list, al = email_attr_list; l && al; l = l->next, al = al->next) {
		gchar *name = NULL, *mail = NULL;
		gchar *attr_str = (gchar *) get_email_location ((EVCardAttribute *) al->data);

		if (!eab_parse_qp_email (l->data, &name, &mail))
			mail = e_text_to_html (l->data, 0);

		g_string_append_printf (
			email,
			"%s%s%s<a href=\"internal-mailto:%d\">%s</a>%s "
			"<span class=\"header\">(%s)</span>",
			nl,
			name ? name : "",
			name ? " &lt;" : "",
			email_num,
			mail,
			name ? "&gt;" : "",
			attr_str ? attr_str : "");
		email_num++;
		nl = "<br>";

		g_free (name);
		g_free (mail);
	}
	g_list_foreach (email_list, (GFunc) g_free, NULL);
	g_list_foreach (email_attr_list, (GFunc) e_vcard_attribute_free, NULL);
	g_list_free (email_list);
	g_list_free (email_attr_list);

	accum = g_string_new ("");

	if (email->len)
		render_table_row (accum, _("Email"), email->str, NULL, 0);

	accum_sip (accum, contact, EAB_CONTACT_FORMATTER_SIP_TYPE_OTHER, NULL, sip_flags);

	accum_attribute (accum, contact, _("Nickname"), E_CONTACT_NICKNAME, NULL, 0);
	accum_attribute_multival (accum, contact, _("AIM"), E_CONTACT_IM_AIM, AIM_ICON, 0);
	accum_attribute_multival (accum, contact, _("GroupWise"), E_CONTACT_IM_GROUPWISE, GROUPWISE_ICON, 0);
	accum_attribute_multival (accum, contact, _("ICQ"), E_CONTACT_IM_ICQ, ICQ_ICON, 0);
	accum_attribute_multival (accum, contact, _("Jabber"), E_CONTACT_IM_JABBER, JABBER_ICON, 0);
	accum_attribute_multival (accum, contact, _("MSN"), E_CONTACT_IM_MSN, MSN_ICON, 0);
	accum_attribute_multival (accum, contact, _("Yahoo"), E_CONTACT_IM_YAHOO, YAHOO_ICON, 0);
	accum_attribute_multival (accum, contact, _("Gadu-Gadu"), E_CONTACT_IM_GADUGADU, GADUGADU_ICON, 0);
	accum_attribute_multival (accum, contact, _("Skype"), E_CONTACT_IM_SKYPE, SKYPE_ICON, 0);
	accum_attribute_multival (accum, contact, _("Twitter"), E_CONTACT_IM_TWITTER, TWITTER_ICON, 0);

	if (accum->len)
		g_string_append_printf (
			buffer,
			"<div class=\"column\" id=\"contact-internet\">"
			"<table border=\"0\" cellspacing=\"5\">%s</table>"
			"</div>", accum->str);

	g_string_free (accum, TRUE);
	g_string_free (email, TRUE);
}
Exemplo n.º 7
0
static void
render_title_block (EABContactFormatter *formatter,
                    EContact *contact,
                    GString *buffer)
{
	const gchar *str;
	gchar *html;
	EContactPhoto *photo;

	g_string_append_printf (
		buffer,
		"<table border=\"0\"><tr>"
		"<td %s valign=\"middle\">", TEXT_IS_RIGHT_TO_LEFT ?
		"align=\"right\"" : "");

	photo = e_contact_get (contact, E_CONTACT_PHOTO);
	if (!photo)
		photo = e_contact_get (contact, E_CONTACT_LOGO);

	if (photo && photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
		gchar *photo_data;
		photo_data = g_base64_encode (
				photo->data.inlined.data,
				photo->data.inlined.length);
		g_string_append_printf (
			buffer,
			"<img id=\"__evo-contact-photo\" border=\"1\" src=\"data:%s;base64,%s\">",
			photo->data.inlined.mime_type,
			photo_data);
	} else if (photo && photo->type == E_CONTACT_PHOTO_TYPE_URI && photo->data.uri && *photo->data.uri) {
		gboolean is_local = g_str_has_prefix (photo->data.uri, "file://");
		const gchar *uri = photo->data.uri;
		/* WebKit 2.2.x doesn't re-escape URIs, thus do this for versions before and after this */
		#if !(WEBKIT_MAJOR_VERSION == 2 && WEBKIT_MINOR_VERSION == 2)
		gchar *unescaped = g_uri_unescape_string (uri, NULL);
		uri = unescaped;
		#endif
		g_string_append_printf (
			buffer, "<img id=\"__evo-contact-photo\" border=\"1\" src=\"%s%s\">",
			is_local ? "evo-" : "", uri);
		#if !(WEBKIT_MAJOR_VERSION == 2 && WEBKIT_MINOR_VERSION == 2)
		g_free (unescaped);
		#endif
	}

	if (photo)
		e_contact_photo_free (photo);

	if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
		g_string_append_printf (buffer, "<img src=\"gtk-stock://%s\">", CONTACT_LIST_ICON);
	}

	g_string_append_printf (
		buffer,
		"</td><td width=\"20\"></td><td %s valign=\"top\">\n",
		TEXT_IS_RIGHT_TO_LEFT ? "align=\"right\"" : "");

	str = e_contact_get_const (contact, E_CONTACT_FILE_AS);
	if (!str)
		str = e_contact_get_const (contact, E_CONTACT_FULL_NAME);

	if (str) {
		html = e_text_to_html (str, 0);
		if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
			g_string_append_printf (
				buffer,
				"<h2><a href=\"internal-mailto:0\">%s</a></h2>",
				html);
		} else {
			g_string_append_printf (buffer, "<h2>%s</h2>", html);
		}
		g_free (html);
	}

	g_string_append (buffer, "</td></tr></table>");
}
Exemplo n.º 8
0
static void
accum_sip (GString *buffer,
	   EContact *contact,
	   EABContactFormatterSIPType use_sip_type,
	   const gchar *icon,
	   guint html_flags)
{
	const gchar *html_label = _("SIP");
	GList *sip_attr_list, *l;
	GString *val = g_string_new ("");
	gchar *tmp;

	sip_attr_list = e_contact_get_attributes (contact, E_CONTACT_SIP);
	for (l = sip_attr_list; l; l = g_list_next (l)) {
		EVCardAttribute *attr = l->data;
		gchar *sip;
		const gchar *str;
		EABContactFormatterSIPType sip_type;

		if (e_vcard_attribute_has_type (attr, "HOME"))
			sip_type = EAB_CONTACT_FORMATTER_SIP_TYPE_HOME;
		else if (e_vcard_attribute_has_type (attr, "WORK"))
			sip_type = EAB_CONTACT_FORMATTER_SIP_TYPE_WORK;
		else
			sip_type = EAB_CONTACT_FORMATTER_SIP_TYPE_OTHER;

		if (sip_type != use_sip_type)
			continue;

		sip = e_vcard_attribute_get_value (attr);
		if (!sip || !*sip) {
			g_free (sip);
			continue;
		}

		tmp = maybe_create_url (sip, html_flags);
		if (tmp)
			str = tmp;
		else
			str = sip;

		if ((html_flags & E_TEXT_TO_HTML_CONVERT_URLS) != 0) {
			gchar *value = e_text_to_html (str, html_flags);

			if (value && *value) {
				if (val->len)
					g_string_append (val, "<br>");
				g_string_append (val, value);
			}

			g_free (value);
		} else {
			if (val->len)
				g_string_append (val, "<br>");
			g_string_append (val, str);
		}

		g_free (tmp);
		g_free (sip);
	}

	if (val->str && *val->str) {
		if ((html_flags & E_TEXT_TO_HTML_CONVERT_URLS) != 0)
			html_flags = 0;

		render_table_row (buffer, html_label, val->str, icon, html_flags);
	}

	g_string_free (val, TRUE);
	g_list_free_full (sip_attr_list, (GDestroyNotify) e_vcard_attribute_free);
}
Exemplo n.º 9
0
static void
accum_address (GString *buffer,
               EContact *contact,
               const gchar *html_label,
               EContactField adr_field,
               EContactField label_field)
{
	EContactAddress *adr;
	const gchar *label;
	GString *map_link = g_string_new ("<br>");

	render_address_link (map_link, contact, adr_field);

	label = e_contact_get_const (contact, label_field);
	if (label) {
		gchar *html = e_text_to_html (label, E_TEXT_TO_HTML_CONVERT_NL);

		if (TEXT_IS_RIGHT_TO_LEFT) {
			g_string_append_printf (
				buffer,
				"<tr>"
				"<td align=\"right\" valign=\"top\" nowrap>%s</td>"
				"<th>%s:<br>%s</th>"
				"<td valign=\"top\" width=\"" IMAGE_COL_WIDTH "\"></td>"
				"</tr>",
				html, html_label, map_link->str);
		} else {
			g_string_append_printf (
				buffer,
				"<tr>"
				"<td width=\"" IMAGE_COL_WIDTH "\"></td>"
				"<th>%s:<br>%s</th>"
				"<td valign=\"top\" nowrap>%s</td>"
				"</tr>",
				html_label, map_link->str, html);
		}

		g_free (html);
		g_string_free (map_link, TRUE);
		return;
	}

	adr = e_contact_get (contact, adr_field);
	if (adr &&
	   (adr->po || adr->ext || adr->street || adr->locality ||
	    adr->region || adr->code || adr->country)) {

		if (TEXT_IS_RIGHT_TO_LEFT) {
			g_string_append_printf (
				buffer, "<tr><td align=\"right\" valign=\"top\" nowrap>");
		} else {
			g_string_append_printf (
				buffer,
				"<tr>"
				"<td valign=\"top\" width=\"" IMAGE_COL_WIDTH "\"></td>"
				"<th>%s:<br>%s</th>"
				"<td valign=\"top\" nowrap>",
				html_label, map_link->str);
		}

		if (adr->po && *adr->po)
			g_string_append_printf (buffer, "%s<br>", adr->po);

		if (adr->ext && *adr->ext)
			g_string_append_printf (buffer, "%s<br>", adr->ext);

		if (adr->street && *adr->street)
			g_string_append_printf (buffer, "%s<br>", adr->street);

		if (adr->locality && *adr->locality)
			g_string_append_printf (buffer, "%s<br>", adr->locality);

		if (adr->region && *adr->region)
			g_string_append_printf (buffer, "%s<br>", adr->region);

		if (adr->code && *adr->code)
			g_string_append_printf (buffer, "%s<br>", adr->code);

		if (adr->country && *adr->country)
			g_string_append_printf (buffer, "%s<br>", adr->country);

		if (TEXT_IS_RIGHT_TO_LEFT) {
			g_string_append_printf (
				buffer,
				"</td><th%s:<br>%s</th>"
				"<td width=\"" IMAGE_COL_WIDTH "\"></td>"
				"</tr>", html_label, map_link->str);
		} else {
			g_string_append_printf (buffer, "</td></tr>");
		}

	}

	if (adr)
		e_contact_address_free (adr);

	g_string_free (map_link, TRUE);
}
Exemplo n.º 10
0
/**
 * e_text_to_html_full:
 * @input: a NUL-terminated input buffer
 * @flags: some combination of the E_TEXT_TO_HTML_* flags defined
 * in e-html-utils.h
 * @color: color for citation highlighting
 *
 * This takes a buffer of text as input and produces a buffer of
 * "equivalent" HTML, subject to certain transformation rules.
 *
 * The set of possible flags is:
 *
 *   - E_TEXT_TO_HTML_PRE: wrap the output HTML in &lt;PRE&gt; and
 *     &lt;/PRE&gt;  Should only be used if @input is the entire
 *     buffer to be converted. If e_text_to_html is being called with
 *     small pieces of data, you should wrap the entire result in
 *     &lt;PRE&gt; yourself.
 *
 *   - E_TEXT_TO_HTML_CONVERT_NL: convert "\n" to "&lt;BR&gt;n" on
 *     output.  (Should not be used with E_TEXT_TO_HTML_PRE, since
 *     that would result in double-newlines.)
 *
 *   - E_TEXT_TO_HTML_CONVERT_SPACES: convert a block of N spaces
 *     into N-1 non-breaking spaces and one normal space. A space
 *     at the start of the buffer is always converted to a
 *     non-breaking space, regardless of the following character,
 *     which probably means you don't want to use this flag on
 *     pieces of data that aren't delimited by at least line breaks.
 *
 *     If E_TEXT_TO_HTML_CONVERT_NL and E_TEXT_TO_HTML_CONVERT_SPACES
 *     are both defined, then TABs will also be converted to spaces.
 *
 *   - E_TEXT_TO_HTML_CONVERT_URLS: wrap &lt;a href="..."&gt; &lt;/a&gt;
 *     around strings that look like URLs.
 *
 *   - E_TEXT_TO_HTML_CONVERT_ADDRESSES: wrap &lt;a href="mailto:..."&gt;
 *     &lt;/a&gt; around strings that look like mail addresses.
 *
 *   - E_TEXT_TO_HTML_MARK_CITATION: wrap &lt;font color="..."&gt;
 *     &lt;/font&gt; around citations (lines beginning with "> ", etc).
 *
 *   - E_TEXT_TO_HTML_ESCAPE_8BIT: flatten everything to US-ASCII
 *
 *   - E_TEXT_TO_HTML_CITE: quote the text with "> " at the start of each
 *     line.
 *
 * Returns: a newly-allocated string containing HTML
 **/
gchar *
e_text_to_html_full (const gchar *input,
                     guint flags,
                     guint32 color)
{
	const guchar *cur, *next, *linestart;
	gchar *buffer = NULL;
	gchar *out = NULL;
	gint buffer_size = 0, col;
	gboolean colored = FALSE, saw_citation = FALSE;

	/* Allocate a translation buffer.  */
	buffer_size = strlen (input) * 2 + 5;
	buffer = g_malloc (buffer_size);

	out = buffer;
	if (flags & E_TEXT_TO_HTML_PRE)
		out += sprintf (out, "<PRE>");

	col = 0;

	for (cur = linestart = (const guchar *) input; cur && *cur; cur = next) {
		gunichar u;

		if (flags & E_TEXT_TO_HTML_MARK_CITATION && col == 0) {
			saw_citation = is_citation (cur, saw_citation);
			if (saw_citation) {
				if (!colored) {
					gchar font[25];

					g_snprintf (font, 25, "<FONT COLOR=\"#%06x\">", color);

					out = check_size (&buffer, &buffer_size, out, 25);
					out += sprintf (out, "%s", font);
					colored = TRUE;
				}
			} else if (colored) {
				const gchar *no_font = "</FONT>";

				out = check_size (&buffer, &buffer_size, out, 9);
				out += sprintf (out, "%s", no_font);
				colored = FALSE;
			}

			/* Display mbox-mangled ">From" as "From" */
			if (*cur == '>' && !saw_citation)
				cur++;
		} else if (flags & E_TEXT_TO_HTML_CITE && col == 0) {
			out = check_size (&buffer, &buffer_size, out, 5);
			out += sprintf (out, "&gt; ");
		}

		u = g_utf8_get_char ((gchar *) cur);
		if (g_unichar_isalpha (u) &&
		    (flags & E_TEXT_TO_HTML_CONVERT_URLS)) {
			gchar *tmpurl = NULL, *refurl = NULL, *dispurl = NULL;

			if (!g_ascii_strncasecmp ((gchar *)cur, "http://", 7) ||
			    !g_ascii_strncasecmp ((gchar *)cur, "https://", 8) ||
			    !g_ascii_strncasecmp ((gchar *)cur, "ftp://", 6) ||
			    !g_ascii_strncasecmp ((gchar *)cur, "nntp://", 7) ||
			    !g_ascii_strncasecmp ((gchar *)cur, "mailto:", 7) ||
			    !g_ascii_strncasecmp ((gchar *)cur, "news:", 5) ||
			    !g_ascii_strncasecmp ((gchar *)cur, "file:", 5) ||
			    !g_ascii_strncasecmp ((gchar *)cur, "callto:", 7) ||
			    !g_ascii_strncasecmp ((gchar *)cur, "h323:", 5) ||
			    !g_ascii_strncasecmp ((gchar *)cur, "sip:", 4) ||
			    !g_ascii_strncasecmp ((gchar *)cur, "webcal:", 7)) {
				tmpurl = url_extract (&cur, TRUE);
				if (tmpurl) {
					refurl = e_text_to_html (tmpurl, 0);
					dispurl = g_strdup (refurl);
				}
			} else if (!g_ascii_strncasecmp ((gchar *)cur, "www.", 4) &&
				   is_url_char (*(cur + 4))) {
				tmpurl = url_extract (&cur, FALSE);
				if (tmpurl) {
					dispurl = e_text_to_html (tmpurl, 0);
					refurl = g_strdup_printf ("http://%s",
								  dispurl);
				}
			}

			if (tmpurl) {
				out = check_size (&buffer, &buffer_size, out,
						  strlen (refurl) +
						  strlen (dispurl) + 15);
				out += sprintf (out,
						"<a href=\"%s\">%s</a>",
						refurl, dispurl);
				col += strlen (tmpurl);
				g_free (tmpurl);
				g_free (refurl);
				g_free (dispurl);
			}

			if (!*cur)
				break;
			u = g_utf8_get_char ((gchar *) cur);
		}

		if (u == '@' && (flags & E_TEXT_TO_HTML_CONVERT_ADDRESSES)) {
			gchar *addr, *dispaddr, *outaddr;

			addr = email_address_extract (&cur, &out, linestart);
			if (addr) {
				dispaddr = e_text_to_html (addr, 0);
				outaddr = g_strdup_printf ("<a href=\"mailto:%s\">%s</a>",
							   addr, dispaddr);
				out = check_size (&buffer, &buffer_size, out, strlen (outaddr));
				out += sprintf (out, "%s", outaddr);
				col += strlen (addr);
				g_free (addr);
				g_free (dispaddr);
				g_free (outaddr);

				if (!*cur)
					break;
				u = g_utf8_get_char ((gchar *) cur);
			}
		}

		if (!g_unichar_validate (u)) {
			/* Sigh. Someone sent undeclared 8-bit data.
			 * Assume it's iso-8859-1.
			 */
			u = *cur;
			next = cur + 1;
		} else
			next = (const guchar *) g_utf8_next_char (cur);

		out = check_size (&buffer, &buffer_size, out, 10);

		switch (u) {
		case '<':
			strcpy (out, "&lt;");
			out += 4;
			col++;
			break;

		case '>':
			strcpy (out, "&gt;");
			out += 4;
			col++;
			break;

		case '&':
			strcpy (out, "&amp;");
			out += 5;
			col++;
			break;

		case '"':
			strcpy (out, "&quot;");
			out += 6;
			col++;
			break;

		case '\n':
			if (flags & E_TEXT_TO_HTML_CONVERT_NL) {
				strcpy (out, "<br>");
				out += 4;
			}
			*out++ = *cur;
			linestart = cur;
			col = 0;
			break;

		case '\t':
			if (flags & (E_TEXT_TO_HTML_CONVERT_SPACES |
				     E_TEXT_TO_HTML_CONVERT_NL)) {
				do {
					out = check_size (&buffer, &buffer_size,
						    out, 7);
					strcpy (out, "&nbsp;");
					out += 6;
					col++;
				} while (col % 8);
				break;
			}
			/* otherwise, FALL THROUGH */

		case ' ':
			if (flags & E_TEXT_TO_HTML_CONVERT_SPACES) {
				if (cur == (const guchar *) input ||
				    *(cur + 1) == ' ' || *(cur + 1) == '\t' ||
				    *(cur - 1) == '\n') {
					strcpy (out, "&nbsp;");
					out += 6;
					col++;
					break;
				}
			}
			/* otherwise, FALL THROUGH */

		default:
			if ((u >= 0x20 && u < 0x80) ||
			    (u == '\r' || u == '\t')) {
				/* Default case, just copy. */
				*out++ = u;
			} else {
				if (flags & E_TEXT_TO_HTML_ESCAPE_8BIT)
					*out++ = '?';
				else
					out += g_snprintf(out, 9, "&#%d;", u);
			}
			col++;
			break;
		}
	}

	out = check_size (&buffer, &buffer_size, out, 7);
	if (flags & E_TEXT_TO_HTML_PRE)
		strcpy (out, "</PRE>");
	else
		*out = '\0';

	return buffer;
}