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