Added tolower function
[freeradius.git] / src / main / xlat.c
index 4794c6c..9c32288 100644 (file)
@@ -26,6 +26,7 @@
 RCSID("$Id$")
 
 #include       <freeradius-devel/radiusd.h>
+#include       <freeradius-devel/md5.h>
 #include       <freeradius-devel/rad_assert.h>
 
 #include       <ctype.h>
@@ -48,6 +49,8 @@ static const char * const internal_xlat[] = {"check",
                                             "reply",
                                             "proxy-request",
                                             "proxy-reply",
+                                            "outer.request",
+                                            "outer.reply",
                                             NULL};
 
 #if REQUEST_MAX_REGEX > 8
@@ -95,9 +98,9 @@ static int valuepair2str(char * out,int outlen,VALUE_PAIR * pair,
 /*
  *     Dynamically translate for check:, request:, reply:, etc.
  */
-static int xlat_packet(void *instance, REQUEST *request,
-                      char *fmt, char *out, size_t outlen,
-                      RADIUS_ESCAPE_STRING func)
+static size_t xlat_packet(void *instance, REQUEST *request,
+                         char *fmt, char *out, size_t outlen,
+                         RADIUS_ESCAPE_STRING func)
 {
        DICT_ATTR       *da;
        VALUE_PAIR      *vp;
@@ -120,15 +123,33 @@ static int xlat_packet(void *instance, REQUEST *request,
                break;
 
        case 3:
+#ifdef WITH_PROXY
                if (request->proxy) vps = request->proxy->vps;
                packet = request->proxy;
+#endif
                break;
 
        case 4:
+#ifdef WITH_PROXY
                if (request->proxy_reply) vps = request->proxy_reply->vps;
                packet = request->proxy_reply;
+#endif
                break;
 
+       case 5:
+               if (request->parent) {
+                       vps = request->parent->packet->vps;
+                       packet = request->parent->packet;
+               }
+               break;
+                       
+       case 6:
+               if (request->parent && request->parent->reply) {
+                       vps = request->parent->reply->vps;
+                       packet = request->parent->reply;
+               }
+               break;
+                       
        default:                /* WTF? */
                return 0;
        }
@@ -138,18 +159,44 @@ static int xlat_packet(void *instance, REQUEST *request,
         */
        da = dict_attrbyname(fmt);
        if (!da) {
-               int count;
-               const char *p = strchr(fmt, '[');
+               int do_number = FALSE;
+               size_t count;
+               const char *p;
                char buffer[256];
 
-               if (!p) return 0;
                if (strlen(fmt) > sizeof(buffer)) return 0;
 
+               p = strchr(fmt, '[');
+               if (!p) {
+                       p = strchr(fmt, '#');
+                       if (!p) return 0;
+                       do_number = TRUE;
+               }
+
                strlcpy(buffer, fmt, p - fmt + 1);
 
                da = dict_attrbyname(buffer);
                if (!da) return 0;
 
+               if (do_number) {
+                       vp = pairfind(vps, da->attr, 0);
+                       if (!vp) return 0;
+
+                       switch (da->type) {
+                       default:
+                               break;
+
+                       case PW_TYPE_INTEGER:
+                       case PW_TYPE_DATE:
+                       case PW_TYPE_SHORT:
+                       case PW_TYPE_BYTE:
+                               snprintf(out, outlen, "%u", vp->lvalue);
+                               return strlen(out);
+                       }
+
+                       goto just_print;
+               }
+
                /*
                 *      %{Attribute-Name[#]} returns the count of
                 *      attributes of that name in the list.
@@ -157,31 +204,31 @@ static int xlat_packet(void *instance, REQUEST *request,
                if ((p[1] == '#') && (p[2] == ']')) {
                        count = 0;
 
-                       for (vp = pairfind(vps, da->attr);
+                       for (vp = pairfind(vps, da->attr, da->vendor);
                             vp != NULL;
-                            vp = pairfind(vp->next, da->attr)) {
+                            vp = pairfind(vp->next, da->attr, da->vendor)) {
                                count++;
                        }
-                       snprintf(out, outlen, "%d", count);
+                       snprintf(out, outlen, "%d", (int) count);
                        return strlen(out);
                }
 
                /*
                 *      %{Attribute-Name[*]} returns ALL of the
                 *      the attributes, separated by a newline.
-                */             
+                */
                if ((p[1] == '*') && (p[2] == ']')) {
                        int total = 0;
 
-                       for (vp = pairfind(vps, da->attr);
+                       for (vp = pairfind(vps, da->attr, da->vendor);
                             vp != NULL;
-                            vp = pairfind(vp->next, da->attr)) {
+                            vp = pairfind(vp->next, da->attr, da->vendor)) {
                                count = valuepair2str(out, outlen - 1, vp, da->type, func);
                                rad_assert(count <= outlen);
                                total += count + 1;
                                outlen -= (count + 1);
                                out += count;
-                               
+
                                *(out++) = '\n';
 
                                if (outlen == 0) break;
@@ -189,7 +236,7 @@ static int xlat_packet(void *instance, REQUEST *request,
 
                        return total;
                }
-               
+
                count = atoi(p + 1);
 
                /*
@@ -197,7 +244,7 @@ static int xlat_packet(void *instance, REQUEST *request,
                 */
                p += 1 + strspn(p + 1, "0123456789");
                if (*p != ']') {
-                       DEBUG2("xlat: Invalid array reference in string at %s %s",
+                       RDEBUG2("xlat: Invalid array reference in string at %s %s",
                               fmt, p);
                        return 0;
                }
@@ -205,9 +252,9 @@ static int xlat_packet(void *instance, REQUEST *request,
                /*
                 *      Find the N'th value.
                 */
-               for (vp = pairfind(vps, da->attr);
+               for (vp = pairfind(vps, da->attr, da->vendor);
                     vp != NULL;
-                    vp = pairfind(vp->next, da->attr)) {
+                    vp = pairfind(vp->next, da->attr, da->vendor)) {
                        if (count == 0) break;
                        count--;
                }
@@ -216,11 +263,11 @@ static int xlat_packet(void *instance, REQUEST *request,
                 *      Non-existent array reference.
                 */
                if (!vp) return 0;
-
+       just_print:
                return valuepair2str(out, outlen, vp, da->type, func);
        }
 
-       vp = pairfind(vps, da->attr);
+       vp = pairfind(vps, da->attr, da->vendor);
        if (!vp) {
                /*
                 *      Some "magic" handlers, which are never in VP's, but
@@ -232,14 +279,14 @@ static int xlat_packet(void *instance, REQUEST *request,
                if (packet) {
                        VALUE_PAIR localvp;
 
-                       localvp.vp_strvalue[0] = 0;
+                       memset(&localvp, 0, sizeof(localvp));
 
                        switch (da->attr) {
                        case PW_PACKET_TYPE:
                        {
                                DICT_VALUE *dval;
-                               
-                               dval = dict_valbyattr(da->attr, packet->code);
+
+                               dval = dict_valbyattr(da->attr, da->vendor, packet->code);
                                if (dval) {
                                        snprintf(out, outlen, "%s", dval->name);
                                } else {
@@ -249,31 +296,39 @@ static int xlat_packet(void *instance, REQUEST *request,
                        }
                        break;
 
+                       case PW_CLIENT_SHORTNAME:
+                               if (request->client && request->client->shortname) {
+                                       strlcpy(out, request->client->shortname, outlen);
+                               } else {
+                                       strlcpy(out, "<UNKNOWN-CLIENT>", outlen);
+                               }
+                               return strlen(out);
+
                        case PW_CLIENT_IP_ADDRESS: /* the same as below */
                        case PW_PACKET_SRC_IP_ADDRESS:
                                if (packet->src_ipaddr.af != AF_INET) {
                                        return 0;
                                }
                                localvp.attribute = da->attr;
-                               localvp.lvalue = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
+                               localvp.vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
                                break;
-                       
+
                        case PW_PACKET_DST_IP_ADDRESS:
                                if (packet->dst_ipaddr.af != AF_INET) {
                                        return 0;
                                }
                                localvp.attribute = da->attr;
-                               localvp.lvalue = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
+                               localvp.vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
                                break;
-                       
+
                        case PW_PACKET_SRC_PORT:
                                localvp.attribute = da->attr;
-                               localvp.lvalue = packet->src_port;
+                               localvp.vp_integer = packet->src_port;
                                break;
-                       
+
                        case PW_PACKET_DST_PORT:
                                localvp.attribute = da->attr;
-                               localvp.lvalue = packet->dst_port;
+                               localvp.vp_integer = packet->dst_port;
                                break;
 
                        case PW_PACKET_AUTHENTICATION_VECTOR:
@@ -293,7 +348,7 @@ static int xlat_packet(void *instance, REQUEST *request,
                                        strlcpy(out, "server_core", outlen);
                                }
                                return strlen(out);
-                       
+
                        case PW_PACKET_SRC_IPV6_ADDRESS:
                                if (packet->src_ipaddr.af != AF_INET6) {
                                        return 0;
@@ -303,7 +358,7 @@ static int xlat_packet(void *instance, REQUEST *request,
                                       &packet->src_ipaddr.ipaddr.ip6addr,
                                       sizeof(packet->src_ipaddr.ipaddr.ip6addr));
                                break;
-                       
+
                        case PW_PACKET_DST_IPV6_ADDRESS:
                                if (packet->dst_ipaddr.af != AF_INET6) {
                                        return 0;
@@ -313,14 +368,23 @@ static int xlat_packet(void *instance, REQUEST *request,
                                       &packet->dst_ipaddr.ipaddr.ip6addr,
                                       sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
                                break;
-                       
-                       case PW_SERVER_IDENTITY:
-                               if (!request->listener || !request->listener->identity) return 0;
 
-                               snprintf(out, outlen, "%s", request->listener->identity);
+                       case PW_VIRTUAL_SERVER:
+                               if (!request->server) return 0;
+
+                               snprintf(out, outlen, "%s", request->server);
                                return strlen(out);
                                break;
-                       
+
+                       case PW_MODULE_RETURN_CODE:
+                               localvp.attribute = da->attr;
+
+                               /*
+                                *      See modcall.c for a bit of a hack.
+                                */
+                               localvp.vp_integer = request->simul_max;
+                               break;
+
                        default:
                                return 0; /* not found */
                                break;
@@ -349,9 +413,9 @@ static int xlat_packet(void *instance, REQUEST *request,
 /*
  *     Pull %{0} to %{8} out of the packet.
  */
-static int xlat_regex(void *instance, REQUEST *request,
-                     char *fmt, char *out, size_t outlen,
-                     RADIUS_ESCAPE_STRING func)
+static size_t xlat_regex(void *instance, REQUEST *request,
+                        char *fmt, char *out, size_t outlen,
+                        RADIUS_ESCAPE_STRING func)
 {
        char *regex;
 
@@ -361,7 +425,7 @@ static int xlat_regex(void *instance, REQUEST *request,
         */
        fmt = fmt;              /* -Wunused */
        func = func;            /* -Wunused FIXME: do escaping? */
-       
+
        regex = request_data_reference(request, request,
                                 REQUEST_DATA_REGEX | *(int *)instance);
        if (!regex) return 0;
@@ -375,6 +439,95 @@ static int xlat_regex(void *instance, REQUEST *request,
 }
 #endif                         /* HAVE_REGEX_H */
 
+
+/*
+ *     Change the debugging level.
+ */
+static size_t xlat_debug(UNUSED void *instance, REQUEST *request,
+                         char *fmt, char *out, size_t outlen,
+                         UNUSED RADIUS_ESCAPE_STRING func)
+{
+       int level = 0;
+
+       if (*fmt) level = atoi(fmt);
+
+       if (level == 0) {
+               request->options = RAD_REQUEST_OPTION_NONE;
+               request->radlog = NULL;
+       } else {
+               if (level > 4) level = 4;
+
+               request->options = level;
+               request->radlog = radlog_request;
+       }
+
+       snprintf(out, outlen, "%d", level);
+       return strlen(out);
+}
+
+
+/*
+ *     Calculate the MD5 hash of a string.
+ */
+static size_t xlat_md5(UNUSED void *instance, REQUEST *request,
+                      char *fmt, char *out, size_t outlen,
+                      UNUSED RADIUS_ESCAPE_STRING func)
+{
+       int i;
+       uint8_t digest[16];
+       FR_MD5_CTX ctx;
+       char buffer[1024];
+
+       if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
+               *out = '\0';
+               return 0;
+       }
+
+       fr_MD5Init(&ctx);
+       fr_MD5Update(&ctx, (void *) buffer, strlen(buffer));
+       fr_MD5Final(digest, &ctx);
+
+       if (outlen < 33) {
+               snprintf(out, outlen, "md5_overflow");
+               return strlen(out);
+       }
+
+       for (i = 0; i < 16; i++) {
+               snprintf(out + i * 2, 3, "%02x", digest[i]);
+       }
+
+       return strlen(out);
+}
+
+
+/*
+ *     Convert a string to lowercase
+ */
+static size_t xlat_lc(UNUSED void *instance, REQUEST *request,
+                      char *fmt, char *out, size_t outlen,
+                      UNUSED RADIUS_ESCAPE_STRING func)
+{
+       char *p, *q;
+       char buffer[1024];
+
+       if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
+               *out = '\0';
+               return 0;
+       }
+
+       for (p = buffer, q = out; *p != '\0'; p++, outlen--) {
+               if (outlen <= 1) break;
+
+               *(q++) = tolower((int) *p);
+       }
+
+       *q = '\0';
+
+       return strlen(out);
+}
+
+
+
 /*
  *     Compare two xlat_t structs, based ONLY on the module name.
  */
@@ -389,11 +542,10 @@ static int xlat_cmp(const void *a, const void *b)
                      ((const xlat_t *)a)->length);
 }
 
-
 /*
  *     find the appropriate registered xlat function.
  */
-static const xlat_t *xlat_find(const char *module)
+static xlat_t *xlat_find(const char *module)
 {
        xlat_t my_xlat;
 
@@ -401,16 +553,9 @@ static const xlat_t *xlat_find(const char *module)
         *      Look for dictionary attributes first.
         */
        if ((dict_attrbyname(module) != NULL) ||
-           (strchr(module, '[') != NULL)) {
-               static const xlat_t dict_xlat = {
-                       "request",
-                       7,
-                       &xlat_inst[1],
-                       xlat_packet,
-                       TRUE
-               };
-
-               return &dict_xlat;
+           (strchr(module, '[') != NULL) ||
+           (strchr(module, '#') != NULL)) {
+               module = "request";
        }
 
        strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
@@ -461,6 +606,14 @@ int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance)
                        c->internal = TRUE;
                }
 
+               /*
+                *      New name: "control"
+                */
+               xlat_register("control", xlat_packet, &xlat_inst[0]);
+               c = xlat_find("control");
+               rad_assert(c != NULL);
+               c->internal = TRUE;
+
 #ifdef HAVE_REGEX_H
                /*
                 *      Register xlat's for regexes.
@@ -474,6 +627,22 @@ int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance)
                        c->internal = TRUE;
                }
 #endif /* HAVE_REGEX_H */
+
+
+               xlat_register("debug", xlat_debug, &xlat_inst[0]);
+               c = xlat_find("debug");
+               rad_assert(c != NULL);
+               c->internal = TRUE;
+
+               xlat_register("md5", xlat_md5, &xlat_inst[0]);
+               c = xlat_find("md5");
+               rad_assert(c != NULL);
+               c->internal = TRUE;
+
+               xlat_register("tolower", xlat_lc, &xlat_inst[0]);
+               c = xlat_find("tolower");
+               rad_assert(c != NULL);
+               c->internal = TRUE;
        }
 
        /*
@@ -496,7 +665,7 @@ int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance)
        /*
         *      Doesn't exist.  Create it.
         */
-       c = rad_malloc(sizeof(xlat_t));
+       c = rad_malloc(sizeof(*c));
        memset(c, 0, sizeof(*c));
 
        c->do_xlat = func;
@@ -546,222 +715,220 @@ void xlat_free(void)
 /*
  *     Decode an attribute name into a string.
  */
-static void decode_attribute(const char **from, char **to, int freespace,
-                            int *open, REQUEST *request,
+static int decode_attribute(const char **from, char **to, int freespace,
+                            REQUEST *request,
                             RADIUS_ESCAPE_STRING func)
 {
        int     do_length = 0;
-       char    xlat_name[128];
-       char    *xlat_string = NULL; /* can be large */
-       int     free_xlat_string = FALSE;
-       const char *p;
-       char *q, *pa;
-       int found=0, retlen=0;
-       int openbraces = *open;
+       char    *xlat_name, *xlat_string;
+       char *p, *q, *l, *next = NULL;
+       int retlen=0;
        const xlat_t *c;
+       int varlen;
+       char buffer[8192];
 
-       p = *from;
        q = *to;
-       pa = &xlat_name[0];
 
        *q = '\0';
 
        /*
-        * Skip the '{' at the front of 'p'
-        * Increment open braces
+        *      Copy the input string to an intermediate buffer where
+        *      we can mangle it.
         */
-       p++;
-       openbraces++;
+       varlen = rad_copy_variable(buffer, *from);
+       if (varlen < 0) {
+               RDEBUG2("Badly formatted variable: %s", *from);
+               return -1;
+       }
+       *from += varlen;
 
+       /*
+        *      Kill the %{} around the data we are looking for.
+        */
+       p = buffer;
+       p[varlen - 1] = '\0';   /*  */
+       p += 2;
        if (*p == '#') {
                p++;
                do_length = 1;
        }
 
        /*
-        *      First, copy the xlat key name to one buffer
+        *      Handle %{%{foo}:-%{bar}}, which is useful, too.
+        *
+        *      Did I mention that this parser is garbage?
         */
-       while (*p && (*p != '}') && (*p != ':')) {
-               *pa++ = *p++;
+       if ((p[0] == '%') && (p[1] == '{')) {
+               int len1, len2;
+               int expand2 = FALSE;
 
-               if (pa >= (xlat_name + sizeof(xlat_name) - 1)) {
-                       /*
-                        *      Skip to the end of the input
-                        */
-                       p += strlen(p);
-                       DEBUG("xlat: Module name is too long in string %%%s",
-                             *from);
-                       goto done;
+               /*
+                *      'p' is after the start of 'buffer', so we can
+                *      safely do this.
+                */
+               len1 = rad_copy_variable(buffer, p);
+               if (len1 < 0) {
+                       RDEBUG2("Badly formatted variable: %s", p);
+                       return -1;
                }
-       }
-       *pa = '\0';
-
-       if (!*p) {
-               DEBUG("xlat: Invalid syntax in %s", *from);
 
                /*
-                *      %{name} is a simple attribute reference,
-                *      or regex reference.
+                *      They did %{%{foo}}, which is stupid, but allowed.
                 */
-       } else if (*p == '}') {
-               openbraces--;
-               rad_assert(openbraces == *open);
-
-               p++;
-               xlat_string = xlat_name;
-               goto do_xlat;
-
-       } else if (p[1] == '-') { /* handle ':- */
-               p += 2;
-               xlat_string = xlat_name;
-               goto do_xlat;
-               
-       } else {      /* module name, followed by per-module string */
-               int stop = 0;
-               int delimitbrace = *open;
-
-               rad_assert(*p == ':');
-               p++;                    /* skip the ':' */
+               if (!p[len1]) {
+                       RDEBUG2("Improperly nested variable; %%{%s}", p);
+                       return -1;
+               }
 
                /*
-                *  If there's a brace immediately following the colon,
-                *  then we've chosen to delimite the per-module string,
-                *  so keep track of that.
+                *      It SHOULD be %{%{foo}:-%{bar}}.  If not, it's
+                *      an error.
                 */
-               if (*p == '{') {
-                       delimitbrace = openbraces;
-                       openbraces++;
-                       p++;
+               if ((p[len1] != ':') || (p[len1 + 1] != '-')) {
+                       RDEBUG2("No trailing :- after variable at %s", p);
+                       return -1;
                }
-               
-               xlat_string = rad_malloc(strlen(p) + 1); /* always returns */
-               free_xlat_string = TRUE;
-               pa = xlat_string;
-               
+
                /*
-                *  Copy over the rest of the string, which is per-module
-                *  data.
+                *      Parse the second bit.  The second bit can be
+                *      either %{foo}, or a string "foo", or a string
+                *      'foo', or just a bare word: foo
                 */
-               while (*p && !stop) {
-                       switch(*p) {
-                               /*
-                                *      What the heck is this supposed
-                                *      to be doing?
-                                */
-                       case '\\':
-                               p++; /* skip it */
-                               *pa++ = *p++;
-                               break;
+               p += len1 + 2;
+               l = buffer + len1 + 1;
 
-                               /*
-                                *      This is pretty hokey...  we
-                                *      should use the functions in
-                                *      util.c
-                                */
-                       case '{':
-                               openbraces++;
-                               *pa++ = *p++;
-                               break;
+               if ((p[0] == '%') && (p[1] == '{')) {
+                       len2 = rad_copy_variable(l, p);
 
-                       case '}':
-                               openbraces--;
-                               if (openbraces == delimitbrace) {
-                                       p++;
-                                       stop=1;
-                               } else {
-                                       *pa++ = *p++;
-                               }
-                               break;
-                               
-                       default:
-                               *pa++ = *p++;
-                               break;
+                       if (len2 < 0) {
+                               RDEBUG2("Invalid text after :- at %s", p);
+                               return -1;
                        }
-               }
+                       p += len2;
+                       expand2 = TRUE;
+
+               } else if ((p[0] == '"') || p[0] == '\'') {
+                       getstring(&p, l, strlen(l));
 
-               *pa = '\0';
+               } else {
+                       l = p;
+               }
 
                /*
-                *      Now check to see if we're at the end of the string
-                *      we were sent.  If we're not, check for :-
+                *      Expand the first one.  If we did, exit the
+                *      conditional.
                 */
-               if (openbraces == delimitbrace) {
-                       if (p[0] == ':' && p[1] == '-') {
-                               p += 2;
-                       }
+               retlen = radius_xlat(q, freespace, buffer, request, func);
+               if (retlen) {
+                       q += retlen;
+                       goto done;
                }
-               
+
+               RDEBUG2("\t... expanding second conditional");
                /*
-                *      Look up almost everything in the new tree of xlat
-                *      functions.  This makes it a little quicker...
+                *      Expand / copy the second string if required.
                 */
-       do_xlat:
-               if ((c = xlat_find(xlat_name)) != NULL) {
-                       if (!c->internal) DEBUG("radius_xlat: Running registered xlat function of module %s for string \'%s\'",
-                                               c->module, xlat_string);
-                       retlen = c->do_xlat(c->instance, request, xlat_string,
-                                           q, freespace, func);
-                       /* If retlen is 0, treat it as not found */
-                       if (retlen > 0) found = 1;
-#ifndef NDEBUG
+               if (expand2) {
+                       retlen = radius_xlat(q, freespace, l,
+                                           request, func);
+                       if (retlen) {
+                               q += retlen;
+                       }
                } else {
-                       /*
-                        *      No attribute by that name, return an error.
-                        */
-                       DEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", xlat_name, *from);
-#endif
+                       strlcpy(q, l, freespace);
+                       q += strlen(q);
                }
+
+               /*
+                *      Else the output is an empty string.
+                */
+               goto done;
        }
 
        /*
-        * Skip to last '}' if attr is found
-        * The rest of the stuff within the braces is
-        * useless if we found what we need
+        *      See if we're supposed to expand a module name.
         */
-       if (found) {
-               if (do_length) {
-                       snprintf(q, freespace, "%d", retlen);
-                       retlen = strlen(q);
+       xlat_name = NULL;
+       for (l = p; *l != '\0'; l++) {
+               if (*l == '\\') {
+                       l++;
+                       continue;
                }
 
-               q += retlen;
+               if (*l == ':') {
+                       xlat_name = p; /* start of name */
+                       *l = '\0';
+                       p = l + 1;
+                       break;
+               }
 
-               while((*p != '\0') && (openbraces > *open)) {
-                       /*
-                        *      Handle escapes outside of the loop.
-                        */
-                       if (*p == '\\') {
-                               p++;
-                               if (!*p) break;
-                               p++; /* get & ignore next character */
-                               continue;
-                       }
+               /*
+                *      Module names can't have spaces.
+                */
+               if ((*l == ' ') || (*l == '\t')) break;
+       }
 
-                       switch (*p) {
-                       default:
-                               break;
+       /*
+        *      %{name} is a simple attribute reference,
+        *      or regex reference.
+        */
+       if (!xlat_name) {
+               xlat_name = xlat_string = p;
+               goto do_xlat;
+       }
 
-                               /*
-                                *  Bare brace
-                                */
-                       case '{':
-                               openbraces++;
-                               break;
+       /*
+        *      Maybe it's the old-style %{foo:-bar}
+        */
+       if (*p == '-') {
+               RDEBUG2("WARNING: Deprecated conditional expansion \":-\".  See \"man unlang\" for details");
+               p++;
 
-                       case '}':
-                               openbraces--;
-                               break;
+               xlat_string = xlat_name;
+               next = p;
+               goto do_xlat;
+       }
+
+       /*
+        *      FIXME: For backwards "WTF" compatibility, check for
+        *      {...}, (after the :), and copy that, too.
+        */
+
+       /* module name, followed by (possibly) per-module string */
+       xlat_string = p;
+       
+do_xlat:
+       if ((c = xlat_find(xlat_name)) != NULL) {
+               if (!c->internal) RDEBUG3("radius_xlat: Running registered xlat function of module %s for string \'%s\'",
+                                         c->module, xlat_string);
+               retlen = c->do_xlat(c->instance, request, xlat_string,
+                                   q, freespace, func);
+               if (retlen > 0) {
+                       if (do_length) {
+                               snprintf(q, freespace, "%d", retlen);
+                               retlen = strlen(q);
                        }
-                       p++;    /* skip the character */
+
+               } else if (next) {
+                       /*
+                        *      Expand the second bit.
+                        */
+                       RDEBUG2("\t... expanding second conditional");
+                       retlen = radius_xlat(q, freespace, next, request, func);
                }
+               q += retlen;
+
+       } else {
+               /*
+                *      No attribute by that name, return an error.
+                */
+               RDEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", xlat_name, *from);
+               return -1;
        }
-       
-       done:
-       if (free_xlat_string) free(xlat_string);
 
-       *open = openbraces;
-       *from = p;
+done:
        *to = q;
+       return 0;
 }
 
 /*
@@ -769,7 +936,7 @@ static void decode_attribute(const char **from, char **to, int freespace,
  *  we use this one.  It simplifies the coding, as the check for
  *  func == NULL only happens once.
  */
-static int xlat_copy(char *out, int outlen, const char *in)
+static size_t xlat_copy(char *out, size_t outlen, const char *in)
 {
        int freespace = outlen;
 
@@ -871,35 +1038,21 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                        }
                        p++;
 
-                       /*
-                        *      Hmmm... ${User-Name} is a synonym for
-                        *      %{User-Name}.
-                        *
-                        *      Why, exactly?
-                        */
-               } else if (c == '$') switch(*p) {
-                       case '{': /* Attribute by Name */
-                               decode_attribute(&p, &q, freespace, &openbraces, request, func);
-                               break;
-                       default:
-                               *q++ = c;
-                               *q++ = *p++;
-                               break;
-
                } else if (c == '%') switch(*p) {
                        case '{':
-                               decode_attribute(&p, &q, freespace, &openbraces, request, func);
+                               p--;
+                               if (decode_attribute(&p, &q, freespace, request, func) < 0) return 0;
                                break;
 
                        case '%':
                                *q++ = *p++;
                                break;
                        case 'a': /* Protocol: */
-                               q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_PROTOCOL),PW_TYPE_INTEGER, func);
+                               q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_PROTOCOL, 0),PW_TYPE_INTEGER, func);
                                p++;
                                break;
                        case 'c': /* Callback-Number */
-                               q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_CALLBACK_NUMBER),PW_TYPE_STRING, func);
+                               q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_CALLBACK_NUMBER, 0),PW_TYPE_STRING, func);
                                p++;
                                break;
                        case 'd': /* request day */
@@ -912,11 +1065,11 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                                p++;
                                break;
                        case 'f': /* Framed IP address */
-                               q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_IP_ADDRESS),PW_TYPE_IPADDR, func);
+                               q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_IP_ADDRESS, 0),PW_TYPE_IPADDR, func);
                                p++;
                                break;
                        case 'i': /* Calling station ID */
-                               q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CALLING_STATION_ID),PW_TYPE_STRING, func);
+                               q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CALLING_STATION_ID, 0),PW_TYPE_STRING, func);
                                p++;
                                break;
                        case 'l': /* request timestamp */
@@ -936,15 +1089,15 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                                p++;
                                break;
                        case 'n': /* NAS IP address */
-                               q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_IP_ADDRESS),PW_TYPE_IPADDR, func);
+                               q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_IP_ADDRESS, 0),PW_TYPE_IPADDR, func);
                                p++;
                                break;
                        case 'p': /* Port number */
-                               q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_PORT),PW_TYPE_INTEGER, func);
+                               q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_PORT, 0),PW_TYPE_INTEGER, func);
                                p++;
                                break;
                        case 's': /* Speed */
-                               q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CONNECT_INFO),PW_TYPE_STRING, func);
+                               q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CONNECT_INFO, 0),PW_TYPE_STRING, func);
                                p++;
                                break;
                        case 't': /* request timestamp */
@@ -956,7 +1109,7 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                                p++;
                                break;
                        case 'u': /* User name */
-                               q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_USER_NAME),PW_TYPE_STRING, func);
+                               q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_USER_NAME, 0),PW_TYPE_STRING, func);
                                p++;
                                break;
                        case 'A': /* radacct_dir */
@@ -965,7 +1118,7 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                                p++;
                                break;
                        case 'C': /* ClientName */
-                               strlcpy(q,client_name_old(&request->packet->src_ipaddr),freespace);
+                               strlcpy(q,request->client->shortname,freespace);
                                q += strlen(q);
                                p++;
                                break;
@@ -993,7 +1146,7 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                                p++;
                                break;
                        case 'M': /* MTU */
-                               q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_MTU),PW_TYPE_INTEGER, func);
+                               q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_MTU, 0),PW_TYPE_INTEGER, func);
                                p++;
                                break;
                        case 'R': /* radius_dir */
@@ -1020,14 +1173,11 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                                p++;
                                break;
                        case 'U': /* Stripped User name */
-                               q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_STRIPPED_USER_NAME),PW_TYPE_STRING, func);
+                               q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_STRIPPED_USER_NAME, 0),PW_TYPE_STRING, func);
                                p++;
                                break;
                        case 'V': /* Request-Authenticator */
-                               if (request->packet->verified)
-                                       strlcpy(q,"Verified",freespace);
-                               else
-                                       strlcpy(q,"None",freespace);
+                               strlcpy(q,"Verified",freespace);
                                q += strlen(q);
                                p++;
                                break;
@@ -1055,7 +1205,7 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                                p++;
                                break;
                        default:
-                               DEBUG2("WARNING: Unknown variable '%%%c': See 'doc/variables.txt'", *p);
+                               RDEBUG2("WARNING: Unknown variable '%%%c': See 'doc/variables.txt'", *p);
                                if (freespace > 2) {
                                        *q++ = '%';
                                        *q++ = *p++;
@@ -1065,7 +1215,7 @@ int radius_xlat(char *out, int outlen, const char *fmt,
        }
        *q = '\0';
 
-       DEBUG2("radius_xlat:  '%s'", out);
+       RDEBUG2("\texpand: %s -> %s", fmt, out);
 
        return strlen(out);
 }