Concatenating/inserting strings with backslashes
Here's something weird. I'm trying to concatenate some strings which contain <backslash> <n> (i.e. not a newline). In a normal string literal, I have to enter four backslashes: update reply { Reply-Message := "a\\\\nb" } ("\\n" gives a newline, "\\\n" gives backslash followed by newline) But when I try to insert one string into another it goes completely haywire. update reply { Reply-Message := "foo\\\\nbar" } update reply { Reply-Message := "%{reply:Reply-Message}\\\\nbaz" } This gives me "foo" <newline> "bar" <newline> "baz". That is, even the second \\\\n is being collapsed into a newline! Some more test cases: update reply { Reply-Message := "foo\\\\nbar" } update reply { Reply-Message := "qux\\\\nbaz" } correctly gives me "qux" <backslash> <n> "baz" update reply { Reply-Message := "foo\\\\nbar" } update reply { Reply-Message := "%{Wibble:-qux}\\\\nbaz" } gives me <newline> "baz". In fact, I need *eight* backslashes to get a literal backslash here: Reply-Message := "%{Wibble:-qux}\\\\\\\\nbaz" So somehow, the presence of a string expansion within a string affects the interpretation of subsequent backslashes within that string. Now, this works: update reply { Reply-Message := "foo\\\\\\\\nbar" } update reply { Reply-Message := "%{reply:Reply-Message}\\\\\\\\nbaz" } But then if I do another layer of string insertion they get translated to newlines again. update reply { Reply-Message := "foo\\\\\\\\nbar" } update reply { Reply-Message := "%{reply:Reply-Message}\\\\\\\\nbaz" } update reply { Reply-Message := "%{reply:Reply-Message}" } This seems pretty broken to me, but if someone would care to explain how to deal with it, please do. Or is there another way I can concatenate strings, which doesn't involve expanding them into another string? Thanks, Brian.
Brian Candler writes:
Or is there another way I can concatenate strings, which doesn't involve expanding them into another string?
The workaround I've used for this is to feed the value through a regexp match to get it into %{1}, which does not seem to be subject to unescaping. try: if (%{reply:Reply-Message} =~ /(.*)/) { update reply { Reply-Message = "stuff %{1}" } }
On 09/11/12 15:39, Brian Candler wrote:
Here's something weird. I'm trying to concatenate some strings which contain <backslash> <n> (i.e. not a newline).
Uh oh... here be dragons!
In a normal string literal, I have to enter four backslashes:
update reply { Reply-Message := "a\\\\nb" }
("\\n" gives a newline, "\\\n" gives backslash followed by newline)
Yeah; I think there is a similar thing happening here to the regexp stuff I discussed on -devel recently. I think what happens in the code is this: 1. lib/token.c:gettoken loads the config file and performs backslash processing on any quoted strings 2. conffile.c:cf_pairtovp loads the VP update list at config load time, and sets the "do_xlat" flag on any that are double-quoted 3. modcall.c:modcall calls radius_update_attrlist 4. evaluate.c:radius_update_attrlist checks the "do_xlat" flag on the VP, which was set at config load, and calls expand_string (which calls radius_xlat) followed by pairparsevalue. The net effect is that: update x { Foo = "a\\\\n" } ...is de-escaped many times: * into "a<backslash><backslash>n" by the gettoken / config file loader * into "a<backslash>n" by radius_xlat * into "a<newline>" by pairparsevalue (on the result of radius_xlat) This kind of thing is pretty common - exim has a similar problem. It's difficult to know what to do about it in a manner that's universally satisfactory. One solution is to not process "\x" anywhere except loading from config files, but that's likely a very significant backwards compatibility break... you also might *want* to provide a way for people to interpret escapes again (though this can be done with an xlat e.g. "%{unescape:%{something-that-returns-backslash-n}}" == "<newline>" Others options exist. Personally I find the existing behaviour quite surprising, but it's also something I very seldom run into, so don't worry too much about.
try:
if (%{reply:Reply-Message} =~ /(.*)/) { update reply { Reply-Message = "stuff %{1}" } }
Nice idea, but it appears to suffer the same expansion problem. As you have written it gives this error: Bare %{...} is invalid in condition at: %{reply:Reply-Message} =~ /(.*)/) Adding the double quotes: update reply { Reply-Message := "foo" } if ("%{reply:Reply-Message}" =~ /(.*)/) { update reply { Reply-Message := "%{1}\\\\nbar" } } if ("%{reply:Reply-Message}" =~ /(.*)/) { update reply { Reply-Message := "%{1}\\\\nbaz" } } This gives foo <newline> bar <newline> baz update reply { Reply-Message := "foo" } if ("%{reply:Reply-Message}" =~ /(.*)/) { update reply { Reply-Message := "%{1}\\\\\\\\nbar" } } if ("%{reply:Reply-Message}" =~ /(.*)/) { update reply { Reply-Message := "%{1}\\\\\\\\nbaz" } } This gives foo <newline> bar <backslash> <n> baz Regards, Brian.
Brian Candler wrote
try:
if (%{reply:Reply-Message} =~ /(.*)/) { update reply { Reply-Message = "stuff %{1}" } }
Nice idea, but it appears to suffer the same expansion problem.
As you have written it gives this error:
Bare %{...} is invalid in condition at: %{reply:Reply-Message} =~ /(.*)/)
Adding the double quotes:
Oh right. I usually do this with e.g. User-Name without having to specify the attribute list explicitly; I forget whether syntax works to do that with a raw variable. I know outer.VarName works raw, so maybe just reply:Reply-Message without the braces or quotes?
Brian Candler wrote:
Here's something weird. I'm trying to concatenate some strings which contain <backslash> <n> (i.e. not a newline).
Well... that's all pretty hacky. It's made worse by Reply-Message being automatically expanded, whereas other attributes aren't. Try your tests below using Filter-Id, and they will be different. The server has grown over the years in a fairly ad-hoc way. I welcome suggestions for sanitizing how it deals with string. Alan DeKok.
participants (4)
-
Alan DeKok -
Brian Candler -
Brian Julin -
Phil Mayers