+static gchar* cite (const time_t sent_date, const gchar *from);
+static void hyperlinkify_plain_text (GString *txt);
+static gint cmp_offsets_reverse (const url_match_t *match1, const url_match_t *match2);
+static void chk_partial_match (const url_match_t *match, guint* offset);
+static GSList* get_url_matches (GString *txt);
+
+static GString* get_next_line (const char *b, const gsize blen, const gchar * iter);
+static int get_indent_level (const char *l);
+static void unquote_line (GString * l);
+static void append_quoted (GString * buf, const int indent, const GString * str,
+ const int cutpoint);
+static int get_breakpoint_utf8 (const gchar * s, const gint indent, const gint limit);
+static int get_breakpoint_ascii (const gchar * s, const gint indent, const gint limit);
+static int get_breakpoint (const gchar * s, const gint indent, const gint limit);
+
+static gchar* modest_text_utils_quote_plain_text (const gchar *text,
+ const gchar *cite,
+ const gchar *signature,
+ GList *attachments,
+ int limit);
+
+static gchar* modest_text_utils_quote_html (const gchar *text,
+ const gchar *cite,
+ const gchar *signature,
+ GList *attachments,
+ int limit);
+static gchar* get_email_from_address (const gchar *address);
+
+
+/* ******************************************************************* */
+/* ************************* PUBLIC FUNCTIONS ************************ */
+/* ******************************************************************* */
+
+gchar *
+modest_text_utils_quote (const gchar *text,
+ const gchar *content_type,
+ const gchar *signature,
+ const gchar *from,
+ const time_t sent_date,
+ GList *attachments,
+ int limit)
+{
+ gchar *retval, *cited;
+
+ g_return_val_if_fail (text, NULL);
+ g_return_val_if_fail (content_type, NULL);
+
+ cited = cite (sent_date, from);
+
+ if (content_type && strcmp (content_type, "text/html") == 0)
+ /* TODO: extract the <body> of the HTML and pass it to
+ the function */
+ retval = modest_text_utils_quote_html (text, cited, signature, attachments, limit);
+ else
+ retval = modest_text_utils_quote_plain_text (text, cited, signature, attachments, limit);
+
+ g_free (cited);
+
+ return retval;
+}
+
+
+gchar *
+modest_text_utils_cite (const gchar *text,
+ const gchar *content_type,
+ const gchar *signature,
+ const gchar *from,
+ time_t sent_date)
+{
+ gchar *retval;
+ gchar *tmp_sig;
+
+ g_return_val_if_fail (text, NULL);
+ g_return_val_if_fail (content_type, NULL);
+
+ if (!signature)
+ retval = g_strdup ("");
+ else if (!strcmp(content_type, "text/html")) {
+ tmp_sig = g_strconcat ("\n", signature, NULL);
+ retval = modest_text_utils_convert_to_html_body(tmp_sig);
+ g_free (tmp_sig);
+ } else {
+ retval = g_strconcat ("\n", signature, NULL);
+ }
+
+ return retval;
+}
+
+static gchar *
+forward_cite (const gchar *from,
+ const gchar *sent,
+ const gchar *to,
+ const gchar *subject)
+{
+ return g_strdup_printf ("%s\n%s %s\n%s %s\n%s %s\n%s %s\n",
+ FORWARD_STRING,
+ FROM_STRING, (from)?from:"",
+ SENT_STRING, sent,
+ TO_STRING, (to)?to:"",
+ SUBJECT_STRING, (subject)?subject:"");
+}
+
+gchar *
+modest_text_utils_inline (const gchar *text,
+ const gchar *content_type,
+ const gchar *signature,
+ const gchar *from,
+ time_t sent_date,
+ const gchar *to,
+ const gchar *subject)
+{
+ gchar sent_str[101];
+ gchar *cited;
+ gchar *retval;
+
+ g_return_val_if_fail (text, NULL);
+ g_return_val_if_fail (content_type, NULL);
+
+ modest_text_utils_strftime (sent_str, 100, "%c", sent_date);
+
+ cited = forward_cite (from, sent_str, to, subject);
+
+ if (content_type && strcmp (content_type, "text/html") == 0)
+ retval = modest_text_utils_quote_html (text, cited, signature, NULL, 80);
+ else
+ retval = modest_text_utils_quote_plain_text (text, cited, signature, NULL, 80);
+
+ g_free (cited);
+ return retval;
+}
+
+/* just to prevent warnings:
+ * warning: `%x' yields only last 2 digits of year in some locales
+ */
+gsize
+modest_text_utils_strftime(char *s, gsize max, const char *fmt, time_t timet)
+{
+ struct tm tm;
+
+ /* does not work on old maemo glib:
+ * g_date_set_time_t (&date, timet);
+ */
+ localtime_r (&timet, &tm);
+ return strftime(s, max, fmt, &tm);
+}
+
+gchar *
+modest_text_utils_derived_subject (const gchar *subject, const gchar *prefix)
+{
+ gchar *tmp;
+
+ g_return_val_if_fail (prefix, NULL);
+
+ if (!subject)
+ return g_strdup (prefix);
+
+ tmp = g_strchug (g_strdup (subject));
+
+ if (!strncmp (tmp, prefix, strlen (prefix))) {
+ return tmp;
+ } else {
+ g_free (tmp);
+ return g_strdup_printf ("%s %s", prefix, subject);
+ }
+}
+
+gchar*
+modest_text_utils_remove_address (const gchar *address_list, const gchar *address)
+{
+ gchar *dup, *token, *ptr, *result;
+ GString *filtered_emails;
+ gchar *email_address;
+
+ g_return_val_if_fail (address_list, NULL);
+
+ if (!address)
+ return g_strdup (address_list);
+
+ email_address = get_email_from_address (address);
+
+ /* search for substring */
+ if (!strstr ((const char *) address_list, (const char *) email_address)) {
+ g_free (email_address);
+ return g_strdup (address_list);
+ }
+
+ dup = g_strdup (address_list);
+ filtered_emails = g_string_new (NULL);
+
+ token = strtok_r (dup, ",", &ptr);
+
+ while (token != NULL) {
+ /* Add to list if not found */
+ if (!strstr ((const char *) token, (const char *) email_address)) {
+ if (filtered_emails->len == 0)
+ g_string_append_printf (filtered_emails, "%s", g_strstrip (token));
+ else
+ g_string_append_printf (filtered_emails, ",%s", g_strstrip (token));
+ }
+ token = strtok_r (NULL, ",", &ptr);
+ }
+ result = filtered_emails->str;
+
+ /* Clean */
+ g_free (email_address);
+ g_free (dup);
+ g_string_free (filtered_emails, FALSE);
+
+ return result;
+}
+
+static void
+modest_text_utils_convert_buffer_to_html (GString *html, const gchar *data)
+{
+ guint i;
+ gboolean space_seen = FALSE;
+ gsize len;
+ guint break_dist = 0; /* distance since last break point */
+
+ len = strlen (data);
+
+ /* replace with special html chars where needed*/
+ for (i = 0; i != len; ++i) {
+ char kar = data[i];
+
+ if (space_seen && kar != ' ') {
+ g_string_append_c (html, ' ');
+ space_seen = FALSE;
+ }
+
+ /* we artificially insert a breakpoint (newline)
+ * after 256, to make sure our lines are not so long
+ * they will DOS the regexping later
+ */
+ if (++break_dist == 256) {
+ g_string_append_c (html, '\n');
+ break_dist = 0;
+ }
+
+ switch (kar) {
+ case 0: break; /* ignore embedded \0s */
+ case '<' : g_string_append (html, "<"); break;
+ case '>' : g_string_append (html, ">"); break;
+ case '&' : g_string_append (html, "&"); break;
+ case '"' : g_string_append (html, """); break;
+ case '\'' : g_string_append (html, "'"); break;
+ case '\n' : g_string_append (html, "<br>\n"); break_dist= 0; break;
+ case '\t' : g_string_append (html, " "); break_dist=0; break; /* note the space at the end*/
+ case ' ':
+ break_dist = 0;
+ if (space_seen) { /* second space in a row */
+ g_string_append (html, " ");
+ space_seen = FALSE;
+ } else
+ space_seen = TRUE;
+ break;
+ default:
+ g_string_append_c (html, kar);
+ }
+ }
+}
+
+gchar*
+modest_text_utils_convert_to_html (const gchar *data)
+{
+ GString *html;
+ gsize len;
+
+ if (!data)
+ return NULL;
+
+ len = strlen (data);
+ html = g_string_sized_new (1.5 * len); /* just a guess... */
+
+ g_string_append_printf (html,
+ "<html><head>"
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf8\">"
+ "</head>"
+ "<body>");
+
+ modest_text_utils_convert_buffer_to_html (html, data);
+
+ g_string_append (html, "</body></html>");
+ hyperlinkify_plain_text (html);
+
+ return g_string_free (html, FALSE);
+}
+
+gchar *
+modest_text_utils_convert_to_html_body (const gchar *data)
+{
+ GString *html;
+ gsize len;
+
+ if (!data)
+ return NULL;
+
+ len = strlen (data);
+ html = g_string_sized_new (1.5 * len); /* just a guess... */
+
+ modest_text_utils_convert_buffer_to_html (html, data);
+
+ hyperlinkify_plain_text (html);
+
+ return g_string_free (html, FALSE);
+}
+
+void
+modest_text_utils_get_addresses_indexes (const gchar *addresses, GSList **start_indexes, GSList **end_indexes)
+{
+ gchar *current, *start, *last_blank;
+ gint start_offset = 0, current_offset = 0;
+
+ g_return_if_fail (start_indexes != NULL);
+ g_return_if_fail (end_indexes != NULL);
+
+ start = (gchar *) addresses;
+ current = start;
+ last_blank = start;
+
+ while (*current != '\0') {
+ if ((start == current)&&((*current == ' ')||(*current == ',')||(*current == ';'))) {
+ start = g_utf8_next_char (start);
+ start_offset++;
+ last_blank = current;
+ } else if ((*current == ',')||(*current == ';')) {
+ gint *start_index, *end_index;
+ start_index = g_new0(gint, 1);
+ end_index = g_new0(gint, 1);
+ *start_index = start_offset;
+ *end_index = current_offset;
+ *start_indexes = g_slist_prepend (*start_indexes, start_index);
+ *end_indexes = g_slist_prepend (*end_indexes, end_index);
+ start = g_utf8_next_char (current);
+ start_offset = current_offset + 1;
+ last_blank = start;
+ } else if (*current == '"') {
+ current = g_utf8_next_char (current);
+ current_offset ++;
+ while ((*current != '"')&&(*current != '\0')) {
+ current = g_utf8_next_char (current);
+ current_offset ++;
+ }
+ }
+
+ current = g_utf8_next_char (current);
+ current_offset ++;
+ }
+
+ if (start != current) {
+ gint *start_index, *end_index;
+ start_index = g_new0(gint, 1);
+ end_index = g_new0(gint, 1);
+ *start_index = start_offset;
+ *end_index = current_offset;
+ *start_indexes = g_slist_prepend (*start_indexes, start_index);
+ *end_indexes = g_slist_prepend (*end_indexes, end_index);
+ }
+
+ *start_indexes = g_slist_reverse (*start_indexes);
+ *end_indexes = g_slist_reverse (*end_indexes);
+
+ return;
+}
+
+GSList *
+modest_text_utils_split_addresses_list (const gchar *addresses)
+{
+ gchar *current, *start, *last_blank;
+ GSList *result = NULL;
+
+ start = (gchar *) addresses;
+ current = start;
+ last_blank = start;
+
+ while (*current != '\0') {
+ if ((start == current)&&((*current == ' ')||(*current == ',')||(*current == ';'))) {
+ start = g_utf8_next_char (start);
+ last_blank = current;
+ } else if ((*current == ',')||(*current == ';')) {
+ gchar *new_address = NULL;
+ new_address = g_strndup (start, current - last_blank);
+ result = g_slist_prepend (result, new_address);
+ start = g_utf8_next_char (current);
+ last_blank = start;
+ } else if (*current == '\"') {
+ if (current == start) {
+ current = g_utf8_next_char (current);
+ start = g_utf8_next_char (start);
+ }
+ while ((*current != '\"')&&(*current != '\0'))
+ current = g_utf8_next_char (current);
+ }
+
+ current = g_utf8_next_char (current);
+ }
+
+ if (start != current) {
+ gchar *new_address = NULL;
+ new_address = g_strndup (start, current - last_blank);
+ result = g_slist_prepend (result, new_address);
+ }
+
+ result = g_slist_reverse (result);
+ return result;
+
+}
+
+void
+modest_text_utils_address_range_at_position (const gchar *recipients_list,
+ gint position,
+ gint *start,
+ gint *end)
+{
+ gchar *current = NULL;
+ gint range_start = 0;
+ gint range_end = 0;
+ gint index;
+ gboolean is_quoted = FALSE;
+
+ index = 0;
+ for (current = (gchar *) recipients_list; *current != '\0'; current = g_utf8_find_next_char (current, NULL)) {
+ gunichar c = g_utf8_get_char (current);
+
+ if ((c == ',') && (!is_quoted)) {
+ if (index < position) {
+ range_start = index + 1;
+ } else {
+ break;
+ }
+ } else if (c == '\"') {
+ is_quoted = !is_quoted;
+ } else if ((c == ' ') &&(range_start == index)) {
+ range_start ++;
+ }
+ index ++;
+ range_end = index;
+ }
+
+ if (start)
+ *start = range_start;
+ if (end)
+ *end = range_end;
+}
+
+
+/* ******************************************************************* */
+/* ************************* UTILIY FUNCTIONS ************************ */
+/* ******************************************************************* */