#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 & 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
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 & 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_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},\
/* 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);
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:"",
{
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 <a@a.com>"
+ 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);
/* 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, ' ');
/* 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;
}
g_string_append (html, "</body></html>");
if (len <= HYPERLINKIFY_MAX_LENGTH)
- hyperlinkify_plain_text (html);
+ hyperlinkify_plain_text (html, 0);
modest_text_utils_convert_buffer_to_html_finish (html);
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);
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] == ';'))
*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 ************************ */
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");
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;
g_free (attachments_string);
if (signature != NULL) {
- q = g_string_append_c (q, '\n');
+ q = g_string_append (q, "\n--\n");
q = g_string_append (q, signature);
+ q = g_string_append_c (q, '\n');
}
return g_string_free (q, FALSE);
"<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" \
"<html>\n" \
"<body>\n" \
- "<br/>%s<br/>" \
"<pre>%s<br/>%s<br/>%s</pre>\n" \
+ "<br/>--<br/>%s<br/>\n" \
"</body>\n" \
"</html>\n";
gchar *attachments_string = NULL;
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);
+ result = g_strdup_printf (format, q_cite, html_text, q_attachments_string, signature_result);
g_free (q_cite);
g_free (html_text);
g_free (attachments_string);
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);
/* 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 */
}
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);
if (!is_submatch) {
/* make a list of our matches (<offset, len, prefix> 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;
}
}
}
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) {
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, "<body>");
+ 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
/* 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];
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 */
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;
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) {
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);
+}