1#
2# XML-RPC CLIENT LIBRARY
3# $Id$
4#
5# an XML-RPC client interface for Python.
6#
7# the marshalling and response parser code can also be used to
8# implement XML-RPC servers.
9#
10# Notes:
11# this version is designed to work with Python 2.1 or newer.
12#
13# History:
14# 1999-01-14 fl  Created
15# 1999-01-15 fl  Changed dateTime to use localtime
16# 1999-01-16 fl  Added Binary/base64 element, default to RPC2 service
17# 1999-01-19 fl  Fixed array data element (from Skip Montanaro)
18# 1999-01-21 fl  Fixed dateTime constructor, etc.
19# 1999-02-02 fl  Added fault handling, handle empty sequences, etc.
20# 1999-02-10 fl  Fixed problem with empty responses (from Skip Montanaro)
21# 1999-06-20 fl  Speed improvements, pluggable parsers/transports (0.9.8)
22# 2000-11-28 fl  Changed boolean to check the truth value of its argument
23# 2001-02-24 fl  Added encoding/Unicode/SafeTransport patches
24# 2001-02-26 fl  Added compare support to wrappers (0.9.9/1.0b1)
25# 2001-03-28 fl  Make sure response tuple is a singleton
26# 2001-03-29 fl  Don't require empty params element (from Nicholas Riley)
27# 2001-06-10 fl  Folded in _xmlrpclib accelerator support (1.0b2)
28# 2001-08-20 fl  Base xmlrpclib.Error on built-in Exception (from Paul Prescod)
29# 2001-09-03 fl  Allow Transport subclass to override getparser
30# 2001-09-10 fl  Lazy import of urllib, cgi, xmllib (20x import speedup)
31# 2001-10-01 fl  Remove containers from memo cache when done with them
32# 2001-10-01 fl  Use faster escape method (80% dumps speedup)
33# 2001-10-02 fl  More dumps microtuning
34# 2001-10-04 fl  Make sure import expat gets a parser (from Guido van Rossum)
35# 2001-10-10 sm  Allow long ints to be passed as ints if they don't overflow
36# 2001-10-17 sm  Test for int and long overflow (allows use on 64-bit systems)
37# 2001-11-12 fl  Use repr() to marshal doubles (from Paul Felix)
38# 2002-03-17 fl  Avoid buffered read when possible (from James Rucker)
39# 2002-04-07 fl  Added pythondoc comments
40# 2002-04-16 fl  Added __str__ methods to datetime/binary wrappers
41# 2002-05-15 fl  Added error constants (from Andrew Kuchling)
42# 2002-06-27 fl  Merged with Python CVS version
43# 2002-10-22 fl  Added basic authentication (based on code from Phillip Eby)
44# 2003-01-22 sm  Add support for the bool type
45# 2003-02-27 gvr Remove apply calls
46# 2003-04-24 sm  Use cStringIO if available
47# 2003-04-25 ak  Add support for nil
48# 2003-06-15 gn  Add support for time.struct_time
49# 2003-07-12 gp  Correct marshalling of Faults
50# 2003-10-31 mvl Add multicall support
51# 2004-08-20 mvl Bump minimum supported Python version to 2.1
52#
53# Copyright (c) 1999-2002 by Secret Labs AB.
54# Copyright (c) 1999-2002 by Fredrik Lundh.
55#
56# info@pythonware.com
57# http://www.pythonware.com
58#
59# --------------------------------------------------------------------
60# The XML-RPC client interface is
61#
62# Copyright (c) 1999-2002 by Secret Labs AB
63# Copyright (c) 1999-2002 by Fredrik Lundh
64#
65# By obtaining, using, and/or copying this software and/or its
66# associated documentation, you agree that you have read, understood,
67# and will comply with the following terms and conditions:
68#
69# Permission to use, copy, modify, and distribute this software and
70# its associated documentation for any purpose and without fee is
71# hereby granted, provided that the above copyright notice appears in
72# all copies, and that both that copyright notice and this permission
73# notice appear in supporting documentation, and that the name of
74# Secret Labs AB or the author not be used in advertising or publicity
75# pertaining to distribution of the software without specific, written
76# prior permission.
77#
78# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
79# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
80# ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
81# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
82# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
83# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
84# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
85# OF THIS SOFTWARE.
86# --------------------------------------------------------------------
87
88"""
89Ported using Python-Future from the Python 3.3 standard library.
90
91An XML-RPC client interface for Python.
92
93The marshalling and response parser code can also be used to
94implement XML-RPC servers.
95
96Exported exceptions:
97
98  Error          Base class for client errors
99  ProtocolError  Indicates an HTTP protocol error
100  ResponseError  Indicates a broken response package
101  Fault          Indicates an XML-RPC fault package
102
103Exported classes:
104
105  ServerProxy    Represents a logical connection to an XML-RPC server
106
107  MultiCall      Executor of boxcared xmlrpc requests
108  DateTime       dateTime wrapper for an ISO 8601 string or time tuple or
109                 localtime integer value to generate a "dateTime.iso8601"
110                 XML-RPC value
111  Binary         binary data wrapper
112
113  Marshaller     Generate an XML-RPC params chunk from a Python data structure
114  Unmarshaller   Unmarshal an XML-RPC response from incoming XML event message
115  Transport      Handles an HTTP transaction to an XML-RPC server
116  SafeTransport  Handles an HTTPS transaction to an XML-RPC server
117
118Exported constants:
119
120  (none)
121
122Exported functions:
123
124  getparser      Create instance of the fastest available parser & attach
125                 to an unmarshalling object
126  dumps          Convert an argument tuple or a Fault instance to an XML-RPC
127                 request (or response, if the methodresponse option is used).
128  loads          Convert an XML-RPC packet to unmarshalled data plus a method
129                 name (None if not present).
130"""
131
132from __future__ import (absolute_import, division, print_function,
133                        unicode_literals)
134from future.builtins import bytes, dict, int, range, str
135
136import base64
137# Py2.7 compatibility hack
138base64.encodebytes = base64.encodestring
139base64.decodebytes = base64.decodestring
140import sys
141import time
142from datetime import datetime
143from future.backports.http import client as http_client
144from future.backports.urllib import parse as urllib_parse
145from future.utils import ensure_new_type
146from xml.parsers import expat
147import socket
148import errno
149from io import BytesIO
150try:
151    import gzip
152except ImportError:
153    gzip = None #python can be built without zlib/gzip support
154
155# --------------------------------------------------------------------
156# Internal stuff
157
158def escape(s):
159    s = s.replace("&", "&")
160    s = s.replace("<", "&lt;")
161    return s.replace(">", "&gt;",)
162
163# used in User-Agent header sent
164__version__ = sys.version[:3]
165
166# xmlrpc integer limits
167MAXINT =  2**31-1
168MININT = -2**31
169
170# --------------------------------------------------------------------
171# Error constants (from Dan Libby's specification at
172# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
173
174# Ranges of errors
175PARSE_ERROR       = -32700
176SERVER_ERROR      = -32600
177APPLICATION_ERROR = -32500
178SYSTEM_ERROR      = -32400
179TRANSPORT_ERROR   = -32300
180
181# Specific errors
182NOT_WELLFORMED_ERROR  = -32700
183UNSUPPORTED_ENCODING  = -32701
184INVALID_ENCODING_CHAR = -32702
185INVALID_XMLRPC        = -32600
186METHOD_NOT_FOUND      = -32601
187INVALID_METHOD_PARAMS = -32602
188INTERNAL_ERROR        = -32603
189
190# --------------------------------------------------------------------
191# Exceptions
192
193##
194# Base class for all kinds of client-side errors.
195
196class Error(Exception):
197    """Base class for client errors."""
198    def __str__(self):
199        return repr(self)
200
201##
202# Indicates an HTTP-level protocol error.  This is raised by the HTTP
203# transport layer, if the server returns an error code other than 200
204# (OK).
205#
206# @param url The target URL.
207# @param errcode The HTTP error code.
208# @param errmsg The HTTP error message.
209# @param headers The HTTP header dictionary.
210
211class ProtocolError(Error):
212    """Indicates an HTTP protocol error."""
213    def __init__(self, url, errcode, errmsg, headers):
214        Error.__init__(self)
215        self.url = url
216        self.errcode = errcode
217        self.errmsg = errmsg
218        self.headers = headers
219    def __repr__(self):
220        return (
221            "<ProtocolError for %s: %s %s>" %
222            (self.url, self.errcode, self.errmsg)
223            )
224
225##
226# Indicates a broken XML-RPC response package.  This exception is
227# raised by the unmarshalling layer, if the XML-RPC response is
228# malformed.
229
230class ResponseError(Error):
231    """Indicates a broken response package."""
232    pass
233
234##
235# Indicates an XML-RPC fault response package.  This exception is
236# raised by the unmarshalling layer, if the XML-RPC response contains
237# a fault string.  This exception can also be used as a class, to
238# generate a fault XML-RPC message.
239#
240# @param faultCode The XML-RPC fault code.
241# @param faultString The XML-RPC fault string.
242
243class Fault(Error):
244    """Indicates an XML-RPC fault package."""
245    def __init__(self, faultCode, faultString, **extra):
246        Error.__init__(self)
247        self.faultCode = faultCode
248        self.faultString = faultString
249    def __repr__(self):
250        return "<Fault %s: %r>" % (ensure_new_type(self.faultCode),
251                                   ensure_new_type(self.faultString))
252
253# --------------------------------------------------------------------
254# Special values
255
256##
257# Backwards compatibility
258
259boolean = Boolean = bool
260
261##
262# Wrapper for XML-RPC DateTime values.  This converts a time value to
263# the format used by XML-RPC.
264# <p>
265# The value can be given as a datetime object, as a string in the
266# format "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
267# time.localtime()), or an integer value (as returned by time.time()).
268# The wrapper uses time.localtime() to convert an integer to a time
269# tuple.
270#
271# @param value The time, given as a datetime object, an ISO 8601 string,
272#              a time tuple, or an integer time value.
273
274
275### For Python-Future:
276def _iso8601_format(value):
277    return "%04d%02d%02dT%02d:%02d:%02d" % (
278                value.year, value.month, value.day,
279                value.hour, value.minute, value.second)
280###
281# Issue #13305: different format codes across platforms
282# _day0 = datetime(1, 1, 1)
283# if _day0.strftime('%Y') == '0001':      # Mac OS X
284#     def _iso8601_format(value):
285#         return value.strftime("%Y%m%dT%H:%M:%S")
286# elif _day0.strftime('%4Y') == '0001':   # Linux
287#     def _iso8601_format(value):
288#         return value.strftime("%4Y%m%dT%H:%M:%S")
289# else:
290#     def _iso8601_format(value):
291#         return value.strftime("%Y%m%dT%H:%M:%S").zfill(17)
292# del _day0
293
294
295def _strftime(value):
296    if isinstance(value, datetime):
297        return _iso8601_format(value)
298
299    if not isinstance(value, (tuple, time.struct_time)):
300        if value == 0:
301            value = time.time()
302        value = time.localtime(value)
303
304    return "%04d%02d%02dT%02d:%02d:%02d" % value[:6]
305
306class DateTime(object):
307    """DateTime wrapper for an ISO 8601 string or time tuple or
308    localtime integer value to generate 'dateTime.iso8601' XML-RPC
309    value.
310    """
311
312    def __init__(self, value=0):
313        if isinstance(value, str):
314            self.value = value
315        else:
316            self.value = _strftime(value)
317
318    def make_comparable(self, other):
319        if isinstance(other, DateTime):
320            s = self.value
321            o = other.value
322        elif isinstance(other, datetime):
323            s = self.value
324            o = _iso8601_format(other)
325        elif isinstance(other, str):
326            s = self.value
327            o = other
328        elif hasattr(other, "timetuple"):
329            s = self.timetuple()
330            o = other.timetuple()
331        else:
332            otype = (hasattr(other, "__class__")
333                     and other.__class__.__name__
334                     or type(other))
335            raise TypeError("Can't compare %s and %s" %
336                            (self.__class__.__name__, otype))
337        return s, o
338
339    def __lt__(self, other):
340        s, o = self.make_comparable(other)
341        return s < o
342
343    def __le__(self, other):
344        s, o = self.make_comparable(other)
345        return s <= o
346
347    def __gt__(self, other):
348        s, o = self.make_comparable(other)
349        return s > o
350
351    def __ge__(self, other):
352        s, o = self.make_comparable(other)
353        return s >= o
354
355    def __eq__(self, other):
356        s, o = self.make_comparable(other)
357        return s == o
358
359    def __ne__(self, other):
360        s, o = self.make_comparable(other)
361        return s != o
362
363    def timetuple(self):
364        return time.strptime(self.value, "%Y%m%dT%H:%M:%S")
365
366    ##
367    # Get date/time value.
368    #
369    # @return Date/time value, as an ISO 8601 string.
370
371    def __str__(self):
372        return self.value
373
374    def __repr__(self):
375        return "<DateTime %r at %x>" % (ensure_new_type(self.value), id(self))
376
377    def decode(self, data):
378        self.value = str(data).strip()
379
380    def encode(self, out):
381        out.write("<value><dateTime.iso8601>")
382        out.write(self.value)
383        out.write("</dateTime.iso8601></value>\n")
384
385def _datetime(data):
386    # decode xml element contents into a DateTime structure.
387    value = DateTime()
388    value.decode(data)
389    return value
390
391def _datetime_type(data):
392    return datetime.strptime(data, "%Y%m%dT%H:%M:%S")
393
394##
395# Wrapper for binary data.  This can be used to transport any kind
396# of binary data over XML-RPC, using BASE64 encoding.
397#
398# @param data An 8-bit string containing arbitrary data.
399
400class Binary(object):
401    """Wrapper for binary data."""
402
403    def __init__(self, data=None):
404        if data is None:
405            data = b""
406        else:
407            if not isinstance(data, (bytes, bytearray)):
408                raise TypeError("expected bytes or bytearray, not %s" %
409                                data.__class__.__name__)
410            data = bytes(data)  # Make a copy of the bytes!
411        self.data = data
412
413    ##
414    # Get buffer contents.
415    #
416    # @return Buffer contents, as an 8-bit string.
417
418    def __str__(self):
419        return str(self.data, "latin-1")  # XXX encoding?!
420
421    def __eq__(self, other):
422        if isinstance(other, Binary):
423            other = other.data
424        return self.data == other
425
426    def __ne__(self, other):
427        if isinstance(other, Binary):
428            other = other.data
429        return self.data != other
430
431    def decode(self, data):
432        self.data = base64.decodebytes(data)
433
434    def encode(self, out):
435        out.write("<value><base64>\n")
436        encoded = base64.encodebytes(self.data)
437        out.write(encoded.decode('ascii'))
438        out.write("</base64></value>\n")
439
440def _binary(data):
441    # decode xml element contents into a Binary structure
442    value = Binary()
443    value.decode(data)
444    return value
445
446WRAPPERS = (DateTime, Binary)
447
448# --------------------------------------------------------------------
449# XML parsers
450
451class ExpatParser(object):
452    # fast expat parser for Python 2.0 and later.
453    def __init__(self, target):
454        self._parser = parser = expat.ParserCreate(None, None)
455        self._target = target
456        parser.StartElementHandler = target.start
457        parser.EndElementHandler = target.end
458        parser.CharacterDataHandler = target.data
459        encoding = None
460        target.xml(encoding, None)
461
462    def feed(self, data):
463        self._parser.Parse(data, 0)
464
465    def close(self):
466        self._parser.Parse("", 1) # end of data
467        del self._target, self._parser # get rid of circular references
468
469# --------------------------------------------------------------------
470# XML-RPC marshalling and unmarshalling code
471
472##
473# XML-RPC marshaller.
474#
475# @param encoding Default encoding for 8-bit strings.  The default
476#     value is None (interpreted as UTF-8).
477# @see dumps
478
479class Marshaller(object):
480    """Generate an XML-RPC params chunk from a Python data structure.
481
482    Create a Marshaller instance for each set of parameters, and use
483    the "dumps" method to convert your data (represented as a tuple)
484    to an XML-RPC params chunk.  To write a fault response, pass a
485    Fault instance instead.  You may prefer to use the "dumps" module
486    function for this purpose.
487    """
488
489    # by the way, if you don't understand what's going on in here,
490    # that's perfectly ok.
491
492    def __init__(self, encoding=None, allow_none=False):
493        self.memo = {}
494        self.data = None
495        self.encoding = encoding
496        self.allow_none = allow_none
497
498    dispatch = {}
499
500    def dumps(self, values):
501        out = []
502        write = out.append
503        dump = self.__dump
504        if isinstance(values, Fault):
505            # fault instance
506            write("<fault>\n")
507            dump({'faultCode': values.faultCode,
508                  'faultString': values.faultString},
509                 write)
510            write("</fault>\n")
511        else:
512            # parameter block
513            # FIXME: the xml-rpc specification allows us to leave out
514            # the entire <params> block if there are no parameters.
515            # however, changing this may break older code (including
516            # old versions of xmlrpclib.py), so this is better left as
517            # is for now.  See @XMLRPC3 for more information. /F
518            write("<params>\n")
519            for v in values:
520                write("<param>\n")
521                dump(v, write)
522                write("</param>\n")
523            write("</params>\n")
524        result = "".join(out)
525        return str(result)
526
527    def __dump(self, value, write):
528        try:
529            f = self.dispatch[type(ensure_new_type(value))]
530        except KeyError:
531            # check if this object can be marshalled as a structure
532            if not hasattr(value, '__dict__'):
533                raise TypeError("cannot marshal %s objects" % type(value))
534            # check if this class is a sub-class of a basic type,
535            # because we don't know how to marshal these types
536            # (e.g. a string sub-class)
537            for type_ in type(value).__mro__:
538                if type_ in self.dispatch.keys():
539                    raise TypeError("cannot marshal %s objects" % type(value))
540            # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
541            # for the p3yk merge, this should probably be fixed more neatly.
542            f = self.dispatch["_arbitrary_instance"]
543        f(self, value, write)
544
545    def dump_nil (self, value, write):
546        if not self.allow_none:
547            raise TypeError("cannot marshal None unless allow_none is enabled")
548        write("<value><nil/></value>")
549    dispatch[type(None)] = dump_nil
550
551    def dump_bool(self, value, write):
552        write("<value><boolean>")
553        write(value and "1" or "0")
554        write("</boolean></value>\n")
555    dispatch[bool] = dump_bool
556
557    def dump_long(self, value, write):
558        if value > MAXINT or value < MININT:
559            raise OverflowError("long int exceeds XML-RPC limits")
560        write("<value><int>")
561        write(str(int(value)))
562        write("</int></value>\n")
563    dispatch[int] = dump_long
564
565    # backward compatible
566    dump_int = dump_long
567
568    def dump_double(self, value, write):
569        write("<value><double>")
570        write(repr(ensure_new_type(value)))
571        write("</double></value>\n")
572    dispatch[float] = dump_double
573
574    def dump_unicode(self, value, write, escape=escape):
575        write("<value><string>")
576        write(escape(value))
577        write("</string></value>\n")
578    dispatch[str] = dump_unicode
579
580    def dump_bytes(self, value, write):
581        write("<value><base64>\n")
582        encoded = base64.encodebytes(value)
583        write(encoded.decode('ascii'))
584        write("</base64></value>\n")
585    dispatch[bytes] = dump_bytes
586    dispatch[bytearray] = dump_bytes
587
588    def dump_array(self, value, write):
589        i = id(value)
590        if i in self.memo:
591            raise TypeError("cannot marshal recursive sequences")
592        self.memo[i] = None
593        dump = self.__dump
594        write("<value><array><data>\n")
595        for v in value:
596            dump(v, write)
597        write("</data></array></value>\n")
598        del self.memo[i]
599    dispatch[tuple] = dump_array
600    dispatch[list] = dump_array
601
602    def dump_struct(self, value, write, escape=escape):
603        i = id(value)
604        if i in self.memo:
605            raise TypeError("cannot marshal recursive dictionaries")
606        self.memo[i] = None
607        dump = self.__dump
608        write("<value><struct>\n")
609        for k, v in value.items():
610            write("<member>\n")
611            if not isinstance(k, str):
612                raise TypeError("dictionary key must be string")
613            write("<name>%s</name>\n" % escape(k))
614            dump(v, write)
615            write("</member>\n")
616        write("</struct></value>\n")
617        del self.memo[i]
618    dispatch[dict] = dump_struct
619
620    def dump_datetime(self, value, write):
621        write("<value><dateTime.iso8601>")
622        write(_strftime(value))
623        write("</dateTime.iso8601></value>\n")
624    dispatch[datetime] = dump_datetime
625
626    def dump_instance(self, value, write):
627        # check for special wrappers
628        if value.__class__ in WRAPPERS:
629            self.write = write
630            value.encode(self)
631            del self.write
632        else:
633            # store instance attributes as a struct (really?)
634            self.dump_struct(value.__dict__, write)
635    dispatch[DateTime] = dump_instance
636    dispatch[Binary] = dump_instance
637    # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
638    # for the p3yk merge, this should probably be fixed more neatly.
639    dispatch["_arbitrary_instance"] = dump_instance
640
641##
642# XML-RPC unmarshaller.
643#
644# @see loads
645
646class Unmarshaller(object):
647    """Unmarshal an XML-RPC response, based on incoming XML event
648    messages (start, data, end).  Call close() to get the resulting
649    data structure.
650
651    Note that this reader is fairly tolerant, and gladly accepts bogus
652    XML-RPC data without complaining (but not bogus XML).
653    """
654
655    # and again, if you don't understand what's going on in here,
656    # that's perfectly ok.
657
658    def __init__(self, use_datetime=False, use_builtin_types=False):
659        self._type = None
660        self._stack = []
661        self._marks = []
662        self._data = []
663        self._methodname = None
664        self._encoding = "utf-8"
665        self.append = self._stack.append
666        self._use_datetime = use_builtin_types or use_datetime
667        self._use_bytes = use_builtin_types
668
669    def close(self):
670        # return response tuple and target method
671        if self._type is None or self._marks:
672            raise ResponseError()
673        if self._type == "fault":
674            raise Fault(**self._stack[0])
675        return tuple(self._stack)
676
677    def getmethodname(self):
678        return self._methodname
679
680    #
681    # event handlers
682
683    def xml(self, encoding, standalone):
684        self._encoding = encoding
685        # FIXME: assert standalone == 1 ???
686
687    def start(self, tag, attrs):
688        # prepare to handle this element
689        if tag == "array" or tag == "struct":
690            self._marks.append(len(self._stack))
691        self._data = []
692        self._value = (tag == "value")
693
694    def data(self, text):
695        self._data.append(text)
696
697    def end(self, tag):
698        # call the appropriate end tag handler
699        try:
700            f = self.dispatch[tag]
701        except KeyError:
702            pass # unknown tag ?
703        else:
704            return f(self, "".join(self._data))
705
706    #
707    # accelerator support
708
709    def end_dispatch(self, tag, data):
710        # dispatch data
711        try:
712            f = self.dispatch[tag]
713        except KeyError:
714            pass # unknown tag ?
715        else:
716            return f(self, data)
717
718    #
719    # element decoders
720
721    dispatch = {}
722
723    def end_nil (self, data):
724        self.append(None)
725        self._value = 0
726    dispatch["nil"] = end_nil
727
728    def end_boolean(self, data):
729        if data == "0":
730            self.append(False)
731        elif data == "1":
732            self.append(True)
733        else:
734            raise TypeError("bad boolean value")
735        self._value = 0
736    dispatch["boolean"] = end_boolean
737
738    def end_int(self, data):
739        self.append(int(data))
740        self._value = 0
741    dispatch["i4"] = end_int
742    dispatch["i8"] = end_int
743    dispatch["int"] = end_int
744
745    def end_double(self, data):
746        self.append(float(data))
747        self._value = 0
748    dispatch["double"] = end_double
749
750    def end_string(self, data):
751        if self._encoding:
752            data = data.decode(self._encoding)
753        self.append(data)
754        self._value = 0
755    dispatch["string"] = end_string
756    dispatch["name"] = end_string # struct keys are always strings
757
758    def end_array(self, data):
759        mark = self._marks.pop()
760        # map arrays to Python lists
761        self._stack[mark:] = [self._stack[mark:]]
762        self._value = 0
763    dispatch["array"] = end_array
764
765    def end_struct(self, data):
766        mark = self._marks.pop()
767        # map structs to Python dictionaries
768        dict = {}
769        items = self._stack[mark:]
770        for i in range(0, len(items), 2):
771            dict[items[i]] = items[i+1]
772        self._stack[mark:] = [dict]
773        self._value = 0
774    dispatch["struct"] = end_struct
775
776    def end_base64(self, data):
777        value = Binary()
778        value.decode(data.encode("ascii"))
779        if self._use_bytes:
780            value = value.data
781        self.append(value)
782        self._value = 0
783    dispatch["base64"] = end_base64
784
785    def end_dateTime(self, data):
786        value = DateTime()
787        value.decode(data)
788        if self._use_datetime:
789            value = _datetime_type(data)
790        self.append(value)
791    dispatch["dateTime.iso8601"] = end_dateTime
792
793    def end_value(self, data):
794        # if we stumble upon a value element with no internal
795        # elements, treat it as a string element
796        if self._value:
797            self.end_string(data)
798    dispatch["value"] = end_value
799
800    def end_params(self, data):
801        self._type = "params"
802    dispatch["params"] = end_params
803
804    def end_fault(self, data):
805        self._type = "fault"
806    dispatch["fault"] = end_fault
807
808    def end_methodName(self, data):
809        if self._encoding:
810            data = data.decode(self._encoding)
811        self._methodname = data
812        self._type = "methodName" # no params
813    dispatch["methodName"] = end_methodName
814
815## Multicall support
816#
817
818class _MultiCallMethod(object):
819    # some lesser magic to store calls made to a MultiCall object
820    # for batch execution
821    def __init__(self, call_list, name):
822        self.__call_list = call_list
823        self.__name = name
824    def __getattr__(self, name):
825        return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
826    def __call__(self, *args):
827        self.__call_list.append((self.__name, args))
828
829class MultiCallIterator(object):
830    """Iterates over the results of a multicall. Exceptions are
831    raised in response to xmlrpc faults."""
832
833    def __init__(self, results):
834        self.results = results
835
836    def __getitem__(self, i):
837        item = self.results[i]
838        if isinstance(type(item), dict):
839            raise Fault(item['faultCode'], item['faultString'])
840        elif type(item) == type([]):
841            return item[0]
842        else:
843            raise ValueError("unexpected type in multicall result")
844
845class MultiCall(object):
846    """server -> a object used to boxcar method calls
847
848    server should be a ServerProxy object.
849
850    Methods can be added to the MultiCall using normal
851    method call syntax e.g.:
852
853    multicall = MultiCall(server_proxy)
854    multicall.add(2,3)
855    multicall.get_address("Guido")
856
857    To execute the multicall, call the MultiCall object e.g.:
858
859    add_result, address = multicall()
860    """
861
862    def __init__(self, server):
863        self.__server = server
864        self.__call_list = []
865
866    def __repr__(self):
867        return "<MultiCall at %x>" % id(self)
868
869    __str__ = __repr__
870
871    def __getattr__(self, name):
872        return _MultiCallMethod(self.__call_list, name)
873
874    def __call__(self):
875        marshalled_list = []
876        for name, args in self.__call_list:
877            marshalled_list.append({'methodName' : name, 'params' : args})
878
879        return MultiCallIterator(self.__server.system.multicall(marshalled_list))
880
881# --------------------------------------------------------------------
882# convenience functions
883
884FastMarshaller = FastParser = FastUnmarshaller = None
885
886##
887# Create a parser object, and connect it to an unmarshalling instance.
888# This function picks the fastest available XML parser.
889#
890# return A (parser, unmarshaller) tuple.
891
892def getparser(use_datetime=False, use_builtin_types=False):
893    """getparser() -> parser, unmarshaller
894
895    Create an instance of the fastest available parser, and attach it
896    to an unmarshalling object.  Return both objects.
897    """
898    if FastParser and FastUnmarshaller:
899        if use_builtin_types:
900            mkdatetime = _datetime_type
901            mkbytes = base64.decodebytes
902        elif use_datetime:
903            mkdatetime = _datetime_type
904            mkbytes = _binary
905        else:
906            mkdatetime = _datetime
907            mkbytes = _binary
908        target = FastUnmarshaller(True, False, mkbytes, mkdatetime, Fault)
909        parser = FastParser(target)
910    else:
911        target = Unmarshaller(use_datetime=use_datetime, use_builtin_types=use_builtin_types)
912        if FastParser:
913            parser = FastParser(target)
914        else:
915            parser = ExpatParser(target)
916    return parser, target
917
918##
919# Convert a Python tuple or a Fault instance to an XML-RPC packet.
920#
921# @def dumps(params, **options)
922# @param params A tuple or Fault instance.
923# @keyparam methodname If given, create a methodCall request for
924#     this method name.
925# @keyparam methodresponse If given, create a methodResponse packet.
926#     If used with a tuple, the tuple must be a singleton (that is,
927#     it must contain exactly one element).
928# @keyparam encoding The packet encoding.
929# @return A string containing marshalled data.
930
931def dumps(params, methodname=None, methodresponse=None, encoding=None,
932          allow_none=False):
933    """data [,options] -> marshalled data
934
935    Convert an argument tuple or a Fault instance to an XML-RPC
936    request (or response, if the methodresponse option is used).
937
938    In addition to the data object, the following options can be given
939    as keyword arguments:
940
941        methodname: the method name for a methodCall packet
942
943        methodresponse: true to create a methodResponse packet.
944        If this option is used with a tuple, the tuple must be
945        a singleton (i.e. it can contain only one element).
946
947        encoding: the packet encoding (default is UTF-8)
948
949    All byte strings in the data structure are assumed to use the
950    packet encoding.  Unicode strings are automatically converted,
951    where necessary.
952    """
953
954    assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance"
955    if isinstance(params, Fault):
956        methodresponse = 1
957    elif methodresponse and isinstance(params, tuple):
958        assert len(params) == 1, "response tuple must be a singleton"
959
960    if not encoding:
961        encoding = "utf-8"
962
963    if FastMarshaller:
964        m = FastMarshaller(encoding)
965    else:
966        m = Marshaller(encoding, allow_none)
967
968    data = m.dumps(params)
969
970    if encoding != "utf-8":
971        xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
972    else:
973        xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
974
975    # standard XML-RPC wrappings
976    if methodname:
977        # a method call
978        if not isinstance(methodname, str):
979            methodname = methodname.encode(encoding)
980        data = (
981            xmlheader,
982            "<methodCall>\n"
983            "<methodName>", methodname, "</methodName>\n",
984            data,
985            "</methodCall>\n"
986            )
987    elif methodresponse:
988        # a method response, or a fault structure
989        data = (
990            xmlheader,
991            "<methodResponse>\n",
992            data,
993            "</methodResponse>\n"
994            )
995    else:
996        return data # return as is
997    return str("").join(data)
998
999##
1000# Convert an XML-RPC packet to a Python object.  If the XML-RPC packet
1001# represents a fault condition, this function raises a Fault exception.
1002#
1003# @param data An XML-RPC packet, given as an 8-bit string.
1004# @return A tuple containing the unpacked data, and the method name
1005#     (None if not present).
1006# @see Fault
1007
1008def loads(data, use_datetime=False, use_builtin_types=False):
1009    """data -> unmarshalled data, method name
1010
1011    Convert an XML-RPC packet to unmarshalled data plus a method
1012    name (None if not present).
1013
1014    If the XML-RPC packet represents a fault condition, this function
1015    raises a Fault exception.
1016    """
1017    p, u = getparser(use_datetime=use_datetime, use_builtin_types=use_builtin_types)
1018    p.feed(data)
1019    p.close()
1020    return u.close(), u.getmethodname()
1021
1022##
1023# Encode a string using the gzip content encoding such as specified by the
1024# Content-Encoding: gzip
1025# in the HTTP header, as described in RFC 1952
1026#
1027# @param data the unencoded data
1028# @return the encoded data
1029
1030def gzip_encode(data):
1031    """data -> gzip encoded data
1032
1033    Encode data using the gzip content encoding as described in RFC 1952
1034    """
1035    if not gzip:
1036        raise NotImplementedError
1037    f = BytesIO()
1038    gzf = gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1)
1039    gzf.write(data)
1040    gzf.close()
1041    encoded = f.getvalue()
1042    f.close()
1043    return encoded
1044
1045##
1046# Decode a string using the gzip content encoding such as specified by the
1047# Content-Encoding: gzip
1048# in the HTTP header, as described in RFC 1952
1049#
1050# @param data The encoded data
1051# @return the unencoded data
1052# @raises ValueError if data is not correctly coded.
1053
1054def gzip_decode(data):
1055    """gzip encoded data -> unencoded data
1056
1057    Decode data using the gzip content encoding as described in RFC 1952
1058    """
1059    if not gzip:
1060        raise NotImplementedError
1061    f = BytesIO(data)
1062    gzf = gzip.GzipFile(mode="rb", fileobj=f)
1063    try:
1064        decoded = gzf.read()
1065    except IOError:
1066        raise ValueError("invalid data")
1067    f.close()
1068    gzf.close()
1069    return decoded
1070
1071##
1072# Return a decoded file-like object for the gzip encoding
1073# as described in RFC 1952.
1074#
1075# @param response A stream supporting a read() method
1076# @return a file-like object that the decoded data can be read() from
1077
1078class GzipDecodedResponse(gzip.GzipFile if gzip else object):
1079    """a file-like object to decode a response encoded with the gzip
1080    method, as described in RFC 1952.
1081    """
1082    def __init__(self, response):
1083        #response doesn't support tell() and read(), required by
1084        #GzipFile
1085        if not gzip:
1086            raise NotImplementedError
1087        self.io = BytesIO(response.read())
1088        gzip.GzipFile.__init__(self, mode="rb", fileobj=self.io)
1089
1090    def close(self):
1091        gzip.GzipFile.close(self)
1092        self.io.close()
1093
1094
1095# --------------------------------------------------------------------
1096# request dispatcher
1097
1098class _Method(object):
1099    # some magic to bind an XML-RPC method to an RPC server.
1100    # supports "nested" methods (e.g. examples.getStateName)
1101    def __init__(self, send, name):
1102        self.__send = send
1103        self.__name = name
1104    def __getattr__(self, name):
1105        return _Method(self.__send, "%s.%s" % (self.__name, name))
1106    def __call__(self, *args):
1107        return self.__send(self.__name, args)
1108
1109##
1110# Standard transport class for XML-RPC over HTTP.
1111# <p>
1112# You can create custom transports by subclassing this method, and
1113# overriding selected methods.
1114
1115class Transport(object):
1116    """Handles an HTTP transaction to an XML-RPC server."""
1117
1118    # client identifier (may be overridden)
1119    user_agent = "Python-xmlrpc/%s" % __version__
1120
1121    #if true, we'll request gzip encoding
1122    accept_gzip_encoding = True
1123
1124    # if positive, encode request using gzip if it exceeds this threshold
1125    # note that many server will get confused, so only use it if you know
1126    # that they can decode such a request
1127    encode_threshold = None #None = don't encode
1128
1129    def __init__(self, use_datetime=False, use_builtin_types=False):
1130        self._use_datetime = use_datetime
1131        self._use_builtin_types = use_builtin_types
1132        self._connection = (None, None)
1133        self._extra_headers = []
1134
1135    ##
1136    # Send a complete request, and parse the response.
1137    # Retry request if a cached connection has disconnected.
1138    #
1139    # @param host Target host.
1140    # @param handler Target PRC handler.
1141    # @param request_body XML-RPC request body.
1142    # @param verbose Debugging flag.
1143    # @return Parsed response.
1144
1145    def request(self, host, handler, request_body, verbose=False):
1146        #retry request once if cached connection has gone cold
1147        for i in (0, 1):
1148            try:
1149                return self.single_request(host, handler, request_body, verbose)
1150            except socket.error as e:
1151                if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE):
1152                    raise
1153            except http_client.BadStatusLine: #close after we sent request
1154                if i:
1155                    raise
1156
1157    def single_request(self, host, handler, request_body, verbose=False):
1158        # issue XML-RPC request
1159        try:
1160            http_conn = self.send_request(host, handler, request_body, verbose)
1161            resp = http_conn.getresponse()
1162            if resp.status == 200:
1163                self.verbose = verbose
1164                return self.parse_response(resp)
1165
1166        except Fault:
1167            raise
1168        except Exception:
1169            #All unexpected errors leave connection in
1170            # a strange state, so we clear it.
1171            self.close()
1172            raise
1173
1174        #We got an error response.
1175        #Discard any response data and raise exception
1176        if resp.getheader("content-length", ""):
1177            resp.read()
1178        raise ProtocolError(
1179            host + handler,
1180            resp.status, resp.reason,
1181            dict(resp.getheaders())
1182            )
1183
1184
1185    ##
1186    # Create parser.
1187    #
1188    # @return A 2-tuple containing a parser and a unmarshaller.
1189
1190    def getparser(self):
1191        # get parser and unmarshaller
1192        return getparser(use_datetime=self._use_datetime,
1193                         use_builtin_types=self._use_builtin_types)
1194
1195    ##
1196    # Get authorization info from host parameter
1197    # Host may be a string, or a (host, x509-dict) tuple; if a string,
1198    # it is checked for a "user:pw@host" format, and a "Basic
1199    # Authentication" header is added if appropriate.
1200    #
1201    # @param host Host descriptor (URL or (URL, x509 info) tuple).
1202    # @return A 3-tuple containing (actual host, extra headers,
1203    #     x509 info).  The header and x509 fields may be None.
1204
1205    def get_host_info(self, host):
1206
1207        x509 = {}
1208        if isinstance(host, tuple):
1209            host, x509 = host
1210
1211        auth, host = urllib_parse.splituser(host)
1212
1213        if auth:
1214            auth = urllib_parse.unquote_to_bytes(auth)
1215            auth = base64.encodebytes(auth).decode("utf-8")
1216            auth = "".join(auth.split()) # get rid of whitespace
1217            extra_headers = [
1218                ("Authorization", "Basic " + auth)
1219                ]
1220        else:
1221            extra_headers = []
1222
1223        return host, extra_headers, x509
1224
1225    ##
1226    # Connect to server.
1227    #
1228    # @param host Target host.
1229    # @return An HTTPConnection object
1230
1231    def make_connection(self, host):
1232        #return an existing connection if possible.  This allows
1233        #HTTP/1.1 keep-alive.
1234        if self._connection and host == self._connection[0]:
1235            return self._connection[1]
1236        # create a HTTP connection object from a host descriptor
1237        chost, self._extra_headers, x509 = self.get_host_info(host)
1238        self._connection = host, http_client.HTTPConnection(chost)
1239        return self._connection[1]
1240
1241    ##
1242    # Clear any cached connection object.
1243    # Used in the event of socket errors.
1244    #
1245    def close(self):
1246        if self._connection[1]:
1247            self._connection[1].close()
1248            self._connection = (None, None)
1249
1250    ##
1251    # Send HTTP request.
1252    #
1253    # @param host Host descriptor (URL or (URL, x509 info) tuple).
1254    # @param handler Targer RPC handler (a path relative to host)
1255    # @param request_body The XML-RPC request body
1256    # @param debug Enable debugging if debug is true.
1257    # @return An HTTPConnection.
1258
1259    def send_request(self, host, handler, request_body, debug):
1260        connection = self.make_connection(host)
1261        headers = self._extra_headers[:]
1262        if debug:
1263            connection.set_debuglevel(1)
1264        if self.accept_gzip_encoding and gzip:
1265            connection.putrequest("POST", handler, skip_accept_encoding=True)
1266            headers.append(("Accept-Encoding", "gzip"))
1267        else:
1268            connection.putrequest("POST", handler)
1269        headers.append(("Content-Type", "text/xml"))
1270        headers.append(("User-Agent", self.user_agent))
1271        self.send_headers(connection, headers)
1272        self.send_content(connection, request_body)
1273        return connection
1274
1275    ##
1276    # Send request headers.
1277    # This function provides a useful hook for subclassing
1278    #
1279    # @param connection httpConnection.
1280    # @param headers list of key,value pairs for HTTP headers
1281
1282    def send_headers(self, connection, headers):
1283        for key, val in headers:
1284            connection.putheader(key, val)
1285
1286    ##
1287    # Send request body.
1288    # This function provides a useful hook for subclassing
1289    #
1290    # @param connection httpConnection.
1291    # @param request_body XML-RPC request body.
1292
1293    def send_content(self, connection, request_body):
1294        #optionally encode the request
1295        if (self.encode_threshold is not None and
1296            self.encode_threshold < len(request_body) and
1297            gzip):
1298            connection.putheader("Content-Encoding", "gzip")
1299            request_body = gzip_encode(request_body)
1300
1301        connection.putheader("Content-Length", str(len(request_body)))
1302        connection.endheaders(request_body)
1303
1304    ##
1305    # Parse response.
1306    #
1307    # @param file Stream.
1308    # @return Response tuple and target method.
1309
1310    def parse_response(self, response):
1311        # read response data from httpresponse, and parse it
1312        # Check for new http response object, otherwise it is a file object.
1313        if hasattr(response, 'getheader'):
1314            if response.getheader("Content-Encoding", "") == "gzip":
1315                stream = GzipDecodedResponse(response)
1316            else:
1317                stream = response
1318        else:
1319            stream = response
1320
1321        p, u = self.getparser()
1322
1323        while 1:
1324            data = stream.read(1024)
1325            if not data:
1326                break
1327            if self.verbose:
1328                print("body:", repr(data))
1329            p.feed(data)
1330
1331        if stream is not response:
1332            stream.close()
1333        p.close()
1334
1335        return u.close()
1336
1337##
1338# Standard transport class for XML-RPC over HTTPS.
1339
1340class SafeTransport(Transport):
1341    """Handles an HTTPS transaction to an XML-RPC server."""
1342
1343    # FIXME: mostly untested
1344
1345    def make_connection(self, host):
1346        if self._connection and host == self._connection[0]:
1347            return self._connection[1]
1348
1349        if not hasattr(http_client, "HTTPSConnection"):
1350            raise NotImplementedError(
1351            "your version of http.client doesn't support HTTPS")
1352        # create a HTTPS connection object from a host descriptor
1353        # host may be a string, or a (host, x509-dict) tuple
1354        chost, self._extra_headers, x509 = self.get_host_info(host)
1355        self._connection = host, http_client.HTTPSConnection(chost,
1356            None, **(x509 or {}))
1357        return self._connection[1]
1358
1359##
1360# Standard server proxy.  This class establishes a virtual connection
1361# to an XML-RPC server.
1362# <p>
1363# This class is available as ServerProxy and Server.  New code should
1364# use ServerProxy, to avoid confusion.
1365#
1366# @def ServerProxy(uri, **options)
1367# @param uri The connection point on the server.
1368# @keyparam transport A transport factory, compatible with the
1369#    standard transport class.
1370# @keyparam encoding The default encoding used for 8-bit strings
1371#    (default is UTF-8).
1372# @keyparam verbose Use a true value to enable debugging output.
1373#    (printed to standard output).
1374# @see Transport
1375
1376class ServerProxy(object):
1377    """uri [,options] -> a logical connection to an XML-RPC server
1378
1379    uri is the connection point on the server, given as
1380    scheme://host/target.
1381
1382    The standard implementation always supports the "http" scheme.  If
1383    SSL socket support is available (Python 2.0), it also supports
1384    "https".
1385
1386    If the target part and the slash preceding it are both omitted,
1387    "/RPC2" is assumed.
1388
1389    The following options can be given as keyword arguments:
1390
1391        transport: a transport factory
1392        encoding: the request encoding (default is UTF-8)
1393
1394    All 8-bit strings passed to the server proxy are assumed to use
1395    the given encoding.
1396    """
1397
1398    def __init__(self, uri, transport=None, encoding=None, verbose=False,
1399                 allow_none=False, use_datetime=False, use_builtin_types=False):
1400        # establish a "logical" server connection
1401
1402        # get the url
1403        type, uri = urllib_parse.splittype(uri)
1404        if type not in ("http", "https"):
1405            raise IOError("unsupported XML-RPC protocol")
1406        self.__host, self.__handler = urllib_parse.splithost(uri)
1407        if not self.__handler:
1408            self.__handler = "/RPC2"
1409
1410        if transport is None:
1411            if type == "https":
1412                handler = SafeTransport
1413            else:
1414                handler = Transport
1415            transport = handler(use_datetime=use_datetime,
1416                                use_builtin_types=use_builtin_types)
1417        self.__transport = transport
1418
1419        self.__encoding = encoding or 'utf-8'
1420        self.__verbose = verbose
1421        self.__allow_none = allow_none
1422
1423    def __close(self):
1424        self.__transport.close()
1425
1426    def __request(self, methodname, params):
1427        # call a method on the remote server
1428
1429        request = dumps(params, methodname, encoding=self.__encoding,
1430                        allow_none=self.__allow_none).encode(self.__encoding)
1431
1432        response = self.__transport.request(
1433            self.__host,
1434            self.__handler,
1435            request,
1436            verbose=self.__verbose
1437            )
1438
1439        if len(response) == 1:
1440            response = response[0]
1441
1442        return response
1443
1444    def __repr__(self):
1445        return (
1446            "<ServerProxy for %s%s>" %
1447            (self.__host, self.__handler)
1448            )
1449
1450    __str__ = __repr__
1451
1452    def __getattr__(self, name):
1453        # magic method dispatcher
1454        return _Method(self.__request, name)
1455
1456    # note: to call a remote object with an non-standard name, use
1457    # result getattr(server, "strange-python-name")(args)
1458
1459    def __call__(self, attr):
1460        """A workaround to get special attributes on the ServerProxy
1461           without interfering with the magic __getattr__
1462        """
1463        if attr == "close":
1464            return self.__close
1465        elif attr == "transport":
1466            return self.__transport
1467        raise AttributeError("Attribute %r not found" % (attr,))
1468
1469# compatibility
1470
1471Server = ServerProxy
1472
1473# --------------------------------------------------------------------
1474# test code
1475
1476if __name__ == "__main__":
1477
1478    # simple test program (from the XML-RPC specification)
1479
1480    # local server, available from Lib/xmlrpc/server.py
1481    server = ServerProxy("http://localhost:8000")
1482
1483    try:
1484        print(server.currentTime.getCurrentTime())
1485    except Error as v:
1486        print("ERROR", v)
1487
1488    multi = MultiCall(server)
1489    multi.getData()
1490    multi.pow(2,9)
1491    multi.add(1,2)
1492    try:
1493        for response in multi():
1494            print(response)
1495    except Error as v:
1496        print("ERROR", v)
1497