[Zope] XML-RPC and binary data

Kyler Laird Kyler@Lairds.com
Mon, 9 Sep 2002 14:32:45 -0500


[I'm copying this to the Zope list so that someone there
will be sure to correct any gross mistakes I make.]

Transfering binary data through XML-RPC does not seem to
be the problem that you described.  xmlrpclib handles it
fine.  Zope receives it fine.  It's just unusable once
it gets to Zope.

As a test, I created a script that made a Binary object
from a simple image
	foo = xmlrpclib.Binary(img.read())
and then called a test script on my Zope server.
	result = Zopesvr.test.xmlrpc(foo)
This worked o.k.  When I printed the string rendition of
what the script received, I got
	<xmlrpclib.Binary instance at 0x98420b4>

Looks good, no?  This is much better than we'd thought;
there is no need to manually base64 encode it on the
client.

However...it turns out that the Binary class in Zope's
xmlrpclib product is quite a kludge.
	#
	# binary data wrapper (NOTE: this is an extension to Userland's
	# XML-RPC protocol! only for use with compatible servers!)
	
	class Binary:
	
	    def __init__(self, data=None):
	        self.data = data
	
	    def decode(self, data):
	        import base64
	        self.data = base64.decodestring(data)
	
	    def encode(self, out):
	        import base64, StringIO
	        out.write("<value><base64>\n")
	        base64.encode(StringIO.StringIO(self.data), out)
	        out.write("</base64></value>\n")

The first thing to notice is that we don't know what type
of data is in a Binary object.  You just initialize it
with some data and then you either encode() or decode() it.
If it was initialized with encoded data and you encode() it
or it was initialized with decoded data and you decode() it,
you'll probably not get what you expected.

The more important deficiency in this class is that even if
you do figure out what kind of data is in there, there's no
way for you (as a normal user) to get at it.  Both decode()
and encode() want to stuff their results in a file object.
I do not know of any way you can get one in Zope.  It does
not matter though - you are not allowed to access them
anyway.

So...you're screwed.  You can't touch the data.  (No, you
can't just use a Binary object with manage_addFile.)

The quick and dirty solution is to add
	import AccessControl
	AccessControl.allow_class(Binary)
to xmlrpclib.py.  Then you can get straight at the "data"
of the Binary object.  (I just did this and passed it to
manage_addImage().  It worked fine.  The data was already
decoded.)

Ideally, you'd add some sane public methods to the Binary
class.  Perhaps "encoded()" and "decoded()" would be a
place to start.  They could return either strings or
StringIO objects.

Sorry I don't have a better solution, but it looks like
you're probably going to have to patch Zope if you don't
want to give up on XML-RPC and just use HTTP POST.  I
appreciate and share your desire to use XML-RPC with Zope,
but until we get Binary straightened out in Zope and basic
authentication added in Python, it just takes too many
kludges to recommend.

--kyler