#
#  Tests for parsing conditional expressions.
#
#  $Id: 9af46e9f76f745451cae24a48703f89f45d0c514 $
#

#
#  A bunch of errors, in the order that the error strings
#  appear in parser.c
#
condition ("foo\
data ERROR offset 6 End of string after escape

condition ("foo
data ERROR offset 2 Unterminated string

condition ()
data ERROR offset 1 Empty string is invalid

condition (!)
data ERROR offset 2 Empty string is invalid

condition (foo == bar
data ERROR offset 11 No closing brace at end of string

condition (|| b)
data ERROR offset 1 Empty string is invalid

condition ((ok || handled) foo)
data ERROR offset 17 Unexpected text after condition

# escapes in names are illegal
condition (ok\ foo || handled)
data ERROR offset 3 Unexpected escape

condition (ok FOO handled)
data ERROR offset 4 Invalid text. Expected comparison operator

condition (ok !x handled)
data ERROR offset 4 Invalid operator

condition (ok =x handled)
data ERROR offset 4 Invalid operator

condition (ok =~ handled)
data ERROR offset 7 Expected regular expression

condition (ok == /foo/)
data ERROR offset 7 Unexpected regular expression

condition (ok == handled"foo")
data ERROR offset 14 Unexpected start of string

# And now we have a bunch of VALID conditions we want to parse.

# sillyness is OK
condition ((((((ok))))))
data ok

#
#  Extra braces get squashed
#
condition (&User-Name == &User-Password)
data &User-Name == &User-Password

condition (!ok)
data !ok

condition !(ok)
data !ok

condition !!ok
data ERROR offset 1 Double negation is invalid

condition !(!ok)
data ok

#
#  These next two are identical after normalization
#
condition (&User-Name == &User-Password || &Filter-Id == &Reply-Message)
data &User-Name == &User-Password || &Filter-Id == &Reply-Message

condition ((&User-Name == &User-Password) || (&Filter-Id == &Reply-Message))
data &User-Name == &User-Password || &Filter-Id == &Reply-Message

condition (!(&User-Name == &User-Password) || (&Filter-Id == &Reply-Message))
data !&User-Name == &User-Password || &Filter-Id == &Reply-Message

#  different from the previous ones.
condition (!((&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)))
data !(&User-Name == &User-Password || &Filter-Id == &Reply-Message)

condition (!(&User-Name == &User-Password) || (&Filter-Id == &Reply-Message))
data !&User-Name == &User-Password || &Filter-Id == &Reply-Message

condition ((a == b) || (c == d)))
data ERROR offset 22 Unexpected closing brace

condition (handled && (Response-Packet-Type == Access-Challenge))
data handled && &Response-Packet-Type == Access-Challenge

# This is OK, without the braces
condition handled && &Response-Packet-Type == Access-Challenge
data handled && &Response-Packet-Type == Access-Challenge

# and this, though it's not a good idea.
condition handled &&&Response-Packet-Type == Access-Challenge
data handled && &Response-Packet-Type == Access-Challenge

condition /foo/ =~ bar
data ERROR offset 0 Conditional check cannot begin with a regular expression

condition reply == request
data ERROR offset 0 Cannot use list references in condition

condition reply == "hello"
data ERROR offset 0 Cannot use list references in condition

condition "hello" == reply
data ERROR offset 0 Cannot use list references in condition

condition request:User-Name == reply:User-Name
data &User-Name == &reply:User-Name

#
#  Convert !~ to !(COND) for regex
#
condition foo =~ /bar/
data foo =~ /bar/

condition foo !~ /bar/
data !foo =~ /bar/

condition !foo !~ /bar/
data foo =~ /bar/

#
#  Convert != to !(COND) for normal checks
#
condition &User-Name == &User-Password
data &User-Name == &User-Password

condition &User-Name != &User-Password
data !&User-Name == &User-Password

condition !&User-Name != &User-Password
data &User-Name == &User-Password

condition <ipv6addr>foo
data ERROR offset 0 Cannot do cast for existence check

condition <ipaddr>Filter-Id == &Framed-IP-Address
data <ipaddr>&Filter-Id == &Framed-IP-Address

condition <ipaddr>Filter-Id == <ipaddr>&Framed-IP-Address
data ERROR offset 21 Unnecessary cast

condition <ipaddr>Filter-Id == <integer>&Framed-IP-Address
data ERROR offset 21 Cannot cast to a different data type

condition <ipaddr>Filter-Id == <blerg>&Framed-IP-Address
data ERROR offset 22 Invalid data type in cast

#
#  Normalize things
#
condition <ipaddr>Filter-Id == "127.0.0.1"
data <ipaddr>&Filter-Id == '127.0.0.1'

condition <ipaddr>127.0.0.1 < &Framed-IP-Address
data &Framed-IP-Address > 127.0.0.1

# =* and !* are only for attrs / lists
condition "foo" !* bar
data ERROR offset 6 Cannot use !* on a string

condition "foo" =* bar
data ERROR offset 6 Cannot use =* on a string

# existence checks don't need the RHS
condition User-Name =* bar
data &User-Name

condition User-Name !* bar
data !&User-Name

condition !User-Name =* bar
data !&User-Name

condition !User-Name !* bar
data &User-Name

# redundant casts get squashed
condition <ipaddr>Framed-IP-Address == 127.0.0.1
data &Framed-IP-Address == 127.0.0.1

condition <cidr>Framed-IP-Address <= 192.168/16
data <ipv4prefix>&Framed-IP-Address <= 192.168/16

# string attributes must be string
condition User-Name == "bob"
data &User-Name == "bob"

condition User-Name == `bob`
data &User-Name == `bob`

condition User-Name == 'bob'
data &User-Name == "bob"

condition User-Name == bob
data ERROR offset 13 Must have string as value for attribute

# Integer (etc.) types must be "bare"
condition Session-Timeout == 10
data &Session-Timeout == 10

condition Session-Timeout == '10'
data ERROR offset 19 Value must be an unquoted string

# Except for dates, which can be humanly readable!
# This one is be an expansion, so it's left as-is.
condition Event-Timestamp == "January 1, 2012 %{blah}"
data &Event-Timestamp == "January 1, 2012 %{blah}"

# This one is NOT an expansion, so it's parsed into normal form
condition Event-Timestamp == 'January 1, 2012'
#data &Event-Timestamp == 'Jan  1 2012 00:00:00 EST'

# literals are parsed when the conditions are parsed
condition <integer>X == 1
data ERROR offset 9 Failed to parse field

condition NAS-Port == X
data ERROR offset 12 Failed to parse value for attribute

#
#  The RHS is a static string, so this gets mashed to a literal,
#  and then statically evaluated.
#
condition <ipaddr>127.0.0.1 == "127.0.0.1"
data true

condition <ipaddr>127.0.0.1 == "%{sql: 127.0.0.1}"
data <ipaddr>127.0.0.1 == "%{sql: 127.0.0.1}"

condition <ether> 00:11:22:33:44:55 == "00:11:22:33:44:55"
data true

condition <ether> 00:11:22:33:44:55 == "ff:11:22:33:44:55"
data false

condition <ether> 00:11:22:33:44:55 == "%{sql:00:11:22:33:44:55}"
data <ether>00:11:22:33:44:55 == "%{sql:00:11:22:33:44:55}"

condition <ether> 00:XX:22:33:44:55 == 00:11:22:33:44:55
data ERROR offset 8 Failed to parse field

#
#  Tests for boolean data types.
#
condition true
data true

condition 1
data true

condition false
data false

condition 0
data false

condition true && (User-Name == "bob")
data &User-Name == "bob"

condition false && (User-Name == "bob")
data false

condition false || (User-Name == "bob")
data &User-Name == "bob"

condition true || (User-Name == "bob")
data true

#
#  Both sides static data with a cast: evaluate at parse time.
#
condition <integer>20 < 100
data true

#
#  Both sides literal: evaluate at parse time
#
condition ('foo' == 'bar')
data false

condition ('foo' < 'bar')
data false

condition ('foo' > 'bar')
data true

condition ('foo' == 'foo')
data true

#
#  Double-quotes strings without expansions are literals
#
condition ("foo" == "%{sql: foo}")
data foo == "%{sql: foo}"

condition ("foo bar" == "%{sql: foo}")
data 'foo bar' == "%{sql: foo}"

condition ("foo" == "bar")
data false

condition ("foo" == 'bar')
data false

#
#  The RHS gets parsed as a VPT_TYPE_DATA, which is
#  a double-quoted string
#
condition (&User-Name == "bob")
data &User-Name == "bob"

condition (&User-Name == "%{sql: blah}")
data &User-Name == "%{sql: blah}"

condition <ipaddr>127.0.0.1 == 2130706433
data true

# /32 suffix should be trimmed for this type
condition <ipaddr>127.0.0.1/32 == 127.0.0.1
data true

condition <ipaddr>127.0.0.1/327 == 127.0.0.1
data ERROR offset 8 Failed to parse field

condition <ipaddr>127.0.0.1/32 == 127.0.0.1
data true

condition (/foo/)
data ERROR offset 1 Conditional check cannot begin with a regular expression

#
#  Tests for (FOO).
#
condition (1)
data true

condition (0)
data false

condition (true)
data true

condition (false)
data false

condition ('')
data false

condition ("")
data false

#
#  Integers are true, as are non-zero strings
#
condition (4)
data true

condition ('a')
data true

condition (a)
data ERROR offset 1 Expected a module return code

#
#  Module return codes are OK
#
condition (ok)
data ok

condition (handled)
data handled

condition (fail)
data fail

condition ("a")
data true

condition (`a`)
data `a`

condition (User-name)
data &User-Name

#
#  Forbidden data types in cast
#
condition (<vsa>"foo" == &User-Name)
data ERROR offset 2 Forbidden data type in cast

#
#  Must have attribute references on the LHS of a condition.
#
condition ("foo" == &User-Name)
data ERROR offset 1 Cannot use attribute reference on right side of condition

#
#  If the LHS is a cast to a type, and the RHS is an attribute
#  of the same type, then re-write it so that the attribute
#  is on the LHS of the condition.
#
condition <string>"foo" == &User-Name
data &User-Name == "foo"

condition <integer>"%{expr: 1 + 1}" < &NAS-Port
data &NAS-Port > "%{expr: 1 + 1}"

condition &Filter-Id == &Framed-IP-Address
data ERROR offset 0 Attribute comparisons must be of the same data type

condition <ipaddr>127.0.0.1 == &Filter-Id
data ERROR offset 0 Attribute comparisons must be of the same data type

condition <ipaddr>&Tmp-Integer64-0 == &Foo-Stuff-Bar
data ERROR offset 0 Unknown attribute

#
#  Casting attributes of different size
#
condition <ipaddr>&Tmp-Integer64-0 == &Framed-IP-Address
data ERROR offset 0 Cannot cast to attribute of incompatible size

condition <ipaddr>&PMIP6-Home-IPv4-HoA == &Framed-IP-Address
data ERROR offset 0 Cannot cast to attribute of incompatible size

# but these are allowed
condition <ether>&Tmp-Integer64-0 == "%{module: foo}"
data <ether>&Tmp-Integer64-0 == "%{module: foo}"

condition <ipaddr>&Filter-Id == &Framed-IP-Address
data <ipaddr>&Filter-Id == &Framed-IP-Address

condition <ipaddr>&Class == &Framed-IP-Address
data <ipaddr>&Class == &Framed-IP-Address

#
#  Tags of zero mean restrict to attributes with no tag
#
condition &Tunnel-Password:0 == "Hello"
data &Tunnel-Password:0 == "Hello"

condition &Tunnel-Password:1 == "Hello"
data &Tunnel-Password:1 == "Hello"

#
#  zero offset into arrays get parsed and ignored
#
condition &User-Name[0] == "bob"
data &User-Name[0] == "bob"

condition &User-Name[1] == "bob"
data &User-Name[1] == "bob"

condition &Tunnel-Password:1[0] == "Hello"
data &Tunnel-Password:1[0] == "Hello"

condition &Tunnel-Password:1[3] == "Hello"
data &Tunnel-Password:1[3] == "Hello"

#
#  This is allowed for pass2-fixups.  Foo-Bar MAY be an attribute.
#  If so allow it so that pass2 can fix it up.  Until then,
#  it's a literal.
#
condition &Foo-Bar
data '&Foo-Bar'

#  Same types are optimized
#
#  FIXME: the tests don't currently run the "pass2" checks.
#  This test should really be:
#
#	data &Acct-Input-Octets > &Session-Timeout
#
condition &Acct-Input-Octets > "%{Session-Timeout}"
data &Acct-Input-Octets > "%{Session-Timeout}"

#  Separate types aren't optimized
condition &Acct-Input-Octets-64 > "%{Session-Timeout}"
data &Acct-Input-Octets-64 > "%{Session-Timeout}"
