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