From 4a4d5de08d6483a348f6b04b9c8b94e4dc5a60d8 Mon Sep 17 00:00:00 2001 From: Ludwig Wacker Date: Fri, 5 Aug 2016 11:58:31 +0200 Subject: [PATCH 1/3] LUNA-42: Catch ET ParseError and re-try using the old behaviour (encode only) --- resources/lib/nvhttp/nvhttp.py | 109 +++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 39 deletions(-) diff --git a/resources/lib/nvhttp/nvhttp.py b/resources/lib/nvhttp/nvhttp.py index d0d5f56..6c1051f 100644 --- a/resources/lib/nvhttp/nvhttp.py +++ b/resources/lib/nvhttp/nvhttp.py @@ -72,18 +72,22 @@ def get_server_info(self): return response.content def get_computer_details(self): - server_info = ET.ElementTree(ET.fromstring(self.re_encode_string(self.get_server_info()))).getroot() - - host = HostDetails() - host.name = self.get_xml_string(server_info, 'hostname') - host.uuid = self.get_xml_string(server_info, 'uniqueid') - host.mac_address = self.get_xml_string(server_info, 'mac') - host.local_ip = self.get_xml_string(server_info, 'LocalIP') - host.remote_ip = self.get_xml_string(server_info, 'ExternalIP') - host.pair_state = int(self.get_xml_string(server_info, 'PairStatus')) - host.state = HostDetails.STATE_ONLINE - - return host + etree = self.build_etree(self.get_server_info()) + if etree is not None: + server_info = ET.ElementTree(etree).getroot() + + host = HostDetails() + host.name = self.get_xml_string(server_info, 'hostname') + host.uuid = self.get_xml_string(server_info, 'uniqueid') + host.mac_address = self.get_xml_string(server_info, 'mac') + host.local_ip = self.get_xml_string(server_info, 'LocalIP') + host.remote_ip = self.get_xml_string(server_info, 'ExternalIP') + host.pair_state = int(self.get_xml_string(server_info, 'PairStatus')) + host.state = HostDetails.STATE_ONLINE + + return host + else: + raise ValueError('ETree is not set.') def open_http_connection(self, url, enable_read_timeout, content_only=True): try: @@ -148,26 +152,31 @@ def get_app_list(self): return applist def get_app_list_from_string(self, xml_string): - applist_root = ET.ElementTree(ET.fromstring(self.re_encode_string(xml_string))).getroot() - applist = [] - - for app in applist_root.findall('App'): - nvapp = NvApp() - if app.find('AppInstallPath') is not None: - nvapp.install_path = app.find('AppInstallPath').text - if app.find('AppTitle') is not None: - nvapp.title = app.find('AppTitle').text.encode('UTF-8') - if app.find('Distributor') is not None: - nvapp.distributor = app.find('Distributor').text - if app.find('ID') is not None: - nvapp.id = app.find('ID').text - if app.find('MaxControllersForSingleSession') is not None: - nvapp.max_controllers = app.find('MaxControllersForSingleSession').text - if app.find('ShortName') is not None: - nvapp.short_name = app.find('ShortName').text - applist.append(nvapp) - - return applist + etree = self.build_etree(xml_string) + if etree is not None: + # ET.fromstring(self.re_encode_string(xml_string)) + applist_root = ET.ElementTree(etree).getroot() + applist = [] + + for app in applist_root.findall('App'): + nvapp = NvApp() + if app.find('AppInstallPath') is not None: + nvapp.install_path = app.find('AppInstallPath').text + if app.find('AppTitle') is not None: + nvapp.title = app.find('AppTitle').text.encode('UTF-8') + if app.find('Distributor') is not None: + nvapp.distributor = app.find('Distributor').text + if app.find('ID') is not None: + nvapp.id = app.find('ID').text + if app.find('MaxControllersForSingleSession') is not None: + nvapp.max_controllers = app.find('MaxControllersForSingleSession').text + if app.find('ShortName') is not None: + nvapp.short_name = app.find('ShortName').text + applist.append(nvapp) + + return applist + else: + raise ValueError('ETree is not set.') def get_box_art(self, app_id, asset_type=2, asset_idx=0): # TODO: What are the other asset types and indices? @@ -207,7 +216,7 @@ def load_or_generate_uid(self): return str(uid) - def re_encode_string(self, xml_string): + def re_encode_string(self, xml_string, only_encode=False): logger = RequiredFeature('logger').request() regex = re.compile('UTF-\d{1,2}') @@ -216,12 +225,34 @@ def re_encode_string(self, xml_string): if specified_encoding is not None: try: logger.info("Trying to re-encode received XML as %s" % specified_encoding.group(0)) - xml_string = xml_string.decode(specified_encoding.group(0)) - xml_string = xml_string.encode(specified_encoding.group(0)) - except UnicodeDecodeError: - logger.info( - "Re-encode failed, trying to decode as UTF-8 and re-encoding as %s." % specified_encoding.group(0)) - xml_string = xml_string.decode('UTF-8') + if not only_encode: + xml_string = xml_string.decode(specified_encoding.group(0)) xml_string = xml_string.encode(specified_encoding.group(0)) + except (UnicodeDecodeError, UnicodeEncodeError) as e: + if not only_encode: + logger.info( + "Re-encode failed, trying to decode as UTF-8") + xml_string = xml_string.decode('UTF-8') + if isinstance(e, UnicodeEncodeError): + encoding = 'UTF-8' if specified_encoding.group(0) == 'UTF-8' else 'UTF-16' + logger.info("Trying to encode as: %s" % encoding) + xml_string = xml_string.encode(encoding) + else: + logger.info("Trying to encode as: %s" % specified_encoding.group(0)) + xml_string = xml_string.encode(specified_encoding.group(0)) return xml_string + + def build_etree(self, xml_string): + try: + etree = ET.fromstring(self.re_encode_string(xml_string)) + except ET.ParseError: + try: + etree = ET.fromstring(self.re_encode_string(xml_string, only_encode=True)) + except ET.ParseError as e: + logger = RequiredFeature('logger').request() + logger.error("Building ETree from XML failed: %s" % e.message) + logger.info(xml_string) + return None + + return etree From 01b22c32024751bed48e3d8298328e38cc4a25c0 Mon Sep 17 00:00:00 2001 From: Ludwig Wacker Date: Fri, 5 Aug 2016 12:39:07 +0200 Subject: [PATCH 2/3] LUNA-42: Bump version --- addon.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon.xml b/addon.xml index 498891e..716e44b 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + From 2a9d14beacd40e0640c4f31490ba95db5f074b66 Mon Sep 17 00:00:00 2001 From: Ludwig Wacker Date: Sat, 6 Aug 2016 15:07:43 +0200 Subject: [PATCH 3/3] LUNA-41: Try all possible decodes and encodes before giving up --- resources/lib/nvhttp/nvhttp.py | 79 ++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/resources/lib/nvhttp/nvhttp.py b/resources/lib/nvhttp/nvhttp.py index 6c1051f..024d63b 100644 --- a/resources/lib/nvhttp/nvhttp.py +++ b/resources/lib/nvhttp/nvhttp.py @@ -216,43 +216,68 @@ def load_or_generate_uid(self): return str(uid) - def re_encode_string(self, xml_string, only_encode=False): + def re_encode_string(self, xml_string): logger = RequiredFeature('logger').request() regex = re.compile('UTF-\d{1,2}') specified_encoding = regex.search(xml_string) + logger.info("Trying to decode as: %s" % 'ASCII') + try: + xml_string = xml_string.decode(encoding='ascii') + except UnicodeDecodeError as e: + logger.info("Decoding as %s failed, trying as %s" % ('ASCII', 'UTF-8')) + try: + xml_string = xml_string.decode(encoding='UTF-8') + except UnicodeDecodeError as e: + logger.info("Decoding as %s failed, trying as %s" % ('UTF-8', 'UTF-16')) + try: + xml_string = xml_string.decode(encoding='UTF-16') + except UnicodeDecodeError as e: + logger.error("Decoding as UTF-16 failed, this was the last attempt. Offending string follows ...") + logger.error(xml_string) + raise ValueError("String Decode Failed") + if specified_encoding is not None: try: - logger.info("Trying to re-encode received XML as %s" % specified_encoding.group(0)) - if not only_encode: - xml_string = xml_string.decode(specified_encoding.group(0)) - xml_string = xml_string.encode(specified_encoding.group(0)) - except (UnicodeDecodeError, UnicodeEncodeError) as e: - if not only_encode: - logger.info( - "Re-encode failed, trying to decode as UTF-8") - xml_string = xml_string.decode('UTF-8') - if isinstance(e, UnicodeEncodeError): - encoding = 'UTF-8' if specified_encoding.group(0) == 'UTF-8' else 'UTF-16' - logger.info("Trying to encode as: %s" % encoding) - xml_string = xml_string.encode(encoding) - else: - logger.info("Trying to encode as: %s" % specified_encoding.group(0)) - xml_string = xml_string.encode(specified_encoding.group(0)) - - return xml_string + logger.info("Trying to encode as specified in XML: %s" % specified_encoding.group(0)) + xml_string = xml_string.encode(encoding=specified_encoding.group(0)) + except UnicodeEncodeError as e: + new_encode_setting = 'UTF-16' if specified_encoding.group(0) == 'UTF-8' else 'UTF-8' + logger.info("Encoding as %s failed, trying as %s" % (specified_encoding.group(0), new_encode_setting)) + try: + xml_string = xml_string.encode(encoding=new_encode_setting) + except UnicodeEncodeError as e: + logger.error( + "Encoding as %s failed, this was the last attempt. Offending string follows ..." % + new_encode_setting) + logger.error(xml_string) + raise ValueError("String Encode Failed") + + return xml_string + else: + logger.info("RegExp couldn't find a match in the XML string ...") + try: + logger.info("Trying to encode as: UTF-8") + xml_string = xml_string.encode(encoding='UTF-8') + except UnicodeEncodeError as e: + logger.info("Encoding as UTF-8 failed, trying as UTF-16") + try: + xml_string = xml_string.encode(encoding='UTF-16') + except UnicodeEncodeError as e: + logger.error("Encoding as UTF-16 failed, this was the last attempt. Offending string follows ...") + logger.error(xml_string) + raise ValueError("String Encode Failed") + + return xml_string def build_etree(self, xml_string): try: etree = ET.fromstring(self.re_encode_string(xml_string)) - except ET.ParseError: - try: - etree = ET.fromstring(self.re_encode_string(xml_string, only_encode=True)) - except ET.ParseError as e: - logger = RequiredFeature('logger').request() - logger.error("Building ETree from XML failed: %s" % e.message) - logger.info(xml_string) - return None + except ET.ParseError as e: + logger = RequiredFeature('logger').request() + logger.error("Building ETree from XML failed: %s. Offending string follows ..." % e.message) + logger.error(xml_string) + raise ValueError("Building ETree Failed") return etree