X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=src%2Fmodest-text-utils.c;h=89dcb919e9423c2353eeec160535e7bbe31ef6a3;hb=7751b2edd7406c961e3972e05121cd6fac267d76;hp=6bba7f153e0f5509ebd4055b16517cedc1d5241a;hpb=d8cca2763d3a9c9e46e827321a83bd26c40871e3;p=modest diff --git a/src/modest-text-utils.c b/src/modest-text-utils.c index 6bba7f1..89dcb91 100644 --- a/src/modest-text-utils.c +++ b/src/modest-text-utils.c @@ -63,8 +63,6 @@ * will hang modest */ #define HYPERLINKIFY_MAX_LENGTH (1024*50) -#define SIGNATURE_MARKER "--" - /* * we need these regexps to find URLs in plain text e-mails @@ -109,8 +107,8 @@ struct _url_match_t { /* note: match MARK_AMP_URI_STR as well, because after txt->html, a '&' will look like $(MARK_AMP_URI_STR)"amp;" */ #define MAIL_VIEWER_URL_MATCH_PATTERNS { \ - { "(feed:|)(file|rtsp|http|ftp|https|mms|mmsh|webcal|feed|rtsp|rdp|lastfm|sip)://[-a-z0-9_$.+!*(),;:@%=?/~#" MARK_AMP_URI_STR \ - "]+[-a-z0-9_$%" MARK_AMP_URI_STR "=?/~#]", \ + { "(feed:|)(file|rtsp|http|ftp|https|mms|mmsh|webcal|feed|rtsp|rdp|lastfm|sip)://[-a-z0-9_$.+!*(),;:@%=\?/~#&" MARK_AMP_URI_STR \ + "]+[-a-z0-9_$%&" MARK_AMP_URI_STR "=?/~#]", \ NULL, NULL },\ { "www\\.[-a-z0-9_$.+!*(),;:@%=?/~#" MARK_AMP_URI_STR "]+[-a-z0-9_$%" MARK_AMP_URI_STR "=?/~#]",\ NULL, "http://" }, \ @@ -216,7 +214,7 @@ modest_text_utils_cite (const gchar *text, if (!signature) { tmp_sig = g_strdup (text); } else { - tmp_sig = g_strconcat (text, "\n", SIGNATURE_MARKER, "\n", signature, NULL); + tmp_sig = g_strconcat (text, "\n", MODEST_TEXT_UTILS_SIGNATURE_MARKER, "\n", signature, NULL); } if (strcmp (content_type, "text/html") == 0) { @@ -318,7 +316,23 @@ modest_text_utils_derived_subject (const gchar *subject, const gchar *prefix) tmp += prefix_len; tmp = g_strchug (tmp); } else { - break; + 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); @@ -328,6 +342,39 @@ modest_text_utils_derived_subject (const gchar *subject, const gchar *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) { @@ -336,26 +383,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 @@ -418,6 +465,9 @@ modest_text_utils_remove_duplicate_addresses (const gchar *address_list) g_slist_foreach (addresses, (GFunc)g_free, NULL); g_slist_free (addresses); + if (new_list == NULL) + new_list = g_strdup (""); + return new_list; } @@ -437,7 +487,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_c (html, ' '); + g_string_append (html, " "); space_seen = FALSE; } @@ -474,7 +524,6 @@ modest_text_utils_convert_buffer_to_html_start (GString *html, const gchar *data break_dist = 0; if (space_seen) { /* second space in a row */ g_string_append (html, " "); - space_seen = FALSE; } else space_seen = TRUE; break; @@ -555,59 +604,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; + + if (strlen (addresses) == 0) + return; - start = (gchar *) addresses; - current = start; - last_blank = start; + str = g_string_new (""); + start = (gchar*) addresses; + cur = (gchar*) addresses; - while (*current != '\0') { - if ((start == current)&&((*current == ' ')||(*current == ',')||(*current == ';'))) { - start = g_utf8_next_char (start); - start_offset++; - last_blank = current; - } else if ((*current == ',')||(*current == ';')) { + 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') + 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); } @@ -768,7 +810,7 @@ get_indent_level (const char *l) /* if we hit the signature marker "-- ", we return -(indent + 1). This * stops reformatting. */ - if (strcmp (l, "-- ") == 0) { + if (strcmp (l, MODEST_TEXT_UTILS_SIGNATURE_MARKER) == 0) { return -1 - indent; } else { return indent; @@ -924,6 +966,7 @@ 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, @@ -932,9 +975,11 @@ modest_text_utils_quote_body (GString *output, const gchar *text, 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); } @@ -966,7 +1011,7 @@ modest_text_utils_quote_plain_text (const gchar *text, q = g_string_new (""); if (signature != NULL) { - q = g_string_append (q, "\n--\n"); + g_string_append_printf (q, "\n%s\n", MODEST_TEXT_UTILS_SIGNATURE_MARKER); q = g_string_append (q, signature); } @@ -1013,7 +1058,7 @@ modest_text_utils_quote_html (const gchar *text, GString *quoted_text; g_string_append (result_string, "
\n"); if (signature) { - quote_html_add_to_gstring (result_string, SIGNATURE_MARKER); + quote_html_add_to_gstring (result_string, MODEST_TEXT_UTILS_SIGNATURE_MARKER); quote_html_add_to_gstring (result_string, signature); } quote_html_add_to_gstring (result_string, cite); @@ -1274,7 +1319,37 @@ modest_text_utils_get_display_address (gchar *address) } +gchar * +modest_text_utils_get_display_addresses (const gchar *recipients) +{ + gchar *addresses; + GSList *recipient_list; + + addresses = NULL; + recipient_list = modest_text_utils_split_addresses_list (recipients); + if (recipient_list) { + GString *add_string = g_string_sized_new (strlen (recipients)); + GSList *iter = recipient_list; + gboolean first = TRUE; + + while (iter) { + /* Strings are changed in place */ + modest_text_utils_get_display_address ((gchar *) iter->data); + if (G_UNLIKELY (first)) { + g_string_append_printf (add_string, "%s", (gchar *) iter->data); + first = FALSE; + } else { + g_string_append_printf (add_string, ", %s", (gchar *) iter->data); + } + iter = g_slist_next (iter); + } + g_slist_foreach (recipient_list, (GFunc) g_free, NULL); + g_slist_free (recipient_list); + addresses = g_string_free (add_string, FALSE); + } + return addresses; +} gchar * @@ -1617,6 +1692,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)) { @@ -1632,9 +1708,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)) { @@ -1654,6 +1732,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; @@ -1685,11 +1766,11 @@ modest_text_utils_get_display_size (guint64 size) else if (KB <= size && size < 100 * KB) return g_strdup_printf (_FM("sfil_li_size_1kb_99kb"), (int) size / KB); else if (100*KB <= size && size < MB) - return g_strdup_printf (_FM("sfil_li_size_100kb_1mb"), (float) size / MB); + return g_strdup_printf (_FM("sfil_li_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); else if (10*MB <= size && size < GB) - return g_strdup_printf (_FM("sfil_li_size_10mb_1gb"), (int) size / MB); + return g_strdup_printf (_FM("sfil_li_size_10mb_1gb"), (float) size / MB); else return g_strdup_printf (_FM("sfil_li_size_1gb_or_greater"), (float) size / GB); } @@ -1891,6 +1972,7 @@ modest_text_utils_simplify_recipients (const gchar *recipients) for (node = addresses; node != NULL; node = g_slist_next (node)) { const gchar *address = (const gchar *) node->data; gchar *left_limit, *right_limit; + left_limit = strstr (address, "<"); right_limit = g_strrstr (address, ">"); @@ -1924,3 +2006,157 @@ modest_text_utils_simplify_recipients (const gchar *recipients) return g_string_free (result, FALSE); } + +GSList * +modest_text_utils_remove_duplicate_addresses_list (GSList *address_list) +{ + GSList *new_list, *iter; + GHashTable *table; + + g_return_val_if_fail (address_list, NULL); + + table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + new_list = address_list; + iter = address_list; + while (iter) { + const gchar* address = (const gchar*)iter->data; + + /* We need only the email to just compare it and not + the full address which would make "a " + different from "a@a.com" */ + const gchar *email = get_email_from_address (address); + + /* ignore the address if already seen */ + if (g_hash_table_lookup (table, email) == 0) { + g_hash_table_insert (table, (gchar*)email, GINT_TO_POINTER(1)); + iter = g_slist_next (iter); + } else { + GSList *tmp = g_slist_next (iter); + new_list = g_slist_delete_link (new_list, iter); + iter = tmp; + } + } + + g_hash_table_unref (table); + + return new_list; +} + +gchar * +modest_text_utils_get_secure_header (const gchar *value, + const gchar *header) +{ + const gint max_len = 128; + 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 = g_utf8_pointer_to_offset (*start, *cur) - + g_utf8_pointer_to_offset (*start, *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); +}