X-Git-Url: http://git.maemo.org/git/?p=modest;a=blobdiff_plain;f=src%2Fmodest-text-utils.c;h=ce015be9d5fa58f8692c4f99290fe77205ad545b;hp=8a83e60856fdc61e566e79beee332eb0595103ae;hb=731fdb1bf7295b056d4fb8f711b35639e83ceafb;hpb=8df610f0be41b018033b2143018f54ff732736eb diff --git a/src/modest-text-utils.c b/src/modest-text-utils.c index 8a83e60..ce015be 100644 --- a/src/modest-text-utils.c +++ b/src/modest-text-utils.c @@ -42,7 +42,7 @@ #include #include #include - +#include #ifdef HAVE_CONFIG_H #include @@ -188,7 +188,7 @@ modest_text_utils_cite (const gchar *text, 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); + retval = modest_text_utils_convert_to_html_body(tmp_sig, -1, TRUE); g_free (tmp_sig); } else { retval = g_strconcat (text, "\n", signature, NULL); @@ -283,7 +283,7 @@ 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); @@ -321,17 +321,17 @@ modest_text_utils_remove_address (const gchar *address_list, const gchar *addres } static void -modest_text_utils_convert_buffer_to_html (GString *html, const gchar *data) +modest_text_utils_convert_buffer_to_html (GString *html, const gchar *data, gssize n) { guint i; gboolean space_seen = FALSE; - gsize len; guint break_dist = 0; /* distance since last break point */ - len = strlen (data); + if (n == -1) + n = strlen (data); /* replace with special html chars where needed*/ - for (i = 0; i != len; ++i) { + for (i = 0; i != n; ++i) { char kar = data[i]; if (space_seen && kar != ' ') { @@ -378,6 +378,8 @@ modest_text_utils_convert_to_html (const gchar *data) { GString *html; gsize len; + + g_return_val_if_fail (data, NULL); if (!data) return NULL; @@ -391,7 +393,7 @@ modest_text_utils_convert_to_html (const gchar *data) "" ""); - modest_text_utils_convert_buffer_to_html (html, data); + modest_text_utils_convert_buffer_to_html (html, data, -1); g_string_append (html, ""); @@ -402,20 +404,22 @@ modest_text_utils_convert_to_html (const gchar *data) } gchar * -modest_text_utils_convert_to_html_body (const gchar *data) +modest_text_utils_convert_to_html_body (const gchar *data, gssize n, gboolean hyperlinkify) { GString *html; - gsize len; - + + g_return_val_if_fail (data, NULL); + if (!data) return NULL; - len = strlen (data); - html = g_string_sized_new (1.5 * len); /* just a guess... */ + if (n == -1) + n = strlen (data); + html = g_string_sized_new (1.5 * n); /* just a guess... */ - modest_text_utils_convert_buffer_to_html (html, data); + modest_text_utils_convert_buffer_to_html (html, data, n); - if (len < HYPERLINKIFY_MAX_LENGTH) + if (hyperlinkify && (n < HYPERLINKIFY_MAX_LENGTH)) hyperlinkify_plain_text (html); return g_string_free (html, FALSE); @@ -524,9 +528,9 @@ modest_text_utils_split_addresses_list (const gchar *addresses) void modest_text_utils_address_range_at_position (const gchar *recipients_list, - gint position, - gint *start, - gint *end) + guint position, + guint *start, + guint *end) { gchar *current = NULL; gint range_start = 0; @@ -534,8 +538,12 @@ modest_text_utils_address_range_at_position (const gchar *recipients_list, gint index; gboolean is_quoted = FALSE; + g_return_if_fail (recipients_list); + g_return_if_fail (position < g_utf8_strlen(recipients_list, -1)); + index = 0; - for (current = (gchar *) recipients_list; *current != '\0'; current = g_utf8_find_next_char (current, NULL)) { + 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)) { @@ -823,12 +831,12 @@ modest_text_utils_quote_html (const gchar *text, if (signature == NULL) signature_result = g_strdup (""); else - signature_result = modest_text_utils_convert_to_html_body (signature); + signature_result = modest_text_utils_convert_to_html_body (signature, -1, TRUE); attachments_string = quoted_attachments (attachments); - q_attachments_string = modest_text_utils_convert_to_html_body (attachments_string); - q_cite = modest_text_utils_convert_to_html_body (cite); - html_text = modest_text_utils_convert_to_html_body (text); + 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); @@ -845,6 +853,52 @@ 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 url_match_pattern_t patterns[] = MAIL_VIEWER_URL_MATCH_PATTERNS; + + +static gboolean +compile_patterns () +{ + guint i; + const size_t pattern_num = sizeof(patterns)/sizeof(url_match_pattern_t); + for (i = 0; i != pattern_num; ++i) { + patterns[i].preg = g_slice_new0 (regex_t); + + /* this should not happen */ + g_return_val_if_fail (regcomp (patterns[i].preg, patterns[i].regex, + REG_ICASE|REG_EXTENDED|REG_NEWLINE) == 0, FALSE); + } + return TRUE; +} + +static void +free_patterns () +{ + guint i; + const size_t pattern_num = sizeof(patterns)/sizeof(url_match_pattern_t); + for (i = 0; i != pattern_num; ++i) { + regfree (patterns[i].preg); + g_slice_free (regex_t, patterns[i].preg); + } /* don't free patterns itself -- it's static */ +} + +void +modest_text_utils_hyperlinkify_begin (void) +{ + if (url_matches_block == 0) + compile_patterns (); + url_matches_block ++; +} + +void +modest_text_utils_hyperlinkify_end (void) +{ + url_matches_block--; + if (url_matches_block <= 0) + free_patterns (); +} + static GSList* get_url_matches (GString *txt) @@ -853,17 +907,11 @@ get_url_matches (GString *txt) guint rv, i, offset = 0; GSList *match_list = NULL; - static url_match_pattern_t patterns[] = MAIL_VIEWER_URL_MATCH_PATTERNS; const size_t pattern_num = sizeof(patterns)/sizeof(url_match_pattern_t); /* initalize the regexps */ - for (i = 0; i != pattern_num; ++i) { - patterns[i].preg = g_slice_new0 (regex_t); + modest_text_utils_hyperlinkify_begin (); - /* this should not happen */ - g_return_val_if_fail (regcomp (patterns[i].preg, patterns[i].regex, - REG_ICASE|REG_EXTENDED|REG_NEWLINE) == 0, NULL); - } /* find all the matches */ for (i = 0; i != pattern_num; ++i) { offset = 0; @@ -904,10 +952,7 @@ get_url_matches (GString *txt) } } - for (i = 0; i != pattern_num; ++i) { - regfree (patterns[i].preg); - g_slice_free (regex_t, patterns[i].preg); - } /* don't free patterns itself -- it's static */ + modest_text_utils_hyperlinkify_end (); /* now sort the list, so the matches are in reverse order of occurence. * that way, we can do the replacements starting from the end, so we don't need @@ -953,42 +998,48 @@ hyperlinkify_plain_text (GString *txt) } - -gchar* +/* for optimization reasons, we change the string in-place */ +void modest_text_utils_get_display_address (gchar *address) { - gchar *cursor; + int i; + + g_return_if_fail (address); if (!address) - return NULL; + return; - g_return_val_if_fail (g_utf8_validate (address, -1, NULL), NULL); + /* should not be needed, and otherwise, we probably won't screw up the address + * more than it already is :) + * g_return_val_if_fail (g_utf8_validate (address, -1, NULL), NULL); + * */ - g_strchug (address); /* remove leading whitespace */ + /* remove leading whitespace */ + if (address[0] == ' ') + g_strchug (address); + + for (i = 0; address[i]; ++i) { + if (address[i] == '<') { + if (G_UNLIKELY(i == 0)) + return; /* there's nothing else, leave it */ + else { + address[i] = '\0'; /* terminate the string here */ + return; + } + } + } +} - /* from display name */ - cursor = g_strstr_len (address, strlen(address), "<"); - if (cursor == address) /* there's nothing else? leave it */ - return address; - if (cursor) - cursor[0]='\0'; - /* remove (bla bla) from display name */ - cursor = g_strstr_len (address, strlen(address), "("); - if (cursor == address) /* there's nothing else? leave it */ - return address; - if (cursor) - cursor[0]='\0'; - g_strchomp (address); /* remove trailing whitespace */ - return address; -} gchar * modest_text_utils_get_email_address (const gchar *full_address) { const gchar *left, *right; + + g_return_val_if_fail (full_address, NULL); if (!full_address) return NULL; @@ -1009,88 +1060,113 @@ modest_text_utils_get_email_address (const gchar *full_address) gint modest_text_utils_get_subject_prefix_len (const gchar *sub) { - gint i; - static const gchar* prefix[] = { - "Re:", "RE:", "RV:", "re:" - "Fwd:", "FWD:", "FW:", "fwd:", "Fw:", "fw:", NULL - }; - - if (!sub || (sub[0] != 'R' && sub[0] != 'F' && sub[0] != 'r' && sub[0] != 'f')) /* optimization */ - return 0; + gint prefix_len = 0; - i = 0; + g_return_val_if_fail (sub, 0); + + if (!sub) + return 0; - while (prefix[i]) { - if (g_str_has_prefix(sub, prefix[i])) { - int prefix_len = strlen(prefix[i]); - if (sub[prefix_len] == ' ') - ++prefix_len; /* ignore space after prefix as well */ - return prefix_len; - } - ++i; + /* optimization: "Re", "RE", "re","Fwd", "FWD", "fwd","FW","Fw", "fw" */ + if (sub[0] != 'R' && sub[0] != 'F' && sub[0] != 'r' && sub[0] != 'f') + return 0; + else if (sub[0] && sub[1] != 'e' && sub[1] != 'E' && sub[1] != 'w' && sub[1] != 'W') + return 0; + + prefix_len = 2; + if (sub[2] == 'd') + ++prefix_len; + + /* skip over a [...] block */ + if (sub[prefix_len] == '[') { + int c = prefix_len + 1; + while (sub[c] && sub[c] != ']') + ++c; + if (sub[c]) + return 0; /* no end to the ']' found */ + else + prefix_len = c + 1; } - return 0; + + /* did we find the ':' ? */ + if (sub[prefix_len] == ':') { + ++prefix_len; + if (sub[prefix_len] == ' ') + ++prefix_len; + prefix_len += modest_text_utils_get_subject_prefix_len (sub + prefix_len); +/* g_warning ("['%s','%s']", sub, (char*) sub + prefix_len); */ + return prefix_len; + } else + return 0; } gint modest_text_utils_utf8_strcmp (const gchar* s1, const gchar *s2, gboolean insensitive) { - gint result = 0; - gchar *n1, *n2; - /* work even when s1 and/or s2 == NULL */ +/* work even when s1 and/or s2 == NULL */ if (G_UNLIKELY(s1 == s2)) return 0; - - /* if it's not case sensitive */ - if (!insensitive) - return strcmp (s1 ? s1 : "", s2 ? s2 : ""); + if (G_UNLIKELY(!s1)) + return -1; + if (G_UNLIKELY(!s2)) + return 1; - n1 = g_utf8_collate_key (s1 ? s1 : "", -1); - n2 = g_utf8_collate_key (s2 ? s2 : "", -1); - - result = strcmp (n1, n2); + /* if it's not case sensitive */ + if (!insensitive) { + + /* optimization: short cut if first char is ascii */ + if (((s1[0] & 0xf0)== 0) && ((s2[0] & 0xf0) == 0)) + return s1[0] - s2[0]; + + return g_utf8_collate (s1, s2); - g_free (n1); - g_free (n2); + } else { + gint result; + gchar *n1, *n2; + + /* optimization: short cut iif first char is ascii */ + if (((s1[0] & 0xf0) == 0) && ((s2[0] & 0xf0) == 0)) + return tolower(s1[0]) - tolower(s2[0]); + + n1 = g_utf8_strdown (s1, -1); + n2 = g_utf8_strdown (s2, -1); + + result = g_utf8_collate (n1, n2); + + g_free (n1); + g_free (n2); - return result; + return result; + } } -gchar* +const gchar* modest_text_utils_get_display_date (time_t date) { - time_t now; - static const guint BUF_SIZE = 64; - static const guint ONE_DAY = 24 * 60 * 60; /* seconds in one day */ - gchar date_buf[BUF_SIZE]; - gchar today_buf [BUF_SIZE]; - - modest_text_utils_strftime (date_buf, BUF_SIZE, "%x", date); +#define DATE_BUF_SIZE 64 + static gchar date_buf[DATE_BUF_SIZE]; + + /* calculate the # of days since epoch for + * for today and for the date provided + * based on idea from pvanhoof */ + int day = time(NULL) / (24 * 60 * 60); + int date_day = date / (24 * 60 * 60); - now = time (NULL); + /* if it's today, show the time, if it's not today, show the date instead */ - /* we check if the date is within the last 24h, if not, we don't - * have to do the extra, expensive strftime, which was very visible - * in the profiles. - */ - if (abs(now - date) < ONE_DAY) { - - /* it's within the last 24 hours, but double check */ - /* use the localized dates */ - modest_text_utils_strftime (today_buf, BUF_SIZE, "%x", now); + 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); - /* if it's today, use the time instead */ - if (strcmp (date_buf, today_buf) == 0) - modest_text_utils_strftime (date_buf, BUF_SIZE, "%X", date); - } - - return g_strdup(date_buf); + return date_buf; /* this is a static buffer, don't free! */ } + gboolean modest_text_utils_validate_folder_name (const gchar *folder_name) { @@ -1131,6 +1207,7 @@ modest_text_utils_validate_folder_name (const gchar *folder_name) return FALSE; } } + return TRUE; /* it's valid! */ } @@ -1143,9 +1220,13 @@ modest_text_utils_validate_domain_name (const gchar *domain) regex_t rx; const gchar* domain_regex = "^[a-z0-9]([.]?[a-z0-9-])*[a-z0-9]$"; + g_return_val_if_fail (domain, FALSE); + if (!domain) return FALSE; + memset (&rx, 0, sizeof(regex_t)); /* coverity wants this... */ + /* domain name: all alphanum or '-' or '.', * but beginning/ending in alphanum */ if (regcomp (&rx, domain_regex, REG_ICASE|REG_EXTENDED|REG_NOSUB)) { @@ -1162,21 +1243,22 @@ modest_text_utils_validate_domain_name (const gchar *domain) gboolean -modest_text_utils_validate_email_address (const gchar *email_address, const gchar **invalid_char_position) +modest_text_utils_validate_email_address (const gchar *email_address, + const gchar **invalid_char_position) { int count = 0; const gchar *c = NULL, *domain = NULL; static gchar *rfc822_specials = "()<>@,;:\\\"[]&"; - - if (invalid_char_position != NULL) + + if (invalid_char_position) *invalid_char_position = NULL; + g_return_val_if_fail (email_address, FALSE); + /* check that the email adress contains exactly one @ */ if (!strstr(email_address, "@") || - (strstr(email_address, "@") != g_strrstr(email_address, "@"))) - { + (strstr(email_address, "@") != g_strrstr(email_address, "@"))) return FALSE; - } /* first we validate the name portion (name@domain) */ for (c = email_address; *c; c++) { @@ -1241,8 +1323,14 @@ modest_text_utils_validate_recipient (const gchar *recipient, const gchar **inva gchar *right_part; gboolean has_error = FALSE; + if (invalid_char_position) + *invalid_char_position = NULL; + + g_return_val_if_fail (recipient, FALSE); + if (modest_text_utils_validate_email_address (recipient, invalid_char_position)) return TRUE; + stripped = g_strdup (recipient); stripped = g_strstrip (stripped); current = stripped; @@ -1348,7 +1436,8 @@ get_email_from_address (const gchar * address) 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, @@ -1365,8 +1454,8 @@ modest_text_utils_text_buffer_get_text (GtkTextBuffer *buffer) gchar *slice, *current; GString *result = g_string_new (""); - g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); - + g_return_val_if_fail (buffer && GTK_IS_TEXT_BUFFER (buffer), NULL); + gtk_text_buffer_get_start_iter (buffer, &start); gtk_text_buffer_get_end_iter (buffer, &end);