mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-01 00:43:37 +00:00
0d9fbc1ad7
This version of SickBeard uses both TVDB and TVRage to search and gather it's series data from allowing you to now have access to and download shows that you couldn't before because of being locked into only what TheTVDB had to offer. Also this edition is based off the code we used in our XEM editon so it does come with scene numbering support as well as all the other features our XEM edition has to offer. Please before using this with your existing database (sickbeard.db) please make a backup copy of it and delete any other database files such as cache.db and failed.db if present, we HIGHLY recommend starting out with no database files at all to make this a fresh start but the choice is at your own risk! Enjoy!
229 lines
8.5 KiB
Python
229 lines
8.5 KiB
Python
import lib.jsonrpclib
|
|
from lib.jsonrpclib import Fault
|
|
from lib.jsonrpclib.jsonrpc import USE_UNIX_SOCKETS
|
|
import SimpleXMLRPCServer
|
|
import SocketServer
|
|
import socket
|
|
import logging
|
|
import os
|
|
import types
|
|
import traceback
|
|
import sys
|
|
try:
|
|
import fcntl
|
|
except ImportError:
|
|
# For Windows
|
|
fcntl = None
|
|
|
|
def get_version(request):
|
|
# must be a dict
|
|
if 'jsonrpc' in request.keys():
|
|
return 2.0
|
|
if 'id' in request.keys():
|
|
return 1.0
|
|
return None
|
|
|
|
def validate_request(request):
|
|
if type(request) is not types.DictType:
|
|
fault = Fault(
|
|
-32600, 'Request must be {}, not %s.' % type(request)
|
|
)
|
|
return fault
|
|
rpcid = request.get('id', None)
|
|
version = get_version(request)
|
|
if not version:
|
|
fault = Fault(-32600, 'Request %s invalid.' % request, rpcid=rpcid)
|
|
return fault
|
|
request.setdefault('params', [])
|
|
method = request.get('method', None)
|
|
params = request.get('params')
|
|
param_types = (types.ListType, types.DictType, types.TupleType)
|
|
if not method or type(method) not in types.StringTypes or \
|
|
type(params) not in param_types:
|
|
fault = Fault(
|
|
-32600, 'Invalid request parameters or method.', rpcid=rpcid
|
|
)
|
|
return fault
|
|
return True
|
|
|
|
class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
|
|
|
|
def __init__(self, encoding=None):
|
|
SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,
|
|
allow_none=True,
|
|
encoding=encoding)
|
|
|
|
def _marshaled_dispatch(self, data, dispatch_method = None):
|
|
response = None
|
|
try:
|
|
request = jsonrpclib.loads(data)
|
|
except Exception, e:
|
|
fault = Fault(-32700, 'Request %s invalid. (%s)' % (data, e))
|
|
response = fault.response()
|
|
return response
|
|
if not request:
|
|
fault = Fault(-32600, 'Request invalid -- no request data.')
|
|
return fault.response()
|
|
if type(request) is types.ListType:
|
|
# This SHOULD be a batch, by spec
|
|
responses = []
|
|
for req_entry in request:
|
|
result = validate_request(req_entry)
|
|
if type(result) is Fault:
|
|
responses.append(result.response())
|
|
continue
|
|
resp_entry = self._marshaled_single_dispatch(req_entry)
|
|
if resp_entry is not None:
|
|
responses.append(resp_entry)
|
|
if len(responses) > 0:
|
|
response = '[%s]' % ','.join(responses)
|
|
else:
|
|
response = ''
|
|
else:
|
|
result = validate_request(request)
|
|
if type(result) is Fault:
|
|
return result.response()
|
|
response = self._marshaled_single_dispatch(request)
|
|
return response
|
|
|
|
def _marshaled_single_dispatch(self, request):
|
|
# TODO - Use the multiprocessing and skip the response if
|
|
# it is a notification
|
|
# Put in support for custom dispatcher here
|
|
# (See SimpleXMLRPCServer._marshaled_dispatch)
|
|
method = request.get('method')
|
|
params = request.get('params')
|
|
try:
|
|
response = self._dispatch(method, params)
|
|
except:
|
|
exc_type, exc_value, exc_tb = sys.exc_info()
|
|
fault = Fault(-32603, '%s:%s' % (exc_type, exc_value))
|
|
return fault.response()
|
|
if 'id' not in request.keys() or request['id'] == None:
|
|
# It's a notification
|
|
return None
|
|
try:
|
|
response = jsonrpclib.dumps(response,
|
|
methodresponse=True,
|
|
rpcid=request['id']
|
|
)
|
|
return response
|
|
except:
|
|
exc_type, exc_value, exc_tb = sys.exc_info()
|
|
fault = Fault(-32603, '%s:%s' % (exc_type, exc_value))
|
|
return fault.response()
|
|
|
|
def _dispatch(self, method, params):
|
|
func = None
|
|
try:
|
|
func = self.funcs[method]
|
|
except KeyError:
|
|
if self.instance is not None:
|
|
if hasattr(self.instance, '_dispatch'):
|
|
return self.instance._dispatch(method, params)
|
|
else:
|
|
try:
|
|
func = SimpleXMLRPCServer.resolve_dotted_attribute(
|
|
self.instance,
|
|
method,
|
|
True
|
|
)
|
|
except AttributeError:
|
|
pass
|
|
if func is not None:
|
|
try:
|
|
if type(params) is types.ListType:
|
|
response = func(*params)
|
|
else:
|
|
response = func(**params)
|
|
return response
|
|
except TypeError:
|
|
return Fault(-32602, 'Invalid parameters.')
|
|
except:
|
|
err_lines = traceback.format_exc().splitlines()
|
|
trace_string = '%s | %s' % (err_lines[-3], err_lines[-1])
|
|
fault = jsonrpclib.Fault(-32603, 'Server error: %s' %
|
|
trace_string)
|
|
return fault
|
|
else:
|
|
return Fault(-32601, 'Method %s not supported.' % method)
|
|
|
|
class SimpleJSONRPCRequestHandler(
|
|
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
|
|
|
|
def do_POST(self):
|
|
if not self.is_rpc_path_valid():
|
|
self.report_404()
|
|
return
|
|
try:
|
|
max_chunk_size = 10*1024*1024
|
|
size_remaining = int(self.headers["content-length"])
|
|
L = []
|
|
while size_remaining:
|
|
chunk_size = min(size_remaining, max_chunk_size)
|
|
L.append(self.rfile.read(chunk_size))
|
|
size_remaining -= len(L[-1])
|
|
data = ''.join(L)
|
|
response = self.server._marshaled_dispatch(data)
|
|
self.send_response(200)
|
|
except Exception, e:
|
|
self.send_response(500)
|
|
err_lines = traceback.format_exc().splitlines()
|
|
trace_string = '%s | %s' % (err_lines[-3], err_lines[-1])
|
|
fault = jsonrpclib.Fault(-32603, 'Server error: %s' % trace_string)
|
|
response = fault.response()
|
|
if response == None:
|
|
response = ''
|
|
self.send_header("Content-type", "application/json-rpc")
|
|
self.send_header("Content-length", str(len(response)))
|
|
self.end_headers()
|
|
self.wfile.write(response)
|
|
self.wfile.flush()
|
|
self.connection.shutdown(1)
|
|
|
|
class SimpleJSONRPCServer(SocketServer.TCPServer, SimpleJSONRPCDispatcher):
|
|
|
|
allow_reuse_address = True
|
|
|
|
def __init__(self, addr, requestHandler=SimpleJSONRPCRequestHandler,
|
|
logRequests=True, encoding=None, bind_and_activate=True,
|
|
address_family=socket.AF_INET):
|
|
self.logRequests = logRequests
|
|
SimpleJSONRPCDispatcher.__init__(self, encoding)
|
|
# TCPServer.__init__ has an extra parameter on 2.6+, so
|
|
# check Python version and decide on how to call it
|
|
vi = sys.version_info
|
|
self.address_family = address_family
|
|
if USE_UNIX_SOCKETS and address_family == socket.AF_UNIX:
|
|
# Unix sockets can't be bound if they already exist in the
|
|
# filesystem. The convention of e.g. X11 is to unlink
|
|
# before binding again.
|
|
if os.path.exists(addr):
|
|
try:
|
|
os.unlink(addr)
|
|
except OSError:
|
|
logging.warning("Could not unlink socket %s", addr)
|
|
# if python 2.5 and lower
|
|
if vi[0] < 3 and vi[1] < 6:
|
|
SocketServer.TCPServer.__init__(self, addr, requestHandler)
|
|
else:
|
|
SocketServer.TCPServer.__init__(self, addr, requestHandler,
|
|
bind_and_activate)
|
|
if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
|
|
flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
|
|
flags |= fcntl.FD_CLOEXEC
|
|
fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
|
|
|
|
class CGIJSONRPCRequestHandler(SimpleJSONRPCDispatcher):
|
|
|
|
def __init__(self, encoding=None):
|
|
SimpleJSONRPCDispatcher.__init__(self, encoding)
|
|
|
|
def handle_jsonrpc(self, request_text):
|
|
response = self._marshaled_dispatch(request_text)
|
|
print 'Content-Type: application/json-rpc'
|
|
print 'Content-Length: %d' % len(response)
|
|
print
|
|
sys.stdout.write(response)
|
|
|
|
handle_xmlrpc = handle_jsonrpc
|