Problem:
I want to send JSON-RPC commands to XBMC on my Raspberry Pi. The XBMC install on there will allow JSON-RPC commands from the local box or a remote box, but I've passworded it. The easiest way to talk to the RPC interface through python appears to be using the python-jsonrpc library from here. The problem is that there's no way to send a login and a password.Investigation:
Here's some simple code that should work (if passwords weren't turned on):
from jsonrpc import ServiceProxy
s = ServiceProxy("http://127.0.0.1:8000/jsonrpc")
print s.GUI.ShowNotification("Hello!","This is a message")
if the library supported login/password combos in urls, I could do this:
from jsonrpc import ServiceProxy
s = ServiceProxy("http://user:password@127.0.0.1:8000/jsonrpc")
print s.GUI.ShowNotification("Hello!","This is a message")
..but it doesn't. If I try it that way the library fails to get the address correctly (probably it's trying to do a DNS search for the string "user:password@127.0.0.1" instead of realising that "127.0.0.1" is the machine to connect to). If I don't put in the login/password combo, it gives me a 401 authentication refused - which is fair enough.
The solution appears to be to use urllib2, which has both basic authentication support and allows you to add headers. I can modify the file python-jsonrpc/jsonrpc/proxy.py in the python-jsonrpc download, run setup.py install again, and bob's your uncle!
...not so fast. For some reason the Basic Authentication in urllib2 isn't working for me. I have no idea why, but it isn't. The password managers aren't triggering, so that the request gets made without authentication and fails with a 401 still.
A little googling throws up this solution - ignore the basic auth support in urllib2, and just use the support for adding headers. Basic auth is just a header "Authorization: Basic <HASH>" ...where <HASH> is login:password base64-encoded. Modifications made, result success!
Solution:
Replacement PYTHON-JSONRPC/JSONRPC/PROXY.PY:
"""
Copyright (c) 2007 Jan-Klaas Kollhof
Modified 2013 Keith Lawrence
This file is part of jsonrpc.
jsonrpc is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This software 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this software; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
import urllib2
import base64
from jsonrpc.json import dumps, loads
class JSONRPCException(Exception):
def __init__(self, rpcError):
Exception.__init__(self)
self.error = rpcError
class ServiceProxy(object):
def __init__(self, serviceURL, serviceName=None, serviceUser=None, servicePassword=None):
self.__serviceURL = serviceURL
self.__serviceName = serviceName
self.__serviceUser = serviceUser
self.__servicePassword = servicePassword
if serviceUser != None:
self.__serviceAuth = base64.standard_b64encode('%s:%s' % (serviceUser,servicePassword))
def __getattr__(self, name):
if self.__serviceName != None:
name = "%s.%s" % (self.__serviceName, name)
return ServiceProxy(self.__serviceURL, name, self.__serviceUser, self.__servicePassword)
def __call__(self, *args):
postdata = dumps({'jsonrpc': '2.0', 'method': self.__serviceName, 'params': args, 'id':'jsonrpc'})
# Comment in following two lines to add header tracing
# opener=urllib2.build_opener(urllib2.HTTPHandler(debuglevel=1))
# urllib2.install_opener(opener)
request = urllib2.Request(url=self.__serviceURL)
request.add_data(postdata)
request.add_header('Content-Type', 'application/json')
if self.__serviceAuth != None:
request.add_header("Authorization", "Basic %s" % self.__serviceAuth)
respdata = urllib2.urlopen(request).read()
resp = loads(respdata)
if 'error' in resp:
raise JSONRPCException(resp['error'])
else:
return resp['result']
Comments
Post a Comment