[geary/wip/714922-multiple-addresses-2] Use RFC822 mailboxes rather than flat strings for alternate emails



commit 001c3713cb62059973bf163fd790aac19801cc74
Author: Jim Nelson <jim yorba org>
Date:   Wed Mar 4 15:40:57 2015 -0800

    Use RFC822 mailboxes rather than flat strings for alternate emails

 .../account-dialog-edit-alternate-emails-pane.vala |   56 +++++++++-----------
 src/client/composer/composer-widget.vala           |   37 +++++--------
 .../formatted-conversation-data.vala               |   35 +++++--------
 src/engine/api/geary-account-information.vala      |   45 ++++++++++------
 src/engine/rfc822/rfc822-mailbox-address.vala      |   21 +++++---
 src/engine/rfc822/rfc822-mailbox-addresses.vala    |   32 +++++++++++-
 src/engine/rfc822/rfc822-utils.vala                |   45 ++++------------
 src/engine/util/util-string.vala                   |    4 ++
 8 files changed, 143 insertions(+), 132 deletions(-)
---
diff --git a/src/client/accounts/account-dialog-edit-alternate-emails-pane.vala 
b/src/client/accounts/account-dialog-edit-alternate-emails-pane.vala
index 295c850..25f583f 100644
--- a/src/client/accounts/account-dialog-edit-alternate-emails-pane.vala
+++ b/src/client/accounts/account-dialog-edit-alternate-emails-pane.vala
@@ -6,12 +6,12 @@
 
 public class AccountDialogEditAlternateEmailsPane : AccountDialogPane {
     private class ListItem : Gtk.Label {
-        public string address;
+        public Geary.RFC822.MailboxAddress mailbox;
         
-        public ListItem(string address) {
-            this.address = address;;
+        public ListItem(Geary.RFC822.MailboxAddress mailbox) {
+            this.mailbox = mailbox;
             
-            label = "<b>%s</b>".printf(Geary.HTML.escape_markup(address));
+            label = "<b>%s</b>".printf(Geary.HTML.escape_markup(mailbox.get_full_address()));
             use_markup = true;
             ellipsize = Pango.EllipsizeMode.END;
             xalign = 0.0f;
@@ -33,8 +33,7 @@ public class AccountDialogEditAlternateEmailsPane : AccountDialogPane {
     
     private Geary.AccountInformation? account_info = null;
     private Geary.RFC822.MailboxAddress? primary_mailbox = null;
-    private Gee.HashSet<string> email_addresses = new Gee.HashSet<string>(
-        Geary.String.stri_hash, Geary.String.stri_equal);
+    private Gee.HashSet<Geary.RFC822.MailboxAddress> mailboxes = new 
Gee.HashSet<Geary.RFC822.MailboxAddress>();
     
     public signal void done();
     
@@ -74,7 +73,7 @@ public class AccountDialogEditAlternateEmailsPane : AccountDialogPane {
         update_button.clicked.connect(on_update_clicked);
     }
     
-    private bool validate_address_text(string email_address, out string? parsed) {
+    private bool validate_address_text(string email_address, out Geary.RFC822.MailboxAddress? parsed) {
         parsed = null;
         
         Geary.RFC822.MailboxAddresses mailboxes = new Geary.RFC822.MailboxAddresses.from_rfc822_string(
@@ -93,14 +92,14 @@ public class AccountDialogEditAlternateEmailsPane : AccountDialogPane {
         if (Geary.String.is_empty(mailbox.address))
             return false;
         
-        parsed = mailbox.address;
+        parsed = mailbox;
         
         return true;
     }
     
     private bool transform_email_to_sensitive(Binding binding, Value source, ref Value target) {
-        string? parsed;
-        target = validate_address_text(email_entry.text, out parsed) && !email_addresses.contains(parsed);
+        Geary.RFC822.MailboxAddress? parsed;
+        target = validate_address_text(email_entry.text, out parsed) && !mailboxes.contains(parsed);
         
         return true;
     }
@@ -118,7 +117,7 @@ public class AccountDialogEditAlternateEmailsPane : AccountDialogPane {
         
         email = account_info.email;
         primary_mailbox = account_info.get_primary_mailbox_address();
-        email_addresses.clear();
+        mailboxes.clear();
         changed = false;
         
         // reset/clear widgets
@@ -130,8 +129,8 @@ public class AccountDialogEditAlternateEmailsPane : AccountDialogPane {
             address_listbox.remove(widget);
         
         // Add all email addresses; add_email_address() silently drops the primary address
-        foreach (string email_address in account_info.get_all_email_addresses())
-            add_email_address(email_address, false);
+        foreach (Geary.RFC822.MailboxAddress mailbox in account_info.get_all_email_addresses())
+            add_mailbox(mailbox, false);
     }
     
     public override void present() {
@@ -142,20 +141,13 @@ public class AccountDialogEditAlternateEmailsPane : AccountDialogPane {
         add_button.has_default = true;
     }
     
-    private void add_email_address(string email_address, bool is_change) {
-        string? parsed_address;
-        if (!validate_address_text(email_address, out parsed_address))
+    private void add_mailbox(Geary.RFC822.MailboxAddress mailbox, bool is_change) {
+        if (mailboxes.contains(mailbox) || primary_mailbox.equal_to(mailbox))
             return;
         
-        if (Geary.String.is_empty(parsed_address))
-            return;
-        
-        if (email_addresses.contains(parsed_address))
-            return;
+        mailboxes.add(mailbox);
         
-        email_addresses.add(parsed_address);
-        
-        ListItem item = new ListItem(parsed_address);
+        ListItem item = new ListItem(mailbox);
         item.show_all();
         address_listbox.add(item);
         
@@ -163,15 +155,15 @@ public class AccountDialogEditAlternateEmailsPane : AccountDialogPane {
             changed = true;
     }
     
-    private void remove_email_address(string address) {
-        if (!email_addresses.remove(address))
+    private void remove_mailbox(Geary.RFC822.MailboxAddress address) {
+        if (!mailboxes.remove(address))
             return;
         
         foreach (Gtk.Widget widget in address_listbox.get_children()) {
             Gtk.ListBoxRow row = (Gtk.ListBoxRow) widget;
             ListItem item = (ListItem) row.get_child();
             
-            if (item.address == address) {
+            if (item.mailbox.equal_to(address)) {
                 address_listbox.remove(widget);
                 
                 changed = true;
@@ -187,7 +179,11 @@ public class AccountDialogEditAlternateEmailsPane : AccountDialogPane {
     }
     
     private void on_add_clicked() {
-        add_email_address(email_entry.text, true);
+        Geary.RFC822.MailboxAddress? parsed;
+        if (!validate_address_text(email_entry.text, out parsed) || parsed == null)
+            return;
+        
+        add_mailbox(parsed, true);
         
         // reset state for next input
         email_entry.text = "";
@@ -197,11 +193,11 @@ public class AccountDialogEditAlternateEmailsPane : AccountDialogPane {
     
     private void on_delete_clicked() {
         if (selected_item != null)
-            remove_email_address(selected_item.address);
+            remove_mailbox(selected_item.mailbox);
     }
     
     private void on_update_clicked() {
-        account_info.replace_alternate_emails(email_addresses);
+        account_info.replace_alternate_emails(mailboxes);
         
         done();
     }
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index f2f2353..f71cf28 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -689,9 +689,9 @@ public class ComposerWidget : Gtk.EventBox {
             compose_type = ComposeType.REPLY_ALL;
             
         to_entry.modified = cc_entry.modified = bcc_entry.modified = false;
-        if (!Geary.RFC822.Utils.equal(to_entry.addresses, reply_to_addresses))
+        if (!to_entry.addresses.equal_to(reply_to_addresses))
             to_entry.modified = true;
-        if (cc != "" && !Geary.RFC822.Utils.equal(cc_entry.addresses, reply_cc_addresses))
+        if (cc != "" && !cc_entry.addresses.equal_to(reply_cc_addresses))
             cc_entry.modified = true;
         if (bcc != "")
             bcc_entry.modified = true;
@@ -718,13 +718,12 @@ public class ComposerWidget : Gtk.EventBox {
         }
     }
     
-    private bool check_preferred_from_address(Gee.List<string> account_addresses,
+    private bool check_preferred_from_address(Gee.List<Geary.RFC822.MailboxAddress> account_addresses,
         Geary.RFC822.MailboxAddresses? referred_addresses) {
         if (referred_addresses != null) {
-            foreach (string address in account_addresses) {
-                if (referred_addresses.contains(address)) {
-                    from = new Geary.RFC822.MailboxAddresses.single(
-                        new Geary.RFC822.MailboxAddress(account.information.real_name, address));
+            foreach (Geary.RFC822.MailboxAddress address in account_addresses) {
+                if (referred_addresses.get_all().contains(address)) {
+                    from = new Geary.RFC822.MailboxAddresses.single(address);
                     return true;
                 }
             }
@@ -737,10 +736,11 @@ public class ComposerWidget : Gtk.EventBox {
             if (referred.from != null)
                 from = referred.from;
         } else {
-            Gee.List<string> account_addresses = account.information.get_all_email_addresses();
+            Gee.List<Geary.RFC822.MailboxAddress> account_addresses = 
account.information.get_all_email_addresses();
             if (!check_preferred_from_address(account_addresses, referred.to)) {
                 if (!check_preferred_from_address(account_addresses, referred.cc))
-                    check_preferred_from_address(account_addresses, referred.bcc);
+                    if (!check_preferred_from_address(account_addresses, referred.bcc))
+                        check_preferred_from_address(account_addresses, referred.from);
             }
         }
     }
@@ -989,7 +989,7 @@ public class ComposerWidget : Gtk.EventBox {
     
     private void add_recipients_and_ids(ComposeType type, Geary.Email referred,
         bool modify_headers = true) {
-        Gee.List<string> sender_addresses = account.information.get_all_email_addresses();
+        Gee.List<Geary.RFC822.MailboxAddress> sender_addresses = 
account.information.get_all_email_addresses();
         Geary.RFC822.MailboxAddresses to_addresses =
             Geary.RFC822.Utils.create_to_addresses_for_reply(referred, sender_addresses);
         Geary.RFC822.MailboxAddresses cc_addresses =
@@ -2272,22 +2272,15 @@ public class ComposerWidget : Gtk.EventBox {
             account.information.get_primary_mailbox_address());
         from_multiple.append_text(primary_address.to_rfc822_string());
         from_list.add(new FromAddressMap(account, primary_address));
-        if (!set_active && Geary.RFC822.Utils.equal(from, primary_address)) {
+        if (!set_active && from.equal_to(primary_address)) {
             from_multiple.set_active(from_list.size - 1);
             set_active = true;
         }
         
         if (account.information.alternate_emails != null) {
-            foreach (string alternate_email in account.information.alternate_emails) {
-                Geary.RFC822.MailboxAddresses addresses =
-                    new Geary.RFC822.MailboxAddresses.from_rfc822_string(alternate_email);
-                if (addresses.size == 0)
-                    continue;
-                
-                if (addresses.size == 1 && Geary.String.is_empty(addresses[0].name)) {
-                    addresses = new Geary.RFC822.MailboxAddresses.single(
-                        new Geary.RFC822.MailboxAddress(account.information.real_name, 
addresses[0].address));
-                }
+            foreach (Geary.RFC822.MailboxAddress alternate_email in account.information.alternate_emails) {
+                Geary.RFC822.MailboxAddresses addresses = new Geary.RFC822.MailboxAddresses.single(
+                    alternate_email);
                 
                 // Displayed in the From dropdown to indicate an "alternate email address"
                 // for an account.  The first printf argument will be the alternate email
@@ -2296,7 +2289,7 @@ public class ComposerWidget : Gtk.EventBox {
                 from_multiple.append_text(display);
                 from_list.add(new FromAddressMap(account, addresses));
                 
-                if (!set_active && Geary.RFC822.Utils.equal(from, addresses)) {
+                if (!set_active && from.equal_to(addresses)) {
                     from_multiple.set_active(from_list.size - 1);
                     set_active = true;
                 }
diff --git a/src/client/conversation-list/formatted-conversation-data.vala 
b/src/client/conversation-list/formatted-conversation-data.vala
index 57a0161..4cd56d2 100644
--- a/src/client/conversation-list/formatted-conversation-data.vala
+++ b/src/client/conversation-list/formatted-conversation-data.vala
@@ -20,22 +20,20 @@ public class FormattedConversationData : Geary.BaseObject {
     private const int FONT_SIZE_PREVIEW = 8;
     
     private class ParticipantDisplay : Geary.BaseObject, Gee.Hashable<ParticipantDisplay> {
-        public string key;
         public Geary.RFC822.MailboxAddress address;
         public bool is_unread;
         
         public ParticipantDisplay(Geary.RFC822.MailboxAddress address, bool is_unread) {
-            key = address.as_key();
             this.address = address;
             this.is_unread = is_unread;
         }
         
-        public string get_full_markup(Gee.List<string> normalized_account_emails) {
-            return get_as_markup((key in normalized_account_emails) ? ME : address.get_short_address());
+        public string get_full_markup(Gee.List<Geary.RFC822.MailboxAddress> account_mailboxes) {
+            return get_as_markup((address in account_mailboxes) ? ME : address.get_short_address());
         }
         
-        public string get_short_markup(Gee.List<string> normalized_account_emails) {
-            if (key in normalized_account_emails)
+        public string get_short_markup(Gee.List<Geary.RFC822.MailboxAddress> account_mailboxes) {
+            if (address in account_mailboxes)
                 return get_as_markup(ME);
             
             string short_address = address.get_short_address().strip();
@@ -45,17 +43,17 @@ public class FormattedConversationData : Geary.BaseObject {
                 string[] tokens = short_address.split(", ", 2);
                 short_address = tokens[1].strip();
                 if (Geary.String.is_empty(short_address))
-                    return get_full_markup(normalized_account_emails);
+                    return get_full_markup(account_mailboxes);
             }
             
             // use first name as delimited by a space
             string[] tokens = short_address.split(" ", 2);
             if (tokens.length < 1)
-                return get_full_markup(normalized_account_emails);
+                return get_full_markup(account_mailboxes);
             
             string first_name = tokens[0].strip();
             if (Geary.String.is_empty_or_whitespace(first_name))
-                return get_full_markup(normalized_account_emails);
+                return get_full_markup(account_mailboxes);
             
             return get_as_markup(first_name);
         }
@@ -66,14 +64,11 @@ public class FormattedConversationData : Geary.BaseObject {
         }
         
         public bool equal_to(ParticipantDisplay other) {
-            if (this == other)
-                return true;
-            
-            return key == other.key;
+            return address.equal_to(other.address);
         }
         
         public uint hash() {
-            return key.hash();
+            return address.hash();
         }
     }
     
@@ -89,13 +84,13 @@ public class FormattedConversationData : Geary.BaseObject {
     public Geary.Email? preview { get; private set; default = null; }
     
     private Geary.App.Conversation? conversation = null;
-    private Gee.List<string>? account_owner_emails = null;
+    private Gee.List<Geary.RFC822.MailboxAddress>? account_owner_emails = null;
     private bool use_to = true;
     private CountBadge count_badge = new CountBadge(2);
     
     // Creates a formatted message data from an e-mail.
     public FormattedConversationData(Geary.App.Conversation conversation, Geary.Email preview,
-        Geary.Folder folder, Gee.List<string> account_owner_emails) {
+        Geary.Folder folder, Gee.List<Geary.RFC822.MailboxAddress> account_owner_emails) {
         assert(preview.fields.fulfills(ConversationListStore.REQUIRED_FIELDS));
         
         this.conversation = conversation;
@@ -176,10 +171,6 @@ public class FormattedConversationData : Geary.BaseObject {
         if (conversation == null || account_owner_emails == null || account_owner_emails.size == 0)
             return "";
         
-        Gee.ArrayList<string> normalized_account_owner_emails = Geary.traverse<string>(account_owner_emails)
-            .map<string>(e => e.normalize().casefold())
-            .to_array_list();
-        
         // Build chronological list of AuthorDisplay records, setting to unread if any message by
         // that author is unread
         Gee.ArrayList<ParticipantDisplay> list = new Gee.ArrayList<ParticipantDisplay>();
@@ -212,14 +203,14 @@ public class FormattedConversationData : Geary.BaseObject {
             rgba_to_markup(get_foreground_rgba(widget, selected))));
         if (list.size == 1) {
             // if only one participant, use full name
-            builder.append(list[0].get_full_markup(normalized_account_owner_emails));
+            builder.append(list[0].get_full_markup(account_owner_emails));
         } else {
             bool first = true;
             foreach (ParticipantDisplay participant in list) {
                 if (!first)
                     builder.append(", ");
                 
-                builder.append(participant.get_short_markup(normalized_account_owner_emails));
+                builder.append(participant.get_short_markup(account_owner_emails));
                 first = false;
             }
         }
diff --git a/src/engine/api/geary-account-information.vala b/src/engine/api/geary-account-information.vala
index b3ec53b..af2d377 100644
--- a/src/engine/api/geary-account-information.vala
+++ b/src/engine/api/geary-account-information.vala
@@ -74,9 +74,12 @@ public class Geary.AccountInformation : BaseObject {
     /**
      * A list of additional email addresses this account accepts.
      *
+     * Use { link add_alternate_email} or { link replace_alternate_emails} rather than edit this
+     * collection directly.
+     *
      * @see get_all_email_addresses
      */
-    public Gee.List<string>? alternate_emails { get; set; }
+    public Gee.List<Geary.RFC822.MailboxAddress>? alternate_emails { get; private set; }
     public Geary.ServiceProvider service_provider { get; set; }
     public int prefetch_period_days { get; set; }
     
@@ -166,10 +169,13 @@ public class Geary.AccountInformation : BaseObject {
             if (alt_email_list.size == 0) {
                 alternate_emails = null;
             } else {
-                alternate_emails = new Gee.ArrayList<string>(String.stri_equal);
+                alternate_emails = new Gee.ArrayList<RFC822.MailboxAddress>();
                 foreach (string alt_email in alt_email_list) {
-                    if (!alternate_emails.contains(alt_email))
-                        alternate_emails.add(alt_email);
+                    RFC822.MailboxAddresses mailboxes = new 
RFC822.MailboxAddresses.from_rfc822_string(alt_email);
+                    foreach (RFC822.MailboxAddress mailbox in mailboxes.get_all()) {
+                        if (!alternate_emails.contains(mailbox))
+                            alternate_emails.add(mailbox);
+                    }
                 }
             }
             
@@ -258,8 +264,8 @@ public class Geary.AccountInformation : BaseObject {
         email = from.email;
         alternate_emails = null;
         if (from.alternate_emails != null) {
-            alternate_emails = new Gee.ArrayList<string>(String.stri_equal);
-            foreach (string alternate_email in from.alternate_emails)
+            alternate_emails = new Gee.ArrayList<RFC822.MailboxAddress>();
+            foreach (RFC822.MailboxAddress alternate_email in from.alternate_emails)
                 alternate_emails.add(alternate_email);
         }
         service_provider = from.service_provider;
@@ -292,9 +298,9 @@ public class Geary.AccountInformation : BaseObject {
     /**
      * Return a list of the primary and all alternate email addresses.
      */
-    public Gee.List<string> get_all_email_addresses() {
-        Gee.ArrayList<string> all = new Gee.ArrayList<string>();
-        all.add(email);
+    public Gee.List<Geary.RFC822.MailboxAddress> get_all_email_addresses() {
+        Gee.ArrayList<RFC822.MailboxAddress> all = new Gee.ArrayList<RFC822.MailboxAddress>();
+        all.add(get_primary_mailbox_address());
         if (alternate_emails != null)
             all.add_all(alternate_emails);
         return all;
@@ -305,9 +311,9 @@ public class Geary.AccountInformation : BaseObject {
      *
      * Duplicates will be ignored.
      */
-    public void add_alternate_email(string email) {
+    public void add_alternate_email(Geary.RFC822.MailboxAddress email) {
         if (alternate_emails == null)
-            alternate_emails = new Gee.ArrayList<string>(String.stri_equal);
+            alternate_emails = new Gee.ArrayList<RFC822.MailboxAddress>();
         
         if (!alternate_emails.contains(email))
             alternate_emails.add(email);
@@ -318,13 +324,13 @@ public class Geary.AccountInformation : BaseObject {
      *
      * Duplicates will be ignored.
      */
-    public void replace_alternate_emails(Gee.Collection<string>? emails) {
+    public void replace_alternate_emails(Gee.Collection<Geary.RFC822.MailboxAddress>? emails) {
         alternate_emails = null;
         
         if (emails == null || emails.size == 0)
             return;
         
-        foreach (string email in emails)
+        foreach (RFC822.MailboxAddress email in emails)
             add_alternate_email(email);
     }
     
@@ -782,8 +788,13 @@ public class Geary.AccountInformation : BaseObject {
         key_file.set_boolean(GROUP, SAVE_SENT_MAIL_KEY, save_sent_mail);
         key_file.set_boolean(GROUP, USE_EMAIL_SIGNATURE_KEY, use_email_signature);
         key_file.set_string(GROUP, EMAIL_SIGNATURE_KEY, email_signature);
-        if (alternate_emails != null && alternate_emails.size > 0)
-            key_file.set_string_list(GROUP, ALTERNATE_EMAILS_KEY, alternate_emails.to_array());
+        if (alternate_emails != null && alternate_emails.size > 0) {
+            string[] list = new string[alternate_emails.size];
+            for (int ctr = 0; ctr < alternate_emails.size; ctr++)
+                list[ctr] = alternate_emails[ctr].to_rfc822_string();
+            
+            key_file.set_string_list(GROUP, ALTERNATE_EMAILS_KEY, list);
+        }
         
         if (service_provider == ServiceProvider.OTHER) {
             key_file.set_value(GROUP, IMAP_HOST, default_imap_server_host);
@@ -874,7 +885,9 @@ public class Geary.AccountInformation : BaseObject {
     }
     
     /**
-     * Returns a MailboxAddresses object with this mailbox address.
+     * Returns MailboxAddresses with the primary mailbox address.
+     *
+     * @see get_primary_mailbox_address
      */
     public RFC822.MailboxAddresses get_primary_from() {
         return new RFC822.MailboxAddresses.single(get_primary_mailbox_address());
diff --git a/src/engine/rfc822/rfc822-mailbox-address.vala b/src/engine/rfc822/rfc822-mailbox-address.vala
index eae6634..20cb457 100644
--- a/src/engine/rfc822/rfc822-mailbox-address.vala
+++ b/src/engine/rfc822/rfc822-mailbox-address.vala
@@ -10,7 +10,8 @@
  * See [[https://tools.ietf.org/html/rfc2822#section-3.4]]
  */
 
-public class Geary.RFC822.MailboxAddress : Geary.MessageData.SearchableMessageData, BaseObject {
+public class Geary.RFC822.MailboxAddress : Geary.MessageData.SearchableMessageData,
+    Gee.Hashable<MailboxAddress>, BaseObject {
     internal delegate string ListToStringDelegate(MailboxAddress address);
     
     /**
@@ -152,13 +153,6 @@ public class Geary.RFC822.MailboxAddress : Geary.MessageData.SearchableMessageDa
     }
     
     /**
-     * Returns a normalized casefolded string of the address, suitable for comparison and hashing.
-     */
-    public string as_key() {
-        return address.normalize().casefold();
-    }
-    
-    /**
      * Returns the address suitable for insertion into an RFC822 message.  RFC822 quoting is
      * performed if required.
      *
@@ -177,6 +171,17 @@ public class Geary.RFC822.MailboxAddress : Geary.MessageData.SearchableMessageDa
         return get_full_address();
     }
     
+    public uint hash() {
+        return String.stri_hash(address);
+    }
+    
+    /**
+     * Equality is defined as a case-insensitive comparison of the { link address}.
+     */
+    public bool equal_to(MailboxAddress other) {
+        return this != other ? String.stri_equal(address, other.address) : true;
+    }
+    
     public string to_string() {
         return get_full_address();
     }
diff --git a/src/engine/rfc822/rfc822-mailbox-addresses.vala b/src/engine/rfc822/rfc822-mailbox-addresses.vala
index 71da8cc..c70f1ff 100644
--- a/src/engine/rfc822/rfc822-mailbox-addresses.vala
+++ b/src/engine/rfc822/rfc822-mailbox-addresses.vala
@@ -5,7 +5,7 @@
  */
 
 public class Geary.RFC822.MailboxAddresses : Geary.MessageData.AbstractMessageData, 
-    Geary.MessageData.SearchableMessageData, Geary.RFC822.MessageData {
+    Geary.MessageData.SearchableMessageData, Geary.RFC822.MessageData, Gee.Hashable<MailboxAddresses> {
     
     public int size { get { return addrs.size; } }
     
@@ -84,6 +84,36 @@ public class Geary.RFC822.MailboxAddresses : Geary.MessageData.AbstractMessageDa
         return MailboxAddress.list_to_string(addrs, "", (a) => a.to_rfc822_string());
     }
     
+    public uint hash() {
+        // create sorted set to ensure ordering no matter the list's order
+        Gee.TreeSet<string> sorted_addresses = traverse<RFC822.MailboxAddress>(addrs)
+            .map<string>(m => m.address)
+            .to_tree_set(String.stri_cmp);
+        
+        // xor all strings in sorted order
+        uint xor = 0;
+        foreach (string address in sorted_addresses)
+            xor ^= address.hash();
+        
+        return xor;
+    }
+    
+    public bool equal_to(MailboxAddresses other) {
+        if (this == other)
+            return true;
+        
+        if (addrs.size != other.addrs.size)
+            return false;
+        
+        Gee.HashSet<RFC822.MailboxAddress> first = new Gee.HashSet<RFC822.MailboxAddress>();
+        first.add_all(addrs);
+        
+        Gee.HashSet<RFC822.MailboxAddress> second = new Gee.HashSet<RFC822.MailboxAddress>();
+        second.add_all(other.addrs);
+        
+        return Collection.are_sets_equal<RFC822.MailboxAddress>(first, second);
+    }
+    
     /**
      * See Geary.MessageData.SearchableMessageData.
      */
diff --git a/src/engine/rfc822/rfc822-utils.vala b/src/engine/rfc822/rfc822-utils.vala
index c196d3f..99bf3cb 100644
--- a/src/engine/rfc822/rfc822-utils.vala
+++ b/src/engine/rfc822/rfc822-utils.vala
@@ -62,23 +62,23 @@ public string create_subject_for_forward(Geary.Email email) {
 // address in the list once. Used to remove the sender's address from a list of addresses being
 // created for the "reply to" recipients.
 private void remove_address(Gee.List<Geary.RFC822.MailboxAddress> addresses,
-    string address, bool empty_ok = false) {
+    RFC822.MailboxAddress address, bool empty_ok = false) {
     for (int i = 0; i < addresses.size; ++i) {
-        if (addresses[i].address == address && (empty_ok || addresses.size > 1))
+        if (addresses[i].equal_to(address) && (empty_ok || addresses.size > 1))
             addresses.remove_at(i--);
     }
 }
 
-private bool email_is_from_sender(Geary.Email email, Gee.List<string>? sender_addresses) {
+private bool email_is_from_sender(Geary.Email email, Gee.List<RFC822.MailboxAddress>? sender_addresses) {
     if (sender_addresses == null)
         return false;
     
-    return Geary.traverse<string>(sender_addresses).any(a =>
-        !String.is_empty(a) && email.from.contains(a));
+    return Geary.traverse<RFC822.MailboxAddress>(sender_addresses)
+        .any(a => email.from.get_all().contains(a));
 }
 
 public Geary.RFC822.MailboxAddresses create_to_addresses_for_reply(Geary.Email email,
-    Gee.List<string>? sender_addresses = null) {
+    Gee.List< Geary.RFC822.MailboxAddress>? sender_addresses = null) {
     Gee.List<Geary.RFC822.MailboxAddress> new_to =
         new Gee.ArrayList<Geary.RFC822.MailboxAddress>();
     
@@ -93,17 +93,15 @@ public Geary.RFC822.MailboxAddresses create_to_addresses_for_reply(Geary.Email e
     
     // Exclude the current sender.  No need to receive the mail they're sending.
     if (sender_addresses != null) {
-        foreach (string address in sender_addresses) {
-            if (!String.is_empty(address))
-                remove_address(new_to, address);
-        }
+        foreach (RFC822.MailboxAddress address in sender_addresses)
+            remove_address(new_to, address);
     }
     
     return new Geary.RFC822.MailboxAddresses(new_to);
 }
 
 public Geary.RFC822.MailboxAddresses create_cc_addresses_for_reply_all(Geary.Email email,
-    Gee.List<string>? sender_addresses = null) {
+    Gee.List<Geary.RFC822.MailboxAddress>? sender_addresses = null) {
     Gee.List<Geary.RFC822.MailboxAddress> new_cc = new Gee.ArrayList<Geary.RFC822.MailboxAddress>();
     
     // If we're replying to something we received, also add other recipients.  Don't do this for
@@ -117,10 +115,8 @@ public Geary.RFC822.MailboxAddresses create_cc_addresses_for_reply_all(Geary.Ema
     
     // Again, exclude the current sender.
     if (sender_addresses != null) {
-        foreach (string address in sender_addresses) {
-            if (!String.is_empty(address))
-                remove_address(new_cc, address, true);
-        }
+        foreach (RFC822.MailboxAddress address in sender_addresses)
+            remove_address(new_cc, address, true);
     }
     
     return new Geary.RFC822.MailboxAddresses(new_cc);
@@ -150,28 +146,11 @@ public Geary.RFC822.MailboxAddresses remove_addresses(Geary.RFC822.MailboxAddres
         result.add_all(from_addresses.get_all());
         if (remove_addresses != null)
             foreach (Geary.RFC822.MailboxAddress address in remove_addresses)
-                remove_address(result, address.address, true);
+                remove_address(result, address, true);
     }
     return new Geary.RFC822.MailboxAddresses(result);
 }
 
-public bool equal(Geary.RFC822.MailboxAddresses? first, Geary.RFC822.MailboxAddresses? second) {
-    bool first_empty = first == null || first.size == 0;
-    bool second_empty = second == null || second.size == 0;
-    if (first_empty && second_empty || first == second)
-        return true;
-    if (first_empty || second_empty || first.size != second.size)
-        return false;
-    
-    Gee.HashSet<string> first_addresses = new Gee.HashSet<string>();
-    Gee.HashSet<string> second_addresses = new Gee.HashSet<string>();
-    foreach (Geary.RFC822.MailboxAddress a in first)
-        first_addresses.add(a.as_key());
-    foreach (Geary.RFC822.MailboxAddress a in second)
-        second_addresses.add(a.as_key());
-    return Geary.Collection.are_sets_equal<string>(first_addresses, second_addresses);
-}
-
 public string reply_references(Geary.Email source) {
     // generate list for References
     Gee.ArrayList<RFC822.MessageID> list = new Gee.ArrayList<RFC822.MessageID>();
diff --git a/src/engine/util/util-string.vala b/src/engine/util/util-string.vala
index 468e09a..3c09bac 100644
--- a/src/engine/util/util-string.vala
+++ b/src/engine/util/util-string.vala
@@ -46,6 +46,10 @@ public bool stri_equal(string a, string b) {
     return str_equal(a.down(), b.down());
 }
 
+public int stri_cmp(string a, string b) {
+    return strcmp(a.down(), b.down());
+}
+
 // Removes redundant spaces, tabs, and newlines.
 public string reduce_whitespace(string _s) {
     string s = _s;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]