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