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