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