6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2002,2006 The FreeRADIUS server project
21 * Copyright 2002 Alan DeKok <aland@ox.org>
24 #include <freeradius-devel/ident.h>
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
32 * Define a structure for our module configuration.
34 typedef struct rlm_expr_t {
38 typedef enum expr_token_t {
51 typedef struct expr_map_t {
56 static expr_map_t map[] =
59 {'-', TOKEN_SUBTRACT },
61 {'*', TOKEN_MULTIPLY },
62 {'%', TOKEN_REMAINDER },
68 static int get_number(REQUEST *request, const char **string, int *answer)
77 * Loop over the input.
82 for (p = *string; *p != '\0'; /* nothing */) {
90 * Discover which token it is.
93 for (i = 0; map[i].token != TOKEN_LAST; i++) {
94 if (*p == map[i].op) {
95 if (this != TOKEN_NONE) {
96 RDEBUG2("Invalid operator at \"%s\"", p);
107 * Found the algebraic operator. Get the next number.
114 * End of a group. Stop.
117 if (this != TOKEN_NONE) {
118 RDEBUG2("Trailing operator before end sub-expression at \"%s\"", p);
126 * Start of a group. Call ourselves recursively.
131 found = get_number(request, &p, &x);
137 * No algrebraic operator found, the next thing
140 * If it isn't, then we die.
142 if ((*p < '0') || (*p > '9')) {
143 RDEBUG2("Not a number at \"%s\"", p);
148 * This is doing it the hard way, but it also allows
149 * us to increment 'p'.
152 while ((*p >= '0') && (*p <= '9')) {
175 result = 0; /* we don't have NaN for integers */
181 case TOKEN_REMAINDER:
183 result = 0; /* we don't have NaN for integers */
203 * We've used this token.
209 * And return the answer to the caller.
217 * Do xlat of strings!
219 static size_t expr_xlat(void *instance, REQUEST *request, char *fmt,
220 char *out, size_t outlen,
221 RADIUS_ESCAPE_STRING func)
224 rlm_expr_t *inst = instance;
228 inst = inst; /* -Wunused */
231 * Do an xlat on the provided string (nice recursive operation).
233 if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
234 radlog(L_ERR, "rlm_expr: xlat failed.");
239 rcode = get_number(request, &p, &result);
245 * We MUST have eaten the entire input string.
248 RDEBUG2("Failed at %s", p);
252 snprintf(out, outlen, "%d", result);
257 * Do any per-module initialization that is separate to each
258 * configured instance of the module. e.g. set up connections
259 * to external databases, read configuration files, set up
260 * dictionary entries, etc.
262 * If configuration information is given in the config section
263 * that must be referenced in later calls, store a handle to it
264 * in *instance otherwise put a null pointer there.
266 static int expr_instantiate(CONF_SECTION *conf, void **instance)
269 const char *xlat_name;
272 * Set up a storage area for instance data
275 inst = rad_malloc(sizeof(rlm_expr_t));
278 memset(inst, 0, sizeof(rlm_expr_t));
280 xlat_name = cf_section_name2(conf);
281 if (xlat_name == NULL)
282 xlat_name = cf_section_name1(conf);
284 inst->xlat_name = strdup(xlat_name);
285 xlat_register(xlat_name, expr_xlat, inst);
288 * Initialize various paircompare functions
290 pair_builtincompare_init();
297 * Detach a instance free all ..
299 static int expr_detach(void *instance)
301 rlm_expr_t *inst = instance;
303 xlat_unregister(inst->xlat_name, expr_xlat);
304 pair_builtincompare_detach();
305 free(inst->xlat_name);
312 * The module name should be the only globally exported symbol.
313 * That is, everything else should be 'static'.
315 * If the module needs to temporarily modify it's instantiation
316 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
317 * The server will then take care of ensuring that the module
318 * is single-threaded.
320 module_t rlm_expr = {
323 RLM_TYPE_CHECK_CONFIG_SAFE, /* type */
324 expr_instantiate, /* instantiation */
325 expr_detach, /* detach */
327 NULL, /* authentication */
328 NULL, /* authorization */
329 NULL, /* pre-accounting */
330 NULL /* accounting */