X-Git-Url: http://git.maemo.org/git/?p=modest;a=blobdiff_plain;f=src%2Fmodest-text-utils.c;h=99cb7107410146b5481e8a5c2f5c15ac4d03df15;hp=01e70d3a59b1f438c1b293385c7fff676f1755b2;hb=06546fe50ce8a03df5fe73027873467aa21e6244;hpb=560694ccca949ae2163180050581a9e1dbff0c2a diff --git a/src/modest-text-utils.c b/src/modest-text-utils.c index 01e70d3..99cb710 100644 --- a/src/modest-text-utils.c +++ b/src/modest-text-utils.c @@ -64,8 +64,6 @@ */ #define HYPERLINKIFY_MAX_LENGTH (1024*50) - - /* * we need these regexps to find URLs in plain text e-mails */ @@ -109,14 +107,14 @@ 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 { \ - { "(file|rtsp|http|ftp|https|mms|mmsh|rtsp|rdp|lastfm)://[-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://" }, \ { "ftp\\.[-a-z0-9_$.+!*(),;:@%=?/~#" MARK_AMP_URI_STR "]+[-a-z0-9_$%" MARK_AMP_URI_STR "=?/~#]",\ NULL, "ftp://" },\ - { "(jabberto|voipto|sipto|sip|chatto|xmpp):[-_a-z@0-9.+]+", \ + { "(jabberto|voipto|sipto|sip|chatto|skype|xmpp):[-_a-z@0-9.+]+", \ NULL, NULL}, \ { "mailto:[-_a-z0-9.\\+]+@[-_a-z0-9.]+", \ NULL, NULL},\ @@ -139,14 +137,15 @@ const guint USER_NAME_FORBIDDEN_CHARS_LENGTH = G_N_ELEMENTS (user_name_forbidden /* private */ static gchar* cite (const time_t sent_date, const gchar *from); -static void hyperlinkify_plain_text (GString *txt); +static void hyperlinkify_plain_text (GString *txt, gint offset); static gint cmp_offsets_reverse (const url_match_t *match1, const url_match_t *match2); -static GSList* get_url_matches (GString *txt); +static GSList* get_url_matches (GString *txt, gint offset); 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, +static void unquote_line (GString * l, const gchar *quote_symbol); +static void append_quoted (GString * buf, const gchar *quote_symbol, + 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); @@ -212,14 +211,17 @@ modest_text_utils_cite (const gchar *text, 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") == 0) { - tmp_sig = g_strconcat ("\n", signature, NULL); - retval = modest_text_utils_convert_to_html_body(tmp_sig, -1, TRUE); + 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); } else { - retval = g_strconcat (text, "\n", signature, NULL); + retval = tmp_sig; } return retval; @@ -278,6 +280,13 @@ modest_text_utils_strftime(char *s, gsize max, const char *fmt, time_t timet) { struct tm tm; + /* To prevent possible problems in strftime that could leave + garbage in the s variable */ + if (s) + s[0] = '\0'; + else + return 0; + /* does not work on old maemo glib: * g_date_set_time_t (&date, timet); */ @@ -288,23 +297,84 @@ 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) { - gchar *tmp; + gchar *tmp, *subject_dup, *retval; + gint prefix_len; g_return_val_if_fail (prefix, NULL); if (!subject || subject[0] == '\0') subject = _("mail_va_no_subject"); - tmp = g_strchug (g_strdup (subject)); + subject_dup = g_strdup (subject); + tmp = g_strchug (subject_dup); - if (!strncmp (tmp, prefix, strlen (prefix))) { - return tmp; - } else { - g_free (tmp); - return g_strdup_printf ("%s %s", prefix, subject); + /* 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); + + retval = g_strdup_printf ("%s %s", prefix, tmp); + g_free (subject_dup); + + 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) { @@ -313,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 @@ -356,34 +426,48 @@ modest_text_utils_remove_duplicate_addresses (const gchar *address_list) { GSList *addresses, *cursor; GHashTable *table; - gchar *new_list; + gchar *new_list = NULL; g_return_val_if_fail (address_list, NULL); - table = g_hash_table_new (g_str_hash, g_str_equal); + table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); addresses = modest_text_utils_split_addresses_list (address_list); - new_list = g_strdup(""); cursor = addresses; while (cursor) { const gchar* address = (const gchar*)cursor->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, address) == 0) { - - gchar *tmp = g_strjoin (",", new_list, address, NULL); - g_free (new_list); + if (g_hash_table_lookup (table, email) == 0) { + gchar *tmp; + + /* Include the full address and not only the + email in the returned list */ + if (!new_list) { + tmp = g_strdup (address); + } else { + tmp = g_strjoin (",", new_list, address, NULL); + g_free (new_list); + } new_list = tmp; - g_hash_table_insert (table, (gchar*)address, GINT_TO_POINTER(1)); + g_hash_table_insert (table, (gchar*)email, GINT_TO_POINTER(1)); } cursor = g_slist_next (cursor); } - g_hash_table_destroy (table); + g_hash_table_unref (table); g_slist_foreach (addresses, (GFunc)g_free, NULL); g_slist_free (addresses); + if (new_list == NULL) + new_list = g_strdup (""); + return new_list; } @@ -403,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; } @@ -440,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; @@ -487,7 +570,7 @@ modest_text_utils_convert_to_html (const gchar *data) g_string_append (html, ""); if (len <= HYPERLINKIFY_MAX_LENGTH) - hyperlinkify_plain_text (html); + hyperlinkify_plain_text (html, 0); modest_text_utils_convert_buffer_to_html_finish (html); @@ -511,7 +594,7 @@ modest_text_utils_convert_to_html_body (const gchar *data, gssize n, gboolean hy modest_text_utils_convert_buffer_to_html_start (html, data, n); if (hyperlinkify && (n < HYPERLINKIFY_MAX_LENGTH)) - hyperlinkify_plain_text (html); + hyperlinkify_plain_text (html, 0); modest_text_utils_convert_buffer_to_html_finish (html); @@ -584,6 +667,7 @@ modest_text_utils_split_addresses_list (const gchar *addresses) const gchar *my_addrs = addresses; const gchar *end; gchar *addr; + gboolean after_at = FALSE; g_return_val_if_fail (addresses, NULL); @@ -598,8 +682,20 @@ modest_text_utils_split_addresses_list (const gchar *addresses) /* nope, we are at the start of some address * now, let's find the end of the address */ end = my_addrs + 1; - while (end[0] && end[0] != ',' && end[0] != ';') + while (end[0] && end[0] != ';' && !(after_at && end[0] == ',')) { + if (end[0] == '\"') { + while (end[0] && end[0] != '\"') + ++end; + } + if (end[0] == '@') { + after_at = TRUE; + } + if ((end[0] && end[0] == '>')&&(end[1] && end[1] == ',')) { + ++end; + break; + } ++end; + } /* we got the address; copy it and remove trailing whitespace */ addr = g_strndup (my_addrs, end - my_addrs); @@ -653,6 +749,30 @@ modest_text_utils_address_range_at_position (const gchar *recipients_list, *end = range_end; } +gchar * +modest_text_utils_address_with_standard_length (const gchar *recipients_list) +{ + gchar ** splitted; + gchar ** current; + GString *buffer = g_string_new (""); + + splitted = g_strsplit (recipients_list, "\n", 0); + current = splitted; + while (*current) { + gchar *line; + if (current != splitted) + buffer = g_string_append_c (buffer, '\n'); + line = g_strndup (*splitted, 1000); + buffer = g_string_append (buffer, line); + g_free (line); + current++; + } + + g_strfreev (splitted); + + return g_string_free (buffer, FALSE); +} + /* ******************************************************************* */ /* ************************* UTILIY FUNCTIONS ************************ */ @@ -697,7 +817,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; @@ -705,15 +825,17 @@ get_indent_level (const char *l) } static void -unquote_line (GString * l) +unquote_line (GString * l, const gchar *quote_symbol) { gchar *p; + gint quote_len; p = l->str; + quote_len = strlen (quote_symbol); while (p[0]) { - if (p[0] == '>') { - if (p[1] == ' ') { - p++; + if (g_str_has_prefix (p, quote_symbol)) { + if (p[quote_len] == ' ') { + p += quote_len; } } else { break; @@ -724,15 +846,19 @@ unquote_line (GString * l) } static void -append_quoted (GString * buf, int indent, const GString * str, +append_quoted (GString * buf, const gchar *quote_symbol, + int indent, const GString * str, const int cutpoint) { int i; + gchar *quote_concat; indent = indent < 0 ? abs (indent) - 1 : indent; + quote_concat = g_strconcat (quote_symbol, " ", NULL); for (i = 0; i <= indent; i++) { - g_string_append (buf, "> "); + g_string_append (buf, quote_concat); } + g_free (quote_concat); if (cutpoint > 0) { g_string_append_len (buf, str->str, cutpoint); } else { @@ -821,37 +947,25 @@ quoted_attachments (GList *attachments) } -static gchar * -modest_text_utils_quote_plain_text (const gchar *text, - const gchar *cite, - const gchar *signature, - GList *attachments, - int limit) +static GString * +modest_text_utils_quote_body (GString *output, const gchar *text, + const gchar *quote_symbol, + int limit) { + const gchar *iter; - gint indent, breakpoint, rem_indent = 0; - GString *q, *l, *remaining; gsize len; - gchar *attachments_string = NULL; - - q = g_string_new ("\n"); - if (signature != NULL) { - q = g_string_append (q, signature); - q = g_string_append_c (q, '\n'); - } - q = g_string_append (q, cite); - q = g_string_append_c (q, '\n'); - - /* remaining will store the rest of the line if we have to break it */ - remaining = g_string_new (""); + gint indent, breakpoint, rem_indent = 0; + GString *l, *remaining; iter = text; len = strlen(text); + remaining = g_string_new (""); do { l = get_next_line (text, len, iter); iter = iter + l->len + 1; indent = get_indent_level (l->str); - unquote_line (l); + unquote_line (l, quote_symbol); if (remaining->len) { if (l->len && indent == rem_indent) { @@ -859,17 +973,20 @@ modest_text_utils_quote_plain_text (const gchar *text, g_string_prepend (l, remaining->str); } else { do { + gunichar remaining_first; breakpoint = get_breakpoint (remaining->str, rem_indent, limit); - append_quoted (q, rem_indent, + 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); } @@ -881,10 +998,36 @@ modest_text_utils_quote_plain_text (const gchar *text, g_string_erase (remaining, 0, 1); } rem_indent = indent; - append_quoted (q, indent, l, breakpoint); + append_quoted (output, quote_symbol, indent, l, breakpoint); g_string_free (l, TRUE); } while ((iter < text + len) || (remaining->str[0])); + return output; +} + +static gchar * +modest_text_utils_quote_plain_text (const gchar *text, + const gchar *cite, + const gchar *signature, + GList *attachments, + int limit) +{ + GString *q; + gchar *attachments_string = NULL; + + q = g_string_new (""); + + if (signature != NULL) { + g_string_append_printf (q, "\n%s\n", MODEST_TEXT_UTILS_SIGNATURE_MARKER); + q = g_string_append (q, signature); + } + + q = g_string_append (q, "\n"); + q = g_string_append (q, cite); + q = g_string_append_c (q, '\n'); + + q = modest_text_utils_quote_body (q, text, ">", limit); + attachments_string = quoted_attachments (attachments); q = g_string_append (q, attachments_string); g_free (attachments_string); @@ -892,6 +1035,17 @@ modest_text_utils_quote_plain_text (const gchar *text, return g_string_free (q, FALSE); } +static void +quote_html_add_to_gstring (GString *string, + const gchar *text) +{ + if (text && strcmp (text, "")) { + gchar *html_text = modest_text_utils_convert_to_html_body (text, -1, TRUE); + g_string_append_printf (string, "%s
", html_text); + g_free (html_text); + } +} + static gchar* modest_text_utils_quote_html (const gchar *text, const gchar *cite, @@ -899,38 +1053,37 @@ modest_text_utils_quote_html (const gchar *text, GList *attachments, int limit) { - gchar *result = NULL; - gchar *signature_result = NULL; - const gchar *format = \ - "\n" \ - "\n" \ - "\n" \ - "
%s
" \ - "
%s
%s
%s
\n" \ - "\n" \ - "\n"; - gchar *attachments_string = NULL; - gchar *q_attachments_string = NULL; - gchar *q_cite = NULL; - gchar *html_text = NULL; - - if (signature == NULL) - signature_result = g_strdup (""); - else - signature_result = modest_text_utils_convert_to_html_body (signature, -1, TRUE); + GString *result_string; + + result_string = + g_string_new ( \ + "\n" \ + "\n" \ + "\n
\n"); + + if (text || cite || signature) { + GString *quoted_text; + g_string_append (result_string, "
\n");
+		if (signature) {
+			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);
+		quoted_text = g_string_new ("");
+		quoted_text = modest_text_utils_quote_body (quoted_text, (text) ? text : "", ">", limit);
+		quote_html_add_to_gstring (result_string, quoted_text->str);
+		g_string_free (quoted_text, TRUE);
+		if (attachments) {
+			gchar *attachments_string = quoted_attachments (attachments);
+			quote_html_add_to_gstring (result_string, attachments_string);
+			g_free (attachments_string);
+		}
+		g_string_append (result_string, "
"); + } + g_string_append (result_string, ""); + g_string_append (result_string, ""); - attachments_string = quoted_attachments (attachments); - q_attachments_string = modest_text_utils_convert_to_html_body (attachments_string, -1, TRUE); - q_cite = modest_text_utils_convert_to_html_body (cite, -1, TRUE); - html_text = modest_text_utils_convert_to_html_body (text, -1, TRUE); - result = g_strdup_printf (format, signature_result, q_cite, html_text, q_attachments_string); - g_free (q_cite); - g_free (html_text); - g_free (attachments_string); - g_free (q_attachments_string); - g_free (signature_result); - - return result; + return g_string_free (result_string, FALSE); } static gint @@ -939,8 +1092,9 @@ cmp_offsets_reverse (const url_match_t *match1, const url_match_t *match2) return match2->offset - match1->offset; } -static gboolean url_matches_block = 0; +static gint url_matches_block = 0; static url_match_pattern_t patterns[] = MAIL_VIEWER_URL_MATCH_PATTERNS; +static GMutex *url_patterns_mutex = NULL; static gboolean @@ -975,25 +1129,33 @@ free_patterns () void modest_text_utils_hyperlinkify_begin (void) { + + if (url_patterns_mutex == NULL) { + url_patterns_mutex = g_mutex_new (); + } + g_mutex_lock (url_patterns_mutex); if (url_matches_block == 0) compile_patterns (); url_matches_block ++; + g_mutex_unlock (url_patterns_mutex); } void modest_text_utils_hyperlinkify_end (void) { + g_mutex_lock (url_patterns_mutex); url_matches_block--; if (url_matches_block <= 0) free_patterns (); + g_mutex_unlock (url_patterns_mutex); } static GSList* -get_url_matches (GString *txt) +get_url_matches (GString *txt, gint offset) { regmatch_t rm; - guint rv, i, offset = 0; + guint rv, i, tmp_offset = 0; GSList *match_list = NULL; const size_t pattern_num = sizeof(patterns)/sizeof(url_match_pattern_t); @@ -1003,13 +1165,13 @@ get_url_matches (GString *txt) /* find all the matches */ for (i = 0; i != pattern_num; ++i) { - offset = 0; + tmp_offset = offset; while (1) { url_match_t *match; gboolean is_submatch; GSList *cursor; - if ((rv = regexec (patterns[i].preg, txt->str + offset, 1, &rm, 0)) != 0) { + if ((rv = regexec (patterns[i].preg, txt->str + tmp_offset, 1, &rm, 0)) != 0) { g_return_val_if_fail (rv == REG_NOMATCH, NULL); /* this should not happen */ break; /* try next regexp */ } @@ -1022,7 +1184,7 @@ get_url_matches (GString *txt) while (cursor && !is_submatch) { const url_match_t *old_match = (const url_match_t *) cursor->data; - guint new_offset = offset + rm.rm_so; + guint new_offset = tmp_offset + rm.rm_so; is_submatch = (new_offset > old_match->offset && new_offset < old_match->offset + old_match->len); cursor = g_slist_next (cursor); @@ -1031,12 +1193,12 @@ get_url_matches (GString *txt) if (!is_submatch) { /* make a list of our matches ( tupels)*/ match = g_slice_new (url_match_t); - match->offset = offset + rm.rm_so; + match->offset = tmp_offset + rm.rm_so; match->len = rm.rm_eo - rm.rm_so; match->prefix = patterns[i].prefix; match_list = g_slist_prepend (match_list, match); } - offset += rm.rm_eo; + tmp_offset += rm.rm_eo; } } @@ -1077,10 +1239,10 @@ replace_string (const gchar *haystack, const gchar *needle, gchar repl) } static void -hyperlinkify_plain_text (GString *txt) +hyperlinkify_plain_text (GString *txt, gint offset) { GSList *cursor; - GSList *match_list = get_url_matches (txt); + GSList *match_list = get_url_matches (txt, offset); /* we will work backwards, so the offsets stay valid */ for (cursor = match_list; cursor; cursor = cursor->next) { @@ -1116,6 +1278,18 @@ hyperlinkify_plain_text (GString *txt) g_slist_free (match_list); } +void +modest_text_utils_hyperlinkify (GString *string_buffer) +{ + gchar *after_body; + gint offset = 0; + + after_body = strstr (string_buffer->str, ""); + if (after_body != NULL) + offset = after_body - string_buffer->str; + hyperlinkify_plain_text (string_buffer, offset); +} + /* for optimization reasons, we change the string in-place */ void @@ -1139,18 +1313,50 @@ modest_text_utils_get_display_address (gchar *address) for (i = 0; address[i]; ++i) { if (address[i] == '<') { - if (G_UNLIKELY(i == 0)) - return; /* there's nothing else, leave it */ - else { + if (G_UNLIKELY(i == 0)) { + break; /* there's nothing else, leave it */ + }else { address[i] = '\0'; /* terminate the string here */ - return; + break; } } } + + g_strchomp (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 * @@ -1201,7 +1407,7 @@ modest_text_utils_get_subject_prefix_len (const gchar *sub) int c = prefix_len + 1; while (sub[c] && sub[c] != ']') ++c; - if (sub[c]) + if (!sub[c]) return 0; /* no end to the ']' found */ else prefix_len = c + 1; @@ -1236,7 +1442,8 @@ modest_text_utils_utf8_strcmp (const gchar* s1, const gchar *s2, gboolean insens if (!insensitive) { /* optimization: shortcut if first char is ascii */ - if (((s1[0] & 0xf0)== 0) && ((s2[0] & 0xf0) == 0)) + if (((s1[0] & 0x80)== 0) && ((s2[0] & 0x80) == 0) && + (s1[0] != s2[0])) return s1[0] - s2[0]; return g_utf8_collate (s1, s2); @@ -1245,8 +1452,9 @@ modest_text_utils_utf8_strcmp (const gchar* s1, const gchar *s2, gboolean insens gint result; gchar *n1, *n2; - /* optimization: short cut iif first char is ascii */ - if (((s1[0] & 0xf0) == 0) && ((s2[0] & 0xf0) == 0)) + /* optimization: shortcut if first char is ascii */ + if (((s1[0] & 0x80) == 0) && ((s2[0] & 0x80) == 0) && + (tolower(s1[0]) != tolower (s2[0]))) return tolower(s1[0]) - tolower(s2[0]); n1 = g_utf8_strdown (s1, -1); @@ -1276,10 +1484,18 @@ modest_text_utils_get_display_date (time_t date) /* if it's today, show the time, if it's not today, show the date instead */ + /* TODO: take into account the system config for 24/12h */ +#ifdef MODEST_TOOLKIT_HILDON2 + if (day == date_day) /* is the date today? */ + modest_text_utils_strftime (date_buf, DATE_BUF_SIZE, _HL("wdgt_va_24h_time"), date); + else + modest_text_utils_strftime (date_buf, DATE_BUF_SIZE, _HL("wdgt_va_date"), date); +#else if (day == date_day) /* is the date today? */ modest_text_utils_strftime (date_buf, DATE_BUF_SIZE, "%X", date); else modest_text_utils_strftime (date_buf, DATE_BUF_SIZE, "%x", date); +#endif return date_buf; /* this is a static buffer, don't free! */ } @@ -1296,9 +1512,8 @@ modest_text_utils_validate_folder_name (const gchar *folder_name) gint i; const gchar **cursor = NULL; const gchar *forbidden_names[] = { /* windows does not like these */ - "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", - "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", - ".", "..", NULL + "CON", "PRN", "AUX", "NUL", ".", "..", "cur", "tmp", "new", + NULL /* cur, tmp, new are reserved for Maildir */ }; /* cannot be NULL */ @@ -1310,6 +1525,10 @@ modest_text_utils_validate_folder_name (const gchar *folder_name) if (len == 0) return FALSE; + /* cannot start with a dot, vfat does not seem to like that */ + if (folder_name[0] == '.') + return FALSE; + /* cannot start or end with a space */ if (g_ascii_isspace(folder_name[0]) || g_ascii_isspace(folder_name[len - 1])) return FALSE; @@ -1318,6 +1537,25 @@ modest_text_utils_validate_folder_name (const gchar *folder_name) for (i = 0; i < len; i++) if (modest_text_utils_is_forbidden_char (folder_name[i], FOLDER_NAME_FORBIDDEN_CHARS)) return FALSE; + + /* Cannot contain Windows port numbers. I'd like to use GRegex + but it's still not available in Maemo. sergio */ + if (!g_ascii_strncasecmp (folder_name, "LPT", 3) || + !g_ascii_strncasecmp (folder_name, "COM", 3)) { + glong val; + gchar *endptr; + + /* We skip the first 3 characters for the + comparison */ + val = strtol(folder_name+3, &endptr, 10); + + /* If the conversion to long succeeded then the string + is not valid for us */ + if (*endptr == '\0') + return FALSE; + else + return TRUE; + } /* cannot contain a forbidden word */ if (len <= 4) { @@ -1523,19 +1761,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"), 0); - if (0 < size && size < KB) - return g_strdup_printf (_FM("sfil_li_size_kb"), 1); + return g_strdup_printf (_FM("sfil_li_size_kb"), (int) 0); + if (0 <= size && size < KB) + return g_strdup_printf (_FM("sfil_li_size_1kb_99kb"), (int) 1); else if (KB <= size && size < 100 * KB) - return g_strdup_printf (_FM("sfil_li_size_1kb_99kb"), size / 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"), 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); + return g_strdup_printf (_FM("sfil_li_size_1gb_or_greater"), (float) size / GB); } static gchar * @@ -1552,11 +1790,11 @@ get_email_from_address (const gchar * address) return g_strndup (left_limit + 1, (right_limit - left_limit) - 1); } -gchar * +gchar * modest_text_utils_get_color_string (GdkColor *color) { g_return_val_if_fail (color, NULL); - + return g_strdup_printf ("#%x%x%x%x%x%x%x%x%x%x%x%x", (color->red >> 12) & 0xf, (color->red >> 8) & 0xf, (color->red >> 4) & 0xf, (color->red) & 0xf, @@ -1688,6 +1926,17 @@ modest_text_utils_buffer_selection_is_valid (GtkTextBuffer *buffer) return result; } +static void +remove_quotes (gchar **quotes) +{ + if (g_str_has_prefix (*quotes, "\"") && g_str_has_suffix (*quotes, "\"")) { + gchar *result; + result = g_strndup ((*quotes)+1, strlen (*quotes) - 2); + g_free (*quotes); + *quotes = result; + } +} + gchar * modest_text_utils_escape_mnemonics (const gchar *text) { @@ -1707,3 +1956,125 @@ modest_text_utils_escape_mnemonics (const gchar *text) return g_string_free (result, FALSE); } + +gchar * +modest_text_utils_simplify_recipients (const gchar *recipients) +{ + GSList *addresses, *node; + GString *result; + gboolean is_first = TRUE; + + if (recipients == NULL) + return g_strdup (""); + + addresses = modest_text_utils_split_addresses_list (recipients); + result = g_string_new (""); + + 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, ">"); + + if (is_first) + is_first = FALSE; + else + result = g_string_append (result, ", "); + + if ((left_limit == NULL)||(right_limit == NULL)|| (left_limit > right_limit)) { + result = g_string_append (result, address); + } else { + gchar *name_side; + gchar *email_side; + name_side = g_strndup (address, left_limit - address); + name_side = g_strstrip (name_side); + remove_quotes (&name_side); + email_side = get_email_from_address (address); + if (name_side && email_side && !strcmp (name_side, email_side)) { + result = g_string_append (result, email_side); + } else { + result = g_string_append (result, address); + } + g_free (name_side); + g_free (email_side); + } + + } + g_slist_foreach (addresses, (GFunc)g_free, NULL); + g_slist_free (addresses); + + 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; +}