From 3abbb95c980deaebb668aca36e9ef89f2f230fb1 Mon Sep 17 00:00:00 2001 From: Jim Nelson Date: Mon, 2 Sep 2013 16:59:01 -0700 Subject: [PATCH] Full normalization when marked messages are not removed: Closes #7402 Because full folder normalization is avoided whenever possible, need to go back to marking messages as removed in the database (vs. just in memory) so if messages are not removed on the server (or the response was not received for some reason), the folder is seen as "dirty" and a full normalization is processed. --- src/engine/imap-db/imap-db-folder.vala | 217 ++++++++++-------- .../imap-engine-generic-folder.vala | 43 +++- .../imap-engine/imap-engine-replay-queue.vala | 3 + .../imap-engine-server-search-email.vala | 5 +- 4 files changed, 157 insertions(+), 111 deletions(-) diff --git a/src/engine/imap-db/imap-db-folder.vala b/src/engine/imap-db/imap-db-folder.vala index b9350f84..6197b71c 100644 --- a/src/engine/imap-db/imap-db-folder.vala +++ b/src/engine/imap-db/imap-db-folder.vala @@ -59,11 +59,13 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { public int64 message_id; public Imap.UID uid; public ImapDB.EmailIdentifier email_id; + public bool marked_removed; - public LocationIdentifier(int64 message_id, Imap.UID uid) { + public LocationIdentifier(int64 message_id, Imap.UID uid, bool marked_removed) { this.message_id = message_id; this.uid = uid; - this.email_id = new ImapDB.EmailIdentifier(message_id, uid); + email_id = new ImapDB.EmailIdentifier(message_id, uid); + this.marked_removed = marked_removed; } } @@ -75,7 +77,6 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { private string account_owner_email; private int64 folder_id; private Geary.Imap.FolderProperties properties; - private Gee.HashSet marked_removed = new Gee.HashSet(); /** * Fired after one or more emails have been fetched with all Fields, and @@ -113,49 +114,6 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { this.properties = properties; } - // Should be called whenever the main Geary.Folder interface is opened or closed - public void reset(){ - lock (marked_removed) { - marked_removed.clear(); - } - - // TODO: Wait for all I/O to complete before exiting - } - - // Returns true if the EmailIdentifier was marked before being removed - private bool unmark_removed(Imap.UID uid) { - lock (marked_removed) { - return marked_removed.remove(uid); - } - } - - private void mark_unmark_removed(Gee.Collection uids, bool mark) { - lock (marked_removed) { - if (mark) - marked_removed.add_all(uids); - else - marked_removed.remove_all(uids); - } - } - - private void clear_marked_removed() { - lock (marked_removed) { - marked_removed.clear(); - } - } - - private bool is_marked_removed(Imap.UID uid) { - lock (marked_removed) { - return marked_removed.contains(uid); - } - } - - private int get_marked_removed_count() { - lock (marked_removed) { - return marked_removed.size; - } - } - public async int get_email_count_async(ListFlags flags, Cancellable? cancellable) throws Error { int count = 0; yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => { @@ -275,8 +233,10 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { // convert initial_id into UID to start walking the list Imap.UID? start_uid = null; if (initial_id != null) { + // use INCLUDE_MARKED_FOR_REMOVE because this is a ranged list ... + // do_results_to_location() will deal with removing EmailIdentifiers if necessary LocationIdentifier? location = do_get_location_for_id(cx, initial_id, - ListFlags.NONE, cancellable); + ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable); if (location == null) return Db.TransactionOutcome.DONE; @@ -296,7 +256,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { } StringBuilder sql = new StringBuilder(""" - SELECT MessageLocationTable.message_id, ordering + SELECT MessageLocationTable.message_id, ordering, remove_marker FROM MessageLocationTable """); if (only_incomplete) { @@ -348,15 +308,18 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { // database ... first, gather locations of all emails in database Gee.List? locations = null; yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => { - LocationIdentifier? start_location = do_get_location_for_id(cx, start_id, ListFlags.NONE, - cancellable); + // use INCLUDE_MARKED_FOR_REMOVE because this is a ranged list ... + // do_results_to_location() will deal with removing EmailIdentifiers if necessary + LocationIdentifier? start_location = do_get_location_for_id(cx, start_id, + ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable); if (start_location == null) return Db.TransactionOutcome.DONE; Imap.UID start_uid = start_location.uid; - LocationIdentifier? end_location = do_get_location_for_id(cx, end_id, ListFlags.NONE, - cancellable); + // see note above about INCLUDE_MARKED_FOR_REMOVE + LocationIdentifier? end_location = do_get_location_for_id(cx, end_id, + ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable); if (end_location == null) return Db.TransactionOutcome.DONE; @@ -371,7 +334,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { return Db.TransactionOutcome.DONE; Db.Statement stmt = cx.prepare(""" - SELECT message_id, ordering + SELECT message_id, ordering, remove_marker FROM MessageLocationTable WHERE folder_id = ? AND ordering >= ? AND ordering <= ? """); @@ -412,7 +375,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { Gee.List? locations = null; yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => { StringBuilder sql = new StringBuilder(""" - SELECT MessageLocationTable.message_id, ordering + SELECT MessageLocationTable.message_id, ordering, remove_marker FROM MessageLocationTable """); @@ -453,7 +416,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { Gee.List locations = new Gee.ArrayList(); yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => { StringBuilder sql = new StringBuilder(""" - SELECT MessageLocationTable.message_id, ordering + SELECT MessageLocationTable.message_id, ordering, remove_marker FROM MessageLocationTable """); @@ -548,8 +511,9 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { return email; } + // Note that this does INCLUDES messages marked for removal public async Gee.SortedSet? list_uids_by_range_async(Imap.UID first_uid, Imap.UID last_uid, - Cancellable? cancellable) throws Error { + bool include_marked_for_removal, Cancellable? cancellable) throws Error { // order correctly Imap.UID start, end; if (first_uid.compare_to(last_uid) < 0) { @@ -563,7 +527,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { Gee.SortedSet uids = new Gee.TreeSet(); yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => { Db.Statement stmt = cx.prepare(""" - SELECT ordering + SELECT ordering, remove_marker FROM MessageLocationTable WHERE folder_id = ? AND ordering >= ? AND ordering <= ? """); @@ -573,7 +537,9 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { Db.Result result = stmt.exec(cancellable); while (!result.finished) { - uids.add(new Imap.UID(result.int64_at(0))); + if (include_marked_for_removal || !result.bool_at(1)) + uids.add(new Imap.UID(result.int64_at(0))); + result.next(cancellable); } @@ -645,11 +611,11 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { } // Returns null if the UID is not found in this Folder. - public async ImapDB.EmailIdentifier? get_id_async(Imap.UID uid, Cancellable? cancellable) - throws Error { + public async ImapDB.EmailIdentifier? get_id_async(Imap.UID uid, ListFlags flags, + Cancellable? cancellable) throws Error { ImapDB.EmailIdentifier? id = null; yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => { - LocationIdentifier? location = do_get_location_for_uid(cx, uid, ListFlags.NONE, + LocationIdentifier? location = do_get_location_for_uid(cx, uid, flags, cancellable); if (location != null) id = location.email_id; @@ -660,12 +626,12 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { return id; } - public async Gee.Set get_ids_async(Gee.Collection uids, - Cancellable? cancellable) throws Error { + public async Gee.Set? get_ids_async(Gee.Collection uids, + ListFlags flags, Cancellable? cancellable) throws Error { Gee.Set ids = new Gee.HashSet(); yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => { foreach (Imap.UID uid in uids) { - LocationIdentifier? location = do_get_location_for_uid(cx, uid, ListFlags.NONE, + LocationIdentifier? location = do_get_location_for_uid(cx, uid, flags, cancellable); if (location != null) ids.add(location.email_id); @@ -724,8 +690,8 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { // remove one at a time, gather UIDs Gee.HashSet uids = new Gee.HashSet(); foreach (ImapDB.EmailIdentifier id in ids) { - LocationIdentifier? location = do_get_location_for_id(cx, id, ListFlags.NONE, - cancellable); + LocationIdentifier? location = do_get_location_for_id(cx, id, + ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable); if (location == null) continue; @@ -737,9 +703,6 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { uids.add(location.uid); } - // Remove any that may have been marked removed - mark_unmark_removed(uids, false); - return Db.TransactionOutcome.COMMIT; }, cancellable); } @@ -750,8 +713,6 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { "DELETE FROM MessageLocationTable WHERE folder_id=?"); stmt.bind_rowid(0, folder_id); - clear_marked_removed(); - return Db.TransactionOutcome.COMMIT; }, cancellable); } @@ -882,7 +843,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { was_unread = true; } - internal_is_marked = unmark_removed(location.uid); + internal_is_marked = location.marked_removed; do_remove_association_with_folder(cx, location, cancellable); @@ -905,18 +866,19 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { Gee.Collection ids, bool mark_removed, Cancellable? cancellable) throws Error { Gee.Set removed_ids = new Gee.HashSet(); - yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => { + yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => { Gee.HashSet uids = new Gee.HashSet(); foreach (ImapDB.EmailIdentifier id in ids) { - LocationIdentifier? location = do_get_location_for_id(cx, id, ListFlags.NONE, - cancellable); + LocationIdentifier? location = do_get_location_for_id(cx, id, + ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable); if (location != null) { uids.add(location.uid); removed_ids.add(location.email_id); } } - mark_unmark_removed(uids, mark_removed); + if (uids.size > 0) + do_mark_unmark_removed(cx, uids, mark_removed, cancellable); return Db.TransactionOutcome.DONE; }, cancellable); @@ -924,8 +886,39 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { return (removed_ids.size > 0) ? removed_ids : null; } + // Returns the number of messages marked for removal in this folder + public async int get_marked_for_remove_count_async(Cancellable? cancellable) throws Error { + int count = 0; + yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => { + count = do_get_marked_removed_count(cx, cancellable); + + return Db.TransactionOutcome.DONE; + }, cancellable); + + return count; + } + + // Clears all remove markers from the folder + public async void clear_remove_markers_async(Cancellable? cancellable) throws Error { + yield db.exec_transaction_async(Db.TransactionType.WO, (cx) => { + Db.Statement stmt = cx.prepare(""" + UPDATE MessageLocationTable + SET remove_marker=? + WHERE folder_id=? AND remove_marker <> ? + """); + stmt.bind_bool(0, false); + stmt.bind_rowid(1, folder_id); + stmt.bind_bool(2, false); + + stmt.exec(cancellable); + + return Db.TransactionOutcome.COMMIT; + }, cancellable); + } + public async Gee.Map? list_email_fields_by_id_async( - Gee.Collection ids, Cancellable? cancellable) throws Error { + Gee.Collection ids, ListFlags flags, Cancellable? cancellable) + throws Error { if (ids.size == 0) return null; @@ -945,7 +938,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { "SELECT fields FROM MessageTable WHERE id = ?"); foreach (ImapDB.EmailIdentifier id in list) { - LocationIdentifier? location_id = do_get_location_for_id(cx, id, ListFlags.NONE, + LocationIdentifier? location_id = do_get_location_for_id(cx, id, flags, cancellable); if (location_id == null) continue; @@ -987,11 +980,40 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { if (results.finished) return 0; - int marked = !flags.include_marked_for_remove() ? get_marked_removed_count() : 0; + int marked = !flags.include_marked_for_remove() ? do_get_marked_removed_count(cx, cancellable) : 0; return Numeric.int_floor(results.int_at(0) - marked, 0); } + private int do_get_marked_removed_count(Db.Connection cx, Cancellable? cancellable) throws Error { + Db.Statement stmt = cx.prepare( + "SELECT COUNT(*) FROM MessageLocationTable WHERE folder_id=? AND remove_marker <> ?"); + stmt.bind_rowid(0, folder_id); + stmt.bind_bool(1, false); + + Db.Result results = stmt.exec(cancellable); + + return !results.finished ? results.int_at(0) : 0; + } + + private void do_mark_unmark_removed(Db.Connection cx, Gee.Collection uids, + bool mark_removed, Cancellable? cancellable) throws Error { + // prepare Statement for reuse + Db.Statement stmt = cx.prepare( + "UPDATE MessageLocationTable SET remove_marker=? WHERE folder_id=? AND ordering=?"); + stmt.bind_bool(0, mark_removed); + stmt.bind_rowid(1, folder_id); + + foreach (Imap.UID uid in uids) { + stmt.bind_int64(2, uid.value); + + stmt.exec(cancellable); + + // keep folder_id and mark_removed, replace UID each iteration + stmt.reset(Db.ResetScope.SAVE_BINDINGS); + } + } + // Returns message_id if duplicate found, associated set to true if message is already associated // with this folder. Only call this on emails that came from the IMAP Folder. private LocationIdentifier? do_search_for_duplicates(Db.Connection cx, Geary.Email email, @@ -1008,7 +1030,8 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { // See if it already exists; first by UID (which is only guaranteed to // be unique in a folder, not account-wide) if (email_id.uid != null) - location = do_get_location_for_uid(cx, email_id.uid, ListFlags.NONE, cancellable); + location = do_get_location_for_uid(cx, email_id.uid, ListFlags.INCLUDE_MARKED_FOR_REMOVE, + cancellable); if (location != null) { associated = true; @@ -1056,17 +1079,18 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { } Db.Statement search_stmt = cx.prepare( - "SELECT ordering FROM MessageLocationTable WHERE message_id=? AND folder_id=?"); + "SELECT ordering, remove_marker FROM MessageLocationTable WHERE message_id=? AND folder_id=?"); search_stmt.bind_rowid(0, message_id); search_stmt.bind_rowid(1, folder_id); Db.Result search_results = search_stmt.exec(cancellable); if (!search_results.finished) { associated = true; - location = new LocationIdentifier(message_id, new Imap.UID(search_results.int64_at(0))); + location = new LocationIdentifier(message_id, new Imap.UID(search_results.int64_at(0)), + search_results.bool_at(1)); } else { assert(email_id.uid != null); - location = new LocationIdentifier(message_id, email_id.uid); + location = new LocationIdentifier(message_id, email_id.uid, false); } return location; @@ -1278,7 +1302,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { private Geary.Email do_location_to_email(Db.Connection cx, LocationIdentifier location, Geary.Email.Field required_fields, ListFlags flags, Cancellable? cancellable) throws Error { - if (!flags.include_marked_for_remove() && is_marked_removed(location.uid)) { + if (!flags.include_marked_for_remove() && location.marked_removed) { throw new EngineError.NOT_FOUND("Message %s marked as removed in %s", location.email_id.to_string(), to_string()); } @@ -1871,7 +1895,8 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { update_stmt.exec(cancellable); } - // Db.Result must include columns for "message_id" and "ordering" from the MessageLocationTable + // Db.Result must include columns for "message_id", "ordering", and "remove_marker" from the + // MessageLocationTable private Gee.List do_results_to_locations(Db.Result results, ListFlags flags, Cancellable? cancellable) throws Error { Gee.List locations = new Gee.ArrayList(); @@ -1881,8 +1906,8 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { do { LocationIdentifier location = new LocationIdentifier(results.rowid_for("message_id"), - new Imap.UID(results.int64_for("ordering"))); - if (!flags.include_marked_for_remove() && is_marked_removed(location.uid)) + new Imap.UID(results.int64_for("ordering")), results.bool_for("remove_marker")); + if (!flags.include_marked_for_remove() && location.marked_removed) continue; locations.add(location); @@ -1894,7 +1919,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { private LocationIdentifier? do_get_location_for_id(Db.Connection cx, ImapDB.EmailIdentifier id, ListFlags flags, Cancellable? cancellable) throws Error { Db.Statement stmt = cx.prepare(""" - SELECT ordering + SELECT ordering, remove_marker FROM MessageLocationTable WHERE folder_id = ? AND message_id = ? """); @@ -1905,20 +1930,16 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { if (result.finished) return null; - Imap.UID uid = new Imap.UID(result.int64_at(0)); - if (!flags.include_marked_for_remove() && is_marked_removed(uid)) - return null; + LocationIdentifier location = new LocationIdentifier(id.message_id, + new Imap.UID(result.int64_at(0)), result.bool_at(1)); - return new LocationIdentifier(id.message_id, uid); + return (!flags.include_marked_for_remove() && location.marked_removed) ? null : location; } private LocationIdentifier? do_get_location_for_uid(Db.Connection cx, Imap.UID uid, ListFlags flags, Cancellable? cancellable) throws Error { - if (!flags.include_marked_for_remove() && is_marked_removed(uid)) - return null; - Db.Statement stmt = cx.prepare(""" - SELECT message_id + SELECT message_id, remove_marker FROM MessageLocationTable WHERE folder_id = ? AND ordering = ? """); @@ -1929,7 +1950,9 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { if (result.finished) return null; - return new LocationIdentifier(result.rowid_at(0), uid); + LocationIdentifier location = new LocationIdentifier(result.rowid_at(0), uid, result.bool_at(1)); + + return (!flags.include_marked_for_remove() && location.marked_removed) ? null : location; } } diff --git a/src/engine/imap-engine/imap-engine-generic-folder.vala b/src/engine/imap-engine/imap-engine-generic-folder.vala index 0f356fdc..0d7a100a 100644 --- a/src/engine/imap-engine/imap-engine-generic-folder.vala +++ b/src/engine/imap-engine/imap-engine-generic-folder.vala @@ -157,12 +157,26 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde assert(local_earliest_id.has_uid()); assert(local_latest_id.has_uid()); + // if any messages are still marked for removal from last time, that means the EXPUNGE + // never arrived from the server, in which case the folder is "dirty" and needs a full + // normalization + int remove_markers = yield local_folder.get_marked_for_remove_count_async(cancellable); + bool is_dirty = (remove_markers != 0); + + if (is_dirty) + debug("%s: %d remove markers found, folder is dirty", to_string(), remove_markers); + // if UIDNEXT has changed, that indicates messages have been appended (and possibly removed) int64 uidnext_diff = remote_properties.uid_next.value - local_properties.uid_next.value; + int local_message_count = (local_properties.select_examine_messages >= 0) + ? local_properties.select_examine_messages : 0; + int remote_message_count = (remote_properties.select_examine_messages >= 0) + ? remote_properties.select_examine_messages : 0; + // if UIDNEXT is the same as last time AND the total count of email is the same, then // nothing has been added or removed - if (uidnext_diff == 0 && local_properties.email_total == remote_properties.email_total) { + if (!is_dirty && uidnext_diff == 0 && local_message_count == remote_message_count) { debug("%s: No messages added/removed since last opened, normalization completed", to_string()); return true; @@ -180,7 +194,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde // (Also, this cannot fail; if this situation exists, then it cannot by definition indicate another // situation, esp. messages being removed.) Imap.UID first_uid; - if (uidnext_diff == (remote_properties.select_examine_messages - local_properties.select_examine_messages)) { + if (!is_dirty && uidnext_diff == (remote_message_count - local_message_count)) { first_uid = local_latest_id.uid.next(); debug("%s: Messages only appended (local/remote UIDNEXT=%s/%s total=%d/%d diff=%s), gathering mail UIDs %s:%s", @@ -199,7 +213,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde // get all the UIDs in said range from the local store, sorted; convert to non-null // for ease of use later Gee.SortedSet? local_uids = yield local_folder.list_uids_by_range_async( - first_uid, last_uid, cancellable); + first_uid, last_uid, true, cancellable); if (local_uids == null) local_uids = new Gee.TreeSet(); @@ -233,6 +247,9 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde discovered_uids.add(remote_uid); } + debug("%s: changes since last seen: removed=%d appended=%d discovered=%d", to_string(), + removed_uids.size, appended_uids.size, discovered_uids.size); + // fetch from the server the local store's required flags for all appended/inserted messages // (which is simply equal to all remaining remote UIDs) Gee.List? to_create = null; @@ -277,12 +294,20 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde // Convert removed UIDs into EmailIdentifiers and detach immediately Gee.Set? removed_ids = null; if (removed_uids.size > 0) { - removed_ids = yield local_folder.get_ids_async(removed_uids, cancellable); - yield local_folder.detach_multiple_emails_async(removed_ids, cancellable); + removed_ids = yield local_folder.get_ids_async(removed_uids, + ImapDB.Folder.ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable); + if (removed_ids != null && removed_ids.size > 0) { + yield local_folder.detach_multiple_emails_async(removed_ids, cancellable); + } } check_open("normalize_folders (removed emails)"); + // remove any extant remove markers, as everything is accounted for now + yield local_folder.clear_remove_markers_async(cancellable); + + check_open("normalize_folders (clear remove markers)"); + // // now normalized // notify subscribers of changes @@ -356,9 +381,6 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde // start the replay queue replay_queue = new ReplayQueue(this); - // reset state in the local (database) folder - local_folder.reset(); - // do NOT open the remote side here; wait for the ReplayQueue to require a remote connection // or wait_for_open_async() to be called ... this allows for fast local-only operations // to occur, local-only either because (a) the folder has all the information required @@ -539,9 +561,6 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde // time, even if remote was not fully opened, as some callers rely on order of signals notify_closed(remote_reason); - // close local store (reset its state) - local_folder.reset(); - // see above note for why this must be called every time notify_closed(local_reason); @@ -921,7 +940,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde check_ids("list_local_email_fields_async", ids); return yield local_folder.list_email_fields_by_id_async( - (Gee.Collection) ids, cancellable); + (Gee.Collection) ids, ImapDB.Folder.ListFlags.NONE, cancellable); } public override async Geary.Email fetch_email_async(Geary.EmailIdentifier id, diff --git a/src/engine/imap-engine/imap-engine-replay-queue.vala b/src/engine/imap-engine/imap-engine-replay-queue.vala index 00160136..441a09a4 100644 --- a/src/engine/imap-engine/imap-engine-replay-queue.vala +++ b/src/engine/imap-engine/imap-engine-replay-queue.vala @@ -196,6 +196,9 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject { } private bool on_notification_timeout() { + if (notification_queue.size == 0) + return false; + debug("%s: Scheduling %d held server notification operations", owner.to_string(), notification_queue.size); 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 b1032dc3..6b3f67c9 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 @@ -34,7 +34,7 @@ private class Geary.ImapEngine.ServerSearchEmail : Geary.ImapEngine.AbstractList // 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(), - cancellable); + ImapDB.Folder.ListFlags.NONE, cancellable); if (first_id == null) yield expand_vector_async(uids.first(), 1); @@ -45,7 +45,8 @@ private class Geary.ImapEngine.ServerSearchEmail : Geary.ImapEngine.AbstractList // to the database yet) // // TODO: We need a sparse version of this to scoop them up all at once - ImapDB.EmailIdentifier? id = yield owner.local_folder.get_id_async(uid, cancellable); + ImapDB.EmailIdentifier? id = yield owner.local_folder.get_id_async(uid, + ImapDB.Folder.ListFlags.NONE, cancellable); if (id != null) local_ids.add(id); }