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