mylar/lib/rtorrent/connection.py

166 lines
5.1 KiB
Python

import logging
import urllib
from rtorrent.common import convert_version_tuple_to_str, join_uri, update_uri
from rtorrent.lib.xmlrpc.clients.http import HTTPServerProxy
from rtorrent.lib.xmlrpc.clients.scgi import SCGIServerProxy
from rtorrent.lib.xmlrpc.transports.basic_auth import BasicAuthTransport
try:
from requests.packages.urllib3 import disable_warnings
disable_warnings()
except ImportError:
print 'Unable to disable warnings for non-https authentication.'
# Try import requests transport (optional)
try:
from rtorrent.lib.xmlrpc.transports.requests_ import RequestsTransport
except ImportError:
RequestsTransport = None
MIN_RTORRENT_VERSION = (0, 8, 1)
MIN_RTORRENT_VERSION_STR = convert_version_tuple_to_str(MIN_RTORRENT_VERSION)
log = logging.getLogger(__name__)
class Connection(object):
def __init__(self, uri, auth=None, verify_ssl=True, sp=None, sp_kwargs=None):
self.auth = auth
self.verify_ssl = verify_ssl
# Transform + Parse URI
self.uri = self._transform_uri(uri)
self.scheme = urllib.splittype(self.uri)[0]
# Construct RPC Client
self.sp = self._get_sp(self.scheme, sp)
self.sp_kwargs = sp_kwargs or {}
self._client = None
self._client_version_tuple = ()
self._rpc_methods = []
@property
def client(self):
if self._client is None:
# Construct new client
self._client = self.connect()
# Return client
return self._client
def connect(self):
log.debug('Connecting to server: %r', self.uri)
if self.auth:
# Construct server proxy with authentication transport
return self.sp(self.uri, transport=self._construct_transport(), **self.sp_kwargs)
# Construct plain server proxy
return self.sp(self.uri, **self.sp_kwargs)
def test(self):
try:
self.verify()
except:
return False
return True
def verify(self):
# check for rpc methods that should be available
assert "system.client_version" in self._get_rpc_methods(), "Required RPC method not available."
assert "system.library_version" in self._get_rpc_methods(), "Required RPC method not available."
# minimum rTorrent version check
assert self._meets_version_requirement() is True,\
"Error: Minimum rTorrent version required is {0}".format(MIN_RTORRENT_VERSION_STR)
#
# Private methods
#
def _construct_transport(self):
# Ensure "auth" parameter is valid
if type(self.auth) is not tuple or len(self.auth) != 3:
raise ValueError('Invalid "auth" parameter format')
# Construct transport with authentication details
method, _, _ = self.auth
secure = self.scheme == 'https'
log.debug('Constructing transport for scheme: %r, authentication method: %r', self.scheme, method)
# Use requests transport (if available)
if RequestsTransport and method in ['basic', 'digest']:
return RequestsTransport(
secure, self.auth,
verify_ssl=self.verify_ssl
)
# Use basic authentication transport
if method == 'basic':
return BasicAuthTransport(secure, self.auth)
# Unsupported authentication method
if method == 'digest':
raise Exception('Digest authentication requires the "requests" library')
raise NotImplementedError('Unknown authentication method: %r' % method)
def _get_client_version_tuple(self):
if not self._client_version_tuple:
if not hasattr(self, "client_version"):
setattr(self, "client_version", self.client.system.client_version())
rtver = getattr(self, "client_version")
self._client_version_tuple = tuple([int(i) for i in rtver.split(".")])
return self._client_version_tuple
def _get_rpc_methods(self):
""" Get list of raw RPC commands
@return: raw RPC commands
@rtype: list
"""
return(self._rpc_methods or self._update_rpc_methods())
@staticmethod
def _get_sp(scheme, sp):
if sp:
return sp
if scheme in ['http', 'https']:
return HTTPServerProxy
if scheme == 'scgi':
return SCGIServerProxy
raise NotImplementedError()
def _meets_version_requirement(self):
return self._get_client_version_tuple() >= MIN_RTORRENT_VERSION
@staticmethod
def _transform_uri(uri):
scheme = urllib.splittype(uri)[0]
if scheme == 'httprpc' or scheme.startswith('httprpc+'):
# Try find HTTPRPC transport (token after '+' in 'httprpc+https'), otherwise assume HTTP
transport = scheme[scheme.index('+') + 1:] if '+' in scheme else 'http'
# Transform URI with new path and scheme
uri = join_uri(uri, 'plugins/httprpc/action.php', construct=False)
return update_uri(uri, scheme=transport)
return uri
def _update_rpc_methods(self):
self._rpc_methods = self.client.system.listMethods()
return self._rpc_methods