X-Git-Url: http://git.maemo.org/git/?p=modest;a=blobdiff_plain;f=src%2Fmodest-text-utils.c;h=8deb5cc2b29abb5de54f1a9da66af22077d73c83;hp=3a89582bdb87680172e9a296043eac068afca844;hb=b82646213f0d683e64a8a3b115d69fb0d243cd2e;hpb=a3524da28501507a8c5dc8319bd62d61448edf46 diff --git a/src/modest-text-utils.c b/src/modest-text-utils.c index 3a89582..8deb5cc 100644 --- a/src/modest-text-utils.c +++ b/src/modest-text-utils.c @@ -65,13 +65,6 @@ #define HYPERLINKIFY_MAX_LENGTH (1024*50) -/* - * we mark the ampersand with \007 when converting text->html - * because after text->html we do hyperlink detecting, which - * could be screwed up by the ampersand - */ -#define MARK_AMP '\007' -#define MARK_AMP_STR "\007" /* * we need these regexps to find URLs in plain text e-mails @@ -90,19 +83,44 @@ struct _url_match_t { const gchar* prefix; }; -/* note: match MARK_AMP_STR as well, because after txt->html, a '&' will look like $(MARK_AMP_STR)"amp;" */ + +/* + * we mark the ampersand with \007 when converting text->html + * because after text->html we do hyperlink detecting, which + * could be screwed up by the ampersand. + * ie. 1<3 ==> 1\007lt;3 + */ +#define MARK_AMP '\007' +#define MARK_AMP_STR "\007" + +/* mark & separately, because they are parts of urls. + * ie. a&b => a\006amp;b, but a>b => a\007gt;b + * + * we need to handle '&' separately, because it can be part of URIs + * (as in href="http://foo.bar?a=1&b=1"), so inside those URIs + * we need to re-replace \006amp; with '&' again, while outside uri's + * it will be '&' + * + * yes, it's messy, but a consequence of doing text->html first, then hyperlinkify + */ +#define MARK_AMP_URI '\006' +#define MARK_AMP_URI_STR "\006" + + +/* 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)://[-a-z0-9_$.+!*(),;:@%=?/~#" MARK_AMP_STR "]+[-a-z0-9_$%" MARK_AMP_STR "=?/~#]",\ + { "(file|rtsp|http|ftp|https|mms|mmsh|rtsp|rdp|lastfm)://[-a-z0-9_$.+!*(),;:@%=?/~#" MARK_AMP_URI_STR \ + "]+[-a-z0-9_$%" MARK_AMP_URI_STR "=?/~#]", \ NULL, NULL },\ - { "^www\\.[-a-z0-9_$.+!*(),;:@%=?/~#" MARK_AMP_STR "]+[-a-z0-9_$%" MARK_AMP_STR "=?/~#]",\ + { "www\\.[-a-z0-9_$.+!*(),;:@%=?/~#" MARK_AMP_URI_STR "]+[-a-z0-9_$%" MARK_AMP_URI_STR "=?/~#]",\ NULL, "http://" }, \ - { "^ftp\\.[-a-z0-9_$.+!*(),;:@%=?/~#" MARK_AMP_STR "]+[-a-z0-9_$%" MARK_AMP_STR "=?/~#]",\ + { "ftp\\.[-a-z0-9_$.+!*(),;:@%=?/~#" MARK_AMP_URI_STR "]+[-a-z0-9_$%" MARK_AMP_URI_STR "=?/~#]",\ NULL, "ftp://" },\ - { "^(voipto|callto|chatto|jabberto|xmpp):[-_a-z@0-9.+]+", \ + { "(jabberto|voipto|sipto|sip|chatto|xmpp):[-_a-z@0-9.+]+", \ NULL, NULL}, \ - { "^mailto:[-_a-z0-9.\\+]+@[-_a-z0-9.]+", \ + { "mailto:[-_a-z0-9.\\+]+@[-_a-z0-9.]+", \ NULL, NULL},\ - { "^[-_a-z0-9.\\+]+@[-_a-z0-9.]+",\ + { "[-_a-z0-9.\\+]+@[-_a-z0-9.]+",\ NULL, "mailto:"}\ } @@ -121,9 +139,9 @@ 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); @@ -213,6 +231,8 @@ forward_cite (const gchar *from, const gchar *to, const gchar *subject) { + g_return_val_if_fail (sent, NULL); + return g_strdup_printf ("%s\n%s %s\n%s %s\n%s %s\n%s %s\n", FORWARD_STRING, FROM_STRING, (from)?from:"", @@ -336,23 +356,27 @@ 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); addresses = modest_text_utils_split_addresses_list (address_list); - new_list = g_strdup(""); cursor = addresses; while (cursor) { const gchar* address = (const gchar*)cursor->data; /* 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); + gchar *tmp; + + 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)); @@ -380,7 +404,7 @@ modest_text_utils_convert_buffer_to_html_start (GString *html, const gchar *data /* replace with special html chars where needed*/ for (i = 0; i != n; ++i) { - char kar = data[i]; + guchar kar = data[i]; if (space_seen && kar != ' ') { g_string_append_c (html, ' '); @@ -390,21 +414,25 @@ modest_text_utils_convert_buffer_to_html_start (GString *html, const gchar *data /* we artificially insert a breakpoint (newline) * after 256, to make sure our lines are not so long * they will DOS the regexping later + * Also, check that kar is ASCII to make sure that we + * don't break a UTF8 char in two */ - if (++break_dist == 256) { + if (++break_dist >= 256 && kar < 127) { g_string_append_c (html, '\n'); break_dist = 0; } switch (kar) { case 0: - case MARK_AMP: /* this is a temp place holder for '&'; we can only + case MARK_AMP: + case MARK_AMP_URI: + /* this is a temp place holder for '&'; we can only * set the real '&' after hyperlink translation, otherwise * we might screw that up */ break; /* ignore embedded \0s and MARK_AMP */ case '<' : g_string_append (html, MARK_AMP_STR "lt;"); break; case '>' : g_string_append (html, MARK_AMP_STR "gt;"); break; - case '&' : g_string_append (html, MARK_AMP_STR "amp;"); break; + case '&' : g_string_append (html, MARK_AMP_URI_STR "amp;"); break; /* special case */ case '"' : g_string_append (html, MARK_AMP_STR "quot;"); break; /* don't convert ' --> wpeditor will try to re-convert it... */ @@ -433,7 +461,7 @@ modest_text_utils_convert_buffer_to_html_finish (GString *html) int i; /* replace all our MARK_AMPs with real ones */ for (i = 0; i != html->len; ++i) - if ((html->str)[i] == MARK_AMP) + if ((html->str)[i] == MARK_AMP || (html->str)[i] == MARK_AMP_URI) (html->str)[i] = '&'; } @@ -463,7 +491,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); @@ -487,7 +515,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); @@ -552,53 +580,6 @@ modest_text_utils_get_addresses_indexes (const gchar *addresses, GSList **start_ return; } -#if 0 -GSList * -modest_text_utils_split_addresses_list (const gchar *addresses) -{ - gchar *current, *start, *last_blank; - GSList *result = NULL; - - start = (gchar *) addresses; - current = start; - last_blank = start; - - while (*current != '\0') { - if ((start == current)&&((*current == ' ')||(*current == ',')||(*current == ';'))) { - start = g_utf8_next_char (start); - last_blank = current; - } else if ((*current == ',')||(*current == ';')) { - gchar *new_address = NULL; - new_address = g_strndup (start, current - last_blank); - result = g_slist_prepend (result, new_address); - start = g_utf8_next_char (current); - last_blank = start; - } else if (*current == '\"') { - if (current == start) { - current = g_utf8_next_char (current); - start = g_utf8_next_char (start); - } - while ((*current != '\"')&&(*current != '\0')) - current = g_utf8_next_char (current); - } - - current = g_utf8_next_char (current); - } - - if (start != current) { - gchar *new_address = NULL; - new_address = g_strndup (start, current - last_blank); - result = g_slist_prepend (result, new_address); - } - - result = g_slist_reverse (result); - return result; - -} -#endif - - - GSList * modest_text_utils_split_addresses_list (const gchar *addresses) @@ -607,6 +588,8 @@ modest_text_utils_split_addresses_list (const gchar *addresses) const gchar *my_addrs = addresses; const gchar *end; gchar *addr; + + 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] == ';')) @@ -674,6 +657,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 ************************ */ @@ -855,10 +862,15 @@ modest_text_utils_quote_plain_text (const gchar *text, gsize len; gchar *attachments_string = NULL; - /* remaining will store the rest of the line if we have to break it */ 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 (""); iter = text; @@ -905,11 +917,6 @@ modest_text_utils_quote_plain_text (const gchar *text, q = g_string_append (q, attachments_string); g_free (attachments_string); - if (signature != NULL) { - q = g_string_append_c (q, '\n'); - q = g_string_append (q, signature); - } - return g_string_free (q, FALSE); } @@ -1011,10 +1018,10 @@ modest_text_utils_hyperlinkify_end (void) 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); @@ -1024,13 +1031,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 */ } @@ -1043,7 +1050,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); @@ -1052,12 +1059,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; } } @@ -1098,10 +1105,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) { @@ -1110,12 +1117,12 @@ hyperlinkify_plain_text (GString *txt) gchar *url = g_strndup (txt->str + match->offset, match->len); gchar *repl = NULL; /* replacement */ - /* the string still contains $(MARK_AMP_STR)"amp;" for each + /* the string still contains $(MARK_AMP_URI_STR)"amp;" for each * '&' in the original, because of the text->html conversion. * in the href-URL (and only there), we must convert that back to * '&' */ - gchar *href_url = replace_string (url, MARK_AMP_STR "amp;", '&'); + gchar *href_url = replace_string (url, MARK_AMP_URI_STR "amp;", '&'); /* the prefix is NULL: use the one that is already there */ repl = g_strdup_printf ("%s", @@ -1137,6 +1144,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 @@ -1256,7 +1275,7 @@ modest_text_utils_utf8_strcmp (const gchar* s1, const gchar *s2, gboolean insens /* if it's not case sensitive */ if (!insensitive) { - /* optimization: short cut if first char is ascii */ + /* optimization: shortcut if first char is ascii */ if (((s1[0] & 0xf0)== 0) && ((s2[0] & 0xf0) == 0)) return s1[0] - s2[0]; @@ -1319,7 +1338,7 @@ modest_text_utils_validate_folder_name (const gchar *folder_name) 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 + ".", "..", "cur", "tmp", "new", NULL /* cur, tmp new are reserved for Maildir */ }; /* cannot be NULL */ @@ -1331,6 +1350,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; @@ -1654,3 +1677,77 @@ modest_text_utils_is_forbidden_char (const gchar character, return FALSE; /* it's valid! */ } + +gchar * +modest_text_utils_label_get_selection (GtkLabel *label) +{ + gint start, end; + gchar *selection; + + if (gtk_label_get_selection_bounds (GTK_LABEL (label), &start, &end)) { + const gchar *start_offset; + const gchar *end_offset; + start_offset = gtk_label_get_text (GTK_LABEL (label)); + start_offset = g_utf8_offset_to_pointer (start_offset, start); + end_offset = gtk_label_get_text (GTK_LABEL (label)); + end_offset = g_utf8_offset_to_pointer (end_offset, end); + selection = g_strndup (start_offset, end_offset - start_offset); + return selection; + } else { + return g_strdup (""); + } +} + +static gboolean +_forward_search_image_char (gunichar ch, + gpointer userdata) +{ + return (ch == 0xFFFC); +} + +gboolean +modest_text_utils_buffer_selection_is_valid (GtkTextBuffer *buffer) +{ + gboolean result; + GtkTextIter start, end; + + g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE); + + result = gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (buffer)); + + /* check there are no images in selection */ + if (result) { + gtk_text_buffer_get_selection_bounds (buffer, &start, &end); + if (gtk_text_iter_get_char (&start)== 0xFFFC) + result = FALSE; + else { + gtk_text_iter_backward_char (&end); + if (gtk_text_iter_forward_find_char (&start, _forward_search_image_char, + NULL, &end)) + result = FALSE; + } + + } + + return result; +} + +gchar * +modest_text_utils_escape_mnemonics (const gchar *text) +{ + const gchar *p; + GString *result = NULL; + + if (text == NULL) + return NULL; + + result = g_string_new (""); + for (p = text; *p != '\0'; p++) { + if (*p == '_') + result = g_string_append (result, "__"); + else + result = g_string_append_c (result, *p); + } + + return g_string_free (result, FALSE); +}