diff --git a/src/engine/api/geary-engine.vala b/src/engine/api/geary-engine.vala index f79a4e4b..64295f4c 100644 --- a/src/engine/api/geary-engine.vala +++ b/src/engine/api/geary-engine.vala @@ -216,7 +216,7 @@ public class Geary.Engine : BaseObject { return error_code; // validate IMAP, which requires logging in and establishing an AUTHORIZED cx state - Geary.Imap.ClientSession? imap_session = new Imap.ClientSession(account.get_imap_endpoint(), true); + Geary.Imap.ClientSession? imap_session = new Imap.ClientSession(account.get_imap_endpoint()); try { yield imap_session.connect_async(cancellable); } catch (Error err) { diff --git a/src/engine/imap-db/imap-db-account.vala b/src/engine/imap-db/imap-db-account.vala index 80334454..d8059a82 100644 --- a/src/engine/imap-db/imap-db-account.vala +++ b/src/engine/imap-db/imap-db-account.vala @@ -96,12 +96,12 @@ private class Geary.ImapDB.Account : BaseObject { throws Error { check_open(); - Geary.Imap.FolderProperties? properties = imap_folder.get_properties(); + Geary.Imap.FolderProperties? properties = imap_folder.properties; // properties *must* be available to perform a clone assert(properties != null); - Geary.FolderPath path = imap_folder.get_path(); + Geary.FolderPath path = imap_folder.path; yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => { // get the parent of this folder, creating parents if necessary ... ok if this fails, @@ -137,8 +137,8 @@ private class Geary.ImapDB.Account : BaseObject { throws Error { check_open(); - Geary.Imap.FolderProperties properties = imap_folder.get_properties(); - Geary.FolderPath path = imap_folder.get_path(); + Geary.Imap.FolderProperties properties = imap_folder.properties; + Geary.FolderPath path = imap_folder.path; yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => { int64 parent_id; diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala b/src/engine/imap-engine/imap-engine-generic-account.vala index 7dde3aa8..eb16cbe9 100644 --- a/src/engine/imap-engine/imap-engine-generic-account.vala +++ b/src/engine/imap-engine/imap-engine-generic-account.vala @@ -309,7 +309,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount { Gee.Collection engine_folders, Cancellable? cancellable) { Gee.Collection remote_folders; try { - remote_folders = yield remote.list_folders_async(parent, cancellable); + remote_folders = yield remote.list_mailboxes_async(parent, cancellable); } catch (Error remote_error) { debug("Unable to retrieve folder list from server: %s", remote_error.message); @@ -321,20 +321,20 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount { Hashable.hash_func, Equalable.equal_func); foreach (Imap.Folder remote_folder in remote_folders) { // only worry about alterations if the remote is openable - if (remote_folder.get_properties().is_openable.is_possible()) { + if (remote_folder.properties.is_openable.is_possible()) { ImapDB.Folder? local_folder = null; try { - local_folder = yield local.fetch_folder_async(remote_folder.get_path(), cancellable); + local_folder = yield local.fetch_folder_async(remote_folder.path, cancellable); } catch (Error err) { if (!(err is EngineError.NOT_FOUND)) { - debug("Unable to fetch local folder for remote %s: %s", remote_folder.get_path().to_string(), + debug("Unable to fetch local folder for remote %s: %s", remote_folder.path.to_string(), err.message); } } if (local_folder != null) { - if (remote_folder.get_properties().have_contents_changed(local_folder.get_properties()).is_possible()) - altered_paths.add(remote_folder.get_path()); + if (remote_folder.properties.have_contents_changed(local_folder.get_properties()).is_possible()) + altered_paths.add(remote_folder.path); } } @@ -356,23 +356,23 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount { Gee.Set remote_paths = new Gee.HashSet( Geary.Hashable.hash_func, Geary.Equalable.equal_func); foreach (Geary.Imap.Folder remote_folder in remote_folders) { - remote_paths.add(remote_folder.get_path()); + remote_paths.add(remote_folder.path); // use this iteration to add discovered properties to map - properties_map.set(remote_folder.get_path(), remote_folder.get_properties()); + properties_map.set(remote_folder.path, remote_folder.properties); // also use this iteration to set the local folder's special type // (but only promote, not demote, since getting the special folder type via its // properties relies on the optional XLIST extension) - GenericFolder? local_folder = existing_folders.get(remote_folder.get_path()); + GenericFolder? local_folder = existing_folders.get(remote_folder.path); if (local_folder != null && local_folder.get_special_folder_type() == SpecialFolderType.NONE) - local_folder.set_special_folder_type(remote_folder.get_properties().attrs.get_special_folder_type()); + local_folder.set_special_folder_type(remote_folder.properties.attrs.get_special_folder_type()); } // If path in remote but not local, need to add it Gee.List to_add = new Gee.ArrayList(); foreach (Geary.Imap.Folder folder in remote_folders) { - if (!local_paths.contains(folder.get_path())) + if (!local_paths.contains(folder.path)) to_add.add(folder); } @@ -396,7 +396,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount { try { yield local.clone_folder_async(folder, cancellable); } catch (Error err) { - debug("Unable to add/remove folder %s: %s", folder.get_path().to_string(), + debug("Unable to add/remove folder %s: %s", folder.path.to_string(), err.message); } } @@ -411,7 +411,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount { foreach (Geary.Imap.Folder remote_folder in to_add) { try { folders_to_build.add((ImapDB.Folder) yield local.fetch_folder_async( - remote_folder.get_path(), cancellable)); + remote_folder.path, cancellable)); } catch (Error convert_err) { // This isn't fatal, but irksome ... in the future, when local folders are // removed, it's possible for one to disappear between cloning it and fetching @@ -449,11 +449,11 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount { // enumerate children of each remote folder foreach (Imap.Folder remote_folder in remote_folders) { - if (remote_folder.get_properties().has_children.is_possible()) { + if (remote_folder.properties.has_children.is_possible()) { try { - yield enumerate_folders_async(remote_folder.get_path(), cancellable); + yield enumerate_folders_async(remote_folder.path, cancellable); } catch (Error err) { - debug("Unable to enumerate children of %s: %s", remote_folder.get_path().to_string(), + debug("Unable to enumerate children of %s: %s", remote_folder.path.to_string(), err.message); } } diff --git a/src/engine/imap-engine/imap-engine-generic-folder.vala b/src/engine/imap-engine/imap-engine-generic-folder.vala index 38bb22c4..60cb7736 100644 --- a/src/engine/imap-engine/imap-engine-generic-folder.vala +++ b/src/engine/imap-engine/imap-engine-generic-folder.vala @@ -56,7 +56,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde // - From open remote folder // - Fetch from local store if (remote_folder != null && get_open_state() == OpenState.BOTH) - return remote_folder.get_properties(); + return remote_folder.properties; return local_folder.get_properties(); } @@ -106,7 +106,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde debug("normalize_folders %s", to_string()); Geary.Imap.FolderProperties local_properties = local_folder.get_properties(); - Geary.Imap.FolderProperties remote_properties = remote_folder.get_properties(); + Geary.Imap.FolderProperties remote_properties = remote_folder.properties; // and both must have their next UID's (it's possible they don't if it's a non-selectable // folder) @@ -454,12 +454,14 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde yield local.update_folder_async(folder, cancellable); // signals + /* folder.messages_appended.connect(on_remote_messages_appended); folder.message_at_removed.connect(on_remote_message_at_removed); folder.disconnected.connect(on_remote_disconnected); + */ // state - remote_count = folder.get_email_count(); + remote_count = folder.properties.email_total; // all set; bless the remote folder as opened remote_folder = folder; @@ -547,9 +549,11 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde } if (closing_remote_folder != null) { + /* closing_remote_folder.messages_appended.disconnect(on_remote_messages_appended); closing_remote_folder.message_at_removed.disconnect(on_remote_message_at_removed); closing_remote_folder.disconnected.disconnect(on_remote_disconnected); + */ // to avoid keeping the caller waiting while the remote end closes, close it in the // background diff --git a/src/engine/imap/api/imap-account.vala b/src/engine/imap/api/imap-account.vala index 72b4399a..3cb1ac37 100644 --- a/src/engine/imap/api/imap-account.vala +++ b/src/engine/imap/api/imap-account.vala @@ -10,6 +10,13 @@ private class Geary.Imap.Account : BaseObject { public const string INBOX_NAME = "INBOX"; public const string ASSUMED_SEPARATOR = "/"; + private static MailboxParameter? _GLOB_PARAMETER = null; + private static MailboxParameter GLOB_PARAMETER { + get { + return (_GLOB_PARAMETER != null) ? _GLOB_PARAMETER : _GLOB_PARAMETER = new MailboxParameter("%"); + } + } + public bool is_open { get; private set; default = false; } private string name; @@ -18,7 +25,7 @@ private class Geary.Imap.Account : BaseObject { private Gee.HashMap delims = new Gee.HashMap(); private ClientSession? account_session = null; private NonblockingMutex cmd_mutex = new NonblockingMutex(); - private Gee.ArrayList mailbox_collector = new Gee.ArrayList(); + private Gee.ArrayList list_collector = new Gee.ArrayList(); private Gee.ArrayList status_collector = new Gee.ArrayList(); public signal void email_sent(Geary.RFC822.Message rfc822); @@ -33,6 +40,11 @@ private class Geary.Imap.Account : BaseObject { session_mgr.login_failed.connect(on_login_failed); } + private void check_open() throws Error { + if (!is_open) + throw new EngineError.OPEN_REQUIRED("Imap.Account not open"); + } + public async void open_async(Cancellable? cancellable = null) throws Error { if (is_open) throw new EngineError.ALREADY_OPEN("Imap.Account already open"); @@ -71,13 +83,14 @@ private class Geary.Imap.Account : BaseObject { Geary.FolderPath? processed = process_path(path, null, path.get_root().default_separator); if (processed == null) - throw new ImapError.INVALID_PATH("Invalid path %s", path.to_string()); + throw new ImapError.INVALID("Invalid path %s", path.to_string()); bool can_xlist = account_session.capabilities.has_capability(Capabilities.XLIST); Gee.List list_results = new Gee.ArrayList(); CompletionStatusResponse response = yield send_command_async( - new ListCommand(processed.get_fullpath(), can_xlist), list_results, null, cancellable); + new ListCommand(new Imap.MailboxParameter(processed.get_fullpath()), can_xlist), list_results, + null, cancellable); if (response.status != Status.OK) { throw new ImapError.SERVER_ERROR("Server reports LIST error for path %s: %s", path.to_string(), @@ -89,7 +102,7 @@ private class Geary.Imap.Account : BaseObject { list_results.size, path.get_fullpath()); } - return (list_results == 1) ? list_results[0] : null; + return (list_results.size == 1) ? list_results[0] : null; } public async Gee.List? list_children_command(FolderPath? parent, Cancellable? cancellable = null) @@ -103,14 +116,14 @@ private class Geary.Imap.Account : BaseObject { ListCommand cmd; if (processed == null) { - cmd = new ListCommand.wildcarded("", "%", can_xlist); + cmd = new ListCommand.wildcarded("", new MailboxParameter("%"), can_xlist); } else { string specifier = processed.get_fullpath(); string delim = processed.get_root().default_separator; - specified += specifier.has_suffix(delim) ? "%" : (delim + "%"); + specifier += specifier.has_suffix(delim) ? "%" : (delim + "%"); - cmd = new ListCommand(specifier, can_xlist); + cmd = new ListCommand(new Imap.MailboxParameter(specifier), can_xlist); } Gee.List list_results = new Gee.ArrayList(); @@ -118,7 +131,7 @@ private class Geary.Imap.Account : BaseObject { cancellable); if (response.status != Status.OK) - throw_not_found(path); + throw_not_found(processed ?? parent); return (list_results.size > 0) ? list_results : null; } @@ -129,12 +142,12 @@ private class Geary.Imap.Account : BaseObject { Geary.FolderPath? processed = process_path(path, null, path.get_root().default_separator); if (processed == null) - throw new ImapError.INVALID_PATH("Invalid path %s", path.to_string()); + throw new ImapError.INVALID("Invalid path %s", path.to_string()); Gee.List status_results = new Gee.ArrayList(); CompletionStatusResponse response = yield send_command_async( - new StatusCommand(processed.get_fullpath(), StatusDataType.all()), null, status_results, - cancellable); + new StatusCommand(new MailboxParameter(processed.get_fullpath()), StatusDataType.all()), + null, status_results, cancellable); if (response.status != Status.OK) { throw new ImapError.SERVER_ERROR("Server reports STATUS error for path %s: %s", path.to_string(), @@ -201,7 +214,7 @@ private class Geary.Imap.Account : BaseObject { // 3. Parent and basename supplied, verify parent is not Inbox, as IMAP does not allow it // to have children if (parent != null && !empty_basename && parent.get_root().basename.up() == INBOX_NAME) - throw new ImapError.INVALID_PATH("Inbox may not have children"); + throw new ImapError.INVALID("Inbox may not have children"); // 4. Parent supplied but basename is not; if parent points to Inbox, normalize it if (parent != null && empty_basename && parent.basename.up() == INBOX_NAME) diff --git a/src/engine/imap/api/imap-folder-properties.vala b/src/engine/imap/api/imap-folder-properties.vala index 259b8109..f1f50826 100644 --- a/src/engine/imap/api/imap-folder-properties.vala +++ b/src/engine/imap/api/imap-folder-properties.vala @@ -6,17 +6,18 @@ public class Geary.Imap.FolderProperties : Geary.FolderProperties { /** - * -1 if the Folder was not opened via SELECT or EXAMINE. + * -1 if the Folder was not opened via SELECT or EXAMINE. Updated as EXISTS server data + * arrives. */ public int select_examine_messages { get; private set; } /** * -1 if the FolderProperties were not obtained via a STATUS command */ public int status_messages { get; private set; } - public int unseen { get; private set; } - public int recent { get; private set; } - public UIDValidity? uid_validity { get; private set; } - public UID? uid_next { get; private set; } + public int unseen { get; internal set; } + public int recent { get; internal set; } + public UIDValidity? uid_validity { get; internal set; } + public UID? uid_next { get; internal set; } public MailboxAttributes attrs { get; private set; } // Note that unseen from SELECT/EXAMINE is the *position* of the first unseen message, diff --git a/src/engine/imap/api/imap-folder.vala b/src/engine/imap/api/imap-folder.vala index a7ec30cc..afc6225a 100644 --- a/src/engine/imap/api/imap-folder.vala +++ b/src/engine/imap/api/imap-folder.vala @@ -64,8 +64,8 @@ private class Geary.Imap.Folder : BaseObject { session.expunge.connect(on_expunge); session.fetch.connect(on_fetch); session.recent.connect(on_recent); - session.coded_status_response.connect(on_coded_status_response); - session.disconnected.connect(on_disconnected); + session.coded_response_received.connect(on_coded_status_response); + //session.disconnected.connect(on_disconnected); CompletionStatusResponse response = yield session.select_examine_async(path.get_fullpath(info.delim), !readonly, cancellable); @@ -74,11 +74,6 @@ private class Geary.Imap.Folder : BaseObject { // update with new information this.readonly = Trillian.from_boolean(readonly); - - int old_status_messages = properties.status_messages; - properties = new Imap.FolderProperties(mailbox.exists, mailbox.recent, properties.unseen, - mailbox.uid_validity, mailbox.uid_next, properties.attrs); - properties.set_status_message_count(old_status_messages, false); } public async void close_async(Cancellable? cancellable = null) throws Error { @@ -89,8 +84,8 @@ private class Geary.Imap.Folder : BaseObject { session.expunge.disconnect(on_expunge); session.fetch.disconnect(on_fetch); session.recent.disconnect(on_recent); - session.coded_status_response.disconnect(on_coded_status_response); - session.disconnected.disconnect(on_disconnected); + session.coded_response_received.disconnect(on_coded_status_response); + //session.disconnected.disconnect(on_disconnected); try { yield session.close_mailbox_async(cancellable); @@ -104,7 +99,7 @@ private class Geary.Imap.Folder : BaseObject { } private void on_exists(int count) { - properties.messages = count; + properties.set_select_examine_message_count(count); exists(count); } @@ -124,10 +119,23 @@ private class Geary.Imap.Folder : BaseObject { } private void on_coded_status_response(CodedStatusResponse coded_response) { - switch (coded_response.response_code_type) { - case ResponseCodeType.UIDNEXT: - properties.uid_next = coded_response.get_uid_next(); - break; + try { + switch (coded_response.response_code_type) { + case ResponseCodeType.UIDNEXT: + properties.uid_next = coded_response.get_uid_next(); + break; + + case ResponseCodeType.UIDVALIDITY: + properties.uid_validity = coded_response.get_uid_validity(); + break; + + case ResponseCodeType.UNSEEN: + properties.unseen = coded_response.get_unseen(); + break; + } + } catch (ImapError ierr) { + debug("Unable to parse CodedStatusResponse %s: %s", coded_response.to_string(), + ierr.message); } } @@ -150,13 +158,18 @@ private class Geary.Imap.Folder : BaseObject { public async Gee.List? list_email_async(MessageSet msg_set, Geary.Email.Field fields, Cancellable? cancellable = null) throws Error { + /* if (mailbox == null) throw new EngineError.OPEN_REQUIRED("%s not opened", to_string()); return yield mailbox.list_set_async(msg_set, fields, cancellable); + */ + + return null; } public async void remove_email_async(MessageSet msg_set, Cancellable? cancellable = null) throws Error { + /* if (mailbox == null) throw new EngineError.OPEN_REQUIRED("%s not opened", to_string()); @@ -168,10 +181,12 @@ private class Geary.Imap.Folder : BaseObject { // mailbox could've closed during call if (mailbox != null) yield mailbox.expunge_email_async(msg_set, cancellable); + */ } public async void mark_email_async(MessageSet msg_set, Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove, Cancellable? cancellable = null) throws Error { + /* if (mailbox == null) throw new EngineError.OPEN_REQUIRED("%s not opened", to_string()); @@ -181,23 +196,28 @@ private class Geary.Imap.Folder : BaseObject { out msg_flags_remove); yield mailbox.mark_email_async(msg_set, msg_flags_add, msg_flags_remove, cancellable); + */ } - + public async void copy_email_async(MessageSet msg_set, Geary.FolderPath destination, Cancellable? cancellable = null) throws Error { + /* if (mailbox == null) throw new EngineError.OPEN_REQUIRED("%s not opened", to_string()); - + yield mailbox.copy_email_async(msg_set, destination, cancellable); + */ } public async void move_email_async(MessageSet msg_set, Geary.FolderPath destination, Cancellable? cancellable = null) throws Error { + /* if (mailbox == null) throw new EngineError.OPEN_REQUIRED("%s not opened", to_string()); - + yield copy_email_async(msg_set, destination, cancellable); yield remove_email_async(msg_set, cancellable); + */ } public string to_string() { diff --git a/src/engine/imap/message/imap-fetched-data.vala b/src/engine/imap/message/imap-fetched-data.vala index 0d131436..15dae598 100644 --- a/src/engine/imap/message/imap-fetched-data.vala +++ b/src/engine/imap/message/imap-fetched-data.vala @@ -49,9 +49,9 @@ public class Geary.Imap.FetchedData : Object { // watch for empty return values if (has_value) - fetched_data.set_data(data_item, decoder.decode(list.get_required(ctr + 1))); + fetched_data.map.set(data_item, decoder.decode(list.get_required(ctr + 1))); else - fetched_data.set_data(data_item, decoder.decode(NilParameter.instance)); + fetched_data.map.set(data_item, decoder.decode(NilParameter.instance)); } } diff --git a/src/engine/imap/message/imap-mailbox-information.vala b/src/engine/imap/message/imap-mailbox-information.vala index 1776fa26..c357ec2f 100644 --- a/src/engine/imap/message/imap-mailbox-information.vala +++ b/src/engine/imap/message/imap-mailbox-information.vala @@ -54,17 +54,17 @@ public class Geary.Imap.MailboxInformation : Object { } public static MailboxInformation decode(ServerData server_data) throws ImapError { - StringParameter cmd = data.get_as_string(1); + StringParameter cmd = server_data.get_as_string(1); if (!cmd.equals_ci(ListCommand.NAME) && !cmd.equals_ci(ListCommand.XLIST_NAME)) - throw ImapError.PARSE_ERROR("Not LIST or XLIST data: %s", server_data.to_string()); + throw new ImapError.PARSE_ERROR("Not LIST or XLIST data: %s", server_data.to_string()); - ListParameter attrs = data.get_as_list(2); + ListParameter attrs = server_data.get_as_list(2); Gee.Collection attrlist = new Gee.ArrayList(); foreach (Parameter attr in attrs.get_all()) { StringParameter? stringp = attr as StringParameter; if (stringp == null) { debug("Bad list attribute \"%s\": Attribute not a string value", - data.to_string()); + server_data.to_string()); continue; } @@ -72,8 +72,8 @@ public class Geary.Imap.MailboxInformation : Object { attrlist.add(new MailboxAttribute(stringp.value)); } - StringParameter? delim = data.get_as_nullable_string(3); - StringParameter mailbox = data.get_as_string(4); + StringParameter? delim = server_data.get_as_nullable_string(3); + StringParameter mailbox = server_data.get_as_string(4); // Set \Inbox to standard path MailboxInformation info; diff --git a/src/engine/imap/message/imap-status-data.vala b/src/engine/imap/message/imap-status-data.vala index 9187a511..fcc1758b 100644 --- a/src/engine/imap/message/imap-status-data.vala +++ b/src/engine/imap/message/imap-status-data.vala @@ -38,7 +38,7 @@ public class Geary.Imap.StatusData : Object { public static StatusData decode(ServerData server_data) throws ImapError { if (!server_data.get_as_string(1).equals_ci(StatusCommand.NAME)) { throw new ImapError.PARSE_ERROR("Bad STATUS command name in response \"%s\"", - response.to_string()); + server_data.to_string()); } int messages = UNSET; @@ -83,7 +83,7 @@ public class Geary.Imap.StatusData : Object { } } catch (ImapError ierr) { message("Bad value at %d/%d in STATUS response \"%s\": %s", ctr, ctr + 1, - response.to_string(), ierr.message); + server_data.to_string(), ierr.message); } } diff --git a/src/engine/imap/response/imap-coded-status-response.vala b/src/engine/imap/response/imap-coded-status-response.vala index 9c4a682a..392b40d2 100644 --- a/src/engine/imap/response/imap-coded-status-response.vala +++ b/src/engine/imap/response/imap-coded-status-response.vala @@ -8,11 +8,12 @@ public class Geary.Imap.CodedStatusResponse : StatusResponse { public ResponseCodeType response_code_type { get; private set; } public ResponseCode response_code { get; private set; } - public CodedStatusResponse() { + public CodedStatusResponse(Tag tag) { + base (tag); } - public CodedStatusResponse.reconstitute(RootParameters root) throws ImapError { - base.reconstitute(root); + public CodedStatusResponse.migrate(RootParameters root) throws ImapError { + base.migrate(root); if (tag.is_tagged()) { throw new ImapError.PARSE_ERROR("Not a CodedStatusResponse: tagged response: %s", diff --git a/src/engine/imap/response/imap-completion-status-response.vala b/src/engine/imap/response/imap-completion-status-response.vala index c6968039..4114e12e 100644 --- a/src/engine/imap/response/imap-completion-status-response.vala +++ b/src/engine/imap/response/imap-completion-status-response.vala @@ -5,11 +5,12 @@ */ public class Geary.Imap.CompletionStatusResponse : StatusResponse { - private CompletionStatusResponse() { + private CompletionStatusResponse(Tag tag) { + base (tag); } - public CompletionStatusResponse.reconstitute(RootParameters root) throws ImapError { - base.reconstitute(root); + public CompletionStatusResponse.migrate(RootParameters root) throws ImapError { + base.migrate(root); // check this is actually a CompletionStatusResponse if (!tag.is_tagged()) { diff --git a/src/engine/imap/response/imap-server-data.vala b/src/engine/imap/response/imap-server-data.vala index de1ed868..92a2a499 100644 --- a/src/engine/imap/response/imap-server-data.vala +++ b/src/engine/imap/response/imap-server-data.vala @@ -7,6 +7,10 @@ public class Geary.Imap.ServerData : ServerResponse { public ServerDataType server_data_type { get; private set; } + protected ServerData(Tag tag) { + base (tag); + } + public ServerData.migrate(RootParameters root) throws ImapError { base.migrate(root); } diff --git a/src/engine/imap/response/imap-server-response.vala b/src/engine/imap/response/imap-server-response.vala index 4cd2e03b..18047118 100644 --- a/src/engine/imap/response/imap-server-response.vala +++ b/src/engine/imap/response/imap-server-response.vala @@ -18,7 +18,7 @@ public abstract class Geary.Imap.ServerResponse : RootParameters { } // The RootParameters are migrated and will be stripped upon exit. - public static ServerResponse migrate_from_server(RootParameters root, out Type response_type) + public static ServerResponse migrate_from_server(RootParameters root) throws ImapError { Tag tag = new Tag.from_parameter(root.get_as_string(0)); if (tag.is_tagged()) { @@ -28,30 +28,24 @@ public abstract class Geary.Imap.ServerResponse : RootParameters { if (statusparam != null) Status.decode(statusparam.value); - // tagged and has proper status, so it's a status response - response_type = Type.STATUS_RESPONSE; - return new StatusResponse.migrate(root); } else if (tag.is_continuation()) { - // nothing to decode; everything after the tag is human-readable stuff - response_type = Type.CONTINUATION_RESPONSE; - return new ContinuationResponse.migrate(root); } // All CompletionStatusResponses are StatusResponses, so check for it first if (CompletionStatusResponse.is_completion_status_response(root)) - return new CompletionStatusResponse.reconstitute(root); + return new CompletionStatusResponse.migrate(root); // All CodedStatusResponses are StatusResponses, so check for it first if (CodedStatusResponse.is_coded_status_response(root)) - return new CodedStatusResponse.reconstitute(root); + return new CodedStatusResponse.migrate(root); if (StatusResponse.is_status_response(root)) - return new StatusResponse.reconstitute(root); + return new StatusResponse.migrate(root); if (ServerData.is_server_data(root)) - return new ServerData.reconstitute(root); + return new ServerData.migrate(root); throw new ImapError.PARSE_ERROR("Unknown server response: %s", root.to_string()); } diff --git a/src/engine/imap/response/imap-status-response.vala b/src/engine/imap/response/imap-status-response.vala index c5b9f95e..eaad2dd3 100644 --- a/src/engine/imap/response/imap-status-response.vala +++ b/src/engine/imap/response/imap-status-response.vala @@ -7,7 +7,8 @@ public class Geary.Imap.StatusResponse : ServerResponse { public Status status { get; private set; } - public StatusResponse() { + public StatusResponse(Tag tag) { + base (tag); } public StatusResponse.migrate(RootParameters root) throws ImapError { diff --git a/src/engine/imap/transport/imap-client-connection.vala b/src/engine/imap/transport/imap-client-connection.vala index f399757f..b7b87b7f 100644 --- a/src/engine/imap/transport/imap-client-connection.vala +++ b/src/engine/imap/transport/imap-client-connection.vala @@ -440,8 +440,9 @@ public class Geary.Imap.ClientConnection : BaseObject { } private void on_parameters_ready(RootParameters root) { + ServerResponse response; try { - ServerResponse response = ServerResponse.from_server(root); + response = ServerResponse.migrate_from_server(root); } catch (ImapError err) { received_bad_response(root, err); @@ -469,7 +470,7 @@ public class Geary.Imap.ClientConnection : BaseObject { return; } - error("[%s] Unknown ServerResponse of type %s received: %s:", to_string(), response.type().name(), + error("[%s] Unknown ServerResponse of type %s received: %s:", to_string(), response.get_type().name(), response.to_string()); } diff --git a/src/engine/imap/transport/imap-client-session-manager.vala b/src/engine/imap/transport/imap-client-session-manager.vala index 289a2fb7..056bd5a2 100644 --- a/src/engine/imap/transport/imap-client-session-manager.vala +++ b/src/engine/imap/transport/imap-client-session-manager.vala @@ -53,31 +53,13 @@ public class Geary.Imap.ClientSessionManager : BaseObject { public ClientSessionManager(AccountInformation account_information) { this.account_information = account_information; - } - - ~ClientSessionManager() { - if (is_opened) - warning("Destroying opened ClientSessionManager"); - } - - public async void open_async() throws Error { - if (is_opened) - throw ImapError.INVALID("ClientSessionManager is already open"); - - is_opened = true; account_information.notify["imap-credentials"].connect(on_imap_credentials_notified); } - public async void close_async() throws Error { - if (!is_opened) - return; - - is_opened = false; - - account_information.notify["imap-credentials"].disconnect(on_imap_credentials_notified); - - // TODO: Tear down all connections + ~ClientSessionManager() { + if (is_open) + warning("Destroying opened ClientSessionManager"); } public async void open_async(Cancellable? cancellable) throws Error { @@ -304,7 +286,7 @@ public class Geary.Imap.ClientSessionManager : BaseObject { assert_not_reached(); } - int token = yield session_mutex.claim_async(cancellable); + int token = yield sessions_mutex.claim_async(cancellable); if (!sessions.contains(session)) debug("Attempting to release a session not owned by client session manager: %s", session.to_string()); @@ -312,7 +294,7 @@ public class Geary.Imap.ClientSessionManager : BaseObject { if (!reserved_sessions.remove(session)) debug("Attempting to release an unreserved session: %s", session.to_string()); - session_mutex.release(ref token); + sessions_mutex.release(ref token); } private void on_disconnected(ClientSession session, ClientSession.DisconnectReason reason) { diff --git a/src/engine/imap/transport/imap-client-session.vala b/src/engine/imap/transport/imap-client-session.vala index ed3af308..034f967e 100644 --- a/src/engine/imap/transport/imap-client-session.vala +++ b/src/engine/imap/transport/imap-client-session.vala @@ -127,7 +127,7 @@ public class Geary.Imap.ClientSession : BaseObject { * capabilities, only watch for them as they're reported. Thus, it's recommended that users * of ClientSession issue a CapabilityCommand (if needed) before login. */ - private Capabilities capabilities { get; private set; default = new Capabilities(0); } + public Capabilities capabilities { get; private set; default = new Capabilities(0); } private Endpoint imap_endpoint; private Geary.State.Machine fsm; @@ -270,7 +270,7 @@ public class Geary.Imap.ClientSession : BaseObject { new Geary.State.Mapping(State.SELECTING, Event.CLOSE_MAILBOX, on_close_mailbox), new Geary.State.Mapping(State.SELECTING, Event.LOGOUT, on_logout), new Geary.State.Mapping(State.SELECTING, Event.DISCONNECT, on_disconnect), - new Geary.State.Mapping(State.SELECTING, Event.RECV_STATUS, on_selecting_recv_status), + new Geary.State.Mapping(State.SELECTING, Event.RECV_STATUS, on_recv_status), new Geary.State.Mapping(State.SELECTING, Event.RECV_COMPLETION, on_selecting_recv_completion), new Geary.State.Mapping(State.SELECTING, Event.SEND_ERROR, on_send_error), new Geary.State.Mapping(State.SELECTING, Event.RECV_ERROR, on_recv_error), @@ -448,7 +448,7 @@ public class Geary.Imap.ClientSession : BaseObject { // only use IDLE when in SELECTED or EXAMINED state cx.set_idle_when_quiet(false); - result.proceed = true; + params.proceed = true; return State.CONNECTING; } @@ -487,7 +487,7 @@ public class Geary.Imap.ClientSession : BaseObject { } } - private void on_connected(uint state, uint event) { + private uint on_connected(uint state, uint event) { debug("[%s] Connected", to_string()); fsm.do_post_transition(() => { connected(); }); @@ -496,7 +496,7 @@ public class Geary.Imap.ClientSession : BaseObject { return state; } - private void on_connecting_recv_status(uint state, uint event, void *user, Object? object) { + private uint on_connecting_recv_status(uint state, uint event, void *user, Object? object) { StatusResponse status_response = (StatusResponse) object; if (status_response.status == Status.OK) @@ -504,7 +504,7 @@ public class Geary.Imap.ClientSession : BaseObject { debug("[%s] Connect denied: %s", to_string(), status_response.to_string()); - fsm.do_post_transition(() => { session_denied(status_response.text); }); + fsm.do_post_transition(() => { session_denied(status_response.get_text()); }); return State.LOGGED_OUT; } @@ -522,12 +522,13 @@ public class Geary.Imap.ClientSession : BaseObject { LoginCommand cmd = new LoginCommand(credentials.user, credentials.pass); MachineParams params = new MachineParams(cmd); - fsm.issue(Event.LOGIN, null, param); + fsm.issue(Event.LOGIN, null, params); if (params.err != null) throw params.err; - assert(result.proceed); + // should always proceed; only an Error could change this + assert(params.proceed); return yield command_transaction_async(cmd, cancellable); } @@ -562,12 +563,12 @@ public class Geary.Imap.ClientSession : BaseObject { yield cx.starttls_async(cancellable); debug("[%s] STARTTLS completed", to_string()); } else { - debug("[%s} STARTTLS refused: %s", to_string(), resp.status_response.to_string()); + debug("[%s} STARTTLS refused: %s", to_string(), resp.status.to_string()); // throw an exception and fail rather than send credentials under suspect // conditions throw new ImapError.NOT_SUPPORTED("STARTTLS refused by %s: %s", to_string(), - resp.status_response.to_string()); + resp.status.to_string()); } break; @@ -781,7 +782,7 @@ public class Geary.Imap.ClientSession : BaseObject { } public bool supports_idle() { - return get_capabilities().has_capability(Capabilities.IDLE); + return capabilities.has_capability(Capabilities.IDLE); } // @@ -862,7 +863,12 @@ public class Geary.Imap.ClientSession : BaseObject { Cancellable? cancellable) throws Error { string? old_mailbox = current_mailbox; - Command cmd = is_select ? new SelectCommand(mailbox) : new ExamineCommand(mailbox); + // Ternary troubles + Command cmd; + if (is_select) + cmd = new SelectCommand(new MailboxParameter(mailbox)); + else + cmd = new ExamineCommand(new MailboxParameter(mailbox)); MachineParams params = new MachineParams(cmd); fsm.issue(Event.SELECT, null, params); @@ -1055,7 +1061,7 @@ public class Geary.Imap.ClientSession : BaseObject { if (params.err != null) throw params.err; - if (result.proceed) + if (params.proceed) yield cx.disconnect_async(cancellable); } @@ -1228,14 +1234,14 @@ public class Geary.Imap.ClientSession : BaseObject { // server) in the context of send_async(), wait for it now if (!seen_completion_responses.has_key(cmd.tag)) { debug("[%s] Waiting for completion status response %s...", to_string(), cmd.to_string()); - waiting_for_completion.set(cmd.tag, new CommandCallback(issue_command_async.callback)); + waiting_for_completion.set(cmd.tag, new CommandCallback(command_transaction_async.callback)); yield; } // it should be seen now; if not, it's because of disconnection cancelling all the outstanding // requests CompletionStatusResponse? completion_response; - if (!seen_completion_response.remove(cmd.tag, out completion_response)) { + if (!seen_completion_responses.unset(cmd.tag, out completion_response)) { assert(cx == null); throw new ImapError.NOT_CONNECTED("Not connected to %s", imap_endpoint.to_string()); @@ -1295,7 +1301,7 @@ public class Geary.Imap.ClientSession : BaseObject { // update state machine before notifying subscribers, who may turn around and query ClientSession fsm.issue(Event.RECV_STATUS, null, coded_response, null); - coded_status_response_received(coded_response); + coded_response_received(coded_response); } private void on_received_completion_status_response(CompletionStatusResponse completion_status_response) { @@ -1309,14 +1315,14 @@ public class Geary.Imap.ClientSession : BaseObject { // this command to the server ... this mechanism (seen_completion_response and // waiting_for_completion) assures that in either case issue_command_async() returns // when the command is completed - seen_completion_response.set(completion_status_response.tag, completion_status_response); + seen_completion_responses.set(completion_status_response.tag, completion_status_response); CommandCallback? cmd_cb; - if (waiting_for_completion.remove(completion_status_response.tag, out cmd_cb)) - Idle.schedule(cmd_cb.callback); + if (waiting_for_completion.unset(completion_status_response.tag, out cmd_cb)) + Idle.add(cmd_cb.callback); } - private void notify(ServerData server_data) throws ImapError { + private void notify_received_data(ServerData server_data) throws ImapError { switch (server_data.server_data_type) { case ServerDataType.CAPABILITY: // update ClientSession capabilities before firing signal, so external signal @@ -1372,7 +1378,7 @@ public class Geary.Imap.ClientSession : BaseObject { // send ServerData to upper layers for processing and storage try { - notify(server_data); + notify_received_data(server_data); } catch (ImapError ierr) { debug("[%s] Failure notifying of server data: %s %s", to_string(), server_data.to_string(), ierr.message);