A Python UPnP Media Server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

111 lines
3.9 KiB

  1. # Licensed under the MIT license
  2. # http://opensource.org/licenses/mit-license.php
  3. # Copyright 2007 - Frank Scholz <coherence@beebits.net>
  4. """ SOAP-lite
  5. some simple functions to implement the SOAP msgs
  6. needed by UPnP with ElementTree
  7. inspired by ElementSOAP.py
  8. """
  9. from et import ET
  10. NS_SOAP_ENV = "{http://schemas.xmlsoap.org/soap/envelope/}"
  11. NS_SOAP_ENC = "{http://schemas.xmlsoap.org/soap/encoding/}"
  12. NS_XSI = "{http://www.w3.org/1999/XMLSchema-instance}"
  13. NS_XSD = "{http://www.w3.org/1999/XMLSchema}"
  14. SOAP_ENCODING = "http://schemas.xmlsoap.org/soap/encoding/"
  15. UPNPERRORS = {401:'Invalid Action',
  16. 402:'Invalid Args',
  17. 501:'Action Failed',
  18. 600:'Argument Value Invalid',
  19. 601:'Argument Value Out of Range',
  20. 602:'Optional Action Not Implemented',
  21. 603:'Out Of Memory',
  22. 604:'Human Intervention Required',
  23. 605:'String Argument Too Long',
  24. 606:'Action Not Authorized',
  25. 607:'Signature Failure',
  26. 608:'Signature Missing',
  27. 609:'Not Encrypted',
  28. 610:'Invalid Sequence',
  29. 611:'Invalid Control URL',
  30. 612:'No Such Session',}
  31. def build_soap_error(status,description='without words'):
  32. """ builds an UPnP SOAP error msg
  33. """
  34. root = ET.Element('s:Fault')
  35. ET.SubElement(root,'faultcode').text='s:Client'
  36. ET.SubElement(root,'faultstring').text='UPnPError'
  37. e = ET.SubElement(root,'detail')
  38. e = ET.SubElement(e, 'UPnPError')
  39. e.attrib['xmlns']='urn:schemas-upnp-org:control-1-0'
  40. ET.SubElement(e,'errorCode').text=str(status)
  41. ET.SubElement(e,'errorDescription').text=UPNPERRORS.get(status,description)
  42. return build_soap_call(None, root, encoding=None)
  43. def build_soap_call(method, arguments, is_response=False,
  44. encoding=SOAP_ENCODING,
  45. envelope_attrib=None,
  46. typed=None):
  47. """ create a shell for a SOAP request or response element
  48. - set method to none to omitt the method element and
  49. add the arguments directly to the body (for an error msg)
  50. - arguments can be a dict or an ET.Element
  51. """
  52. envelope = ET.Element("s:Envelope")
  53. if envelope_attrib:
  54. for n in envelope_attrib:
  55. envelope.attrib.update({n[0] : n[1]})
  56. else:
  57. envelope.attrib.update({'s:encodingStyle' : "http://schemas.xmlsoap.org/soap/encoding/"})
  58. envelope.attrib.update({'xmlns:s' :"http://schemas.xmlsoap.org/soap/envelope/"})
  59. body = ET.SubElement(envelope, "s:Body")
  60. if method:
  61. # append the method call
  62. if is_response is True:
  63. method += "Response"
  64. re = ET.SubElement(body,method)
  65. if encoding:
  66. re.set(NS_SOAP_ENV + "encodingStyle", encoding)
  67. else:
  68. re = body
  69. # append the arguments
  70. if isinstance(arguments,dict):
  71. type_map = {str: 'xsd:string',
  72. bytes: 'xsd:string',
  73. int: 'xsd:int',
  74. float: 'xsd:float',
  75. bool: 'xsd:boolean'}
  76. for arg_name, arg_val in arguments.items():
  77. arg_type = type_map[type(arg_val)]
  78. if isinstance(arg_val, bytes):
  79. arg_val = arg_val.decode('utf-8')
  80. arg_val = str(arg_val)
  81. if arg_type == 'xsd:boolean':
  82. arg_val = arg_val.lower()
  83. e = ET.SubElement(re, arg_name)
  84. if typed and arg_type:
  85. if not isinstance(type, ET.QName):
  86. arg_type = ET.QName("http://www.w3.org/1999/XMLSchema", arg_type)
  87. e.set(NS_XSI + "type", arg_type)
  88. e.text = arg_val
  89. else:
  90. re.append(arguments)
  91. preamble = """<?xml version="1.0" encoding="utf-8"?>"""
  92. return bytes(preamble + ET.tostring(envelope, 'unicode'), 'ascii')