X-Git-Url: http://git.maemo.org/git/?p=modest;a=blobdiff_plain;f=src%2Fmodest-text-utils.c;h=9b0cef4a05d9d6d002022bb19b8967bc60156b97;hp=0dd72292b589fea88eb50f7a4adf170abb2dd6b6;hb=b93b64adaaf2947f8b35889981af6ad96b18fd31;hpb=674685c46e0ffd6a061f21930a1a33e5f579fa91 diff --git a/src/modest-text-utils.c b/src/modest-text-utils.c index 0dd7229..9b0cef4 100644 --- a/src/modest-text-utils.c +++ b/src/modest-text-utils.c @@ -1,74 +1,446 @@ -/* modest-ui.c */ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ -/* insert (c)/licensing information) */ -#include +#include #include - -#include "modest-text-utils.h" +#include +#include +#include +#include +#include +#include #ifdef HAVE_CONFIG_H #include -#endif /*HAVE_CONFIG_H*/ +#endif /*HAVE_CONFIG_H */ + +/* defines */ +#define FORWARD_STRING _("-----Forwarded Message-----") +#define FROM_STRING _("From:") +#define SENT_STRING _("Sent:") +#define TO_STRING _("To:") +#define SUBJECT_STRING _("Subject:") +#define EMPTY_STRING "" + +/* + * we need these regexps to find URLs in plain text e-mails + */ +typedef struct _url_match_pattern_t url_match_pattern_t; +struct _url_match_pattern_t { + gchar *regex; + regex_t *preg; + gchar *prefix; +}; + +typedef struct _url_match_t url_match_t; +struct _url_match_t { + guint offset; + guint len; + const gchar* prefix; +}; +#define MAIL_VIEWER_URL_MATCH_PATTERNS { \ + { "(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://" },\ + { "ftp\\.[-a-z0-9.]+[-a-z0-9](:[0-9]*)?(/[-A-Za-z0-9_$.+!*(),;:@%&=?/~#]*[^]}\\),?!;:\"]?)?",\ + NULL, "ftp://" },\ + { "(voipto|callto|chatto|jabberto|xmpp):[-_a-z@0-9.\\+]+", \ + NULL, NULL}, \ + { "mailto:[-_a-z0-9.\\+]+@[-_a-z0-9.]+", \ + NULL, NULL},\ + { "[-_a-z0-9.\\+]+@[-_a-z0-9.]+",\ + NULL, "mailto:"}\ + } /* private */ -static GString * -get_next_line(GtkTextBuffer *b, GtkTextIter *iter); +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 int -get_indent_level(const char *l); +static GString* get_next_line (const char *b, const gsize blen, const gchar * iter); +static int get_indent_level (const char *l); +static void unquote_line (GString * l); +static void append_quoted (GString * buf, const int indent, const GString * str, + const int cutpoint); +static int get_breakpoint_utf8 (const gchar * s, const gint indent, const gint limit); +static int get_breakpoint_ascii (const gchar * s, const gint indent, const gint limit); +static int get_breakpoint (const gchar * s, const gint indent, const gint limit); -static void -unquote_line(GString *l); +static gchar* modest_text_utils_quote_plain_text (const gchar *text, + const gchar *cite, + int limit); -static void -append_quoted(GString *buf, const int indent, const GString *str, const int cutpoint); +static gchar* modest_text_utils_quote_html (const gchar *text, + const gchar *cite, + int limit); -static int -get_breakpoint_utf8(const gchar *s, const gint indent, const gint limit); -static int -get_breakpoint_ascii(const gchar *s, const gint indent, const gint limit); +/* ******************************************************************* */ +/* ************************* PUBLIC FUNCTIONS ************************ */ +/* ******************************************************************* */ -static int -get_breakpoint(const gchar *s, const gint indent, const gint limit); +gchar * +modest_text_utils_quote (const gchar *text, + const gchar *content_type, + const gchar *from, + const time_t sent_date, + int limit) +{ + gchar *retval, *cited; -static GString * -get_next_line(GtkTextBuffer *b, GtkTextIter *iter) + g_return_val_if_fail (text, NULL); + g_return_val_if_fail (content_type, NULL); + + cited = cite (sent_date, from); + + if (content_type && strcmp (content_type, "text/html") == 0) + /* TODO: extract the of the HTML and pass it to + the function */ + retval = modest_text_utils_quote_html (text, cited, limit); + else + retval = modest_text_utils_quote_plain_text (text, cited, limit); + + g_free (cited); + + return retval; +} + + +gchar * +modest_text_utils_cite (const gchar *text, + const gchar *content_type, + const gchar *from, + time_t sent_date) { - GtkTextIter iter2, iter3; - gchar *tmp; - GString *line; + gchar *tmp, *retval; + + g_return_val_if_fail (text, NULL); + g_return_val_if_fail (content_type, NULL); + + tmp = cite (sent_date, from); + retval = g_strdup_printf ("%s%s\n", tmp, text); + g_free (tmp); + + return retval; +} + +gchar * +modest_text_utils_inline (const gchar *text, + const gchar *content_type, + const gchar *from, + time_t sent_date, + const gchar *to, + const gchar *subject) +{ + gchar sent_str[101]; + const gchar *plain_format = "%s\n%s %s\n%s %s\n%s %s\n%s %s\n\n%s"; + const gchar *html_format = \ + "%s
\n\n" \ + "\n" \ + "\n" \ + "\n" \ + "\n" \ + "

%s"; + const gchar *format; + + g_return_val_if_fail (text, NULL); + g_return_val_if_fail (content_type, NULL); + g_return_val_if_fail (text, NULL); - if (gtk_text_iter_is_end(iter)) - return g_string_new(""); + 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; + else + format = plain_format; + + return g_strdup_printf (format, + 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); +} + +/* just to prevent warnings: + * warning: `%x' yields only last 2 digits of year in some locales + */ +gsize +modest_text_utils_strftime(char *s, gsize max, const char *fmt, time_t timet) +{ + struct tm tm; + + /* does not work on old maemo glib: + * g_date_set_time_t (&date, timet); + */ + localtime_r (&timet, &tm); + + return strftime(s, max, fmt, &tm); +} + +gchar * +modest_text_utils_derived_subject (const gchar *subject, const gchar *prefix) +{ + gchar *tmp; + + g_return_val_if_fail (prefix, NULL); - gtk_text_buffer_get_iter_at_line_offset(b, - &iter2, - gtk_text_iter_get_line(iter), - gtk_text_iter_get_chars_in_line(iter) - 1 - ); - iter3 = iter2; - gtk_text_iter_forward_char(&iter2); - if (gtk_text_iter_is_end(&iter2)) { - tmp = gtk_text_buffer_get_text(b, &iter2, iter, FALSE); + if (!subject) + return g_strdup (prefix); + + tmp = g_strchug (g_strdup (subject)); + + if (!strncmp (tmp, prefix, strlen (prefix))) { + return tmp; } else { - tmp = gtk_text_buffer_get_text(b, &iter3, iter, FALSE); + g_free (tmp); + return g_strdup_printf ("%s %s", prefix, subject); } - line = g_string_new(tmp); - - gtk_text_iter_forward_line(iter); +} + +gchar* +modest_text_utils_remove_address (const gchar *address_list, const gchar *address) +{ + gchar *dup, *token, *ptr, *result; + GString *filtered_emails; + + g_return_val_if_fail (address_list, NULL); + + if (!address) + return g_strdup (address_list); + + /* search for substring */ + if (!strstr ((const char *) address_list, (const char *) address)) + return g_strdup (address_list); + + dup = g_strdup (address_list); + filtered_emails = g_string_new (NULL); - return line; + token = strtok_r (dup, ",", &ptr); + + while (token != NULL) { + /* Add to list if not found */ + if (!strstr ((const char *) token, (const char *) address)) { + if (filtered_emails->len == 0) + g_string_append_printf (filtered_emails, "%s", g_strstrip (token)); + else + g_string_append_printf (filtered_emails, ",%s", g_strstrip (token)); + } + token = strtok_r (NULL, ",", &ptr); + } + result = filtered_emails->str; + + /* Clean */ + g_free (dup); + g_string_free (filtered_emails, FALSE); + + return result; } +gchar* +modest_text_utils_convert_to_html (const gchar *data) +{ + guint i; + gboolean first_space = TRUE; + GString *html; + gsize len; + + if (!data) + return NULL; + + len = strlen (data); + html = g_string_sized_new (len + 100); /* just a guess... */ + + g_string_append_printf (html, + "" + "" + "" + "" + ""); + + /* replace with special html chars where needed*/ + for (i = 0; i != len; ++i) { + char kar = data[i]; + switch (kar) { + + case 0: break; /* ignore embedded \0s */ + 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; + default: + if (kar == ' ') { + g_string_append (html, first_space ? " " : " "); + first_space = FALSE; + } else if (kar == '\t') + g_string_append (html, "    "); + else { + int charnum = 0; + first_space = TRUE; + /* optimization trick: accumulate 'normal' chars, then copy */ + do { + kar = data [++charnum + i]; + + } while ((i + charnum < len) && + (kar > '>' || (kar != '<' && kar != '>' + && kar != '&' && kar != ' ' + && kar != '\n' && kar != '\t'))); + g_string_append_len (html, &data[i], charnum); + i += (charnum - 1); + } + } + } + + g_string_append (html, "
"); + hyperlinkify_plain_text (html); + + return g_string_free (html, FALSE); +} + +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 == ','))) { + start++; + last_blank = current; + } else if (*current == ',') { + gchar *new_address = NULL; + new_address = g_strndup (start, current - last_blank); + result = g_slist_prepend (result, new_address); + start = current + 1; + last_blank = start; + } else if (*current == '\"') { + if (current == start) { + current++; + start++; + } + while ((*current != '\"')&&(*current != '\0')) + current++; + } + + 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; + +} + +void +modest_text_utils_address_range_at_position (const gchar *recipients_list, + gint position, + gint *start, + gint *end) +{ + gchar *current = NULL; + gint range_start = 0; + gint range_end = 0; + gint index; + gboolean is_quoted = FALSE; + + index = 0; + for (current = (gchar *) recipients_list; *current != '\0'; current = g_utf8_find_next_char (current, NULL)) { + gunichar c = g_utf8_get_char (current); + + if ((c == ',') && (!is_quoted)) { + if (index < position) { + range_start = index + 1; + } else { + break; + } + } else if (c == '\"') { + is_quoted = !is_quoted; + } else if ((c == ' ') &&(range_start == index)) { + range_start ++; + } + index ++; + range_end = index; + } + + if (start) + *start = range_start; + if (end) + *end = range_end; +} + + +/* ******************************************************************* */ +/* ************************* UTILIY FUNCTIONS ************************ */ +/* ******************************************************************* */ + +static GString * +get_next_line (const gchar * b, const gsize blen, const gchar * iter) +{ + GString *gs; + const gchar *i0; + + if (iter > b + blen) + return g_string_new(""); + + i0 = iter; + while (iter[0]) { + if (iter[0] == '\n') + break; + iter++; + } + gs = g_string_new_len (i0, iter - i0); + return gs; +} static int -get_indent_level(const char *l) +get_indent_level (const char *l) { int indent = 0; - + while (l[0]) { if (l[0] == '>') { indent++; @@ -79,24 +451,24 @@ get_indent_level(const char *l) break; } l++; - + } - /* if we hit the signature marker "-- ", we return -(indent + 1). This - * stops reformatting. - */ - if (strcmp(l, "-- ") == 0) { - return -1-indent; + /* if we hit the signature marker "-- ", we return -(indent + 1). This + * stops reformatting. + */ + if (strcmp (l, "-- ") == 0) { + return -1 - indent; } else { return indent; } } static void -unquote_line(GString *l) { - GString *r; +unquote_line (GString * l) +{ gchar *p; - + p = l->str; while (p[0]) { if (p[0] == '>') { @@ -112,56 +484,60 @@ unquote_line(GString *l) { } static void -append_quoted(GString *buf, int indent, const GString *str, const int cutpoint) { +append_quoted (GString * buf, int indent, const GString * str, + const int cutpoint) +{ int i; - - indent = indent < 0? abs(indent) -1 : indent; - for (i=0; i<=indent; i++) { - g_string_append(buf, "> "); + + indent = indent < 0 ? abs (indent) - 1 : indent; + for (i = 0; i <= indent; i++) { + g_string_append (buf, "> "); } if (cutpoint > 0) { - g_string_append_len(buf, str->str, cutpoint); + g_string_append_len (buf, str->str, cutpoint); } else { - g_string_append(buf, str->str); + g_string_append (buf, str->str); } - g_string_append(buf, "\n"); + g_string_append (buf, "\n"); } static int -get_breakpoint_utf8(const gchar *s, gint indent, const gint limit) { +get_breakpoint_utf8 (const gchar * s, gint indent, const gint limit) +{ gint index = 0; const gchar *pos, *last; gunichar *uni; - - indent = indent < 0? abs(indent) -1 : indent; - + + indent = indent < 0 ? abs (indent) - 1 : indent; + last = NULL; pos = s; - uni = g_utf8_to_ucs4_fast(s, -1, NULL); + uni = g_utf8_to_ucs4_fast (s, -1, NULL); while (pos[0]) { if ((index + 2 * indent > limit) && last) { - g_free(uni); + g_free (uni); return last - s; } - if (g_unichar_isspace(uni[index])) { + if (g_unichar_isspace (uni[index])) { last = pos; } - pos = g_utf8_next_char(pos); + pos = g_utf8_next_char (pos); index++; } - g_free(uni); - return strlen(s); + g_free (uni); + return strlen (s); } static int -get_breakpoint_ascii(const gchar *s, const gint indent, const gint limit) { +get_breakpoint_ascii (const gchar * s, const gint indent, const gint limit) +{ gint i, last; - - last = strlen(s); + + last = strlen (s); if (last + 2 * indent < limit) return last; - - for ( i=strlen(s) ; i>0; i-- ) { + + for (i = strlen (s); i > 0; i--) { if (s[i] == ' ') { if (i + 2 * indent <= limit) { return i; @@ -174,76 +550,400 @@ get_breakpoint_ascii(const gchar *s, const gint indent, const gint limit) { } static int -get_breakpoint(const gchar *s, const gint indent, const gint limit) { - - if (g_utf8_validate(s, -1, NULL)) { - return get_breakpoint_utf8(s, indent, limit); - } else { /* assume ASCII */ - g_warning("invalid UTF-8 in msg"); - return get_breakpoint_ascii(s, indent, limit); - } -} - - -gchar * -modest_text_utils_quote(const gchar *to_quote, const gchar *from, const time_t sent_date, const int limit) +get_breakpoint (const gchar * s, const gint indent, const gint limit) { - GtkTextBuffer *buf; - - buf = gtk_text_buffer_new (NULL); - gtk_text_buffer_set_text(buf, to_quote, -1); - return modest_text_utils_quote_text_buffer(buf, from, sent_date, limit); + + if (g_utf8_validate (s, -1, NULL)) { + return get_breakpoint_utf8 (s, indent, limit); + } else { /* assume ASCII */ + //g_warning("invalid UTF-8 in msg"); + return get_breakpoint_ascii (s, indent, limit); + } } -gchar * -modest_text_utils_quote_text_buffer(GtkTextBuffer *buf, const gchar *from, const time_t sent_date, const int limit) +static gchar * +cite (const time_t sent_date, const gchar *from) { - - GtkTextIter iter; - gint indent, breakpoint, rem_indent; gchar sent_str[101]; - GString *q, *l, *remaining; /* quoted msg, line */ - + /* format sent_date */ - strftime(sent_str, 100, "%c", localtime(&sent_date)); - q = g_string_new(""); - g_string_printf(q, "On %s, %s wrote:\n", sent_str, from); - + modest_text_utils_strftime (sent_str, 100, "%c", sent_date); + return g_strdup_printf (N_("On %s, %s wrote:\n"), + sent_str, + (from) ? from : EMPTY_STRING); +} + + +static gchar * +modest_text_utils_quote_plain_text (const gchar *text, + const gchar *cite, + int limit) +{ + const gchar *iter; + gint indent, breakpoint, rem_indent = 0; + GString *q, *l, *remaining; + gsize len; + /* remaining will store the rest of the line if we have to break it */ - remaining = g_string_new(""); - - - gtk_text_buffer_get_iter_at_line(buf, &iter, 0); + q = g_string_new (cite); + remaining = g_string_new (""); + + iter = text; + len = strlen(text); do { - l = get_next_line(buf, &iter); - indent = get_indent_level(l->str); - unquote_line(l); - + l = get_next_line (text, len, iter); + iter = iter + l->len + 1; + indent = get_indent_level (l->str); + unquote_line (l); + if (remaining->len) { if (l->len && indent == rem_indent) { - g_string_prepend(l, " "); - g_string_prepend(l, remaining->str); + g_string_prepend (l, " "); + g_string_prepend (l, remaining->str); } else { do { - breakpoint = get_breakpoint(remaining->str, rem_indent, limit); - append_quoted(q, rem_indent, remaining, breakpoint); - g_string_erase(remaining, 0, breakpoint); + breakpoint = + get_breakpoint (remaining->str, + rem_indent, + limit); + append_quoted (q, rem_indent, + remaining, breakpoint); + g_string_erase (remaining, 0, + breakpoint); if (remaining->str[0] == ' ') { - g_string_erase(remaining, 0, 1); - } + g_string_erase (remaining, 0, + 1); + } } while (remaining->len); } } - g_string_free(remaining, TRUE); - breakpoint = get_breakpoint(l->str, indent, limit); - remaining = g_string_new(l->str + breakpoint); + g_string_free (remaining, TRUE); + breakpoint = get_breakpoint (l->str, indent, limit); + remaining = g_string_new (l->str + breakpoint); if (remaining->str[0] == ' ') { - g_string_erase(remaining, 0, 1); + g_string_erase (remaining, 0, 1); } rem_indent = indent; - append_quoted(q, indent, l, breakpoint); - g_string_free(l, TRUE); - } while (remaining->str[0] || !gtk_text_iter_is_end(&iter)); + append_quoted (q, indent, l, breakpoint); + g_string_free (l, TRUE); + } while ((iter < text + len) || (remaining->str[0])); + + return g_string_free (q, FALSE); +} + +static gchar* +modest_text_utils_quote_html (const gchar *text, + const gchar *cite, + int limit) +{ + const gchar *format = \ + "\n" \ + "\n" \ + "\n" \ + "%s" \ + "
\n%s\n
\n" \ + "\n" \ + "\n"; + + return g_strdup_printf (format, cite, text); +} + +static gint +cmp_offsets_reverse (const url_match_t *match1, const url_match_t *match2) +{ + return match2->offset - match1->offset; +} + + + +/* + * 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) +{ + regmatch_t rm; + guint rv, i, offset = 0; + GSList *match_list = NULL; + + static url_match_pattern_t patterns[] = MAIL_VIEWER_URL_MATCH_PATTERNS; + const size_t pattern_num = sizeof(patterns)/sizeof(url_match_pattern_t); + + /* initalize the regexps */ + for (i = 0; i != pattern_num; ++i) { + patterns[i].preg = g_slice_new0 (regex_t); + + /* this should not happen */ + g_return_val_if_fail (regcomp (patterns[i].preg, patterns[i].regex, + REG_ICASE|REG_EXTENDED|REG_NEWLINE) == 0, NULL); + } + /* find all the matches */ + for (i = 0; i != pattern_num; ++i) { + offset = 0; + while (1) { + int test_offset; + 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); + match->offset = 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; + } + } + + for (i = 0; i != pattern_num; ++i) { + regfree (patterns[i].preg); + g_slice_free (regex_t, patterns[i].preg); + } /* don't free patterns itself -- it's static */ - return g_string_free(q, FALSE); + /* now sort the list, so the matches are in reverse order of occurence. + * that way, we can do the replacements starting from the end, so we don't need + * to recalculate the offsets + */ + match_list = g_slist_sort (match_list, + (GCompareFunc)cmp_offsets_reverse); + return match_list; +} + + + +static void +hyperlinkify_plain_text (GString *txt) +{ + GSList *cursor; + GSList *match_list = get_url_matches (txt); + + /* we will work backwards, so the offsets stay valid */ + for (cursor = match_list; cursor; cursor = cursor->next) { + + url_match_t *match = (url_match_t*) cursor->data; + gchar *url = g_strndup (txt->str + match->offset, match->len); + gchar *repl = NULL; /* replacement */ + + /* the prefix is NULL: use the one that is already there */ + repl = g_strdup_printf ("%s", + match->prefix ? match->prefix : EMPTY_STRING, + url, url); + + /* replace the old thing with our hyperlink + * replacement thing */ + g_string_erase (txt, match->offset, match->len); + g_string_insert (txt, match->offset, repl); + + g_free (url); + g_free (repl); + + g_slice_free (url_match_t, match); + } + + g_slist_free (match_list); +} + + + +gchar* +modest_text_utils_get_display_address (gchar *address) +{ + gchar *cursor; + + if (!address) + return NULL; + + g_return_val_if_fail (g_utf8_validate (address, -1, NULL), NULL); + + g_strchug (address); /* remove leading whitespace */ + + /* from display name */ + cursor = g_strstr_len (address, strlen(address), "<"); + if (cursor == address) /* there's nothing else? leave it */ + return address; + if (cursor) + cursor[0]='\0'; + + /* remove (bla bla) from display name */ + cursor = g_strstr_len (address, strlen(address), "("); + if (cursor == address) /* there's nothing else? leave it */ + return address; + if (cursor) + cursor[0]='\0'; + + g_strchomp (address); /* remove trailing whitespace */ + + return address; +} + + + +gint +modest_text_utils_get_subject_prefix_len (const gchar *sub) +{ + gint i; + static const gchar* prefix[] = { + "Re:", "RE:", "Fwd:", "FWD:", "FW:", NULL + }; + + if (!sub || (sub[0] != 'R' && sub[0] != 'F')) /* optimization */ + return 0; + + i = 0; + + while (prefix[i]) { + if (g_str_has_prefix(sub, prefix[i])) { + int prefix_len = strlen(prefix[i]); + if (sub[prefix_len] == ' ') + ++prefix_len; /* ignore space after prefix as well */ + return prefix_len; + } + ++i; + } + return 0; +} + + +gint +modest_text_utils_utf8_strcmp (const gchar* s1, const gchar *s2, gboolean insensitive) +{ + gint result = 0; + gchar *n1, *n2; + + /* work even when s1 and/or s2 == NULL */ + if (G_UNLIKELY(s1 == s2)) + return 0; + + /* if it's not case sensitive */ + if (!insensitive) + return strcmp (s1 ? s1 : "", s2 ? s2 : ""); + + n1 = g_utf8_collate_key (s1 ? s1 : "", -1); + n2 = g_utf8_collate_key (s2 ? s2 : "", -1); + + result = strcmp (n1, n2); + + g_free (n1); + g_free (n2); + + return result; +} + + +gchar* +modest_text_utils_get_display_date (time_t date) +{ + time_t now; + const guint BUF_SIZE = 64; + gchar date_buf[BUF_SIZE]; + gchar now_buf [BUF_SIZE]; + + now = time (NULL); + + modest_text_utils_strftime (date_buf, BUF_SIZE, "%d/%m/%Y", date); + modest_text_utils_strftime (now_buf, BUF_SIZE, "%d/%m/%Y", now); /* today */ + + /* 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, "%H:%M %P", date); + + return g_strdup(date_buf); +} + +gboolean +modest_text_utils_validate_email_address (const gchar *email_address) +{ + int count = 0; + const gchar *c = NULL, *domain = NULL; + static gchar *rfc822_specials = "()<>@,;:\\\"[]"; + + /* first we validate the name portion (name@domain) */ + for (c = email_address; *c; c++) { + if (*c == '\"' && + (c == email_address || + *(c - 1) == '.' || + *(c - 1) == '\"')) { + while (*++c) { + if (*c == '\"') + break; + if (*c == '\\' && (*++c == ' ')) + continue; + if (*c <= ' ' || *c >= 127) + return FALSE; + } + if (!*c++) + return FALSE; + if (*c == '@') + break; + if (*c != '.') + return FALSE; + continue; + } + if (*c == '@') + break; + if (*c <= ' ' || *c >= 127) + return FALSE; + if (strchr(rfc822_specials, *c)) + return FALSE; + } + if (c == email_address || *(c - 1) == '.') + return FALSE; + + /* next we validate the domain portion (name@domain) */ + if (!*(domain = ++c)) + return FALSE; + do { + if (*c == '.') { + if (c == domain || *(c - 1) == '.') + return FALSE; + count++; + } + if (*c <= ' ' || *c >= 127) + return FALSE; + if (strchr(rfc822_specials, *c)) + return FALSE; + } while (*++c); + + return (count >= 1) ? TRUE : FALSE; +} + + + + +gchar * +modest_text_utils_get_display_size (guint size) +{ + const guint KB=1024; + const guint MB=1024 * KB; + const guint GB=1024 * MB; + const guint TB=1024 * GB; + + if (size < KB) + return g_strdup_printf (_("%0.1f Kb"), (double)size / KB); + else if (size < MB) + return g_strdup_printf (_("%d Kb"), size / KB); + else if (size < GB) + return g_strdup_printf (_("%d Mb"), size / MB); + else if (size < TB) + return g_strdup_printf (_("%d Gb"), size/ GB); + else + return g_strdup_printf (_("Very big")); }
%s%s
%s%s
%s%s
%s%s