[geary/wip/789924-network-transition-redux: 9/10] Require users of MinimalFolder's folder session claim it.



commit 6609b4c353a7739664e91ecd03ddb3491a509081
Author: Michael James Gratton <mike vee net>
Date:   Fri Jan 26 13:49:52 2018 +1030

    Require users of MinimalFolder's folder session claim it.
    
    This makes MinimalFolder.remote_folder private, replacing it with
    claim_remote_session method that waits until one is ready and returns
    it. This fixes null object errors when there is no session, i.e. when the
    remote server is not available.
    
    * src/engine/imap-engine/imap-engine-minimal-folder.vala (MinimalFolder):
      Make remote_folder private, rename to remote_session and update
      internal uses. Add claim_remote_session method to allow users to access
      a session when one is available, update previous users of remote_folder
      to use this instead.
    
    * src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
      (RemoteBatchOperation): Pass in local and remote session objects to the
      ctor rather than a MinimalFolder instance so we don't need to keep
      re-claiming the remote session. Update call sites.
    
    * src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala
      (ReplayAppend): Pass in a cancellable to the ctor so we can cancel
      claiming a session and the remote operation, rather than just
      hanging. Update call sites.

 .../imap-engine/imap-engine-minimal-folder.vala    |   55 +++++++++----
 .../imap-engine-abstract-list-email.vala           |   75 +++++++++++------
 .../replay-ops/imap-engine-copy-email.vala         |   10 ++-
 .../replay-ops/imap-engine-create-email.vala       |   19 +++--
 .../replay-ops/imap-engine-empty-folder.vala       |   12 ++-
 .../replay-ops/imap-engine-fetch-email.vala        |    7 +-
 .../replay-ops/imap-engine-mark-email.vala         |   12 ++-
 .../replay-ops/imap-engine-move-email-commit.vala  |   31 ++++---
 .../replay-ops/imap-engine-remove-email.vala       |   12 ++-
 .../replay-ops/imap-engine-replay-append.vala      |   88 ++++++++++----------
 .../imap-engine-server-search-email.vala           |   10 ++-
 11 files changed, 203 insertions(+), 128 deletions(-)
---
diff --git a/src/engine/imap-engine/imap-engine-minimal-folder.vala 
b/src/engine/imap-engine/imap-engine-minimal-folder.vala
index 9527a0e..6795c94 100644
--- a/src/engine/imap-engine/imap-engine-minimal-folder.vala
+++ b/src/engine/imap-engine/imap-engine-minimal-folder.vala
@@ -57,7 +57,6 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
     public override Geary.ProgressMonitor opening_monitor { get { return _opening_monitor; } }
 
     internal ImapDB.Folder local_folder  { get; protected set; }
-    internal Imap.FolderSession? remote_folder { get; protected set; default = null; }
     internal int remote_count { get; private set; default = -1; }
 
     internal ReplayQueue replay_queue { get; private set; }
@@ -71,6 +70,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
     private int open_count = 0;
 
     private TimeoutManager remote_open_timer;
+    private Imap.FolderSession? remote_session = null;
     private Nonblocking.ReportingSemaphore<bool> remote_wait_semaphore =
         new Nonblocking.ReportingSemaphore<bool>(false);
     private Nonblocking.Semaphore closed_semaphore = new Nonblocking.Semaphore();
@@ -182,7 +182,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         if (this.open_count == 0)
             return Geary.Folder.OpenState.CLOSED;
 
-        return (this.remote_folder != null)
+        return (this.remote_session != null)
            ? Geary.Folder.OpenState.BOTH
            : Geary.Folder.OpenState.LOCAL;
     }
@@ -211,7 +211,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
             // even if opened or opening, or if forcing a re-open, respect the NO_DELAY flag
             if (open_flags.is_all_set(OpenFlags.NO_DELAY)) {
                 // add NO_DELAY flag if it forces an open
-                if (this.remote_folder == null)
+                if (this.remote_session == null)
                     this.open_flags |= OpenFlags.NO_DELAY;
 
                 this.open_remote_session.begin();
@@ -267,7 +267,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         check_open("wait_for_remote_async");
 
         // if remote has not yet been opened, do it now ...
-        if (this.remote_folder == null) {
+        if (this.remote_session == null) {
             this.open_remote_session.begin();
         }
 
@@ -275,6 +275,27 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
             throw new EngineError.ALREADY_CLOSED("%s failed to open", to_string());
     }
 
+    /**
+     * Returns a valid IMAP folder session when one is available.
+     *
+     * Implementations may use this to acquire an IMAP session for
+     * performing folder-related work. The call will wait until a
+     * connection is established then return the session.
+     *
+     * The session returned is guaranteed to be open upon return,
+     * however may close afterwards due to this folder closing, or the
+     * network connection going away.
+     *
+     * The folder must have been opened before calling this method.
+     */
+    public async Imap.FolderSession claim_remote_session(Cancellable? cancellable = null)
+        throws Error {
+        check_open("claim_remote_session");
+        debug("%s: Acquiring folder session", this.to_string());
+        yield this.wait_for_remote_async(cancellable);
+        return this.remote_session;
+    }
+
     /** {@inheritDoc} */
     public override async bool close_async(Cancellable? cancellable = null) throws Error {
         // Check open_count but only decrement inside of replay queue
@@ -309,13 +330,13 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         }
     }
 
-    private async void normalize_folders(Geary.Imap.FolderSession remote_folder,
+    private async void normalize_folders(Geary.Imap.FolderSession session,
                                          Cancellable? cancellable)
         throws Error {
         debug("%s: Begin normalizing remote and local folders", to_string());
 
         Geary.Imap.FolderProperties local_properties = this.local_folder.get_properties();
-        Geary.Imap.FolderProperties remote_properties = remote_folder.folder.properties;
+        Geary.Imap.FolderProperties remote_properties = session.folder.properties;
 
         // and both must have their next UID's (it's possible they don't if it's a non-selectable
         // folder)
@@ -453,7 +474,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         check_open("normalize_folders (list local)");
         
         // Do the same on the remote ... make non-null for ease of use later
-        Gee.Set<Imap.UID>? remote_uids = yield remote_folder.list_uids_async(
+        Gee.Set<Imap.UID>? remote_uids = yield session.list_uids_async(
             new Imap.MessageSet.uid_range(first_uid, last_uid), cancellable);
         if (remote_uids == null)
             remote_uids = new Gee.HashSet<Imap.UID>();
@@ -510,7 +531,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
             // detection)
             Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(remote_uids);
             foreach (Imap.MessageSet msg_set in msg_sets) {
-                Gee.List<Geary.Email>? list = yield remote_folder.list_email_async(msg_set,
+                Gee.List<Geary.Email>? list = yield session.list_email_async(msg_set,
                     ImapDB.Folder.REQUIRED_FIELDS, cancellable);
                 if (list != null && list.size > 0)
                     to_create.add_all(list);
@@ -643,8 +664,8 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
      * Unhooks the IMAP folder session and returns it to the account.
      */
     internal async void close_remote_session(Folder.CloseReason remote_reason) {
-        Imap.FolderSession session = this.remote_folder;
-        this.remote_folder = null;
+        Imap.FolderSession session = this.remote_session;
+        this.remote_session = null;
         this.remote_count = -1;
         notify_closed(remote_reason);
 
@@ -668,8 +689,8 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         // Close the prefetcher early so it stops using the remote ASAP
         this.email_prefetcher.close();
 
-        if (this.remote_folder != null)
-            _properties.remove(this.remote_folder.folder.properties);
+        if (this.remote_session != null)
+            _properties.remove(this.remote_session.folder.properties);
 
         // block anyone from wait_for_remote_async(), as this is no longer open
         this.remote_wait_semaphore.reset();
@@ -799,7 +820,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
             // else having called this just before we did.
             if (this.open_count > 0 &&
                 this._account.session_pool.is_ready &&
-                this.remote_folder == null) {
+                this.remote_session == null) {
 
                 this.opening_monitor.notify_start();
                 yield open_remote_session_locked(this.open_cancellable);
@@ -898,7 +919,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
 
         // Phase 3: Move in place and notify waiters
 
-        this.remote_folder = session;
+        this.remote_session = session;
 
         // notify any subscribers with similar information
         notify_opened(Geary.Folder.OpenState.BOTH, this.remote_count);
@@ -960,7 +981,9 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         this.remote_count = reported_remote_count;
 
         if (positions.size > 0) {
-            ReplayAppend op = new ReplayAppend(this, reported_remote_count, positions);
+            ReplayAppend op = new ReplayAppend(
+                this, reported_remote_count, positions, this.open_cancellable
+            );
             op.email_appended.connect(notify_email_appended);
             op.email_locally_appended.connect(notify_email_locally_appended);
             op.email_count_changed.connect(notify_email_count_changed);
@@ -1210,7 +1233,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
 
     public override string to_string() {
         return "%s (open_count=%d remote_opened=%s)".printf(
-            base.to_string(), open_count, (remote_folder != null).to_string()
+            base.to_string(), open_count, (this.remote_session != null).to_string()
         );
     }
 
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
index c58977e..c6aa149 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
@@ -8,56 +8,67 @@
  * A base class for building replay operations that list messages.
  */
 private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.SendReplayOperation {
+
     private static int total_fetches_avoided = 0;
-    
+
     private class RemoteBatchOperation : Nonblocking.BatchOperation {
         // IN
-        public MinimalFolder owner;
+        public Imap.FolderSession remote;
+        public ImapDB.Folder local;
         public Imap.MessageSet msg_set;
         public Geary.Email.Field unfulfilled_fields;
         public Geary.Email.Field required_fields;
-        
+
         // OUT
         public Gee.Set<Geary.EmailIdentifier> created_ids = new Gee.HashSet<Geary.EmailIdentifier>();
-        
-        public RemoteBatchOperation(MinimalFolder owner, Imap.MessageSet msg_set,
-            Geary.Email.Field unfulfilled_fields, Geary.Email.Field required_fields) {
-            this.owner = owner;
+
+        public RemoteBatchOperation(Imap.FolderSession remote,
+                                    ImapDB.Folder local,
+                                    Imap.MessageSet msg_set,
+                                    Geary.Email.Field unfulfilled_fields,
+                                    Geary.Email.Field required_fields) {
+            this.remote = remote;
+            this.local = local;
             this.msg_set = msg_set;
             this.unfulfilled_fields = unfulfilled_fields;
             this.required_fields = required_fields;
         }
-        
+
         public override async Object? execute_async(Cancellable? cancellable) throws Error {
             // fetch from remote folder
-            Gee.List<Geary.Email>? list = yield owner.remote_folder.list_email_async(msg_set,
-                unfulfilled_fields, cancellable);
+            Gee.List<Geary.Email>? list = yield this.remote.list_email_async(
+                msg_set, unfulfilled_fields, cancellable
+            );
             if (list == null || list.size == 0)
                 return null;
-            
+
             // TODO: create_or_merge_email_async() should only write if something has changed
-            Gee.Map<Geary.Email, bool> created_or_merged = yield 
owner.local_folder.create_or_merge_email_async(
+            Gee.Map<Geary.Email, bool> created_or_merged = yield this.local.create_or_merge_email_async(
                 list, cancellable);
             for (int ctr = 0; ctr < list.size; ctr++) {
                 Geary.Email email = list[ctr];
-                
+
                 // if created, add to id pool
                 if (created_or_merged.get(email))
                     created_ids.add(email.id);
-                
+
                 // if remote email doesn't fulfills all required fields, fetch full and return that
                 // TODO: Need a sparse ID fetch in ImapDB.Folder to do this all at once
                 if (!email.fields.fulfills(required_fields)) {
-                    email = yield owner.local_folder.fetch_email_async((ImapDB.EmailIdentifier) email.id,
-                        required_fields, ImapDB.Folder.ListFlags.NONE, cancellable);
+                    email = yield this.local.fetch_email_async(
+                        (ImapDB.EmailIdentifier) email.id,
+                        required_fields,
+                        ImapDB.Folder.ListFlags.NONE,
+                        cancellable
+                    );
                     list[ctr] = email;
                 }
             }
-            
+
             return list;
         }
     }
-    
+
     // The accumulated Email from the list operation.  Should only be accessed once the operation
     // has completed.
     public Gee.List<Geary.Email> accumulator = new Gee.ArrayList<Geary.Email>();
@@ -146,7 +157,10 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
             Geary.Email.Field, Imap.UID>();
         foreach (Imap.UID uid in unfulfilled.keys)
             reverse_unfulfilled.set(unfulfilled.get(uid), uid);
-        
+
+        Imap.FolderSession remote =
+            yield this.owner.claim_remote_session(cancellable);
+
         // schedule operations to remote for each set of email with unfulfilled fields and merge
         // in results, pulling out the entire email
         Nonblocking.Batch batch = new Nonblocking.Batch();
@@ -154,15 +168,20 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
             Gee.Collection<Imap.UID> unfulfilled_uids = reverse_unfulfilled.get(unfulfilled_fields);
             if (unfulfilled_uids.size == 0)
                 continue;
-            
+
             Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(unfulfilled_uids);
             foreach (Imap.MessageSet msg_set in msg_sets) {
-                RemoteBatchOperation remote_op = new RemoteBatchOperation(owner, msg_set,
-                    unfulfilled_fields, required_fields);
+                RemoteBatchOperation remote_op = new RemoteBatchOperation(
+                    remote,
+                    this.owner.local_folder,
+                    msg_set,
+                    unfulfilled_fields,
+                    required_fields
+                );
                 batch.add(remote_op);
             }
         }
-        
+
         yield batch.execute_all_async(cancellable);
         batch.throw_first_exception();
         
@@ -251,9 +270,11 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
         int64 high_pos = -1;
         int64 initial_pos = -1;
 
+        Imap.FolderSession remote =
+            yield this.owner.claim_remote_session(cancellable);
         if (initial_uid != null) {
             Gee.Map<Imap.UID, Imap.SequenceNumber>? map =
-            yield owner.remote_folder.uid_to_position_async(
+            yield remote.uid_to_position_async(
                 new Imap.MessageSet.uid(initial_uid), cancellable
             );
             Imap.SequenceNumber? pos = map.get(initial_uid);
@@ -302,10 +323,10 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
             owner.to_string(), msg_set.to_string(),
             (initial_uid != null) ? initial_uid.to_string() : "(null)", count, actual_count.to_string(),
             local_count, remote_count, flags.is_oldest_to_newest().to_string());
-        
-        Gee.List<Geary.Email>? list = yield owner.remote_folder.list_email_async(msg_set,
+
+        Gee.List<Geary.Email>? list = yield remote.list_email_async(msg_set,
             Geary.Email.Field.NONE, cancellable);
-        
+
         Gee.Set<Imap.UID> uids = new Gee.HashSet<Imap.UID>();
         if (list != null) {
             // add all the new email to the unfulfilled list, which ensures (when replay_remote_async
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
index bcf1b66..8049714 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
@@ -45,17 +45,19 @@ private class Geary.ImapEngine.CopyEmail : Geary.ImapEngine.SendReplayOperation
         
         Gee.Set<Imap.UID>? uids = yield engine.local_folder.get_uids_async(to_copy,
             ImapDB.Folder.ListFlags.NONE, cancellable);
-        
+
         if (uids != null && uids.size > 0) {
             Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(uids);
+            Imap.FolderSession remote =
+                yield this.engine.claim_remote_session(cancellable);
             foreach (Imap.MessageSet msg_set in msg_sets) {
-                Gee.Map<Imap.UID, Imap.UID>? src_dst_uids = yield engine.remote_folder.copy_email_async(
-                    msg_set, destination, cancellable);
+                Gee.Map<Imap.UID, Imap.UID>? src_dst_uids =
+                    yield remote.copy_email_async(msg_set, destination, cancellable);
                 if (src_dst_uids != null)
                     destination_uids.add_all(src_dst_uids.values);
             }
         }
-        
+
         return ReplayOperation.Status.COMPLETED;
     }
 
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala
index d62ea7d..3a4ea8a 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala
@@ -46,24 +46,29 @@ private class Geary.ImapEngine.CreateEmail : Geary.ImapEngine.SendReplayOperatio
         // Deal with cancellable manually since create_email_async cannot be cancelled.
         if (cancellable.is_cancelled())
             throw new IOError.CANCELLED("CreateEmail op cancelled immediately");
-        
+
+        Imap.FolderSession remote =
+            yield this.engine.claim_remote_session(cancellable);
+
         // use IMAP APPEND command on remote folders, which doesn't require opening a folder ...
         // if retrying after a successful create, rfc822 will be null
         if (rfc822 != null)
-            created_id = yield engine.remote_folder.create_email_async(rfc822, flags, date_received);
-        
+            created_id = yield remote.create_email_async(rfc822, flags, date_received);
+
         // because this command retries, the create completed, remove the RFC822 message to prevent
         // creating it twice
         rfc822 = null;
-        
+
         // If the user cancelled the operation, we need to wipe the new message to keep this
         // operation atomic.
         if (cancellable.is_cancelled()) {
             if (created_id != null) {
-                yield engine.remote_folder.remove_email_async(
-                    new Imap.MessageSet.uid(((ImapDB.EmailIdentifier) created_id).uid).to_list(), null);
+                yield remote.remove_email_async(
+                    new Imap.MessageSet.uid(((ImapDB.EmailIdentifier) created_id).uid).to_list(),
+                    null
+                );
             }
-            
+
             throw new IOError.CANCELLED("CreateEmail op cancelled after create");
         }
         
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
index b0d9adf..e64e1d0 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
@@ -52,17 +52,19 @@ private class Geary.ImapEngine.EmptyFolder : Geary.ImapEngine.SendReplayOperatio
         if (removed_ids != null)
             ids.add_all(removed_ids);
     }
-    
+
     public override async ReplayOperation.Status replay_remote_async() throws Error {
         // STORE and EXPUNGE using positional addressing: "1:*"
+        Imap.FolderSession remote =
+            yield this.engine.claim_remote_session(cancellable);
         Imap.MessageSet msg_set = new Imap.MessageSet.range_to_highest(
             new Imap.SequenceNumber(Imap.SequenceNumber.MIN));
-        
-        yield engine.remote_folder.remove_email_async(msg_set.to_list(), cancellable);
-        
+
+        yield remote.remove_email_async(msg_set.to_list(), cancellable);
+
         return ReplayOperation.Status.COMPLETED;
     }
-    
+
     public override async void backout_local_async() throws Error {
         if (removed_ids != null && removed_ids.size > 0) {
             yield engine.local_folder.mark_removed_async(removed_ids, false, cancellable);
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-fetch-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-fetch-email.vala
index 580a41d..a6da5c0 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-fetch-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-fetch-email.vala
@@ -90,10 +90,13 @@ private class Geary.ImapEngine.FetchEmail : Geary.ImapEngine.SendReplayOperation
             throw new EngineError.NOT_FOUND("Unable to fetch %s in %s (removed from remote)",
                 id.to_string(), engine.to_string());
         }
-        
+
+        Imap.FolderSession remote =
+            yield this.engine.claim_remote_session(cancellable);
+
         // fetch only the remaining fields from the remote folder (if only pulling partial information,
         // will merge at end of this method)
-        Gee.List<Geary.Email>? list = yield engine.remote_folder.list_email_async(
+        Gee.List<Geary.Email>? list = yield remote.list_email_async(
             new Imap.MessageSet.uid(uid), remaining_fields, cancellable);
         if (list == null || list.size != 1)
             throw new EngineError.NOT_FOUND("Unable to fetch %s in %s", id.to_string(), engine.to_string());
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-mark-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-mark-email.vala
index 4f326d2..5368671 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-mark-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-mark-email.vala
@@ -62,12 +62,16 @@ private class Geary.ImapEngine.MarkEmail : Geary.ImapEngine.SendReplayOperation
         // potentially empty due to writebehind operation
         if (original_flags.size == 0)
             return ReplayOperation.Status.COMPLETED;
-        
+
+        Imap.FolderSession remote =
+            yield this.engine.claim_remote_session(cancellable);
+
         Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(
             ImapDB.EmailIdentifier.to_uids(original_flags.keys));
-        yield engine.remote_folder.mark_email_async(msg_sets, flags_to_add, flags_to_remove,
-            cancellable);
-        
+        yield remote.mark_email_async(
+            msg_sets, flags_to_add, flags_to_remove, cancellable
+        );
+
         return ReplayOperation.Status.COMPLETED;
     }
     
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
index 94e719f..57947ab 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
@@ -50,30 +50,37 @@ private class Geary.ImapEngine.MoveEmailCommit : Geary.ImapEngine.SendReplayOper
         
         if (remaining_msg_sets == null || remaining_msg_sets.size == 0)
             return ReplayOperation.Status.COMPLETED;
-        
+
+        Imap.FolderSession remote =
+            yield this.engine.claim_remote_session(cancellable);
+
         Gee.Iterator<Imap.MessageSet> iter = remaining_msg_sets.iterator();
         while (iter.next()) {
             // don't use Cancellable throughout I/O operations in order to assure transaction completes
             // fully
-            if (cancellable != null && cancellable.is_cancelled())
-                throw new IOError.CANCELLED("Move email to %s cancelled", engine.remote_folder.to_string());
-            
+            if (cancellable != null && cancellable.is_cancelled()) {
+                throw new IOError.CANCELLED(
+                    "Move email to %s cancelled", this.destination.to_string()
+                );
+            }
+
             Imap.MessageSet msg_set = iter.get();
-            
-            Gee.Map<Imap.UID, Imap.UID>? map = yield engine.remote_folder.copy_email_async(msg_set,
-                destination, null);
+
+            Gee.Map<Imap.UID, Imap.UID>? map = yield remote.copy_email_async(
+                msg_set, destination, null
+            );
             if (map != null)
                 destination_uids.add_all(map.values);
-            
-            yield engine.remote_folder.remove_email_async(msg_set.to_list(), null);
-            
+
+            yield remote.remove_email_async(msg_set.to_list(), null);
+
             // completed successfully, remove from list in case of retry
             iter.remove();
         }
-        
+
         return ReplayOperation.Status.COMPLETED;
     }
-    
+
     public override async void backout_local_async() throws Error {
         if (to_move.size == 0)
             return;
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-remove-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-remove-email.vala
index 03e944f..acc60aa 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-remove-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-remove-email.vala
@@ -55,21 +55,23 @@ private class Geary.ImapEngine.RemoveEmail : Geary.ImapEngine.SendReplayOperatio
         if (removed_ids != null)
             ids.add_all(removed_ids);
     }
-    
+
     public override async ReplayOperation.Status replay_remote_async() throws Error {
         if (removed_ids.size == 0)
             return ReplayOperation.Status.COMPLETED;
-        
+
         // Remove from server. Note that this causes the receive replay queue to kick into
         // action, removing the e-mail but *NOT* firing a signal; the "remove marker" indicates
         // that the signal has already been fired.
         Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(
             ImapDB.EmailIdentifier.to_uids(removed_ids));
-        yield engine.remote_folder.remove_email_async(msg_sets, cancellable);
-        
+        Imap.FolderSession remote =
+            yield this.engine.claim_remote_session(cancellable);
+        yield remote.remove_email_async(msg_sets, cancellable);
+
         return ReplayOperation.Status.COMPLETED;
     }
-    
+
     public override async void backout_local_async() throws Error {
         if (removed_ids != null && removed_ids.size > 0) {
             yield engine.local_folder.mark_removed_async(removed_ids, false, cancellable);
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala
index 11ab3f7..6091052 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala
@@ -9,22 +9,27 @@ private class Geary.ImapEngine.ReplayAppend : Geary.ImapEngine.ReplayOperation {
     private MinimalFolder owner;
     private int remote_count;
     private Gee.List<Imap.SequenceNumber> positions;
+    private Cancellable cancellable;
 
     public signal void email_appended(Gee.Collection<Geary.EmailIdentifier> ids);
     public signal void email_locally_appended(Gee.Collection<Geary.EmailIdentifier> ids);
     public signal void email_count_changed(int count, Folder.CountChangeReason reason);
 
 
-    public ReplayAppend(MinimalFolder owner, int remote_count, Gee.List<Imap.SequenceNumber> positions) {
+    public ReplayAppend(MinimalFolder owner,
+                        int remote_count,
+                        Gee.List<Imap.SequenceNumber> positions,
+                        Cancellable cancellable) {
         // IGNORE remote errors because the reconnect will re-normalize the folder, making this
         // append moot
         base ("Append", Scope.REMOTE_ONLY, OnError.IGNORE);
-        
+
         this.owner = owner;
         this.remote_count = remote_count;
         this.positions = positions;
+        this.cancellable = cancellable;
     }
-    
+
     public override void notify_remote_removed_position(Imap.SequenceNumber removed) {
         Gee.List<Imap.SequenceNumber> new_positions = new Gee.ArrayList<Imap.SequenceNumber>();
         foreach (Imap.SequenceNumber? position in positions) {
@@ -58,7 +63,8 @@ private class Geary.ImapEngine.ReplayAppend : Geary.ImapEngine.ReplayOperation {
     public override async void backout_local_async() throws Error {
     }
 
-    public override async ReplayOperation.Status replay_remote_async() {
+    public override async ReplayOperation.Status replay_remote_async()
+        throws Error {
         if (this.positions.size > 0)
             yield do_replay_appended_messages();
 
@@ -74,7 +80,8 @@ private class Geary.ImapEngine.ReplayAppend : Geary.ImapEngine.ReplayOperation {
     // properly relative to the end of the message list; once this is done, notify user of new
     // messages.  If duplicates, create_email_async() will fall through to an updated merge,
     // which is exactly what we want.
-    private async void do_replay_appended_messages() {
+    private async void do_replay_appended_messages()
+        throws Error {
         StringBuilder positions_builder = new StringBuilder("( ");
         foreach (Imap.SequenceNumber remote_position in this.positions)
             positions_builder.append_printf("%s ", remote_position.to_string());
@@ -85,51 +92,46 @@ private class Geary.ImapEngine.ReplayAppend : Geary.ImapEngine.ReplayOperation {
 
         Gee.HashSet<Geary.EmailIdentifier> created = new Gee.HashSet<Geary.EmailIdentifier>();
         Gee.HashSet<Geary.EmailIdentifier> appended = new Gee.HashSet<Geary.EmailIdentifier>();
-        try {
-            Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.sparse(this.positions);
-            foreach (Imap.MessageSet msg_set in msg_sets) {
-                Gee.List<Geary.Email>? list = yield this.owner.remote_folder.list_email_async(msg_set,
-                    ImapDB.Folder.REQUIRED_FIELDS, null);
-                if (list != null && list.size > 0) {
-                    debug("%s do_replay_appended_message: %d new messages in %s", to_string(),
-                        list.size, msg_set.to_string());
-
-                    // need to report both if it was created (not known before) and appended (which
-                    // could mean created or simply a known email associated with this folder)
-                    Gee.Map<Geary.Email, bool> created_or_merged =
-                        yield this.owner.local_folder.create_or_merge_email_async(list, null);
-                    foreach (Geary.Email email in created_or_merged.keys) {
-                        // true means created
-                        if (created_or_merged.get(email)) {
-                            debug("%s do_replay_appended_message: appended email ID %s added",
-                                to_string(), email.id.to_string());
-
-                            created.add(email.id);
-                        } else {
-                            debug("%s do_replay_appended_message: appended email ID %s associated",
-                                to_string(), email.id.to_string());
-                        }
-
-                        appended.add(email.id);
+        Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.sparse(this.positions);
+        Imap.FolderSession remote =
+            yield this.owner.claim_remote_session(this.cancellable);
+        foreach (Imap.MessageSet msg_set in msg_sets) {
+            Gee.List<Geary.Email>? list = yield remote.list_email_async(
+                msg_set, ImapDB.Folder.REQUIRED_FIELDS, this.cancellable
+            );
+            if (list != null && list.size > 0) {
+                debug("%s do_replay_appended_message: %d new messages in %s", to_string(),
+                      list.size, msg_set.to_string());
+
+                // need to report both if it was created (not known before) and appended (which
+                // could mean created or simply a known email associated with this folder)
+                Gee.Map<Geary.Email, bool> created_or_merged =
+                    yield this.owner.local_folder.create_or_merge_email_async(list, this.cancellable);
+                foreach (Geary.Email email in created_or_merged.keys) {
+                    // true means created
+                    if (created_or_merged.get(email)) {
+                        debug("%s do_replay_appended_message: appended email ID %s added",
+                              to_string(), email.id.to_string());
+
+                        created.add(email.id);
+                    } else {
+                        debug("%s do_replay_appended_message: appended email ID %s associated",
+                              to_string(), email.id.to_string());
                     }
-                } else {
-                    debug("%s do_replay_appended_message: no new messages in %s", to_string(),
-                        msg_set.to_string());
+
+                    appended.add(email.id);
                 }
+            } else {
+                debug("%s do_replay_appended_message: no new messages in %s", to_string(),
+                      msg_set.to_string());
             }
-        } catch (Error err) {
-            debug("%s do_replay_appended_message: Unable to process: %s",
-                to_string(), err.message);
         }
 
         // store the reported count, *not* the current count (which is updated outside the of
         // the queue) to ensure that updates happen serially and reflect committed local changes
-        try {
-            yield this.owner.local_folder.update_remote_selected_message_count(this.remote_count, null);
-        } catch (Error err) {
-            debug("%s do_replay_appended_message: Unable to save appended remote count %d: %s",
-                to_string(), this.remote_count, err.message);
-        }
+        yield this.owner.local_folder.update_remote_selected_message_count(
+            this.remote_count, this.cancellable
+        );
 
         if (appended.size > 0)
             email_appended(appended);
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-server-search-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-server-search-email.vala
index 3984961..cee124d 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-server-search-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-server-search-email.vala
@@ -29,12 +29,16 @@ private class Geary.ImapEngine.ServerSearchEmail : Geary.ImapEngine.AbstractList
         // accumulate nothing, nothing unfulfilled (yet)
         return ReplayOperation.Status.CONTINUE;
     }
-    
+
     public override async ReplayOperation.Status replay_remote_async() throws Error {
-        Gee.SortedSet<Imap.UID>? uids = yield owner.remote_folder.search_async(criteria, cancellable);
+        Imap.FolderSession remote =
+            yield this.owner.claim_remote_session(this.cancellable);
+        Gee.SortedSet<Imap.UID>? uids = yield remote.search_async(
+            criteria, this.cancellable
+        );
         if (uids == null || uids.size == 0)
             return ReplayOperation.Status.COMPLETED;
-        
+
         // if the earliest UID is not in the local store, then need to expand vector to it
         Geary.EmailIdentifier? first_id = yield owner.local_folder.get_id_async(uids.first(),
             ImapDB.Folder.ListFlags.NONE, cancellable);


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