2 from Config import Config
5 from Utilities import *
10 from wstools.XMLname import fromXMLname
12 try: from M2Crypto import SSL
15 ident = '$Id: Parser.py 1497 2010-03-08 06:06:52Z pooryorick $'
16 from version import __version__
19 ################################################################################
21 ################################################################################
23 def __init__(self, name, frame):
27 self.subpos = frame.namecounts.get(name, 0)
30 return "<%s %s at %d>" % (self.__class__, self.name, id(self))
33 return "<%s %s at %d>" % (self.__class__, self.name, id(self))
35 class SOAPParser(xml.sax.handler.ContentHandler):
37 def __init__(self, name, kind = None, attrs = {}, rules = {}):
48 def append(self, name, data, attrs):
49 self.names.append(name)
50 self.contents.append(data)
51 self.subattrs.append(attrs)
53 if self.namecounts.has_key(name):
54 self.namecounts[name] += 1
56 self.namecounts[name] = 1
58 def _placeItem(self, name, value, pos, subpos = 0, attrs = None):
59 self.contents[pos] = value
62 self.attrs.update(attrs)
65 return len(self.contents)
68 return "<%s %s at %d>" % (self.__class__, self.name, id(self))
70 def __init__(self, rules = None):
71 xml.sax.handler.ContentHandler.__init__(self)
76 self._next = "E" # Keeping state for message validity
77 self._stack = [self.Frame('SOAP')]
79 # Make two dictionaries to store the prefix <-> URI mappings, and
80 # initialize them with the default
81 self._prem = {NS.XML_T: NS.XML}
82 self._prem_r = {NS.XML: NS.XML_T}
87 def startElementNS(self, name, qname, attrs):
92 if self._prem_r.has_key(prefix):
93 tag = self._prem_r[name[0]] + ':' + name[1]
95 tag = prefix + ":" + tag
98 # Workaround two sax bugs
99 if name[0] == None and name[1][0] == ' ':
100 name = (None, name[1][1:])
104 # First some checking of the layout of the message
106 if self._next == "E":
107 if name[1] != 'Envelope':
108 raise Error, "expected `SOAP-ENV:Envelope', " \
109 "got `%s'" % toStr( name )
110 if name[0] != NS.ENV:
111 raise faultType, ("%s:VersionMismatch" % NS.ENV_T,
112 "Don't understand version `%s' Envelope" % name[0])
115 elif self._next == "HorB":
116 if name[0] == NS.ENV and name[1] in ("Header", "Body"):
120 "expected `SOAP-ENV:Header' or `SOAP-ENV:Body', " \
121 "got `%s'" % toStr( name )
122 elif self._next == "B":
123 if name == (NS.ENV, "Body"):
126 raise Error, "expected `SOAP-ENV:Body', " \
127 "got `%s'" % toStr( name )
128 elif self._next == "":
129 raise Error, "expected nothing, " \
130 "got `%s'" % toStr( name )
133 if len(self._stack) == 2:
137 rules = self._stack[-1].rules[name[1]]
141 if type(rules) not in (NoneType, DictType):
144 kind = attrs.get((NS.ENC, 'arrayType'))
147 del attrs._attrs[(NS.ENC, 'arrayType')]
152 kind = (self._prem[kind[:i]], kind[i + 1:])
158 self.pushFrame(self.Frame(name[1], kind, attrs._attrs, rules))
160 self._data = [] # Start accumulating
162 def pushFrame(self, frame):
163 self._stack.append(frame)
166 return self._stack.pop()
168 def endElementNS(self, name, qname):
169 # Workaround two sax bugs
170 if name[0] == None and name[1][0] == ' ':
171 ns, name = None, name[1][1:]
173 ns, name = tuple(name)
175 name = fromXMLname(name) # convert to SOAP 1.2 XML name encoding
177 if self._next == "E":
178 raise Error, "didn't get SOAP-ENV:Envelope"
179 if self._next in ("HorB", "B"):
180 raise Error, "didn't get SOAP-ENV:Body"
182 cur = self.popFrame()
187 if attrs.has_key((None, 'id')):
188 idval = attrs[(None, 'id')]
190 if self._ids.has_key(idval):
191 raise Error, "duplicate id `%s'" % idval
193 del attrs[(None, 'id')]
197 if len(self._stack) == 3:
198 if attrs.has_key((NS.ENC, 'root')):
199 root = int(attrs[(NS.ENC, 'root')])
201 # Do some preliminary checks. First, if root="0" is present,
202 # the element must have an id. Next, if root="n" is present,
203 # n something other than 0 or 1, raise an exception.
207 raise Error, "non-root element must have an id"
209 raise Error, "SOAP-ENC:root must be `0' or `1'"
211 del attrs[(NS.ENC, 'root')]
214 href = attrs.get((None, 'href'))
217 raise Error, "Non-local hrefs are not yet suppported."
218 if self._data != None and \
219 string.join(self._data, "").strip() != '':
220 raise Error, "hrefs can't have data"
224 if self._ids.has_key(href):
225 data = self._ids[href]
227 data = RefHolder(name, self._stack[-1])
229 if self._refs.has_key(href):
230 self._refs[href].append(data)
232 self._refs[href] = [data]
234 del attrs[(None, 'href')]
242 if attrs.has_key((i, 'type')):
243 kind = attrs[(i, 'type')]
244 del attrs[(i, 'type')]
250 kind = (self._prem[kind[:i]], kind[i + 1:])
254 # XXX What to do here? (None, kind) is just going to fail in convertType
255 #print "Kind with no NS:", kind
261 for i in (NS.XSI, NS.XSI2):
262 if attrs.has_key((i, 'null')):
263 null = attrs[(i, 'null')]
264 del attrs[(i, 'null')]
266 if attrs.has_key((NS.XSI3, 'nil')):
267 null = attrs[(NS.XSI3, 'nil')]
268 del attrs[(NS.XSI3, 'nil')]
273 # check for nil='true'
274 if type(null) in (StringType, UnicodeType):
275 if null.lower() == 'true':
278 # check for nil=1, but watch out for string values
281 except ValueError, e:
282 if not e[0].startswith("invalid literal for int()"):
288 (self._data != None and string.join(self._data, "").strip() != ''):
289 raise Error, "nils can't have data"
295 if len(self._stack) == 2:
296 if (ns, name) == (NS.ENV, "Header"):
297 self.header = data = headerType(attrs = attrs)
300 elif (ns, name) == (NS.ENV, "Body"):
301 self.body = data = bodyType(attrs = attrs)
304 elif len(self._stack) == 3 and self._next == None:
305 if (ns, name) == (NS.ENV, "Fault"):
307 self._next = None # allow followons
311 #print "data=", self._data
313 #print "cur.kind=", cur.kind
314 #print "cur.rules=", cur.rules
318 if cur.rules != None:
321 if type(rule) in (StringType, UnicodeType):
322 rule = (None, rule) # none flags special handling
323 elif type(rule) == ListType:
330 # XXX What if rule != kind?
332 data = rule(string.join(self._data, ""))
333 elif type(rule) == DictType:
334 data = structType(name = (ns, name), attrs = attrs)
335 elif rule[1][:9] == 'arrayType':
336 data = self.convertType(cur.contents,
339 data = self.convertType(string.join(self._data, ""),
344 #print "No rules, using kind or cur.kind..."
346 if (kind == None and cur.kind != None) or \
347 (kind == (NS.ENC, 'Array')):
351 kind = 'ur-type[%d]' % len(cur)
355 if len(cur.namecounts) == 1:
356 elemsname = cur.names[0]
360 data = self.startArray((ns, name), kind, attrs, elemsname)
364 if len(self._stack) == 3 and kind == None and \
366 (self._data == None or string.join(self._data, "").strip() == ''):
367 data = structType(name = (ns, name), attrs = attrs)
370 if len(cur) == 0 and ns != NS.URN:
371 # Nothing's been added to the current frame so it must be a
376 # print "attrs:", attrs
377 # print "kind:", kind
381 # If the current item's container is an array, it will
382 # have a kind. If so, get the bit before the first [,
383 # which is the type of the array, therefore the type of
386 kind = self._stack[-1].kind
389 i = kind[1].find('[')
391 kind = (kind[0], kind[1][:i])
397 data = self.convertType(string.join(self._data, ""),
399 except UnknownTypeError:
405 if self._data == None:
408 data = string.join(self._data, "")
411 try: data = str(data)
416 data = structType(name = (ns, name), attrs = attrs)
420 if isinstance(data, compoundType):
421 for i in range(len(cur)):
423 data._addItem(cur.names[i], v, cur.subattrs[i])
425 if isinstance(v, RefHolder):
429 self._stack[-1].append(name, data, attrs)
432 self._ids[idval] = data
434 if self._refs.has_key(idval):
435 for i in self._refs[idval]:
436 i.parent._placeItem(i.name, data, i.pos, i.subpos, attrs)
438 del self._refs[idval]
440 self.attrs[id(data)] = attrs
442 if isinstance(data, anyType):
443 data._setAttrs(attrs)
445 self._data = None # Stop accumulating
447 def endDocument(self):
448 if len(self._refs) == 1:
450 "unresolved reference " + self._refs.keys()[0]
451 elif len(self._refs) > 1:
453 "unresolved references " + ', '.join(self._refs.keys())
455 def startPrefixMapping(self, prefix, uri):
456 self._prem[prefix] = uri
457 self._prem_r[uri] = prefix
459 def endPrefixMapping(self, prefix):
461 del self._prem_r[self._prem[prefix]]
462 del self._prem[prefix]
466 def characters(self, c):
467 if self._data != None:
470 arrayre = '^(?:(?P<ns>[^:]*):)?' \
472 '(?:\[(?P<rank>,*)\])?' \
473 '(?:\[(?P<asize>\d+(?:,\d+)*)?\])$'
475 def startArray(self, name, kind, attrs, elemsname):
476 if type(self.arrayre) == StringType:
477 self.arrayre = re.compile (self.arrayre)
479 offset = attrs.get((NS.ENC, "offset"))
482 del attrs[(NS.ENC, "offset")]
485 if offset[0] == '[' and offset[-1] == ']':
486 offset = int(offset[1:-1])
492 raise AttributeError, "invalid Array offset"
497 m = self.arrayre.search(kind)
505 return arrayType(None, name, attrs, offset, m.group('rank'),
506 m.group('asize'), elemsname)
507 elif m.group('ns') != None:
508 return typedArrayType(None, name,
509 (self._prem[m.group('ns')], t), attrs, offset,
510 m.group('rank'), m.group('asize'), elemsname)
512 return typedArrayType(None, name, (None, t), attrs, offset,
513 m.group('rank'), m.group('asize'), elemsname)
515 raise AttributeError, "invalid Array type `%s'" % kind
519 class DATETIMECONSTS:
520 SIGNre = '(?P<sign>-?)'
521 CENTURYre = '(?P<century>\d{2,})'
522 YEARre = '(?P<year>\d{2})'
523 MONTHre = '(?P<month>\d{2})'
524 DAYre = '(?P<day>\d{2})'
525 HOURre = '(?P<hour>\d{2})'
526 MINUTEre = '(?P<minute>\d{2})'
527 SECONDre = '(?P<second>\d{2}(?:\.\d*)?)'
528 TIMEZONEre = '(?P<zulu>Z)|(?P<tzsign>[-+])(?P<tzhour>\d{2}):' \
529 '(?P<tzminute>\d{2})'
533 __allres = {'sign': SIGNre, 'century': CENTURYre, 'year': YEARre,
534 'month': MONTHre, 'day': DAYre, 'hour': HOURre,
535 'minute': MINUTEre, 'second': SECONDre, 'timezone': TIMEZONEre,
536 'b': BOSre, 'e': EOSre}
538 dateTime = '%(b)s%(sign)s%(century)s%(year)s-%(month)s-%(day)sT' \
539 '%(hour)s:%(minute)s:%(second)s(%(timezone)s)?%(e)s' % __allres
540 timeInstant = dateTime
541 timePeriod = dateTime
542 time = '%(b)s%(hour)s:%(minute)s:%(second)s(%(timezone)s)?%(e)s' % \
544 date = '%(b)s%(sign)s%(century)s%(year)s-%(month)s-%(day)s' \
545 '(%(timezone)s)?%(e)s' % __allres
546 century = '%(b)s%(sign)s%(century)s(%(timezone)s)?%(e)s' % __allres
547 gYearMonth = '%(b)s%(sign)s%(century)s%(year)s-%(month)s' \
548 '(%(timezone)s)?%(e)s' % __allres
549 gYear = '%(b)s%(sign)s%(century)s%(year)s(%(timezone)s)?%(e)s' % \
552 gMonthDay = '%(b)s--%(month)s-%(day)s(%(timezone)s)?%(e)s' % __allres
553 recurringDate = gMonthDay
554 gDay = '%(b)s---%(day)s(%(timezone)s)?%(e)s' % __allres
556 gMonth = '%(b)s--%(month)s--(%(timezone)s)?%(e)s' % __allres
559 recurringInstant = '%(b)s%(sign)s(%(century)s|-)(%(year)s|-)-' \
560 '(%(month)s|-)-(%(day)s|-)T' \
561 '(%(hour)s|-):(%(minute)s|-):(%(second)s|-)' \
562 '(%(timezone)s)?%(e)s' % __allres
564 duration = '%(b)s%(sign)sP' \
565 '((?P<year>\d+)Y)?' \
566 '((?P<month>\d+)M)?' \
569 '((?P<hour>\d+)H)?' \
570 '((?P<minute>\d+)M)?' \
571 '((?P<second>\d*(?:\.\d*)?)S)?)?%(e)s' % \
574 timeDuration = duration
576 # The extra 31 on the front is:
577 # - so the tuple is 1-based
578 # - so months[month-1] is December's days if month is 1
580 months = (31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
582 def convertDateTime(self, value, kind):
583 def getZoneOffset(d):
587 if d['zulu'] == None:
588 zoffs = 60 * int(d['tzhour']) + int(d['tzminute'])
589 if d['tzsign'] != '-':
596 def applyZoneOffset(months, zoffs, date, minfield, posday = 1):
597 if zoffs == 0 and (minfield > 4 or 0 <= date[5] < 60):
600 if minfield > 5: date[5] = 0
601 if minfield > 4: date[4] = 0
604 date[4] += int(date[5]) / 60
609 if minfield > 3 or 0 <= date[4] < 60: return date
611 date[3] += date[4] / 60
614 if minfield > 2 or 0 <= date[3] < 24: return date
616 date[2] += date[3] / 24
620 if posday and date[2] <= 0:
621 date[2] += 31 # zoffs is at most 99:59, so the
622 # day will never be less than -3
626 # The date[1] == 3 (instead of == 2) is because we're
627 # going back a month, so we need to know if the previous
628 # month is February, so we test if this month is March.
630 leap = minfield == 0 and date[1] == 3 and \
631 date[0] % 4 == 0 and \
632 (date[0] % 100 != 0 or date[0] % 400 == 0)
634 if 0 < date[2] <= months[date[1]] + leap: break
636 date[2] += months[date[1] - 1] + leap
640 if date[1] > 0: break
644 if minfield > 0: break
651 exp = getattr(self.DATETIMECONSTS, kind)
652 except AttributeError:
655 if type(exp) == StringType:
656 exp = re.compile(exp)
657 setattr (self.DATETIMECONSTS, kind, exp)
659 m = exp.search(value)
666 f = ('century', 'year', 'month', 'day',
667 'hour', 'minute', 'second')
668 fn = len(f) # Index of first non-None value
671 if kind in ('duration', 'timeDuration'):
672 if d['sep'] != None and d['hour'] == None and \
673 d['minute'] == None and d['second'] == None:
678 for i in range(len(f)):
686 except ValueError: s = long(s)
692 if fn > len(r): # Any non-Nones?
700 if kind == 'recurringInstant':
701 for i in range(len(f)):
704 if s == None or s == '-':
729 if fn < len(r) and d['sign'] == '-':
734 return tuple(applyZoneOffset(self.DATETIMECONSTS.months,
735 getZoneOffset(d), r, fn, 0))
737 r = [0, 0, 1, 1, 0, 0, 0]
739 for i in range(len(f)):
745 if field == 'second':
758 if fn > len(r): # Any non-Nones?
768 if d.get('sign') == '-':
773 zoffs = getZoneOffset(d)
776 r = applyZoneOffset(self.DATETIMECONSTS.months, zoffs, r, fn)
778 if kind == 'century':
783 for i in range(1, len(f)):
791 raise Error, "invalid %s value `%s' - %s" % (kind, value, e)
795 'nonPositiveInteger': (0, None, 0),
796 'non-positive-integer': (0, None, 0),
797 'negativeInteger': (0, None, -1),
798 'negative-integer': (0, None, -1),
799 'long': (1, -9223372036854775808L,
800 9223372036854775807L),
801 'int': (0, -2147483648L, 2147483647L),
802 'short': (0, -32768, 32767),
803 'byte': (0, -128, 127),
804 'nonNegativeInteger': (0, 0, None),
805 'non-negative-integer': (0, 0, None),
806 'positiveInteger': (0, 1, None),
807 'positive-integer': (0, 1, None),
808 'unsignedLong': (1, 0, 18446744073709551615L),
809 'unsignedInt': (0, 0, 4294967295L),
810 'unsignedShort': (0, 0, 65535),
811 'unsignedByte': (0, 0, 255),
815 'float': (7.0064923216240861E-46, -3.4028234663852886E+38,
816 3.4028234663852886E+38),
817 'double': (2.4703282292062327E-324, -1.7976931348623158E+308,
818 1.7976931348623157E+308),
820 zerofloatre = '[1-9]'
823 def convertType(self, d, t, attrs, config=Config):
824 if t[0] is None and t[1] is not None:
826 if type[:9] == 'arrayType':
827 index_eq = type.find('=')
828 index_obr = type.find('[')
829 index_cbr = type.find(']')
830 elemtype = type[index_eq+1:index_obr]
831 elemnum = type[index_obr+1:index_cbr]
832 if elemtype=="ur-type":
835 newarr = map( lambda(di):
836 self.convertToBasicTypes(d=di,
837 t = ( NS.XSD, elemtype),
845 return self.convertToBasicTypes(d, t, attrs, config)
848 def convertToSOAPpyTypes(self, d, t, attrs, config=Config):
852 def convertToBasicTypes(self, d, t, attrs, config=Config):
856 #print "convertToBasicTypes:"
857 #print " requested_type=", t
861 # print "convertToBasicTypes:"
862 # print " requested_type=", t
864 # print " attrs=", attrs
865 # print " t[0]=", t[0]
866 # print " t[1]=", t[1]
868 # print " in?", t[0] in NS.EXSD_L
870 if t[0] in NS.EXSD_L:
871 if t[1]=="integer": # unbounded integer type
879 if self.intlimits.has_key (t[1]): # range-bounded integer types
880 l = self.intlimits[t[1]]
884 if l[1] != None and d < l[1]:
885 raise UnderflowError, "%s too small" % d
886 if l[2] != None and d > l[2]:
887 raise OverflowError, "%s too large" % d
889 if l[0] or len(attrs):
899 if t[1] in ("bool", "boolean"):
900 d = d.strip().lower()
901 if d in ('0', 'false'):
903 if d in ('1', 'true'):
905 raise AttributeError, "invalid boolean value"
906 if t[1] in ('double','float'):
907 l = self.floatlimits[t[1]]
908 s = d.strip().lower()
910 # Explicitly check for NaN and Infinities
913 elif s[0:2]=="inf" or s[0:3]=="+inf":
915 elif s[0:3] == "-inf":
920 if config.strict_range:
923 raise ValueError, "invalid %s: %s" % (t[1], s)
924 elif fpconst.isNegInf(d):
926 raise UnderflowError, "%s too small: %s" % (t[1], s)
927 elif fpconst.isPosInf(d):
928 if s[0:2] != 'inf' and s[0:3] != '+inf':
929 raise OverflowError, "%s too large: %s" % (t[1], s)
930 elif d < 0 and d < l[1]:
931 raise UnderflowError, "%s too small: %s" % (t[1], s)
932 elif d > 0 and ( d < l[0] or d > l[2] ):
933 raise OverflowError, "%s too large: %s" % (t[1], s)
935 if type(self.zerofloatre) == StringType:
936 self.zerofloatre = re.compile(self.zerofloatre)
938 if self.zerofloatre.search(s):
939 raise UnderflowError, "invalid %s: %s" % (t[1], s)
942 if t[1] in ("dateTime", "date", "timeInstant", "time"):
943 return self.convertDateTime(d, t[1])
944 if t[1] == "decimal":
946 if t[1] in ("language", "QName", "NOTATION", "NMTOKEN", "Name",
947 "NCName", "ID", "IDREF", "ENTITY"):
948 return collapseWhiteSpace(d)
949 if t[1] in ("IDREFS", "ENTITIES", "NMTOKENS"):
950 d = collapseWhiteSpace(d)
953 if t[1] in ("base64", "base64Binary"):
955 return base64.decodestring(d)
958 if t[1] == "hexBinary":
960 return decodeHexString(d)
964 return urllib.unquote(collapseWhiteSpace(d))
965 if t[1] in ("normalizedString", "token"):
966 return collapseWhiteSpace(d)
970 return base64.decodestring(d)
976 e = attrs[(None, 'encoding')]
980 return decodeHexString(d)
982 return base64.decodestring(d)
988 raise Error, "unknown or missing binary encoding"
990 return urllib.unquote(collapseWhiteSpace(d))
991 if t[1] == "recurringInstant":
992 return self.convertDateTime(d, t[1])
993 if t[0] in (NS.XSD2, NS.ENC):
994 if t[1] == "uriReference":
995 return urllib.unquote(collapseWhiteSpace(d))
996 if t[1] == "timePeriod":
997 return self.convertDateTime(d, t[1])
998 if t[1] in ("century", "year"):
999 return self.convertDateTime(d, t[1])
1000 if t[0] in (NS.XSD, NS.XSD2, NS.ENC):
1001 if t[1] == "timeDuration":
1002 return self.convertDateTime(d, t[1])
1004 if t[1] == "anyURI":
1005 return urllib.unquote(collapseWhiteSpace(d))
1006 if t[1] in ("gYearMonth", "gMonthDay"):
1007 return self.convertDateTime(d, t[1])
1009 return self.convertDateTime(d, t[1])
1010 if t[1] == "gMonth":
1011 return self.convertDateTime(d, t[1])
1013 return self.convertDateTime(d, t[1])
1014 if t[1] == "duration":
1015 return self.convertDateTime(d, t[1])
1016 if t[0] in (NS.XSD2, NS.XSD3):
1018 return collapseWhiteSpace(d)
1019 if t[1] == "recurringDate":
1020 return self.convertDateTime(d, t[1])
1022 return self.convertDateTime(d, t[1])
1023 if t[1] == "recurringDay":
1024 return self.convertDateTime(d, t[1])
1027 return collapseWhiteSpace(d)
1029 raise UnknownTypeError, "unknown type `%s'" % (str(t[0]) + ':' + t[1])
1032 ################################################################################
1033 # call to SOAPParser that keeps all of the info
1034 ################################################################################
1035 def _parseSOAP(xml_str, rules = None):
1037 from cStringIO import StringIO
1039 from StringIO import StringIO
1041 parser = xml.sax.make_parser()
1042 t = SOAPParser(rules = rules)
1043 parser.setContentHandler(t)
1044 e = xml.sax.handler.ErrorHandler()
1045 parser.setErrorHandler(e)
1047 inpsrc = xml.sax.xmlreader.InputSource()
1048 inpsrc.setByteStream(StringIO(xml_str))
1050 # turn on namespace mangeling
1051 parser.setFeature(xml.sax.handler.feature_namespaces,1)
1054 parser.parse(inpsrc)
1055 except xml.sax.SAXParseException, e:
1056 parser._parser = None
1061 ################################################################################
1062 # SOAPParser's more public interface
1063 ################################################################################
1064 def parseSOAP(xml_str, attrs = 0):
1065 t = _parseSOAP(xml_str)
1068 return t.body, t.attrs
1072 def parseSOAPRPC(xml_str, header = 0, body = 0, attrs = 0, rules = None):
1074 t = _parseSOAP(xml_str, rules = rules)
1077 # Empty string, for RPC this translates into a void
1078 if type(p) in (type(''), type(u'')) and p in ('', u''):
1080 for k in t.body.__dict__.keys():
1083 p = structType(name)
1085 if header or body or attrs:
1087 if header : ret += (t.header,)
1088 if body: ret += (t.body,)
1089 if attrs: ret += (t.attrs,)