From 97271640aa4cef11d3499ad1527d0c0837156822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20S=C3=A1nchez=20Villaf=C3=A1n?= Date: Sun, 30 May 2021 18:38:04 -0500 Subject: [PATCH] fix: fix authentication failures allow subsequent requests to use the same authentication instead of re-authenticating all the time --- pyW215/pyW215.py | 61 +++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/pyW215/pyW215.py b/pyW215/pyW215.py index 74c7191..5634684 100644 --- a/pyW215/pyW215.py +++ b/pyW215/pyW215.py @@ -17,7 +17,6 @@ ON = 'ON' OFF = 'OFF' - class SmartPlug(object): """ Class to access: @@ -84,7 +83,7 @@ def controlParameters(self, module, status): {}1'''.format(self.moduleParameters(module), status) else: return '''{}Socket 1Socket 1 - {}'''.format(self.moduleParameters(module), status) + {}1'''.format(self.moduleParameters(module), status) def radioParameters(self, radio): """Returns RadioID as XML. @@ -131,8 +130,8 @@ def SOAPAction(self, Action, responseElement, params="", recursive=False): self.authenticated = self.auth() auth = self.authenticated # If not legacy protocol, ensure auth() is called for every call - if not self.use_legacy_protocol: - self.authenticated = None + #if not self.use_legacy_protocol: + # self.authenticated = None if auth is None: return None @@ -178,6 +177,11 @@ def SOAPAction(self, Action, responseElement, params="", recursive=False): _LOGGER.warning("Could not find %s in response." % responseElement) self._error_report = True return None + + if value == 'ERROR' and self._error_report is False: + _LOGGER.warning("Value of response element %s was \"ERROR\"." % responseElement) + self._error_report = True + return None self._error_report = False return value @@ -335,8 +339,10 @@ def auth(self): CookieResponse = root.find('.//{http://purenetworks.com/HNAP1/}Cookie') PublickeyResponse = root.find('.//{http://purenetworks.com/HNAP1/}PublicKey') - if ( - ChallengeResponse == None or CookieResponse == None or PublickeyResponse == None) and self._error_report is False: + if (ChallengeResponse == None or CookieResponse == None or PublickeyResponse == None) and self._error_report is False: + print( 'payload\n', payload.decode(), '\n' ) + print( 'headers\n', headers, '\n' ) + print( 'xmlData\n', xmlData, '\n' ) _LOGGER.warning("Failed to receive initial authentication from smartplug.") self._error_report = True return None @@ -352,11 +358,16 @@ def auth(self): PrivateKey = hmac.new((Publickey + self.password).encode(), (Challenge).encode(), digestmod=hashlib.md5).hexdigest().upper() login_pwd = hmac.new(PrivateKey.encode(), Challenge.encode(), digestmod=hashlib.md5).hexdigest().upper() + time_stamp = str(round(time.time() / 1e6)) + action_url = '"http://purenetworks.com/HNAP1/Login"' + AUTHKey = hmac.new(PrivateKey.encode(), (time_stamp + action_url).encode(), digestmod=hashlib.md5).hexdigest().upper() + " " + time_stamp + response_payload = self.auth_payload(login_pwd) + # Build response to initial request headers = {'Content-Type': '"text/xml; charset=utf-8"', 'SOAPAction': '"http://purenetworks.com/HNAP1/Login"', - 'HNAP_AUTH': '"{}"'.format(PrivateKey), + 'HNAP_AUTH': '"{}"'.format(AUTHKey), 'Cookie': 'uid={}'.format(Cookie)} response = urlopen(Request(self.url, response_payload, headers)) xmlData = response.read().decode() @@ -376,18 +387,17 @@ def auth(self): def initial_auth_payload(self): """Return the initial authentication payload.""" - return b''' - - + return ''' + + - request - admin - - + request + {} + + - - - ''' + +'''.format(self.user).encode() def auth_payload(self, login_pwd): """Generate a new payload containing generated hash information. @@ -397,16 +407,15 @@ def auth_payload(self, login_pwd): """ payload = ''' - - + + - login - {} - {} - + login + {} + {} + - - - '''.format(self.user, login_pwd) + +'''.format(self.user, login_pwd) return payload.encode()