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