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