Modified webpage: now tinymail repository is in gitorious.
[modest] / src / modest-text-utils.c
index be439ac..cc02fb5 100644 (file)
@@ -41,6 +41,7 @@
 #include <regex.h>
 #include <modest-tny-platform-factory.h>
 #include <modest-text-utils.h>
+#include <modest-account-mgr-helpers.h>
 #include <modest-runtime.h>
 #include <ctype.h>
 
@@ -163,6 +164,8 @@ static gchar*   modest_text_utils_quote_html       (const gchar *text,
                                                    GList *attachments,
                                                    int limit);
 static gchar*   get_email_from_address (const gchar *address);
+static void     remove_extra_spaces (gchar *string);
+
 
 
 /* ******************************************************************* */
@@ -205,23 +208,26 @@ modest_text_utils_cite (const gchar *text,
                        const gchar *from,
                        time_t sent_date)
 {
-       gchar *retval;
-       gchar *tmp_sig;
-       
+       gchar *retval, *tmp;
+
        g_return_val_if_fail (text, NULL);
        g_return_val_if_fail (content_type, NULL);
-       
-       if (!signature) {
-               tmp_sig = g_strdup (text);
-       } else {
-               tmp_sig = g_strconcat (text, "\n", MODEST_TEXT_UTILS_SIGNATURE_MARKER, "\n", signature, NULL);
-       }
 
        if (strcmp (content_type, "text/html") == 0) {
-               retval = modest_text_utils_convert_to_html_body (tmp_sig, -1, TRUE);
-               g_free (tmp_sig);
+               tmp = modest_text_utils_convert_to_html_body (text, -1, TRUE);
+               if (signature) {
+                       gchar *colored_signature = modest_text_utils_create_colored_signature (signature);
+                       retval = g_strconcat (tmp, colored_signature, NULL);
+                       g_free (colored_signature);
+                       g_free (tmp);
+               } else {
+                       retval = tmp;
+               }
        } else {
-               retval = tmp_sig;
+               if (signature)
+                       retval = g_strconcat (text, "\n", MODEST_TEXT_UTILS_SIGNATURE_MARKER, "\n", signature, NULL);
+               else
+                       retval = g_strdup (text);
        }
 
        return retval;
@@ -295,12 +301,12 @@ modest_text_utils_strftime(char *s, gsize max, const char *fmt, time_t timet)
 }
 
 gchar *
-modest_text_utils_derived_subject (const gchar *subject, const gchar *prefix)
+modest_text_utils_derived_subject (const gchar *subject, gboolean is_reply)
 {
-       gchar *tmp, *subject_dup, *retval;
-       gint prefix_len;
-
-       g_return_val_if_fail (prefix, NULL);
+       gchar *tmp, *subject_dup, *retval, *prefix;
+       const gchar *untranslated_prefix;
+       gint prefix_len, untranslated_prefix_len;
+       gboolean found = FALSE;
 
        if (!subject || subject[0] == '\0')
                subject = _("mail_va_no_subject");
@@ -308,40 +314,78 @@ modest_text_utils_derived_subject (const gchar *subject, const gchar *prefix)
        subject_dup = g_strdup (subject);
        tmp = g_strchug (subject_dup);
 
-       /* We do not want things like "Re: Re: Re:" or "Fw: Fw:" so
-          delete the previous ones */
-       prefix_len = strlen (prefix);
-       do {
-               if (g_str_has_prefix (tmp, prefix)) {
-                       tmp += prefix_len;
-                       tmp = g_strchug (tmp);
-               } else {
-                       gchar *prefix_down, *tmp_down;
-
-                       /* We need this to properly check the cases of
-                          some clients adding FW: instead of Fw: for
-                          example */
-                       prefix_down = g_utf8_strdown (prefix, -1);
-                       tmp_down = g_utf8_strdown (tmp, -1);
-                       if (g_str_has_prefix (tmp_down, prefix_down)) {
-                               tmp += prefix_len;
-                               tmp = g_strchug (tmp);
-                               g_free (prefix_down);
-                               g_free (tmp_down);
-                       } else {
-                               g_free (prefix_down);
-                               g_free (tmp_down);
-                               break;
-                       }
-               }
-       } while (tmp);
+       prefix = (is_reply) ? _("mail_va_re") : _("mail_va_fw");
+       prefix = g_strconcat (prefix, ":", NULL);
+       prefix_len = g_utf8_strlen (prefix, -1);
 
-       retval = g_strdup_printf ("%s %s", prefix, tmp);
-       g_free (subject_dup);
+       untranslated_prefix =  (is_reply) ? "Re:" : "Fw:";
+       untranslated_prefix_len = 3;
+
+       if (g_str_has_prefix (tmp, prefix) ||
+           g_str_has_prefix (tmp, untranslated_prefix)) {
+               found = TRUE;
+       } else {
+               gchar *prefix_down, *tmp_down, *untranslated_down;
+
+               prefix_down = g_utf8_strdown (prefix, -1);
+               untranslated_down = g_utf8_strdown (untranslated_prefix, -1);
+               tmp_down = g_utf8_strdown (tmp, -1);
+               if (g_str_has_prefix (tmp_down, prefix_down) ||
+                   g_str_has_prefix (tmp_down, untranslated_down) ||
+                   (!is_reply && g_str_has_prefix (tmp_down, "fwd:")))
+                       found = TRUE;
+
+               g_free (prefix_down);
+               g_free (untranslated_down);
+               g_free (tmp_down);
+       }
+
+       if (found) {
+               /* If the prefix is already present do not touch the subject */
+               retval = subject_dup;
+       } else {
+               /* Normal case, add the prefix */
+               retval = g_strdup_printf ("%s %s", untranslated_prefix, tmp);
+               g_free (subject_dup);
+       }
+       g_free (prefix);
 
        return retval;
 }
 
+
+/* Performs a case-insensitive strstr for ASCII strings */
+static const gchar *
+ascii_stristr(const gchar *haystack, const gchar *needle)
+{
+       int needle_len;
+       int haystack_len;
+       const gchar *pos;
+       const gchar *max_pos;
+
+       if (haystack == NULL || needle == NULL) {
+               return haystack;  /* as in strstr */
+       }
+
+       needle_len = strlen(needle);
+
+       if (needle_len == 0) {
+               return haystack;  /* as in strstr */
+       }
+
+       haystack_len = strlen(haystack);
+       max_pos = haystack + haystack_len - needle_len;
+
+       for (pos = haystack; pos <= max_pos; pos++) {
+               if (g_ascii_strncasecmp (pos, needle, needle_len) == 0) {
+                       return pos;
+               }
+       }
+
+       return NULL;
+}
+
+
 gchar*
 modest_text_utils_remove_address (const gchar *address_list, const gchar *address)
 {
@@ -350,26 +394,26 @@ modest_text_utils_remove_address (const gchar *address_list, const gchar *addres
        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)) {
+       if (!ascii_stristr ((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 (!ascii_stristr ((const char *) token, (const char *) email_address)) {
                        if (filtered_emails->len == 0)
                                g_string_append_printf (filtered_emails, "%s", g_strstrip (token));
                        else
@@ -454,7 +498,7 @@ modest_text_utils_convert_buffer_to_html_start (GString *html, const gchar *data
                guchar kar = data[i];
                
                if (space_seen && kar != ' ') {
-                       g_string_append (html, "&nbsp;");
+                       g_string_append (html, "&#32;");
                        space_seen = FALSE;
                }
                
@@ -484,14 +528,13 @@ modest_text_utils_convert_buffer_to_html_start (GString *html, const gchar *data
 
                /* don't convert &apos; --> wpeditor will try to re-convert it... */    
                //case '\'' : g_string_append (html, "&apos;"); break;
-               case '\n' : g_string_append (html, "<br>\n");break_dist= 0; break;
+               case '\n' : g_string_append (html, "<br/>\n");break_dist= 0; break;
                case '\t' : g_string_append (html, MARK_AMP_STR "nbsp;" MARK_AMP_STR "nbsp;" MARK_AMP_STR "nbsp; ");
                        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, "&nbsp; ");
-                               space_seen = FALSE;
                        } else
                                space_seen = TRUE;
                        break;
@@ -572,59 +615,52 @@ modest_text_utils_convert_to_html_body (const gchar *data, gssize n, gboolean hy
 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;
+       GString *str;
+       gchar *start, *cur;
 
-       g_return_if_fail (start_indexes != NULL);
-       g_return_if_fail (end_indexes != NULL);
+       if (!addresses)
+               return;
 
-       start = (gchar *) addresses;
-       current = start;
-       last_blank = start;
+       if (strlen (addresses) == 0)
+               return;
 
-       while (*current != '\0') {
-               if ((start == current)&&((*current == ' ')||(*current == ',')||(*current == ';'))) {
-                       start = g_utf8_next_char (start);
-                       start_offset++;
-                       last_blank = current;
-               } else if ((*current == ',')||(*current == ';')) {
+       str = g_string_new ("");
+       start = (gchar*) addresses;
+       cur = (gchar*) addresses;
+
+       for (cur = start; *cur != '\0'; cur = g_utf8_next_char (cur)) {
+               if (*cur == ',' || *cur == ';') {
                        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;
+                       gchar *next_char = g_utf8_next_char (cur);
+
+                       if (!g_utf8_strchr (start, (cur - start + 1), g_utf8_get_char ("@")) &&
+                           next_char && *next_char != '\n' && *next_char != '\0')
+                               continue;
+
+                       start_index = g_new0 (gint, 1);
+                       end_index = g_new0 (gint, 1);
+                       *start_index = g_utf8_pointer_to_offset (addresses, start);
+                       *end_index = g_utf8_pointer_to_offset (addresses, cur);;
                        *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 ++;
-                       }
+                       start = g_utf8_next_char (cur);
                }
-                               
-               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);
+       if (start != cur) {
+               gint *start_index, *end_index;
+               start_index = g_new0 (gint, 1);
+               end_index = g_new0 (gint, 1);
+               *start_index = g_utf8_pointer_to_offset (addresses, start);
+               *end_index = g_utf8_pointer_to_offset (addresses, cur);;
+               *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;
+       if (*start_indexes)
+               *start_indexes = g_slist_reverse (*start_indexes);
+       if (*end_indexes)
+               *end_indexes = g_slist_reverse (*end_indexes);
 }
 
 
@@ -638,15 +674,16 @@ modest_text_utils_split_addresses_list (const gchar *addresses)
        gboolean after_at = FALSE;
 
        g_return_val_if_fail (addresses, NULL);
-       
-       /* skip any space, ',', ';' at the start */
-       while (my_addrs && (my_addrs[0] == ' ' || my_addrs[0] == ',' || my_addrs[0] == ';'))
+
+       /* skip any space, ',', ';' '\n' at the start */
+       while (my_addrs && (my_addrs[0] == ' ' || my_addrs[0] == ',' ||
+                           my_addrs[0] == ';' || my_addrs[0] == '\n'))
               ++my_addrs;
 
        /* are we at the end of addresses list? */
        if (!my_addrs[0])
                return NULL;
-       
+
        /* nope, we are at the start of some address
         * now, let's find the end of the address */
        end = my_addrs + 1;
@@ -669,12 +706,56 @@ modest_text_utils_split_addresses_list (const gchar *addresses)
        addr = g_strndup (my_addrs, end - my_addrs);
        g_strchomp (addr);
 
+       remove_extra_spaces (addr);
+
        head = g_slist_append (NULL, addr);
        head->next = modest_text_utils_split_addresses_list (end); /* recurse */
 
        return head;
 }
 
+gchar *
+modest_text_utils_join_addresses (const gchar *from,
+                                 const gchar *to,
+                                 const gchar *cc,
+                                 const gchar *bcc)
+{
+       GString *buffer;
+       gboolean add_separator = FALSE;
+
+       buffer = g_string_new ("");
+
+       if (from && strlen (from)) {
+               buffer = g_string_append (buffer, from);
+               add_separator = TRUE;
+       }
+       if (to && strlen (to)) {
+               if (add_separator)
+                       buffer = g_string_append (buffer, "; ");
+               else
+                       add_separator = TRUE;
+
+               buffer = g_string_append (buffer, to);
+       }
+       if (cc && strlen (cc)) {
+               if (add_separator)
+                       buffer = g_string_append (buffer, "; ");
+               else
+                       add_separator = TRUE;
+
+               buffer = g_string_append (buffer, cc);
+       }
+       if (bcc && strlen (bcc)) {
+               if (add_separator)
+                       buffer = g_string_append (buffer, "; ");
+               else
+                       add_separator = TRUE;
+
+               buffer = g_string_append (buffer, bcc);
+       }
+
+       return g_string_free (buffer, FALSE);
+}
 
 void
 modest_text_utils_address_range_at_position (const gchar *recipients_list,
@@ -802,9 +883,11 @@ unquote_line (GString * l, const gchar *quote_symbol)
        quote_len = strlen (quote_symbol);
        while (p[0]) {
                if (g_str_has_prefix (p, quote_symbol)) {
-                       if (p[quote_len] == ' ') {
-                               p += quote_len;
+                       p+=quote_len;
+                       while (p[0] && p[0] == ' ') {
+                               p++;
                        }
+                       continue;
                } else {
                        break;
                }
@@ -842,6 +925,9 @@ get_breakpoint_utf8 (const gchar * s, gint indent, const gint limit)
        const gchar *pos, *last;
        gunichar *uni;
 
+       if (2*indent >= limit)
+               return strlen (s);
+
        indent = indent < 0 ? abs (indent) - 1 : indent;
 
        last = NULL;
@@ -925,15 +1011,42 @@ modest_text_utils_quote_body (GString *output, const gchar *text,
        gsize len;
        gint indent, breakpoint, rem_indent = 0;
        GString *l, *remaining;
+       gchar *forced_wrap_append;
 
        iter = text;
        len = strlen(text);
        remaining = g_string_new ("");
+       forced_wrap_append = NULL;
        do {
-               l = get_next_line (text, len, iter);
-               iter = iter + l->len + 1;
-               indent = get_indent_level (l->str);
-               unquote_line (l, quote_symbol);
+
+               if (forced_wrap_append) {
+                       gint next_line_indent;
+                       gint l_len_with_indent;
+
+                       g_string_erase (remaining, 0, -1);
+                       next_line_indent = get_indent_level (iter);
+                       l = get_next_line (text, len, iter);
+                       l_len_with_indent = l->len;
+                       unquote_line (l, quote_symbol);
+                       if ((l->str && l->str[0] == '\0') || (next_line_indent != indent)) {
+                               g_string_free (l, TRUE);
+                               l = g_string_new (forced_wrap_append);
+                       } else {
+                               gunichar first_in_l;
+                               iter = iter + l_len_with_indent + 1;
+                               first_in_l = g_utf8_get_char_validated (l->str, l->len);
+                               if (!g_unichar_isspace (first_in_l)) 
+                                       l = g_string_prepend (l, " ");
+                               l = g_string_prepend (l, forced_wrap_append);
+                       }
+                       g_free (forced_wrap_append);
+                       forced_wrap_append = NULL;
+               } else {
+                       l = get_next_line (text, len, iter);
+                       iter = iter + l->len + 1;
+                       indent = get_indent_level (l->str);
+                       unquote_line (l, quote_symbol);
+               }
 
                if (remaining->len) {
                        if (l->len && indent == rem_indent) {
@@ -941,17 +1054,26 @@ modest_text_utils_quote_body (GString *output, const gchar *text,
                                g_string_prepend (l, remaining->str);
                        } else {
                                do {
+                                       gunichar remaining_first;
                                        breakpoint =
                                                get_breakpoint (remaining->str,
                                                                rem_indent,
                                                                limit);
-                                       append_quoted (output, quote_symbol, rem_indent,
-                                                      remaining, breakpoint);
+                                       if (breakpoint < remaining->len) {
+                                               g_free (forced_wrap_append);
+                                               forced_wrap_append = g_strdup (remaining->str + breakpoint);
+                                       } else {
+                                               if (!forced_wrap_append)
+                                                       append_quoted (output, quote_symbol, rem_indent,
+                                                                      remaining, breakpoint);
+                                       }
                                        g_string_erase (remaining, 0,
                                                        breakpoint);
-                                       if (remaining->str[0] == ' ') {
-                                               g_string_erase (remaining, 0,
-                                                               1);
+                                       remaining_first = g_utf8_get_char_validated (remaining->str, remaining->len);
+                                       if (remaining_first != ((gunichar) -1)) {
+                                               if (g_unichar_isspace (remaining_first)) {
+                                                       g_string_erase (remaining, 0, g_utf8_next_char (remaining->str) - remaining->str);
+                                               }
                                        }
                                } while (remaining->len);
                        }
@@ -963,9 +1085,13 @@ modest_text_utils_quote_body (GString *output, const gchar *text,
                        g_string_erase (remaining, 0, 1);
                }
                rem_indent = indent;
+               if (remaining->len > 0) {
+                       g_free (forced_wrap_append);
+                       forced_wrap_append = g_strdup (remaining->str);
+               }
                append_quoted (output, quote_symbol, indent, l, breakpoint);
                g_string_free (l, TRUE);
-       } while ((iter < text + len) || (remaining->str[0]));
+       } while ((iter < text + len) || (remaining->str[0]) || forced_wrap_append);
 
        return output;
 }
@@ -1020,18 +1146,19 @@ modest_text_utils_quote_html (const gchar *text,
 {
        GString *result_string;
 
-       result_string = 
+       result_string =
                g_string_new ( \
                              "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" \
                              "<html>\n"                                \
-                             "<body>\n<br/>\n");
+                             "<body>\n");
 
        if (text || cite || signature) {
                GString *quoted_text;
                g_string_append (result_string, "<pre>\n");
                if (signature) {
-                       quote_html_add_to_gstring (result_string, MODEST_TEXT_UTILS_SIGNATURE_MARKER);
-                       quote_html_add_to_gstring (result_string, signature);
+                       gchar *colored_signature = modest_text_utils_create_colored_signature (signature);
+                       g_string_append_printf (result_string, "%s<br/>", colored_signature);
+                       g_free (colored_signature);
                }
                quote_html_add_to_gstring (result_string, cite);
                quoted_text = g_string_new ("");
@@ -1664,6 +1791,7 @@ modest_text_utils_validate_recipient (const gchar *recipient, const gchar **inva
 
        /* quoted string */
        if (*current == '\"') {
+               gchar *last_quote = NULL;
                current = g_utf8_next_char (current);
                has_error = TRUE;
                for (; *current != '\0'; current = g_utf8_next_char (current)) {
@@ -1679,9 +1807,11 @@ modest_text_utils_validate_recipient (const gchar *recipient, const gchar **inva
                        } else if (*current == '\"') {
                                has_error = FALSE;
                                current = g_utf8_next_char (current);
-                               break;
+                               last_quote = current;
                        }
                }
+               if (last_quote)
+                       current = g_utf8_next_char (last_quote);
        } else {
                has_error = TRUE;
                for (current = stripped ; *current != '\0'; current = g_utf8_next_char (current)) {
@@ -1701,6 +1831,9 @@ modest_text_utils_validate_recipient (const gchar *recipient, const gchar **inva
        g_free (stripped);
        right_part = g_strstrip (right_part);
 
+       if (g_str_has_suffix (right_part, ",") || g_str_has_suffix (right_part, ";"))
+               right_part [(strlen (right_part) - 1)] = '\0';
+
        if (g_str_has_prefix (right_part, "<") &&
            g_str_has_suffix (right_part, ">")) {
                gchar *address;
@@ -1726,19 +1859,19 @@ modest_text_utils_get_display_size (guint64 size)
        const guint GB=1024 * MB;
 
        if (size == 0)
-               return g_strdup_printf (_FM("sfil_li_size_kb"), (int) 0);
+               return g_strdup_printf (_FM_SIZE_KB, (int) 0);
        if (0 <= size && size < KB)
-               return g_strdup_printf (_FM("sfil_li_size_1kb_99kb"), (int) 1);
+               return g_strdup_printf (_FM_SIZE_1KB_99KB, (int) 1);
        else if (KB <= size && size < 100 * KB)
-               return g_strdup_printf (_FM("sfil_li_size_1kb_99kb"), (int) size / KB);
+               return g_strdup_printf (_FM_SIZE_1KB_99KB, (int) size / KB);
        else if (100*KB <= size && size < MB)
-               return g_strdup_printf (_FM("sfil_li_size_100kb_1mb"), (int) size / KB);
+               return g_strdup_printf (_FM_SIZE_100KB_1MB, (int) size / KB);
        else if (MB <= size && size < 10*MB)
-               return g_strdup_printf (_FM("sfil_li_size_1mb_10mb"), (float) size / MB);
+               return g_strdup_printf (_FM_SIZE_1MB_10MB, (float) size / MB);
        else if (10*MB <= size && size < GB)
-               return g_strdup_printf (_FM("sfil_li_size_10mb_1gb"), (float) size / MB);
+               return g_strdup_printf (_FM_SIZE_10MB_1GB, (float) size / MB);
        else
-               return g_strdup_printf (_FM("sfil_li_size_1gb_or_greater"), (float) size / GB);
+               return g_strdup_printf (_FM_SIZE_1GB_OR_GREATER, (float) size / GB);
 }
 
 static gchar *
@@ -1902,6 +2035,20 @@ remove_quotes (gchar **quotes)
        }
 }
 
+static void
+remove_extra_spaces (gchar *string)
+{
+       gchar *start;
+
+       start = string;
+       while (start && start[0] != '\0') {
+               if ((start[0] == ' ') && (start[1] == ' ')) {
+                       g_strchug (start+1);
+               }
+               start++;
+       }
+}
+
 gchar *
 modest_text_utils_escape_mnemonics (const gchar *text)
 {
@@ -1939,9 +2086,6 @@ modest_text_utils_simplify_recipients (const gchar *recipients)
                const gchar *address = (const gchar *) node->data;
                gchar *left_limit, *right_limit;
 
-               if (address && strstr(address, "undisclosed-recipients"))
-                       continue;
-
                left_limit = strstr (address, "<");
                right_limit = g_strrstr (address, ">");
 
@@ -2011,3 +2155,213 @@ modest_text_utils_remove_duplicate_addresses_list (GSList *address_list)
 
        return new_list;
 }
+
+gchar *
+modest_text_utils_get_secure_header (const gchar *value,
+                                    const gchar *header)
+{
+       const gint max_len = 16384;
+       gchar *new_value = NULL;
+       gchar *needle = g_strrstr (value, header);
+
+       if (needle && value != needle)
+               new_value = g_strdup (needle + strlen (header));
+
+       if (!new_value)
+               new_value = g_strdup (value);
+
+       /* Do a max length check to prevent DoS attacks caused by huge
+          malformed headers */
+       if (g_utf8_validate (new_value, -1, NULL)) {
+               if (g_utf8_strlen (new_value, -1) > max_len) {
+                       gchar *tmp = g_malloc0 (max_len * 4);
+                       g_utf8_strncpy (tmp, (const gchar *) new_value, max_len);
+                       g_free (new_value);
+                       new_value = tmp;
+               }
+       } else {
+               if (strlen (new_value) > max_len) {
+                       gchar *tmp = g_malloc0 (max_len);
+                       strncpy (new_value, tmp, max_len);
+                       g_free (new_value);
+                       new_value = tmp;
+               }
+       }
+
+       return new_value;
+}
+
+static gboolean
+is_quoted (const char *start, const gchar *end)
+{
+       gchar *c;
+
+       c = (gchar *) start;
+       while (*c == ' ')
+               c = g_utf8_next_char (c);
+
+       if (*c == '\0' || *c != '\"')
+               return FALSE;
+
+       c = (gchar *) end;
+       while (*c == ' ' && c != start)
+               c = g_utf8_prev_char (c);
+
+       if (c == start || *c != '\"')
+               return FALSE;
+
+       return TRUE;
+}
+
+
+static void
+quote_name_part (GString **str, gchar **cur, gchar **start)
+{
+       gchar *blank;
+       gint str_len = *cur - *start;
+
+       while (**start == ' ') {
+               *start = g_utf8_next_char (*start);
+               str_len--;
+       }
+
+       blank = g_utf8_strrchr (*start, str_len, g_utf8_get_char (" "));
+       if (blank && (blank != *start)) {
+               if (is_quoted (*start, blank - 1)) {
+                       *str = g_string_append_len (*str, *start, str_len);
+                       *str = g_string_append (*str, ";");
+                       *start = g_utf8_next_char (*cur);
+               } else {
+                       *str = g_string_append_c (*str, '"');
+                       *str = g_string_append_len (*str, *start,
+                                                   (g_utf8_pointer_to_offset (*start, blank) -
+                                                    g_utf8_pointer_to_offset (*start, *start)));
+                       *str = g_string_append_c (*str, '"');
+                       *str = g_string_append_len (*str, blank,
+                                                   (g_utf8_pointer_to_offset (*start, *cur) -
+                                                    g_utf8_pointer_to_offset (*start, blank)));
+                       *str = g_string_append (*str, ";");
+                       *start = g_utf8_next_char (*cur);
+               }
+       } else {
+               *str = g_string_append_len (*str, *start, str_len);
+               *str = g_string_append (*str, ";");
+               *start = g_utf8_next_char (*cur);
+       }
+}
+
+gchar *
+modest_text_utils_quote_names (const gchar *recipients)
+{
+       GString *str;
+       gchar *start, *cur;
+
+       str = g_string_new ("");
+       start = (gchar*) recipients;
+       cur = (gchar*) recipients;
+
+       for (cur = start; *cur != '\0'; cur = g_utf8_next_char (cur)) {
+               if (*cur == ',' || *cur == ';') {
+                       if (!g_utf8_strchr (start, (cur - start + 1), g_utf8_get_char ("@")))
+                               continue;
+                       quote_name_part (&str, &cur, &start);
+               }
+       }
+
+       quote_name_part (&str, &cur, &start);
+
+       return g_string_free (str, FALSE);
+}
+
+/* Returns TRUE if there is no recipients in the text buffer. Note
+   that strings like " ; , " contain only separators and thus no
+   recipients */
+gboolean
+modest_text_utils_no_recipient (GtkTextBuffer *buffer)
+{
+       gboolean retval = TRUE;
+       gchar *text, *tmp;
+       GtkTextIter start, end;
+
+       if (gtk_text_buffer_get_char_count (buffer) == 0)
+               return TRUE;
+
+       gtk_text_buffer_get_start_iter (buffer, &start);
+       gtk_text_buffer_get_end_iter (buffer, &end);
+
+       text = g_strstrip (gtk_text_buffer_get_text (buffer, &start, &end, FALSE));
+       if (!g_strcmp0 (text, ""))
+               return TRUE;
+
+       tmp = text;
+       while (tmp && *tmp != '\0') {
+               if ((*tmp != ',') && (*tmp != ';') &&
+                   (*tmp != '\r') && (*tmp != '\n') &&
+                   (*tmp != ' ')) {
+                       retval = FALSE;
+                       break;
+               } else {
+                       tmp++;
+               }
+       }
+       g_free (text);
+
+       return retval;
+}
+
+gchar *
+modest_text_utils_create_colored_signature (const gchar *signature)
+{
+       gchar *gray_color_markup = NULL, *retval;
+       GdkColor color;
+       GtkWidget *widget;
+
+       /* Get color from widgets */
+       widget = (GtkWidget *) modest_window_mgr_get_current_top (modest_runtime_get_window_mgr ());
+       if (widget && gtk_style_lookup_color (gtk_widget_get_style (widget), "SecondaryTextColor", &color))
+               gray_color_markup = modest_text_utils_get_color_string (&color);
+
+       retval = g_strdup_printf ("<br/>\n<font color=\"%s\">%s<br/>\n%s<br/>\n</font>",
+                                 (gray_color_markup) ? gray_color_markup : "#babababababa",
+                                 MODEST_TEXT_UTILS_SIGNATURE_MARKER,
+                                 signature);
+
+       if (gray_color_markup)
+               g_free (gray_color_markup);
+
+       return retval;
+}
+
+gboolean
+modest_text_utils_live_search_find (const gchar *haystack, const gchar *needles)
+{
+       gchar *haystack_fold;
+       gchar *needles_fold;
+       gchar **needle, **current;
+       gboolean match;
+
+       match = FALSE;
+
+       haystack_fold = g_utf8_casefold (haystack, -1);
+       needles_fold = g_utf8_casefold (needles, -1);
+
+       needle = g_strsplit (needles_fold, " ", -1);
+
+       current = needle;
+       while (current && *current != NULL) {
+
+               if (g_strstr_len (haystack_fold, -1, *current) != NULL) {
+                       match = TRUE;
+               } else {
+                       match = FALSE;
+                       break;
+               }
+               current++;
+       }
+
+       g_strfreev (needle);
+       g_free (needles_fold);
+       g_free (haystack_fold);
+       
+       return match;
+}