Fix a problem in modest_text_utils_convert_buffer_to_html_start()
[modest] / src / modest-text-utils.c
index 9d14bb1..38f55ad 100644 (file)
 #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.
- * ie. 1<3 ==> 1\007lt;3
- */
-#define MARK_AMP '\007'
-#define MARK_AMP_STR "\007"
-
-/* mark &amp; separately, because they are parts of urls.
- * ie. a&b => 1\008amp;b
- */
-#define MARK_AMP_URI '\006'
-#define MARK_AMP_URI_STR "\006"
-
 
 /*
  * we need these regexps to find URLs in plain text e-mails
@@ -98,15 +83,40 @@ struct _url_match_t {
        const gchar* prefix;
 };
 
+
+/*
+ * 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 &amp; 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 '&amp;'
+ * 
+ * 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_URI_STR "]+[-a-z0-9_$%" MARK_AMP_URI_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_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://" },\
-       { "(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.]+",                    \
          NULL, NULL},\
@@ -221,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:"",
@@ -379,7 +391,7 @@ modest_text_utils_remove_duplicate_addresses (const gchar *address_list)
 static void
 modest_text_utils_convert_buffer_to_html_start (GString *html, const gchar *data, gssize n)
 {
-       guint           i;
+       guint           i = 0;
        gboolean        space_seen = FALSE;
        guint           break_dist = 0; /* distance since last break point */
 
@@ -387,7 +399,7 @@ modest_text_utils_convert_buffer_to_html_start (GString *html, const gchar *data
                n = strlen (data);
 
        /* replace with special html chars where needed*/
-       for (i = 0; i != n; ++i)  {
+       while (i != n) {
                char kar = data[i];
                
                if (space_seen && kar != ' ') {
@@ -431,8 +443,25 @@ modest_text_utils_convert_buffer_to_html_start (GString *html, const gchar *data
                                space_seen = TRUE;
                        break;
                default:
-                       g_string_append_c (html, kar);
+                       /* Optimization to copy single ascii
+                        * characters faster */
+                       if (kar > 31 && kar < 127) {
+                               g_string_append_c (html, kar);
+                       } else {
+                               /* Important: copy full UTF-8 characters,
+                                * don't copy them byte by byte */
+                               gunichar c = g_utf8_get_char_validated (data+i, -1);
+                               if (c != (gunichar) -1 && c != (gunichar) -2) {
+                                       const gchar *copyfrom = data + i;
+                                       int len = g_utf8_next_char(copyfrom) - copyfrom;
+                                       g_string_append_len (html, copyfrom, len);
+                                       i += len - 1;
+                               } else {
+                                       g_warning ("%s: non-UTF8 byte found, skipping", __FUNCTION__);
+                               }
+                       }
                }
+               i++;
        }
 }
 
@@ -570,6 +599,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] == ';'))
@@ -1219,7 +1250,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];
                
@@ -1617,3 +1648,57 @@ 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;
+}