[geary/cherry-pick-03f05c64] Merge branch 'mjog/986-namespace-assert' into 'mainline'




commit b66ccafc050364e70b66f7ee0d36304db9b15d3e
Author: Michael Gratton <mike vee net>
Date:   Thu Oct 1 23:09:53 2020 +0000

    Merge branch 'mjog/986-namespace-assert' into 'mainline'
    
    Disconnect from IMAP client sessions when logging out
    
    Closes #986
    
    See merge request GNOME/geary!586
    
    (cherry picked from commit 03f05c64489c5cfb588565fae63f346ba69c25d2)
    
    2093aa32 Geary.Imap.Session: Avoid critical when client session logged out
    1c951f89 Geary.Imap.SessionObject: Ensure the session is connected when accessed
    b209f84e Geary.Imap.FolderSession: Ensure client session is selected when accessed
    ac425f57 Geary.Imap.SessionObject: Rename `claim_session` to `get_session`
    ed4ba331 Geary.State.Machine: Support GObject notify signal for state changes
    41be8693 Geary.Imap.ClientSession: Treat logout as disconnect

 src/engine/imap/api/imap-account-session.vala      |  10 +-
 src/engine/imap/api/imap-client-service.vala       |  38 +++---
 src/engine/imap/api/imap-folder-session.vala       |  39 +++++--
 src/engine/imap/api/imap-session-object.vala       |  42 ++++---
 src/engine/imap/transport/imap-client-session.vala | 130 ++++++++++++---------
 src/engine/imap/transport/imap-deserializer.vala   |   4 +-
 src/engine/state/state-machine.vala                |  37 ++----
 .../imap/transport/imap-client-session-test.vala   |  40 +++----
 test/integration/imap/client-session.vala          |   2 +-
 9 files changed, 194 insertions(+), 148 deletions(-)
---
diff --git a/src/engine/imap/api/imap-account-session.vala b/src/engine/imap/api/imap-account-session.vala
index 238bd85cc..813d9e5e2 100644
--- a/src/engine/imap/api/imap-account-session.vala
+++ b/src/engine/imap/api/imap-account-session.vala
@@ -45,7 +45,7 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject {
      */
     public async FolderPath get_default_personal_namespace(Cancellable? cancellable)
     throws Error {
-        ClientSession session = claim_session();
+        ClientSession session = get_session();
         Gee.List<Namespace> personal = session.get_personal_namespaces();
         if (personal.is_empty) {
             throw new ImapError.INVALID("No personal namespace found");
@@ -69,7 +69,7 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject {
     public bool is_folder_path_valid(FolderPath? path) throws GLib.Error {
         bool is_valid = false;
         if (path != null) {
-            ClientSession session = claim_session();
+            ClientSession session = get_session();
             try {
                 session.get_mailbox_for_path(path);
                 is_valid = true;
@@ -94,7 +94,7 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject {
                                           Geary.Folder.SpecialUse? use,
                                           Cancellable? cancellable)
     throws Error {
-        ClientSession session = claim_session();
+        ClientSession session = get_session();
         MailboxSpecifier mailbox = session.get_mailbox_for_path(path);
         bool can_create_special = session.capabilities.has_capability(Capabilities.CREATE_SPECIAL_USE);
         CreateCommand cmd = (
@@ -125,7 +125,7 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject {
     public async Imap.Folder fetch_folder_async(FolderPath path,
                                                 Cancellable? cancellable)
         throws Error {
-        ClientSession session = claim_session();
+        ClientSession session = get_session();
         Imap.Folder? folder = this.folders.get(path);
         if (folder == null) {
             Gee.List<MailboxInformation>? mailboxes = yield send_list_async(
@@ -169,7 +169,7 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject {
         fetch_child_folders_async(FolderPath parent,
                                   GLib.Cancellable? cancellable)
         throws GLib.Error {
-        ClientSession session = claim_session();
+        ClientSession session = get_session();
         Gee.List<Imap.Folder> children = new Gee.ArrayList<Imap.Folder>();
         Gee.List<MailboxInformation> mailboxes = yield send_list_async(
             session, parent, true, cancellable
diff --git a/src/engine/imap/api/imap-client-service.vala b/src/engine/imap/api/imap-client-service.vala
index e0aad41b3..da3598f38 100644
--- a/src/engine/imap/api/imap-client-service.vala
+++ b/src/engine/imap/api/imap-client-service.vala
@@ -252,7 +252,7 @@ public class Geary.Imap.ClientService : Geary.ClientService {
         if (!disconnect) {
             // If the session has a mailbox selected, close it before
             // adding it back to the pool
-            ClientSession.ProtocolState proto = session.get_protocol_state();
+            ClientSession.ProtocolState proto = session.protocol_state;
             if (proto == ClientSession.ProtocolState.SELECTED ||
                 proto == ClientSession.ProtocolState.SELECTING) {
                 // always close mailbox to return to authorized state
@@ -263,7 +263,7 @@ public class Geary.Imap.ClientService : Geary.ClientService {
                           session.to_string(), imap_error.message);
                     disconnect = true;
                 }
-                if (session.get_protocol_state() != AUTHORIZED) {
+                if (session.protocol_state != AUTHORIZED) {
                     // Closing it didn't leave it in the desired
                     // state, so drop it
                     disconnect = true;
@@ -393,7 +393,7 @@ public class Geary.Imap.ClientService : Geary.ClientService {
     /** Determines if a session is valid, disposing of it if not. */
     private async bool check_session(ClientSession target, bool claiming) {
         bool valid = false;
-        switch (target.get_protocol_state()) {
+        switch (target.protocol_state) {
         case ClientSession.ProtocolState.AUTHORIZED:
         case ClientSession.ProtocolState.CLOSING_MAILBOX:
             valid = true;
@@ -472,7 +472,7 @@ public class Geary.Imap.ClientService : Geary.ClientService {
 
         // Only bother tracking disconnects and enabling keeping alive
         // now the session is properly established.
-        new_session.disconnected.connect(on_disconnected);
+        new_session.notify["disconnected"].connect(on_session_disconnected);
         new_session.enable_keepalives(selected_keepalive_sec,
                                       unselected_keepalive_sec,
                                       selected_with_idle_keepalive_sec);
@@ -509,7 +509,7 @@ public class Geary.Imap.ClientService : Geary.ClientService {
     }
 
     private async void disconnect_session(ClientSession session) {
-        if (session.get_protocol_state() != NOT_CONNECTED) {
+        if (session.protocol_state != NOT_CONNECTED) {
             debug("Logging out session: %s", session.to_string());
             // No need to remove it after logging out, the
             // disconnected handler will do that for us.
@@ -548,21 +548,27 @@ public class Geary.Imap.ClientService : Geary.ClientService {
         }
 
         if (removed) {
-            session.disconnected.disconnect(on_disconnected);
+            session.notify["disconnected"].connect(on_session_disconnected);
         }
         return removed;
     }
 
-    private void on_disconnected(ClientSession session,
-                                 ClientSession.DisconnectReason reason) {
-        debug(
-            "Session disconnected: %s: %s",
-            session.to_string(), reason.to_string()
-        );
-        this.remove_session_async.begin(
-            session,
-            (obj, res) => { this.remove_session_async.end(res); }
-        );
+    private void on_session_disconnected(GLib.Object source,
+                                         GLib.ParamSpec param) {
+        var session = source as ClientSession;
+        if (session != null &&
+            session.protocol_state == NOT_CONNECTED &&
+            session.disconnected != null) {
+            debug(
+                "Session disconnected: %s: %s",
+                session.to_string(),
+                session.disconnected.to_string()
+            );
+            this.remove_session_async.begin(
+                session,
+                (obj, res) => { this.remove_session_async.end(res); }
+            );
+        }
     }
 
 }
diff --git a/src/engine/imap/api/imap-folder-session.vala b/src/engine/imap/api/imap-folder-session.vala
index 092c06cde..8a2290cb8 100644
--- a/src/engine/imap/api/imap-folder-session.vala
+++ b/src/engine/imap/api/imap-folder-session.vala
@@ -38,6 +38,8 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
     /** Determines if this folder accepts custom IMAP flags. */
     public Trillian accepts_user_flags { get; private set; default = Trillian.UNKNOWN; }
 
+    private MailboxSpecifier mailbox;
+
     private Quirks quirks;
 
     private Nonblocking.Mutex cmd_mutex = new Nonblocking.Mutex();
@@ -107,9 +109,9 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
         session.search.connect(on_search);
         session.status_response_received.connect(on_status_response);
 
-        MailboxSpecifier mailbox = session.get_mailbox_for_path(folder.path);
+        this.mailbox = session.get_mailbox_for_path(folder.path);
         StatusResponse? response = yield session.select_async(
-            mailbox, cancellable
+            this.mailbox, cancellable
         );
         throw_on_not_ok(response, "SELECT " + this.folder.path.to_string());
 
@@ -125,7 +127,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
      */
     public async void enable_idle(Cancellable? cancellable)
         throws Error {
-        ClientSession session = claim_session();
+        ClientSession session = get_session();
         int token = yield this.cmd_mutex.claim_async(cancellable);
         Error? cmd_err = null;
         try {
@@ -301,7 +303,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
                             Gee.Set<Imap.UID>? search_results,
                             GLib.Cancellable? cancellable)
         throws GLib.Error {
-        ClientSession session = claim_session();
+        ClientSession session = get_session();
         Gee.Map<Command, StatusResponse>? responses = null;
         int token = yield this.cmd_mutex.claim_async(cancellable);
 
@@ -646,7 +648,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
     public async void remove_email_async(Gee.List<MessageSet> msg_sets,
                                          GLib.Cancellable? cancellable)
         throws GLib.Error {
-        ClientSession session = claim_session();
+        ClientSession session = get_session();
         Gee.List<MessageFlag> flags = new Gee.ArrayList<MessageFlag>();
         flags.add(MessageFlag.DELETED);
 
@@ -719,7 +721,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
                                                      FolderPath destination,
                                                      GLib.Cancellable? cancellable)
         throws GLib.Error {
-        ClientSession session = claim_session();
+        ClientSession session = get_session();
 
         MailboxSpecifier mailbox = session.get_mailbox_for_path(destination);
         CopyCommand cmd = new CopyCommand(msg_set, mailbox, cancellable);
@@ -1107,8 +1109,6 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
                                                            Geary.EmailFlags? flags,
                                                            GLib.DateTime? date_received)
         throws GLib.Error {
-        ClientSession session = claim_session();
-
         MessageFlags? msg_flags = null;
         if (flags != null) {
             Imap.EmailFlags imap_flags = Imap.EmailFlags.from_api_email_flags(flags);
@@ -1121,9 +1121,8 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
         if (date_received != null)
             internaldate = new InternalDate.from_date_time(date_received);
 
-        MailboxSpecifier mailbox = session.get_mailbox_for_path(this.folder.path);
         AppendCommand cmd = new AppendCommand(
-            mailbox,
+            this.mailbox,
             msg_flags,
             internaldate,
             message.get_rfc822_buffer(),
@@ -1161,6 +1160,26 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
         );
     }
 
+    /**
+     * Returns a valid IMAP client session for use by this object.
+     *
+     * In addition to the checks made by {@link
+     * SessionObject.get_session}, this method also ensures that the
+     * IMAP session is in the SELECTED state for the correct mailbox.
+     */
+    protected override ClientSession get_session()
+        throws ImapError {
+        var session = base.get_session();
+        if (session.protocol_state != SELECTED &&
+            !this.mailbox.equal_to(session.selected_mailbox)) {
+            throw new ImapError.NOT_CONNECTED(
+                "IMAP object no longer SELECTED for %s",
+                this.mailbox.to_string()
+            );
+        }
+        return session;
+    }
+
     // HACK: See https://bugzilla.gnome.org/show_bug.cgi?id=714902
     //
     // Detect when a server has returned a BAD response to FETCH
diff --git a/src/engine/imap/api/imap-session-object.vala b/src/engine/imap/api/imap-session-object.vala
index 4937d4626..80695fcaf 100644
--- a/src/engine/imap/api/imap-session-object.vala
+++ b/src/engine/imap/api/imap-session-object.vala
@@ -39,7 +39,7 @@ public abstract class Geary.Imap.SessionObject : BaseObject, Logging.Source {
      */
     protected SessionObject(ClientSession session) {
         this.session = session;
-        this.session.disconnected.connect(on_disconnected);
+        this.session.notify["protocol-state"].connect(on_session_state_change);
     }
 
     ~SessionObject() {
@@ -63,7 +63,9 @@ public abstract class Geary.Imap.SessionObject : BaseObject, Logging.Source {
         this.session = null;
 
         if (old_session != null) {
-            old_session.disconnected.disconnect(on_disconnected);
+            old_session.notify["protocol-state"].disconnect(
+                on_session_state_change
+            );
         }
 
         return old_session;
@@ -83,25 +85,37 @@ public abstract class Geary.Imap.SessionObject : BaseObject, Logging.Source {
     }
 
     /**
-     * Obtains IMAP session the server for use by this object.
+     * Returns a valid IMAP client session for use by this object.
      *
-     * @throws ImapError.NOT_CONNECTED if the session with the server
-     * server has been dropped via {@link close}, or because
-     * the connection was lost.
+     * @throws ImapError.NOT_CONNECTED if the client session has been
+     * dropped via {@link close}, if the client session is logging out
+     * or has been closed, or because the connection to the server was
+     * lost.
      */
-    protected ClientSession claim_session()
+    protected virtual ClientSession get_session()
         throws ImapError {
-        if (this.session == null) {
-            throw new ImapError.NOT_CONNECTED("IMAP object has no session");
+        if (this.session == null ||
+            this.session.protocol_state == NOT_CONNECTED) {
+            throw new ImapError.NOT_CONNECTED(
+                "IMAP object has no session or is not connected"
+            );
         }
         return this.session;
     }
 
-    private void on_disconnected(ClientSession.DisconnectReason reason) {
-        debug("Disconnected %s", reason.to_string());
-
-        close();
-        disconnected(reason);
+    private void on_session_state_change() {
+        if (this.session != null &&
+            this.session.protocol_state == NOT_CONNECTED) {
+            // Disconnect reason will null when the session is being
+            // logged out but the logout command has not yet been
+            // completed.
+            var reason = (
+                this.session.disconnected ??
+                ClientSession.DisconnectReason.LOCAL_CLOSE
+            );
+            close();
+            disconnected(reason);
+        }
     }
 
 }
diff --git a/src/engine/imap/transport/imap-client-session.vala 
b/src/engine/imap/transport/imap-client-session.vala
index f42112f26..ba125616e 100644
--- a/src/engine/imap/transport/imap-client-session.vala
+++ b/src/engine/imap/transport/imap-client-session.vala
@@ -90,7 +90,7 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
      *
      * See [[http://tools.ietf.org/html/rfc3501#section-3]]
      *
-     * @see get_protocol_state
+     * @see protocol_state
      */
     public enum ProtocolState {
         NOT_CONNECTED,
@@ -230,6 +230,55 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
         "Geary.Imap.ClientSession", State.NOT_CONNECTED, State.COUNT, Event.COUNT,
         state_to_string, event_to_string);
 
+
+    /**
+     * Returns the current IMAP protocol state for the session.
+     */
+    public ProtocolState protocol_state {
+        get {
+            var state = ProtocolState.NOT_CONNECTED;
+            switch (fsm.state) {
+            case State.NOT_CONNECTED:
+            case State.LOGOUT:
+            case State.CLOSED:
+                state = NOT_CONNECTED;
+                break;
+
+            case State.NOAUTH:
+                state = UNAUTHORIZED;
+                break;
+
+            case State.AUTHORIZED:
+                state = AUTHORIZED;
+                break;
+
+            case State.SELECTED:
+                state = SELECTED;
+                break;
+
+            case State.CONNECTING:
+                state = CONNECTING;
+                break;
+
+            case State.AUTHORIZING:
+                state = AUTHORIZING;
+                break;
+
+            case State.SELECTING:
+                state = SELECTING;
+                break;
+
+            case State.CLOSING_MAILBOX:
+                state = CLOSING_MAILBOX;
+                break;
+            }
+            return state;
+        }
+    }
+
+    /** Specifies the reason the session was disconnected, if any. */
+    public DisconnectReason? disconnected { get; private set; default = null; }
+
     /**
      * Set of IMAP extensions reported as being supported by the server.
      *
@@ -330,9 +379,6 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
     // Connection state changes
     //
 
-    /** Emitted when the session is disconnected for any reason. */
-    public signal void disconnected(DisconnectReason reason);
-
     /** Emitted when an IMAP command status response is received. */
     public signal void status_response_received(StatusResponse status_response);
 
@@ -482,19 +528,25 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
             new Geary.State.Mapping(State.CLOSED, Event.RECV_ERROR, Geary.State.nop),
         };
 
-        fsm = new Geary.State.Machine(machine_desc, mappings, on_ignored_transition);
-        fsm.set_logging(false);
+        this.fsm = new Geary.State.Machine(
+            machine_desc,
+            mappings,
+            on_ignored_transition
+        );
+        this.fsm.notify["state"].connect(
+            () => this.notify_property("protocol_state")
+        );
     }
 
     ~ClientSession() {
-        switch (fsm.get_state()) {
+        switch (fsm.state) {
             case State.NOT_CONNECTED:
             case State.CLOSED:
                 // no problem-o
             break;
 
             default:
-                warning("ClientSession ref dropped while still active");
+                GLib.warning("ClientSession ref dropped while still active");
         }
     }
 
@@ -599,6 +651,9 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
             }
             if (ns == null) {
                 // fall back to the default personal namespace
+                if (this.personal_namespaces.is_empty) {
+                    throw new ImapError.UNAVAILABLE("No personal namespace");
+                }
                 ns = this.personal_namespaces[0];
             }
 
@@ -634,43 +689,6 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
         return delim;
     }
 
-    /**
-     * Returns the current {@link ProtocolState} of the {@link ClientSession} and, if selected,
-     * the current mailbox.
-     */
-    public ProtocolState get_protocol_state() {
-        switch (fsm.get_state()) {
-            case State.NOT_CONNECTED:
-            case State.LOGOUT:
-            case State.CLOSED:
-                return ProtocolState.NOT_CONNECTED;
-
-            case State.NOAUTH:
-                return ProtocolState.UNAUTHORIZED;
-
-            case State.AUTHORIZED:
-                return ProtocolState.AUTHORIZED;
-
-            case State.SELECTED:
-                return ProtocolState.SELECTED;
-
-            case State.CONNECTING:
-                return ProtocolState.CONNECTING;
-
-            case State.AUTHORIZING:
-                return ProtocolState.AUTHORIZING;
-
-            case State.SELECTING:
-                return ProtocolState.SELECTING;
-
-            case State.CLOSING_MAILBOX:
-                return ProtocolState.CLOSING_MAILBOX;
-
-            default:
-                assert_not_reached();
-        }
-    }
-
     // Some commands require waiting for a completion response in order to shift the state machine's
     // State; this allocates such a wait, returning false if another command is outstanding also
     // waiting for one to finish
@@ -779,7 +797,7 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
 
     private bool on_greeting_timeout() {
         // if still in CONNECTING state, the greeting never arrived
-        if (fsm.get_state() == State.CONNECTING)
+        if (fsm.state == State.CONNECTING)
             fsm.issue(Event.TIMEOUT);
 
         return false;
@@ -1195,7 +1213,7 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
     public void enable_idle()
         throws GLib.Error {
         if (this.is_idle_supported) {
-            switch (get_protocol_state()) {
+            switch (this.protocol_state) {
             case ProtocolState.AUTHORIZING:
             case ProtocolState.AUTHORIZED:
             case ProtocolState.SELECTED:
@@ -1216,7 +1234,7 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
         unschedule_keepalive();
 
         uint seconds;
-        switch (get_protocol_state()) {
+        switch (this.protocol_state) {
             case ProtocolState.NOT_CONNECTED:
             case ProtocolState.CONNECTING:
                 return;
@@ -1553,10 +1571,11 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
         MachineParams params = (MachineParams) object;
 
         assert(params.cmd is LogoutCommand);
-        if (!reserve_state_change_cmd(params, state, event))
-            return state;
+        if (reserve_state_change_cmd(params, state, event)) {
+            state = State.LOGOUT;
+        }
 
-        return State.LOGOUT;
+        return state;
     }
 
     private uint on_logging_out_recv_status(uint state,
@@ -1623,7 +1642,7 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
         }
 
         drop_connection();
-        disconnected(DisconnectReason.LOCAL_CLOSE);
+        this.disconnected = DisconnectReason.LOCAL_CLOSE;
 
         if (disconnect_err != null)
             throw disconnect_err;
@@ -1642,12 +1661,12 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
         return (this.selected_mailbox == null)
             ? new Logging.State(
                 this,
-                this.fsm.get_state_string(fsm.get_state())
+                this.fsm.get_state_string(fsm.state)
             )
             : new Logging.State(
                 this,
                 "%s:%s selected %s",
-                this.fsm.get_state_string(fsm.get_state()),
+                this.fsm.get_state_string(fsm.state),
                 this.selected_mailbox.to_string(),
                 this.selected_readonly ? "RO" : "RW"
             );
@@ -1659,6 +1678,8 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
     }
 
     private async void do_disconnect(DisconnectReason reason) {
+        this.disconnected = reason;
+
         try {
             yield this.cx.disconnect_async();
         } catch (GLib.Error err) {
@@ -1666,7 +1687,6 @@ public class Geary.Imap.ClientSession : BaseObject, Logging.Source {
         }
 
         drop_connection();
-        disconnected(reason);
     }
 
     //
diff --git a/src/engine/imap/transport/imap-deserializer.vala 
b/src/engine/imap/transport/imap-deserializer.vala
index 249f7c856..559a5e784 100644
--- a/src/engine/imap/transport/imap-deserializer.vala
+++ b/src/engine/imap/transport/imap-deserializer.vala
@@ -294,7 +294,7 @@ public class Geary.Imap.Deserializer : BaseObject, Logging.Source {
 
     /** {@inheritDoc} */
     public Logging.State to_logging_state() {
-        return new Logging.State(this, fsm.get_state_string(fsm.get_state()));
+        return new Logging.State(this, fsm.get_state_string(fsm.state));
     }
 
     /** Sets the connection's logging parent. */
@@ -429,7 +429,7 @@ public class Geary.Imap.Deserializer : BaseObject, Logging.Source {
     }
 
     private Mode get_mode() {
-        switch (fsm.get_state()) {
+        switch (fsm.state) {
             case State.LITERAL_DATA:
                 return Mode.BLOCK;
 
diff --git a/src/engine/state/state-machine.vala b/src/engine/state/state-machine.vala
index 351babb85..ef32555d2 100644
--- a/src/engine/state/state-machine.vala
+++ b/src/engine/state/state-machine.vala
@@ -5,13 +5,20 @@
  */
 
 public class Geary.State.Machine : BaseObject {
+
+    /** The state machine's current state. */
+    public uint state { get; private set; }
+
+    /** Determines if the state machine crashes your app when mis-configured. */
+    public bool abort_on_no_transition { get; set; default = true; }
+
+    /** Determines if transition logging is enabled. */
+    public bool logging { get; private set; default = false; }
+
     private Geary.State.MachineDescriptor descriptor;
-    private uint state;
     private Mapping[,] transitions;
     private unowned Transition? default_transition;
     private bool locked = false;
-    private bool abort_on_no_transition = true;
-    private bool logging = false;
     private unowned PostTransition? post_transition = null;
     private void *post_user = null;
     private Object? post_object = null;
@@ -39,26 +46,6 @@ public class Geary.State.Machine : BaseObject {
         }
     }
 
-    public uint get_state() {
-        return state;
-    }
-
-    public bool get_abort_on_no_transition() {
-        return abort_on_no_transition;
-    }
-
-    public void set_abort_on_no_transition(bool abort) {
-        abort_on_no_transition = abort;
-    }
-
-    public void set_logging(bool logging) {
-        this.logging = logging;
-    }
-
-    public bool is_logging() {
-        return logging;
-    }
-
     public uint issue(uint event, void *user = null, Object? object = null, Error? err = null) {
         assert(event < descriptor.event_count);
         assert(state < descriptor.state_count);
@@ -70,7 +57,7 @@ public class Geary.State.Machine : BaseObject {
             string msg = "%s: No transition defined for %s@%s".printf(to_string(),
                 descriptor.get_event_string(event), descriptor.get_state_string(state));
 
-            if (get_abort_on_no_transition())
+            if (this.abort_on_no_transition)
                 error(msg);
             else
                 critical(msg);
@@ -96,7 +83,7 @@ public class Geary.State.Machine : BaseObject {
         }
         locked = false;
 
-        if (is_logging())
+        if (this.logging)
             message("%s: %s", to_string(), get_transition_string(old_state, event, state));
 
         // Perform post-transition if registered
diff --git a/test/engine/imap/transport/imap-client-session-test.vala 
b/test/engine/imap/transport/imap-client-session-test.vala
index 2aed92118..c9a4165c4 100644
--- a/test/engine/imap/transport/imap-client-session-test.vala
+++ b/test/engine/imap/transport/imap-client-session-test.vala
@@ -44,17 +44,17 @@ class Geary.Imap.ClientSessionTest : TestCase {
         this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
 
         var test_article = new ClientSession(new_endpoint(), new Quirks());
-        assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+        assert_true(test_article.protocol_state == NOT_CONNECTED);
 
         test_article.connect_async.begin(
             CONNECT_TIMEOUT, null, this.async_completion
         );
         test_article.connect_async.end(async_result());
-        assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+        assert_true(test_article.protocol_state == UNAUTHORIZED);
 
         test_article.disconnect_async.begin(null, this.async_completion);
         test_article.disconnect_async.end(async_result());
-        assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+        assert_true(test_article.protocol_state == NOT_CONNECTED);
 
         TestServer.Result result = this.server.wait_for_script(this.main_loop);
         assert_true(
@@ -148,13 +148,13 @@ class Geary.Imap.ClientSessionTest : TestCase {
         this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
 
         var test_article = new ClientSession(new_endpoint(), new Quirks());
-        assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+        assert_true(test_article.protocol_state == NOT_CONNECTED);
 
         test_article.connect_async.begin(
             CONNECT_TIMEOUT, null, this.async_completion
         );
         test_article.connect_async.end(async_result());
-        assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+        assert_true(test_article.protocol_state == UNAUTHORIZED);
 
         test_article.login_async.begin(
             new Credentials(PASSWORD, "test", "password"),
@@ -162,11 +162,11 @@ class Geary.Imap.ClientSessionTest : TestCase {
             this.async_completion
         );
         test_article.login_async.end(async_result());
-        assert_true(test_article.get_protocol_state() == AUTHORIZED);
+        assert_true(test_article.protocol_state == AUTHORIZED);
 
         test_article.disconnect_async.begin(null, this.async_completion);
         test_article.disconnect_async.end(async_result());
-        assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+        assert_true(test_article.protocol_state == NOT_CONNECTED);
 
         TestServer.Result result = this.server.wait_for_script(this.main_loop);
         assert_true(
@@ -185,17 +185,17 @@ class Geary.Imap.ClientSessionTest : TestCase {
         this.server.add_script_line(DISCONNECT, "");
 
         var test_article = new ClientSession(new_endpoint(), new Quirks());
-        assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+        assert_true(test_article.protocol_state == NOT_CONNECTED);
 
         test_article.connect_async.begin(
             CONNECT_TIMEOUT, null, this.async_completion
         );
         test_article.connect_async.end(async_result());
-        assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+        assert_true(test_article.protocol_state == UNAUTHORIZED);
 
         test_article.logout_async.begin(null, this.async_completion);
         test_article.logout_async.end(async_result());
-        assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+        assert_true(test_article.protocol_state == NOT_CONNECTED);
 
         TestServer.Result result = this.server.wait_for_script(this.main_loop);
         assert_true(
@@ -216,13 +216,13 @@ class Geary.Imap.ClientSessionTest : TestCase {
         this.server.add_script_line(DISCONNECT, "");
 
         var test_article = new ClientSession(new_endpoint(), new Quirks());
-        assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+        assert_true(test_article.protocol_state == NOT_CONNECTED);
 
         test_article.connect_async.begin(
             CONNECT_TIMEOUT, null, this.async_completion
         );
         test_article.connect_async.end(async_result());
-        assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+        assert_true(test_article.protocol_state == UNAUTHORIZED);
 
         test_article.login_async.begin(
             new Credentials(PASSWORD, "test", "password"),
@@ -230,11 +230,11 @@ class Geary.Imap.ClientSessionTest : TestCase {
             this.async_completion
         );
         test_article.login_async.end(async_result());
-        assert_true(test_article.get_protocol_state() == AUTHORIZED);
+        assert_true(test_article.protocol_state == AUTHORIZED);
 
         test_article.logout_async.begin(null, this.async_completion);
         test_article.logout_async.end(async_result());
-        assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+        assert_true(test_article.protocol_state == NOT_CONNECTED);
 
         TestServer.Result result = this.server.wait_for_script(this.main_loop);
         assert_true(
@@ -261,13 +261,13 @@ class Geary.Imap.ClientSessionTest : TestCase {
         this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
 
         var test_article = new ClientSession(new_endpoint(), new Quirks());
-        assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+        assert_true(test_article.protocol_state == NOT_CONNECTED);
 
         test_article.connect_async.begin(
             CONNECT_TIMEOUT, null, this.async_completion
         );
         test_article.connect_async.end(async_result());
-        assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+        assert_true(test_article.protocol_state == UNAUTHORIZED);
 
         test_article.initiate_session_async.begin(
             new Credentials(PASSWORD, "test", "password"),
@@ -305,13 +305,13 @@ class Geary.Imap.ClientSessionTest : TestCase {
         this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
 
         var test_article = new ClientSession(new_endpoint(), new Quirks());
-        assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+        assert_true(test_article.protocol_state == NOT_CONNECTED);
 
         test_article.connect_async.begin(
             CONNECT_TIMEOUT, null, this.async_completion
         );
         test_article.connect_async.end(async_result());
-        assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+        assert_true(test_article.protocol_state == UNAUTHORIZED);
 
         test_article.initiate_session_async.begin(
             new Credentials(PASSWORD, "test", "password"),
@@ -368,13 +368,13 @@ class Geary.Imap.ClientSessionTest : TestCase {
         this.server.add_script_line(WAIT_FOR_DISCONNECT, "");
 
         var test_article = new ClientSession(new_endpoint(), new Quirks());
-        assert_true(test_article.get_protocol_state() == NOT_CONNECTED);
+        assert_true(test_article.protocol_state == NOT_CONNECTED);
 
         test_article.connect_async.begin(
             CONNECT_TIMEOUT, null, this.async_completion
         );
         test_article.connect_async.end(async_result());
-        assert_true(test_article.get_protocol_state() == UNAUTHORIZED);
+        assert_true(test_article.protocol_state == UNAUTHORIZED);
 
         test_article.initiate_session_async.begin(
             new Credentials(PASSWORD, "test", "password"),
diff --git a/test/integration/imap/client-session.vala b/test/integration/imap/client-session.vala
index ca813b79b..257d73199 100644
--- a/test/integration/imap/client-session.vala
+++ b/test/integration/imap/client-session.vala
@@ -34,7 +34,7 @@ class Integration.Imap.ClientSession : TestCase {
     }
 
     public override void tear_down() throws GLib.Error {
-        if (this.session.get_protocol_state() != NOT_CONNECTED) {
+        if (this.session.protocol_state != NOT_CONNECTED) {
             this.session.disconnect_async.begin(null, this.async_completion);
             this.session.disconnect_async.end(async_result());
         }


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