def parse_response(self, action_name, content, encoding=None, envelope_attrib=None, typed=None): """ Parses a response from the server with the given action name and content. """ register_namespace('', None) if encoding is None: encoding = self._encoding if envelope_attrib is None: envelope_attrib = self._envelope_attrib if typed is None: typed = self._typed try: docNode = xml_fromstring(content) except ParseError: # Try removing any extra XML declarations in case there are more than one. # This sometimes happens when a device sends its own XML config files. content = remove_extraneous_xml_declarations(content) docNode = xml_fromstring(content) except ValueError: # This can occur when requests returns a `str` (unicode) but there's also an XML # declaration, which lxml doesn't like. docNode = xml_fromstring(content.encode('utf8')) resp_body = None if typed: resp_body = docNode.find(".//{%s}%sResponse" % (typed, action_name)) else: resp_body = docNode.find(".//%sResponse" % action_name) if resp_body is None: msg = ( 'Returned XML did not include an element which matches namespace %r and tag name' ' \'%sResponse\'.' % (typed, action_name)) print(msg + '\n' + xml_tostring( docNode, short_empty_elements=False).decode('utf8')) raise SOAPProtocolError(msg) # Sometimes devices return XML strings as their argument values without escaping them with # CDATA. This checks to see if the argument has been parsed as XML and un-parses it if so. resp_dict = {} for arg in resp_body.getchildren(): children = arg.getchildren() if children: resp_dict[arg.tag] = "\n".join( xml_tostring(x) for x in children) else: if arg.text is None: resp_dict[arg.tag] = "" else: resp_dict[arg.tag] = arg.text return resp_dict
def create_response(self, action_name: str, arguments: dict, encoding=None, envelope_attrib=None, typed=None): """ Creates a Soap response to the action with the specified arguments. """ register_namespace('', None) if encoding is None: encoding = self._encoding if envelope_attrib is None: envelope_attrib = self._envelope_attrib if typed is None: typed = self._typed envelope = Element("s:Envelope") if envelope_attrib: for eakey, eaval in envelope_attrib: envelope.attrib.update({eakey: eaval}) else: envelope.attrib.update({'xmlns:s': NS_SOAP_ENV}) envelope.attrib.update({'s:encodingStyle': encoding}) body = SubElement(envelope, "s:Body") action_name_tag_name = action_name + "Response" methElement = SubElement(body, action_name_tag_name) if encoding: methElement.set(NS_SOAP_ENV + "encodingStyle", encoding) if arguments: for arg_name, arg_val in arguments.items(): py_type = type(arg_val) soap_type = PYTHON_TO_SOAP_TYPE_MAP[py_type] if soap_type == 'xsd:string': arg_val = arg_val # pylint: disable=self-assigning-variable elif soap_type == 'xsd:int' or soap_type == 'xsd:float': arg_val = str(arg_val) elif soap_type == 'xsd:boolean': arg_val = "1" if arg_val else "0" argElement = SubElement(methElement, arg_name) if typed and soap_type: if not isinstance(type, QName): arg_type = QName(NS_XSD, soap_type) argElement.set(NS_XSI + "type", soap_type) argElement.text = arg_val else: methElement.text = "" envelope_content = xml_tostring(envelope, short_empty_elements=False) content = XML_DOCUMENT_DECLARATION + "\n" + str_cast(envelope_content) return content
def xml_dump(obj, root=None, encoding='us-ascii'): """ Dispatch function to turn a python object into an xml string. @param obj: python container or xml root object @param root: Element instance to act as the root """ if isinstance(obj, dict): obj = _dict_toxml(obj, root) elif isinstance(obj, Iterable) and not isinstance(obj, str): obj = _iter_toxml(root, "Response", obj) return xml_tostring(obj, encoding)
def commit(self): if self._done: raise RuntimeError("Can't commit twice or after close") self._done = True data = xml_tostring(self.xml) result = yield from self.bucket._request(Request("POST", '/' + self.key, { 'uploadId': self.upload_id, }, headers={ 'CONTENT-LENGTH': str(len(data)), 'HOST': self.bucket._host, 'CONTENT-TYPE': 'application/xml', }, payload=data)) try: xml = yield from result.read() if result.status != 200: raise errors.AWSException.from_bytes(result.status, xml) xml = parse_xml(xml) return xml.find('s3:ETag', namespaces=NS) finally: result.close()
def _find_value(self, path, namespaces) -> Union[str, None]: """ Lookup a node using the path specified and then get its text value. :param path: The path to the node that is being found. :param namespaces: A dictionary of namespaces to use when processing the XML elements. :returns: The text value of the node associated with the specified path or None. """ # pylint: disable=broad-except rtnval = None try: valNode = self._ref_node.find(path, namespaces=namespaces) if valNode is not None: rtnval = valNode.text except Exception as err: print(str(err)) register_namespace('', None) xmlcontent = xml_tostring(self._ref_node) print(xmlcontent) print("") return rtnval
def commit(self): if self._done: raise RuntimeError("Can't commit twice or after close") self._done = True data = xml_tostring(self.xml) result = yield from self.bucket._request( Request("POST", '/' + self.key, { 'uploadId': self.upload_id, }, headers={ 'CONTENT-LENGTH': str(len(data)), 'HOST': self.bucket._host, 'CONTENT-TYPE': 'application/xml', }, payload=data)) try: xml = yield from result.read() if result.status != 200: raise errors.AWSException.from_bytes(result.status, xml) xml = parse_xml(xml) return xml.find('s3:ETag', namespaces=NS) finally: result.close()
def parse_response_error_for_upnp(self, action_name, content, status_code, extra=None, encoding=None, envelope_attrib=None, typed=None): """ Parse response error for a upnp response. """ register_namespace('', None) if encoding is None: encoding = self._encoding if envelope_attrib is None: envelope_attrib = self._envelope_attrib if typed is None: typed = self._typed try: docNode = xml_fromstring(content) except ParseError: # Try removing any extra XML declarations in case there are more than one. # This sometimes happens when a device sends its own XML config files. content = remove_extraneous_xml_declarations(content) docNode = xml_fromstring(content) except ValueError: # This can occur when requests returns a `str` (unicode) but there's also an XML # declaration, which lxml doesn't like. docNode = xml_fromstring(content.encode('utf8')) resp_body = None if typed: resp_body = docNode.find(".//{%s}Fault" % (NS_SOAP_ENV, )) else: resp_body = docNode.find(".//Fault") if resp_body is None: msg = ( 'Returned XML did not include an element which matches namespace %r and tag name' ' \'%sFault\'.' % (typed, action_name)) print(msg + '\n' + xml_tostring( docNode, short_empty_elements=False).decode('utf8')) raise SOAPProtocolError(msg) # Lets try to extract the XML response error information try: faultCode = resp_body.find(".//faultcode").text faultString = resp_body.find(".//faultstring").text detail = resp_body.find(".//detail") upnpErrorNode = detail.find(".//{%s}UPnPError" % NS_UPNP_CONTROL) errorCode = int( upnpErrorNode.find(".//{%s}errorCode" % NS_UPNP_CONTROL).text) errorDescription = upnpErrorNode.find(".//{%s}errorDescription" % NS_UPNP_CONTROL) if errorDescription is None: errorDescription = UPNP_ERROR_TEST_LOOKUP.get( errorCode, "Unknown error.") except Exception as xcpt: errmsg = "Unable to process xml response: status=%r\n%s" % ( status_code, content) if extra is not None: errmsg += "EXTRA:\n%s" % extra raise SOAPProtocolError(errmsg) from xcpt return errorCode, errorDescription
def record_description(self, ip_addr: str, urlBase: str, manufacturer: str, modelName: str, docTree: ElementTree, devNode: Element, namespaces: str, upnp_recording: bool = False): """ Called to record a description of a UPNP root device. :param urlBase: The base url as detailed in the description document or if not specified as determined by looking at the host information. :param manufacturer: The manufacturer of the device. :param modelName: The model name of the device. :param docTree: The XML document tree from the root of the device description. :param devNode: The 'device' element node from the device description. :param namespaces: A dictionary of namespaced to use when processing the XML document. :param upnp_recording: Force the recording of the device description and will overwrite existing device descriptions. """ manufacturerNormalized = normalize_name_for_path(manufacturer) modelName = normalize_name_for_path(modelName) root_dev_dir = os.path.join(DIR_UPNP_SCAN_INTEGRATION_ROOTDEVICES, manufacturerNormalized) if not os.path.exists(root_dev_dir): os.makedirs(root_dev_dir) root_dev_def_file = os.path.join(root_dev_dir, modelName + ".xml") if upnp_recording: console_msg_lines = [ "================================================================", " Recording Device: {}".format(ip_addr), "------------------------- Detail -------------------------------", " Manufacturer: {}".format(manufacturer), " Model: {}".format(modelName), " Device File: {}".format(root_dev_def_file) ] console_msg = os.linesep.join(console_msg_lines) print(console_msg) if upnp_recording or not os.path.exists(root_dev_def_file): docNode = docTree.getroot() register_namespace('', namespaces['']) pretty_dev_content = xml_tostring(docNode, short_empty_elements=False) with open(root_dev_def_file, 'wb') as rddf: rddf.write(pretty_dev_content) indent = " " next_indent = indent + " " embDevList = devNode.find("deviceList", namespaces=namespaces) if embDevList is not None: if upnp_recording: print("{}[ Embedded Devices ]".format(indent)) for embDevNode in embDevList: self._record_embedded_device(urlBase, manufacturerNormalized, embDevNode, namespaces, upnp_recording=upnp_recording, indent=next_indent) if upnp_recording: print("") svcList = devNode.find("serviceList", namespaces=namespaces) if svcList is not None: if upnp_recording: print("{}[ Services ]".format(indent)) for svcNode in svcList: self._record_service(urlBase, manufacturerNormalized, svcNode, namespaces, upnp_recording=upnp_recording, indent=next_indent) if upnp_recording: print("") if upnp_recording: print() return
def _record_embedded_device(self, urlBase: str, manufacturer: str, embDevNode: Element, namespaces: Optional[dict] = None, upnp_recording: bool = False, indent=" "): """ Method called for recording the description of an embedded device from a device description. :param manufacturer: The manufacturer of the device. :param embDevNode: The XML element node of the embedded device. :param namespaces: A dictionary of namespaces to use when processing the device description. :param upnp_recording: A boolean value indicating if the embedded device description should be recorded regardless of whether an existing description has been recorded. """ # pylint: disable=no-self-use deviceTypeNode = embDevNode.find("deviceType", namespaces=namespaces) if deviceTypeNode is not None: deviceType = deviceTypeNode.text dyn_dev_dir = os.path.join( DIR_UPNP_SCAN_INTEGRATION_EMBEDDEDDEVICES, manufacturer) if not os.path.exists(dyn_dev_dir): os.makedirs(dyn_dev_dir) dyn_dev_filename = os.path.join(dyn_dev_dir, deviceType + ".xml") if upnp_recording or not os.path.exists(dyn_dev_filename): if upnp_recording: dev_msg_lines = [ "{}Embedded Device Type: {}".format( indent, deviceType), "{}Embedded Device File: {}".format( indent, dyn_dev_filename) ] dev_msg = os.linesep.join(dev_msg_lines) print(dev_msg) pretty_sl_content = "" srcSvcListNode = embDevNode.find("serviceList", namespaces=namespaces) if srcSvcListNode is not None: svcs_indent = indent + " " if upnp_recording: print("{}[ Embedded Services ]".format(indent)) for svcNode in srcSvcListNode: self._record_service(urlBase, manufacturer, svcNode, namespaces, upnp_recording=upnp_recording, indent=svcs_indent) if upnp_recording: print("") register_namespace('', namespaces['']) pretty_sl_content = xml_tostring(srcSvcListNode) with open(dyn_dev_filename, 'wb') as edf: edf.write(b"<device>\n") edf.write(pretty_sl_content) edf.write(b"</device\n") return