* make modest handle mailing lists somewhat correcter (RFC2369):
authorDirk-Jan C. Binnema <dirk-jan.binnema@nokia.com>
Sun, 16 Dec 2007 12:48:55 +0000 (12:48 +0000)
committerDirk-Jan C. Binnema <dirk-jan.binnema@nokia.com>
Sun, 16 Dec 2007 12:48:55 +0000 (12:48 +0000)
- with-reply all, reply to both the old From: and the Reply-To: address
  (in non-mailinglist cases, we ignore From: if there's a Reply-To:)
- remove duplicate entries in the To: field
- added modest_text_utils_remove_duplicate_addresses (name says it all)
- rewrote modest_text_utils_split_addresses_list; the old version appended
          the ","-seperator in some cases; the old impl is still there but #ifdef'd-out
  jdapena: please check the new impl, I think you wrote the old one
- added modest_tny_msg_get_header to get some header; I think a version
          of this function should go to Tinymail instead;

should fix NB #78638

Note: the real bug there seems to be that tny_header_get_reply_to always(?)
returns NULL; I worked around it by using the abovementioned,
modest_tny_msg_get_header (msg, "Reply-To")
now it works.

pmo-trunk-r3927

src/modest-text-utils.c
src/modest-text-utils.h
src/modest-tny-msg.c
src/modest-tny-msg.h

index f6f6efb..de70fa4 100644 (file)
@@ -330,10 +330,48 @@ modest_text_utils_remove_address (const gchar *address_list, const gchar *addres
        return result;
 }
 
+
+gchar*
+modest_text_utils_remove_duplicate_addresses (const gchar *address_list)
+{
+       GSList *addresses, *cursor;
+       GHashTable *table;
+       gchar *new_list;
+       
+       g_return_val_if_fail (address_list, NULL);
+
+       table = g_hash_table_new (g_str_hash, g_str_equal);
+       addresses = modest_text_utils_split_addresses_list (address_list);
+
+       new_list = g_strdup("");
+       cursor = addresses;
+       while (cursor) {
+               const gchar* address = (const gchar*)cursor->data;
+
+               /* 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);
+                       new_list = tmp;
+                       
+                       g_hash_table_insert (table, (gchar*)address, GINT_TO_POINTER(1));
+               }
+               cursor = g_slist_next (cursor);
+       }
+
+       g_hash_table_destroy (table);
+       g_slist_foreach (addresses, (GFunc)g_free, NULL);
+       g_slist_free (addresses);
+
+       return new_list;
+}
+
+
 static void
 modest_text_utils_convert_buffer_to_html_start (GString *html, const gchar *data, gssize n)
 {
-       guint            i;
+       guint           i;
        gboolean        space_seen = FALSE;
        guint           break_dist = 0; /* distance since last break point */
 
@@ -514,6 +552,7 @@ modest_text_utils_get_addresses_indexes (const gchar *addresses, GSList **start_
        return;
 }
 
+#if 0
 GSList *
 modest_text_utils_split_addresses_list (const gchar *addresses)
 {
@@ -556,6 +595,43 @@ modest_text_utils_split_addresses_list (const gchar *addresses)
        return result;
 
 }
+#endif
+
+
+
+
+GSList *
+modest_text_utils_split_addresses_list (const gchar *addresses)
+{
+       GSList *head;
+       const gchar *my_addrs = addresses;
+       const gchar *end;
+       gchar *addr;
+       
+       /* skip any space, ',', ';' at the start */
+       while (my_addrs && (my_addrs[0] == ' ' || my_addrs[0] == ',' || my_addrs[0] == ';'))
+              ++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;
+       while (end[0] && end[0] != ',' && end[0] != ';')
+               ++end;
+
+       /* we got the address; copy it and remove trailing whitespace */
+       addr = g_strndup (my_addrs, end - my_addrs);
+       g_strchomp (addr);
+
+       head = g_slist_append (NULL, addr);
+       head->next = modest_text_utils_split_addresses_list (end); /* recurse */
+
+       return head;
+}
+
 
 void
 modest_text_utils_address_range_at_position (const gchar *recipients_list,
index 4077bab..7944dbe 100644 (file)
@@ -128,11 +128,11 @@ gchar*   modest_text_utils_inline (const gchar *text,
 
 /**
  * modest_text_utils_remove_address
- * @address_list: none-NULL string with a comma-separated list of email addresses
+ * @address_list: non-NULL string with a comma-separated list of email addresses
  * @address: an specific e-mail address 
  *
  * remove a specific address from a list of email addresses; if @address
- * is NULL, returns an unchanged @address_list
+ * is NULL, returns an unchanged (but newly allocated) @address_list
  * 
  * Returns: a newly allocated string containing the new list, or NULL
  * in case of error or the original @address_list was NULL
@@ -140,6 +140,20 @@ gchar*   modest_text_utils_inline (const gchar *text,
 gchar*   modest_text_utils_remove_address (const gchar *address_list, 
                                           const gchar *address);
 
+
+/**
+ * modest_text_utils_remove_duplicate_addresses
+ * @address_list: non-NULL string with a comma-separated list of email addresses
+ *
+ * remove duplicate addresses from a list of email addresses
+ * 
+ * Returns: a newly allocated string containing the new list, or NULL
+ * in case of error or the original @address_list was NULL
+ */
+gchar*   modest_text_utils_remove_duplicate_addresses (const gchar *address_list); 
+                                                   
+
+
 /**
  * modest_text_utils_address_range_at_position:
  * @address_list: non-NULL utf8 string containing a list of addresses
@@ -352,10 +366,11 @@ gboolean     modest_text_utils_validate_recipient (const gchar *recipient,
  * obtains a GSList of addresses from a string of addresses
  * in the format understood by email protocols
  *
- * Returns: a GSList of strings
+ * Returns: a newly allocated GSList of strings
  **/
 GSList      *modest_text_utils_split_addresses_list (const gchar *addresses);
 
+
 /**
  * modest_text_utils_get_addresses_indexes:
  * @addresses: a string
index 6e77d81..fb70314 100644 (file)
@@ -619,38 +619,87 @@ modest_tny_msg_create_forward_msg (TnyMsg *msg,
 }
 
 
+gchar*
+modest_tny_msg_get_header (TnyMsg *msg, const gchar *header)
+{
+       TnyList *pairs;
+       TnyIterator *iter;
+       gchar *val;
+       
+       g_return_val_if_fail (msg && TNY_IS_MSG(msg), NULL);
+       g_return_val_if_fail (header, NULL);
+
+       pairs = tny_simple_list_new ();
+
+       tny_mime_part_get_header_pairs (TNY_MIME_PART(msg), pairs);
+       iter = tny_list_create_iterator (pairs);
+
+       val = NULL;
+       while (!tny_iterator_is_done(iter) && !val) {
+
+               TnyPair *pair = (TnyPair*)tny_iterator_get_current(iter);
+               if (strcasecmp (header, tny_pair_get_name(pair)) == 0)
+                       val = g_strdup (tny_pair_get_value(pair));
+               
+               tny_iterator_next (iter);
+       }
+
+       g_object_unref (pairs);
+       g_object_unref (iter);
+
+       return val;
+}
+
+
+
 /* get the new To:, based on the old header,
  * result is newly allocated or NULL in case of error
- * TODO: mailing list handling
  * */
 static gchar*
-get_new_to (TnyHeader *header, const gchar* from, ModestTnyMsgReplyMode mode)
+get_new_to (TnyMsg *msg, TnyHeader *header, const gchar* from,
+           ModestTnyMsgReplyMode reply_mode)
 {
-       const gchar* old_to;
-       const gchar* old_reply_to_from;
-
+       const gchar* old_reply_to;
+       const gchar* old_from;
        gchar* new_to;
        
+       /* according to RFC2369 (http://www.faqs.org/rfcs/rfc2369.html), we
+        * can identify Mailing-List posts by the List-Help header.
+        * for mailing lists, both the Reply-To: and From: should be included
+        * in the new To:; for now, we're ignoring List-Post
+        */
+       gchar* list_help = modest_tny_msg_get_header (msg, "List-Help");
+       gboolean is_mailing_list = (list_help != NULL);
+       g_free (list_help);
+
+
        /* reply to sender, use ReplyTo or From */
-       old_reply_to_from = tny_header_get_replyto (header);
-       if (old_reply_to_from)
-               new_to = g_strdup (old_reply_to_from);
-       else {
-               old_reply_to_from = tny_header_get_from (header);
-               if (old_reply_to_from)
-                       new_to = g_strdup (old_reply_to_from);
-               else {
-                       g_warning ("%s: failed to get either Reply-To: or From: from header",
-                                  __FUNCTION__);
-                       return NULL;
-               }
+       //old_reply_to = tny_header_get_replyto (header);
+       old_reply_to = modest_tny_msg_get_header (msg, "Reply-To"); 
+       old_from     = tny_header_get_from (header);
+       
+       if (!old_from &&  !old_reply_to) {
+               g_warning ("%s: failed to get either Reply-To: or From: from header",
+                          __FUNCTION__);
+               return NULL;
        }
-                       
+       
+       /* for mailing lists, use both Reply-To and From if we did a
+        * 'Reply All:'
+        * */
+       if (is_mailing_list && reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL &&
+           old_reply_to && old_from && strcmp (old_from, old_reply_to) != 0)
+               new_to = g_strjoin (",", old_reply_to, old_from, NULL);
+       else
+               /* otherwise use either Reply-To: (preferred) or From: */
+               new_to = g_strdup (old_reply_to ? old_reply_to : old_from);
+
        /* in case of ReplyAll, we need to add the Recipients in the old To: */
-       if (mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
-               old_to = tny_header_get_to (header);
+       if (reply_mode == MODEST_TNY_MSG_REPLY_MODE_ALL) {
+               const gchar *old_to = tny_header_get_to (header);
                if (!old_to) 
-                       g_warning ("%s: no To: address found in source mail", __FUNCTION__);
+                       g_warning ("%s: no To: address found in source mail",
+                                  __FUNCTION__);
                else {
                        /* append the old To: */
                        gchar *tmp = g_strjoin (",", new_to, old_to, NULL);
@@ -663,7 +712,12 @@ get_new_to (TnyHeader *header, const gchar* from, ModestTnyMsgReplyMode mode)
        gchar *tmp = modest_text_utils_remove_address (new_to, from);
        g_free (new_to);
        new_to = tmp;
-       
+
+       /* remove duplicate entries */
+       tmp = modest_text_utils_remove_duplicate_addresses (new_to);
+       g_free (new_to);
+       new_to = tmp;
+
        return new_to;
 }
 
@@ -671,7 +725,7 @@ get_new_to (TnyHeader *header, const gchar* from, ModestTnyMsgReplyMode mode)
 /* get the new Cc:, based on the old header,
  * result is newly allocated or NULL in case of error */
 static gchar*
-get_new_cc (TnyHeader *header, const gchar* from, ModestTnyMsgReplyMode mode)
+get_new_cc (TnyHeader *header, const gchar* from)
 {
        const gchar *old_cc;
 
@@ -695,7 +749,6 @@ modest_tny_msg_create_reply_msg (TnyMsg *msg,
        TnyMsg *new_msg = NULL;
        TnyHeader *new_header;
        gchar *new_to, *new_cc = NULL;
-       
        TnyList *parts = NULL;
        GList *attachments_list = NULL;
 
@@ -723,17 +776,16 @@ modest_tny_msg_create_reply_msg (TnyMsg *msg,
        else
                header = tny_msg_get_header (msg);
 
-       new_header = tny_msg_get_header(new_msg);
        
-       new_to = get_new_to (header, from, reply_mode);
+       new_header = tny_msg_get_header(new_msg);
+       new_to = get_new_to (msg, header, from, reply_mode);
        if (!new_to)
                g_warning ("%s: failed to get new To:", __FUNCTION__);
        else {
                tny_header_set_to (new_header, new_to);
                g_free (new_to);
-       }
-               
-       new_cc = get_new_cc (header, from, reply_mode);
+       }       
+       new_cc = get_new_cc (header, from);
        if (new_cc) { 
                tny_header_set_cc (new_header, new_cc);
                g_free (new_cc);
index 95bfbc6..f83eb2f 100644 (file)
@@ -115,7 +115,7 @@ TnyMimePart*  modest_tny_msg_find_body_part  (TnyMsg * self, gboolean want_html)
 
 /**
  * modest_tny_msg_find_body:
- * @self: 
+ * @self: some #TnyMsg
  * @want_html: 
  * @is_html: if the original body was html or plain text
  * 
@@ -128,6 +128,20 @@ TnyMimePart*  modest_tny_msg_find_body_part  (TnyMsg * self, gboolean want_html)
 gchar*        modest_tny_msg_get_body        (TnyMsg *self, gboolean want_html, gboolean *is_html);
 
 
+
+/**
+ * modest_tny_msg_get_header:
+ * @self: some #TnyMsg 
+ * @header: the header to get
+ * 
+ * gets the mail header for a #TnyMsg as a newly allocated string,
+ * or NULL if it cannot be found
+ * 
+ * Returns: the header
+ **/
+gchar*       modest_tny_msg_get_header (TnyMsg *msg, const gchar *header);
+
+
 /**
  * modest_tny_msg_create_forward_msg:
  * @msg: a valid #TnyMsg instance