X-Git-Url: http://git.maemo.org/git/?p=modest;a=blobdiff_plain;f=src%2Fmodest-text-utils.c;h=51e340ebde976d00b48e2ffeff5790010296a226;hp=df9b846f6837fb495eb7b2582fb2a9358ecdcb10;hb=92bef44ea70f8b4e16c78591bc12f58fac9a7c53;hpb=3e8ecec4aa187f222e95e305f70b5f67ee60d4ce diff --git a/src/modest-text-utils.c b/src/modest-text-utils.c index df9b846..51e340e 100644 --- a/src/modest-text-utils.c +++ b/src/modest-text-utils.c @@ -28,8 +28,14 @@ */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif /*_GNU_SOURCE*/ +#include /* for strcasestr */ + + #include -#include #include #include #include @@ -51,6 +57,14 @@ #define EMPTY_STRING "" /* + * do the hyperlinkification only for texts < 50 Kb, + * as it's quite slow. Without this, e.g. mail with + * an uuencoded part (which is not recognized as attachment, + * will hang modest + */ +#define HYPERLINKIFY_MAX_LENGTH (1024*50) + +/* * we need these regexps to find URLs in plain text e-mails */ typedef struct _url_match_pattern_t url_match_pattern_t; @@ -71,7 +85,7 @@ struct _url_match_t { { "(file|rtsp|http|ftp|https)://[-A-Za-z0-9_$.+!*(),;:@%&=?/~#]+[-A-Za-z0-9_$%&=?/~#]",\ NULL, NULL },\ { "www\\.[-a-z0-9.]+[-a-z0-9](:[0-9]*)?(/[-A-Za-z0-9_$.+!*(),;:@%&=?/~#]*[^]}\\),?!;:\"]?)?",\ - NULL, "http://" },\ + NULL, "http://" }, \ { "ftp\\.[-a-z0-9.]+[-a-z0-9](:[0-9]*)?(/[-A-Za-z0-9_$.+!*(),;:@%&=?/~#]*[^]}\\),?!;:\"]?)?",\ NULL, "ftp://" },\ { "(voipto|callto|chatto|jabberto|xmpp):[-_a-z@0-9.\\+]+", \ @@ -86,7 +100,6 @@ struct _url_match_t { static gchar* cite (const time_t sent_date, const gchar *from); static void hyperlinkify_plain_text (GString *txt); static gint cmp_offsets_reverse (const url_match_t *match1, const url_match_t *match2); -static void chk_partial_match (const url_match_t *match, guint* offset); static GSList* get_url_matches (GString *txt); static GString* get_next_line (const char *b, const gsize blen, const gchar * iter); @@ -140,7 +153,7 @@ modest_text_utils_quote (const gchar *text, retval = modest_text_utils_quote_plain_text (text, cited, signature, attachments, limit); g_free (cited); - + return retval; } @@ -159,19 +172,32 @@ modest_text_utils_cite (const gchar *text, g_return_val_if_fail (content_type, NULL); if (!signature) - tmp_sig = g_strdup (""); + retval = g_strdup (""); else if (!strcmp(content_type, "text/html")) { - tmp_sig = modest_text_utils_convert_to_html_body(signature); + tmp_sig = g_strconcat ("\n", signature, NULL); + retval = modest_text_utils_convert_to_html_body(tmp_sig); + g_free (tmp_sig); } else { - tmp_sig = g_strdup (signature); + retval = g_strconcat ("\n", signature, NULL); } - retval = g_strdup_printf ("\n%s\n", tmp_sig); - g_free (tmp_sig); - return retval; } +static gchar * +forward_cite (const gchar *from, + const gchar *sent, + const gchar *to, + const gchar *subject) +{ + return g_strdup_printf ("%s\n%s %s\n%s %s\n%s %s\n%s %s\n", + FORWARD_STRING, + FROM_STRING, (from)?from:"", + SENT_STRING, sent, + TO_STRING, (to)?to:"", + SUBJECT_STRING, (subject)?subject:""); +} + gchar * modest_text_utils_inline (const gchar *text, const gchar *content_type, @@ -182,47 +208,23 @@ modest_text_utils_inline (const gchar *text, const gchar *subject) { gchar sent_str[101]; - gchar *formatted_signature; - const gchar *plain_format = "%s%s\n%s %s\n%s %s\n%s %s\n%s %s\n\n%s"; - const gchar *html_format = \ - "%s%s
\n\n" \ - "\n" \ - "\n" \ - "\n" \ - "\n" \ - "

%s"; - const gchar *format; - + gchar *cited; + gchar *retval; + g_return_val_if_fail (text, NULL); g_return_val_if_fail (content_type, NULL); - g_return_val_if_fail (text, NULL); modest_text_utils_strftime (sent_str, 100, "%c", sent_date); - if (!strcmp (content_type, "text/html")) - /* TODO: extract the of the HTML and pass it to - the function */ - format = html_format; + cited = forward_cite (from, sent_str, to, subject); + + if (content_type && strcmp (content_type, "text/html") == 0) + retval = modest_text_utils_quote_html (text, cited, signature, NULL, 80); else - format = plain_format; - - if (signature != NULL) { - if (!strcmp (content_type, "text/html")) { - formatted_signature = g_strconcat (signature, "
", NULL); - } else { - formatted_signature = g_strconcat (signature, "\n", NULL); - } - } else { - formatted_signature = ""; - } - - return g_strdup_printf (format, formatted_signature, - FORWARD_STRING, - FROM_STRING, (from) ? from : EMPTY_STRING, - SENT_STRING, sent_str, - TO_STRING, (to) ? to : EMPTY_STRING, - SUBJECT_STRING, (subject) ? subject : EMPTY_STRING, - text); + retval = modest_text_utils_quote_plain_text (text, cited, signature, NULL, 80); + + g_free (cited); + return retval; } /* just to prevent warnings: @@ -246,9 +248,9 @@ modest_text_utils_derived_subject (const gchar *subject, const gchar *prefix) gchar *tmp; g_return_val_if_fail (prefix, NULL); - - if (!subject) - return g_strdup (prefix); + + if (!subject || subject[0] == '\0') + subject = _("mail_va_no_subject"); tmp = g_strchug (g_strdup (subject)); @@ -311,6 +313,7 @@ modest_text_utils_convert_buffer_to_html (GString *html, const gchar *data) guint i; gboolean space_seen = FALSE; gsize len; + guint break_dist = 0; /* distance since last break point */ len = strlen (data); @@ -323,6 +326,15 @@ modest_text_utils_convert_buffer_to_html (GString *html, const gchar *data) space_seen = FALSE; } + /* we artificially insert a breakpoint (newline) + * after 256, to make sure our lines are not so long + * they will DOS the regexping later + */ + if (++break_dist == 256) { + g_string_append_c (html, '\n'); + break_dist = 0; + } + switch (kar) { case 0: break; /* ignore embedded \0s */ case '<' : g_string_append (html, "<"); break; @@ -330,9 +342,10 @@ modest_text_utils_convert_buffer_to_html (GString *html, const gchar *data) case '&' : g_string_append (html, "&"); break; case '"' : g_string_append (html, """); break; case '\'' : g_string_append (html, "'"); break; - case '\n' : g_string_append (html, "
\n"); break; - case '\t' : g_string_append (html, "    "); break; /* note the space at the end*/ + case '\n' : g_string_append (html, "
\n"); break_dist= 0; break; + case '\t' : g_string_append (html, "    "); break_dist=0; break; /* note the space at the end*/ case ' ': + break_dist = 0; if (space_seen) { /* second space in a row */ g_string_append (html, "  "); space_seen = FALSE; @@ -366,7 +379,9 @@ modest_text_utils_convert_to_html (const gchar *data) modest_text_utils_convert_buffer_to_html (html, data); g_string_append (html, ""); - hyperlinkify_plain_text (html); + + if (len <= HYPERLINKIFY_MAX_LENGTH) + hyperlinkify_plain_text (html); return g_string_free (html, FALSE); } @@ -385,7 +400,8 @@ modest_text_utils_convert_to_html_body (const gchar *data) modest_text_utils_convert_buffer_to_html (html, data); - hyperlinkify_plain_text (html); + if (len < HYPERLINKIFY_MAX_LENGTH) + hyperlinkify_plain_text (html); return g_string_free (html, FALSE); } @@ -786,6 +802,7 @@ modest_text_utils_quote_html (const gchar *text, "\n"; gchar *attachments_string = NULL; gchar *q_attachments_string = NULL; + gchar *q_cite = NULL; gchar *html_text = NULL; if (signature == NULL) @@ -795,8 +812,10 @@ modest_text_utils_quote_html (const gchar *text, 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); - result = g_strdup_printf (format, signature_result, cite, html_text, q_attachments_string); + 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); @@ -811,16 +830,6 @@ cmp_offsets_reverse (const url_match_t *match1, const url_match_t *match2) } - -/* - * check if the match is inside an existing match... */ -static void -chk_partial_match (const url_match_t *match, guint* offset) -{ - if (*offset >= match->offset && *offset < match->offset + match->len) - *offset = -1; -} - static GSList* get_url_matches (GString *txt) { @@ -843,28 +852,39 @@ get_url_matches (GString *txt) for (i = 0; i != pattern_num; ++i) { offset = 0; while (1) { - int test_offset; + url_match_t *match; + gboolean is_submatch; + GSList *cursor; + if ((rv = regexec (patterns[i].preg, txt->str + offset, 1, &rm, 0)) != 0) { g_return_val_if_fail (rv == REG_NOMATCH, NULL); /* this should not happen */ break; /* try next regexp */ } if (rm.rm_so == -1) break; - - /* FIXME: optimize this */ - /* to avoid partial matches on something that was already found... */ - /* check_partial_match will put -1 in the data ptr if that is the case */ - test_offset = offset + rm.rm_so; - g_slist_foreach (match_list, (GFunc)chk_partial_match, &test_offset); - /* make a list of our matches ( tupels)*/ - if (test_offset != -1) { - url_match_t *match = g_slice_new (url_match_t); + is_submatch = FALSE; + /* check old matches to see if this has already been matched */ + cursor = match_list; + while (cursor && !is_submatch) { + const url_match_t *old_match = + (const url_match_t *) cursor->data; + guint new_offset = offset + rm.rm_so; + is_submatch = (new_offset > old_match->offset && + new_offset < old_match->offset + old_match->len); + cursor = g_slist_next (cursor); + } + + if (!is_submatch) { + /* make a list of our matches ( tupels)*/ + match = g_slice_new (url_match_t); match->offset = offset + rm.rm_so; match->len = rm.rm_eo - rm.rm_so; match->prefix = patterns[i].prefix; + g_warning ("<%d, %d, %s>", match->offset, match->len, match->prefix); match_list = g_slist_prepend (match_list, match); } + offset += rm.rm_eo; } } @@ -950,7 +970,26 @@ modest_text_utils_get_display_address (gchar *address) return address; } +gchar * +modest_text_utils_get_email_address (const gchar *full_address) +{ + const gchar *left, *right; + + if (!full_address) + return NULL; + + g_return_val_if_fail (g_utf8_validate (full_address, -1, NULL), NULL); + + left = g_strrstr_len (full_address, strlen(full_address), "<"); + if (left == NULL) + return g_strdup (full_address); + + right = g_strstr_len (left, strlen(left), ">"); + if (right == NULL) + return g_strdup (full_address); + return g_strndup (left + 1, right - left - 1); +} gint modest_text_utils_get_subject_prefix_len (const gchar *sub) @@ -1009,30 +1048,123 @@ gchar* modest_text_utils_get_display_date (time_t date) { time_t now; - const guint BUF_SIZE = 64; + static const guint BUF_SIZE = 64; + static const guint ONE_DAY = 24 * 60 * 60; /* seconds in one day */ gchar date_buf[BUF_SIZE]; - gchar now_buf [BUF_SIZE]; - + gchar today_buf [BUF_SIZE]; + + modest_text_utils_strftime (date_buf, BUF_SIZE, "%x", date); + now = time (NULL); - /* use the localized dates */ - modest_text_utils_strftime (date_buf, BUF_SIZE, "%x", date); - modest_text_utils_strftime (now_buf, BUF_SIZE, "%x", now); - - /* if this is today, get the time instead of the date */ - if (strcmp (date_buf, now_buf) == 0) - modest_text_utils_strftime (date_buf, BUF_SIZE, "%X", date); + /* 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 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); } -gboolean -modest_text_utils_validate_email_address (const gchar *email_address) + +gboolean +modest_text_utils_validate_folder_name (const gchar *folder_name) +{ + /* based on http://msdn2.microsoft.com/en-us/library/aa365247.aspx, + * with some extras */ + + guint len; + const gchar **cursor; + const gchar *forbidden_chars[] = { + "<", ">", ":", "\"", "/", "\\", "|", "?", "*", "^", "%", "$", 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 + }; + + /* cannot be NULL */ + if (!folder_name) + return FALSE; + + /* cannot be empty */ + len = strlen(folder_name); + if (len == 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; + + /* cannot contain a forbidden char */ + for (cursor = forbidden_chars; cursor && *cursor; ++cursor) + if (strstr(folder_name, *cursor) != NULL) + return FALSE; + + /* cannot contain a forbidden word */ + if (len <= 4) { + for (cursor = forbidden_names; cursor && *cursor; ++cursor) { + if (g_ascii_strcasecmp (folder_name, *cursor) == 0) + return FALSE; + } + } + return TRUE; /* it's valid! */ +} + + + +gboolean +modest_text_utils_validate_domain_name (const gchar *domain) +{ + gboolean valid = FALSE; + regex_t rx; + const gchar* domain_regex = "^[a-z0-9]([.]?[a-z0-9-])*[a-z0-9]$"; + + if (!domain) + return FALSE; + + /* domain name: all alphanum or '-' or '.', + * but beginning/ending in alphanum */ + if (regcomp (&rx, domain_regex, REG_ICASE|REG_EXTENDED|REG_NOSUB)) { + g_warning ("BUG: error in regexp"); + return FALSE; + } + + valid = (regexec (&rx, domain, 1, NULL, 0) == 0); + regfree (&rx); + + return valid; +} + + + +gboolean +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 = "()<>@,;:\\\"[]"; + static gchar *rfc822_specials = "()<>@,;:\\\"[]&"; + if (invalid_char_position != NULL) + *invalid_char_position = NULL; + + /* check that the email adress contains exactly one @ */ + if (!strstr(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++) { if (*c == '\"' && @@ -1059,8 +1191,11 @@ modest_text_utils_validate_email_address (const gchar *email_address) break; if (*c <= ' ' || *c >= 127) return FALSE; - if (strchr(rfc822_specials, *c)) + if (strchr(rfc822_specials, *c)) { + if (invalid_char_position) + *invalid_char_position = c; return FALSE; + } } if (c == email_address || *(c - 1) == '.') return FALSE; @@ -1070,27 +1205,30 @@ modest_text_utils_validate_email_address (const gchar *email_address) return FALSE; do { if (*c == '.') { - if (c == domain || *(c - 1) == '.') + if (c == domain || *(c - 1) == '.' || *(c + 1) == '\0') return FALSE; count++; } if (*c <= ' ' || *c >= 127) return FALSE; - if (strchr(rfc822_specials, *c)) + if (strchr(rfc822_specials, *c)) { + if (invalid_char_position) + *invalid_char_position = c; return FALSE; + } } while (*++c); return (count >= 1) ? TRUE : FALSE; } gboolean -modest_text_utils_validate_recipient (const gchar *recipient) +modest_text_utils_validate_recipient (const gchar *recipient, const gchar **invalid_char_position) { gchar *stripped, *current; gchar *right_part; gboolean has_error = FALSE; - if (modest_text_utils_validate_email_address (recipient)) + if (modest_text_utils_validate_email_address (recipient, invalid_char_position)) return TRUE; stripped = g_strdup (recipient); stripped = g_strstrip (stripped); @@ -1147,7 +1285,7 @@ modest_text_utils_validate_recipient (const gchar *recipient) address = g_strndup (right_part+1, strlen (right_part) - 2); g_free (right_part); - valid = modest_text_utils_validate_email_address (address); + valid = modest_text_utils_validate_email_address (address, invalid_char_position); g_free (address); return valid; } else { @@ -1193,3 +1331,51 @@ get_email_from_address (const gchar * address) else return g_strndup (left_limit + 1, (right_limit - left_limit) - 1); } + +gchar * +modest_text_utils_get_color_string (GdkColor *color) +{ + + 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, + (color->green >> 12) & 0xf, (color->green >> 8) & 0xf, + (color->green >> 4) & 0xf, (color->green) & 0xf, + (color->blue >> 12) & 0xf, (color->blue >> 8) & 0xf, + (color->blue >> 4) & 0xf, (color->blue) & 0xf); +} + +gchar * +modest_text_utils_text_buffer_get_text (GtkTextBuffer *buffer) +{ + GtkTextIter start, end; + gchar *slice, *current; + GString *result = g_string_new (""); + + g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); + + gtk_text_buffer_get_start_iter (buffer, &start); + gtk_text_buffer_get_end_iter (buffer, &end); + + slice = gtk_text_buffer_get_slice (buffer, &start, &end, FALSE); + current = slice; + + while (current && current != '\0') { + if (g_utf8_get_char (current) == 0xFFFC) { + result = g_string_append_c (result, ' '); + current = g_utf8_next_char (current); + } else { + gchar *next = g_utf8_strchr (current, -1, 0xFFFC); + if (next == NULL) { + result = g_string_append (result, current); + } else { + result = g_string_append_len (result, current, next - current); + } + current = next; + } + } + g_free (slice); + + return g_string_free (result, FALSE); + +}
%s%s
%s%s
%s%s
%s%s