PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/lib/python/indra/base/llsd.py

https://bitbucket.org/lindenlab/viewer-beta/
Python | 1052 lines | 993 code | 10 blank | 49 comment | 7 complexity | e2733d1d09a28fdd1f5da1363e247fa9 MD5 | raw file
Possible License(s): LGPL-2.1
  1. """\
  2. @file llsd.py
  3. @brief Types as well as parsing and formatting functions for handling LLSD.
  4. $LicenseInfo:firstyear=2006&license=mit$
  5. Copyright (c) 2006-2009, Linden Research, Inc.
  6. Permission is hereby granted, free of charge, to any person obtaining a copy
  7. of this software and associated documentation files (the "Software"), to deal
  8. in the Software without restriction, including without limitation the rights
  9. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. copies of the Software, and to permit persons to whom the Software is
  11. furnished to do so, subject to the following conditions:
  12. The above copyright notice and this permission notice shall be included in
  13. all copies or substantial portions of the Software.
  14. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. THE SOFTWARE.
  21. $/LicenseInfo$
  22. """
  23. import datetime
  24. import base64
  25. import string
  26. import struct
  27. import time
  28. import types
  29. import re
  30. from indra.util.fastest_elementtree import ElementTreeError, fromstring
  31. from indra.base import lluuid
  32. # cllsd.c in server/server-1.25 has memory leaks,
  33. # so disabling cllsd for now
  34. #try:
  35. # import cllsd
  36. #except ImportError:
  37. # cllsd = None
  38. cllsd = None
  39. int_regex = re.compile(r"[-+]?\d+")
  40. real_regex = re.compile(r"[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?")
  41. alpha_regex = re.compile(r"[a-zA-Z]+")
  42. date_regex = re.compile(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})T"
  43. r"(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})"
  44. r"(?P<second_float>(\.\d+)?)Z")
  45. #date: d"YYYY-MM-DDTHH:MM:SS.FFFFFFZ"
  46. class LLSDParseError(Exception):
  47. pass
  48. class LLSDSerializationError(TypeError):
  49. pass
  50. class binary(str):
  51. pass
  52. class uri(str):
  53. pass
  54. BOOL_TRUE = ('1', '1.0', 'true')
  55. BOOL_FALSE = ('0', '0.0', 'false', '')
  56. def format_datestr(v):
  57. """ Formats a datetime or date object into the string format shared by xml and notation serializations."""
  58. if hasattr(v, 'microsecond'):
  59. return v.isoformat() + 'Z'
  60. else:
  61. return v.strftime('%Y-%m-%dT%H:%M:%SZ')
  62. def parse_datestr(datestr):
  63. """Parses a datetime object from the string format shared by xml and notation serializations."""
  64. if datestr == "":
  65. return datetime.datetime(1970, 1, 1)
  66. match = re.match(date_regex, datestr)
  67. if not match:
  68. raise LLSDParseError("invalid date string '%s'." % datestr)
  69. year = int(match.group('year'))
  70. month = int(match.group('month'))
  71. day = int(match.group('day'))
  72. hour = int(match.group('hour'))
  73. minute = int(match.group('minute'))
  74. second = int(match.group('second'))
  75. seconds_float = match.group('second_float')
  76. microsecond = 0
  77. if seconds_float:
  78. microsecond = int(float('0' + seconds_float) * 1e6)
  79. return datetime.datetime(year, month, day, hour, minute, second, microsecond)
  80. def bool_to_python(node):
  81. val = node.text or ''
  82. if val in BOOL_TRUE:
  83. return True
  84. else:
  85. return False
  86. def int_to_python(node):
  87. val = node.text or ''
  88. if not val.strip():
  89. return 0
  90. return int(val)
  91. def real_to_python(node):
  92. val = node.text or ''
  93. if not val.strip():
  94. return 0.0
  95. return float(val)
  96. def uuid_to_python(node):
  97. return lluuid.UUID(node.text)
  98. def str_to_python(node):
  99. return node.text or ''
  100. def bin_to_python(node):
  101. return binary(base64.decodestring(node.text or ''))
  102. def date_to_python(node):
  103. val = node.text or ''
  104. if not val:
  105. val = "1970-01-01T00:00:00Z"
  106. return parse_datestr(val)
  107. def uri_to_python(node):
  108. val = node.text or ''
  109. if not val:
  110. return None
  111. return uri(val)
  112. def map_to_python(node):
  113. result = {}
  114. for index in range(len(node))[::2]:
  115. result[node[index].text] = to_python(node[index+1])
  116. return result
  117. def array_to_python(node):
  118. return [to_python(child) for child in node]
  119. NODE_HANDLERS = dict(
  120. undef=lambda x: None,
  121. boolean=bool_to_python,
  122. integer=int_to_python,
  123. real=real_to_python,
  124. uuid=uuid_to_python,
  125. string=str_to_python,
  126. binary=bin_to_python,
  127. date=date_to_python,
  128. uri=uri_to_python,
  129. map=map_to_python,
  130. array=array_to_python,
  131. )
  132. def to_python(node):
  133. return NODE_HANDLERS[node.tag](node)
  134. class Nothing(object):
  135. pass
  136. class LLSDXMLFormatter(object):
  137. def __init__(self):
  138. self.type_map = {
  139. type(None) : self.UNDEF,
  140. bool : self.BOOLEAN,
  141. int : self.INTEGER,
  142. long : self.INTEGER,
  143. float : self.REAL,
  144. lluuid.UUID : self.UUID,
  145. binary : self.BINARY,
  146. str : self.STRING,
  147. unicode : self.STRING,
  148. uri : self.URI,
  149. datetime.datetime : self.DATE,
  150. datetime.date : self.DATE,
  151. list : self.ARRAY,
  152. tuple : self.ARRAY,
  153. types.GeneratorType : self.ARRAY,
  154. dict : self.MAP,
  155. LLSD : self.LLSD
  156. }
  157. def elt(self, name, contents=None):
  158. if(contents is None or contents is ''):
  159. return "<%s />" % (name,)
  160. else:
  161. if type(contents) is unicode:
  162. contents = contents.encode('utf-8')
  163. return "<%s>%s</%s>" % (name, contents, name)
  164. def xml_esc(self, v):
  165. if type(v) is unicode:
  166. v = v.encode('utf-8')
  167. return v.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
  168. def LLSD(self, v):
  169. return self.generate(v.thing)
  170. def UNDEF(self, v):
  171. return self.elt('undef')
  172. def BOOLEAN(self, v):
  173. if v:
  174. return self.elt('boolean', 'true')
  175. else:
  176. return self.elt('boolean', 'false')
  177. def INTEGER(self, v):
  178. return self.elt('integer', v)
  179. def REAL(self, v):
  180. return self.elt('real', v)
  181. def UUID(self, v):
  182. if(v.isNull()):
  183. return self.elt('uuid')
  184. else:
  185. return self.elt('uuid', v)
  186. def BINARY(self, v):
  187. return self.elt('binary', base64.encodestring(v))
  188. def STRING(self, v):
  189. return self.elt('string', self.xml_esc(v))
  190. def URI(self, v):
  191. return self.elt('uri', self.xml_esc(str(v)))
  192. def DATE(self, v):
  193. return self.elt('date', format_datestr(v))
  194. def ARRAY(self, v):
  195. return self.elt('array', ''.join([self.generate(item) for item in v]))
  196. def MAP(self, v):
  197. return self.elt(
  198. 'map',
  199. ''.join(["%s%s" % (self.elt('key', self.xml_esc(str(key))), self.generate(value))
  200. for key, value in v.items()]))
  201. typeof = type
  202. def generate(self, something):
  203. t = self.typeof(something)
  204. if self.type_map.has_key(t):
  205. return self.type_map[t](something)
  206. else:
  207. raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % (
  208. t, something))
  209. def _format(self, something):
  210. return '<?xml version="1.0" ?>' + self.elt("llsd", self.generate(something))
  211. def format(self, something):
  212. if cllsd:
  213. return cllsd.llsd_to_xml(something)
  214. return self._format(something)
  215. _g_xml_formatter = None
  216. def format_xml(something):
  217. global _g_xml_formatter
  218. if _g_xml_formatter is None:
  219. _g_xml_formatter = LLSDXMLFormatter()
  220. return _g_xml_formatter.format(something)
  221. class LLSDXMLPrettyFormatter(LLSDXMLFormatter):
  222. def __init__(self, indent_atom = None):
  223. # Call the super class constructor so that we have the type map
  224. super(LLSDXMLPrettyFormatter, self).__init__()
  225. # Override the type map to use our specialized formatters to
  226. # emit the pretty output.
  227. self.type_map[list] = self.PRETTY_ARRAY
  228. self.type_map[tuple] = self.PRETTY_ARRAY
  229. self.type_map[types.GeneratorType] = self.PRETTY_ARRAY,
  230. self.type_map[dict] = self.PRETTY_MAP
  231. # Private data used for indentation.
  232. self._indent_level = 1
  233. if indent_atom is None:
  234. self._indent_atom = ' '
  235. else:
  236. self._indent_atom = indent_atom
  237. def _indent(self):
  238. "Return an indentation based on the atom and indentation level."
  239. return self._indent_atom * self._indent_level
  240. def PRETTY_ARRAY(self, v):
  241. rv = []
  242. rv.append('<array>\n')
  243. self._indent_level = self._indent_level + 1
  244. rv.extend(["%s%s\n" %
  245. (self._indent(),
  246. self.generate(item))
  247. for item in v])
  248. self._indent_level = self._indent_level - 1
  249. rv.append(self._indent())
  250. rv.append('</array>')
  251. return ''.join(rv)
  252. def PRETTY_MAP(self, v):
  253. rv = []
  254. rv.append('<map>\n')
  255. self._indent_level = self._indent_level + 1
  256. keys = v.keys()
  257. keys.sort()
  258. rv.extend(["%s%s\n%s%s\n" %
  259. (self._indent(),
  260. self.elt('key', key),
  261. self._indent(),
  262. self.generate(v[key]))
  263. for key in keys])
  264. self._indent_level = self._indent_level - 1
  265. rv.append(self._indent())
  266. rv.append('</map>')
  267. return ''.join(rv)
  268. def format(self, something):
  269. data = []
  270. data.append('<?xml version="1.0" ?>\n<llsd>')
  271. data.append(self.generate(something))
  272. data.append('</llsd>\n')
  273. return '\n'.join(data)
  274. def format_pretty_xml(something):
  275. """@brief Serialize a python object as 'pretty' llsd xml.
  276. The output conforms to the LLSD DTD, unlike the output from the
  277. standard python xml.dom DOM::toprettyxml() method which does not
  278. preserve significant whitespace.
  279. This function is not necessarily suited for serializing very large
  280. objects. It is not optimized by the cllsd module, and sorts on
  281. dict (llsd map) keys alphabetically to ease human reading.
  282. """
  283. return LLSDXMLPrettyFormatter().format(something)
  284. class LLSDNotationFormatter(object):
  285. def __init__(self):
  286. self.type_map = {
  287. type(None) : self.UNDEF,
  288. bool : self.BOOLEAN,
  289. int : self.INTEGER,
  290. long : self.INTEGER,
  291. float : self.REAL,
  292. lluuid.UUID : self.UUID,
  293. binary : self.BINARY,
  294. str : self.STRING,
  295. unicode : self.STRING,
  296. uri : self.URI,
  297. datetime.datetime : self.DATE,
  298. datetime.date : self.DATE,
  299. list : self.ARRAY,
  300. tuple : self.ARRAY,
  301. types.GeneratorType : self.ARRAY,
  302. dict : self.MAP,
  303. LLSD : self.LLSD
  304. }
  305. def LLSD(self, v):
  306. return self.generate(v.thing)
  307. def UNDEF(self, v):
  308. return '!'
  309. def BOOLEAN(self, v):
  310. if v:
  311. return 'true'
  312. else:
  313. return 'false'
  314. def INTEGER(self, v):
  315. return "i%s" % v
  316. def REAL(self, v):
  317. return "r%s" % v
  318. def UUID(self, v):
  319. return "u%s" % v
  320. def BINARY(self, v):
  321. return 'b64"' + base64.encodestring(v) + '"'
  322. def STRING(self, v):
  323. if isinstance(v, unicode):
  324. v = v.encode('utf-8')
  325. return "'%s'" % v.replace("\\", "\\\\").replace("'", "\\'")
  326. def URI(self, v):
  327. return 'l"%s"' % str(v).replace("\\", "\\\\").replace('"', '\\"')
  328. def DATE(self, v):
  329. return 'd"%s"' % format_datestr(v)
  330. def ARRAY(self, v):
  331. return "[%s]" % ','.join([self.generate(item) for item in v])
  332. def MAP(self, v):
  333. def fix(key):
  334. if isinstance(key, unicode):
  335. return key.encode('utf-8')
  336. return key
  337. return "{%s}" % ','.join(["'%s':%s" % (fix(key).replace("\\", "\\\\").replace("'", "\\'"), self.generate(value))
  338. for key, value in v.items()])
  339. def generate(self, something):
  340. t = type(something)
  341. handler = self.type_map.get(t)
  342. if handler:
  343. return handler(something)
  344. else:
  345. try:
  346. return self.ARRAY(iter(something))
  347. except TypeError:
  348. raise LLSDSerializationError(
  349. "Cannot serialize unknown type: %s (%s)" % (t, something))
  350. def format(self, something):
  351. return self.generate(something)
  352. def format_notation(something):
  353. return LLSDNotationFormatter().format(something)
  354. def _hex_as_nybble(hex):
  355. if (hex >= '0') and (hex <= '9'):
  356. return ord(hex) - ord('0')
  357. elif (hex >= 'a') and (hex <='f'):
  358. return 10 + ord(hex) - ord('a')
  359. elif (hex >= 'A') and (hex <='F'):
  360. return 10 + ord(hex) - ord('A');
  361. class LLSDBinaryParser(object):
  362. def __init__(self):
  363. pass
  364. def parse(self, buffer, ignore_binary = False):
  365. """
  366. This is the basic public interface for parsing.
  367. @param buffer the binary data to parse in an indexable sequence.
  368. @param ignore_binary parser throws away data in llsd binary nodes.
  369. @return returns a python object.
  370. """
  371. self._buffer = buffer
  372. self._index = 0
  373. self._keep_binary = not ignore_binary
  374. return self._parse()
  375. def _parse(self):
  376. cc = self._buffer[self._index]
  377. self._index += 1
  378. if cc == '{':
  379. return self._parse_map()
  380. elif cc == '[':
  381. return self._parse_array()
  382. elif cc == '!':
  383. return None
  384. elif cc == '0':
  385. return False
  386. elif cc == '1':
  387. return True
  388. elif cc == 'i':
  389. # 'i' = integer
  390. idx = self._index
  391. self._index += 4
  392. return struct.unpack("!i", self._buffer[idx:idx+4])[0]
  393. elif cc == ('r'):
  394. # 'r' = real number
  395. idx = self._index
  396. self._index += 8
  397. return struct.unpack("!d", self._buffer[idx:idx+8])[0]
  398. elif cc == 'u':
  399. # 'u' = uuid
  400. idx = self._index
  401. self._index += 16
  402. return lluuid.uuid_bits_to_uuid(self._buffer[idx:idx+16])
  403. elif cc == 's':
  404. # 's' = string
  405. return self._parse_string()
  406. elif cc in ("'", '"'):
  407. # delimited/escaped string
  408. return self._parse_string_delim(cc)
  409. elif cc == 'l':
  410. # 'l' = uri
  411. return uri(self._parse_string())
  412. elif cc == ('d'):
  413. # 'd' = date in seconds since epoch
  414. idx = self._index
  415. self._index += 8
  416. seconds = struct.unpack("!d", self._buffer[idx:idx+8])[0]
  417. return datetime.datetime.fromtimestamp(seconds)
  418. elif cc == 'b':
  419. binary = self._parse_string()
  420. if self._keep_binary:
  421. return binary
  422. # *NOTE: maybe have a binary placeholder which has the
  423. # length.
  424. return None
  425. else:
  426. raise LLSDParseError("invalid binary token at byte %d: %d" % (
  427. self._index - 1, ord(cc)))
  428. def _parse_map(self):
  429. rv = {}
  430. size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0]
  431. self._index += 4
  432. count = 0
  433. cc = self._buffer[self._index]
  434. self._index += 1
  435. key = ''
  436. while (cc != '}') and (count < size):
  437. if cc == 'k':
  438. key = self._parse_string()
  439. elif cc in ("'", '"'):
  440. key = self._parse_string_delim(cc)
  441. else:
  442. raise LLSDParseError("invalid map key at byte %d." % (
  443. self._index - 1,))
  444. value = self._parse()
  445. rv[key] = value
  446. count += 1
  447. cc = self._buffer[self._index]
  448. self._index += 1
  449. if cc != '}':
  450. raise LLSDParseError("invalid map close token at byte %d." % (
  451. self._index,))
  452. return rv
  453. def _parse_array(self):
  454. rv = []
  455. size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0]
  456. self._index += 4
  457. count = 0
  458. cc = self._buffer[self._index]
  459. while (cc != ']') and (count < size):
  460. rv.append(self._parse())
  461. count += 1
  462. cc = self._buffer[self._index]
  463. if cc != ']':
  464. raise LLSDParseError("invalid array close token at byte %d." % (
  465. self._index,))
  466. self._index += 1
  467. return rv
  468. def _parse_string(self):
  469. size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0]
  470. self._index += 4
  471. rv = self._buffer[self._index:self._index+size]
  472. self._index += size
  473. return rv
  474. def _parse_string_delim(self, delim):
  475. list = []
  476. found_escape = False
  477. found_hex = False
  478. found_digit = False
  479. byte = 0
  480. while True:
  481. cc = self._buffer[self._index]
  482. self._index += 1
  483. if found_escape:
  484. if found_hex:
  485. if found_digit:
  486. found_escape = False
  487. found_hex = False
  488. found_digit = False
  489. byte <<= 4
  490. byte |= _hex_as_nybble(cc)
  491. list.append(chr(byte))
  492. byte = 0
  493. else:
  494. found_digit = True
  495. byte = _hex_as_nybble(cc)
  496. elif cc == 'x':
  497. found_hex = True
  498. else:
  499. if cc == 'a':
  500. list.append('\a')
  501. elif cc == 'b':
  502. list.append('\b')
  503. elif cc == 'f':
  504. list.append('\f')
  505. elif cc == 'n':
  506. list.append('\n')
  507. elif cc == 'r':
  508. list.append('\r')
  509. elif cc == 't':
  510. list.append('\t')
  511. elif cc == 'v':
  512. list.append('\v')
  513. else:
  514. list.append(cc)
  515. found_escape = False
  516. elif cc == '\\':
  517. found_escape = True
  518. elif cc == delim:
  519. break
  520. else:
  521. list.append(cc)
  522. return ''.join(list)
  523. class LLSDNotationParser(object):
  524. """ Parse LLSD notation:
  525. map: { string:object, string:object }
  526. array: [ object, object, object ]
  527. undef: !
  528. boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE
  529. integer: i####
  530. real: r####
  531. uuid: u####
  532. string: "g\'day" | 'have a "nice" day' | s(size)"raw data"
  533. uri: l"escaped"
  534. date: d"YYYY-MM-DDTHH:MM:SS.FFZ"
  535. binary: b##"ff3120ab1" | b(size)"raw data"
  536. """
  537. def __init__(self):
  538. pass
  539. def parse(self, buffer, ignore_binary = False):
  540. """
  541. This is the basic public interface for parsing.
  542. @param buffer the notation string to parse.
  543. @param ignore_binary parser throws away data in llsd binary nodes.
  544. @return returns a python object.
  545. """
  546. if buffer == "":
  547. return False
  548. self._buffer = buffer
  549. self._index = 0
  550. return self._parse()
  551. def _parse(self):
  552. cc = self._buffer[self._index]
  553. self._index += 1
  554. if cc == '{':
  555. return self._parse_map()
  556. elif cc == '[':
  557. return self._parse_array()
  558. elif cc == '!':
  559. return None
  560. elif cc == '0':
  561. return False
  562. elif cc == '1':
  563. return True
  564. elif cc in ('F', 'f'):
  565. self._skip_alpha()
  566. return False
  567. elif cc in ('T', 't'):
  568. self._skip_alpha()
  569. return True
  570. elif cc == 'i':
  571. # 'i' = integer
  572. return self._parse_integer()
  573. elif cc == ('r'):
  574. # 'r' = real number
  575. return self._parse_real()
  576. elif cc == 'u':
  577. # 'u' = uuid
  578. return self._parse_uuid()
  579. elif cc in ("'", '"', 's'):
  580. return self._parse_string(cc)
  581. elif cc == 'l':
  582. # 'l' = uri
  583. delim = self._buffer[self._index]
  584. self._index += 1
  585. val = uri(self._parse_string(delim))
  586. if len(val) == 0:
  587. return None
  588. return val
  589. elif cc == ('d'):
  590. # 'd' = date in seconds since epoch
  591. return self._parse_date()
  592. elif cc == 'b':
  593. return self._parse_binary()
  594. else:
  595. raise LLSDParseError("invalid token at index %d: %d" % (
  596. self._index - 1, ord(cc)))
  597. def _parse_binary(self):
  598. i = self._index
  599. if self._buffer[i:i+2] == '64':
  600. q = self._buffer[i+2]
  601. e = self._buffer.find(q, i+3)
  602. try:
  603. return base64.decodestring(self._buffer[i+3:e])
  604. finally:
  605. self._index = e + 1
  606. else:
  607. raise LLSDParseError('random horrible binary format not supported')
  608. def _parse_map(self):
  609. """ map: { string:object, string:object } """
  610. rv = {}
  611. cc = self._buffer[self._index]
  612. self._index += 1
  613. key = ''
  614. found_key = False
  615. while (cc != '}'):
  616. if not found_key:
  617. if cc in ("'", '"', 's'):
  618. key = self._parse_string(cc)
  619. found_key = True
  620. elif cc.isspace() or cc == ',':
  621. cc = self._buffer[self._index]
  622. self._index += 1
  623. else:
  624. raise LLSDParseError("invalid map key at byte %d." % (
  625. self._index - 1,))
  626. elif cc.isspace() or cc == ':':
  627. cc = self._buffer[self._index]
  628. self._index += 1
  629. continue
  630. else:
  631. self._index += 1
  632. value = self._parse()
  633. rv[key] = value
  634. found_key = False
  635. cc = self._buffer[self._index]
  636. self._index += 1
  637. return rv
  638. def _parse_array(self):
  639. """ array: [ object, object, object ] """
  640. rv = []
  641. cc = self._buffer[self._index]
  642. while (cc != ']'):
  643. if cc.isspace() or cc == ',':
  644. self._index += 1
  645. cc = self._buffer[self._index]
  646. continue
  647. rv.append(self._parse())
  648. cc = self._buffer[self._index]
  649. if cc != ']':
  650. raise LLSDParseError("invalid array close token at index %d." % (
  651. self._index,))
  652. self._index += 1
  653. return rv
  654. def _parse_uuid(self):
  655. match = re.match(lluuid.UUID.uuid_regex, self._buffer[self._index:])
  656. if not match:
  657. raise LLSDParseError("invalid uuid token at index %d." % self._index)
  658. (start, end) = match.span()
  659. start += self._index
  660. end += self._index
  661. self._index = end
  662. return lluuid.UUID(self._buffer[start:end])
  663. def _skip_alpha(self):
  664. match = re.match(alpha_regex, self._buffer[self._index:])
  665. if match:
  666. self._index += match.end()
  667. def _parse_date(self):
  668. delim = self._buffer[self._index]
  669. self._index += 1
  670. datestr = self._parse_string(delim)
  671. return parse_datestr(datestr)
  672. def _parse_real(self):
  673. match = re.match(real_regex, self._buffer[self._index:])
  674. if not match:
  675. raise LLSDParseError("invalid real token at index %d." % self._index)
  676. (start, end) = match.span()
  677. start += self._index
  678. end += self._index
  679. self._index = end
  680. return float( self._buffer[start:end] )
  681. def _parse_integer(self):
  682. match = re.match(int_regex, self._buffer[self._index:])
  683. if not match:
  684. raise LLSDParseError("invalid integer token at index %d." % self._index)
  685. (start, end) = match.span()
  686. start += self._index
  687. end += self._index
  688. self._index = end
  689. return int( self._buffer[start:end] )
  690. def _parse_string(self, delim):
  691. """ string: "g\'day" | 'have a "nice" day' | s(size)"raw data" """
  692. rv = ""
  693. if delim in ("'", '"'):
  694. rv = self._parse_string_delim(delim)
  695. elif delim == 's':
  696. rv = self._parse_string_raw()
  697. else:
  698. raise LLSDParseError("invalid string token at index %d." % self._index)
  699. return rv
  700. def _parse_string_delim(self, delim):
  701. """ string: "g'day 'un" | 'have a "nice" day' """
  702. list = []
  703. found_escape = False
  704. found_hex = False
  705. found_digit = False
  706. byte = 0
  707. while True:
  708. cc = self._buffer[self._index]
  709. self._index += 1
  710. if found_escape:
  711. if found_hex:
  712. if found_digit:
  713. found_escape = False
  714. found_hex = False
  715. found_digit = False
  716. byte <<= 4
  717. byte |= _hex_as_nybble(cc)
  718. list.append(chr(byte))
  719. byte = 0
  720. else:
  721. found_digit = True
  722. byte = _hex_as_nybble(cc)
  723. elif cc == 'x':
  724. found_hex = True
  725. else:
  726. if cc == 'a':
  727. list.append('\a')
  728. elif cc == 'b':
  729. list.append('\b')
  730. elif cc == 'f':
  731. list.append('\f')
  732. elif cc == 'n':
  733. list.append('\n')
  734. elif cc == 'r':
  735. list.append('\r')
  736. elif cc == 't':
  737. list.append('\t')
  738. elif cc == 'v':
  739. list.append('\v')
  740. else:
  741. list.append(cc)
  742. found_escape = False
  743. elif cc == '\\':
  744. found_escape = True
  745. elif cc == delim:
  746. break
  747. else:
  748. list.append(cc)
  749. return ''.join(list)
  750. def _parse_string_raw(self):
  751. """ string: s(size)"raw data" """
  752. # Read the (size) portion.
  753. cc = self._buffer[self._index]
  754. self._index += 1
  755. if cc != '(':
  756. raise LLSDParseError("invalid string token at index %d." % self._index)
  757. rparen = self._buffer.find(')', self._index)
  758. if rparen == -1:
  759. raise LLSDParseError("invalid string token at index %d." % self._index)
  760. size = int(self._buffer[self._index:rparen])
  761. self._index = rparen + 1
  762. delim = self._buffer[self._index]
  763. self._index += 1
  764. if delim not in ("'", '"'):
  765. raise LLSDParseError("invalid string token at index %d." % self._index)
  766. rv = self._buffer[self._index:(self._index + size)]
  767. self._index += size
  768. cc = self._buffer[self._index]
  769. self._index += 1
  770. if cc != delim:
  771. raise LLSDParseError("invalid string token at index %d." % self._index)
  772. return rv
  773. def format_binary(something):
  774. return '<?llsd/binary?>\n' + _format_binary_recurse(something)
  775. def _format_binary_recurse(something):
  776. def _format_list(something):
  777. array_builder = []
  778. array_builder.append('[' + struct.pack('!i', len(something)))
  779. for item in something:
  780. array_builder.append(_format_binary_recurse(item))
  781. array_builder.append(']')
  782. return ''.join(array_builder)
  783. if something is None:
  784. return '!'
  785. elif isinstance(something, LLSD):
  786. return _format_binary_recurse(something.thing)
  787. elif isinstance(something, bool):
  788. if something:
  789. return '1'
  790. else:
  791. return '0'
  792. elif isinstance(something, (int, long)):
  793. return 'i' + struct.pack('!i', something)
  794. elif isinstance(something, float):
  795. return 'r' + struct.pack('!d', something)
  796. elif isinstance(something, lluuid.UUID):
  797. return 'u' + something._bits
  798. elif isinstance(something, binary):
  799. return 'b' + struct.pack('!i', len(something)) + something
  800. elif isinstance(something, str):
  801. return 's' + struct.pack('!i', len(something)) + something
  802. elif isinstance(something, unicode):
  803. something = something.encode('utf-8')
  804. return 's' + struct.pack('!i', len(something)) + something
  805. elif isinstance(something, uri):
  806. return 'l' + struct.pack('!i', len(something)) + something
  807. elif isinstance(something, datetime.datetime):
  808. seconds_since_epoch = time.mktime(something.timetuple())
  809. return 'd' + struct.pack('!d', seconds_since_epoch)
  810. elif isinstance(something, (list, tuple)):
  811. return _format_list(something)
  812. elif isinstance(something, dict):
  813. map_builder = []
  814. map_builder.append('{' + struct.pack('!i', len(something)))
  815. for key, value in something.items():
  816. if isinstance(key, unicode):
  817. key = key.encode('utf-8')
  818. map_builder.append('k' + struct.pack('!i', len(key)) + key)
  819. map_builder.append(_format_binary_recurse(value))
  820. map_builder.append('}')
  821. return ''.join(map_builder)
  822. else:
  823. try:
  824. return _format_list(list(something))
  825. except TypeError:
  826. raise LLSDSerializationError(
  827. "Cannot serialize unknown type: %s (%s)" %
  828. (type(something), something))
  829. def parse_binary(binary):
  830. if binary.startswith('<?llsd/binary?>'):
  831. just_binary = binary.split('\n', 1)[1]
  832. else:
  833. just_binary = binary
  834. return LLSDBinaryParser().parse(just_binary)
  835. def parse_xml(something):
  836. try:
  837. return to_python(fromstring(something)[0])
  838. except ElementTreeError, err:
  839. raise LLSDParseError(*err.args)
  840. def parse_notation(something):
  841. return LLSDNotationParser().parse(something)
  842. def parse(something):
  843. try:
  844. something = string.lstrip(something) #remove any pre-trailing whitespace
  845. if something.startswith('<?llsd/binary?>'):
  846. return parse_binary(something)
  847. # This should be better.
  848. elif something.startswith('<'):
  849. return parse_xml(something)
  850. else:
  851. return parse_notation(something)
  852. except KeyError, e:
  853. raise Exception('LLSD could not be parsed: %s' % (e,))
  854. class LLSD(object):
  855. def __init__(self, thing=None):
  856. self.thing = thing
  857. def __str__(self):
  858. return self.toXML(self.thing)
  859. parse = staticmethod(parse)
  860. toXML = staticmethod(format_xml)
  861. toPrettyXML = staticmethod(format_pretty_xml)
  862. toBinary = staticmethod(format_binary)
  863. toNotation = staticmethod(format_notation)
  864. undef = LLSD(None)
  865. XML_MIME_TYPE = 'application/llsd+xml'
  866. BINARY_MIME_TYPE = 'application/llsd+binary'
  867. # register converters for llsd in mulib, if it is available
  868. try:
  869. from mulib import stacked, mu
  870. stacked.NoProducer() # just to exercise stacked
  871. mu.safe_load(None) # just to exercise mu
  872. except:
  873. # mulib not available, don't print an error message since this is normal
  874. pass
  875. else:
  876. mu.add_parser(parse, XML_MIME_TYPE)
  877. mu.add_parser(parse, 'application/llsd+binary')
  878. def llsd_convert_xml(llsd_stuff, request):
  879. request.write(format_xml(llsd_stuff))
  880. def llsd_convert_binary(llsd_stuff, request):
  881. request.write(format_binary(llsd_stuff))
  882. for typ in [LLSD, dict, list, tuple, str, int, long, float, bool, unicode, type(None)]:
  883. stacked.add_producer(typ, llsd_convert_xml, XML_MIME_TYPE)
  884. stacked.add_producer(typ, llsd_convert_xml, 'application/xml')
  885. stacked.add_producer(typ, llsd_convert_xml, 'text/xml')
  886. stacked.add_producer(typ, llsd_convert_binary, 'application/llsd+binary')
  887. stacked.add_producer(LLSD, llsd_convert_xml, '*/*')
  888. # in case someone is using the legacy mu.xml wrapper, we need to
  889. # tell mu to produce application/xml or application/llsd+xml
  890. # (based on the accept header) from raw xml. Phoenix 2008-07-21
  891. stacked.add_producer(mu.xml, mu.produce_raw, XML_MIME_TYPE)
  892. stacked.add_producer(mu.xml, mu.produce_raw, 'application/xml')
  893. # mulib wsgi stuff
  894. # try:
  895. # from mulib import mu, adapters
  896. #
  897. # # try some known attributes from mulib to be ultra-sure we've imported it
  898. # mu.get_current
  899. # adapters.handlers
  900. # except:
  901. # # mulib not available, don't print an error message since this is normal
  902. # pass
  903. # else:
  904. # def llsd_xml_handler(content_type):
  905. # def handle_llsd_xml(env, start_response):
  906. # llsd_stuff, _ = mu.get_current(env)
  907. # result = format_xml(llsd_stuff)
  908. # start_response("200 OK", [('Content-Type', content_type)])
  909. # env['mu.negotiated_type'] = content_type
  910. # yield result
  911. # return handle_llsd_xml
  912. #
  913. # def llsd_binary_handler(content_type):
  914. # def handle_llsd_binary(env, start_response):
  915. # llsd_stuff, _ = mu.get_current(env)
  916. # result = format_binary(llsd_stuff)
  917. # start_response("200 OK", [('Content-Type', content_type)])
  918. # env['mu.negotiated_type'] = content_type
  919. # yield result
  920. # return handle_llsd_binary
  921. #
  922. # adapters.DEFAULT_PARSERS[XML_MIME_TYPE] = parse
  923. # for typ in [LLSD, dict, list, tuple, str, int, float, bool, unicode, type(None)]:
  924. # for content_type in (XML_MIME_TYPE, 'application/xml'):
  925. # adapters.handlers.set_handler(typ, llsd_xml_handler(content_type), content_type)
  926. #
  927. # adapters.handlers.set_handler(typ, llsd_binary_handler(BINARY_MIME_TYPE), BINARY_MIME_TYPE)
  928. #
  929. # adapters.handlers.set_handler(LLSD, llsd_xml_handler(XML_MIME_TYPE), '*/*')