From 624a7171beb0a0f85738cf9fcdbf2571d54550ca Mon Sep 17 00:00:00 2001 From: Brendan Long Date: Mon, 12 Aug 2013 16:30:17 -0700 Subject: [PATCH 1/5] Content menu missing labels in composer: Closes #7322 --- THANKS | 1 + ui/composer.glade | 25 ++++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/THANKS b/THANKS index 8751bb44..e6641e03 100644 --- a/THANKS +++ b/THANKS @@ -16,6 +16,7 @@ Jens Georg Michael George Sven Hagemann Mathias Hasselmann +Brendan Long Timo Kluck Avi Levy Kai Mast diff --git a/ui/composer.glade b/ui/composer.glade index f70cba50..05dec081 100644 --- a/ui/composer.glade +++ b/ui/composer.glade @@ -3,23 +3,38 @@ - + + Undo + undo + - + + Redo + redo + - + + Cut + edit-cut + - + + Copy + edit-copy + - + + Paste + edit-paste + From cf33c69930b6a91116d351b26e760d7e0a999bab Mon Sep 17 00:00:00 2001 From: Brendan Long Date: Mon, 12 Aug 2013 16:45:11 -0700 Subject: [PATCH 2/5] Build with --enable-ref-tracking: Closes #7262 --- src/engine/api/geary-base-object.vala | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/engine/api/geary-base-object.vala b/src/engine/api/geary-base-object.vala index aef74740..92644083 100644 --- a/src/engine/api/geary-base-object.vala +++ b/src/engine/api/geary-base-object.vala @@ -10,8 +10,14 @@ public abstract class Geary.BaseObject : Object { protected BaseObject() { lock (refmap) { - if (refmap == null) - refmap = new Gee.HashMap(direct_hash, direct_equal); + if (refmap == null) { + // because strings are unowned and guaranteed to be + // unique by GType, use direct comparison functions, + // more efficient then string hash/equal + refmap = new Gee.HashMap( + Gee.Functions.get_hash_func_for(typeof(void*)), + Gee.Functions.get_equal_func_for(typeof(void*))); + } unowned string classname = get_classname(); refmap.set(classname, refmap.get(classname) + 1); @@ -42,7 +48,7 @@ public abstract class Geary.BaseObject : Object { Gee.ArrayList list = new Gee.ArrayList(); list.add_all(refmap.keys); - list.sort(strcmp); + list.sort(); foreach (unowned string classname in list) outs.printf("%9d %s\n", refmap.get(classname), classname); } From ec10e06213f8b3fd3155a3d8333f7ec47181226b Mon Sep 17 00:00:00 2001 From: Jim Nelson Date: Mon, 12 Aug 2013 17:51:10 -0700 Subject: [PATCH 3/5] Background sync'ing should stop when vector is complete: Closes #7325 Account synchronizer now stops when it recognizes that all the mail in the folder is present (but perhaps not completely downloaded) from the server. The email prefetcher takes care of ther rest. Also discovered a regression from last week's commit that prevented vector expansion when using find_earliest_email_async(); fixed here as well. --- .../imap-engine/imap-engine-account-synchronizer.vala | 11 +++++++++++ .../imap-engine/imap-engine-generic-folder.vala | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/engine/imap-engine/imap-engine-account-synchronizer.vala b/src/engine/imap-engine/imap-engine-account-synchronizer.vala index 3e4ce488..916404ff 100644 --- a/src/engine/imap-engine/imap-engine-account-synchronizer.vala +++ b/src/engine/imap-engine/imap-engine-account-synchronizer.vala @@ -369,6 +369,17 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject { } else if (epoch_id != null) { oldest_local_id = epoch_id; } + + // look for complete synchronization of UIDs (i.e. complete vector normalization) + // no need to keep searching once this happens + int local_count = yield folder.local_folder.get_email_count_async(ImapDB.Folder.ListFlags.NONE, + bg_cancellable); + if (local_count >= folder.properties.email_total) { + debug("Total vector normalization for %s: %d/%d emails", folder.to_string(), local_count, + folder.properties.email_total); + + break; + } } while (current_epoch.compare(epoch) > 0); } else { debug("No expansion necessary for %s, oldest local (%s) is before epoch (%s)", diff --git a/src/engine/imap-engine/imap-engine-generic-folder.vala b/src/engine/imap-engine/imap-engine-generic-folder.vala index 186e6632..15fe61ed 100644 --- a/src/engine/imap-engine/imap-engine-generic-folder.vala +++ b/src/engine/imap-engine/imap-engine-generic-folder.vala @@ -1113,7 +1113,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde // class. Magically generating EmailIdentifiers is a bad idea. (That's why this uses // list_email_by_id_async() and not fetch_email_async(), and why OLDEST_TO_NEWEST is set.) Gee.List? list = yield list_email_by_id_async(found_id, 1, Geary.Email.Field.NONE, - ListFlags.OLDEST_TO_NEWEST, cancellable); + ListFlags.OLDEST_TO_NEWEST | ListFlags.INCLUDING_ID, cancellable); if (list == null || list.size == 0) return null; From 6197c11fa325b04fc957a99ba65bf756167315ce Mon Sep 17 00:00:00 2001 From: Jim Nelson Date: Mon, 12 Aug 2013 18:59:27 -0700 Subject: [PATCH 4/5] Reply not included due to bogus References: field: Closes #7333 --- src/engine/rfc822/rfc822-message-data.vala | 66 +++++++++++++++++----- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/src/engine/rfc822/rfc822-message-data.vala b/src/engine/rfc822/rfc822-message-data.vala index a6a04be2..0186da5a 100644 --- a/src/engine/rfc822/rfc822-message-data.vala +++ b/src/engine/rfc822/rfc822-message-data.vala @@ -45,26 +45,64 @@ public class Geary.RFC822.MessageIDList : Geary.MessageData.AbstractMessageData, public MessageIDList.from_rfc822_string(string value) { this (); - string[] ids = value.split_set(" \n\r\t"); + // Have seen some mailers use commas between Message-IDs, meaning that the standard + // whitespace tokenizer is not sufficient; however, can't add the comma (or every other + // delimiter that mailers dream up) because it may be used within a Message-ID. The + // only guarantee made of a Message-ID is that it's surrounded by angle brackets, so + // mark anything not an angle bracket as a space and strip + // + // NOTE: Seen at least one spamfilter mailer that imaginatively uses parens instead of + // angle brackets for its Message-IDs; accounting for that as well here. + StringBuilder canonicalized = new StringBuilder(); + int index = 0; + unichar ch; + bool in_message_id = false; + while (value.get_next_char(ref index, out ch)) { + switch (ch) { + case '<': + in_message_id = true; + break; + + case '(': + if (!in_message_id) { + ch = '<'; + in_message_id = true; + } + break; + + case '>': + in_message_id = false; + break; + + case ')': + if (in_message_id) { + ch = '>'; + in_message_id = false; + } + break; + + // anything not inside the message-id brackets is turned into spaces + default: + if (!in_message_id) + ch = ' '; + break; + } + + canonicalized.append_unichar(ch); + } + + if (value != canonicalized.str) + debug("Message-ID list corrected: \"%s\" -> \"%s\"", value, canonicalized.str); + + // there's some additional paranoia here with getting the Message-ID sliced out of the + // strings, but it's worth it to get a valid Message-ID or none at all vs. a bogus one + string[] ids = canonicalized.str.split(" "); foreach (string id in ids) { if (String.is_empty(id)) continue; - // Have seen some mailers use commas between Message-IDs, meaning that the standard - // whitespace tokenizer is not sufficient; however, can't add the comma (or every other - // delimiter that mailers dream up) because it may be used within a Message-ID. The - // only guarantee made of a Message-ID is that it's surrounded by angle brackets, so - // mark anything not an angle bracket as a space and strip - // - // NOTE: Seen at least one spamfilter mailer that imaginatively uses parens instead of - // angle brackets for its Message-IDs; accounting for that as well here. int start = id.index_of_char('<'); - if (start < 0) - start = id.index_of_char('('); - int end = id.last_index_of_char('>'); - if (end < 0) - end = id.last_index_of_char(')'); // if either end not found or the end comes before the beginning, invalid Message-ID if (start < 0 || end < 0 || (start >= end)) { From a029b1fa3bd7f2aa531a2f9e83f1487a2b7b1685 Mon Sep 17 00:00:00 2001 From: Jim Nelson Date: Tue, 13 Aug 2013 13:22:40 -0700 Subject: [PATCH 5/5] Merging in some bux fixes from conversation-mop-up branch Charles discovered that some of the operations in ImapEngine.GenericFolder were exiting before waiting for the command to complete, meaning the async call returned while the operation was still running. This has a broader problem if the command is executed (with a yield) and then the folder is closed. --- .../imap-engine-generic-folder.vala | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/engine/imap-engine/imap-engine-generic-folder.vala b/src/engine/imap-engine/imap-engine-generic-folder.vala index 15fe61ed..94b50f73 100644 --- a/src/engine/imap-engine/imap-engine-generic-folder.vala +++ b/src/engine/imap-engine/imap-engine-generic-folder.vala @@ -1028,7 +1028,11 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde check_open("expunge_email_async"); check_ids("expunge_email_async", email_ids); - replay_queue.schedule(new ExpungeEmail(this, email_ids, cancellable)); + ExpungeEmail expunge = new ExpungeEmail(this, (Gee.List) email_ids, + cancellable); + replay_queue.schedule(expunge); + + yield expunge.wait_for_ready_async(cancellable); } private void check_open(string method) throws EngineError { @@ -1058,22 +1062,29 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde Cancellable? cancellable = null) throws Error { check_open("mark_email_async"); - replay_queue.schedule(new MarkEmail(this, to_mark, flags_to_add, flags_to_remove, - cancellable)); + MarkEmail mark = new MarkEmail(this, to_mark, flags_to_add, flags_to_remove, cancellable); + replay_queue.schedule(mark); + yield mark.wait_for_ready_async(cancellable); } public virtual async void copy_email_async(Gee.List to_copy, Geary.FolderPath destination, Cancellable? cancellable = null) throws Error { check_open("copy_email_async"); + check_ids("copy_email_async", to_copy); - replay_queue.schedule(new CopyEmail(this, to_copy, destination)); + CopyEmail copy = new CopyEmail(this, (Gee.List) to_copy, destination); + replay_queue.schedule(copy); + yield copy.wait_for_ready_async(cancellable); } public virtual async void move_email_async(Gee.List to_move, Geary.FolderPath destination, Cancellable? cancellable = null) throws Error { check_open("move_email_async"); + check_ids("move_email_async", to_move); - replay_queue.schedule(new MoveEmail(this, to_move, destination)); + MoveEmail move = new MoveEmail(this, (Gee.List) to_move, destination); + replay_queue.schedule(move); + yield move.wait_for_ready_async(cancellable); } private void on_email_flags_changed(Gee.Map changed) {