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.
This commit is contained in:
Pedro Algarvio 2010-12-20 22:47:40 +00:00
parent e17c035521
commit b2a16a0240
5 changed files with 125 additions and 89 deletions

View File

@ -198,6 +198,8 @@ class Daemon(object):
:returns: str, the version number :returns: str, the version number
""" """
print '\n\ndaemon.info called\n\n'
return deluge.common.get_version() return deluge.common.get_version()
@export() @export()

View File

@ -55,7 +55,8 @@ except ImportError:
import deluge.component as component import deluge.component as component
import deluge.configmanager 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_RESPONSE = 1
RPC_ERROR = 2 RPC_ERROR = 2
@ -117,12 +118,6 @@ def format_request(call):
else: else:
return s return s
class DelugeError(Exception):
pass
class NotAuthorizedError(DelugeError):
pass
class ServerContextFactory(object): class ServerContextFactory(object):
def getContext(self): def getContext(self):
""" """
@ -253,24 +248,10 @@ class DelugeRPCProtocol(Protocol):
"".join(traceback.format_tb(exceptionTraceback))) "".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 # 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 self.sendData((RPC_RESPONSE, request_id, deluge.common.get_version()))
# and see if the user exists. return
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
elif method == "daemon.login": elif method == "daemon.login":
# This is a special case and used in the initial connection process # This is a special case and used in the initial connection process
# We need to authenticate the user here # We need to authenticate the user here
@ -374,6 +355,7 @@ class RPCServer(component.Component):
# Holds the interested event list for the sessions # Holds the interested event list for the sessions
self.factory.interested_events = {} self.factory.interested_events = {}
self.listen = listen
if not listen: if not listen:
return return
@ -458,6 +440,8 @@ class RPCServer(component.Component):
:rtype: string :rtype: string
""" """
if not self.listen:
return "localclient"
session_id = self.get_session_id() session_id = self.get_session_id()
if session_id > -1 and session_id in self.factory.authorized_sessions: if session_id > -1 and session_id in self.factory.authorized_sessions:
return self.factory.authorized_sessions[session_id][1] return self.factory.authorized_sessions[session_id][1]
@ -472,6 +456,8 @@ class RPCServer(component.Component):
:returns: the auth level :returns: the auth level
:rtype: int :rtype: int
""" """
if not self.listen:
return AUTH_LEVEL_ADMIN
return self.factory.authorized_sessions[self.get_session_id()][0] return self.factory.authorized_sessions[self.get_session_id()][0]
def get_rpc_auth_level(self, rpc): def get_rpc_auth_level(self, rpc):

View File

@ -48,3 +48,14 @@ class InvalidTorrentError(DelugeError):
class InvalidPathError(DelugeError): class InvalidPathError(DelugeError):
pass 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

View File

@ -261,27 +261,7 @@ class DaemonSSLProxy(DaemonProxy):
self.disconnect_deferred = None self.disconnect_deferred = None
self.disconnect_callback = None self.disconnect_callback = None
def peek(self, host, port, username): def connect(self, host, port):
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):
""" """
Connects to a daemon at host:port Connects to a daemon at host:port
@ -298,13 +278,13 @@ class DaemonSSLProxy(DaemonProxy):
self.port = port self.port = port
self.__connector = reactor.connectSSL(self.host, self.port, self.__factory, ssl.ClientContextFactory()) self.__connector = reactor.connectSSL(self.host, self.port, self.__factory, ssl.ClientContextFactory())
self.connect_deferred = defer.Deferred() self.connect_deferred = defer.Deferred()
self.login_deferred = defer.Deferred() self.daemon_info_deferred = defer.Deferred()
# Upon connect we do a 'daemon.login' RPC # 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) self.connect_deferred.addErrback(self.__on_connect_fail)
return self.login_deferred return self.daemon_info_deferred
def disconnect(self): def disconnect(self):
log.debug("sslproxy.disconnect()") log.debug("sslproxy.disconnect()")
@ -418,19 +398,37 @@ class DaemonSSLProxy(DaemonProxy):
log.error(msg) log.error(msg)
return error_data return error_data
def __on_connect(self, result, username, password): def __on_connect(self, result):
log.debug("__on_connect called") log.debug("__on_connect called")
self.__login_deferred = self.call("daemon.login", username, password)
self.__login_deferred.addCallback(self.__on_login, username) def on_info(daemon_info):
self.__login_deferred.addErrback(self.__on_login_fail) 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): def __on_connect_fail(self, reason):
log.debug("__on_connect_fail called") log.debug("__on_connect_fail called")
log.debug("connect_fail: %s", reason) 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): def __on_login(self, result, username):
log.debug("__on_login called") log.debug("__on_login called: %s %s", username, result)
self.username = username self.username = username
# We need to tell the daemon what events we're interested in receiving # We need to tell the daemon what events we're interested in receiving
if self.__factory.event_handlers: if self.__factory.event_handlers:
@ -441,15 +439,6 @@ class DaemonSSLProxy(DaemonProxy):
log.debug("_on_login_fail(): %s", result) log.debug("_on_login_fail(): %s", result)
self.login_deferred.errback(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): def set_disconnect_callback(self, cb):
""" """
Set a function to be called when the connection to the daemon is lost 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.connected = True
self.host = "localhost" self.host = "localhost"
self.port = 58846 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.username = "localclient"
self.authentication_level = AUTH_LEVEL_ADMIN
# Register the event handlers # Register the event handlers
for event in event_handlers: for event in event_handlers:
for handler in event_handlers[event]: for handler in event_handlers[event]:
@ -574,32 +566,55 @@ class Client(object):
self._daemon_proxy = DaemonSSLProxy(dict(self.__event_handlers)) self._daemon_proxy = DaemonSSLProxy(dict(self.__event_handlers))
self._daemon_proxy.set_disconnect_callback(self.__on_disconnect) 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): def on_connect_fail(result):
log.debug("on_connect_fail: %s", result) log.debug("on_connect_fail: %s", result)
self.disconnect() self.disconnect()
return result return result
d.addErrback(on_connect_fail) 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 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)) # def authenticate(self, username="", password=""):
self._daemon_proxy.set_disconnect_callback(self.__on_disconnect) # if not self.connected():
d = self._daemon_proxy.peek(host, port, username) # raise Exception("You first need to call connect")
def on_connect_fail(result): # if not username and self._daemon_proxy.host in ("127.0.0.1", "localhost"):
log.debug("on_connect_fail: %s", result) # # No username was provided and it's the localhost, so we can try
self.disconnect() # # to grab the credentials from the auth file.
return result # import common
# username, password = common.get_localhost_auth()
d.addErrback(on_connect_fail) #
return d # 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): def disconnect(self):
""" """
@ -646,6 +661,10 @@ class Client(object):
else: else:
return True 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): def is_localhost(self):
""" """
Checks if the current connected host is a localhost or not. Checks if the current connected host is a localhost or not.

View File

@ -300,6 +300,7 @@ class ConnectionManager(component.Component):
row = self.__get_host_row(host_id) row = self.__get_host_row(host_id)
if row: if row:
row[HOSTLIST_COL_STATUS] = _("Offline") row[HOSTLIST_COL_STATUS] = _("Offline")
# row[HOSTLIST_COL_VERSION] = ""
self.__update_buttons() self.__update_buttons()
for row in self.liststore: for row in self.liststore:
@ -307,22 +308,30 @@ class ConnectionManager(component.Component):
host = row[HOSTLIST_COL_HOST] host = row[HOSTLIST_COL_HOST]
port = row[HOSTLIST_COL_PORT] port = row[HOSTLIST_COL_PORT]
user = row[HOSTLIST_COL_USER] user = row[HOSTLIST_COL_USER]
password = row[HOSTLIST_COL_PASS]
if client.connected() and \ if client.connected() and \
(host, port, "localclient" if not user and host in ("127.0.0.1", "localhost") else user) == client.connection_info(): (host, port, "localclient" if not user and host in ("127.0.0.1", "localhost") else user) == client.connection_info():
def on_info(info): def on_info(info):
log.debug("\n\nClient connected, query info: %s:%s\n\n", info, self.running)
if not self.running: if not self.running:
return return
row[HOSTLIST_COL_VERSION] = info row[HOSTLIST_COL_VERSION] = info
self.__update_buttons() 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") 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 continue
# Create a new Client instance # Create a new Client instance
c = deluge.ui.client.Client() c = deluge.ui.client.Client()
d = c.peek(host, port, user) d = c.connect(host, port)
d.addCallback(on_connect, c, host_id) d.addCallback(on_connect, c, host_id)
d.addErrback(on_connect_failed, 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() details=traceback.format_exc(tb[2])).run()
# Signal handlers # Signal handlers
def __connect(self, host_id, host, port, user, password): def __connect(self, host_id, host, port, username, password):
def do_connect(*args): 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.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 return d
if client.connected(): if client.connected():
@ -447,16 +456,25 @@ that you forgot to install the deluged package or it's not in your PATH.")).run(
else: else:
return do_connect() return do_connect()
def __on_connected(self, connector, host_id): def __on_connected(self, daemon_info, host_id):
log.debug("__on_connected called") # log.debug("__on_connected called for hostid: %s connector: %s",
# host_id, daemon_info)
if self.gtkui_config["autoconnect"]: if self.gtkui_config["autoconnect"]:
self.gtkui_config["autoconnect_host_id"] = host_id 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) self.connection_manager.response(gtk.RESPONSE_OK)
component.start() component.start()
def __on_connected_failed(self, reason, host_id, host, port, user): def __on_connected_failed(self, reason, host_id, host, port, user):
log.exception(reason)
if reason.value.exception_type == "PasswordRequired": if reason.value.exception_type == "PasswordRequired":
log.debug("PasswordRequired exception") log.debug("PasswordRequired exception")
dialog = dialogs.AuthenticationDialog(reason.value.exception_msg) dialog = dialogs.AuthenticationDialog(reason.value.exception_msg)