diff --git a/ddclient.in b/ddclient.in index 380a617..6b6268a 100755 --- a/ddclient.in +++ b/ddclient.in @@ -1610,6 +1610,9 @@ sub read_recap { ###################################################################### ## parse_assignments(string) return (rest, %variables) ## parse_assignment(string) return (name, value, rest) +# +# Parsing stops upon encountering non-assignment text (e.g., hostname after the assignments) or an +# unquoted/unescaped newline. ###################################################################### sub parse_assignments { my ($rest) = @_; @@ -1617,7 +1620,7 @@ sub parse_assignments { while (1) { (my $name, my $value, $rest) = parse_assignment($rest); - $rest =~ s/^[,\s]+//; + $rest =~ s/^(?:[^\S\n]|,)+//; # Remove leading commas and non-newline whitespace. return ($rest, %variables) if !defined($name); if ($name eq 'fw-banlocal' || $name eq 'if-skip') { warning("'$name' is deprecated and does nothing"); @@ -1631,7 +1634,9 @@ sub parse_assignment { my ($name, $value); my ($escape, $quote) = (0, ''); - if ($rest =~ /^[,\s]*([a-z][0-9a-z_-]*)=(.*)/i) { + # Ignore leading commas and non-newline whitespace. (An unquoted/unescaped newline terminates + # the assignment search.) + if ($rest =~ qr/^(?:[^\S\n]|,)*([a-z][0-9a-z_-]*)=(.*)/is) { ($name, $rest, $value) = ($1, $2, ''); while (length(my $c = substr($rest, 0, 1))) { diff --git a/t/parse_assignments.pl b/t/parse_assignments.pl index d71f4a8..ab965b9 100644 --- a/t/parse_assignments.pl +++ b/t/parse_assignments.pl @@ -48,6 +48,10 @@ my @test_cases = ( tc('env: unset', "a_env=UNSET", {}, ""), tc('env: set', "a_env=TEST", { a => 'val' }, ""), tc('env: single quoted', "a_env='TEST'", { a => 'val' }, ""), + tc('newline: quoted value', "a='1\n2'", { a => "1\n2" }, ""), + tc('newline: escaped value', "a=1\\\n2", { a => "1\n2" }, ""), + tc('newline: between vars', "a=1 \n b=2", { a => '1' }, "\n b=2"), + tc('newline: terminating', "a=1 \n", { a => '1' }, "\n"), ); delete($ENV{''});