diff --git a/certbot-compatibility-test/nginx/nginx-roundtrip-testdata/anothermapcase/nginx.conf b/certbot-compatibility-test/nginx/nginx-roundtrip-testdata/anothermapcase/nginx.conf new file mode 100644 index 000000000..b3ca02f92 --- /dev/null +++ b/certbot-compatibility-test/nginx/nginx-roundtrip-testdata/anothermapcase/nginx.conf @@ -0,0 +1,3 @@ +map $uri $blogname{ + ~^(?P/[^/]+/)files/(.*) $blogpath ; +} diff --git a/certbot-nginx/certbot_nginx/nginxparser.py b/certbot-nginx/certbot_nginx/nginxparser.py index d6c352296..1859777d8 100644 --- a/certbot-nginx/certbot_nginx/nginxparser.py +++ b/certbot-nginx/certbot_nginx/nginxparser.py @@ -23,10 +23,12 @@ class RawNginxParser(object): right_bracket = space.leaveWhitespace() + Literal("}").suppress() semicolon = Literal(";").suppress() key = Word(alphanums + "_/+-.") - dollar_var = Combine(Literal('$') + nonspace) + dollar_var = Combine(Literal('$') + Regex(r"[^\{\};,\s]+")) condition = Regex(r"\(.+\)") # Matches anything that is not a special character AND any chars in single # or double quotes + # All of these COULD be upgraded to something like + # https://stackoverflow.com/a/16130746 value = Regex(r"((\".*\")?(\'.*\')?[^\{\};,]?)+") location = CharsNotIn("{};," + string.whitespace) # modifier for location uri [ = | ~ | ~* | ^~ ] @@ -38,19 +40,35 @@ class RawNginxParser(object): assignment = space + key + Optional(space + value, default=None) + semicolon location_statement = space + Optional(modifier) + Optional(space + location + space) if_statement = space + Literal("if") + space + condition + space - map_statement = space + Literal("map") + space + nonspace + space + dollar_var + space - block = Forward() + map_statement = space + Literal("map") + space + nonspace + space + dollar_var + space + # This is NOT an accurate way to parse nginx map entries; it's almost + # certianly too permissive and may be wrong in other ways, but it should + # preserve things correctly in mmmmost or all cases. + # + # - I can neither prove nor disprove that it is corect wrt all escaped + # semicolon situations + # Addresses https://github.com/fatiherikli/nginxparser/issues/19 + map_pattern = Regex(r'".*"') | Regex(r"'.*'") | nonspace + map_entry = space + map_pattern + space + value + space + semicolon + map_block = Group( + # key could for instance be "server" or "http", or "location" (in which case + # location_statement needs to have a non-empty location) + Group(map_statement).leaveWhitespace() + + left_bracket + + Group(ZeroOrMore(Group(comment | map_entry)) + space).leaveWhitespace() + + right_bracket) + + block = Forward() block << Group( # key could for instance be "server" or "http", or "location" (in which case # location_statement needs to have a non-empty location) - (Group(space + key + location_statement) ^ Group(if_statement) ^ - Group(map_statement)).leaveWhitespace() + + (Group(space + key + location_statement) ^ Group(if_statement)).leaveWhitespace() + left_bracket + - Group(ZeroOrMore(Group(comment | assignment) | block) + space).leaveWhitespace() + - right_bracket) + Group(ZeroOrMore(Group(comment | assignment) | block | map_block) + space).leaveWhitespace() + + right_bracket) - script = OneOrMore(Group(comment | assignment) ^ block) + space + stringEnd + script = OneOrMore(Group(comment | assignment) ^ block ^ map_block) + space + stringEnd script.parseWithTabs() def __init__(self, source):