From b2a16a024089d42265b487e59251d47f408e9ede Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Mon, 20 Dec 2010 22:47:40 +0000 Subject: [PATCH] Move deluge errors to the errors module, they will be reused later on other parts of code. Now, calling connect on client has two behaviours, if username/password is passed the client connects, authenticates and returns daemon info, if username/password is not passed, only the daemon info is returned. This might change a bit later on though. --- deluge/core/daemon.py | 2 + deluge/core/rpcserver.py | 34 ++----- deluge/error.py | 11 +++ deluge/ui/client.py | 133 +++++++++++++++------------ deluge/ui/gtkui/connectionmanager.py | 34 +++++-- 5 files changed, 125 insertions(+), 89 deletions(-) diff --git a/deluge/core/daemon.py b/deluge/core/daemon.py index 5cad58af0..968534900 100644 --- a/deluge/core/daemon.py +++ b/deluge/core/daemon.py @@ -198,6 +198,8 @@ class Daemon(object): :returns: str, the version number """ + print '\n\ndaemon.info called\n\n' + return deluge.common.get_version() @export() diff --git a/deluge/core/rpcserver.py b/deluge/core/rpcserver.py index 7d6033e24..9beb2c201 100644 --- a/deluge/core/rpcserver.py +++ b/deluge/core/rpcserver.py @@ -55,7 +55,8 @@ except ImportError: import deluge.component as component import deluge.configmanager -from deluge.core.authmanager import AUTH_LEVEL_NONE, AUTH_LEVEL_DEFAULT +from deluge.core.authmanager import AUTH_LEVEL_NONE, AUTH_LEVEL_DEFAULT, AUTH_LEVEL_ADMIN +from deluge.error import DelugeError, NotAuthorizedError RPC_RESPONSE = 1 RPC_ERROR = 2 @@ -117,12 +118,6 @@ def format_request(call): else: return s -class DelugeError(Exception): - pass - -class NotAuthorizedError(DelugeError): - pass - class ServerContextFactory(object): def getContext(self): """ @@ -253,24 +248,10 @@ class DelugeRPCProtocol(Protocol): "".join(traceback.format_tb(exceptionTraceback))) )) - if method == "daemon.peek": + if method == "daemon.info": # This is a special case and used in the initial connection process - # We need to peek the user here in order to get an auth level back - # and see if the user exists. - try: - ret = component.get("AuthManager").peek(*args, **kwargs) - if ret: - self.factory.authorized_sessions[self.transport.sessionno] = (ret, args[0]) - self.factory.session_protocols[self.transport.sessionno] = self - except Exception, e: - sendError() - log.exception(e) - else: - self.sendData((RPC_RESPONSE, request_id, ret)) - if not ret: - self.transport.loseConnection() - finally: - return + self.sendData((RPC_RESPONSE, request_id, deluge.common.get_version())) + return elif method == "daemon.login": # This is a special case and used in the initial connection process # We need to authenticate the user here @@ -374,6 +355,7 @@ class RPCServer(component.Component): # Holds the interested event list for the sessions self.factory.interested_events = {} + self.listen = listen if not listen: return @@ -458,6 +440,8 @@ class RPCServer(component.Component): :rtype: string """ + if not self.listen: + return "localclient" session_id = self.get_session_id() if session_id > -1 and session_id in self.factory.authorized_sessions: return self.factory.authorized_sessions[session_id][1] @@ -472,6 +456,8 @@ class RPCServer(component.Component): :returns: the auth level :rtype: int """ + if not self.listen: + return AUTH_LEVEL_ADMIN return self.factory.authorized_sessions[self.get_session_id()][0] def get_rpc_auth_level(self, rpc): diff --git a/deluge/error.py b/deluge/error.py index 24c1e8352..eed5309f4 100644 --- a/deluge/error.py +++ b/deluge/error.py @@ -48,3 +48,14 @@ class InvalidTorrentError(DelugeError): class InvalidPathError(DelugeError): pass + +class NotAuthorizedError(DelugeError): + pass + +class BadLoginError(DelugeError): + pass + +class AuthenticationRequired(BadLoginError): + def __init__(self, message, username): + super(AuthenticationRequired, self).__init__(message) + self.username = username diff --git a/deluge/ui/client.py b/deluge/ui/client.py index 13a7be87c..f71956175 100644 --- a/deluge/ui/client.py +++ b/deluge/ui/client.py @@ -261,27 +261,7 @@ class DaemonSSLProxy(DaemonProxy): self.disconnect_deferred = None self.disconnect_callback = None - def peek(self, host, port, username): - self.host = host - self.port = port - self.__connector = reactor.connectSSL(self.host, self.port, self.__factory, ssl.ClientContextFactory()) - self.connect_deferred = defer.Deferred() - self.peek_deferred = defer.Deferred() - - def on_connect(result, username): - self.__login_deferred = self.call("daemon.peek", username) - self.__login_deferred.addCallback(self.__on_peek, username) - self.__login_deferred.addErrback(self.__on_peek_fail) - - def on_connect_fail(reason): - log.debug("connect_fail: %s", reason) - self.peek_deferred.errback(reason) - - self.connect_deferred.addCallback(on_connect, username) - self.connect_deferred.addErrback(on_connect_fail) - return self.peek_deferred - - def connect(self, host, port, username, password): + def connect(self, host, port): """ Connects to a daemon at host:port @@ -298,13 +278,13 @@ class DaemonSSLProxy(DaemonProxy): self.port = port self.__connector = reactor.connectSSL(self.host, self.port, self.__factory, ssl.ClientContextFactory()) self.connect_deferred = defer.Deferred() - self.login_deferred = defer.Deferred() + self.daemon_info_deferred = defer.Deferred() # Upon connect we do a 'daemon.login' RPC - self.connect_deferred.addCallback(self.__on_connect, username, password) + self.connect_deferred.addCallback(self.__on_connect) self.connect_deferred.addErrback(self.__on_connect_fail) - return self.login_deferred + return self.daemon_info_deferred def disconnect(self): log.debug("sslproxy.disconnect()") @@ -418,19 +398,37 @@ class DaemonSSLProxy(DaemonProxy): log.error(msg) return error_data - def __on_connect(self, result, username, password): + def __on_connect(self, result): log.debug("__on_connect called") - self.__login_deferred = self.call("daemon.login", username, password) - self.__login_deferred.addCallback(self.__on_login, username) - self.__login_deferred.addErrback(self.__on_login_fail) + + def on_info(daemon_info): + self.daemon_info = daemon_info + log.debug("Got info from daemon: %s", daemon_info) + self.daemon_info_deferred.callback(daemon_info) + + def on_info_fail(reason): + log.debug("Failed to get info from daemon: %s", reason) + self.daemon_info_deferred.errback(reason) + + self.call("daemon.info").addCallback(on_info).addErrback(on_info_fail) + return self.daemon_info_deferred def __on_connect_fail(self, reason): log.debug("__on_connect_fail called") log.debug("connect_fail: %s", reason) - self.login_deferred.errback(reason) + log.exception(reason) + self.daemon_info_deferred.errback(reason) + + def authenticate(self, username, password): + log.debug("%s.authenticate: %s", self.__class__.__name__, username) + self.login_deferred = defer.Deferred() + d = self.call("daemon.login", username, password) + d.addCallback(self.__on_login, username) + d.addErrback(self.__on_login_fail) + return self.login_deferred def __on_login(self, result, username): - log.debug("__on_login called") + log.debug("__on_login called: %s %s", username, result) self.username = username # We need to tell the daemon what events we're interested in receiving if self.__factory.event_handlers: @@ -441,15 +439,6 @@ class DaemonSSLProxy(DaemonProxy): log.debug("_on_login_fail(): %s", result) self.login_deferred.errback(result) - def __on_peek(self, result, username): - log.debug("__on_peek called. result: %s", result) - self.username = username - self.peek_deferred.callback(result) - - def __on_peek_fail(self, result): - log.debug("__on_peek_fail called. result: %s", result) - self.peek_deferred.errback(result) - def set_disconnect_callback(self, cb): """ Set a function to be called when the connection to the daemon is lost @@ -471,7 +460,10 @@ class DaemonClassicProxy(DaemonProxy): self.connected = True self.host = "localhost" self.port = 58846 + # Running in classic mode, it's safe to import auth level + from deluge.core.authmanager import AUTH_LEVEL_ADMIN self.username = "localclient" + self.authentication_level = AUTH_LEVEL_ADMIN # Register the event handlers for event in event_handlers: for handler in event_handlers[event]: @@ -574,32 +566,55 @@ class Client(object): self._daemon_proxy = DaemonSSLProxy(dict(self.__event_handlers)) self._daemon_proxy.set_disconnect_callback(self.__on_disconnect) - d = self._daemon_proxy.connect(host, port, username, password) + d = self._daemon_proxy.connect(host, port) def on_connect_fail(result): log.debug("on_connect_fail: %s", result) self.disconnect() return result - d.addErrback(on_connect_fail) + + if username or password: + auth_deferred = defer.Deferred() + + def on_authenticate(result, daemon_info): + log.debug("Authentication sucessfull: %s", result) + self.authentication_level = result + auth_deferred.callback(daemon_info) + + def on_authenticate_fail(reason): + log.debug("Failed to authenticate") + log.exception(reason) + auth_deferred.errback(reason) + + def on_connected(daemon_version): + log.debug("Client.connect.on_connected: %s", daemon_version) + print 1234, self._daemon_proxy + d = self._daemon_proxy.authenticate(username, password) + print 1234, d + d.addCallback(on_authenticate, daemon_version) + d.addErrback(on_authenticate_fail) +# return d + + d.addCallback(on_connected) + return auth_deferred return d - def peek(self, host="127.0.0.1", port=58846, username=""): - if not username and host in ("127.0.0.1", "localhost"): - # No username was provided and it's the localhost, so we can try - # to grab the credentials from the auth file. - import common - username, password = common.get_localhost_auth() - self._daemon_proxy = DaemonSSLProxy(dict(self.__event_handlers)) - self._daemon_proxy.set_disconnect_callback(self.__on_disconnect) - d = self._daemon_proxy.peek(host, port, username) - def on_connect_fail(result): - log.debug("on_connect_fail: %s", result) - self.disconnect() - return result - - d.addErrback(on_connect_fail) - return d +# def authenticate(self, username="", password=""): +# if not self.connected(): +# raise Exception("You first need to call connect") +# if not username and self._daemon_proxy.host in ("127.0.0.1", "localhost"): +# # No username was provided and it's the localhost, so we can try +# # to grab the credentials from the auth file. +# import common +# username, password = common.get_localhost_auth() +# +# def on_authenticate_fail(reason): +# log.debug("Failed to authenticate %s@%s:%s") +# +# d = self._daemon_proxy.authenticate(username, password) +# d.addErrback(on_authenticate_fail) +# return d def disconnect(self): """ @@ -646,6 +661,10 @@ class Client(object): else: return True + def daemon_info(self): + return self._daemon_proxy.daemon_info_deferred + return defer.succeed(self._daemon_proxy.daemon_info or None) + def is_localhost(self): """ Checks if the current connected host is a localhost or not. diff --git a/deluge/ui/gtkui/connectionmanager.py b/deluge/ui/gtkui/connectionmanager.py index a97029fc1..5066c067c 100644 --- a/deluge/ui/gtkui/connectionmanager.py +++ b/deluge/ui/gtkui/connectionmanager.py @@ -300,6 +300,7 @@ class ConnectionManager(component.Component): row = self.__get_host_row(host_id) if row: row[HOSTLIST_COL_STATUS] = _("Offline") +# row[HOSTLIST_COL_VERSION] = "" self.__update_buttons() for row in self.liststore: @@ -307,22 +308,30 @@ class ConnectionManager(component.Component): host = row[HOSTLIST_COL_HOST] port = row[HOSTLIST_COL_PORT] user = row[HOSTLIST_COL_USER] - password = row[HOSTLIST_COL_PASS] if client.connected() and \ (host, port, "localclient" if not user and host in ("127.0.0.1", "localhost") else user) == client.connection_info(): def on_info(info): + log.debug("\n\nClient connected, query info: %s:%s\n\n", info, self.running) if not self.running: return row[HOSTLIST_COL_VERSION] = info self.__update_buttons() + print row[HOSTLIST_COL_ID], row[HOSTLIST_COL_HOST], row[HOSTLIST_COL_PORT], row[HOSTLIST_COL_USER], row[HOSTLIST_COL_VERSION] + + def on_info_fail(reason): + print '\n\n' + log.exception(reason) + print '\n\n' + row[HOSTLIST_COL_STATUS] = _("Connected") - client.daemon.info().addCallback(on_info) + log.debug("\n\nquery daemons info\n\n") + client.daemon.info().addCallback(on_info).addErrback(on_info_fail) continue # Create a new Client instance c = deluge.ui.client.Client() - d = c.peek(host, port, user) + d = c.connect(host, port) d.addCallback(on_connect, c, host_id) d.addErrback(on_connect_failed, host_id) @@ -435,11 +444,11 @@ that you forgot to install the deluged package or it's not in your PATH.")).run( details=traceback.format_exc(tb[2])).run() # Signal handlers - def __connect(self, host_id, host, port, user, password): + def __connect(self, host_id, host, port, username, password): def do_connect(*args): - d = client.connect(host, port, user, password) + d = client.connect(host, port, username, password) d.addCallback(self.__on_connected, host_id) - d.addErrback(self.__on_connected_failed, host_id, host, port, user) + d.addErrback(self.__on_connected_failed, host_id, host, port, username) return d if client.connected(): @@ -447,16 +456,25 @@ that you forgot to install the deluged package or it's not in your PATH.")).run( else: return do_connect() - def __on_connected(self, connector, host_id): - log.debug("__on_connected called") + def __on_connected(self, daemon_info, host_id): +# log.debug("__on_connected called for hostid: %s connector: %s", +# host_id, daemon_info) if self.gtkui_config["autoconnect"]: self.gtkui_config["autoconnect_host_id"] = host_id +# row = self.__get_host_row(host_id) +# row[HOSTLIST_COL_STATUS] = _("Connected") +# row[HOSTLIST_COL_VERSION] = daemon_info +# +# # Update the status of the hosts +# self.__update_list() + self.connection_manager.response(gtk.RESPONSE_OK) component.start() def __on_connected_failed(self, reason, host_id, host, port, user): + log.exception(reason) if reason.value.exception_type == "PasswordRequired": log.debug("PasswordRequired exception") dialog = dialogs.AuthenticationDialog(reason.value.exception_msg)