+
+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
+modest_text_utils_get_display_address (gchar *address)
+{
+ int i;
+
+ g_return_if_fail (address);
+
+ if (!address)
+ return;
+
+ /* should not be needed, and otherwise, we probably won't screw up the address
+ * more than it already is :)
+ * g_return_val_if_fail (g_utf8_validate (address, -1, NULL), NULL);
+ * */
+
+ /* remove leading whitespace */
+ if (address[0] == ' ')
+ g_strchug (address);
+
+ for (i = 0; address[i]; ++i) {
+ if (address[i] == '<') {
+ if (G_UNLIKELY(i == 0)) {
+ break; /* there's nothing else, leave it */
+ }else {
+ address[i] = '\0'; /* terminate the string here */
+ 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 *
+modest_text_utils_get_email_address (const gchar *full_address)
+{
+ const gchar *left, *right;
+
+ g_return_val_if_fail (full_address, NULL);
+
+ if (!full_address)
+ return NULL;
+
+ g_return_val_if_fail (g_utf8_validate (full_address, -1, NULL), NULL);
+
+ left = g_strrstr_len (full_address, strlen(full_address), "<");
+ if (left == NULL)
+ return g_strdup (full_address);
+
+ right = g_strstr_len (left, strlen(left), ">");
+ if (right == NULL)
+ return g_strdup (full_address);
+
+ return g_strndup (left + 1, right - left - 1);
+}
+
+gint
+modest_text_utils_get_subject_prefix_len (const gchar *sub)
+{
+ gint prefix_len = 0;
+
+ g_return_val_if_fail (sub, 0);
+
+ if (!sub)
+ return 0;
+
+ /* optimization: "Re", "RE", "re","Fwd", "FWD", "fwd","FW","Fw", "fw" */
+ if (sub[0] != 'R' && sub[0] != 'F' && sub[0] != 'r' && sub[0] != 'f')
+ return 0;
+ else if (sub[0] && sub[1] != 'e' && sub[1] != 'E' && sub[1] != 'w' && sub[1] != 'W')
+ return 0;
+
+ prefix_len = 2;
+ if (sub[2] == 'd')
+ ++prefix_len;
+
+ /* skip over a [...] block */
+ if (sub[prefix_len] == '[') {
+ int c = prefix_len + 1;
+ while (sub[c] && sub[c] != ']')
+ ++c;
+ if (!sub[c])
+ return 0; /* no end to the ']' found */
+ else
+ prefix_len = c + 1;
+ }
+
+ /* did we find the ':' ? */
+ if (sub[prefix_len] == ':') {
+ ++prefix_len;
+ if (sub[prefix_len] == ' ')
+ ++prefix_len;
+ prefix_len += modest_text_utils_get_subject_prefix_len (sub + prefix_len);
+/* g_warning ("['%s','%s']", sub, (char*) sub + prefix_len); */
+ return prefix_len;
+ } else
+ return 0;
+}
+
+
+gint
+modest_text_utils_utf8_strcmp (const gchar* s1, const gchar *s2, gboolean insensitive)
+{
+
+/* work even when s1 and/or s2 == NULL */
+ if (G_UNLIKELY(s1 == s2))
+ return 0;
+ if (G_UNLIKELY(!s1))
+ return -1;
+ if (G_UNLIKELY(!s2))
+ return 1;
+
+ /* if it's not case sensitive */
+ if (!insensitive) {
+
+ /* optimization: shortcut if first char is ascii */
+ if (((s1[0] & 0x80)== 0) && ((s2[0] & 0x80) == 0) &&
+ (s1[0] != s2[0]))
+ return s1[0] - s2[0];
+
+ return g_utf8_collate (s1, s2);
+
+ } else {
+ gint result;
+ gchar *n1, *n2;
+
+ /* optimization: shortcut if first char is ascii */
+ if (((s1[0] & 0x80) == 0) && ((s2[0] & 0x80) == 0) &&
+ (tolower(s1[0]) != tolower (s2[0])))
+ return tolower(s1[0]) - tolower(s2[0]);
+
+ n1 = g_utf8_strdown (s1, -1);
+ n2 = g_utf8_strdown (s2, -1);
+
+ result = g_utf8_collate (n1, n2);
+
+ g_free (n1);
+ g_free (n2);
+
+ return result;
+ }
+}
+
+
+const gchar*
+modest_text_utils_get_display_date (time_t date)
+{
+#define DATE_BUF_SIZE 64
+ static gchar date_buf[DATE_BUF_SIZE];
+
+ /* calculate the # of days since epoch for
+ * for today and for the date provided
+ * based on idea from pvanhoof */
+ int day = time(NULL) / (24 * 60 * 60);
+ int date_day = date / (24 * 60 * 60);
+
+ /* 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! */
+}
+
+
+
+gboolean
+modest_text_utils_validate_folder_name (const gchar *folder_name)
+{
+ /* based on http://msdn2.microsoft.com/en-us/library/aa365247.aspx,
+ * with some extras */
+
+ guint len;
+ gint i;
+ const gchar **cursor = NULL;
+ const gchar *forbidden_names[] = { /* windows does not like these */
+ "CON", "PRN", "AUX", "NUL", ".", "..", "cur", "tmp", "new",
+ NULL /* cur, tmp, new are reserved for Maildir */
+ };
+
+ /* cannot be NULL */
+ if (!folder_name)
+ return FALSE;
+
+ /* cannot be empty */
+ len = strlen(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;
+
+ /* cannot contain a forbidden char */
+ 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) {
+ for (cursor = forbidden_names; cursor && *cursor; ++cursor) {
+ if (g_ascii_strcasecmp (folder_name, *cursor) == 0)
+ return FALSE;
+ }
+ }
+
+ return TRUE; /* it's valid! */
+}
+
+
+
+gboolean
+modest_text_utils_validate_domain_name (const gchar *domain)
+{
+ gboolean valid = FALSE;
+ regex_t rx;
+ const gchar* domain_regex = "^([a-z0-9-]*[a-z0-9]\\.)+[a-z0-9-]*[a-z0-9]$";
+
+ g_return_val_if_fail (domain, FALSE);
+
+ if (!domain)
+ return FALSE;
+
+ memset (&rx, 0, sizeof(regex_t)); /* coverity wants this... */
+
+ /* domain name: all alphanum or '-' or '.',
+ * but beginning/ending in alphanum */
+ if (regcomp (&rx, domain_regex, REG_ICASE|REG_EXTENDED|REG_NOSUB)) {
+ g_warning ("BUG: error in regexp");
+ return FALSE;
+ }
+
+ valid = (regexec (&rx, domain, 1, NULL, 0) == 0);
+ regfree (&rx);
+
+ return valid;
+}
+
+
+
+gboolean
+modest_text_utils_validate_email_address (const gchar *email_address,
+ const gchar **invalid_char_position)
+{
+ int count = 0;
+ const gchar *c = NULL, *domain = NULL;
+ static gchar *rfc822_specials = "()<>@,;:\\\"[]&";
+
+ if (invalid_char_position)
+ *invalid_char_position = NULL;
+
+ g_return_val_if_fail (email_address, FALSE);
+
+ /* check that the email adress contains exactly one @ */
+ if (!strstr(email_address, "@") ||
+ (strstr(email_address, "@") != g_strrstr(email_address, "@")))
+ return FALSE;
+
+ /* 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)) {
+ if (invalid_char_position)
+ *invalid_char_position = 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) == '.' || *(c + 1) == '\0')
+ return FALSE;
+ count++;
+ }
+ if (*c <= ' ' || *c >= 127)
+ return FALSE;
+ if (strchr(rfc822_specials, *c)) {
+ if (invalid_char_position)
+ *invalid_char_position = c;
+ return FALSE;
+ }
+ } while (*++c);
+
+ return (count >= 1) ? TRUE : FALSE;
+}
+
+gboolean
+modest_text_utils_validate_recipient (const gchar *recipient, const gchar **invalid_char_position)
+{
+ gchar *stripped, *current;
+ gchar *right_part;
+ gboolean has_error = FALSE;
+
+ if (invalid_char_position)
+ *invalid_char_position = NULL;
+
+ g_return_val_if_fail (recipient, FALSE);
+
+ if (modest_text_utils_validate_email_address (recipient, invalid_char_position))
+ return TRUE;
+
+ stripped = g_strdup (recipient);
+ stripped = g_strstrip (stripped);
+ current = stripped;
+
+ if (*current == '\0') {
+ g_free (stripped);
+ return FALSE;
+ }
+
+ /* 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)) {
+ if (*current == '\\') {
+ /* TODO: This causes a warning, which breaks the build,
+ * because a gchar cannot be < 0.
+ * murrayc.
+ if (current[1] <0) {
+ has_error = TRUE;
+ break;
+ }
+ */
+ } else if (*current == '\"') {
+ has_error = FALSE;
+ current = g_utf8_next_char (current);
+ 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)) {
+ if (*current == '<') {
+ has_error = FALSE;
+ break;
+ }
+ }
+ }
+
+ if (has_error) {
+ g_free (stripped);
+ return FALSE;
+ }
+
+ right_part = g_strdup (current);
+ 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;
+ gboolean valid;
+
+ address = g_strndup (right_part+1, strlen (right_part) - 2);
+ g_free (right_part);
+ valid = modest_text_utils_validate_email_address (address, invalid_char_position);
+ g_free (address);
+ return valid;
+ } else {
+ g_free (right_part);
+ return FALSE;
+ }
+}
+
+
+gchar *
+modest_text_utils_get_display_size (guint64 size)
+{
+ const guint KB=1024;
+ const guint MB=1024 * KB;
+ const guint GB=1024 * MB;
+
+ if (size == 0)
+ return g_strdup_printf (_FM("sfil_li_size_kb"), (int) 0);
+ if (0 <= size && size < KB)
+ return g_strdup_printf (_FM("sfil_li_size_1kb_99kb"), (int) 1);
+ else if (KB <= size && size < 100 * KB)
+ return g_strdup_printf (_FM("sfil_li_size_1kb_99kb"), (int) size / KB);
+ else if (100*KB <= size && size < MB)
+ return g_strdup_printf (_FM("sfil_li_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);
+ else if (10*MB <= size && size < GB)
+ return g_strdup_printf (_FM("sfil_li_size_10mb_1gb"), (float) size / MB);
+ else
+ return g_strdup_printf (_FM("sfil_li_size_1gb_or_greater"), (float) size / GB);
+}
+
+static gchar *
+get_email_from_address (const gchar * address)
+{
+ gchar *left_limit, *right_limit;
+
+ left_limit = strstr (address, "<");
+ right_limit = g_strrstr (address, ">");
+
+ if ((left_limit == NULL)||(right_limit == NULL)|| (left_limit > right_limit))
+ return g_strdup (address);
+ else
+ return g_strndup (left_limit + 1, (right_limit - left_limit) - 1);
+}
+
+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,
+ (color->green >> 12) & 0xf, (color->green >> 8) & 0xf,
+ (color->green >> 4) & 0xf, (color->green) & 0xf,
+ (color->blue >> 12) & 0xf, (color->blue >> 8) & 0xf,
+ (color->blue >> 4) & 0xf, (color->blue) & 0xf);
+}
+
+gchar *
+modest_text_utils_text_buffer_get_text (GtkTextBuffer *buffer)
+{
+ GtkTextIter start, end;
+ gchar *slice, *current;
+ GString *result = g_string_new ("");
+
+ g_return_val_if_fail (buffer && GTK_IS_TEXT_BUFFER (buffer), NULL);
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ slice = gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
+ current = slice;
+
+ while (current && current != '\0') {
+ if (g_utf8_get_char (current) == 0xFFFC) {
+ result = g_string_append_c (result, ' ');
+ current = g_utf8_next_char (current);
+ } else {
+ gchar *next = g_utf8_strchr (current, -1, 0xFFFC);
+ if (next == NULL) {
+ result = g_string_append (result, current);
+ } else {
+ result = g_string_append_len (result, current, next - current);
+ }
+ current = next;
+ }
+ }
+ g_free (slice);
+
+ return g_string_free (result, FALSE);
+
+}
+
+gboolean
+modest_text_utils_is_forbidden_char (const gchar character,
+ ModestTextUtilsForbiddenCharType type)
+{
+ gint i, len;
+ const gchar *forbidden_chars = NULL;
+
+ /* We need to get the length in the switch because the
+ compiler needs to know the size at compile time */
+ switch (type) {
+ case ACCOUNT_TITLE_FORBIDDEN_CHARS:
+ forbidden_chars = account_title_forbidden_chars;
+ len = G_N_ELEMENTS (account_title_forbidden_chars);
+ break;
+ case FOLDER_NAME_FORBIDDEN_CHARS:
+ forbidden_chars = folder_name_forbidden_chars;
+ len = G_N_ELEMENTS (folder_name_forbidden_chars);
+ break;
+ case USER_NAME_FORBIDDEN_NAMES:
+ forbidden_chars = user_name_forbidden_chars;
+ len = G_N_ELEMENTS (user_name_forbidden_chars);
+ break;
+ default:
+ g_return_val_if_reached (TRUE);
+ }
+
+ for (i = 0; i < len ; i++)
+ if (forbidden_chars[i] == character)
+ return TRUE;
+
+ 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;
+}
+
+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)
+{
+ 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);
+}
+
+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 <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, 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, (blank - *start));
+ *str = g_string_append_c (*str, '"');
+ *str = g_string_append_len (*str, blank, (*cur - 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);
+}