X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=src%2Fmodest-text-utils.c;h=df90b99ab00f0678c3839a2f84dde6c5e45ee63f;hb=49fcf350e11ac4bb02f21eeec62b82a701eec0bc;hp=deb2971688832d6fcdbda925349c52984a7cb1a4;hpb=506d8ddd3ac4f45ad5fffb7d2ec83d306fc6f19b;p=modest
diff --git a/src/modest-text-utils.c b/src/modest-text-utils.c
index deb2971..df90b99 100644
--- a/src/modest-text-utils.c
+++ b/src/modest-text-utils.c
@@ -63,8 +63,6 @@
* will hang modest
*/
#define HYPERLINKIFY_MAX_LENGTH (1024*50)
-#define SIGNATURE_MARKER "--"
-
/*
* we need these regexps to find URLs in plain text e-mails
@@ -109,14 +107,14 @@ struct _url_match_t {
/* 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|mms|mmsh|rtsp|rdp|lastfm)://[-a-z0-9_$.+!*(),;:@%=?/~#" MARK_AMP_URI_STR \
- "]+[-a-z0-9_$%" MARK_AMP_URI_STR "=?/~#]", \
+ { "(feed:|)(file|rtsp|http|ftp|https|mms|mmsh|webcal|feed|rtsp|rdp|lastfm|sip)://[-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://" },\
- { "(jabberto|voipto|sipto|sip|chatto|xmpp):[-_a-z@0-9.+]+", \
+ { "(jabberto|voipto|sipto|sip|chatto|skype|xmpp):[-_a-z@0-9.+]+", \
NULL, NULL}, \
{ "mailto:[-_a-z0-9.\\+]+@[-_a-z0-9.]+", \
NULL, NULL},\
@@ -165,6 +163,8 @@ static gchar* modest_text_utils_quote_html (const gchar *text,
GList *attachments,
int limit);
static gchar* get_email_from_address (const gchar *address);
+static void remove_extra_spaces (gchar *string);
+
/* ******************************************************************* */
@@ -213,14 +213,17 @@ modest_text_utils_cite (const gchar *text,
g_return_val_if_fail (text, NULL);
g_return_val_if_fail (content_type, NULL);
- if (!signature)
- retval = g_strdup ("");
- else if (strcmp(content_type, "text/html") == 0) {
- tmp_sig = g_strconcat ("\n", SIGNATURE_MARKER,"\n", signature, NULL);
- retval = modest_text_utils_convert_to_html_body(tmp_sig, -1, TRUE);
+ if (!signature) {
+ tmp_sig = g_strdup (text);
+ } else {
+ tmp_sig = g_strconcat (text, "\n", MODEST_TEXT_UTILS_SIGNATURE_MARKER, "\n", signature, NULL);
+ }
+
+ if (strcmp (content_type, "text/html") == 0) {
+ retval = modest_text_utils_convert_to_html_body (tmp_sig, -1, TRUE);
g_free (tmp_sig);
} else {
- retval = g_strconcat (text, "\n", SIGNATURE_MARKER, "\n", signature, NULL);
+ retval = tmp_sig;
}
return retval;
@@ -283,6 +286,8 @@ modest_text_utils_strftime(char *s, gsize max, const char *fmt, time_t timet)
garbage in the s variable */
if (s)
s[0] = '\0';
+ else
+ return 0;
/* does not work on old maemo glib:
* g_date_set_time_t (&date, timet);
@@ -292,12 +297,13 @@ modest_text_utils_strftime(char *s, gsize max, const char *fmt, time_t timet)
}
gchar *
-modest_text_utils_derived_subject (const gchar *subject, const gchar *prefix)
+modest_text_utils_derived_subject (const gchar *subject, gboolean is_reply)
{
- gchar *tmp, *subject_dup, *retval;
- gint prefix_len;
-
- g_return_val_if_fail (prefix, NULL);
+ gchar *tmp, *subject_dup, *retval, *prefix;
+ const gchar *untranslated_prefix;
+ gint prefix_len, untranslated_prefix_len;
+ gboolean translated_found = FALSE;
+ gboolean first_time;
if (!subject || subject[0] == '\0')
subject = _("mail_va_no_subject");
@@ -305,24 +311,99 @@ modest_text_utils_derived_subject (const gchar *subject, const gchar *prefix)
subject_dup = g_strdup (subject);
tmp = g_strchug (subject_dup);
+ prefix = (is_reply) ? _("mail_va_re") : _("mail_va_fw");
+ prefix = g_strconcat (prefix, ":", NULL);
+ prefix_len = g_utf8_strlen (prefix, -1);
+
+ untranslated_prefix = (is_reply) ? "Re:" : "Fw:";
+ untranslated_prefix_len = 3;
+
/* We do not want things like "Re: Re: Re:" or "Fw: Fw:" so
delete the previous ones */
- prefix_len = strlen (prefix);
+ first_time = TRUE;
do {
if (g_str_has_prefix (tmp, prefix)) {
tmp += prefix_len;
tmp = g_strchug (tmp);
+ /* Do not consider translated prefixes in the
+ middle of a Re:Re:..Re: like sequence */
+ if (G_UNLIKELY (first_time))
+ translated_found = TRUE;
+ } else if (g_str_has_prefix (tmp, untranslated_prefix)) {
+ tmp += untranslated_prefix_len;
+ tmp = g_strchug (tmp);
} else {
- break;
+ gchar *prefix_down, *tmp_down;
+
+ /* We need this to properly check the cases of
+ some clients adding FW: instead of Fw: for
+ example */
+ prefix_down = g_utf8_strdown (prefix, -1);
+ tmp_down = g_utf8_strdown (tmp, -1);
+ if (g_str_has_prefix (tmp_down, prefix_down)) {
+ tmp += prefix_len;
+ tmp = g_strchug (tmp);
+ g_free (prefix_down);
+ g_free (tmp_down);
+ } else {
+ g_free (prefix_down);
+ g_free (tmp_down);
+ break;
+ }
}
+ first_time = FALSE;
} while (tmp);
- retval = g_strdup_printf ("%s %s", prefix, tmp);
+ if (!g_strcmp0 (subject, tmp)) {
+ /* normal case */
+ retval = g_strdup_printf ("%s %s", untranslated_prefix, tmp);
+ } else {
+ if (translated_found) {
+ /* Found a translated prefix, i.e, "VS:" in Finish */
+ retval = g_strdup_printf ("%s %s", prefix, tmp);
+ } else {
+ retval = g_strdup_printf ("%s %s", untranslated_prefix, tmp);
+ }
+ }
g_free (subject_dup);
+ g_free (prefix);
return retval;
}
+
+/* Performs a case-insensitive strstr for ASCII strings */
+static const gchar *
+ascii_stristr(const gchar *haystack, const gchar *needle)
+{
+ int needle_len;
+ int haystack_len;
+ const gchar *pos;
+ const gchar *max_pos;
+
+ if (haystack == NULL || needle == NULL) {
+ return haystack; /* as in strstr */
+ }
+
+ needle_len = strlen(needle);
+
+ if (needle_len == 0) {
+ return haystack; /* as in strstr */
+ }
+
+ haystack_len = strlen(haystack);
+ max_pos = haystack + haystack_len - needle_len;
+
+ for (pos = haystack; pos <= max_pos; pos++) {
+ if (g_ascii_strncasecmp (pos, needle, needle_len) == 0) {
+ return pos;
+ }
+ }
+
+ return NULL;
+}
+
+
gchar*
modest_text_utils_remove_address (const gchar *address_list, const gchar *address)
{
@@ -331,26 +412,26 @@ 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);
email_address = get_email_from_address (address);
-
+
/* search for substring */
- if (!strstr ((const char *) address_list, (const char *) email_address)) {
+ if (!ascii_stristr ((const char *) address_list, (const char *) email_address)) {
g_free (email_address);
return g_strdup (address_list);
}
dup = g_strdup (address_list);
filtered_emails = g_string_new (NULL);
-
+
token = strtok_r (dup, ",", &ptr);
while (token != NULL) {
/* Add to list if not found */
- if (!strstr ((const char *) token, (const char *) email_address)) {
+ if (!ascii_stristr ((const char *) token, (const char *) email_address)) {
if (filtered_emails->len == 0)
g_string_append_printf (filtered_emails, "%s", g_strstrip (token));
else
@@ -413,6 +494,9 @@ modest_text_utils_remove_duplicate_addresses (const gchar *address_list)
g_slist_foreach (addresses, (GFunc)g_free, NULL);
g_slist_free (addresses);
+ if (new_list == NULL)
+ new_list = g_strdup ("");
+
return new_list;
}
@@ -432,7 +516,7 @@ modest_text_utils_convert_buffer_to_html_start (GString *html, const gchar *data
guchar kar = data[i];
if (space_seen && kar != ' ') {
- g_string_append_c (html, ' ');
+ g_string_append (html, " ");
space_seen = FALSE;
}
@@ -462,14 +546,13 @@ modest_text_utils_convert_buffer_to_html_start (GString *html, const gchar *data
/* don't convert ' --> wpeditor will try to re-convert it... */
//case '\'' : g_string_append (html, "'"); break;
- case '\n' : g_string_append (html, "
\n");break_dist= 0; break;
+ case '\n' : g_string_append (html, "
\n");break_dist= 0; break;
case '\t' : g_string_append (html, MARK_AMP_STR "nbsp;" MARK_AMP_STR "nbsp;" MARK_AMP_STR "nbsp; ");
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;
} else
space_seen = TRUE;
break;
@@ -550,59 +633,52 @@ modest_text_utils_convert_to_html_body (const gchar *data, gssize n, gboolean hy
void
modest_text_utils_get_addresses_indexes (const gchar *addresses, GSList **start_indexes, GSList **end_indexes)
{
- gchar *current, *start, *last_blank;
- gint start_offset = 0, current_offset = 0;
+ GString *str;
+ gchar *start, *cur;
- g_return_if_fail (start_indexes != NULL);
- g_return_if_fail (end_indexes != NULL);
+ if (!addresses)
+ return;
+
+ if (strlen (addresses) == 0)
+ return;
- start = (gchar *) addresses;
- current = start;
- last_blank = start;
+ str = g_string_new ("");
+ start = (gchar*) addresses;
+ cur = (gchar*) addresses;
- while (*current != '\0') {
- if ((start == current)&&((*current == ' ')||(*current == ',')||(*current == ';'))) {
- start = g_utf8_next_char (start);
- start_offset++;
- last_blank = current;
- } else if ((*current == ',')||(*current == ';')) {
+ for (cur = start; *cur != '\0'; cur = g_utf8_next_char (cur)) {
+ if (*cur == ',' || *cur == ';') {
gint *start_index, *end_index;
- start_index = g_new0(gint, 1);
- end_index = g_new0(gint, 1);
- *start_index = start_offset;
- *end_index = current_offset;
+ gchar *next_char = g_utf8_next_char (cur);
+
+ if (!g_utf8_strchr (start, (cur - start + 1), g_utf8_get_char ("@")) &&
+ next_char && *next_char != '\n')
+ continue;
+
+ start_index = g_new0 (gint, 1);
+ end_index = g_new0 (gint, 1);
+ *start_index = g_utf8_pointer_to_offset (addresses, start);
+ *end_index = g_utf8_pointer_to_offset (addresses, cur);;
*start_indexes = g_slist_prepend (*start_indexes, start_index);
*end_indexes = g_slist_prepend (*end_indexes, end_index);
- start = g_utf8_next_char (current);
- start_offset = current_offset + 1;
- last_blank = start;
- } else if (*current == '"') {
- current = g_utf8_next_char (current);
- current_offset ++;
- while ((*current != '"')&&(*current != '\0')) {
- current = g_utf8_next_char (current);
- current_offset ++;
- }
+ start = g_utf8_next_char (cur);
}
-
- current = g_utf8_next_char (current);
- current_offset ++;
}
- if (start != current) {
- gint *start_index, *end_index;
- start_index = g_new0(gint, 1);
- end_index = g_new0(gint, 1);
- *start_index = start_offset;
- *end_index = current_offset;
- *start_indexes = g_slist_prepend (*start_indexes, start_index);
- *end_indexes = g_slist_prepend (*end_indexes, end_index);
+ if (start != cur) {
+ gint *start_index, *end_index;
+ start_index = g_new0 (gint, 1);
+ end_index = g_new0 (gint, 1);
+ *start_index = g_utf8_pointer_to_offset (addresses, start);
+ *end_index = g_utf8_pointer_to_offset (addresses, cur);;
+ *start_indexes = g_slist_prepend (*start_indexes, start_index);
+ *end_indexes = g_slist_prepend (*end_indexes, end_index);
}
-
- *start_indexes = g_slist_reverse (*start_indexes);
- *end_indexes = g_slist_reverse (*end_indexes);
- return;
+ if (*start_indexes)
+ *start_indexes = g_slist_reverse (*start_indexes);
+ if (*end_indexes)
+ *end_indexes = g_slist_reverse (*end_indexes);
}
@@ -616,15 +692,16 @@ modest_text_utils_split_addresses_list (const gchar *addresses)
gboolean after_at = FALSE;
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] == ';'))
+
+ /* skip any space, ',', ';' '\n' at the start */
+ while (my_addrs && (my_addrs[0] == ' ' || my_addrs[0] == ',' ||
+ my_addrs[0] == ';' || my_addrs[0] == '\n'))
++my_addrs;
/* are we at the end of addresses list? */
if (!my_addrs[0])
return NULL;
-
+
/* nope, we are at the start of some address
* now, let's find the end of the address */
end = my_addrs + 1;
@@ -647,12 +724,56 @@ modest_text_utils_split_addresses_list (const gchar *addresses)
addr = g_strndup (my_addrs, end - my_addrs);
g_strchomp (addr);
+ remove_extra_spaces (addr);
+
head = g_slist_append (NULL, addr);
head->next = modest_text_utils_split_addresses_list (end); /* recurse */
return head;
}
+gchar *
+modest_text_utils_join_addresses (const gchar *from,
+ const gchar *to,
+ const gchar *cc,
+ const gchar *bcc)
+{
+ GString *buffer;
+ gboolean add_separator = FALSE;
+
+ buffer = g_string_new ("");
+
+ if (from && strlen (from)) {
+ buffer = g_string_append (buffer, from);
+ add_separator = TRUE;
+ }
+ if (to && strlen (to)) {
+ if (add_separator)
+ buffer = g_string_append (buffer, "; ");
+ else
+ add_separator = TRUE;
+
+ buffer = g_string_append (buffer, to);
+ }
+ if (cc && strlen (cc)) {
+ if (add_separator)
+ buffer = g_string_append (buffer, "; ");
+ else
+ add_separator = TRUE;
+
+ buffer = g_string_append (buffer, cc);
+ }
+ if (bcc && strlen (bcc)) {
+ if (add_separator)
+ buffer = g_string_append (buffer, "; ");
+ else
+ add_separator = TRUE;
+
+ buffer = g_string_append (buffer, bcc);
+ }
+
+ return g_string_free (buffer, FALSE);
+}
void
modest_text_utils_address_range_at_position (const gchar *recipients_list,
@@ -763,7 +884,7 @@ get_indent_level (const char *l)
/* if we hit the signature marker "-- ", we return -(indent + 1). This
* stops reformatting.
*/
- if (strcmp (l, "-- ") == 0) {
+ if (strcmp (l, MODEST_TEXT_UTILS_SIGNATURE_MARKER) == 0) {
return -1 - indent;
} else {
return indent;
@@ -919,6 +1040,7 @@ modest_text_utils_quote_body (GString *output, const gchar *text,
g_string_prepend (l, remaining->str);
} else {
do {
+ gunichar remaining_first;
breakpoint =
get_breakpoint (remaining->str,
rem_indent,
@@ -927,9 +1049,11 @@ modest_text_utils_quote_body (GString *output, const gchar *text,
remaining, breakpoint);
g_string_erase (remaining, 0,
breakpoint);
- if (remaining->str[0] == ' ') {
- g_string_erase (remaining, 0,
- 1);
+ remaining_first = g_utf8_get_char_validated (remaining->str, remaining->len);
+ if (remaining_first != ((gunichar) -1)) {
+ if (g_unichar_isspace (remaining_first)) {
+ g_string_erase (remaining, 0, g_utf8_next_char (remaining->str) - remaining->str);
+ }
}
} while (remaining->len);
}
@@ -961,7 +1085,7 @@ modest_text_utils_quote_plain_text (const gchar *text,
q = g_string_new ("");
if (signature != NULL) {
- q = g_string_append (q, "\n--\n");
+ g_string_append_printf (q, "\n%s\n", MODEST_TEXT_UTILS_SIGNATURE_MARKER);
q = g_string_append (q, signature);
}
@@ -1008,12 +1132,12 @@ modest_text_utils_quote_html (const gchar *text,
GString *quoted_text;
g_string_append (result_string, "
\n"); if (signature) { - quote_html_add_to_gstring (result_string, SIGNATURE_MARKER); + quote_html_add_to_gstring (result_string, MODEST_TEXT_UTILS_SIGNATURE_MARKER); quote_html_add_to_gstring (result_string, signature); } quote_html_add_to_gstring (result_string, cite); quoted_text = g_string_new (""); - quoted_text = modest_text_utils_quote_body (quoted_text, text, ">", limit); + quoted_text = modest_text_utils_quote_body (quoted_text, (text) ? text : "", ">", limit); quote_html_add_to_gstring (result_string, quoted_text->str); g_string_free (quoted_text, TRUE); if (attachments) { @@ -1037,6 +1161,7 @@ cmp_offsets_reverse (const url_match_t *match1, const url_match_t *match2) static gint url_matches_block = 0; static url_match_pattern_t patterns[] = MAIL_VIEWER_URL_MATCH_PATTERNS; +static GMutex *url_patterns_mutex = NULL; static gboolean @@ -1071,17 +1196,25 @@ free_patterns () void modest_text_utils_hyperlinkify_begin (void) { + + if (url_patterns_mutex == NULL) { + url_patterns_mutex = g_mutex_new (); + } + g_mutex_lock (url_patterns_mutex); if (url_matches_block == 0) compile_patterns (); url_matches_block ++; + g_mutex_unlock (url_patterns_mutex); } void modest_text_utils_hyperlinkify_end (void) { + g_mutex_lock (url_patterns_mutex); url_matches_block--; if (url_matches_block <= 0) free_patterns (); + g_mutex_unlock (url_patterns_mutex); } @@ -1247,18 +1380,50 @@ modest_text_utils_get_display_address (gchar *address) for (i = 0; address[i]; ++i) { if (address[i] == '<') { - if (G_UNLIKELY(i == 0)) - return; /* there's nothing else, leave it */ - else { + if (G_UNLIKELY(i == 0)) { + break; /* there's nothing else, leave it */ + }else { address[i] = '\0'; /* terminate the string here */ - return; + break; } } } + + g_strchomp (address); } +gchar * +modest_text_utils_get_display_addresses (const gchar *recipients) +{ + gchar *addresses; + GSList *recipient_list; + + addresses = NULL; + recipient_list = modest_text_utils_split_addresses_list (recipients); + if (recipient_list) { + GString *add_string = g_string_sized_new (strlen (recipients)); + GSList *iter = recipient_list; + gboolean first = TRUE; + + while (iter) { + /* Strings are changed in place */ + modest_text_utils_get_display_address ((gchar *) iter->data); + if (G_UNLIKELY (first)) { + g_string_append_printf (add_string, "%s", (gchar *) iter->data); + first = FALSE; + } else { + g_string_append_printf (add_string, ", %s", (gchar *) iter->data); + } + iter = g_slist_next (iter); + } + g_slist_foreach (recipient_list, (GFunc) g_free, NULL); + g_slist_free (recipient_list); + addresses = g_string_free (add_string, FALSE); + } + return addresses; +} gchar * @@ -1387,10 +1552,17 @@ modest_text_utils_get_display_date (time_t date) /* if it's today, show the time, if it's not today, show the date instead */ /* TODO: take into account the system config for 24/12h */ +#ifdef MODEST_TOOLKIT_HILDON2 if (day == date_day) /* is the date today? */ modest_text_utils_strftime (date_buf, DATE_BUF_SIZE, _HL("wdgt_va_24h_time"), date); else modest_text_utils_strftime (date_buf, DATE_BUF_SIZE, _HL("wdgt_va_date"), date); +#else + 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); +#endif return date_buf; /* this is a static buffer, don't free! */ } @@ -1594,6 +1766,7 @@ modest_text_utils_validate_recipient (const gchar *recipient, const gchar **inva /* quoted string */ if (*current == '\"') { + gchar *last_quote = NULL; current = g_utf8_next_char (current); has_error = TRUE; for (; *current != '\0'; current = g_utf8_next_char (current)) { @@ -1609,9 +1782,11 @@ modest_text_utils_validate_recipient (const gchar *recipient, const gchar **inva } else if (*current == '\"') { has_error = FALSE; current = g_utf8_next_char (current); - break; + last_quote = current; } } + if (last_quote) + current = g_utf8_next_char (last_quote); } else { has_error = TRUE; for (current = stripped ; *current != '\0'; current = g_utf8_next_char (current)) { @@ -1631,6 +1806,9 @@ modest_text_utils_validate_recipient (const gchar *recipient, const gchar **inva g_free (stripped); right_part = g_strstrip (right_part); + if (g_str_has_suffix (right_part, ",") || g_str_has_suffix (right_part, ";")) + right_part [(strlen (right_part) - 1)] = '\0'; + if (g_str_has_prefix (right_part, "<") && g_str_has_suffix (right_part, ">")) { gchar *address; @@ -1656,19 +1834,19 @@ modest_text_utils_get_display_size (guint64 size) const guint GB=1024 * MB; if (size == 0) - return g_strdup_printf (_FM("sfil_li_size_kb")); + return g_strdup_printf (_FM_SIZE_KB, (int) 0); if (0 <= size && size < KB) - return g_strdup_printf (_FM("sfil_li_size_1kb_99kb"), 1); + return g_strdup_printf (_FM_SIZE_1KB_99KB, (int) 1); else if (KB <= size && size < 100 * KB) - return g_strdup_printf (_FM("sfil_li_size_1kb_99kb"), size / KB); + return g_strdup_printf (_FM_SIZE_1KB_99KB, (int) size / KB); else if (100*KB <= size && size < MB) - return g_strdup_printf (_FM("sfil_li_size_100kb_1mb"), (float) size / MB); + return g_strdup_printf (_FM_SIZE_100KB_1MB, (int) size / KB); else if (MB <= size && size < 10*MB) - return g_strdup_printf (_FM("sfil_li_size_1mb_10mb"), (float) size / MB); + return g_strdup_printf (_FM_SIZE_1MB_10MB, (float) size / MB); else if (10*MB <= size && size < GB) - return g_strdup_printf (_FM("sfil_li_size_10mb_1gb"), size / MB); + return g_strdup_printf (_FM_SIZE_10MB_1GB, (float) size / MB); else - return g_strdup_printf (_FM("sfil_li_size_1gb_or_greater"), (float) size / GB); + return g_strdup_printf (_FM_SIZE_1GB_OR_GREATER, (float) size / GB); } static gchar * @@ -1685,11 +1863,11 @@ get_email_from_address (const gchar * address) return g_strndup (left_limit + 1, (right_limit - left_limit) - 1); } -gchar * +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, @@ -1821,6 +1999,31 @@ modest_text_utils_buffer_selection_is_valid (GtkTextBuffer *buffer) return result; } +static void +remove_quotes (gchar **quotes) +{ + if (g_str_has_prefix (*quotes, "\"") && g_str_has_suffix (*quotes, "\"")) { + gchar *result; + result = g_strndup ((*quotes)+1, strlen (*quotes) - 2); + g_free (*quotes); + *quotes = result; + } +} + +static void +remove_extra_spaces (gchar *string) +{ + gchar *start; + + start = string; + while (start && start[0] != '\0') { + if ((start[0] == ' ') && (start[1] == ' ')) { + g_strchug (start+1); + } + start++; + } +} + gchar * modest_text_utils_escape_mnemonics (const gchar *text) { @@ -1840,3 +2043,207 @@ modest_text_utils_escape_mnemonics (const gchar *text) return g_string_free (result, FALSE); } + +gchar * +modest_text_utils_simplify_recipients (const gchar *recipients) +{ + GSList *addresses, *node; + GString *result; + gboolean is_first = TRUE; + + if (recipients == NULL) + return g_strdup (""); + + addresses = modest_text_utils_split_addresses_list (recipients); + result = g_string_new (""); + + for (node = addresses; node != NULL; node = g_slist_next (node)) { + const gchar *address = (const gchar *) node->data; + gchar *left_limit, *right_limit; + + left_limit = strstr (address, "<"); + right_limit = g_strrstr (address, ">"); + + if (is_first) + is_first = FALSE; + else + result = g_string_append (result, ", "); + + if ((left_limit == NULL)||(right_limit == NULL)|| (left_limit > right_limit)) { + result = g_string_append (result, address); + } else { + gchar *name_side; + gchar *email_side; + name_side = g_strndup (address, left_limit - address); + name_side = g_strstrip (name_side); + remove_quotes (&name_side); + email_side = get_email_from_address (address); + if (name_side && email_side && !strcmp (name_side, email_side)) { + result = g_string_append (result, email_side); + } else { + result = g_string_append (result, address); + } + g_free (name_side); + g_free (email_side); + } + + } + g_slist_foreach (addresses, (GFunc)g_free, NULL); + g_slist_free (addresses); + + return g_string_free (result, FALSE); + +} + +GSList * +modest_text_utils_remove_duplicate_addresses_list (GSList *address_list) +{ + GSList *new_list, *iter; + GHashTable *table; + + g_return_val_if_fail (address_list, NULL); + + table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + new_list = address_list; + iter = address_list; + while (iter) { + const gchar* address = (const gchar*)iter->data; + + /* We need only the email to just compare it and not + the full address which would make "a " + 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, email) == 0) { + g_hash_table_insert (table, (gchar*)email, GINT_TO_POINTER(1)); + iter = g_slist_next (iter); + } else { + GSList *tmp = g_slist_next (iter); + new_list = g_slist_delete_link (new_list, iter); + iter = tmp; + } + } + + g_hash_table_unref (table); + + return new_list; +} + +gchar * +modest_text_utils_get_secure_header (const gchar *value, + const gchar *header) +{ + const gint max_len = 16384; + gchar *new_value = NULL; + gchar *needle = g_strrstr (value, header); + + if (needle && value != needle) + new_value = g_strdup (needle + strlen (header)); + + if (!new_value) + new_value = g_strdup (value); + + /* Do a max length check to prevent DoS attacks caused by huge + malformed headers */ + if (g_utf8_validate (new_value, -1, NULL)) { + if (g_utf8_strlen (new_value, -1) > max_len) { + gchar *tmp = g_malloc0 (max_len * 4); + g_utf8_strncpy (tmp, (const gchar *) new_value, max_len); + g_free (new_value); + new_value = tmp; + } + } else { + if (strlen (new_value) > max_len) { + gchar *tmp = g_malloc0 (max_len); + strncpy (new_value, tmp, max_len); + g_free (new_value); + new_value = tmp; + } + } + + return new_value; +} + +static gboolean +is_quoted (const char *start, const gchar *end) +{ + gchar *c; + + c = (gchar *) start; + while (*c == ' ') + c = g_utf8_next_char (c); + + if (*c == '\0' || *c != '\"') + return FALSE; + + c = (gchar *) end; + while (*c == ' ' && c != start) + c = g_utf8_prev_char (c); + + if (c == start || *c != '\"') + return FALSE; + + return TRUE; +} + + +static void +quote_name_part (GString **str, gchar **cur, gchar **start) +{ + gchar *blank; + gint str_len = *cur - *start; + + while (**start == ' ') { + *start = g_utf8_next_char (*start); + str_len--; + } + + blank = g_utf8_strrchr (*start, str_len, g_utf8_get_char (" ")); + if (blank && (blank != *start)) { + if (is_quoted (*start, blank - 1)) { + *str = g_string_append_len (*str, *start, str_len); + *str = g_string_append (*str, ";"); + *start = g_utf8_next_char (*cur); + } else { + *str = g_string_append_c (*str, '"'); + *str = g_string_append_len (*str, *start, + (g_utf8_pointer_to_offset (*start, blank) - + g_utf8_pointer_to_offset (*start, *start))); + *str = g_string_append_c (*str, '"'); + *str = g_string_append_len (*str, blank, + (g_utf8_pointer_to_offset (*start, *cur) - + g_utf8_pointer_to_offset (*start, blank))); + *str = g_string_append (*str, ";"); + *start = g_utf8_next_char (*cur); + } + } else { + *str = g_string_append_len (*str, *start, str_len); + *str = g_string_append (*str, ";"); + *start = g_utf8_next_char (*cur); + } +} + +gchar * +modest_text_utils_quote_names (const gchar *recipients) +{ + GString *str; + gchar *start, *cur; + + str = g_string_new (""); + start = (gchar*) recipients; + cur = (gchar*) recipients; + + for (cur = start; *cur != '\0'; cur = g_utf8_next_char (cur)) { + if (*cur == ',' || *cur == ';') { + if (!g_utf8_strchr (start, (cur - start + 1), g_utf8_get_char ("@"))) + continue; + quote_name_part (&str, &cur, &start); + } + } + + quote_name_part (&str, &cur, &start); + + return g_string_free (str, FALSE); +}