Esempio n. 1
0
class DelugeRPCProtocol(DelugeTransferProtocol):
    def message_received(self, request):
        """
        This method is called whenever a message is received from a client.  The
        only message that a client sends to the server is a RPC Request message.
        If the RPC Request message is valid, then the method is called in
        :meth:`dispatch`.
        
        :param request: the request from the client.
        :type data: tuple

        """
        if type(request) is not tuple:
            log.debug("Received invalid message: type is not tuple")
            return

        if len(request) < 1:
            log.debug("Received invalid message: there are no items")
            return

        for call in request:
            if len(call) != 4:
                log.debug(
                    "Received invalid rpc request: number of items "
                    "in request is %s", len(call))
                continue
            #log.debug("RPCRequest: %s", format_request(call))
            reactor.callLater(0, self.dispatch, *call)

    def sendData(self, data):
        """
        Sends the data to the client.

        :param data: the object that is to be sent to the client.  This should
            be one of the RPC message types.
        :type data: object

        """
        self.transfer_message(data)

    def connectionMade(self):
        """
        This method is called when a new client connects.
        """
        peer = self.transport.getPeer()
        log.info("Deluge Client connection made from: %s:%s", peer.host,
                 peer.port)
        # Set the initial auth level of this session to AUTH_LEVEL_NONE
        self.factory.authorized_sessions[
            self.transport.sessionno] = AUTH_LEVEL_NONE

    def connectionLost(self, reason):
        """
        This method is called when the client is disconnected.

        :param reason: the reason the client disconnected.
        :type reason: str

        """

        # We need to remove this session from various dicts
        del self.factory.authorized_sessions[self.transport.sessionno]
        if self.transport.sessionno in self.factory.session_protocols:
            del self.factory.session_protocols[self.transport.sessionno]
        if self.transport.sessionno in self.factory.interested_events:
            del self.factory.interested_events[self.transport.sessionno]

        log.info("Deluge client disconnected: %s", reason.value)

    def valid_session(self):
        return self.transport.sessionno in self.factory.authorized_sessions

    def dispatch(self, request_id, method, args, kwargs):
        """
        This method is run when a RPC Request is made.  It will run the local method
        and will send either a RPC Response or RPC Error back to the client.

        :param request_id: the request_id from the client (sent in the RPC Request)
        :type request_id: int
        :param method: the local method to call. It must be registered with
            the :class:`RPCServer`.
        :type method: str
        :param args: the arguments to pass to `method`
        :type args: list
        :param kwargs: the keyword-arguments to pass to `method`
        :type kwargs: dict

        """
        def sendError():
            """
            Sends an error response with the contents of the exception that was raised.
            """
            exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
            formated_tb = "".join(traceback.format_tb(exceptionTraceback))
            try:
                self.sendData((RPC_ERROR, request_id, exceptionType.__name__,
                               exceptionValue._args, exceptionValue._kwargs,
                               formated_tb))
            except Exception, err:
                # This most likely not a deluge exception, let's wrap it
                log.error(
                    "An exception occurred while sending RPC_ERROR to "
                    "client. Wrapping it and resending. Error to "
                    "send(causing exception goes next):\n%s", formated_tb)
                log.exception(err)
                try:
                    raise WrappedException(str(exceptionValue),
                                           exceptionType.__name__, formated_tb)
                except:
                    sendError()

        if method == "daemon.info":
            # This is a special case and used in the initial connection process
            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
            log.debug("RPC dispatch daemon.login")
            try:
                client_version = kwargs.pop('client_version', None)
                if client_version is None:
                    raise IncompatibleClient(deluge.common.get_version())
                ret = component.get("AuthManager").authorize(*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()
                if not isinstance(e, _ClientSideRecreateError):
                    log.exception(e)
            else:
                self.sendData((RPC_RESPONSE, request_id, (ret)))
                if not ret:
                    self.transport.loseConnection()
            finally:
Esempio n. 2
0
    def dispatch(self, request_id, method, args, kwargs):
        """
        This method is run when a RPC Request is made.  It will run the local method
        and will send either a RPC Response or RPC Error back to the client.

        :param request_id: the request_id from the client (sent in the RPC Request)
        :type request_id: int
        :param method: the local method to call. It must be registered with
            the :class:`RPCServer`.
        :type method: str
        :param args: the arguments to pass to `method`
        :type args: list
        :param kwargs: the keyword-arguments to pass to `method`
        :type kwargs: dict

        """

        def send_error():
            """
            Sends an error response with the contents of the exception that was raised.
            """
            exc_type, exc_value, dummy_exc_trace = sys.exc_info()
            formated_tb = traceback.format_exc()
            try:
                self.sendData(
                    (
                        RPC_ERROR,
                        request_id,
                        exc_type.__name__,
                        exc_value._args,
                        exc_value._kwargs,
                        formated_tb,
                    )
                )
            except AttributeError:
                # This is not a deluge exception (object has no attribute '_args), let's wrap it
                log.warning(
                    'An exception occurred while sending RPC_ERROR to '
                    'client. Wrapping it and resending. Error to '
                    'send(causing exception goes next):\n%s',
                    formated_tb,
                )
                try:
                    raise WrappedException(
                        str(exc_value), exc_type.__name__, formated_tb
                    )
                except WrappedException:
                    send_error()
            except Exception as ex:
                log.error(
                    'An exception occurred while sending RPC_ERROR to client: %s', ex
                )

        if method == 'daemon.info':
            # This is a special case and used in the initial connection process
            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
            log.debug('RPC dispatch daemon.login')
            try:
                client_version = kwargs.pop('client_version', None)
                if client_version is None:
                    raise IncompatibleClient(deluge.common.get_version())
                ret = component.get('AuthManager').authorize(*args, **kwargs)
                if ret:
                    self.factory.authorized_sessions[
                        self.transport.sessionno
                    ] = self.AuthLevel(ret, args[0])
                    self.factory.session_protocols[self.transport.sessionno] = self
            except Exception as ex:
                send_error()
                if not isinstance(ex, _ClientSideRecreateError):
                    log.exception(ex)
            else:
                self.sendData((RPC_RESPONSE, request_id, (ret)))
                if not ret:
                    self.transport.loseConnection()
            return

        # Anything below requires a valid session
        if not self.valid_session():
            return

        if method == 'daemon.set_event_interest':
            log.debug('RPC dispatch daemon.set_event_interest')
            # This special case is to allow clients to set which events they are
            # interested in receiving.
            # We are expecting a sequence from the client.
            try:
                if self.transport.sessionno not in self.factory.interested_events:
                    self.factory.interested_events[self.transport.sessionno] = []
                self.factory.interested_events[self.transport.sessionno].extend(args[0])
            except Exception:
                send_error()
            else:
                self.sendData((RPC_RESPONSE, request_id, (True)))
            return

        if method not in self.factory.methods:
            try:
                # Raise exception to be sent back to client
                raise AttributeError('RPC call on invalid function: %s' % method)
            except AttributeError:
                send_error()
                return

        log.debug('RPC dispatch %s', method)
        try:
            method_auth_requirement = self.factory.methods[method]._rpcserver_auth_level
            auth_level = self.factory.authorized_sessions[
                self.transport.sessionno
            ].auth_level
            if auth_level < method_auth_requirement:
                # This session is not allowed to call this method
                log.debug(
                    'Session %s is attempting an unauthorized method call!',
                    self.transport.sessionno,
                )
                raise NotAuthorizedError(auth_level, method_auth_requirement)
            # Set the session_id in the factory so that methods can know
            # which session is calling it.
            self.factory.session_id = self.transport.sessionno
            ret = self.factory.methods[method](*args, **kwargs)
        except Exception as ex:
            send_error()
            # Don't bother printing out DelugeErrors, because they are just
            # for the client
            if not isinstance(ex, DelugeError):
                log.exception('Exception calling RPC request: %s', ex)
        else:
            # Check if the return value is a deferred, since we'll need to
            # wait for it to fire before sending the RPC_RESPONSE
            if isinstance(ret, defer.Deferred):

                def on_success(result):
                    try:
                        self.sendData((RPC_RESPONSE, request_id, result))
                    except Exception:
                        send_error()
                    return result

                def on_fail(failure):
                    try:
                        failure.raiseException()
                    except Exception:
                        send_error()
                    return failure

                ret.addCallbacks(on_success, on_fail)
            else:
                self.sendData((RPC_RESPONSE, request_id, ret))
Esempio n. 3
0
                except:
                    sendError()

        if method == "daemon.info":
            # This is a special case and used in the initial connection process
            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
            log.debug("RPC dispatch daemon.login")
            try:
                client_version = kwargs.pop('client_version', None)
                if client_version is None:
                    raise IncompatibleClient(deluge.common.get_version())
                ret = component.get("AuthManager").authorize(*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()
                if not isinstance(e, _ClientSideRecreateError):
                    log.exception(e)
            else:
                self.sendData((RPC_RESPONSE, request_id, (ret)))
                if not ret:
                    self.transport.loseConnection()
            finally: