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