]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Remove rlm_expr, and replace with %{expr:...} as a built-in
authorAlan T. DeKok <aland@freeradius.org>
Mon, 6 Jun 2022 19:01:24 +0000 (15:01 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 6 Jun 2022 21:39:04 +0000 (17:39 -0400)
and update the docs and tests to match.

27 files changed:
debian/freeradius-config.postinst
doc/antora/modules/raddb/pages/mods-available/expr.adoc
doc/antora/modules/reference/nav.adoc
doc/antora/modules/reference/pages/index.adoc
doc/antora/modules/reference/pages/type/index.adoc
doc/antora/modules/reference/pages/type/string/double.adoc
doc/antora/modules/reference/pages/unlang/condition/cmp.adoc
doc/antora/modules/reference/pages/unlang/condition/regex.adoc
doc/antora/modules/reference/pages/unlang/edit.adoc
doc/antora/modules/reference/pages/unlang/expression.adoc [new file with mode: 0644]
doc/antora/modules/reference/pages/unlang/index.adoc
doc/antora/modules/reference/pages/xlat/builtin.adoc
doc/antora/modules/reference/pages/xlat/index.adoc
doc/antora/modules/tutorials/pages/variables.adoc
raddb/all.mk
raddb/mods-available/expr [deleted file]
src/lib/unlang/xlat_builtin.c
src/modules/rlm_expr/README.md [deleted file]
src/modules/rlm_expr/all.mk [deleted file]
src/modules/rlm_expr/rlm_expr.c [deleted file]
src/modules/rlm_expr/rlm_expr.h [deleted file]
src/tests/auth/unit_test_module.conf
src/tests/keywords/dhcpv4.conf
src/tests/keywords/dhcpv6.conf
src/tests/keywords/radius.conf
src/tests/modules/unit_test_module.conf
src/tests/xlat/unit_test_module.conf

index 1ab7ab30be1864c67deb2aa188942253a426c378..51e221ddf2714bfc0ee147a662c7673a503f4d25 100644 (file)
@@ -35,7 +35,7 @@ case "$1" in
           # Create links for default modules
           for mod in always attr_filter cache_eap chap client \
               delay detail detail.log dhcpv4 digest eap \
-              eap_inner echo exec expr files linelog logintime \
+              eap_inner echo exec files linelog logintime \
               mschap ntlm_auth pap pam passwd radutmp \
               soh sradutmp stats unix unpack utf8 ; do
             if test ! -h /etc/freeradius/mods-enabled/$mod && \
index b18f43dc53fd58357efc1721177c3148cdb636ce..e253ea9efd635b1ca4ed2e69fdaccd2a3caf3f62 100644 (file)
@@ -1,69 +1,10 @@
-
-
-
-
 = Expr Module
 
-This module performs mathematical calculations:
-
-NOTE: This module is not called directly in any section, it is
-invoked through the dynamic expansion of strings.
-
-e.g:
-
-  Attribute-Name = "%{expr:2 + 3 + &NAS-Port}"
-
-It supports the following operators (in order of precedence).
-
-[options="header,autowidth"]
-|===
-| Operator  | Description
-| &         | binary AND
-| \|        | binary OR
-| <<          | left shift
-| >>        | right shift
-| +         | addition
-| -         | subtraction
-| *         | multiply
-| /         | divide
-| %%        | remainder
-| ^         | exponentiation
-| (...)     | sub-expression
-|===
-
-Operator precedence follows the normal rules.
-Division by zero means that the entire expression is invalid.
-
-All calculations are done on signed 63-bit integers.
-e.g. `int64_t`.  This should be sufficient for all normal
-purposes.
-
-  * Hex numbers are supported:         0xabcdef
-  * It also allows unary negation:     -1
-  * And twos complement:               ~1
-  * Otherwise numbers are decimal.
-
-As with all string expansions, you can nest the expansions:
-
-  %{expr: %{NAS-Port} + 1}
-  %{expr: %{sql:SELECT ... } + 1}
-
-Attribute references are supported for integer attributes.
-e.g. `&NAS-Port`.  The benefit of using attribute references
-is that the expression is calculated directly on the
-attribute.
-
-
-
-## Configuration Settings
-
-This module takes no configuration.
-
-
-
-== Default Configuration
+The `expr` module has been remove, and replaced with an equivalent
+`%{expr:...}` in the xref:xlat/builtin.adoc[built-in expansions].  The
+parameters to the `%{expr:...}` is an
+xref:reference:unlang/expression.adoc[Unlang expression].
 
-```
-expr {
-}
-```
+Most people should see no change in functionality.  However, the new
+expressions offer significantly more operators, and work on more data
+types.
index 4fc7873354b631da29ec561d2092f8bee2a31360..feb51d0d7d4b80786ce5b5c95742dd364bf56ce0 100644 (file)
@@ -33,6 +33,7 @@
 
 *** xref:unlang/condition/index.adoc[Conditional Expressions]
 **** xref:unlang/condition/cmp.adoc[Comparisons]
+**** xref:unlang/expressions.adoc[expressions]
 **** xref:unlang/condition/operands.adoc[Operands]
 **** xref:unlang/condition/return_code.adoc[The Return Code Operator]
 **** xref:unlang/condition/eq.adoc[The '==' Operator]
@@ -55,7 +56,7 @@
 *** xref:type/string/backticks.adoc[Backtick-quoted string]
 *** xref:type/string/unquoted.adoc[Unquoted Strings]
 
-** xref:xlat/index.adoc[String Expansion]
+** xref:xlat/index.adoc[Dynamic Expansion]
 *** xref:xlat/alternation.adoc[Alternation Syntax]
 *** xref:xlat/builtin.adoc[Built-in Expansions]
 *** xref:xlat/character.adoc[Single Letter Expansions]
index 34aee7b6118af68fae25a4f806736aabe8d4cff5..2d57e729035f315973568261a7ecd6cdb97927af 100644 (file)
@@ -4,4 +4,4 @@ We have a lot of stuff
 
 * xref:type/index.adoc[Data Types] in the server
 * xref:unlang/index.adoc[Unlang] syntax
-* xref:xlat/index.adoc[String expansions] i.e. "xlat"s.
+* xref:xlat/index.adoc[Dynamic expansions] i.e. "xlat"s.
index 1eed668c0f4215d7519f759319b79c19e3a9fe53..c054a094e71af43dbd1c3247ed1901b458e77af0 100644 (file)
@@ -87,24 +87,24 @@ error cases where the parsing was incorrect.
 The solution is to resolve these ambiguities by allowing the values to
 be cast to a particular type.  Casting a value to a type tells the
 interpreter how that value should be parsed.  Casting is done by
-prefixing a value with the type name, surrounded by angle brackets;
-`<...>`.
+prefixing a value with the type name, surrounded by brackets;
+`(...)`.
 
 .Syntax
 ----
-<...>value
+(...)value
 ----
 
 We can add a cast to the above example, as follows:
 
 [source,unlang]
 ----
-if ("%{sql:SELECT ipaddress FROM table WHERE user=%{User-Name}}" == <ipaddr>192.0.2.1) }
+if ("%{sql:SELECT ipaddress FROM table WHERE user=%{User-Name}}" == (ipaddr)192.0.2.1) }
     ....
 }
 ----
 
-In this example, we prefix the IP address with the string `<ipaddr>`.
+In this example, we prefix the IP address with the string `(ipaddr)`.
 The interpreter then knows that the value `192.0.2.` should be
 interpreted as the data type `ipaddr`, and not as the literal string
 `"192.0.2."`.
@@ -113,5 +113,15 @@ For a full list of data types which can be used in a cast, please see
 the xref:unlang/type/all_types.adoc[list of data types] page, and the
 "Basic Type Types" section.
 
+In most cases, the server can automatically determine what data type
+to use.  The cast syntax is used when either the data type is
+ambiguous, or when data should be normalized prior to comparison, or
+when a specific data type is required.
+
+=== Compatibility
+
+For compatibility with version 3, the `<cast>` syntax is also
+supported.  We recommend, however, that people use the new syntax.
+
 // Copyright (C) 2021 Network RADIUS SAS.  Licenced under CC-by-NC 4.0.
 // Development of this documentation was sponsored by Network RADIUS SAS.
index bc12a5ec379775ecc5ea7a0834e3406715dcacfd..ad2775e9cc7189bfc2bc17eb9c4ce9d2fb81594b 100644 (file)
@@ -13,7 +13,7 @@ that the double quoted strings can be dynamically expanded. The syntax
 run-time expansion. The difference between the two methods is that the
 `${...}` form is expanded when the server loads the configuration
 files and is valid anywhere in the configuration files. The `%{...}`
-xref:xlat/index.adoc[string expansion] form is valid only in conditional
+xref:xlat/index.adoc[dynamic expansion] form is valid only in conditional
 expressions and attribute assignments.
 
 The output of the dynamic expansion can be interpreted as a string,
index 8c9c2d438ddb4784c1e18e4f9d801f8df5b4c87c..bbefd0d4cd63b17cdd81172ac1b4d5dfc0c9521a 100644 (file)
@@ -68,16 +68,16 @@ configuration files.
 .Syntax
 [source,unlang]
 ----
-<cast>lhs OP rhs
+(cast)lhs OP rhs
 ----
 
-The `cast` text can be any one of the standard RADIUS dictionary data
+The `cast` text can be any one of the supported data
 types, as with the following example:
 
 .Example
 [source,unlang]
 ----
-<ipaddr>&Class == 127.0.0.1
+(ipaddr)&Class == 127.0.0.1
 ----
 
 In this example, the `Class` attribute is treated as if it was an IPv4
@@ -90,7 +90,7 @@ done in a type-safe manner, instead of performing a string comparison.
 .Example
 [source,unlang]
 ----
-<integer>`/bin/echo 00` == 0
+(integer)`/bin/echo 00` == 0
 ----
 
 In this example, the string output of the `echo` program is interpreted as an
@@ -100,5 +100,10 @@ the comparison will match.  If the comparison had been performed via
 string equality checks, then the comparison would fail, because the
 strings `00` and `0` are different.
 
+=== Compatibility
+
+For compatibility with version 3, the `<cast>` syntax is also
+supported.  We recommend, however, that people use the new syntax.
+
 // Copyright (C) 2021 Network RADIUS SAS.  Licenced under CC-by-NC 4.0.
 // Development of this documentation was sponsored by Network RADIUS SAS.
index bd6b3895a9b49e6696799adac34d0705476bdd0d..0d0766b8da7973fa36536f938735abf91df23cfe 100644 (file)
@@ -142,7 +142,7 @@ When the server starts any regular expressions comparisons it finds will be
 pre-compiled, and if support is available, JIT'd (converted to machine code)
 to ensure fast execution.
 
-If a pattern contains a xref:xlat/index.adoc[string expansion], the pattern
+If a pattern contains a xref:xlat/index.adoc[dynamic expansion], the pattern
 cannot be compiled on startup, and will be compiled at runtime each time the
 expression is evaluated. The server will also turn off JITing for runtime
 compiled expressions, as the overhead is greater than the time that would be
@@ -159,11 +159,11 @@ if (&User-Name =~ /^@%{Tmp-String-0}$/) {
 ====
 
 To ensure optimal performance you should limit the number of patterns
-containing xref:xlat/index.adoc[string expansions], and if using PCRE, combine
+containing xref:xlat/index.adoc[dynamic expansions], and if using PCRE, combine
 multiple expressions operating on the same subject into a single expression
 using the PCRE alternation '|' operator.
 
-.Using multiple string expansions and the PCRE alternation operator
+.Using multiple dynamic expansions and the PCRE alternation operator
 ====
 [source,unlang]
 ----
index 1ecc87f1b7177e66009a9232e0ac1f0d166cc1d3..bc260bbb1d65f714e16de413bfc3aa2086beaa7f 100644 (file)
@@ -84,11 +84,11 @@ operator which is permitted is `=`.  The server *will not start* if
 any other operator is used._
 
 If the _<rhs>_ is an "in-place" list, then all of the
-xref:xlat/index.adoc[string expansions] are valid, just as are
+xref:xlat/index.adoc[dynamic expansions] are valid, just as are
 xref:reference:unlang/attr.adoc[attribute references].
 
 As a special case, the _<rhs>_ can also be a string, or a
-xref:xlat/index.adoc[string expansion].  If so, the string is
+xref:xlat/index.adoc[dynamic expansion].  If so, the string is
 interpreted as a set of attribute definitions, as if it was an
 "in-place" list.  For example, `"Filter-Id = foo"`
 
@@ -283,7 +283,7 @@ change the attribute _value_.
 
 The _<rhs>_ can be a reference to another attribute
 (e.g. `request.Filter-Id`).  If the field is a double-quoted string,
-it undergoes xref:xlat/index.adoc[string expansion], and the resulting
+it undergoes xref:xlat/index.adoc[dynamic expansion], and the resulting
 value is processed as described above.
 
 In most cases, the edit operations "do the right thing".  For example,
diff --git a/doc/antora/modules/reference/pages/unlang/expression.adoc b/doc/antora/modules/reference/pages/unlang/expression.adoc
new file mode 100644 (file)
index 0000000..d99ffa6
--- /dev/null
@@ -0,0 +1,110 @@
+= Expressions
+
+Expressions can be used inside of xref:xlat/index.adoc[dynamic expansions], or inside of xref:condition/index.adoc[conditions].
+
+The following operators are supported (in order of precedence).
+
+[options="header,autowidth"]
+|===
+| Operator  | Description
+| &         | binary AND
+| \|        | binary OR
+| <<       | left shift
+| >>        | right shift
+| +         | addition
+| -         | subtraction
+| *         | multiplication
+| /         | division
+| %%        | remainder TODO
+| ^         | xor
+| (...)     | sub-expression
+|===
+
+The following unary operators are also supported:
+
+[options="header,autowidth"]
+|===
+| Operator  | Description
+| -         | unary minus
+| ~         | unary complement
+| !         | unary not
+|===
+
+Operator precedence follows the normal rules.
+Division by zero means that the entire expression is invalid.
+
+== Conditions
+
+xref:condition/index.adoc[Conditions] in expressions are also
+supported.  For example:
+
+[source,unlang]
+----
+&NAS-Port = 5 + (&User-Name == "bob")
+----
+
+This expression will return `6` if the users name is `bob`, and `5` otherwise.
+
+Similarly, expressions are also supported in conditions.  There is no
+need to use `%{expr:...}` in conditions, as was needed in earlier
+versions of the server.
+
+== Data Types
+
+The new expression parser accepts significantly more data types than
+the old `rlm_expr` module.  The `rlm_expr` module assumed that all
+inputs were signed 64-bit integers.  Any attempt to use other data
+types resulted in an error.
+
+For example, the `+` operator can be applied to `string` and `octet`
+data types.  (And incidentally, `-` is the inverse of `+`!)
+
+[source,unlang]
+----
+&Reply-Message := "foo" + "bar"
+----
+
+Will result in `&Reply-Message == "foobar"`.
+
+The suffix can then be "subtracted" off, with:
+
+[source,unlang]
+----
+&Reply-Message -= "bar"
+----
+
+Will result in `&Reply-Message == "foo"` !.
+
+Other data types will generally yield results which make sense.  For
+example:
+
+* adding an integer to an `ipv4prefix` type will result in an `ipv4addr` data type,
+* `&` and `|` will work on `string` and `octets` data types,
+* Using `&` with `ipv4addr` data types an `uint32` will result in an `ipv4prefix` data type,
+* `ipv4addr`s can be subtracted, and will return a number,
+* `date`s can be subtracted, and will return a `time_delta`,
+* operations on integers are upgraded to the next largest integer size when necessary,
+* the logical operators `&&` and `||` return the value which caused them to succeed, e.g. `&Foo := (&User-Password || "help")` will return the contents of `&User-Name` if it exists, otherwise it will return the string `help`.
+
+In most cases, the data types are derived from the attribute
+dictionaries.  However, it is sometimes necessary to force the fields
+(or output) of an expression to be parsed in a particular manner.
+
+== Casts
+
+xref:type/index.adoc[Type casting] is supported via the `(type)`
+syntax.  The old-style syntax of `<type>` is accepted, but is
+deprecated.
+
+[source,unlang]
+----
+&NAS-Port-Id = (uint32) "%{sql: SELECT...}" + 4
+----
+
+== Errors
+
+Mathematical operations which cause overflow, underflow, or division
+by zero will return a `null` result.  This result will propagate
+through any calculations, so that an expression which relies on `null`
+will also return `null`.
+
index 4e21d1ca811dd6dfc612ca95e72dc34276bfd4cd..525438056f78373b62993c693120202fadacb785 100644 (file)
@@ -85,12 +85,12 @@ update reply {
 }
 ----
 
-== String Expansions
+== Dynamic Expansions
 
-xref:xlat/index.adoc[String expansion] Using `%{...}` to perform dynamic
-string expansions. (also known as xref:xlat/index.adoc[xlat])
+xref:xlat/index.adoc[Dynamic expansion] Using `%{...}` to perform dynamic
+expansions. (also known as xref:xlat/index.adoc[xlat])
 
-String expansions are usually performed in order to get additional
+Dynamic expansions are usually performed in order to get additional
 information which is not immediately available to the policy.  This
 information can be taken from almost any source, including other
 attributes, databases, and scripts.
index d7aa20cf879cc8b6e7e54c32ae6fae15da8a20bb..28677d368cb1ae9eafbed0dcc5c7e74c6efe9f99 100644 (file)
@@ -947,6 +947,70 @@ Debug :   pcre2                    : 10.33 (2019-04-16) - retrieved at build tim
 ....
 ====
 
+=== +%(eval:<string>)+
+
+Evaluates the string as an expansion, and returns the result.  The main difference between using this expansion and just using `%{...}` is that the string being evaluated can be dynamically changed.
+
+.Return: _data_
+
+.Example:
+
+[source,unlang]
+----
+if (&User-Name == "bob") {
+    update request {
+        &Tmp-String-0 := "&User-Name"
+   }
+} else {
+    update request {
+        &Tmp-String-0 := "not bob!"
+   }
+}
+
+update reply {
+    &Reply-Message := "%{eval:&Tmp-String-0}"
+}
+
+----
+
+.Output when `&User-Name == bob`
+
+```
+bob
+```
+
+.Output when `&User-Name == not bob`
+
+```
+not bob!
+```
+
+
+=== +%(expr:<string>)+
+
+Evaluates the string as an xref:reference:unlang/expression.adoc[Unlang expression], and returns the result.  Please see the
+xref:reference:unlang/expression.adoc[Unlang expression] page for full
+documentation on expressions.
+
+.Return: _data_
+
+.Example:
+
+[source,unlang]
+----
+update reply
+        &Tmp-String-0 := "%{expr: 1 + 2}"
+   }
+}
+
+----
+
+.Output
+
+```
+3
+```
+
 === +%(nexttime:<time>)+
 
 Calculate number of seconds until next n hour(`s`), day(`s`), week(`s`), year(`s`).
index 67c3a754d74f85f4157199447ee4dce333c29f1a..0ba53685a1058bb8ccc619bba966ddbb3d751c5d 100644 (file)
@@ -1,10 +1,10 @@
-= String Expansion
+= Dynamic Expansion
 
-String expansion is a feature that allows strings to dynamically
-define their value at run time. For historical reasons, these string
-expansions are called "xlats".
+Dynamic expansion is a feature that allows values to be dynamically
+expanded at run time. For historical reasons, these string expansions
+are called "xlats".
 
-String expansion is performed via the following syntax:
+Dynamioc expansion is performed via the following syntax:
 
 `%{...}`
 
@@ -12,7 +12,7 @@ Where the `%{` signals the start of a dynamic expansion, and `}`
 signals the end of the dynamic expansion.  The contents of the
 expansion can be many things:
 
-.String Expansions
+.Types of Expansions
 [options="header"]
 |=====
 | Keyword                                           | Description
@@ -29,13 +29,13 @@ refers to the User-Name in a request, via:
 
 `%{User-Name}`
 
-This string expansion is done only for double-quoted strings and for
+This expansion is done only for double-quoted strings and for
 the back-tick operator.
 
 == Caveats
 
 Unlike other languages, there is no way to define new variables.  All
-of the string expansions must refer to attributes that already exist,
+of the expansions must refer to attributes that already exist,
 or to modules that will return a string value.
 
 == Character Escaping
index 6fcd07204c63599f50c83ba33a30ab497a1763d7..7c0013dbdc5e9170a526cb5b7d4e95c0e868a42a 100644 (file)
@@ -12,7 +12,7 @@
 
 *`man` page:* `radiusd.conf`
 
-*documentation page:* xref:reference:unlang/xlat/index.adoc[String expansions]
+*documentation page:* xref:reference:unlang/xlat/index.adoc[Dynamic expansions]
 
 There are two kinds of variables within the server. The first is within
 `radiusd.conf` and related files. These variables are referenced via the
index 0c722c8fc502aebc5b2e33b82866608136d0f25f..9a5d0e42b1c718aca72fa3486964640aa67801c0 100644 (file)
@@ -9,7 +9,7 @@ LOCAL_SITES :=          $(addprefix raddb/sites-enabled/,$(DEFAULT_SITES))
 
 DEFAULT_MODULES :=     always attr_filter cache_eap chap client \
                        delay detail detail.log digest eap \
-                       eap_inner echo escape exec expr files linelog logintime \
+                       eap_inner echo escape exec files linelog logintime \
                        mschap ntlm_auth pap passwd radutmp \
                        soh sradutmp stats unix unpack utf8
 
diff --git a/raddb/mods-available/expr b/raddb/mods-available/expr
deleted file mode 100644 (file)
index c7e00dd..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-#  -*- text -*-
-#
-#
-#  $Id$
-
-#######################################################################
-#
-#  = Expr Module
-#
-#  This module performs mathematical calculations:
-#
-#  NOTE: This module is not called directly in any section, it is
-#  invoked through the dynamic expansion of strings.
-#
-#  e.g:
-#
-#    Attribute-Name = "%{expr:2 + 3 + &NAS-Port}"
-#
-#  It supports the following operators (in order of precedence).
-#
-#  [options="header,autowidth"]
-#  |===
-#  | Operator  | Description
-#  | &         | binary AND
-#  | \|        | binary OR
-#  | <<               | left shift
-#  | >>        | right shift
-#  | +         | addition
-#  | -         | subtraction
-#  | *         | multiply
-#  | /         | divide
-#  | %%        | remainder
-#  | ^         | exponentiation
-#  | (...)     | sub-expression
-#  |===
-#
-#  Operator precedence follows the normal rules.
-#  Division by zero means that the entire expression is invalid.
-#
-#  All calculations are done on signed 63-bit integers.
-#  e.g. `int64_t`.  This should be sufficient for all normal
-#  purposes.
-#
-#  * Hex numbers are supported:                0xabcdef
-#  * It also allows unary negation:    -1
-#  * And twos complement:              ~1
-#  * Otherwise numbers are decimal.
-#
-#  As with all string expansions, you can nest the expansions:
-#
-#    %{expr: %{NAS-Port} + 1}
-#    %{expr: %{sql:SELECT ... } + 1}
-#
-#  Attribute references are supported for integer attributes.
-#  e.g. `&NAS-Port`.  The benefit of using attribute references
-#  is that the expression is calculated directly on the
-#  attribute.
-#
-
-#
-#  ## Configuration Settings
-#
-#  This module takes no configuration.
-#
-expr {
-
-}
index 0a015ba06708e3f53e452a1608765da256e7e579..be070fd4c61e7215b3126204d6e7fc797e960c43 100644 (file)
@@ -1591,6 +1591,82 @@ static xlat_action_t xlat_func_eval(TALLOC_CTX *ctx, fr_dcursor_t *out,
         *      Parse the input as a literal expansion
         */
        if (xlat_tokenize_ephemeral(rctx,
+                                   &rctx->ex, unlang_interpret_event_list(request),
+                                   &FR_SBUFF_IN(arg->vb_strvalue, arg->vb_length),
+                                   &(fr_sbuff_parse_rules_t){
+                                           .escapes = &escape_rules
+                                   },
+                                   &(tmpl_rules_t){
+                                           .attr = {
+                                               .allow_unknown = false,
+                                               .allow_unresolved = false,
+                                               .allow_foreign = false,
+                                               .dict_def = request->dict
+                                       },
+                                       .at_runtime = true
+                                   }) < 0) {
+               RPEDEBUG("Failed parsing expansion");
+       error:
+               talloc_free(rctx);
+               return XLAT_ACTION_FAIL;
+       }
+
+       /*
+        *      Call the resolution function so we produce
+        *      good errors about what function was
+        *      unresolved.
+        */
+       if (rctx->ex->flags.needs_resolving &&
+           (xlat_resolve(rctx->ex, &(xlat_res_rules_t){ .allow_unresolved = false }) < 0)) {
+               RPEDEBUG("Unresolved expansion functions in expansion");
+               goto error;
+
+       }
+
+       if (unlang_xlat_yield(request, xlat_eval_resume, NULL, rctx) != XLAT_ACTION_YIELD) goto error;
+
+       if (unlang_xlat_push(ctx, &rctx->last_success, out->dlist,
+                            request, rctx->ex, UNLANG_SUB_FRAME) < 0) goto error;
+
+       return XLAT_ACTION_PUSH_UNLANG;
+}
+
+/** Dynamically evaluate an expression string
+ *
+ * @ingroup xlat_functions
+ */
+static xlat_action_t xlat_func_expr(TALLOC_CTX *ctx, fr_dcursor_t *out,
+                                   UNUSED xlat_ctx_t const *xctx,
+                                   request_t *request, fr_value_box_list_t *in)
+{
+       xlat_eval_rctx_t        *rctx;
+       fr_value_box_t          *arg = fr_dlist_head(in);
+
+       /*
+        *      These are escaping rules applied to the
+        *      input string. They're mostly here to
+        *      allow \% and \\ to work.
+        *
+        *      Everything else should be passed in as
+        *      unescaped data.
+        */
+       static fr_sbuff_unescape_rules_t const escape_rules = {
+               .name = "xlat",
+               .chr = '\\',
+               .subs = {
+                       ['%'] = '%',
+                       ['\\'] = '\\',
+               },
+               .do_hex = false,
+               .do_oct = false
+       };
+
+       MEM(rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), xlat_eval_rctx_t));
+
+       /*
+        *      Parse the input as an expression.
+        */
+       if (xlat_tokenize_ephemeral_expression(rctx,
                                    &rctx->ex, unlang_interpret_event_list(request),
                                    &FR_SBUFF_IN(arg->vb_strvalue, arg->vb_length),
                                    &(fr_sbuff_parse_rules_t){
@@ -3691,6 +3767,7 @@ do { \
        XLAT_REGISTER_MONO("urlquote", xlat_func_urlquote, xlat_func_urlquote_arg);
        XLAT_REGISTER_MONO("urlunquote", xlat_func_urlunquote, xlat_func_urlunquote_arg);
        XLAT_REGISTER_MONO("eval", xlat_func_eval, xlat_func_eval_arg);
+       XLAT_REGISTER_MONO("expr", xlat_func_expr, xlat_func_eval_arg);
 
 #undef XLAT_REGISTER_MONO
 #define XLAT_REGISTER_MONO(_xlat, _func, _arg) \
diff --git a/src/modules/rlm_expr/README.md b/src/modules/rlm_expr/README.md
deleted file mode 100644 (file)
index 093747c..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# rlm_expr
-## Metadata
-<dl>
-  <dt>category</dt><dd>policy</dd>
-</dl>
-
-## Summary
-Registers a string expansion "%{expr:}" that allows basic arithmetic and binary operations.
diff --git a/src/modules/rlm_expr/all.mk b/src/modules/rlm_expr/all.mk
deleted file mode 100644 (file)
index d48c6b7..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-TARGETNAME     := rlm_expr
-
-TARGET         := $(TARGETNAME)$(L)
-SOURCES                := $(TARGETNAME).c
-
-LOG_ID_LIB     = 18
diff --git a/src/modules/rlm_expr/rlm_expr.c b/src/modules/rlm_expr/rlm_expr.c
deleted file mode 100644 (file)
index f9d4643..0000000
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- *   This program is is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-/**
- * $Id$
- * @file rlm_expr.c
- * @brief Register an xlat expansion to perform basic mathematical operations.
- *
- * @copyright 2001,2006 The FreeRADIUS server project
- * @copyright 2002 Alan DeKok (aland@freeradius.org)
- */
-RCSID("$Id$")
-USES_APPLE_DEPRECATED_API
-
-#include <freeradius-devel/server/base.h>
-#include <freeradius-devel/server/module_rlm.h>
-#include <freeradius-devel/server/tmpl_dcursor.h>
-#include <freeradius-devel/util/debug.h>
-
-#include <ctype.h>
-
-#include "rlm_expr.h"
-
-/** Calculate powers
- *
- * @author Orson Peters
- * @note Borrowed from the gist here: https://gist.github.com/nightcracker/3551590.
- *
- * @param base a 32bit signed integer.
- * @param exp amount to raise base by.
- * @return base ^ pow, or 0 on underflow/overflow.
- */
-static int64_t fr_pow(int64_t base, int64_t exp)
-{
-       static const uint8_t highest_bit_set[] = {
-               0, 1, 2, 2, 3, 3, 3, 3,
-               4, 4, 4, 4, 4, 4, 4, 4,
-               5, 5, 5, 5, 5, 5, 5, 5,
-               5, 5, 5, 5, 5, 5, 5, 5,
-               6, 6, 6, 6, 6, 6, 6, 6,
-               6, 6, 6, 6, 6, 6, 6, 6,
-               6, 6, 6, 6, 6, 6, 6, 6,
-               6, 6, 6, 6, 6, 6, 6, 6 // anything past 63 is a guaranteed overflow with base > 1
-       };
-
-       int64_t result = 1;
-
-       if (exp > 63) {
-               if (base == 1) return 1;
-               if (base == -1) return 1 - 2 * (exp & 1);
-               return 0;       /* overflow */
-       }
-
-       switch (highest_bit_set[exp]) {
-       case 6:
-               if (exp & 1) result *= base;
-               exp >>= 1;
-               base *= base;
-               FALL_THROUGH;
-       case 5:
-               if (exp & 1) result *= base;
-               exp >>= 1;
-               base *= base;
-               FALL_THROUGH;
-       case 4:
-               if (exp & 1) result *= base;
-               exp >>= 1;
-               base *= base;
-               FALL_THROUGH;
-       case 3:
-               if (exp & 1) result *= base;
-               exp >>= 1;
-               base *= base;
-               FALL_THROUGH;
-       case 2:
-               if (exp & 1) result *= base;
-               exp >>= 1;
-               base *= base;
-               FALL_THROUGH;
-       case 1:
-               if (exp & 1) result *= base;
-               FALL_THROUGH;
-       default:
-               return result;
-       }
-}
-
-/*
- *     Start of expression calculator.
- */
-typedef enum expr_token_t {
-       TOKEN_NONE = 0,
-       TOKEN_INTEGER,
-
-       TOKEN_AND,
-       TOKEN_OR,
-
-       TOKEN_LSHIFT,
-       TOKEN_RSHIFT,
-
-       TOKEN_ADD,
-       TOKEN_SUBTRACT,
-
-       TOKEN_DIVIDE,
-       TOKEN_REMAINDER,
-       TOKEN_MULTIPLY,
-
-       TOKEN_POWER,
-       TOKEN_LAST
-} expr_token_t;
-
-static int precedence[TOKEN_LAST + 1] = {
-       0, 0, 1, 1,             /* and or */
-       2, 2, 3, 3,             /* shift add */
-       4, 4, 4, 5,             /* mul, pow */
-       0
-};
-
-typedef struct {
-       char op;
-       expr_token_t token;
-} expr_map_t;
-
-static expr_map_t map[] =
-{
-       {'+',   TOKEN_ADD },
-       {'-',   TOKEN_SUBTRACT },
-       {'/',   TOKEN_DIVIDE },
-       {'*',   TOKEN_MULTIPLY },
-       {'%',   TOKEN_REMAINDER },
-       {'&',   TOKEN_AND },
-       {'|',   TOKEN_OR },
-       {'^',   TOKEN_POWER },
-       {0,     TOKEN_LAST}
-};
-
-static bool get_expression(request_t *request, char const **string, int64_t *answer, expr_token_t prev);
-
-static bool get_number(request_t *request, char const **string, int64_t *answer)
-{
-       fr_sbuff_term_t const           bareword_terminals =
-                                       FR_SBUFF_TERMS(
-                                               L("\t"),
-                                               L("\n"),
-                                               L(" "),
-                                               L("%"),
-                                               L("&"),
-                                               L(")"),
-                                               L("+"),
-                                               L("-"),
-                                               L("/"),
-                                               L("^"),
-                                               L("|")
-                                       );
-       fr_sbuff_parse_rules_t const    p_rules = { .terminals = &bareword_terminals };
-       int64_t                         x;
-       bool                            invert = false;
-       bool                            negative = false;
-       char const                      *p = *string;
-       tmpl_t                          *vpt = NULL;
-
-       /*
-        *      Look for a number.
-        */
-       fr_skip_whitespace(p);
-
-       /*
-        *      ~1 == 0xff...ffe
-        */
-       if (*p == '~') {
-               invert = true;
-               p++;
-       }
-
-       /*
-        *  No algrebraic operator found, the next thing
-        *  MUST be a number.
-        *
-        *  If it isn't, then we die.
-        */
-       if ((*p == '0') && (p[1] == 'x')) {
-               char *end;
-
-               x = strtoul(p, &end, 16);
-               p = end;
-               goto done;
-       }
-
-       if (*p == '-') {
-               negative = true;
-               p++;
-       }
-
-       /*
-        *      Look for an attribute.
-        */
-       if (*p == '&') {
-               int                     i, max, err;
-               ssize_t                 slen;
-               fr_pair_t               *vp;
-               fr_dcursor_t            cursor;
-               tmpl_dcursor_ctx_t      cc;
-
-               slen = tmpl_afrom_attr_substr(request, NULL, &vpt,
-                                             &FR_SBUFF_IN(p, strlen(p)),
-                                             &p_rules,
-                                             &(tmpl_rules_t){
-                                               .attr = {
-                                                       .dict_def = request->dict
-                                               }
-                                             });
-               if (slen <= 0) {
-                       RPEDEBUG("Failed parsing attribute name '%s'", p);
-                       return false;
-               }
-
-               p += slen;
-
-               if (tmpl_num(vpt) == NUM_COUNT) {
-                       REDEBUG("Attribute count is not supported");
-                       return false;
-               }
-
-               if (tmpl_num(vpt) == NUM_ALL) {
-                       max = 65535;
-               } else {
-                       max = 1;
-               }
-
-               x = 0;
-               for (i = 0, vp = tmpl_dcursor_init(&err, NULL, &cc, &cursor, request, vpt);
-                    (i < max) && (vp != NULL);
-                    i++, vp = fr_dcursor_next(&cursor)) {
-                       int64_t         y;
-                       fr_value_box_t  value;
-
-                       switch (vp->vp_type) {
-                               case FR_TYPE_UINT64:
-                                       if (vp->vp_uint64 > INT64_MAX) {
-                                               /*
-                                                *      So we can print out the correct value
-                                                *      in the overflow error message.
-                                                */
-                                               fr_value_box_copy(NULL, &value, &vp->data);
-                                               goto overflow;
-                                       }
-                                       y = (int64_t)vp->vp_uint64;
-                                       break;
-
-                       case FR_TYPE_INT64:
-                               y = vp->vp_int64;
-                               break;
-
-                               /*
-                                *      Scale the time_delta to whatever precision is defined by the
-                                *      attribute.  This is generally what the user expects.  i.e. if
-                                *      the attribute says it's in milliseconds, then print out an
-                                *      integer number of milliseconds
-                                */
-                       case FR_TYPE_TIME_DELTA:
-                               y = fr_time_delta_to_integer(vp->vp_time_delta, vp->data.enumv ? vp->data.enumv->flags.flag_time_res : FR_TIME_RES_SEC);
-                               break;
-
-                       case FR_TYPE_DATE:
-                               /*
-                                *      This is wrong... we should really do scaling, but internally
-                                *      we have vp_date as fr_unix_time_t, and not as fr_time_t.
-                                *
-                                *      Perhaps a better solution is to simply have the dictionaries
-                                *      disallow any precision for the 'date' data type.
-                                */
-                               y = fr_unix_time_to_sec(vp->vp_date);
-                               break;
-
-                       case FR_TYPE_STRUCTURAL:
-                               REDEBUG("Cannot convert %s of type '%s' to integer",
-                                       vp->da->name, fr_type_to_str(vp->vp_type));
-                               goto error;
-
-                       default:
-                               if (fr_value_box_cast(vp, &value, FR_TYPE_UINT64, NULL, &vp->data) < 0) {
-                                       RPEDEBUG("Failed converting &%.*s to an integer value", (int) vpt->len,
-                                                vpt->name);
-                               error:
-                                       tmpl_dursor_clear(&cc);
-                                       return false;
-                               }
-                               if (value.vb_uint64 > INT64_MAX) {
-                               overflow:
-                                       talloc_free(vpt);
-                                       REDEBUG("Value of &%.*s (%pV) would overflow a signed 64bit integer "
-                                               "(our internal arithmetic type)", (int)vpt->len, vpt->name, &value);
-                                       goto error;
-                               }
-                               y = (int64_t)value.vb_uint64;
-
-                               RINDENT();
-                               RDEBUG3("&%.*s --> %" PRIu64, (int)vpt->len, vpt->name, y);
-                               REXDENT();
-                               break;
-                       }
-
-                       /*
-                        *      Check for overflow without actually overflowing.
-                        */
-                       if ((y > 0) && (x > (int64_t) INT64_MAX - y)) goto overflow;
-
-                       if ((y < 0) && (x < (int64_t) INT64_MIN - y)) goto overflow;
-
-                       x += y;
-               } /* loop over all found VPs */
-               tmpl_dursor_clear(&cc);
-
-               if (err != 0) {
-                       RWDEBUG("Can't find %.*s.  Using 0 as operand value", (int)vpt->len, vpt->name);
-                       goto done;
-               }
-
-               goto done;
-       }
-
-       /*
-        *      Do brackets recursively
-        */
-       if (*p == '(') {
-               p++;
-               if (!get_expression(request, &p, &x, TOKEN_NONE)) return false;
-
-               if (*p != ')') {
-                       REDEBUG("No trailing ')'");
-                       return false;
-               }
-               p++;
-               goto done;
-       }
-
-       if ((*p < '0') || (*p > '9')) {
-               REDEBUG("Not a number at \"%s\"", p);
-               return false;
-       }
-
-       /*
-        *  This is doing it the hard way, but it also allows
-        *  us to increment 'p'.
-        */
-       x = 0;
-       while ((*p >= '0') && (*p <= '9')) {
-               x *= 10;
-               x += (*p - '0');
-               p++;
-       }
-
-done:
-       if (vpt) talloc_free(vpt);
-
-       if (invert) x = ~x;
-
-       if (negative) x = -x;
-
-       *string = p;
-       *answer = x;
-       return true;
-}
-
-static bool calc_result(request_t *request, int64_t lhs, expr_token_t op, int64_t rhs, int64_t *answer)
-{
-       switch (op) {
-       default:
-       case TOKEN_SUBTRACT:
-               rhs = -rhs;
-               FALL_THROUGH;
-
-       case TOKEN_ADD:
-               if ((rhs > 0) && (lhs > (int64_t) INT64_MAX - rhs)) {
-               overflow:
-                       REDEBUG("Numerical overflow in expression!");
-                       return false;
-               }
-
-               if ((rhs < 0) && (lhs < (int64_t) INT64_MIN - rhs)) goto overflow;
-
-               *answer = lhs + rhs;
-               break;
-
-       case TOKEN_DIVIDE:
-               if (rhs == 0) {
-                       REDEBUG("Division by zero in expression!");
-                       return false;
-               }
-
-               *answer = lhs / rhs;
-               break;
-
-       case TOKEN_REMAINDER:
-               if (rhs == 0) {
-                       REDEBUG("Division by zero!");
-                       return false;
-               }
-
-               *answer = lhs % rhs;
-               break;
-
-       case TOKEN_MULTIPLY:
-               *answer = lhs * rhs;
-               break;
-
-       case TOKEN_LSHIFT:
-               if (rhs > 62) {
-                       REDEBUG("Shift must be less than 62 (was %lld)", (long long int) rhs);
-                       return false;
-               }
-
-               *answer = lhs << rhs;
-               break;
-
-       case TOKEN_RSHIFT:
-               if (rhs > 62) {
-                       REDEBUG("Shift must be less than 62 (was %lld)", (long long int) rhs);
-                       return false;
-               }
-
-               *answer = lhs >> rhs;
-               break;
-
-       case TOKEN_AND:
-               *answer = lhs & rhs;
-               break;
-
-       case TOKEN_OR:
-               *answer = lhs | rhs;
-               break;
-
-       case TOKEN_POWER:
-               if (rhs > 63) {
-                       REDEBUG("Exponent must be between 0-63 (was %lld)", (long long int) rhs);
-                       return false;
-               }
-
-               if (lhs > 65535) {
-                       REDEBUG("Base must be between 0-65535 (was %lld)", (long long int) lhs);
-                       return false;
-               }
-
-               *answer = fr_pow(lhs, rhs);
-               break;
-       }
-
-       return true;
-}
-
-static bool get_operator(request_t *request, char const **string, expr_token_t *op)
-{
-       int             i;
-       char const      *p = *string;
-
-       /*
-        *      All tokens are one character.
-        */
-       for (i = 0; map[i].token != TOKEN_LAST; i++) {
-               if (*p == map[i].op) {
-                       *op = map[i].token;
-                       *string = p + 1;
-                       return true;
-               }
-       }
-
-       if ((p[0] == '<') && (p[1] == '<')) {
-               *op = TOKEN_LSHIFT;
-               *string = p + 2;
-               return true;
-       }
-
-       if ((p[0] == '>') && (p[1] == '>')) {
-               *op = TOKEN_RSHIFT;
-               *string = p + 2;
-               return true;
-       }
-
-       REDEBUG("Expected operator at \"%s\"", p);
-
-       return false;
-}
-
-
-static bool get_expression(request_t *request, char const **string, int64_t *answer, expr_token_t prev)
-{
-       int64_t         lhs, rhs;
-       char const      *p, *op_p;
-       expr_token_t    this;
-
-       p = *string;
-
-       if (!get_number(request, &p, &lhs)) return false;
-
-redo:
-       fr_skip_whitespace(p);
-
-       /*
-        *      A number by itself is OK.
-        */
-       if (!*p || (*p == ')')) {
-               *answer = lhs;
-               *string = p;
-               return true;
-       }
-
-       /*
-        *      Peek at the operator.
-        */
-       op_p = p;
-       if (!get_operator(request, &p, &this)) return false;
-
-       /*
-        *      a + b + c ... = (a + b) + c ...
-        *      a * b + c ... = (a * b) + c ...
-        *
-        *      Feed the current number to the caller, who will take
-        *      care of continuing.
-        */
-       if (precedence[this] <= precedence[prev]) {
-               *answer = lhs;
-               *string = op_p;
-               return true;
-       }
-
-       /*
-        *      a + b * c ... = a + (b * c) ...
-        */
-       if (!get_expression(request, &p, &rhs, this)) return false;
-
-       if (!calc_result(request, lhs, this, rhs, answer)) return false;
-
-       /*
-        *      There may be more to calculate.  The answer we
-        *      calculated here is now the LHS of the lower priority
-        *      operation which follows the current expression.  e.g.
-        *
-        *      a * b + c ... = (a * b) + c ...
-        *                    =       d + c ...
-        */
-       lhs = *answer;
-       goto redo;
-}
-
-static xlat_arg_parser_t const expr_xlat_arg = {
-       .required = true, .concat = true, .type = FR_TYPE_STRING
-};
-
-/** Xlat expressions
- *
- * Example (NAS-Port = 1):
-@verbatim
-"%{expr:2 + 3 + &NAS-Port}" == 6
-@endverbatim
- *
- * @ingroup xlat_functions
- */
-static xlat_action_t expr_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
-                              UNUSED xlat_ctx_t const *xctx,
-                              request_t *request, fr_value_box_list_t *in)
-{
-       int64_t         result;
-       fr_value_box_t  *arg = fr_dlist_head(in);
-       char const      *p = arg->vb_strvalue;
-       fr_value_box_t  *vb;
-
-       if (!get_expression(request, &p, &result, TOKEN_NONE)) return XLAT_ACTION_FAIL;
-
-       if (*p) {
-               REDEBUG("Invalid text after expression: %s", p);
-               return XLAT_ACTION_FAIL;
-       }
-
-       MEM(vb = fr_value_box_alloc_null(ctx));
-       fr_value_box_int64(vb, NULL, result, false);
-       fr_dcursor_append(out, vb);
-       return XLAT_ACTION_DONE;
-}
-
-/*
- *     Do any per-module initialization that is separate to each
- *     configured instance of the module.  e.g. set up connections
- *     to external databases, read configuration files, set up
- *     dictionary entries, etc.
- *
- *     If configuration information is given in the config section
- *     that must be referenced in later calls, store a handle to it
- *     in *instance otherwise put a null pointer there.
- */
-static int mod_bootstrap(module_inst_ctx_t const *mctx)
-{
-       xlat_t          *xlat;
-
-       xlat = xlat_register_module(mctx->inst->data, mctx, mctx->inst->name, expr_xlat, NULL);
-       xlat_func_mono(xlat, &expr_xlat_arg);
-
-       return 0;
-}
-
-/*
- *     The module name should be the only globally exported symbol.
- *     That is, everything else should be 'static'.
- *
- *     If the module needs to temporarily modify it's instantiation
- *     data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
- *     The server will then take care of ensuring that the module
- *     is single-threaded.
- */
-extern module_rlm_t rlm_expr;
-module_rlm_t rlm_expr = {
-       .common = {
-               .magic          = MODULE_MAGIC_INIT,
-               .name           = "expr",
-               .bootstrap      = mod_bootstrap
-       }
-};
diff --git a/src/modules/rlm_expr/rlm_expr.h b/src/modules/rlm_expr/rlm_expr.h
deleted file mode 100644 (file)
index ac6e458..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-/*
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * @copyright 2007 The FreeRADIUS server project
- * @copyright 2007 Alan DeKok (aland@freeradius.org)
- */
-RCSIDH(rlm_expr_h, "$Id$")
-
-void pair_builtincompare_add(void *instance);
index b2f8adda5f88d264c4e78f91f0f8b07b17cf0a4d..b103cc095990c8a5f732ee55784be9ecaed4351b 100644 (file)
@@ -20,8 +20,6 @@ modules {
 
        $INCLUDE ${raddb}/mods-enabled/chap
 
-       $INCLUDE ${raddb}/mods-enabled/expr
-
        $INCLUDE ${raddb}/mods-enabled/digest
 }
 
index 8c92bb84a75272bcfdb3ec459a939a9250fc281b..bd7e67edfcba987dd64f2064240b108d8ae23839 100644 (file)
@@ -2,8 +2,6 @@
 modules {
        $INCLUDE ${raddb}/mods-enabled/always
 
-       $INCLUDE ${raddb}/mods-enabled/expr
-
        $INCLUDE ${raddb}/mods-enabled/escape
 
        delay reschedule {
index c37bc8549c9b734353fbbbf83b0b6a911b1dfa61..7176367a8af0a7aaed1868f902c8cac4b75bcad8 100644 (file)
@@ -2,8 +2,6 @@
 modules {
        $INCLUDE ${raddb}/mods-enabled/always
 
-       $INCLUDE ${raddb}/mods-enabled/expr
-
        $INCLUDE ${raddb}/mods-enabled/escape
 
        delay reschedule {
index 041cb0d0040e82409b0170b16d17493fdea826a4..af087e326fc8a97cf7aa974c274f97e21bea5e08 100644 (file)
@@ -3,8 +3,6 @@ modules {
 
        $INCLUDE ${raddb}/mods-enabled/pap
 
-       $INCLUDE ${raddb}/mods-enabled/expr
-
        $INCLUDE ${raddb}/mods-enabled/escape
 
        delay reschedule {
index 7edba2b5f4b5d23c7561acc78f258139cef0e16d..d6241d110e358d2d4521fcf9efec31f7b42610f1 100644 (file)
@@ -23,8 +23,6 @@ modules {
 
        $INCLUDE ${raddb}/mods-enabled/pap
 
-       $INCLUDE ${raddb}/mods-enabled/expr
-
        $INCLUDE $ENV{MODULE_TEST_DIR}/module.conf
 }
 
index 6661f7c8e188970b4ad34ef80f47df388cb81d62..c17c7934173591e5c4d476a45da64c532674f8db 100644 (file)
@@ -17,8 +17,6 @@ modules {
        $INCLUDE ${raddb}/mods-enabled/always
 
        $INCLUDE ${raddb}/mods-enabled/pap
-
-       $INCLUDE ${raddb}/mods-enabled/expr
 }
 
 server default {