diff --git a/fhem/CHANGED b/fhem/CHANGED index 1a63e4ad8..8a452c8b6 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - change: 76_SMAPortal: imptove cookie/login management - feature: 49_SSCam / 49_SSCamSTRM: new capability operate PTZ Zoom cameras - changed: 76_SMAPortal: get plantOid from cookie if not in JSON - bugfix: 70_ZoneMinder: fix afterInitialized (thx GeberNehmer) diff --git a/fhem/FHEM/76_SMAPortal.pm b/fhem/FHEM/76_SMAPortal.pm index eac8ba0bd..d9278d374 100644 --- a/fhem/FHEM/76_SMAPortal.pm +++ b/fhem/FHEM/76_SMAPortal.pm @@ -136,6 +136,7 @@ BEGIN { # Versions History intern my %vNotesIntern = ( + "3.1.2" => "25.06.2020 don't delete cookie after every data retrieval, change login management ", "3.1.1" => "24.06.2020 change german Error regex, get plantOid from cookie if not in JSON ", "3.1.0" => "20.06.2020 language of SMA Portal messages depend on global language attribute, avoid order problems by ". "executing retrieve master data firstly every time", @@ -200,8 +201,8 @@ my %vNotesIntern = ( ); # Voreinstellungen -my $maxretries = 6; # max. Anzahl Wiederholungen in einem Abruf-Zyklus -my $thold = int($maxretries/2); # Schwellenwert nicht erfolgreicher Leseversuche in einem Zyklus mit dem gleichen Cookie +my $maxretries = 8; # max. Anzahl Wiederholungen in einem Abruf-Zyklus +my $thold = int($maxretries/2); # Schwellenwert nicht erfolgreicher Leseversuche in einem Zyklus mit dem gleichen Cookie, Standard: int($maxretries/2) my $sleepretry = 0.5; # Sleep zwischen Data Call Retries (ohne Threshold Überschreitung) my $sleepexc = 2; # Sleep vor neuem Datencall nach Überschreitung Threshold (Data Calls mit gleichem Cookie) my $defmaxcycles = 19; # Standard max. Anzahl Datenabrufzyklen abgeleitet von Interval 120 (wird bei Automatic berechnet) @@ -381,6 +382,7 @@ sub Set { ## no critic 'complexity' ($success) = setcredentials($hash,$prop,$prop1); if($success) { + delcookiefile ($hash); CallInfo($hash); return "Username and Password saved successfully"; } else { @@ -658,7 +660,8 @@ sub Attr { $val = ($do == 1 ? "disabled" : "initialized"); if($do) { - deleteData($hash); + deleteData ($hash); + delcookiefile ($hash); delete $hash->{MODE}; RemoveInternalTimer($hash); } else { @@ -812,16 +815,16 @@ return ($interval,$maxcycles,$timeout,$ctime); ################################################################ sub GetSetData { ## no critic 'complexity' my ($string) = @_; - my ($name,$getp,$setp) = split("\\|",$string); - my $hash = $defs{$name}; - my $useragent = AttrVal($name, "userAgent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)"); - my $cookieLocation = AttrVal($name, "cookieLocation", "./log/".$name."_cookie.txt"); - my $v5d = AttrVal($name, "verbose5Data", "none"); - my $verbose = AttrVal($name, "verbose", 3); - my $lang = AttrVal("global", "language", "EN"); - my $state = "ok"; - my ($st,$lc) = ("",""); - my @da = (); + my ($name,$getp,$setp) = split("\\|",$string); + my $hash = $defs{$name}; + my $useragent = AttrVal($name, "userAgent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)"); + my $cookieLocation = AttrVal($name, "cookieLocation", "./log/".$name."_cookie.txt"); + my $v5d = AttrVal($name, "verbose5Data", "none"); + my $verbose = AttrVal($name, "verbose", 3); + my $lang = AttrVal("global", "language", "EN"); + my $state = "ok"; + my ($st,$lc) = ("",""); + my @da = (); my ($errstate,$reread,$retry,$exceed,$newcycle) = (0,0,0,0,0); @@ -866,7 +869,7 @@ sub GetSetData { ## no cri ### Login ############## my $paref = [ $name, $ua, $state, $errstate ]; - ($state, $errstate) = _checkLogin ($paref); + ($state, $errstate) = _doLogin ($paref); if($errstate) { $st = encode_base64 ( $state,""); @@ -944,7 +947,7 @@ sub GetSetData { ## no cri goto &GetSetData if($reread); # Wiederholung Datenabruf innerhalb eines Cycle - my $retc = $hash->{HELPER}{RETRIES}; # aktuelle Retry-Zähler + my $retc = $hash->{HELPER}{RETRIES}; # aktuelle Retry-Zähler if($retry && $retc < $maxretries) { # neuer Retry im gleichen Zyklus (nicht wenn Verbraucher schalten) $hash->{HELPER}{RETRIES}++; @@ -964,7 +967,7 @@ sub GetSetData { ## no cri my $ac = $hash->{HELPER}{ACTCYCLE}; my $maxcycles = (controlParams $name)[1]; if($retry && $ac < $maxcycles) { # neuer Zyklus (nicht wenn Verbraucher schalten) - Log3 ($name, 3, qq{$name - Maximum retries reached, delete cookie and start new cycle ...}); + Log3 ($name, 3, qq{$name - Maximum retries reached, start new data get cycle ...}); $newcycle = 1; return "$name|$exceed|$newcycle|$errstate|$getp|$setp"; } @@ -976,6 +979,7 @@ sub GetSetData { ## no cri if(@da) { $lc = join "###", @da; $lc = encode_base64 ( $lc, ""); + Log3 ($name, 3, "$name - data retrieved successfully."); } return "$name|$exceed|$newcycle|$errstate|$getp|$setp|$st|$lc"; @@ -984,7 +988,7 @@ return "$name|$exceed|$newcycle|$errstate|$getp|$setp|$st|$lc"; ################################################################ # Login Status checken und ggf. einloggen ################################################################ -sub _checkLogin { +sub _doLogin { my $paref = shift; my $name = $paref->[0]; my $ua = $paref->[1]; @@ -994,30 +998,31 @@ sub _checkLogin { my $hash = $defs{$name}; my $v5d = AttrVal($name, "verbose5Data", "none"); my $verbose = AttrVal($name, "verbose", 3); - - my $loginp = $ua->post('https://www.sunnyportal.com/Templates/Start.aspx'); - my $retcode = $loginp->code; - my $location = $loginp->header('Location') // ""; if($verbose == 5 && $v5d =~ /loginData/) { $ua->add_handler( request_send => sub { shift->dump; return } ); # for debugging $ua->add_handler( response_done => sub { shift->dump; return } ); } + my ($success, $username, $password) = getcredentials($hash,0); # gespeicherte Credentials abrufen + + my $loginp = $ua->post('https://www.sunnyportal.com/Templates/Start.aspx'); + my $retcode = $loginp->code; + my $location = $loginp->header('Location') // ""; + my $cookie = $loginp->header('Set-Cookie') // ""; + if ($loginp->is_success) { if($v5d =~ /loginData/) { Log3 ($name, 5, "$name - Status Login Page: ".$loginp->status_line); - Log3 ($name, 5, "$name - Header Location: ".$location); + Log3 ($name, 5, "$name - Header Location: ". $location); + Log3 ($name, 5, "$name - Header Set-Cookie: ".$cookie); } $retcode = $loginp->code; $location = $loginp->header('Location') // ""; - if($location ne "/FixedPages/HoManLive.aspx" || $retcode ne "302") { # keine aktive Session -> neuer Login + if(!__isLoggedIn ($name,$username,$loginp)) { # keine aktive Session -> neuer Login Log3 ($name, 4, "$name - User not logged in. Try login with credentials ..."); - - # Credentials abrufen - my ($success, $username, $password) = getcredentials($hash,0); if(!$success) { Log3($name, 1, qq{$name - Credentials couldn't be retrieved successfully - make sure you've set it with "set $name credentials "}); @@ -1044,29 +1049,31 @@ sub _checkLogin { my ($logname) = $sc =~ /SunnyPortalLoginInfo=Username=(.*?)&/sx; Log3 ($name, 5, "$name - Header Set-Cookie: ".$sc) if($v5d =~ /loginData/); - if($logname && $logname eq $username) { # Login erfolgeich(Landing Pages können im Portal eingestellt werden!) - Log3 ($name, 3, "$name - Login into SMA-Portal successfully done with user: $logname"); + if(__isLoggedIn ($name,$username,$loginp)) { # Login erfolgeich(Landing Pages können im Portal eingestellt werden!) handleCounter ($name, "dailyIssueCookieCounter"); # Cookie Ausstellungszähler setzen - BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "loginState:successful", "oldlogintime:".(gettimeofday())[0] ], 1); $errstate = 0; } else { Log3 ($name, 2, "$name - ERROR - Login into SMA-Portal failed !"); - $state = "login failed - check user and password"; + $state = "login failed"; BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "loginState:failed", "NULL" ], 1); $errstate = 1; } } } - } elsif($loginp->is_redirect) { + } elsif ($loginp->is_redirect) { $retcode = $loginp->code; $location = $loginp->header('Location') // ""; + Log3 ($name, 3, "$name - User is already logged in."); + if($v5d =~ /loginData/) { - Log3 ($name, 5, "$name - Redirect return code: ".$retcode); + Log3 ($name, 5, "$name - Redirect return code: ". $retcode ); Log3 ($name, 5, "$name - Redirect Header Location: ".$location); } + + BlockingInformParent("FHEM::SMAPortal::setFromBlocking", [$name, "loginState:successful", "NULL" ], 1); $errstate = 0; } else { @@ -1082,15 +1089,34 @@ sub _checkLogin { return ($state, $errstate); } +################################################################ +# Login Status testen +################################################################ +sub __isLoggedIn { + my $name = shift; + my $username = shift; + my $loginp = shift; + + my $sc = $loginp->header('Set-Cookie') // ""; + my ($logname) = $sc =~ /SunnyPortalLoginInfo=Username=(.*?)&/sx; + + if($logname && $logname eq $username) { + Log3 ($name, 3, "$name - Login into SMA-Portal successfully done with user: $logname"); + return 1; + } + +return 0; +} + ################################################################ # Abruf Live Daten ################################################################ sub _getLiveData { ## no critic "not used" - my $paref = shift; - my $name = $paref->{name}; - my $ua = $paref->{ua}; # LWP Useragent - my $state = $paref->{state}; - my $daref = $paref->{daref}; # Referenz zum Datenarray + my $paref = shift; + my $name = $paref->{name}; + my $ua = $paref->{ua}; # LWP Useragent + my $state = $paref->{state}; + my $daref = $paref->{daref}; # Referenz zum Datenarray my ($reread,$retry,$errstate) = (0,0,0); @@ -1761,6 +1787,7 @@ sub ParseData { ## no critic if($newcycle && $ac < $maxcycles) { delete($hash->{HELPER}{RUNNING_PID}); + delcookiefile ($hash); $hash->{HELPER}{GETTER} = $getp; $hash->{HELPER}{SETTER} = $setp; $hash->{HELPER}{ACTCYCLE}++; @@ -1859,7 +1886,7 @@ sub finalCleanup { $hash->{HELPER}{GETTER} = "all"; $hash->{HELPER}{SETTER} = "none"; - delcookiefile ($hash); + #delcookiefile ($hash); return; }