Bug in parseParams: Anführungszeichen schützen Strings mit '=' Zeichen nicht

Begonnen von hexenmeister, 28 Februar 2018, 00:13:13

Vorheriges Thema - Nächstes Thema

hexenmeister

Moin!
Bei der Verwendung der Methode 'parseParams' ist aufgefallen, dass diese (zumindest aus meiner Sicht) nicht korrekt mit Parametern umgeht, die zwar wie "Key=Value"-Paare aussehen, jedoch durch Anführungszeichen geschützt (und dadurch eingentlich Einzelparameter) sind. Sie werden gnadenlos mitzerlegt (s. u. Parameter "a4=x").

fhem> {Dumper(parseParams('a1 a2 a3 b1=x b2=x b3="a=x" "a4=x"'))}
$VAR1 = [
          'a1',
          'a2',
          'a3'
        ];
$VAR2 = {
          'b1' => 'x',
          'b3' => 'a=x',
          'b2' => 'x',
          '"a4' => 'x"'
        };



Im Anhang ist ein einfaches Patch, das diese Situation behebt:

fhem> {Dumper(parseParams('a1 a2 a3 b1=x b2=x b3="a=x" "a4=x"'))}
$VAR1 = [
          'a1',
          'a2',
          'a3',
          'a4=x'
        ];
$VAR2 = {
          'b2' => 'x',
          'b3' => 'a=x',
          'b1' => 'x'
        };


Bitte um Prüfung ung ggf. übernahme.

Grüße
Alexander
Maintainer: MQTT_GENERIC_BRIDGE, SYSMON, SMARTMON, systemd_watchdog, MQTT, MQTT_DEVICE, MQTT_BRIDGE
Contrib: dev_proxy

hexenmeister

Und wenn wir schon dabei sind, hätte ich noch einen Wunsch... :)

Mir ist aufgefallen, dass die Methode 'parseParams' in MQTT-Modul reinkopiert wurde (dort ist auch der oben beschriebene Fehler festgestellt worden), mit der Änderung, dass Zeichen ':' anstatt '=' als 'key=Value'-Trenner verwendet wird. Halte ich für unschön und schlage vor die Methode in fhem.pl um einen optionalen Parameter zu erweitern, der eine Redefinition dieses Trenners erlaubt (natürlich mit einem Defaultwert gleich '=').

fhem> {Dumper(parseParams('a1 a2 a3 b1=x b2=x b3="a=x" "a4=x"',undef,undef,':'))}
$VAR1 = [
          'a1',
          'a2',
          'a3',
          'b1=x',
          'b2=x',
          'b3="a=x"',
          'a4=x'
        ];
$VAR2 = {};


Ich würde dem Modul-Maintainer anschliessend auch gern einen 'Bereinigungspatch' schicken.

Patch anbei :)

Maintainer: MQTT_GENERIC_BRIDGE, SYSMON, SMARTMON, systemd_watchdog, MQTT, MQTT_DEVICE, MQTT_BRIDGE
Contrib: dev_proxy

Phill

Find ich gut. Es gibt aber eine (kleine) Unstimmigkeit. Die man vielleicht gleich glatt ziehen kann/sollte.

Der Patch ignoriert, wie unmittelbar davor bei der Perl-Klammererkennung, die führenden Leerzeichen.
    # the key can not start with a { -> it must be a perl expression # vim:}
    } elsif( $key =~ m/^\s*{/ ) { # for vim: }
      $value = $param;
      $key = undef;
    }
+    # the key can not start with a ' or "
+    elsif ( $key =~ m/^\s*('|")/ ) {

Im Anschluss bei der Entfernung  der Anführungszeichen und Zusammensetzung von $value werden keine führenden Anführungszeichen mehr ignoriert.
    #collect all parts until the closing ' or "
    while( $param && $value =~ m/^('|")/ && $value !~ m/$1$/ ) {
      my $next = shift(@params);
      last if( !defined($next) );
      $value .= $joiner . $next;
    }
    #remove matching ' or " from the start and end
    if( $value =~ m/^('|")/ && $value =~ m/$1$/ ) {
      $value =~ s/^.(.*).$/$1/;
    }

Leerzeichen können übrigens an dieser Stelle nur auftreten wenn $seperator  kein Leerzeichen ist.
a1=a:a2=b:"a3=c": "a4=d": "a5=e:f:g"
Folge:
a4 wird (neu) als Wertepaar ignoriert aber die Anführungszeichen bleiben wie bisher.
"a5=e, f und g" wandern in den Array. Vorher wurde "a5=e gehasht. f und g" wanderten in den Array

Lösungsansatz:
1. Bei deiner Anführungszeichenerkennung wird \s* raus genommen, dann ist es Einheitlich inkompatibel zu umgebenden Leerzeichen. Macht es aber bei a4 und a5 nicht besser.
2. Man fügt dem anschließenden Code s.o. 6x "\s*" hinzu. Dann werden Leerzeichen bei Anführungszeichen so wie bei { } ignoriert.
    #collect all parts until the closing ' or "
    while( $param && $value =~ m/^\s*('|")/ && $value !~ m/$1\s*$/ ) {
      my $next = shift(@params);
      last if( !defined($next) );
      $value .= $joiner . $next;
    }
    #remove matching ' or " from the start and end
    if( $value =~ m/^\s*('|")/ && $value =~ m/$1\s*$/ ) {
      $value =~ s/^\s*.(.*).\s*$/$1/;
    }

3. Man ignoriert die umgebenen Leerzeichen schon bei dem split am Anfang. Dann kann zusätzlich in der Perl-Klammererkennung das \s* raus, was den ganzen Code etwas leichter verständlich macht.
Die Frage ist dann nur, ob irgendwo umgebende Leerzeichen im Attribut gewünscht sind!?

  my @params;
  if( ref($cmd) eq 'ARRAY' ) {
    @params = @{$cmd};
  } else {
-    @params = split($separator, $cmd);
+   @params = split(/\s*$separator\s*/, $cmd);
#oder
#+ @params = split($separator, trim($cmd));
  }


Gruß
Homebrew 1-Wire / HomeMatic Mix - Cubietruck mit FHEM als Server - Raspberry PI 3 als Informationsanzeige im MagicMirror Stil - Raspberry Pi 1 als Klingelanlage - VDR

Mein Modul: Talk2Fhem - Mein Tipp: https://forum.fhem.de/index.php/topic,82442.0.html

rudolfkoenig

Ich verstehe noch nicht, warum die urspruengliche Zerlegung des gezeigten Beispiels ein Problem ist, und Phills fix macht mir Angst :)

Den zweiten Patch finde ich aber sinnvoll, ich habe es kurz getestet und eingecheckt.

hexenmeister

Zitat von: rudolfkoenig am 28 Februar 2018, 22:11:19
Ich verstehe noch nicht, warum die urspruengliche Zerlegung des gezeigten Beispiels ein Problem ist, und Phills fix macht mir Angst :)

Weil ein Paar in der Form: "a4 => x" entsteht. Also mit dem Schlüssel "a4 und Wert x"
Und es leider nicht möglich ist a4=x als eine einzelne ganze Zeichenkette zu bekommen.
Ansonsten schützen ja die Anführungszeichen die damit umschlossenen Parameter vor der Zerlegung.
Außerdem ist sonst die Zerlegung auch nicht konsequent, fügt man ein Leerzeich vor dem Trenner, verhält sich die Funktion anders, als wenn der String ein Leerzeichen nach dem Trenner enthält.
fhem> {Dumper(parseParams('"asd=asdasd"'))}
$VAR1 = [];
$VAR2 = {
          '"asd' => 'asdasd"'
        };

fhem> {Dumper(parseParams('"as d=asdasd"'))}
$VAR1 = [
          'as d=asdasd'
        ];
$VAR2 = {};

fhem> {Dumper(parseParams('"asd=as dasd"'))}
$VAR1 = [
          'dasd"'
        ];
$VAR2 = {
          '"asd' => 'as'
        };


P.S. Phills Idee habe ich (glaube ich) verstanden und finde sie sinnig. Den Code muss ich mir aber auch noch mal zu Gemüte führen :)
Maintainer: MQTT_GENERIC_BRIDGE, SYSMON, SMARTMON, systemd_watchdog, MQTT, MQTT_DEVICE, MQTT_BRIDGE
Contrib: dev_proxy

rudolfkoenig

ZitatWeil ein Paar in der Form: "a4 => x" entsteht. Also mit dem Schlüssel "a4 und Wert x"
Na das habe ich schon kapiert, aber wieso besteht man auf sowas?
Wenn man parseParams aufruft, dann geht man doch von key=value Syntax aus.

hexenmeister

Naja, die Funktion liefert ja getrennt die Paare und die Enzelwerte. Die letzteren aus meiner Sicht nicht konsequent, da nicht jeder Wert möglich.
Aufgefallen ist das im Kontext des MQTT-Moduls, auch wenn der Syntax dort nachträglich wohl mit etwas 'Gewalt' reingepresst wurde.

Auch bei Paarzerlegung gibt es wahrscheinlich ein Problem: ein Key-Wert mit dem '='-Zeichen dürfte auch nicht möglich sein.

Maintainer: MQTT_GENERIC_BRIDGE, SYSMON, SMARTMON, systemd_watchdog, MQTT, MQTT_DEVICE, MQTT_BRIDGE
Contrib: dev_proxy