diff options
Diffstat (limited to 'libdino/src/service')
-rw-r--r-- | libdino/src/service/connection_manager.vala | 129 | ||||
-rw-r--r-- | libdino/src/service/module_manager.vala | 17 |
2 files changed, 92 insertions, 54 deletions
diff --git a/libdino/src/service/connection_manager.vala b/libdino/src/service/connection_manager.vala index 9d069efa..03c97bdb 100644 --- a/libdino/src/service/connection_manager.vala +++ b/libdino/src/service/connection_manager.vala @@ -9,6 +9,7 @@ public class ConnectionManager { public signal void stream_opened(Account account, Core.XmppStream stream); public signal void connection_state_changed(Account account, ConnectionState state); + public signal void connection_error(Account account, ConnectionError error); public enum ConnectionState { CONNECTED, @@ -17,12 +18,32 @@ public class ConnectionManager { } private ArrayList<Account> connection_todo = new ArrayList<Account>(Account.equals_func); - private HashMap<Account, Connection> stream_states = new HashMap<Account, Connection>(Account.hash_func, Account.equals_func); + private HashMap<Account, Connection> connections = new HashMap<Account, Connection>(Account.hash_func, Account.equals_func); + private HashMap<Account, ConnectionError> connection_errors = new HashMap<Account, ConnectionError>(Account.hash_func, Account.equals_func); + private NetworkManager? network_manager; private Login1Manager? login1; private ModuleManager module_manager; public string? log_options; + public class ConnectionError { + + public enum Source { + CONNECTION, + SASL, + STREAM_ERROR + } + + public Source source; + public string? identifier; + public StreamError.Flag? flag; + + public ConnectionError(Source source, string? identifier) { + this.source = source; + this.identifier = identifier; + } + } + private class Connection { public Core.XmppStream stream { get; set; } public ConnectionState connection_state { get; set; default = ConnectionState.DISCONNECTED; } @@ -46,26 +67,33 @@ public class ConnectionManager { } public Core.XmppStream? get_stream(Account account) { - if (get_connection_state(account) == ConnectionState.CONNECTED) { - return stream_states[account].stream; + if (get_state(account) == ConnectionState.CONNECTED) { + return connections[account].stream; } return null; } - public ConnectionState get_connection_state(Account account) { - if (stream_states.has_key(account)){ - return stream_states[account].connection_state; + public ConnectionState get_state(Account account) { + if (connections.has_key(account)){ + return connections[account].connection_state; } return ConnectionState.DISCONNECTED; } + public ConnectionError? get_error(Account account) { + if (connection_errors.has_key(account)) { + return connection_errors[account]; + } + return null; + } + public ArrayList<Account> get_managed_accounts() { return connection_todo; } public Core.XmppStream? connect(Account account) { if (!connection_todo.contains(account)) connection_todo.add(account); - if (!stream_states.has_key(account)) { + if (!connections.has_key(account)) { return connect_(account); } else { check_reconnect(account); @@ -76,18 +104,18 @@ public class ConnectionManager { public void disconnect(Account account) { change_connection_state(account, ConnectionState.DISCONNECTED); connection_todo.remove(account); - if (stream_states.has_key(account)) { + if (connections.has_key(account)) { try { - stream_states[account].stream.disconnect(); + connections[account].stream.disconnect(); + connections.unset(account); } catch (Error e) { } } } private Core.XmppStream? connect_(Account account, string? resource = null) { + if (connections.has_key(account)) connections[account].stream.remove_modules(); + connection_errors.unset(account); if (resource == null) resource = account.resourcepart; - if (stream_states.has_key(account)) { - stream_states[account].stream.remove_modules(); - } Core.XmppStream stream = new Core.XmppStream(); foreach (Core.XmppStreamModule module in module_manager.get_modules(account, resource)) { @@ -96,11 +124,15 @@ public class ConnectionManager { stream.log = new Core.XmppLog(account.bare_jid.to_string(), log_options); Connection connection = new Connection(stream, new DateTime.now_local()); - stream_states[account] = connection; + connections[account] = connection; change_connection_state(account, ConnectionState.CONNECTING); stream.stream_negotiated.connect((stream) => { change_connection_state(account, ConnectionState.CONNECTED); }); + stream.get_module(PlainSasl.Module.IDENTITY).received_auth_failure.connect((stream, node) => { + set_connection_error(account, ConnectionError.Source.SASL, null); + change_connection_state(account, ConnectionState.DISCONNECTED); + }); new Thread<void*> (null, () => { try { stream.connect(account.domainpart); @@ -108,10 +140,13 @@ public class ConnectionManager { stderr.printf("Stream Error: %s\n", e.message); change_connection_state(account, ConnectionState.DISCONNECTED); if (!connection_todo.contains(account)) { - stream_states.unset(account); + connections.unset(account); } else { - interpret_reconnect_flags(account, stream.get_flag(StreamError.Flag.IDENTITY) ?? - new StreamError.Flag() { reconnection_recomendation = StreamError.Flag.Reconnect.NOW }); + StreamError.Flag? flag = stream.get_flag(StreamError.Flag.IDENTITY); + if (flag != null) { + set_connection_error(account, ConnectionError.Source.STREAM_ERROR, flag.error_type); + } + interpret_connection_error(account); } } return null; @@ -121,29 +156,33 @@ public class ConnectionManager { return stream; } - private void interpret_reconnect_flags(Account account, StreamError.Flag stream_error_flag) { - int wait_sec = 10; + private void interpret_connection_error(Account account) { + ConnectionError? error = connection_errors[account]; + int wait_sec = 5; + if (error == null) { + wait_sec = 3; + } else if (error.source == ConnectionError.Source.STREAM_ERROR && error.flag != null) { + if (error.flag.resource_rejected) { + connect_(account, account.resourcepart + "-" + random_uuid()); + return; + } + switch (error.flag.reconnection_recomendation) { + case StreamError.Flag.Reconnect.NOW: + wait_sec = 5; break; + case StreamError.Flag.Reconnect.LATER: + wait_sec = 60; break; + case StreamError.Flag.Reconnect.NEVER: + return; + } + } else if (error.source == ConnectionError.Source.SASL) { + return; + } if (network_manager != null && network_manager.State != NetworkManager.CONNECTED_GLOBAL) { wait_sec = 60; } - switch (stream_error_flag.reconnection_recomendation) { - case StreamError.Flag.Reconnect.NOW: - wait_sec = 10; - break; - case StreamError.Flag.Reconnect.LATER: - case StreamError.Flag.Reconnect.UNKNOWN: - wait_sec = 60; - break; - case StreamError.Flag.Reconnect.NEVER: - return; - } print(@"recovering in $wait_sec\n"); Timeout.add_seconds(wait_sec, () => { - if (stream_error_flag.resource_rejected) { - connect_(account, account.resourcepart + "-" + random_uuid()); - } else { - connect_(account); - } + connect_(account); return false; }); } @@ -156,16 +195,16 @@ public class ConnectionManager { private void check_reconnect(Account account) { PingResponseListenerImpl ping_response_listener = new PingResponseListenerImpl(this, account); - Core.XmppStream stream = stream_states[account].stream; + Core.XmppStream stream = connections[account].stream; stream.get_module(Xep.Ping.Module.IDENTITY).send_ping(stream, account.domainpart, ping_response_listener); Timeout.add_seconds(5, () => { - if (stream_states[account].stream != stream) return false; + if (connections[account].stream != stream) return false; if (ping_response_listener.acked) return false; change_connection_state(account, ConnectionState.DISCONNECTED); try { - stream_states[account].stream.disconnect(); + connections[account].stream.disconnect(); } catch (Error e) { } return false; }); @@ -207,8 +246,8 @@ public class ConnectionManager { Xmpp.Presence.Stanza presence = new Xmpp.Presence.Stanza(); presence.type_ = Xmpp.Presence.Stanza.TYPE_UNAVAILABLE; try { - stream_states[account].stream.get_module(Presence.Module.IDENTITY).send_presence(stream_states[account].stream, presence); - stream_states[account].stream.disconnect(); + connections[account].stream.get_module(Presence.Module.IDENTITY).send_presence(connections[account].stream, presence); + connections[account].stream.disconnect(); } catch (Error e) { print(@"on_prepare_for_sleep error $(e.message)\n"); } } } else { @@ -218,8 +257,16 @@ public class ConnectionManager { } private void change_connection_state(Account account, ConnectionState state) { - stream_states[account].connection_state = state; - connection_state_changed(account, state); + if (connections.has_key(account)) { + connections[account].connection_state = state; + connection_state_changed(account, state); + } + } + + private void set_connection_error(Account account, ConnectionError.Source source, string? id) { + ConnectionError error = new ConnectionError(source, id); + connection_errors[account] = error; + connection_error(account, error); } } diff --git a/libdino/src/service/module_manager.vala b/libdino/src/service/module_manager.vala index ab268876..9a510662 100644 --- a/libdino/src/service/module_manager.vala +++ b/libdino/src/service/module_manager.vala @@ -33,18 +33,11 @@ public class ModuleManager { public ArrayList<Core.XmppStreamModule> get_modules(Account account, string? resource = null) { ArrayList<Core.XmppStreamModule> modules = new ArrayList<Core.XmppStreamModule>(); lock (module_map) { - if (!module_map.has_key(account)) { - initialize(account); - } - foreach (Core.XmppStreamModule module in module_map[account]) { - if (Bind.Module.IDENTITY.matches(module)) { - // TODO: argh?! - modules.add(new Bind.Module(resource == null ? account.resourcepart : resource)); - } else { - modules.add(module); - } - } + if (!module_map.has_key(account)) initialize(account); + foreach (Core.XmppStreamModule module in module_map[account]) modules.add(module); } + modules.add(new Bind.Module(resource == null ? account.resourcepart : resource)); + modules.add(new PlainSasl.Module(account.bare_jid.to_string(), account.password)); return modules; } @@ -52,8 +45,6 @@ public class ModuleManager { lock(module_map) { module_map[account] = new ArrayList<Core.XmppStreamModule>(); module_map[account].add(new Tls.Module()); - module_map[account].add(new PlainSasl.Module(account.bare_jid.to_string(), account.password)); - module_map[account].add(new Bind.Module(account.resourcepart)); module_map[account].add(new Roster.Module()); module_map[account].add(new Xep.ServiceDiscovery.Module.with_identity("client", "pc")); module_map[account].add(new Xep.PrivateXmlStorage.Module()); |