5 changed files with 317 additions and 63 deletions
@ -0,0 +1,73 @@ |
|||
# |
|||
# Copyright (c) 2013 Dean Gardiner, <gardiner91@gmail.com> |
|||
# |
|||
# Permission is hereby granted, free of charge, to any person obtaining |
|||
# a copy of this software and associated documentation files (the |
|||
# "Software"), to deal in the Software without restriction, including |
|||
# without limitation the rights to use, copy, modify, merge, publish, |
|||
# distribute, sublicense, and/or sell copies of the Software, and to |
|||
# permit persons to whom the Software is furnished to do so, subject to |
|||
# the following conditions: |
|||
# |
|||
# The above copyright notice and this permission notice shall be |
|||
# included in all copies or substantial portions of the Software. |
|||
# |
|||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|||
|
|||
from base64 import encodestring |
|||
import string |
|||
import xmlrpclib |
|||
|
|||
|
|||
class BasicAuthTransport(xmlrpclib.Transport): |
|||
def __init__(self, username=None, password=None): |
|||
xmlrpclib.Transport.__init__(self) |
|||
|
|||
self.username = username |
|||
self.password = password |
|||
|
|||
def send_auth(self, h): |
|||
if self.username is not None and self.password is not None: |
|||
h.putheader('AUTHORIZATION', "Basic %s" % string.replace( |
|||
encodestring("%s:%s" % (self.username, self.password)), |
|||
"\012", "" |
|||
)) |
|||
|
|||
def single_request(self, host, handler, request_body, verbose=0): |
|||
# issue XML-RPC request |
|||
|
|||
h = self.make_connection(host) |
|||
if verbose: |
|||
h.set_debuglevel(1) |
|||
|
|||
try: |
|||
self.send_request(h, handler, request_body) |
|||
self.send_host(h, host) |
|||
self.send_user_agent(h) |
|||
self.send_auth(h) |
|||
self.send_content(h, request_body) |
|||
|
|||
response = h.getresponse(buffering=True) |
|||
if response.status == 200: |
|||
self.verbose = verbose |
|||
return self.parse_response(response) |
|||
except xmlrpclib.Fault: |
|||
raise |
|||
except Exception: |
|||
self.close() |
|||
raise |
|||
|
|||
#discard any response data and raise exception |
|||
if response.getheader("content-length", 0): |
|||
response.read() |
|||
raise xmlrpclib.ProtocolError( |
|||
host + handler, |
|||
response.status, response.reason, |
|||
response.msg, |
|||
) |
@ -0,0 +1,201 @@ |
|||
#!/usr/bin/python |
|||
|
|||
# rtorrent_xmlrpc |
|||
# (c) 2011 Roger Que <alerante@bellsouth.net> |
|||
# |
|||
# Python module for interacting with rtorrent's XML-RPC interface |
|||
# directly over SCGI, instead of through an HTTP server intermediary. |
|||
# Inspired by Glenn Washburn's xmlrpc2scgi.py [1], but subclasses the |
|||
# built-in xmlrpclib classes so that it is compatible with features |
|||
# such as MultiCall objects. |
|||
# |
|||
# [1] <http://libtorrent.rakshasa.no/wiki/UtilsXmlrpc2scgi> |
|||
# |
|||
# Usage: server = SCGIServerProxy('scgi://localhost:7000/') |
|||
# server = SCGIServerProxy('scgi:///path/to/scgi.sock') |
|||
# print server.system.listMethods() |
|||
# mc = xmlrpclib.MultiCall(server) |
|||
# mc.get_up_rate() |
|||
# mc.get_down_rate() |
|||
# print mc() |
|||
# |
|||
# |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 of the License, or |
|||
# (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with this program; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# In addition, as a special exception, the copyright holders give |
|||
# permission to link the code of portions of this program with the |
|||
# OpenSSL library under certain conditions as described in each |
|||
# individual source file, and distribute linked combinations |
|||
# including the two. |
|||
# |
|||
# You must obey the GNU General Public License in all respects for |
|||
# all of the code used other than OpenSSL. If you modify file(s) |
|||
# with this exception, you may extend this exception to your version |
|||
# of the file(s), but you are not obligated to do so. If you do not |
|||
# wish to do so, delete this exception statement from your version. |
|||
# If you delete this exception statement from all source files in the |
|||
# program, then also delete it here. |
|||
# |
|||
# |
|||
# |
|||
# Portions based on Python's xmlrpclib: |
|||
# |
|||
# Copyright (c) 1999-2002 by Secret Labs AB |
|||
# Copyright (c) 1999-2002 by Fredrik Lundh |
|||
# |
|||
# By obtaining, using, and/or copying this software and/or its |
|||
# associated documentation, you agree that you have read, understood, |
|||
# and will comply with the following terms and conditions: |
|||
# |
|||
# Permission to use, copy, modify, and distribute this software and |
|||
# its associated documentation for any purpose and without fee is |
|||
# hereby granted, provided that the above copyright notice appears in |
|||
# all copies, and that both that copyright notice and this permission |
|||
# notice appear in supporting documentation, and that the name of |
|||
# Secret Labs AB or the author not be used in advertising or publicity |
|||
# pertaining to distribution of the software without specific, written |
|||
# prior permission. |
|||
# |
|||
# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD |
|||
# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- |
|||
# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR |
|||
# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY |
|||
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
|||
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS |
|||
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
|||
# OF THIS SOFTWARE. |
|||
|
|||
import re |
|||
import socket |
|||
import urllib |
|||
import xmlrpclib |
|||
|
|||
|
|||
class SCGITransport(xmlrpclib.Transport): |
|||
def single_request(self, host, handler, request_body, verbose=0): |
|||
# Add SCGI headers to the request. |
|||
headers = {'CONTENT_LENGTH': str(len(request_body)), 'SCGI': '1'} |
|||
header = '\x00'.join(('%s\x00%s' % item for item in headers.iteritems())) + '\x00' |
|||
header = '%d:%s' % (len(header), header) |
|||
request_body = '%s,%s' % (header, request_body) |
|||
|
|||
sock = None |
|||
|
|||
try: |
|||
if host: |
|||
host, port = urllib.splitport(host) |
|||
addrinfo = socket.getaddrinfo(host, port, socket.AF_INET, |
|||
socket.SOCK_STREAM) |
|||
sock = socket.socket(*addrinfo[0][:3]) |
|||
sock.connect(addrinfo[0][4]) |
|||
else: |
|||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
|||
sock.connect(handler) |
|||
|
|||
self.verbose = verbose |
|||
|
|||
sock.send(request_body) |
|||
return self.parse_response(sock.makefile()) |
|||
finally: |
|||
if sock: |
|||
sock.close() |
|||
|
|||
def parse_response(self, response): |
|||
p, u = self.getparser() |
|||
|
|||
response_body = '' |
|||
while True: |
|||
data = response.read(1024) |
|||
if not data: |
|||
break |
|||
response_body += data |
|||
|
|||
# Remove SCGI headers from the response. |
|||
response_header, response_body = re.split(r'\n\s*?\n', response_body, |
|||
maxsplit=1) |
|||
|
|||
if self.verbose: |
|||
print 'body:', repr(response_body) |
|||
|
|||
p.feed(response_body) |
|||
p.close() |
|||
|
|||
return u.close() |
|||
|
|||
|
|||
class SCGIServerProxy(xmlrpclib.ServerProxy): |
|||
def __init__(self, uri, transport=None, encoding=None, verbose=False, |
|||
allow_none=False, use_datetime=False): |
|||
type, uri = urllib.splittype(uri) |
|||
if type not in ('scgi'): |
|||
raise IOError('unsupported XML-RPC protocol') |
|||
self.__host, self.__handler = urllib.splithost(uri) |
|||
if not self.__handler: |
|||
self.__handler = '/' |
|||
|
|||
if transport is None: |
|||
transport = SCGITransport(use_datetime=use_datetime) |
|||
self.__transport = transport |
|||
|
|||
self.__encoding = encoding |
|||
self.__verbose = verbose |
|||
self.__allow_none = allow_none |
|||
|
|||
def __close(self): |
|||
self.__transport.close() |
|||
|
|||
def __request(self, methodname, params): |
|||
# call a method on the remote server |
|||
|
|||
request = xmlrpclib.dumps(params, methodname, encoding=self.__encoding, |
|||
allow_none=self.__allow_none) |
|||
|
|||
response = self.__transport.request( |
|||
self.__host, |
|||
self.__handler, |
|||
request, |
|||
verbose=self.__verbose |
|||
) |
|||
|
|||
if len(response) == 1: |
|||
response = response[0] |
|||
|
|||
return response |
|||
|
|||
def __repr__(self): |
|||
return ( |
|||
"<SCGIServerProxy for %s%s>" % |
|||
(self.__host, self.__handler) |
|||
) |
|||
|
|||
__str__ = __repr__ |
|||
|
|||
def __getattr__(self, name): |
|||
# magic method dispatcher |
|||
return xmlrpclib._Method(self.__request, name) |
|||
|
|||
# note: to call a remote object with an non-standard name, use |
|||
# result getattr(server, "strange-python-name")(args) |
|||
|
|||
def __call__(self, attr): |
|||
"""A workaround to get special attributes on the ServerProxy |
|||
without interfering with the magic __getattr__ |
|||
""" |
|||
if attr == "close": |
|||
return self.__close |
|||
elif attr == "transport": |
|||
return self.__transport |
|||
raise AttributeError("Attribute %r not found" % (attr,)) |
Loading…
Reference in new issue