Massively cleaned up #include's, so they're in a consistent
[freeradius.git] / src / main / xlat.c
index 4e9fa93..4794c6c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * xlat.c      Translate strings.  This is the first version of xlat 
+ * xlat.c      Translate strings.  This is the first version of xlat
  *             incorporated to RADIUS
  *
  * Version:    $Id$
  *
  *   You should have received a copy of the GNU General Public License
  *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
- * Copyright 2000  The FreeRADIUS server project
+ * Copyright 2000,2006  The FreeRADIUS server project
  * Copyright 2000  Alan DeKok <aland@ox.org>
  */
 
-static const char rcsid[] = 
-"$Id$";
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
 
-#include       "autoconf.h"
-#include       "libradius.h"
+#include       <freeradius-devel/radiusd.h>
+#include       <freeradius-devel/rad_assert.h>
 
-#include       <stdio.h>
-#include       <stdlib.h>
-#include       <string.h>
 #include       <ctype.h>
 
-#include       "radiusd.h"
+typedef struct xlat_t {
+       char            module[MAX_STRING_LEN];
+       int             length;
+       void            *instance;
+       RAD_XLAT_FUNC   do_xlat;
+       int             internal;       /* not allowed to re-define these */
+} xlat_t;
 
-struct xlat_cmp {
-       char module[MAX_STRING_LEN];
-       int length;
-       void *instance;
-       RAD_XLAT_FUNC do_xlat;
-       struct xlat_cmp *next;
-};
-
-static struct xlat_cmp *cmp = NULL;
+static rbtree_t *xlat_root = NULL;
 
 /*
- *      Register an xlat function.
+ *     Define all xlat's in the structure.
  */
-int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance)
-{
-       struct xlat_cmp      *c;
+static const char * const internal_xlat[] = {"check",
+                                            "request",
+                                            "reply",
+                                            "proxy-request",
+                                            "proxy-reply",
+                                            NULL};
 
-       if (module == NULL || strlen(module) == 0){
-               DEBUG("xlat_register: Invalid module name");
-               return -1;
-       }
+#if REQUEST_MAX_REGEX > 8
+#error Please fix the following line
+#endif
+static const int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };  /* up to 8 for regex */
 
-       xlat_unregister(module, func);
 
-       c = rad_malloc(sizeof(struct xlat_cmp));
+/*
+ *     Convert the value on a VALUE_PAIR to string
+ */
+static int valuepair2str(char * out,int outlen,VALUE_PAIR * pair,
+                        int type, RADIUS_ESCAPE_STRING func)
+{
+       char buffer[MAX_STRING_LEN * 4];
 
-       c->do_xlat = func;
-       strNcpy(c->module, module, sizeof(c->module));
-       c->length = strlen(c->module);
-       c->instance = instance;
-       c->next = cmp;
-       cmp = c;
+       if (pair != NULL) {
+               vp_prints_value(buffer, sizeof(buffer), pair, -1);
+               return func(out, outlen, buffer);
+       }
 
-       return 0;
+       switch (type) {
+       case PW_TYPE_STRING :
+               strlcpy(out,"_",outlen);
+               break;
+       case PW_TYPE_INTEGER :
+               strlcpy(out,"0",outlen);
+               break;
+       case PW_TYPE_IPADDR :
+               strlcpy(out,"?.?.?.?",outlen);
+               break;
+       case PW_TYPE_IPV6ADDR :
+               strlcpy(out,":?:",outlen);
+               break;
+       case PW_TYPE_DATE :
+               strlcpy(out,"0",outlen);
+               break;
+       default :
+               strlcpy(out,"unknown_type",outlen);
+       }
+       return strlen(out);
 }
 
+
 /*
- *      Unregister an xlat function.
+ *     Dynamically translate for check:, request:, reply:, etc.
  */
-void xlat_unregister(const char *module, RAD_XLAT_FUNC func)
+static int xlat_packet(void *instance, REQUEST *request,
+                      char *fmt, char *out, size_t outlen,
+                      RADIUS_ESCAPE_STRING func)
 {
-       struct xlat_cmp      *c, *last;
+       DICT_ATTR       *da;
+       VALUE_PAIR      *vp;
+       VALUE_PAIR      *vps = NULL;
+       RADIUS_PACKET   *packet = NULL;
+
+       switch (*(int*) instance) {
+       case 0:
+               vps = request->config_items;
+               break;
 
-       last = NULL;
-       for (c = cmp; c; c = c->next) {
-               if (strncmp(c->module,module,c->length) == 0 && c->do_xlat == func)
-                       break;
-               last = c;
+       case 1:
+               vps = request->packet->vps;
+               packet = request->packet;
+               break;
+
+       case 2:
+               vps = request->reply->vps;
+               packet = request->reply;
+               break;
+
+       case 3:
+               if (request->proxy) vps = request->proxy->vps;
+               packet = request->proxy;
+               break;
+
+       case 4:
+               if (request->proxy_reply) vps = request->proxy_reply->vps;
+               packet = request->proxy_reply;
+               break;
+
+       default:                /* WTF? */
+               return 0;
        }
 
-       if (c == NULL) return;
+       /*
+        *      The "format" string is the attribute name.
+        */
+       da = dict_attrbyname(fmt);
+       if (!da) {
+               int count;
+               const char *p = strchr(fmt, '[');
+               char buffer[256];
+
+               if (!p) return 0;
+               if (strlen(fmt) > sizeof(buffer)) return 0;
+
+               strlcpy(buffer, fmt, p - fmt + 1);
+
+               da = dict_attrbyname(buffer);
+               if (!da) return 0;
+
+               /*
+                *      %{Attribute-Name[#]} returns the count of
+                *      attributes of that name in the list.
+                */
+               if ((p[1] == '#') && (p[2] == ']')) {
+                       count = 0;
+
+                       for (vp = pairfind(vps, da->attr);
+                            vp != NULL;
+                            vp = pairfind(vp->next, da->attr)) {
+                               count++;
+                       }
+                       snprintf(out, outlen, "%d", 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);
+                            vp != NULL;
+                            vp = pairfind(vp->next, da->attr)) {
+                               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;
+                       }
+
+                       return total;
+               }
+               
+               count = atoi(p + 1);
 
-       if (last != NULL)
-               last->next = c->next;
-       else
-               cmp = c->next;
+               /*
+                *      Skip the numbers.
+                */
+               p += 1 + strspn(p + 1, "0123456789");
+               if (*p != ']') {
+                       DEBUG2("xlat: Invalid array reference in string at %s %s",
+                              fmt, p);
+                       return 0;
+               }
+
+               /*
+                *      Find the N'th value.
+                */
+               for (vp = pairfind(vps, da->attr);
+                    vp != NULL;
+                    vp = pairfind(vp->next, da->attr)) {
+                       if (count == 0) break;
+                       count--;
+               }
+
+               /*
+                *      Non-existent array reference.
+                */
+               if (!vp) return 0;
+
+               return valuepair2str(out, outlen, vp, da->type, func);
+       }
 
-       free(c);
+       vp = pairfind(vps, da->attr);
+       if (!vp) {
+               /*
+                *      Some "magic" handlers, which are never in VP's, but
+                *      which are in the packet.
+                *
+                *      FIXME: We should really do this in a more
+                *      intelligent way...
+                */
+               if (packet) {
+                       VALUE_PAIR localvp;
+
+                       localvp.vp_strvalue[0] = 0;
+
+                       switch (da->attr) {
+                       case PW_PACKET_TYPE:
+                       {
+                               DICT_VALUE *dval;
+                               
+                               dval = dict_valbyattr(da->attr, packet->code);
+                               if (dval) {
+                                       snprintf(out, outlen, "%s", dval->name);
+                               } else {
+                                       snprintf(out, outlen, "%d", packet->code);
+                               }
+                               return strlen(out);
+                       }
+                       break;
+
+                       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;
+                               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;
+                               break;
+                       
+                       case PW_PACKET_SRC_PORT:
+                               localvp.attribute = da->attr;
+                               localvp.lvalue = packet->src_port;
+                               break;
+                       
+                       case PW_PACKET_DST_PORT:
+                               localvp.attribute = da->attr;
+                               localvp.lvalue = packet->dst_port;
+                               break;
+
+                       case PW_PACKET_AUTHENTICATION_VECTOR:
+                               localvp.attribute = da->attr;
+                               memcpy(localvp.vp_strvalue, packet->vector,
+                                      sizeof(packet->vector));
+                               localvp.length = sizeof(packet->vector);
+                               break;
+
+                               /*
+                                *      Authorization, accounting, etc.
+                                */
+                       case PW_REQUEST_PROCESSING_STAGE:
+                               if (request->component) {
+                                       strlcpy(out, request->component, outlen);
+                               } else {
+                                       strlcpy(out, "server_core", outlen);
+                               }
+                               return strlen(out);
+                       
+                       case PW_PACKET_SRC_IPV6_ADDRESS:
+                               if (packet->src_ipaddr.af != AF_INET6) {
+                                       return 0;
+                               }
+                               localvp.attribute = da->attr;
+                               memcpy(localvp.vp_strvalue,
+                                      &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;
+                               }
+                               localvp.attribute = da->attr;
+                               memcpy(localvp.vp_strvalue,
+                                      &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);
+                               return strlen(out);
+                               break;
+                       
+                       default:
+                               return 0; /* not found */
+                               break;
+                       }
+
+                       localvp.type = da->type;
+                       return valuepair2str(out, outlen, &localvp,
+                                            da->type, func);
+               }
+
+               /*
+                *      Not found, die.
+                */
+               return 0;
+       }
+
+       if (!vps) return 0;     /* silently fail */
+
+       /*
+        *      Convert the VP to a string, and return it.
+        */
+       return valuepair2str(out, outlen, vp, da->type, func);
 }
 
+#ifdef HAVE_REGEX_H
 /*
- * find the appropriate registered xlat function.
+ *     Pull %{0} to %{8} out of the packet.
  */
-static struct xlat_cmp *find_xlat_func(const char *module)
+static int xlat_regex(void *instance, REQUEST *request,
+                     char *fmt, char *out, size_t outlen,
+                     RADIUS_ESCAPE_STRING func)
 {
-       struct xlat_cmp *c;
+       char *regex;
 
-       for (c = cmp; c; c = c->next){
-               if (strncmp(c->module,module,c->length) == 0 && *(module+c->length) == ':')
-                       break;
+       /*
+        *      We cheat: fmt is "0" to "8", but those numbers
+        *      are already in the "instance".
+        */
+       fmt = fmt;              /* -Wunused */
+       func = func;            /* -Wunused FIXME: do escaping? */
+       
+       regex = request_data_reference(request, request,
+                                REQUEST_DATA_REGEX | *(int *)instance);
+       if (!regex) return 0;
+
+       /*
+        *      Copy UP TO "freespace" bytes, including
+        *      a zero byte.
+        */
+       strlcpy(out, regex, outlen);
+       return strlen(out);
+}
+#endif                         /* HAVE_REGEX_H */
+
+/*
+ *     Compare two xlat_t structs, based ONLY on the module name.
+ */
+static int xlat_cmp(const void *a, const void *b)
+{
+       if (((const xlat_t *)a)->length != ((const xlat_t *)b)->length) {
+               return ((const xlat_t *)a)->length - ((const xlat_t *)b)->length;
        }
 
-       return c;
+       return memcmp(((const xlat_t *)a)->module,
+                     ((const xlat_t *)b)->module,
+                     ((const xlat_t *)a)->length);
 }
 
 
 /*
-   Convert the value on a VALUE_PAIR to string
-*/
-static int valuepair2str(char * out,int outlen,VALUE_PAIR * pair,
-                        int type, RADIUS_ESCAPE_STRING func)
+ *     find the appropriate registered xlat function.
+ */
+static const xlat_t *xlat_find(const char *module)
 {
-       char buffer[MAX_STRING_LEN * 4];
+       xlat_t my_xlat;
 
-       if (pair != NULL) {
-               vp_prints_value(buffer, sizeof(buffer), pair, 0);
-               return func(out, outlen, buffer);
+       /*
+        *      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;
        }
 
-       switch (type) {
-       case PW_TYPE_STRING :
-               strNcpy(out,"_",outlen);
-               break;
-       case PW_TYPE_INTEGER :
-               strNcpy(out,"0",outlen);
-               break;
-       case PW_TYPE_IPADDR :
-               strNcpy(out,"?.?.?.?",outlen);
-               break;
-       case PW_TYPE_DATE :
-               strNcpy(out,"0",outlen);
-               break;
-       default :
-               strNcpy(out,"unknown_type",outlen);
-       }
-       return strlen(out);
+       strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
+       my_xlat.length = strlen(my_xlat.module);
+
+       return rbtree_finddata(xlat_root, &my_xlat);
 }
 
+
 /*
- *     Decode an attribute name from a particular RADIUS_PACKET
- *     into a string.
+ *      Register an xlat function.
  */
-static int decode_attr_packet(const char *from, char **to, int freespace,
-                             RADIUS_PACKET *packet,
-                             RADIUS_ESCAPE_STRING func)
-
+int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance)
 {
-       DICT_ATTR *tmpda;
-       VALUE_PAIR *vp;
-       
-       tmpda = dict_attrbyname(from);
-       if (!tmpda) return 0;
+       xlat_t  *c;
+       xlat_t  my_xlat;
+
+       if ((module == NULL) || (strlen(module) == 0)) {
+               DEBUG("xlat_register: Invalid module name");
+               return -1;
+       }
 
        /*
-        *      See if the VP is defined.
+        *      First time around, build up the tree...
+        *
+        *      FIXME: This code should be hoisted out of this function,
+        *      and into a global "initialization".  But it isn't critical...
         */
-       vp = pairfind(packet->vps, tmpda->attr);
-       if (vp) {
-               *to += valuepair2str(*to, freespace, vp,
-                                    tmpda->type, func);
-               return 1;
+       if (!xlat_root) {
+               int i;
+#ifdef HAVE_REGEX_H
+               char buffer[2];
+#endif
+
+               xlat_root = rbtree_create(xlat_cmp, free, 0);
+               if (!xlat_root) {
+                       DEBUG("xlat_register: Failed to create tree.");
+                       return -1;
+               }
+
+               /*
+                *      Register the internal packet xlat's.
+                */
+               for (i = 0; internal_xlat[i] != NULL; i++) {
+                       xlat_register(internal_xlat[i], xlat_packet, &xlat_inst[i]);
+                       c = xlat_find(internal_xlat[i]);
+                       rad_assert(c != NULL);
+                       c->internal = TRUE;
+               }
+
+#ifdef HAVE_REGEX_H
+               /*
+                *      Register xlat's for regexes.
+                */
+               buffer[1] = '\0';
+               for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
+                       buffer[0] = '0' + i;
+                       xlat_register(buffer, xlat_regex, &xlat_inst[i]);
+                       c = xlat_find(buffer);
+                       rad_assert(c != NULL);
+                       c->internal = TRUE;
+               }
+#endif /* HAVE_REGEX_H */
        }
 
        /*
-        *      Non-protocol attributes.
+        *      If it already exists, replace the instance.
         */
-       switch (tmpda->attr) {
-               case PW_PACKET_TYPE:
-               {
-                       DICT_VALUE *dval;
-
-                       dval = dict_valbyattr(tmpda->attr, packet->code);
-                       if (dval) {
-                               snprintf(*to, freespace, "%s", dval->name);
-                       } else {
-                               snprintf(*to, freespace, "%d", packet->code);
-                       }
-                       *to += strlen(*to);
-                       return 1;
+       strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
+       my_xlat.length = strlen(my_xlat.module);
+       c = rbtree_finddata(xlat_root, &my_xlat);
+       if (c) {
+               if (c->internal) {
+                       DEBUG("xlat_register: Cannot re-define internal xlat");
+                       return -1;
                }
-               break;
 
-               default:
-                       break;
+               c->do_xlat = func;
+               c->instance = instance;
+               return 0;
        }
 
+       /*
+        *      Doesn't exist.  Create it.
+        */
+       c = rad_malloc(sizeof(xlat_t));
+       memset(c, 0, sizeof(*c));
+
+       c->do_xlat = func;
+       strlcpy(c->module, module, sizeof(c->module));
+       c->length = strlen(c->module);
+       c->instance = instance;
+
+       rbtree_insert(xlat_root, c);
+
        return 0;
 }
 
 /*
- * Decode an attribute name from a particular VALUE_PAIR*
- * into a string.
+ *      Unregister an xlat function.
+ *
+ *     We can only have one function to call per name, so the
+ *     passing of "func" here is extraneous.
  */
-static int decode_attr_vps(const char *from, char **to, int freespace,
-                             VALUE_PAIR *vps,
-                             RADIUS_ESCAPE_STRING func)
-
+void xlat_unregister(const char *module, RAD_XLAT_FUNC func)
 {
-       DICT_ATTR *tmpda;
-       VALUE_PAIR *vp;
-       
-       tmpda = dict_attrbyname(from);
-       if (!tmpda) return 0;
+       rbnode_t        *node;
+       xlat_t          my_xlat;
 
-       /*
-        *      See if the VP is defined.
-        */
-       vp = pairfind(vps, tmpda->attr);
-       if (vp) {
-               *to += valuepair2str(*to, freespace, vp,
-                                    tmpda->type, func);
-               return 1;
-       }
+       func = func;            /* -Wunused */
 
-       return 0;
+       if (!module) return;
+
+       strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
+       my_xlat.length = strlen(my_xlat.module);
+
+       node = rbtree_find(xlat_root, &my_xlat);
+       if (!node) return;
+
+       rbtree_delete(xlat_root, node);
 }
 
 /*
- *  Decode an attribute name into a string.
+ *     De-register all xlat functions,
+ *     used mainly for debugging.
+ */
+void xlat_free(void)
+{
+       rbtree_free(xlat_root);
+}
+
+
+/*
+ *     Decode an attribute name into a string.
  */
 static void decode_attribute(const char **from, char **to, int freespace,
                             int *open, REQUEST *request,
                             RADIUS_ESCAPE_STRING func)
 {
-       char attrname[256];
+       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 stop=0, found=0;
+       int found=0, retlen=0;
        int openbraces = *open;
-       struct xlat_cmp *c;
+       const xlat_t *c;
 
        p = *from;
        q = *to;
-       pa = &attrname[0];
+       pa = &xlat_name[0];
 
        *q = '\0';
 
-       /* 
-        * Skip the '{' at the front of 'p' 
-        * Increment open braces 
-        */ 
+       /*
+        * Skip the '{' at the front of 'p'
+        * Increment open braces
+        */
        p++;
        openbraces++;
 
+       if (*p == '#') {
+               p++;
+               do_length = 1;
+       }
+
        /*
-        *  Copy over the rest of the string.
+        *      First, copy the xlat key name to one buffer
         */
-       while ((*p) && (!stop)) {
-               switch(*p) {
+       while (*p && (*p != '}') && (*p != ':')) {
+               *pa++ = *p++;
+
+               if (pa >= (xlat_name + sizeof(xlat_name) - 1)) {
                        /*
-                        *  Allow braces inside things, too.
+                        *      Skip to the end of the input
                         */
+                       p += strlen(p);
+                       DEBUG("xlat: Module name is too long in string %%%s",
+                             *from);
+                       goto done;
+               }
+       }
+       *pa = '\0';
+
+       if (!*p) {
+               DEBUG("xlat: Invalid syntax in %s", *from);
+
+               /*
+                *      %{name} is a simple attribute reference,
+                *      or regex reference.
+                */
+       } 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 there's a brace immediately following the colon,
+                *  then we've chosen to delimite the per-module string,
+                *  so keep track of that.
+                */
+               if (*p == '{') {
+                       delimitbrace = openbraces;
+                       openbraces++;
+                       p++;
+               }
+               
+               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.
+                */
+               while (*p && !stop) {
+                       switch(*p) {
+                               /*
+                                *      What the heck is this supposed
+                                *      to be doing?
+                                */
                        case '\\':
                                p++; /* skip it */
                                *pa++ = *p++;
                                break;
 
+                               /*
+                                *      This is pretty hokey...  we
+                                *      should use the functions in
+                                *      util.c
+                                */
                        case '{':
                                openbraces++;
                                *pa++ = *p++;
                                break;
-                               
+
                        case '}':
                                openbraces--;
-                               if (openbraces == *open) {
+                               if (openbraces == delimitbrace) {
                                        p++;
                                        stop=1;
                                } else {
                                        *pa++ = *p++;
                                }
                                break;
-
-                               /*
-                                *  Attr-Name1:-Attr-Name2
-                                *
-                                *  Use Attr-Name1, and if not found,
-                                *  use Attr-Name2.
-                                */
-                       case ':':
-                               if (p[1] == '-') {
-                                       p += 2;
-                                       stop = 1;
-                                       break;
-                               }
-                               /* else FALL-THROUGH */
-
+                               
                        default:
                                *pa++ = *p++;
                                break;
+                       }
                }
-       }
-       *pa = '\0';
-
-       /*
-        *      Find an attribute from the reply.
-        */
-       if (strncasecmp(attrname,"reply:",6) == 0) {
-               found = decode_attr_packet(&attrname[6], &q, freespace,
-                                          request->reply, func);
-
-               /*
-                *      Find an attribute from the request.
-                */
-       } else if (strncasecmp(attrname,"request:",8) == 0) {
-               found = decode_attr_packet(&attrname[8], &q, freespace,
-                                          request->packet, func);
-
-               /*
-                *      Find an attribute from the config items.
-                */
-       } else if (strncasecmp(attrname,"check:",6) == 0) {
-               found = decode_attr_vps(&attrname[6], &q, freespace,
-                                          request->config_items, func);
-
-               /*
-                *      Find an attribute from the proxy request.
-                */
-       } else if ((strncasecmp(attrname,"proxy-request:",14) == 0) &&
-                  (request->proxy_reply != NULL)) {
-               found = decode_attr_packet(&attrname[14], &q, freespace,
-                                          request->proxy, func);
-
-               /*
-                *      Find an attribute from the proxy reply.
-                */
-       } else if ((strncasecmp(attrname,"proxy-reply:",12) == 0) &&
-                  (request->proxy_reply != NULL)) {
-               found = decode_attr_packet(&attrname[12], &q, freespace,
-                                          request->proxy_reply, func);
 
-               /*
-                *      Find a string from a registered function.
-                */
-       } else if ((c = find_xlat_func(attrname)) != NULL) {
-               DEBUG("radius_xlat: Running registered xlat function of module %s for string \'%s\'",
-                     c->module, attrname+ c->length + 1);
-               q += c->do_xlat(c->instance, request, attrname+(c->length+1), q, freespace, func);
-               found = 1;
+               *pa = '\0';
 
                /*
-                *      Nothing else, it MUST be a bare attribute name.
+                *      Now check to see if we're at the end of the string
+                *      we were sent.  If we're not, check for :-
                 */
-       } else if (decode_attr_packet(attrname, &q, freespace, request->packet, func)) {
-               found = 1;
-
-       } else if (dict_attrbyname(attrname) == NULL) {
+               if (openbraces == delimitbrace) {
+                       if (p[0] == ':' && p[1] == '-') {
+                               p += 2;
+                       }
+               }
+               
                /*
-                *      No attribute by that name, return an error.
+                *      Look up almost everything in the new tree of xlat
+                *      functions.  This makes it a little quicker...
                 */
-               DEBUG2("WARNING: Attempt to use unknown xlat function, or non-existent attribute in string %%{%s}", attrname);
-
-       } /* else the attribute is known, but not in the request */
+       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
+               } else {
+                       /*
+                        *      No attribute by that name, return an error.
+                        */
+                       DEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", xlat_name, *from);
+#endif
+               }
+       }
 
        /*
         * Skip to last '}' if attr is found
@@ -364,19 +719,29 @@ static void decode_attribute(const char **from, char **to, int freespace,
         * useless if we found what we need
         */
        if (found) {
-               while((*p != '\0') && (openbraces > 0)) {
+               if (do_length) {
+                       snprintf(q, freespace, "%d", retlen);
+                       retlen = strlen(q);
+               }
+
+               q += retlen;
+
+               while((*p != '\0') && (openbraces > *open)) {
+                       /*
+                        *      Handle escapes outside of the loop.
+                        */
+                       if (*p == '\\') {
+                               p++;
+                               if (!*p) break;
+                               p++; /* get & ignore next character */
+                               continue;
+                       }
+
                        switch (*p) {
                        default:
                                break;
 
                                /*
-                                *  Ensure that escaped braces are allowed.
-                                */
-                       case '\\':
-                               p++; /* skip the escaped character */
-                               break;
-
-                               /*
                                 *  Bare brace
                                 */
                        case '{':
@@ -390,6 +755,9 @@ static void decode_attribute(const char **from, char **to, int freespace,
                        p++;    /* skip the character */
                }
        }
+       
+       done:
+       if (free_xlat_string) free(xlat_string);
 
        *open = openbraces;
        *from = p;
@@ -403,30 +771,23 @@ static void decode_attribute(const char **from, char **to, int freespace,
  */
 static int xlat_copy(char *out, int outlen, const char *in)
 {
-       int len = 0;
+       int freespace = outlen;
 
-       while (*in) {
-               /*
-                *  Truncate, if too much.
-                */
-               if (len >= outlen) {
-                       break;
-               }
+       rad_assert(outlen > 0);
 
+       while ((*in) && (freespace > 1)) {
                /*
                 *  Copy data.
                 *
                 *  FIXME: Do escaping of bad stuff!
                 */
-               *out = *in;
+               *(out++) = *(in++);
 
-               out++;
-               in++;
-               len++;
+               freespace--;
        }
-
        *out = '\0';
-       return len;
+
+       return (outlen - freespace); /* count does not include NUL */
 }
 
 /*
@@ -437,22 +798,28 @@ static int xlat_copy(char *out, int outlen, const char *in)
 int radius_xlat(char *out, int outlen, const char *fmt,
                REQUEST *request, RADIUS_ESCAPE_STRING func)
 {
-       int i, c,freespace;
+       int c, len, freespace;
        const char *p;
        char *q;
+       char *nl;
        VALUE_PAIR *tmp;
        struct tm *TM, s_TM;
        char tmpdt[40]; /* For temporary storing of dates */
        int openbraces=0;
 
        /*
+        *      Catch bad modules.
+        */
+       if (!fmt || !out || !request) return 0;
+
+       /*
         *  Ensure that we always have an escaping function.
         */
        if (func == NULL) {
                func = xlat_copy;
        }
 
-       q = out;
+               q = out;
        p = fmt;
        while (*p) {
                /* Calculate freespace in output */
@@ -465,7 +832,7 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                        /*
                         * We check if we're inside an open brace.  If we are
                         * then we assume this brace is NOT literal, but is
-                        * a closing brace and apply it 
+                        * a closing brace and apply it
                         */
                        if ((c == '}') && openbraces) {
                                openbraces--;
@@ -503,6 +870,13 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                                break;
                        }
                        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);
@@ -530,9 +904,11 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                                break;
                        case 'd': /* request day */
                                TM = localtime_r(&request->timestamp, &s_TM);
-                               strftime(tmpdt,sizeof(tmpdt),"%d",TM);
-                               strNcpy(q,tmpdt,freespace);
-                               q += strlen(q);
+                               len = strftime(tmpdt, sizeof(tmpdt), "%d", TM);
+                               if (len > 0) {
+                                       strlcpy(q, tmpdt, freespace);
+                                       q += strlen(q);
+                               }
                                p++;
                                break;
                        case 'f': /* Framed IP address */
@@ -546,15 +922,17 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                        case 'l': /* request timestamp */
                                snprintf(tmpdt, sizeof(tmpdt), "%lu",
                                         (unsigned long) request->timestamp);
-                               strNcpy(q,tmpdt,freespace);
+                               strlcpy(q,tmpdt,freespace);
                                q += strlen(q);
                                p++;
                                break;
                        case 'm': /* request month */
                                TM = localtime_r(&request->timestamp, &s_TM);
-                               strftime(tmpdt,sizeof(tmpdt),"%m",TM);
-                               strNcpy(q,tmpdt,freespace);
-                               q += strlen(q);
+                               len = strftime(tmpdt, sizeof(tmpdt), "%m", TM);
+                               if (len > 0) {
+                                       strlcpy(q, tmpdt, freespace);
+                                       q += strlen(q);
+                               }
                                p++;
                                break;
                        case 'n': /* NAS IP address */
@@ -570,7 +948,10 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                                p++;
                                break;
                        case 't': /* request timestamp */
-                               CTIME_R(&request->timestamp, q, freespace);
+                               CTIME_R(&request->timestamp, tmpdt, sizeof(tmpdt));
+                               nl = strchr(tmpdt, '\n');
+                               if (nl) *nl = '\0';
+                               strlcpy(q, tmpdt, freespace);
                                q += strlen(q);
                                p++;
                                break;
@@ -579,31 +960,35 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                                p++;
                                break;
                        case 'A': /* radacct_dir */
-                               strNcpy(q,radacct_dir,freespace-1);
+                               strlcpy(q,radacct_dir,freespace);
                                q += strlen(q);
                                p++;
                                break;
                        case 'C': /* ClientName */
-                               strNcpy(q,client_name(request->packet->src_ipaddr),freespace-1);
+                               strlcpy(q,client_name_old(&request->packet->src_ipaddr),freespace);
                                q += strlen(q);
                                p++;
                                break;
                        case 'D': /* request date */
                                TM = localtime_r(&request->timestamp, &s_TM);
-                               strftime(tmpdt,sizeof(tmpdt),"%Y%m%d",TM);
-                               strNcpy(q,tmpdt,freespace);
-                               q += strlen(q);
+                               len = strftime(tmpdt, sizeof(tmpdt), "%Y%m%d", TM);
+                               if (len > 0) {
+                                       strlcpy(q, tmpdt, freespace);
+                                       q += strlen(q);
+                               }
                                p++;
                                break;
                        case 'H': /* request hour */
                                TM = localtime_r(&request->timestamp, &s_TM);
-                               strftime(tmpdt,sizeof(tmpdt),"%H",TM);
-                               strNcpy(q,tmpdt,freespace);
-                               q += strlen(q);
+                               len = strftime(tmpdt, sizeof(tmpdt), "%H", TM);
+                               if (len > 0) {
+                                       strlcpy(q, tmpdt, freespace);
+                                       q += strlen(q);
+                               }
                                p++;
                                break;
                        case 'L': /* radlog_dir */
-                               strNcpy(q,radlog_dir,freespace-1);
+                               strlcpy(q,radlog_dir,freespace);
                                q += strlen(q);
                                p++;
                                break;
@@ -612,22 +997,26 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                                p++;
                                break;
                        case 'R': /* radius_dir */
-                               strNcpy(q,radius_dir,freespace-1);
+                               strlcpy(q,radius_dir,freespace);
                                q += strlen(q);
                                p++;
                                break;
                        case 'S': /* request timestamp in SQL format*/
                                TM = localtime_r(&request->timestamp, &s_TM);
-                               strftime(tmpdt,sizeof(tmpdt),"%Y-%m-%d %H:%M:%S",TM);
-                               strNcpy(q,tmpdt,freespace);
-                               q += strlen(q);
+                               len = strftime(tmpdt, sizeof(tmpdt), "%Y-%m-%d %H:%M:%S", TM);
+                               if (len > 0) {
+                                       strlcpy(q, tmpdt, freespace);
+                                       q += strlen(q);
+                               }
                                p++;
                                break;
                        case 'T': /* request timestamp */
                                TM = localtime_r(&request->timestamp, &s_TM);
-                               strftime(tmpdt,sizeof(tmpdt),"%Y-%m-%d-%H.%M.%S.000000",TM);
-                               strNcpy(q,tmpdt,freespace);
-                               q += strlen(q);
+                               len = strftime(tmpdt, sizeof(tmpdt), "%Y-%m-%d-%H.%M.%S.000000", TM);
+                               if (len > 0) {
+                                       strlcpy(q, tmpdt, freespace);
+                                       q += strlen(q);
+                               }
                                p++;
                                break;
                        case 'U': /* Stripped User name */
@@ -636,27 +1025,29 @@ int radius_xlat(char *out, int outlen, const char *fmt,
                                break;
                        case 'V': /* Request-Authenticator */
                                if (request->packet->verified)
-                                       strNcpy(q,"Verified",freespace-1);
+                                       strlcpy(q,"Verified",freespace);
                                else
-                                       strNcpy(q,"None",freespace-1);
+                                       strlcpy(q,"None",freespace);
                                q += strlen(q);
                                p++;
                                break;
                        case 'Y': /* request year */
                                TM = localtime_r(&request->timestamp, &s_TM);
-                               strftime(tmpdt,sizeof(tmpdt),"%Y",TM);
-                               strNcpy(q,tmpdt,freespace);
-                               q += strlen(q);
+                               len = strftime(tmpdt, sizeof(tmpdt), "%Y", TM);
+                               if (len > 0) {
+                                       strlcpy(q, tmpdt, freespace);
+                                       q += strlen(q);
+                               }
                                p++;
                                break;
                        case 'Z': /* Full request pairs except password */
                                tmp = request->packet->vps;
                                while (tmp && (freespace > 3)) {
-                                       if (tmp->attribute != PW_PASSWORD) {
+                                       if (tmp->attribute != PW_USER_PASSWORD) {
                                                *q++ = '\t';
-                                               i = vp_prints(q,freespace-2,tmp);
-                                               q += i;
-                                               freespace -= (i+2);
+                                               len = vp_prints(q, freespace - 2, tmp);
+                                               q += len;
+                                               freespace -= (len + 2);
                                                *q++ = '\n';
                                        }
                                        tmp = tmp->next;