mylar/lib/deluge_client/client.py

112 lines
3.3 KiB
Python

import logging
import socket
import ssl
import struct
import zlib
from .rencode import dumps, loads
RPC_RESPONSE = 1
RPC_ERROR = 2
RPC_EVENT = 3
#MESSAGE_HEADER_SIZE = 5
READ_SIZE = 10
logger = logging.getLogger(__name__)
class ConnectionLostException(Exception):
pass
class CallTimeoutException(Exception):
pass
class DelugeRPCClient(object):
timeout = 20
def __init__(self, host, port, username, password):
self.host = host
self.port = port
self.username = username
self.password = password
self.request_id = 1
self.connected = False
self._create_socket()
def _create_socket(self, ssl_version=None):
if ssl_version is not None:
self._socket = ssl.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM), ssl_version=ssl_version)
else:
self._socket = ssl.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
self._socket.settimeout(self.timeout)
def connect(self):
"""
Connects to the Deluge instance
"""
logger.info('Connecting to %s:%s' % (self.host, self.port))
try:
self._socket.connect((self.host, self.port))
except ssl.SSLError as e:
if e.reason != 'UNSUPPORTED_PROTOCOL' or not hasattr(ssl, 'PROTOCOL_SSLv3'):
raise
logger.warning('Was unable to ssl handshake, trying to force SSLv3 (insecure)')
self._create_socket(ssl_version=ssl.PROTOCOL_SSLv3)
self._socket.connect((self.host, self.port))
logger.debug('Connected to Deluge, logging in')
result = self.call('daemon.login', self.username, self.password)
logger.debug('Logged in with value %r' % result)
self.connected = True
def disconnect(self):
"""
Disconnect from deluge
"""
if self.connected:
self._socket.close()
def call(self, method, *args, **kwargs):
"""
Calls an RPC function
"""
self.request_id += 1
logger.debug('Calling reqid %s method %r with args:%r kwargs:%r' % (self.request_id, method, args, kwargs))
req = ((self.request_id, method, args, kwargs), )
req = zlib.compress(dumps(req))
#self._socket.send('D' + struct.pack("!i", len(req))) # seems to be for the future !
self._socket.send(req)
data = b''
while True:
try:
d = self._socket.recv(READ_SIZE)
except ssl.SSLError:
raise CallTimeoutException()
data += d
try:
data = zlib.decompress(data)
except zlib.error:
if not d:
raise ConnectionLostException()
continue
break
data = list(loads(data))
msg_type = data.pop(0)
request_id = data.pop(0)
if msg_type == RPC_ERROR:
exception_type, exception_msg, traceback = data[0]
exception = type(str(exception_type), (Exception, ), {})
exception_msg = '%s\n\n%s' % (exception_msg, traceback)
raise exception(exception_msg)
elif msg_type == RPC_RESPONSE:
retval = data[0]
return retval