3e3edad477fba55f13f8506a03f5a262811f3738
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * Copyright 2000,2006  The FreeRADIUS server project
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  */
24
25 #include <freeradius-devel/ident.h>
26 RCSID("$Id$")
27
28 #include        <freeradius-devel/radiusd.h>
29 #include        <freeradius-devel/md5.h>
30 #include        <freeradius-devel/rad_assert.h>
31
32 #include        <ctype.h>
33
34 typedef struct xlat_t {
35         char            module[MAX_STRING_LEN];
36         int             length;
37         void            *instance;
38         RAD_XLAT_FUNC   do_xlat;
39         int             internal;       /* not allowed to re-define these */
40 } xlat_t;
41
42 static rbtree_t *xlat_root = NULL;
43
44 /*
45  *      Define all xlat's in the structure.
46  */
47 static const char * const internal_xlat[] = {"check",
48                                              "request",
49                                              "reply",
50                                              "proxy-request",
51                                              "proxy-reply",
52                                              "outer.request",
53                                              "outer.reply",
54                                              "outer.control",
55                                              NULL};
56
57 #if REQUEST_MAX_REGEX > 8
58 #error Please fix the following line
59 #endif
60 static int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; /* up to 8 for regex */
61
62
63 /*
64  *      Convert the value on a VALUE_PAIR to string
65  */
66 static int valuepair2str(char * out,int outlen,VALUE_PAIR * pair,
67                          int type, RADIUS_ESCAPE_STRING func)
68 {
69         char buffer[MAX_STRING_LEN * 4];
70
71         if (pair != NULL) {
72                 vp_prints_value(buffer, sizeof(buffer), pair, -1);
73                 return func(out, outlen, buffer);
74         }
75
76         switch (type) {
77         case PW_TYPE_STRING :
78                 strlcpy(out,"_",outlen);
79                 break;
80         case PW_TYPE_INTEGER :
81                 strlcpy(out,"0",outlen);
82                 break;
83         case PW_TYPE_IPADDR :
84                 strlcpy(out,"?.?.?.?",outlen);
85                 break;
86         case PW_TYPE_IPV6ADDR :
87                 strlcpy(out,":?:",outlen);
88                 break;
89         case PW_TYPE_DATE :
90                 strlcpy(out,"0",outlen);
91                 break;
92         default :
93                 strlcpy(out,"unknown_type",outlen);
94         }
95         return strlen(out);
96 }
97
98
99 /*
100  *      Dynamically translate for check:, request:, reply:, etc.
101  */
102 static size_t xlat_packet(void *instance, REQUEST *request,
103                           char *fmt, char *out, size_t outlen,
104                           RADIUS_ESCAPE_STRING func)
105 {
106         DICT_ATTR       *da;
107         VALUE_PAIR      *vp;
108         VALUE_PAIR      *vps = NULL;
109         RADIUS_PACKET   *packet = NULL;
110
111         switch (*(int*) instance) {
112         case 0:
113                 vps = request->config_items;
114                 break;
115
116         case 1:
117                 vps = request->packet->vps;
118                 packet = request->packet;
119                 break;
120
121         case 2:
122                 vps = request->reply->vps;
123                 packet = request->reply;
124                 break;
125
126         case 3:
127 #ifdef WITH_PROXY
128                 if (request->proxy) vps = request->proxy->vps;
129                 packet = request->proxy;
130 #endif
131                 break;
132
133         case 4:
134 #ifdef WITH_PROXY
135                 if (request->proxy_reply) vps = request->proxy_reply->vps;
136                 packet = request->proxy_reply;
137 #endif
138                 break;
139
140         case 5:
141                 if (request->parent) {
142                         vps = request->parent->packet->vps;
143                         packet = request->parent->packet;
144                 }
145                 break;
146                         
147         case 6:
148                 if (request->parent && request->parent->reply) {
149                         vps = request->parent->reply->vps;
150                         packet = request->parent->reply;
151                 }
152                 break;
153                         
154         case 7:
155                 if (request->parent) {
156                         vps = request->parent->config_items;
157                 }
158                 break;
159                         
160         default:                /* WTF? */
161                 return 0;
162         }
163
164         /*
165          *      The "format" string is the attribute name.
166          */
167         da = dict_attrbyname(fmt);
168         if (!da) {
169                 int do_number = FALSE;
170                 size_t count;
171                 const char *p;
172                 char buffer[256];
173
174                 if (strlen(fmt) > sizeof(buffer)) return 0;
175
176                 p = strchr(fmt, '[');
177                 if (!p) {
178                         p = strchr(fmt, '#');
179                         if (!p) return 0;
180                         do_number = TRUE;
181                 }
182
183                 strlcpy(buffer, fmt, p - fmt + 1);
184
185                 da = dict_attrbyname(buffer);
186                 if (!da) return 0;
187
188                 if (do_number) {
189                         vp = pairfind(vps, da->attr, 0);
190                         if (!vp) return 0;
191
192                         switch (da->type) {
193                         default:
194                                 break;
195
196                         case PW_TYPE_INTEGER:
197                         case PW_TYPE_DATE:
198                         case PW_TYPE_SHORT:
199                         case PW_TYPE_BYTE:
200                                 snprintf(out, outlen, "%u", vp->lvalue);
201                                 return strlen(out);
202                         }
203
204                         goto just_print;
205                 }
206
207                 /*
208                  *      %{Attribute-Name[#]} returns the count of
209                  *      attributes of that name in the list.
210                  */
211                 if ((p[1] == '#') && (p[2] == ']')) {
212                         count = 0;
213
214                         for (vp = pairfind(vps, da->attr, da->vendor);
215                              vp != NULL;
216                              vp = pairfind(vp->next, da->attr, da->vendor)) {
217                                 count++;
218                         }
219                         snprintf(out, outlen, "%d", (int) count);
220                         return strlen(out);
221                 }
222
223                 /*
224                  *      %{Attribute-Name[*]} returns ALL of the
225                  *      the attributes, separated by a newline.
226                  */
227                 if ((p[1] == '*') && (p[2] == ']')) {
228                         int total = 0;
229
230                         for (vp = pairfind(vps, da->attr, da->vendor);
231                              vp != NULL;
232                              vp = pairfind(vp->next, da->attr, da->vendor)) {
233                                 count = valuepair2str(out, outlen - 1, vp, da->type, func);
234                                 rad_assert(count <= outlen);
235                                 total += count + 1;
236                                 outlen -= (count + 1);
237                                 out += count;
238
239                                 *(out++) = '\n';
240
241                                 if (outlen <= 1) break;
242                         }
243
244                         *out = '\0';
245                         return total;
246                 }
247
248                 count = atoi(p + 1);
249
250                 /*
251                  *      Skip the numbers.
252                  */
253                 p += 1 + strspn(p + 1, "0123456789");
254                 if (*p != ']') {
255                         RDEBUG2("xlat: Invalid array reference in string at %s %s",
256                                fmt, p);
257                         return 0;
258                 }
259
260                 /*
261                  *      Find the N'th value.
262                  */
263                 for (vp = pairfind(vps, da->attr, da->vendor);
264                      vp != NULL;
265                      vp = pairfind(vp->next, da->attr, da->vendor)) {
266                         if (count == 0) break;
267                         count--;
268                 }
269
270                 /*
271                  *      Non-existent array reference.
272                  */
273                 if (!vp) return 0;
274         just_print:
275                 return valuepair2str(out, outlen, vp, da->type, func);
276         }
277
278         vp = pairfind(vps, da->attr, da->vendor);
279         if (!vp) {
280                 /*
281                  *      Some "magic" handlers, which are never in VP's, but
282                  *      which are in the packet.
283                  *
284                  *      FIXME: We should really do this in a more
285                  *      intelligent way...
286                  */
287                 if (packet) {
288                         VALUE_PAIR localvp;
289
290                         memset(&localvp, 0, sizeof(localvp));
291
292                         switch (da->attr) {
293                         case PW_PACKET_TYPE:
294                         {
295                                 DICT_VALUE *dval;
296
297                                 dval = dict_valbyattr(da->attr, da->vendor, packet->code);
298                                 if (dval) {
299                                         snprintf(out, outlen, "%s", dval->name);
300                                 } else {
301                                         snprintf(out, outlen, "%d", packet->code);
302                                 }
303                                 return strlen(out);
304                         }
305                         break;
306
307                         case PW_CLIENT_SHORTNAME:
308                                 if (request->client && request->client->shortname) {
309                                         strlcpy(out, request->client->shortname, outlen);
310                                 } else {
311                                         strlcpy(out, "<UNKNOWN-CLIENT>", outlen);
312                                 }
313                                 return strlen(out);
314
315                         case PW_CLIENT_IP_ADDRESS: /* the same as below */
316                         case PW_PACKET_SRC_IP_ADDRESS:
317                                 if (packet->src_ipaddr.af != AF_INET) {
318                                         return 0;
319                                 }
320                                 localvp.attribute = da->attr;
321                                 localvp.vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
322                                 break;
323
324                         case PW_PACKET_DST_IP_ADDRESS:
325                                 if (packet->dst_ipaddr.af != AF_INET) {
326                                         return 0;
327                                 }
328                                 localvp.attribute = da->attr;
329                                 localvp.vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
330                                 break;
331
332                         case PW_PACKET_SRC_PORT:
333                                 localvp.attribute = da->attr;
334                                 localvp.vp_integer = packet->src_port;
335                                 break;
336
337                         case PW_PACKET_DST_PORT:
338                                 localvp.attribute = da->attr;
339                                 localvp.vp_integer = packet->dst_port;
340                                 break;
341
342                         case PW_PACKET_AUTHENTICATION_VECTOR:
343                                 localvp.attribute = da->attr;
344                                 memcpy(localvp.vp_strvalue, packet->vector,
345                                        sizeof(packet->vector));
346                                 localvp.length = sizeof(packet->vector);
347                                 break;
348
349                                 /*
350                                  *      Authorization, accounting, etc.
351                                  */
352                         case PW_REQUEST_PROCESSING_STAGE:
353                                 if (request->component) {
354                                         strlcpy(out, request->component, outlen);
355                                 } else {
356                                         strlcpy(out, "server_core", outlen);
357                                 }
358                                 return strlen(out);
359
360                         case PW_PACKET_SRC_IPV6_ADDRESS:
361                                 if (packet->src_ipaddr.af != AF_INET6) {
362                                         return 0;
363                                 }
364                                 localvp.attribute = da->attr;
365                                 memcpy(localvp.vp_strvalue,
366                                        &packet->src_ipaddr.ipaddr.ip6addr,
367                                        sizeof(packet->src_ipaddr.ipaddr.ip6addr));
368                                 break;
369
370                         case PW_PACKET_DST_IPV6_ADDRESS:
371                                 if (packet->dst_ipaddr.af != AF_INET6) {
372                                         return 0;
373                                 }
374                                 localvp.attribute = da->attr;
375                                 memcpy(localvp.vp_strvalue,
376                                        &packet->dst_ipaddr.ipaddr.ip6addr,
377                                        sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
378                                 break;
379
380                         case PW_VIRTUAL_SERVER:
381                                 if (!request->server) return 0;
382
383                                 snprintf(out, outlen, "%s", request->server);
384                                 return strlen(out);
385                                 break;
386
387                         case PW_MODULE_RETURN_CODE:
388                                 localvp.attribute = da->attr;
389
390                                 /*
391                                  *      See modcall.c for a bit of a hack.
392                                  */
393                                 localvp.vp_integer = request->simul_max;
394                                 break;
395
396                         default:
397                                 return 0; /* not found */
398                                 break;
399                         }
400
401                         localvp.type = da->type;
402                         return valuepair2str(out, outlen, &localvp,
403                                              da->type, func);
404                 }
405
406                 /*
407                  *      Not found, die.
408                  */
409                 return 0;
410         }
411
412         if (!vps) return 0;     /* silently fail */
413
414         /*
415          *      Convert the VP to a string, and return it.
416          */
417         return valuepair2str(out, outlen, vp, da->type, func);
418 }
419
420 /*
421  *      Print data as integer, not as VALUE.
422  */
423 static size_t xlat_integer(UNUSED void *instance, REQUEST *request,
424                            char *fmt, char *out, size_t outlen,
425                            UNUSED RADIUS_ESCAPE_STRING func)
426 {
427         VALUE_PAIR *vp;
428
429         while (isspace((int) *fmt)) fmt++;
430
431         if (!radius_get_vp(request, fmt, &vp) || !vp) {
432                 *out = '\0';
433                 return 0;
434         }
435
436         if ((vp->type != PW_TYPE_IPADDR) &&
437             (vp->type != PW_TYPE_INTEGER) &&
438             (vp->type != PW_TYPE_SHORT) &&
439             (vp->type != PW_TYPE_BYTE) &&
440             (vp->type != PW_TYPE_DATE)) {
441                 *out = '\0';
442                 return 0;
443         }
444
445         return snprintf(out, outlen, "%u", vp->vp_integer);
446 }
447
448 /*
449  *      Print data as string, if possible.
450  */
451 static size_t xlat_string(UNUSED void *instance, REQUEST *request,
452                           char *fmt, char *out, size_t outlen,
453                           UNUSED RADIUS_ESCAPE_STRING func)
454 {
455         int len;
456         VALUE_PAIR *vp;
457
458         while (isspace((int) *fmt)) fmt++;
459
460         if (outlen < 3) {
461         nothing:
462                 *out = '\0';
463                 return 0;
464         }
465
466         if (!radius_get_vp(request, fmt, &vp)) goto nothing;
467
468         if (!vp) goto nothing;
469
470         if (vp->type != PW_TYPE_OCTETS) goto nothing;
471
472         *out++ = '"';
473         len = fr_print_string(vp->vp_strvalue, vp->length, out + 1, outlen - 3);
474         out[len] = '"';
475         out[len + 1] = '\0';
476
477         return len + 2;
478 }
479
480 #ifdef HAVE_REGEX_H
481 /*
482  *      Pull %{0} to %{8} out of the packet.
483  */
484 static size_t xlat_regex(void *instance, REQUEST *request,
485                          char *fmt, char *out, size_t outlen,
486                          RADIUS_ESCAPE_STRING func)
487 {
488         char *regex;
489
490         /*
491          *      We cheat: fmt is "0" to "8", but those numbers
492          *      are already in the "instance".
493          */
494         fmt = fmt;              /* -Wunused */
495         func = func;            /* -Wunused FIXME: do escaping? */
496
497         regex = request_data_reference(request, request,
498                                  REQUEST_DATA_REGEX | *(int *)instance);
499         if (!regex) return 0;
500
501         /*
502          *      Copy UP TO "freespace" bytes, including
503          *      a zero byte.
504          */
505         strlcpy(out, regex, outlen);
506         return strlen(out);
507 }
508 #endif                          /* HAVE_REGEX_H */
509
510
511 /*
512  *      Change the debugging level.
513  */
514 static size_t xlat_debug(UNUSED void *instance, REQUEST *request,
515                           char *fmt, char *out, size_t outlen,
516                           UNUSED RADIUS_ESCAPE_STRING func)
517 {
518         int level = 0;
519
520         if (*fmt) level = atoi(fmt);
521
522         if (level == 0) {
523                 request->options = RAD_REQUEST_OPTION_NONE;
524                 request->radlog = NULL;
525         } else {
526                 if (level > 4) level = 4;
527
528                 request->options = level;
529                 request->radlog = radlog_request;
530         }
531
532         snprintf(out, outlen, "%d", level);
533         return strlen(out);
534 }
535
536
537 /*
538  *      Calculate the MD5 hash of a string.
539  */
540 static size_t xlat_md5(UNUSED void *instance, REQUEST *request,
541                        char *fmt, char *out, size_t outlen,
542                        UNUSED RADIUS_ESCAPE_STRING func)
543 {
544         int i;
545         uint8_t digest[16];
546         FR_MD5_CTX ctx;
547         char buffer[1024];
548
549         if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
550                 *out = '\0';
551                 return 0;
552         }
553
554         fr_MD5Init(&ctx);
555         fr_MD5Update(&ctx, (void *) buffer, strlen(buffer));
556         fr_MD5Final(digest, &ctx);
557
558         if (outlen < 33) {
559                 snprintf(out, outlen, "md5_overflow");
560                 return strlen(out);
561         }
562
563         for (i = 0; i < 16; i++) {
564                 snprintf(out + i * 2, 3, "%02x", digest[i]);
565         }
566
567         return strlen(out);
568 }
569
570
571 /*
572  *      Convert a string to lowercase
573  */
574 static size_t xlat_lc(UNUSED void *instance, REQUEST *request,
575                        char *fmt, char *out, size_t outlen,
576                        UNUSED RADIUS_ESCAPE_STRING func)
577 {
578         char *p, *q;
579         char buffer[1024];
580
581         if (outlen <= 1) return 0;
582
583         if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
584                 *out = '\0';
585                 return 0;
586         }
587
588         for (p = buffer, q = out; *p != '\0'; p++, outlen--) {
589                 if (outlen <= 1) break;
590
591                 *(q++) = tolower((int) *p);
592         }
593
594         *q = '\0';
595
596         return strlen(out);
597 }
598
599
600 /*
601  *      Convert a string to uppercase
602  */
603 static size_t xlat_uc(UNUSED void *instance, REQUEST *request,
604                        char *fmt, char *out, size_t outlen,
605                        UNUSED RADIUS_ESCAPE_STRING func)
606 {
607         char *p, *q;
608         char buffer[1024];
609
610         if (outlen <= 1) return 0;
611
612         if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
613                 *out = '\0';
614                 return 0;
615         }
616
617         for (p = buffer, q = out; *p != '\0'; p++, outlen--) {
618                 if (outlen <= 1) break;
619
620                 *(q++) = toupper((int) *p);
621         }
622
623         *q = '\0';
624
625         return strlen(out);
626 }
627
628
629 /*
630  *      Compare two xlat_t structs, based ONLY on the module name.
631  */
632 static int xlat_cmp(const void *a, const void *b)
633 {
634         if (((const xlat_t *)a)->length != ((const xlat_t *)b)->length) {
635                 return ((const xlat_t *)a)->length - ((const xlat_t *)b)->length;
636         }
637
638         return memcmp(((const xlat_t *)a)->module,
639                       ((const xlat_t *)b)->module,
640                       ((const xlat_t *)a)->length);
641 }
642
643 /*
644  *      find the appropriate registered xlat function.
645  */
646 static xlat_t *xlat_find(const char *module)
647 {
648         xlat_t my_xlat;
649
650         /*
651          *      Look for dictionary attributes first.
652          */
653         if ((dict_attrbyname(module) != NULL) ||
654             (strchr(module, '[') != NULL) ||
655             (strchr(module, '#') != NULL)) {
656                 module = "request";
657         }
658
659         strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
660         my_xlat.length = strlen(my_xlat.module);
661
662         return rbtree_finddata(xlat_root, &my_xlat);
663 }
664
665
666 /*
667  *      Register an xlat function.
668  */
669 int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance)
670 {
671         xlat_t  *c;
672         xlat_t  my_xlat;
673
674         if ((module == NULL) || (strlen(module) == 0)) {
675                 DEBUG("xlat_register: Invalid module name");
676                 return -1;
677         }
678
679         /*
680          *      First time around, build up the tree...
681          *
682          *      FIXME: This code should be hoisted out of this function,
683          *      and into a global "initialization".  But it isn't critical...
684          */
685         if (!xlat_root) {
686                 int i;
687 #ifdef HAVE_REGEX_H
688                 char buffer[2];
689 #endif
690
691                 xlat_root = rbtree_create(xlat_cmp, free, 0);
692                 if (!xlat_root) {
693                         DEBUG("xlat_register: Failed to create tree.");
694                         return -1;
695                 }
696
697                 /*
698                  *      Register the internal packet xlat's.
699                  */
700                 for (i = 0; internal_xlat[i] != NULL; i++) {
701                         xlat_register(internal_xlat[i], xlat_packet, &xlat_inst[i]);
702                         c = xlat_find(internal_xlat[i]);
703                         rad_assert(c != NULL);
704                         c->internal = TRUE;
705                 }
706
707                 /*
708                  *      New name: "control"
709                  */
710                 xlat_register("control", xlat_packet, &xlat_inst[0]);
711                 c = xlat_find("control");
712                 rad_assert(c != NULL);
713                 c->internal = TRUE;
714
715                 xlat_register("integer", xlat_integer, "");
716                 c = xlat_find("integer");
717                 rad_assert(c != NULL);
718                 c->internal = TRUE;
719
720                 xlat_register("string", xlat_string, "");
721                 c = xlat_find("string");
722                 rad_assert(c != NULL);
723                 c->internal = TRUE;
724
725 #ifdef HAVE_REGEX_H
726                 /*
727                  *      Register xlat's for regexes.
728                  */
729                 buffer[1] = '\0';
730                 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
731                         buffer[0] = '0' + i;
732                         xlat_register(buffer, xlat_regex, &xlat_inst[i]);
733                         c = xlat_find(buffer);
734                         rad_assert(c != NULL);
735                         c->internal = TRUE;
736                 }
737 #endif /* HAVE_REGEX_H */
738
739
740                 xlat_register("debug", xlat_debug, &xlat_inst[0]);
741                 c = xlat_find("debug");
742                 rad_assert(c != NULL);
743                 c->internal = TRUE;
744
745                 xlat_register("md5", xlat_md5, &xlat_inst[0]);
746                 c = xlat_find("md5");
747                 rad_assert(c != NULL);
748                 c->internal = TRUE;
749
750                 xlat_register("tolower", xlat_lc, &xlat_inst[0]);
751                 c = xlat_find("tolower");
752                 rad_assert(c != NULL);
753                 c->internal = TRUE;
754
755                 xlat_register("toupper", xlat_uc, &xlat_inst[0]);
756                 c = xlat_find("toupper");
757                 rad_assert(c != NULL);
758                 c->internal = TRUE;
759         }
760
761         /*
762          *      If it already exists, replace the instance.
763          */
764         strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
765         my_xlat.length = strlen(my_xlat.module);
766         c = rbtree_finddata(xlat_root, &my_xlat);
767         if (c) {
768                 if (c->internal) {
769                         DEBUG("xlat_register: Cannot re-define internal xlat");
770                         return -1;
771                 }
772
773                 c->do_xlat = func;
774                 c->instance = instance;
775                 return 0;
776         }
777
778         /*
779          *      Doesn't exist.  Create it.
780          */
781         c = rad_malloc(sizeof(*c));
782         memset(c, 0, sizeof(*c));
783
784         c->do_xlat = func;
785         strlcpy(c->module, module, sizeof(c->module));
786         c->length = strlen(c->module);
787         c->instance = instance;
788
789         rbtree_insert(xlat_root, c);
790
791         return 0;
792 }
793
794 /*
795  *      Unregister an xlat function.
796  *
797  *      We can only have one function to call per name, so the
798  *      passing of "func" here is extraneous.
799  */
800 void xlat_unregister(const char *module, RAD_XLAT_FUNC func)
801 {
802         rbnode_t        *node;
803         xlat_t          my_xlat;
804
805         func = func;            /* -Wunused */
806
807         if (!module) return;
808
809         strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
810         my_xlat.length = strlen(my_xlat.module);
811
812         node = rbtree_find(xlat_root, &my_xlat);
813         if (!node) return;
814
815         rbtree_delete(xlat_root, node);
816 }
817
818 /*
819  *      De-register all xlat functions,
820  *      used mainly for debugging.
821  */
822 void xlat_free(void)
823 {
824         rbtree_free(xlat_root);
825 }
826
827
828 /*
829  *      Decode an attribute name into a string.
830  */
831 static int decode_attribute(const char **from, char **to, int freespace,
832                              REQUEST *request,
833                              RADIUS_ESCAPE_STRING func)
834 {
835         int     do_length = 0;
836         char    *xlat_name, *xlat_str;
837         char *p, *q, *l, *next = NULL;
838         int retlen=0;
839         const xlat_t *c;
840         int varlen;
841         char buffer[8192];
842
843         q = *to;
844
845         *q = '\0';
846
847         /*
848          *      Copy the input string to an intermediate buffer where
849          *      we can mangle it.
850          */
851         varlen = rad_copy_variable(buffer, *from);
852         if (varlen < 0) {
853                 RDEBUG2("ERROR: Badly formatted variable: %s", *from);
854                 return -1;
855         }
856         *from += varlen;
857
858         /*
859          *      Kill the %{} around the data we are looking for.
860          */
861         p = buffer;
862         p[varlen - 1] = '\0';   /*  */
863         p += 2;
864         if (*p == '#') {
865                 p++;
866                 do_length = 1;
867         }
868
869         /*
870          *      Handle %{%{foo}:-%{bar}}, which is useful, too.
871          *
872          *      Did I mention that this parser is garbage?
873          */
874         if ((p[0] == '%') && (p[1] == '{')) {
875                 int len1, len2;
876                 int expand2 = FALSE;
877
878                 /*
879                  *      'p' is after the start of 'buffer', so we can
880                  *      safely do this.
881                  */
882                 len1 = rad_copy_variable(buffer, p);
883                 if (len1 < 0) {
884                         RDEBUG2("ERROR: Badly formatted variable: %s", p);
885                         return -1;
886                 }
887
888                 /*
889                  *      They did %{%{foo}}, which is stupid, but allowed.
890                  */
891                 if (!p[len1]) {
892                         RDEBUG2("Improperly nested variable; %%{%s}", p);
893                         return -1;
894                 }
895
896                 /*
897                  *      It SHOULD be %{%{foo}:-%{bar}}.  If not, it's
898                  *      an error.
899                  */
900                 if ((p[len1] != ':') || (p[len1 + 1] != '-')) {
901                         RDEBUG2("No trailing :- after variable at %s", p);
902                         return -1;
903                 }
904
905                 /*
906                  *      Parse the second bit.  The second bit can be
907                  *      either %{foo}, or a string "foo", or a string
908                  *      'foo', or just a bare word: foo
909                  */
910                 p += len1 + 2;
911                 l = buffer + len1 + 1;
912
913                 if ((p[0] == '%') && (p[1] == '{')) {
914                         len2 = rad_copy_variable(l, p);
915
916                         if (len2 < 0) {
917                                 RDEBUG2("ERROR: Invalid text after :- at %s", p);
918                                 return -1;
919                         }
920                         p += len2;
921                         expand2 = TRUE;
922
923                 } else if ((p[0] == '"') || p[0] == '\'') {
924                   getstring((const char **) &p, l, strlen(l));
925
926                 } else {
927                         l = p;
928                 }
929
930                 /*
931                  *      Expand the first one.  If we did, exit the
932                  *      conditional.
933                  */
934                 retlen = radius_xlat(q, freespace, buffer, request, func);
935                 if (retlen) {
936                         q += retlen;
937                         goto done;
938                 }
939
940                 RDEBUG2("\t... expanding second conditional");
941                 /*
942                  *      Expand / copy the second string if required.
943                  */
944                 if (expand2) {
945                         retlen = radius_xlat(q, freespace, l,
946                                             request, func);
947                         if (retlen) {
948                                 q += retlen;
949                         }
950                 } else {
951                         strlcpy(q, l, freespace);
952                         q += strlen(q);
953                 }
954
955                 /*
956                  *      Else the output is an empty string.
957                  */
958                 goto done;
959         }
960
961         /*
962          *      See if we're supposed to expand a module name.
963          */
964         xlat_name = NULL;
965         for (l = p; *l != '\0'; l++) {
966                 if (*l == '\\') {
967                         l++;
968                         continue;
969                 }
970
971                 if (*l == ':') {
972                         xlat_name = p; /* start of name */
973                         *l = '\0';
974                         p = l + 1;
975                         break;
976                 }
977
978                 /*
979                  *      Module names can't have spaces.
980                  */
981                 if ((*l == ' ') || (*l == '\t')) break;
982         }
983
984         /*
985          *      %{name} is a simple attribute reference,
986          *      or regex reference.
987          */
988         if (!xlat_name) {
989                 xlat_name = xlat_str = p;
990                 goto do_xlat;
991         }
992
993         /*
994          *      Maybe it's the old-style %{foo:-bar}
995          */
996         if (*p == '-') {
997                 RDEBUG2("WARNING: Deprecated conditional expansion \":-\".  See \"man unlang\" for details");
998                 p++;
999
1000                 xlat_str = xlat_name;
1001                 next = p;
1002                 goto do_xlat;
1003         }
1004
1005         /*
1006          *      FIXME: For backwards "WTF" compatibility, check for
1007          *      {...}, (after the :), and copy that, too.
1008          */
1009
1010         /* module name, followed by (possibly) per-module string */
1011         xlat_str = p;
1012         
1013 do_xlat:
1014         if ((c = xlat_find(xlat_name)) != NULL) {
1015                 if (!c->internal) RDEBUG3("radius_xlat: Running registered xlat function of module %s for string \'%s\'",
1016                                           c->module, xlat_str);
1017                 retlen = c->do_xlat(c->instance, request, xlat_str,
1018                                     q, freespace, func);
1019                 if (retlen > 0) {
1020                         if (do_length) {
1021                                 snprintf(q, freespace, "%d", retlen);
1022                                 retlen = strlen(q);
1023                         }
1024
1025                 } else if (next) {
1026                         /*
1027                          *      Expand the second bit.
1028                          */
1029                         RDEBUG2("\t... expanding second conditional");
1030                         retlen = radius_xlat(q, freespace, next, request, func);
1031                 }
1032                 q += retlen;
1033
1034         } else {
1035                 /*
1036                  *      No attribute by that name, return an error.
1037                  */
1038                 RDEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", xlat_name, *from);
1039                 return -1;
1040         }
1041
1042 done:
1043         *to = q;
1044         return 0;
1045 }
1046
1047 /*
1048  *  If the caller doesn't pass xlat an escape function, then
1049  *  we use this one.  It simplifies the coding, as the check for
1050  *  func == NULL only happens once.
1051  */
1052 static size_t xlat_copy(char *out, size_t outlen, const char *in)
1053 {
1054         int freespace = outlen;
1055
1056         if (outlen < 1) return 0;
1057
1058         while ((*in) && (freespace > 1)) {
1059                 /*
1060                  *  Copy data.
1061                  *
1062                  *  FIXME: Do escaping of bad stuff!
1063                  */
1064                 *(out++) = *(in++);
1065
1066                 freespace--;
1067         }
1068         *out = '\0';
1069
1070         return (outlen - freespace); /* count does not include NUL */
1071 }
1072
1073 /*
1074  *      Replace %<whatever> in a string.
1075  *
1076  *      See 'doc/variables.txt' for more information.
1077  */
1078 int radius_xlat(char *out, int outlen, const char *fmt,
1079                 REQUEST *request, RADIUS_ESCAPE_STRING func)
1080 {
1081         int c, len, freespace;
1082         const char *p;
1083         char *q;
1084         char *nl;
1085         VALUE_PAIR *tmp;
1086         struct tm *TM, s_TM;
1087         char tmpdt[40]; /* For temporary storing of dates */
1088         int openbraces=0;
1089
1090         /*
1091          *      Catch bad modules.
1092          */
1093         if (!fmt || !out || !request) return 0;
1094
1095         /*
1096          *  Ensure that we always have an escaping function.
1097          */
1098         if (func == NULL) {
1099                 func = xlat_copy;
1100         }
1101
1102         q = out;
1103         p = fmt;
1104         while (*p) {
1105                 /* Calculate freespace in output */
1106                 freespace = outlen - (q - out);
1107                 if (freespace <= 1)
1108                         break;
1109                 c = *p;
1110
1111                 if ((c != '%') && (c != '$') && (c != '\\')) {
1112                         /*
1113                          * We check if we're inside an open brace.  If we are
1114                          * then we assume this brace is NOT literal, but is
1115                          * a closing brace and apply it
1116                          */
1117                         if ((c == '}') && openbraces) {
1118                                 openbraces--;
1119                                 p++; /* skip it */
1120                                 continue;
1121                         }
1122                         *q++ = *p++;
1123                         continue;
1124                 }
1125
1126                 /*
1127                  *      There's nothing after this character, copy
1128                  *      the last '%' or "$' or '\\' over to the output
1129                  *      buffer, and exit.
1130                  */
1131                 if (*++p == '\0') {
1132                         *q++ = c;
1133                         break;
1134                 }
1135
1136                 if (c == '\\') {
1137                         switch(*p) {
1138                         case '\\':
1139                                 *q++ = *p;
1140                                 break;
1141                         case 't':
1142                                 *q++ = '\t';
1143                                 break;
1144                         case 'n':
1145                                 *q++ = '\n';
1146                                 break;
1147                         default:
1148                                 *q++ = c;
1149                                 *q++ = *p;
1150                                 break;
1151                         }
1152                         p++;
1153
1154                 } else if (c == '%') switch(*p) {
1155                         case '{':
1156                                 p--;
1157                                 if (decode_attribute(&p, &q, freespace, request, func) < 0) return 0;
1158                                 break;
1159
1160                         case '%':
1161                                 *q++ = *p++;
1162                                 break;
1163                         case 'a': /* Protocol: */
1164                                 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_PROTOCOL, 0),PW_TYPE_INTEGER, func);
1165                                 p++;
1166                                 break;
1167                         case 'c': /* Callback-Number */
1168                                 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_CALLBACK_NUMBER, 0),PW_TYPE_STRING, func);
1169                                 p++;
1170                                 break;
1171                         case 'd': /* request day */
1172                                 TM = localtime_r(&request->timestamp, &s_TM);
1173                                 len = strftime(tmpdt, sizeof(tmpdt), "%d", TM);
1174                                 if (len > 0) {
1175                                         strlcpy(q, tmpdt, freespace);
1176                                         q += strlen(q);
1177                                 }
1178                                 p++;
1179                                 break;
1180                         case 'f': /* Framed IP address */
1181                                 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_IP_ADDRESS, 0),PW_TYPE_IPADDR, func);
1182                                 p++;
1183                                 break;
1184                         case 'i': /* Calling station ID */
1185                                 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CALLING_STATION_ID, 0),PW_TYPE_STRING, func);
1186                                 p++;
1187                                 break;
1188                         case 'l': /* request timestamp */
1189                                 snprintf(tmpdt, sizeof(tmpdt), "%lu",
1190                                          (unsigned long) request->timestamp);
1191                                 strlcpy(q,tmpdt,freespace);
1192                                 q += strlen(q);
1193                                 p++;
1194                                 break;
1195                         case 'm': /* request month */
1196                                 TM = localtime_r(&request->timestamp, &s_TM);
1197                                 len = strftime(tmpdt, sizeof(tmpdt), "%m", TM);
1198                                 if (len > 0) {
1199                                         strlcpy(q, tmpdt, freespace);
1200                                         q += strlen(q);
1201                                 }
1202                                 p++;
1203                                 break;
1204                         case 'n': /* NAS IP address */
1205                                 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_IP_ADDRESS, 0),PW_TYPE_IPADDR, func);
1206                                 p++;
1207                                 break;
1208                         case 'p': /* Port number */
1209                                 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_PORT, 0),PW_TYPE_INTEGER, func);
1210                                 p++;
1211                                 break;
1212                         case 's': /* Speed */
1213                                 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CONNECT_INFO, 0),PW_TYPE_STRING, func);
1214                                 p++;
1215                                 break;
1216                         case 't': /* request timestamp */
1217                                 CTIME_R(&request->timestamp, tmpdt, sizeof(tmpdt));
1218                                 nl = strchr(tmpdt, '\n');
1219                                 if (nl) *nl = '\0';
1220                                 strlcpy(q, tmpdt, freespace);
1221                                 q += strlen(q);
1222                                 p++;
1223                                 break;
1224                         case 'u': /* User name */
1225                                 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_USER_NAME, 0),PW_TYPE_STRING, func);
1226                                 p++;
1227                                 break;
1228                         case 'A': /* radacct_dir */
1229                                 strlcpy(q,radacct_dir,freespace);
1230                                 q += strlen(q);
1231                                 p++;
1232                                 break;
1233                         case 'C': /* ClientName */
1234                                 strlcpy(q,request->client->shortname,freespace);
1235                                 q += strlen(q);
1236                                 p++;
1237                                 break;
1238                         case 'D': /* request date */
1239                                 TM = localtime_r(&request->timestamp, &s_TM);
1240                                 len = strftime(tmpdt, sizeof(tmpdt), "%Y%m%d", TM);
1241                                 if (len > 0) {
1242                                         strlcpy(q, tmpdt, freespace);
1243                                         q += strlen(q);
1244                                 }
1245                                 p++;
1246                                 break;
1247                         case 'H': /* request hour */
1248                                 TM = localtime_r(&request->timestamp, &s_TM);
1249                                 len = strftime(tmpdt, sizeof(tmpdt), "%H", TM);
1250                                 if (len > 0) {
1251                                         strlcpy(q, tmpdt, freespace);
1252                                         q += strlen(q);
1253                                 }
1254                                 p++;
1255                                 break;
1256                         case 'L': /* radlog_dir */
1257                                 strlcpy(q,radlog_dir,freespace);
1258                                 q += strlen(q);
1259                                 p++;
1260                                 break;
1261                         case 'M': /* MTU */
1262                                 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_MTU, 0),PW_TYPE_INTEGER, func);
1263                                 p++;
1264                                 break;
1265                         case 'R': /* radius_dir */
1266                                 strlcpy(q,radius_dir,freespace);
1267                                 q += strlen(q);
1268                                 p++;
1269                                 break;
1270                         case 'S': /* request timestamp in SQL format*/
1271                                 TM = localtime_r(&request->timestamp, &s_TM);
1272                                 len = strftime(tmpdt, sizeof(tmpdt), "%Y-%m-%d %H:%M:%S", TM);
1273                                 if (len > 0) {
1274                                         strlcpy(q, tmpdt, freespace);
1275                                         q += strlen(q);
1276                                 }
1277                                 p++;
1278                                 break;
1279                         case 'T': /* request timestamp */
1280                                 TM = localtime_r(&request->timestamp, &s_TM);
1281                                 len = strftime(tmpdt, sizeof(tmpdt), "%Y-%m-%d-%H.%M.%S.000000", TM);
1282                                 if (len > 0) {
1283                                         strlcpy(q, tmpdt, freespace);
1284                                         q += strlen(q);
1285                                 }
1286                                 p++;
1287                                 break;
1288                         case 'U': /* Stripped User name */
1289                                 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_STRIPPED_USER_NAME, 0),PW_TYPE_STRING, func);
1290                                 p++;
1291                                 break;
1292                         case 'V': /* Request-Authenticator */
1293                                 strlcpy(q,"Verified",freespace);
1294                                 q += strlen(q);
1295                                 p++;
1296                                 break;
1297                         case 'Y': /* request year */
1298                                 TM = localtime_r(&request->timestamp, &s_TM);
1299                                 len = strftime(tmpdt, sizeof(tmpdt), "%Y", TM);
1300                                 if (len > 0) {
1301                                         strlcpy(q, tmpdt, freespace);
1302                                         q += strlen(q);
1303                                 }
1304                                 p++;
1305                                 break;
1306                         case 'Z': /* Full request pairs except password */
1307                                 tmp = request->packet->vps;
1308                                 while (tmp && (freespace > 3)) {
1309                                         if (tmp->attribute != PW_USER_PASSWORD) {
1310                                                 *q++ = '\t';
1311                                                 len = vp_prints(q, freespace - 2, tmp);
1312                                                 q += len;
1313                                                 freespace -= (len + 2);
1314                                                 *q++ = '\n';
1315                                         }
1316                                         tmp = tmp->next;
1317                                 }
1318                                 p++;
1319                                 break;
1320                         default:
1321                                 RDEBUG2("WARNING: Unknown variable '%%%c': See 'doc/variables.txt'", *p);
1322                                 if (freespace > 2) {
1323                                         *q++ = '%';
1324                                         *q++ = *p++;
1325                                 }
1326                                 break;
1327                 }
1328         }
1329         *q = '\0';
1330
1331         RDEBUG2("\texpand: %s -> %s", fmt, out);
1332
1333         return strlen(out);
1334 }