Massively cleaned up #include's, so they're in a consistent
[freeradius.git] / src / modules / rlm_expr / rlm_expr.c
1 /*
2  * rlm_expr.c
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2002,2006  The FreeRADIUS server project
21  * Copyright 2002  Alan DeKok <aland@ox.org>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29
30 /*
31  *      Define a structure for our module configuration.
32  */
33 typedef struct rlm_expr_t {
34         char *xlat_name;
35 } rlm_expr_t;
36
37 typedef enum expr_token_t {
38   TOKEN_NONE = 0,
39   TOKEN_INTEGER,
40   TOKEN_ADD,
41   TOKEN_SUBTRACT,
42   TOKEN_DIVIDE,
43   TOKEN_REMAINDER,
44   TOKEN_MULTIPLY,
45   TOKEN_AND,
46   TOKEN_OR,
47   TOKEN_LAST
48 } expr_token_t;
49
50 typedef struct expr_map_t {
51         char op;
52         expr_token_t token;
53 } expr_map_t;
54
55 static expr_map_t map[] =
56 {
57         {'+',   TOKEN_ADD },
58         {'-',   TOKEN_SUBTRACT },
59         {'/',   TOKEN_DIVIDE },
60         {'*',   TOKEN_MULTIPLY },
61         {'%',   TOKEN_REMAINDER },
62         {'&',   TOKEN_AND },
63         {'|',   TOKEN_OR },
64         {0,     TOKEN_LAST}
65 };
66
67 static int get_number(REQUEST *request, const char **string, int *answer)
68 {
69         int             i, found;
70         uint32_t        result;
71         int             x;
72         const char      *p;
73         expr_token_t    this;
74
75         /*
76          *  Loop over the input.
77          */
78         result = 0;
79         this = TOKEN_NONE;
80
81         for (p = *string; *p != '\0'; /* nothing */) {
82                 if ((*p == ' ') ||
83                     (*p == '\t')) {
84                         p++;
85                         continue;
86                 }
87
88                 /*
89                  *  Discover which token it is.
90                  */
91                 found = FALSE;
92                 for (i = 0; map[i].token != TOKEN_LAST; i++) {
93                         if (*p == map[i].op) {
94                                 if (this != TOKEN_NONE) {
95                                         DEBUG2("rlm_expr: Invalid operator at \"%s\"", p);
96                                         return -1;
97                                 }
98                                 this = map[i].token;
99                                 p++;
100                                 found = TRUE;
101                                 break;
102                         }
103                 }
104
105                 /*
106                  *  Found the algebraic operator.  Get the next number.
107                  */
108                 if (found) {
109                         continue;
110                 }
111
112                 /*
113                  *  End of a group.  Stop.
114                  */
115                 if (*p == ')') {
116                         if (this != TOKEN_NONE) {
117                                 DEBUG2("rlm_expr: Trailing operator before end sub-expression at \"%s\"", p);
118                                 return -1;
119                         }
120                         p++;
121                         break;
122                 }
123
124                 /*
125                  *  Start of a group.  Call ourselves recursively.
126                  */
127                 if (*p == '(') {
128                         p++;
129
130                         found = get_number(request, &p, &x);
131                         if (found < 0) {
132                                 return -1;
133                         }
134                 } else {
135                         /*
136                          *  No algrebraic operator found, the next thing
137                          *  MUST be a number.
138                          *
139                          *  If it isn't, then we die.
140                          */
141                         if ((*p < '0') || (*p > '9')) {
142                                 DEBUG2("rlm_expr: Not a number at \"%s\"", p);
143                                 return -1;
144                         }
145
146                         /*
147                          *  This is doing it the hard way, but it also allows
148                          *  us to increment 'p'.
149                          */
150                         x = 0;
151                         while ((*p >= '0') && (*p <= '9')) {
152                                 x *= 10;
153                                 x += (*p - '0');
154                                 p++;
155                         }
156                 }
157
158                 switch (this) {
159                 default:
160                 case TOKEN_NONE:
161                         result = x;
162                         break;
163
164                 case TOKEN_ADD:
165                         result += x;
166                         break;
167
168                 case TOKEN_SUBTRACT:
169                         result -= x;
170                         break;
171
172                 case TOKEN_DIVIDE:
173                         result /= x;
174                         break;
175
176                 case TOKEN_REMAINDER:
177                         result %= x;
178                         break;
179
180                 case TOKEN_MULTIPLY:
181                         result *= x;
182                         break;
183
184                 case TOKEN_AND:
185                         result &= x;
186                         break;
187
188                 case TOKEN_OR:
189                         result |= x;
190                         break;
191                 }
192
193                 /*
194                  *  We've used this token.
195                  */
196                 this = TOKEN_NONE;
197         }
198
199         /*
200          *  And return the answer to the caller.
201          */
202         *string = p;
203         *answer = result;
204         return 0;
205 }
206
207 /*
208  *  Do xlat of strings!
209  */
210 static int expr_xlat(void *instance, REQUEST *request, char *fmt, char *out, int outlen,
211                                         RADIUS_ESCAPE_STRING func)
212 {
213         int             rcode, result;
214         rlm_expr_t      *inst = instance;
215         const           char *p;
216         char            buffer[256];
217
218         inst = inst;            /* -Wunused */
219
220         /*
221          * Do an xlat on the provided string (nice recursive operation).
222          */
223         if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
224                 radlog(L_ERR, "rlm_expr: xlat failed.");
225                 return 0;
226         }
227
228         p = buffer;
229         rcode = get_number(request, &p, &result);
230         if (rcode < 0) {
231                 return 0;
232         }
233
234         /*
235          *  We MUST have eaten the entire input string.
236          */
237         if (*p != '\0') {
238                 DEBUG2("rlm_expr: Failed at %s", p);
239                 return 0;
240         }
241
242         snprintf(out, outlen, "%d", result);
243         return strlen(out);
244 }
245
246 /*
247  *      Do any per-module initialization that is separate to each
248  *      configured instance of the module.  e.g. set up connections
249  *      to external databases, read configuration files, set up
250  *      dictionary entries, etc.
251  *
252  *      If configuration information is given in the config section
253  *      that must be referenced in later calls, store a handle to it
254  *      in *instance otherwise put a null pointer there.
255  */
256 static int expr_instantiate(CONF_SECTION *conf, void **instance)
257 {
258         rlm_expr_t      *inst;
259         char            *xlat_name;
260
261         /*
262          *      Set up a storage area for instance data
263          */
264
265         inst = rad_malloc(sizeof(rlm_expr_t));
266         if (!inst)
267                 return -1;
268         memset(inst, 0, sizeof(rlm_expr_t));
269
270         xlat_name = cf_section_name2(conf);
271         if (xlat_name == NULL)
272                 xlat_name = cf_section_name1(conf);
273         if (xlat_name){
274                 inst->xlat_name = strdup(xlat_name);
275                 xlat_register(xlat_name, expr_xlat, inst);
276         }
277         /*
278          * Initialize various paircompare functions
279          */
280         pair_builtincompare_init();
281         *instance = inst;
282
283         return 0;
284 }
285
286 /*
287  * Detach a instance free all ..
288  */
289 static int expr_detach(void *instance)
290 {
291         rlm_expr_t      *inst = instance;
292
293         xlat_unregister(inst->xlat_name, expr_xlat);
294         pair_builtincompare_detach();
295         free(inst->xlat_name);
296
297         free(inst);
298         return 0;
299 }
300
301 /*
302  *      The module name should be the only globally exported symbol.
303  *      That is, everything else should be 'static'.
304  *
305  *      If the module needs to temporarily modify it's instantiation
306  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
307  *      The server will then take care of ensuring that the module
308  *      is single-threaded.
309  */
310 module_t rlm_expr = {
311         RLM_MODULE_INIT,
312         "expr",                         /* Name */
313         RLM_TYPE_THREAD_SAFE,           /* type */
314         expr_instantiate,               /* instantiation */
315         expr_detach,                    /* detach */
316         {
317                 NULL,                   /* authentication */
318                 NULL,                   /* authorization */
319                 NULL,                   /* pre-accounting */
320                 NULL                    /* accounting */
321         },
322 };