1 #!/usr/bin/env python 2 3 """ 4 XML-RPC support using libxml2dom. 5 6 See: http://www.xmlrpc.com/spec 7 8 Copyright (C) 2007 Paul Boddie <paul@boddie.org.uk> 9 10 This program is free software; you can redistribute it and/or modify it under 11 the terms of the GNU Lesser General Public License as published by the Free 12 Software Foundation; either version 3 of the License, or (at your option) any 13 later version. 14 15 This program is distributed in the hope that it will be useful, but WITHOUT 16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 17 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 18 details. 19 20 You should have received a copy of the GNU Lesser General Public License along 21 with this program. If not, see <http://www.gnu.org/licenses/>. 22 23 -------- 24 25 The sending and receiving of XML-RPC messages can be done using traditional HTTP 26 libraries. 27 28 See tests/xmlrpc_test.py for more details. 29 """ 30 31 import libxml2dom 32 from libxml2dom.macrolib import * 33 from libxml2dom.macrolib import \ 34 createDocument as Node_createDocument 35 36 class XMLRPCImplementation(libxml2dom.Implementation): 37 38 "Contains an XML-RPC-specific implementation." 39 40 # Wrapping of documents. 41 42 def adoptDocument(self, node): 43 return XMLRPCDocument(node, self) 44 45 # Factory functions. 46 47 def get_node(self, _node, context_node): 48 49 """ 50 Get a libxml2dom node for the given low-level '_node' and libxml2dom 51 'context_node'. 52 """ 53 54 if Node_nodeType(_node) == context_node.ELEMENT_NODE: 55 56 # Make special elements. 57 58 if Node_localName(_node) in ("methodCall", "methodResponse"): 59 return XMLRPCMethodElement(_node, self, context_node.ownerDocument) 60 elif Node_localName(_node) == "methodName": 61 return XMLRPCMethodNameElement(_node, self, context_node.ownerDocument) 62 elif Node_localName(_node) == "fault": 63 return XMLRPCFaultElement(_node, self, context_node.ownerDocument) 64 elif Node_localName(_node) == "string": 65 return XMLRPCStringElement(_node, self, context_node.ownerDocument) 66 elif Node_localName(_node) in ("int", "i4"): 67 return XMLRPCIntegerElement(_node, self, context_node.ownerDocument) 68 elif Node_localName(_node) == "boolean": 69 return XMLRPCBooleanElement(_node, self, context_node.ownerDocument) 70 elif Node_localName(_node) == "double": 71 return XMLRPCDoubleElement(_node, self, context_node.ownerDocument) 72 elif Node_localName(_node) == "dateTime.iso8601": 73 return XMLRPCDateTimeElement(_node, self, context_node.ownerDocument) 74 elif Node_localName(_node) == "base64": 75 return XMLRPCBase64Element(_node, self, context_node.ownerDocument) 76 elif Node_localName(_node) == "struct": 77 return XMLRPCStructElement(_node, self, context_node.ownerDocument) 78 elif Node_localName(_node) == "member": 79 return XMLRPCMemberElement(_node, self, context_node.ownerDocument) 80 81 # Otherwise, make generic XML-RPC elements. 82 83 return XMLRPCElement(_node, self, context_node.ownerDocument) 84 85 else: 86 return libxml2dom.Implementation.get_node(self, _node, context_node) 87 88 # Convenience functions. 89 90 def createXMLRPCMessage(self, namespaceURI, localName): 91 92 "Create a new XML-RPC message document (fragment)." 93 94 return XMLRPCDocument(Node_createDocument(namespaceURI, localName, None), self).documentElement 95 96 # Node classes. 97 98 class XMLRPCNode(libxml2dom.Node): 99 100 "Convenience modifications to nodes specific to libxml2dom.xmlrpc." 101 102 pass 103 104 class XMLRPCDocument(libxml2dom._Document, XMLRPCNode): 105 106 "An XML-RPC document fragment." 107 108 def _method(self): 109 return self.xpath("./methodCall|./methodResponse")[0] 110 111 method = property(_method) 112 113 class XMLRPCElement(XMLRPCNode): 114 115 "An XML-RPC element." 116 117 pass 118 119 class XMLRPCMethodElement(XMLRPCNode): 120 121 "An XML-RPC method element." 122 123 def _fault(self): 124 return self.xpath("./fault")[0] 125 126 def _methodName(self): 127 return self.xpath("./methodName")[0] 128 129 def _parameters(self): 130 return self.xpath("./params/param/value//*[not(./*)]") 131 132 def _parameterValues(self): 133 values = {} 134 for parameter in self.parameters: 135 values[(parameter.namespaceURI, parameter.localName)] = parameter.textContent.strip() 136 return values 137 138 def createFault(self): 139 return self.ownerDocument.createElement("fault") 140 141 fault = property(_fault) 142 methodName = property(_methodName) 143 parameters = property(_parameters) 144 parameterValues = property(_parameterValues) 145 146 class XMLRPCStringElement(XMLRPCNode): 147 148 "An XML-RPC string element." 149 150 def _value(self): 151 return self.textContent.strip() 152 153 def _setValue(self, value): 154 for node in self.childNodes: 155 self.removeChild(node) 156 text = self.ownerDocument.createTextNode(value) 157 self.appendChild(text) 158 159 value = property(_value, _setValue) 160 161 class XMLRPCMethodNameElement(XMLRPCStringElement): 162 163 "An XML-RPC method element." 164 165 pass 166 167 class XMLRPCIntegerElement(XMLRPCStringElement): 168 169 "An XML-RPC integer element." 170 171 pass 172 173 class XMLRPCBooleanElement(XMLRPCStringElement): 174 175 "An XML-RPC boolean element." 176 177 pass 178 179 class XMLRPCDoubleElement(XMLRPCStringElement): 180 181 "An XML-RPC double floating point number element." 182 183 pass 184 185 class XMLRPCDateTimeElement(XMLRPCStringElement): 186 187 "An XML-RPC date/time element." 188 189 pass 190 191 class XMLRPCBase64Element(XMLRPCStringElement): 192 193 "An XML-RPC integer element." 194 195 pass 196 197 class XMLRPCStructElement(XMLRPCNode): 198 199 "An XML-RPC structure element." 200 201 def _members(self): 202 return self.xpath("./member") 203 204 members = property(_members) 205 206 class XMLRPCMemberElement(XMLRPCNode): 207 208 "An XML-RPC structure member element." 209 210 def _name(self): 211 return self.xpath("./name")[0].textContent.strip() 212 213 def _value(self): 214 return self.xpath("./value")[0] 215 216 name = property(_name) 217 value = property(_value) 218 219 class XMLRPCFaultElement(XMLRPCNode): 220 221 "An XML-RPC fault element." 222 223 def _code(self): 224 return self.xpath("./value/struct/member[./name/text() = 'faultCode']/value/int")[0] 225 226 def _reason(self): 227 return self.xpath("./value/struct/member[./name/text() = 'faultString']/value/string")[0] 228 229 code = property(_code) 230 reason = property(_reason) 231 232 # Utility functions. 233 234 createDocument = libxml2dom.createDocument 235 createDocumentType = libxml2dom.createDocumentType 236 237 def createXMLRPCMessage(namespaceURI, localName): 238 return default_impl.createXMLRPCMessage(namespaceURI, localName) 239 240 def parse(stream_or_string, html=0, htmlencoding=None, unfinished=0, impl=None): 241 return libxml2dom.parse(stream_or_string, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) 242 243 def parseFile(filename, html=0, htmlencoding=None, unfinished=0, impl=None): 244 return libxml2dom.parseFile(filename, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) 245 246 def parseString(s, html=0, htmlencoding=None, unfinished=0, impl=None): 247 return libxml2dom.parseString(s, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) 248 249 def parseURI(uri, html=0, htmlencoding=None, unfinished=0, impl=None): 250 return libxml2dom.parseURI(uri, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) 251 252 # Single instance of the implementation. 253 254 default_impl = XMLRPCImplementation() 255 256 # vim: tabstop=4 expandtab shiftwidth=4