e73f0cee5a45dd2b3b793dcffc5a11c53ee0d3c4
[freeradius.git] / src / main / xlat.c
1 /*
2  * xlat.c       Translate strings.  This is the first version of xlat 
3  *              incorporated to RADIUS
4  *
5  * Version:     $Id$
6  *
7  *   This program is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU General Public License as published by
9  *   the Free Software Foundation; either version 2 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, write to the Free Software
19  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * Copyright 2000  The FreeRADIUS server project
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  */
24
25 static const char rcsid[] = 
26 "$Id$";
27
28 #include        "autoconf.h"
29 #include        "libradius.h"
30
31 #include        <stdio.h>
32 #include        <stdlib.h>
33 #include        <string.h>
34 #include        <ctype.h>
35
36 #include        "radiusd.h"
37
38 struct xlat_cmp {
39         char module[MAX_STRING_LEN];
40         int length;
41         void *instance;
42         RAD_XLAT_FUNC do_xlat;
43         struct xlat_cmp *next;
44 };
45
46 static struct xlat_cmp *cmp;
47
48 /*
49  *      Register an xlat function.
50  */
51 int xlat_register(char *module, RAD_XLAT_FUNC func, void *instance)
52 {
53         struct xlat_cmp      *c;
54
55         if (module == NULL || strlen(module) == 0){
56                 DEBUG("xlat_register: Invalid module name");
57                 return -1;
58         }
59
60         xlat_unregister(module, func);
61
62         c = rad_malloc(sizeof(struct xlat_cmp));
63
64         c->do_xlat = func;
65         strncpy(c->module, module, MAX_STRING_LEN);
66         c->length = strlen(c->module);
67         c->instance = instance;
68         c->next = cmp;
69         cmp = c;
70
71         return 0;
72 }
73
74 /*
75  *      Unregister an xlat function.
76  */
77 void xlat_unregister(char *module, RAD_XLAT_FUNC func)
78 {
79         struct xlat_cmp      *c, *last;
80
81         last = NULL;
82         for (c = cmp; c; c = c->next) {
83                 if (strncmp(c->module,module,c->length) == 0 && c->do_xlat == func)
84                         break;
85                 last = c;
86         }
87
88         if (c == NULL) return;
89
90         if (last != NULL)
91                 last->next = c->next;
92         else
93                 cmp = c->next;
94
95         free(c);
96 }
97
98 /*
99  * find the appropriate registered xlat function.
100  */
101 static struct xlat_cmp *find_xlat_func(char *module)
102 {
103         struct xlat_cmp *c;
104
105         for (c = cmp; c; c = c->next){
106                 if (strncmp(c->module,module,c->length) == 0 && *(module+c->length) == ':')
107                         break;
108         }
109
110         return c;
111 }
112
113
114 /*
115    Convert the value on a VALUE_PAIR to string
116 */
117 static int valuepair2str(char * out,int outlen,VALUE_PAIR * pair,
118                          int type, RADIUS_ESCAPE_STRING func)
119 {
120         char buffer[MAX_STRING_LEN * 4];
121
122         if (pair != NULL) {
123                 if (func) {
124                         vp_prints_value(buffer, sizeof(buffer), pair, 0);
125                         return func(out, outlen, buffer);
126                 } else {
127                         return vp_prints_value(out, outlen, pair, 0);
128                 }
129         } else {
130                 switch (type) {
131                         case PW_TYPE_STRING :
132                                 strNcpy(out,"_",outlen);
133                                 break;
134                         case PW_TYPE_INTEGER :
135                                 strNcpy(out,"0",outlen);
136                                 break;
137                         case PW_TYPE_IPADDR :
138                                 strNcpy(out,"?.?.?.?",outlen);
139                                 break;
140                         case PW_TYPE_DATE :
141                                 strNcpy(out,"0",outlen);
142                                 break;
143                         default :
144                                 strNcpy(out,"unknown_type",outlen);
145                 }
146         return strlen(out);
147         }
148 }
149
150 /*
151  *  Decode an attribute name into a string.
152  */
153 static void decode_attribute(const char **from, char **to, int freespace, int *open, REQUEST *request, RADIUS_ESCAPE_STRING func)
154 {
155
156         DICT_ATTR *tmpda;
157         VALUE_PAIR *tmppair;
158         char attrname[256];
159         const char *p;
160         char *q, *pa;
161         int stop=0, found=0;
162         int openbraces = *open;
163         struct xlat_cmp *c;
164
165         p = *from;
166         q = *to;
167         pa = &attrname[0];
168
169         *q = '\0';
170
171         /* 
172          * Skip the '}' at the front of 'p' 
173          * Increment open braces 
174          */ 
175         p++;
176         openbraces++;
177
178         while ((*p) && (!stop)) {
179                 switch(*p) {
180                         case '}':
181                                 stop=1;
182                                 break;
183
184                         case ':':
185                                 if(*(p+1) && (*(p+1) == '-')) {
186                                         p+=2;
187                                         stop=1;
188                                         break;
189                                 }
190                                 /* else FALL-THROUGH */
191
192                         default:
193                                 *pa++ = *p++;
194                                 break;
195                 }
196         }
197         *pa = '\0';
198
199         if (strncasecmp(attrname,"reply:",6) == 0) {
200                 if((tmpda = dict_attrbyname(&attrname[6])) && 
201                                 (tmppair = pairfind(request->reply->vps, tmpda->attr))) {
202                         q += valuepair2str(q,freespace,tmppair,tmpda->type, func);
203                         found = 1;
204                 }
205         } else if (strncasecmp(attrname,"request:",8) == 0) {
206                 if((tmpda = dict_attrbyname(&attrname[8])) && 
207                                 (tmppair = pairfind(request->packet->vps, tmpda->attr))) {
208                         q += valuepair2str(q,freespace,tmppair,tmpda->type, func);
209                         found = 1;
210                 }
211         } else if ((c = find_xlat_func(attrname)) != NULL){
212                 DEBUG("radius_xlat: Runing registered xlat function of module %s for string \'%s\'",
213                                 c->module, attrname+(c->length+1));
214                 q += c->do_xlat(c->instance, request, attrname+(c->length+1), q, freespace, func);
215                 found = 1;
216         } else {
217                 if((tmpda = dict_attrbyname(attrname)) && 
218                                 (tmppair = pairfind(request->packet->vps,tmpda->attr))) {
219                         q += valuepair2str(q,freespace,tmppair,tmpda->type, func);
220                         found = 1;
221                 }
222         } 
223
224         /*
225          * Skip to last '}' if attr is found
226          * The rest of the stuff within the braces is
227          * useless if we found what we need
228          */
229         if(found) {
230                 while((*p != '\0') && (openbraces > 0)) {
231                         if(*p == '}') 
232                                 openbraces--;
233                         if(*p == '{') 
234                                 openbraces++;
235                         if (openbraces > 0)
236                                 p++;
237                 }
238         } else {
239                 p--;
240         }
241
242         *open = openbraces;
243         *from = p;
244         *to = q;
245
246 }
247
248
249 /*
250  *      Replace %<whatever> in a string.
251  *
252  *      %a       Protocol (SLIP/PPP)
253  *      %c       Callback-Number
254  *      %d       request day (DD)
255  *      %f       Framed IP address
256  *      %i       Calling Station ID
257  *      %l       request timestamp
258  *      %m       request month (MM)
259  *      %n       NAS IP address
260  *      %p       Port number
261  *      %s       Speed (PW_CONNECT_INFO)
262  *      %t       request in ctime format
263  *      %u       User name
264  *      %A       radacct_dir
265  *      %C       clientname
266  *      %D       request date (YYYYMMDD)
267  *      %L       radlog_dir
268  *      %M       MTU
269  *      %R       radius_dir
270  *      %S       request timestamp in database format (w/ spaces)
271  *      %T       request timestamp in database format
272  *      %U       Stripped User name
273  *      %V       Request-Authenticator (Verified/None)
274  *      %Y       request year (YYYY)
275  *      %Z       All request attributes except password (must have big buffer)
276  *      ${AttributeName}                Corresponding value for AttributeName in request
277  *      ${request:AttributeName}        Corresponding value for AttributeName in request
278  *      ${reply:AttributeName}          Corresponding value for AttributeName in reply
279  */
280
281 int radius_xlat(char *out, int outlen, const char *fmt,
282                 REQUEST *request, RADIUS_ESCAPE_STRING func)
283 {
284         int i, c,freespace;
285         const char *p;
286         char *q;
287         VALUE_PAIR *tmp;
288         struct tm *TM, s_TM;
289         char tmpdt[40]; /* For temporary storing of dates */
290         int openbraces=0;
291
292         q = out;
293         for (p = fmt; *p ; p++) {
294         /* Calculate freespace in output */
295         freespace = outlen - (q - out);
296                 if (freespace <= 1)
297                         break;
298                 c = *p;
299                 if ((c != '%') && (c != '$') && (c != '\\')) {
300                         /*
301                          * We check if we're inside an open brace.  If we are
302                          * then we assume this brace is NOT literal, but is
303                          * a closing brace and apply it 
304                          */
305                         if((c == '}') && openbraces) {
306                                 openbraces--;
307                                 continue;
308                         }
309                         *q++ = *p;
310                         continue;
311                 }
312                 if (*++p == '\0') break;
313                 if (c == '\\') switch(*p) {
314                         case '\\':
315                                 *q++ = *p;
316                                 break;
317                         case 't':
318                                 *q++ = '\t';
319                                 break;
320                         case 'n':
321                                 *q++ = '\n';
322                                 break;
323                         default:
324                                 *q++ = c;
325                                 *q++ = *p;
326                                 break;
327                 } else if (c == '$') switch(*p) {
328                         case '{': /* Attribute by Name */
329                                 decode_attribute(&p, &q, freespace, &openbraces, request, func);
330                                 break;
331                         default:
332                                 *q++ = c;
333                                 *q++ = *p;
334                                 break;
335
336                 } else if (c == '%') switch(*p) {
337                         case '{':
338                                 decode_attribute(&p, &q, freespace, &openbraces, request, func);
339                                 break;
340
341                         case '%':
342                                 *q++ = *p;
343                                 break;
344                         case 'a': /* Protocol: */
345                                 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_PROTOCOL),PW_TYPE_INTEGER, func);
346                                 break;
347                         case 'c': /* Callback-Number */
348                                 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_CALLBACK_NUMBER),PW_TYPE_STRING, func);
349                                 break;
350                         case 'd': /* request year */
351                                 TM = localtime_r(&request->timestamp, &s_TM);
352                                 strftime(tmpdt,sizeof(tmpdt),"%d",TM);
353                                 strNcpy(q,tmpdt,freespace);
354                                 q += strlen(q);
355                                 break;
356                         case 'f': /* Framed IP address */
357                                 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_IP_ADDRESS),PW_TYPE_IPADDR, func);
358                                 break;
359                         case 'i': /* Calling station ID */
360                                 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CALLING_STATION_ID),PW_TYPE_STRING, func);
361                                 break;
362                         case 'l': /* request timestamp */
363                                 snprintf(tmpdt, sizeof(tmpdt), "%ld",request->timestamp);
364                                 strNcpy(q,tmpdt,freespace);
365                                 q += strlen(q);
366                                 break;
367                         case 'm': /* request month */
368                                 TM = localtime_r(&request->timestamp, &s_TM);
369                                 strftime(tmpdt,sizeof(tmpdt),"%m",TM);
370                                 strNcpy(q,tmpdt,freespace);
371                                 q += strlen(q);
372                                 break;
373                         case 'n': /* NAS IP address */
374                                 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_IP_ADDRESS),PW_TYPE_IPADDR, func);
375                                 break;
376                         case 'p': /* Port number */
377                                 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_PORT_ID),PW_TYPE_INTEGER, func);
378                                 break;
379                         case 's': /* Speed */
380                                 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CONNECT_INFO),PW_TYPE_STRING, func);
381                                 break;
382                         case 't': /* request timestamp */
383                                 ctime_r(&request->timestamp, q);
384                                 q += strlen(q);
385                                 break;
386                         case 'u': /* User name */
387                                 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_USER_NAME),PW_TYPE_STRING, func);
388                                 break;
389                         case 'A': /* radacct_dir */
390                                 strNcpy(q,radacct_dir,freespace-1);
391                                 q += strlen(q);
392                                 break;
393                         case 'C': /* ClientName */
394                                 strNcpy(q,client_name(request->packet->src_ipaddr),freespace-1);
395                                 q += strlen(q);
396                                 break;
397                         case 'D': /* request date */
398                                 TM = localtime_r(&request->timestamp, &s_TM);
399                                 strftime(tmpdt,sizeof(tmpdt),"%Y%m%d",TM);
400                                 strNcpy(q,tmpdt,freespace);
401                                 q += strlen(q);
402                                 break;
403                         case 'L': /* radlog_dir */
404                                 strNcpy(q,radlog_dir,freespace-1);
405                                 q += strlen(q);
406                                 break;
407                         case 'M': /* MTU */
408                                 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_MTU),PW_TYPE_INTEGER, func);
409                                 break;
410                         case 'R': /* radius_dir */
411                                 strNcpy(q,radius_dir,freespace-1);
412                                 q += strlen(q);
413                                 break;
414                         case 'S': /* request timestamp in SQL format*/
415                                 TM = localtime_r(&request->timestamp, &s_TM);
416                                 strftime(tmpdt,sizeof(tmpdt),"%Y-%m-%d %H:%M:%S",TM);
417                                 strNcpy(q,tmpdt,freespace);
418                                 q += strlen(q);
419                                 break;
420                         case 'T': /* request timestamp */
421                                 TM = localtime_r(&request->timestamp, &s_TM);
422                                 strftime(tmpdt,sizeof(tmpdt),"%Y-%m-%d-%H.%M.%S.000000",TM);
423                                 strNcpy(q,tmpdt,freespace);
424                                 q += strlen(q);
425                                 break;
426                         case 'U': /* Stripped User name */
427                                 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_STRIPPED_USER_NAME),PW_TYPE_STRING, func);
428                                 break;
429                         case 'V': /* Request-Authenticator */
430                                 if (request->packet->verified)
431                                         strNcpy(q,"Verified",freespace-1);
432                                 else
433                                         strNcpy(q,"None",freespace-1);
434                                 q += strlen(q);
435                                 break;
436                         case 'Y': /* request year */
437                                 TM = localtime_r(&request->timestamp, &s_TM);
438                                 strftime(tmpdt,sizeof(tmpdt),"%Y",TM);
439                                 strNcpy(q,tmpdt,freespace);
440                                 q += strlen(q);
441                                 break;
442                         case 'Z': /* Full request pairs except password */
443                                 tmp = request->packet->vps;
444                                 while (tmp && (freespace > 3)) {
445                                         if (tmp->attribute != PW_PASSWORD) {
446                                                 *q++ = '\t';
447                                                 i = vp_prints(q,freespace-2,tmp);
448                                                 q += i;
449                                                 freespace -= (i+2);
450                                                 *q++ = '\n';
451                                         }
452                                         tmp = tmp->next;
453                                 }
454                                 break;
455                         default:
456                                 *q++ = '%';
457                                 *q++ = *p;
458                                 break;
459                 }
460         }
461         *q = '\0';
462
463         DEBUG2("radius_xlat:  '%s'", out);
464
465         return strlen(out);
466 }