Even after writing plenty of Asterisk PBX dialplan, I occasionally get bitten by the unintuitiveness of the parser.

A few rules to avoid mistakes, are:

  • Always use double quotes on no side of the expression, or better yet, on both if there is a chance that the value is empty:
    $[${HANGUPCAUSE}=17] or
    $["${value_which_may_be_empty}"="somevalue"]
  • Otherwise try to avoid double quotes (and semi-colons, and backslashes) whenever possible. If you need to escape them, it's too easy to get it wrong.
  • Always strip double quotes from any user input. Always.
     ; imagine this variable comes from a valid SIP header
     same => n,Set(fromuser="!="InVaLiD"|")
     ; without this next line, you're screwed
     same => n,Set(fromuser=${FILTER(A-Za-z0-9_-,${fromuser})})
     ; if we did not filter, this comparison would look like this:
     ;   $[""!="InVaLiD"|""="secret"]
     same => n,Set(is_superuser=${IF($["${fromuser}"="secret"]?1:0)})
    

Today, I ran into this example, where — this time — the colon adds the magic. (Indented for clarity.)

 ;; drop optional <sip:...> -- changed between asterisk 10 and 11
 same => n,Set(
   tmp=${IF($["${SIPTRANSFER_REFERER:0:5}"="<sip:"]?
            ${SIPTRANSFER_REFERER:5:-1}:
            ${SIPTRANSFER_REFERER}
           )})

You'd be tempted to guess that it works as advertised, converting <sip:123@domain:5060> to 123@domain:5060 and leaving the latter as-is, but you'd be wrong.

For the input without surrounding <sip:...>, it expands into this:

 ;; drop optional <sip:...> -- changed between asterisk 10 and 11
 same => n,Set(tmp=${IF($["123@d"="<sip:"]?omain:506:123@domain:5060)})

Notice the colons? If we indent that for clarity, it looks like this:

 ;; drop optional <sip:...> -- changed between asterisk 10 and 11
 same => n,Set(
   tmp=${IF($["123@d"="<sip:"]?
            omain:
            506:123@domain:5060
           )})

Drat, that came out wrong. tmp is now 506:123@domain:5060.

When changed to this, it works as intended:

 same => n,Set(tmp=${SIPTRANSFER_REFERER})
 ;; drop optional <sip:...> -- changed between asterisk 10 and 11
 same => n,ExecIf($["${tmp:0:5}"="<sip:"]?Set(tmp=${tmp:5:-1}))

security asterisk