2 * xlat.c Translate strings. This is the first version of xlat
3 * incorporated to RADIUS
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.
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.
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
21 * Copyright 2000,2006 The FreeRADIUS server project
22 * Copyright 2000 Alan DeKok <aland@ox.org>
25 #include <freeradius-devel/ident.h>
28 #include <freeradius-devel/radiusd.h>
29 #include <freeradius-devel/md5.h>
30 #include <freeradius-devel/rad_assert.h>
34 typedef struct xlat_t {
35 char module[MAX_STRING_LEN];
38 RAD_XLAT_FUNC do_xlat;
39 int internal; /* not allowed to re-define these */
42 static rbtree_t *xlat_root = NULL;
45 * Define all xlat's in the structure.
47 static const char * const internal_xlat[] = {"check",
57 #if REQUEST_MAX_REGEX > 8
58 #error Please fix the following line
60 static const int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; /* up to 8 for regex */
64 * Convert the value on a VALUE_PAIR to string
66 static int valuepair2str(char * out,int outlen,VALUE_PAIR * pair,
67 int type, RADIUS_ESCAPE_STRING func)
69 char buffer[MAX_STRING_LEN * 4];
72 vp_prints_value(buffer, sizeof(buffer), pair, -1);
73 return func(out, outlen, buffer);
78 strlcpy(out,"_",outlen);
80 case PW_TYPE_INTEGER :
81 strlcpy(out,"0",outlen);
84 strlcpy(out,"?.?.?.?",outlen);
86 case PW_TYPE_IPV6ADDR :
87 strlcpy(out,":?:",outlen);
90 strlcpy(out,"0",outlen);
93 strlcpy(out,"unknown_type",outlen);
98 static VALUE_PAIR *pairfind_tag(VALUE_PAIR *vps, int attr, int tag)
100 VALUE_PAIR *vp = vps;
103 vp = pairfind(vp, attr);
106 if (!vp) return NULL;
108 if (!vp->flags.has_tag) return NULL;
110 if (vp->flags.tag == tag) return vp;
117 * Dynamically translate for check:, request:, reply:, etc.
119 static size_t xlat_packet(void *instance, REQUEST *request,
120 char *fmt, char *out, size_t outlen,
121 RADIUS_ESCAPE_STRING func)
125 VALUE_PAIR *vps = NULL;
126 RADIUS_PACKET *packet = NULL;
128 switch (*(int*) instance) {
130 vps = request->config_items;
134 vps = request->packet->vps;
135 packet = request->packet;
139 vps = request->reply->vps;
140 packet = request->reply;
145 if (request->proxy) vps = request->proxy->vps;
146 packet = request->proxy;
152 if (request->proxy_reply) vps = request->proxy_reply->vps;
153 packet = request->proxy_reply;
158 if (request->parent) {
159 vps = request->parent->packet->vps;
160 packet = request->parent->packet;
165 if (request->parent && request->parent->reply) {
166 vps = request->parent->reply->vps;
167 packet = request->parent->reply;
172 if (request->parent) {
173 vps = request->parent->config_items;
182 * The "format" string is the attribute name.
184 da = dict_attrbyname(fmt);
186 int do_number = FALSE;
187 int do_array = FALSE;
188 int do_count = FALSE;
195 if (strlen(fmt) > sizeof(buffer)) return 0;
197 strlcpy(buffer, fmt, sizeof(buffer));
200 * %{Attribute-name#} - print integer version of it.
202 p = buffer + strlen(buffer) - 1;
209 * %{Attribute-Name:tag} - get the name with the specified
212 p = strchr(buffer, ':');
213 if (p && (p[1] != '-')) {
220 * Allow %{Attribute-Name:tag[...]}
226 * %{Attribute-Name[...] does more stuff
234 } else if (p[1] == '*') {
238 p += 1 + strspn(p + 1, "0123456789");
240 RDEBUG2("xlat: Invalid array reference in string at %s %s",
248 * We COULD argue about %{Attribute-Name[#]#} etc.
249 * But that looks like more work than it's worth.
252 da = dict_attrbyname(buffer);
256 * No array, print the tagged attribute.
259 vp = pairfind_tag(vps, da->attr, tag);
266 * Array[#] - return the total
269 for (vp = pairfind_tag(vps, da->attr, tag);
271 vp = pairfind_tag(vp->next, da->attr, tag)) {
275 snprintf(out, outlen, "%d", (int) total);
280 * %{Attribute-Name[*]} returns ALL of the
281 * the attributes, separated by a newline.
284 for (vp = pairfind_tag(vps, da->attr, tag);
286 vp = pairfind_tag(vp->next, da->attr, tag)) {
287 count = valuepair2str(out, outlen - 1, vp, da->type, func);
288 rad_assert(count <= outlen);
290 outlen -= (count + 1);
295 if (outlen <= 1) break;
303 * Find the N'th value.
305 for (vp = pairfind_tag(vps, da->attr, tag);
307 vp = pairfind_tag(vp->next, da->attr, tag)) {
308 if (total == count) break;
317 * Non-existent array reference.
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)) {
332 return snprintf(out, outlen, "%u", vp->vp_integer);
335 return valuepair2str(out, outlen, vp, da->type, func);
338 vp = pairfind(vps, da->attr);
341 * Some "magic" handlers, which are never in VP's, but
342 * which are in the packet.
344 * FIXME: We should really do this in a more
350 memset(&localvp, 0, sizeof(localvp));
357 dval = dict_valbyattr(da->attr, packet->code);
359 snprintf(out, outlen, "%s", dval->name);
361 snprintf(out, outlen, "%d", packet->code);
367 case PW_CLIENT_SHORTNAME:
368 if (request->client && request->client->shortname) {
369 strlcpy(out, request->client->shortname, outlen);
371 strlcpy(out, "<UNKNOWN-CLIENT>", outlen);
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) {
380 localvp.attribute = da->attr;
381 localvp.vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
384 case PW_PACKET_DST_IP_ADDRESS:
385 if (packet->dst_ipaddr.af != AF_INET) {
388 localvp.attribute = da->attr;
389 localvp.vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
392 case PW_PACKET_SRC_PORT:
393 localvp.attribute = da->attr;
394 localvp.vp_integer = packet->src_port;
397 case PW_PACKET_DST_PORT:
398 localvp.attribute = da->attr;
399 localvp.vp_integer = packet->dst_port;
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);
410 * Authorization, accounting, etc.
412 case PW_REQUEST_PROCESSING_STAGE:
413 if (request->component) {
414 strlcpy(out, request->component, outlen);
416 strlcpy(out, "server_core", outlen);
420 case PW_PACKET_SRC_IPV6_ADDRESS:
421 if (packet->src_ipaddr.af != AF_INET6) {
424 localvp.attribute = da->attr;
425 memcpy(localvp.vp_strvalue,
426 &packet->src_ipaddr.ipaddr.ip6addr,
427 sizeof(packet->src_ipaddr.ipaddr.ip6addr));
430 case PW_PACKET_DST_IPV6_ADDRESS:
431 if (packet->dst_ipaddr.af != AF_INET6) {
434 localvp.attribute = da->attr;
435 memcpy(localvp.vp_strvalue,
436 &packet->dst_ipaddr.ipaddr.ip6addr,
437 sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
440 case PW_VIRTUAL_SERVER:
441 if (!request->server) return 0;
443 snprintf(out, outlen, "%s", request->server);
447 case PW_MODULE_RETURN_CODE:
448 localvp.attribute = da->attr;
451 * See modcall.c for a bit of a hack.
453 localvp.vp_integer = request->simul_max;
457 return 0; /* not found */
461 localvp.type = da->type;
462 return valuepair2str(out, outlen, &localvp,
472 if (!vps) return 0; /* silently fail */
475 * Convert the VP to a string, and return it.
477 return valuepair2str(out, outlen, vp, da->type, func);
481 * Print data as integer, not as VALUE.
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)
489 while (isspace((int) *fmt)) fmt++;
491 if (!radius_get_vp(request, fmt, &vp) || !vp) {
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)) {
505 return snprintf(out, outlen, "%u", vp->vp_integer);
509 * Print data as string, if possible.
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)
518 while (isspace((int) *fmt)) fmt++;
526 if (!radius_get_vp(request, fmt, &vp)) goto nothing;
528 if (!vp) goto nothing;
530 if (vp->type != PW_TYPE_OCTETS) goto nothing;
532 len = fr_print_string(vp->vp_strvalue, vp->length, out, outlen);
540 * Pull %{0} to %{8} out of the packet.
542 static size_t xlat_regex(void *instance, REQUEST *request,
543 char *fmt, char *out, size_t outlen,
544 RADIUS_ESCAPE_STRING func)
549 * We cheat: fmt is "0" to "8", but those numbers
550 * are already in the "instance".
552 fmt = fmt; /* -Wunused */
553 func = func; /* -Wunused FIXME: do escaping? */
555 regex = request_data_reference(request, request,
556 REQUEST_DATA_REGEX | *(int *)instance);
557 if (!regex) return 0;
560 * Copy UP TO "freespace" bytes, including
563 strlcpy(out, regex, outlen);
566 #endif /* HAVE_REGEX_H */
570 * Change the debugging level.
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)
578 if (*fmt) level = atoi(fmt);
581 request->options = RAD_REQUEST_OPTION_NONE;
582 request->radlog = NULL;
584 if (level > 4) level = 4;
586 request->options = level;
587 request->radlog = radlog_request;
590 snprintf(out, outlen, "%d", level);
596 * Calculate the MD5 hash of a string.
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)
607 if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
613 fr_MD5Update(&ctx, (void *) buffer, strlen(buffer));
614 fr_MD5Final(digest, &ctx);
617 snprintf(out, outlen, "md5_overflow");
621 for (i = 0; i < 16; i++) {
622 snprintf(out + i * 2, 3, "%02x", digest[i]);
630 * Convert a string to lowercase
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)
639 if (outlen <= 1) return 0;
641 if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
646 for (p = buffer, q = out; *p != '\0'; p++, outlen--) {
647 if (outlen <= 1) break;
649 *(q++) = tolower((int) *p);
659 * Convert a string to uppercase
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)
668 if (outlen <= 1) return 0;
670 if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) {
675 for (p = buffer, q = out; *p != '\0'; p++, outlen--) {
676 if (outlen <= 1) break;
678 *(q++) = toupper((int) *p);
688 * Compare two xlat_t structs, based ONLY on the module name.
690 static int xlat_cmp(const void *a, const void *b)
692 if (((const xlat_t *)a)->length != ((const xlat_t *)b)->length) {
693 return ((const xlat_t *)a)->length - ((const xlat_t *)b)->length;
696 return memcmp(((const xlat_t *)a)->module,
697 ((const xlat_t *)b)->module,
698 ((const xlat_t *)a)->length);
703 * find the appropriate registered xlat function.
705 static xlat_t *xlat_find(const char *module)
709 strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
710 my_xlat.length = strlen(my_xlat.module);
712 return rbtree_finddata(xlat_root, &my_xlat);
717 * Register an xlat function.
719 int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance)
724 if (!module || !*module) {
725 DEBUG("xlat_register: Invalid module name");
730 * First time around, build up the tree...
732 * FIXME: This code should be hoisted out of this function,
733 * and into a global "initialization". But it isn't critical...
741 xlat_root = rbtree_create(xlat_cmp, free, 0);
743 DEBUG("xlat_register: Failed to create tree.");
748 * Register the internal packet xlat's.
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);
758 * New name: "control"
760 xlat_register("control", xlat_packet, &xlat_inst[0]);
761 c = xlat_find("control");
762 rad_assert(c != NULL);
765 xlat_register("integer", xlat_integer, "");
766 c = xlat_find("integer");
767 rad_assert(c != NULL);
770 xlat_register("string", xlat_string, "");
771 c = xlat_find("string");
772 rad_assert(c != NULL);
777 * Register xlat's for regexes.
780 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
782 xlat_register(buffer, xlat_regex, &xlat_inst[i]);
783 c = xlat_find(buffer);
784 rad_assert(c != NULL);
787 #endif /* HAVE_REGEX_H */
790 xlat_register("debug", xlat_debug, &xlat_inst[0]);
791 c = xlat_find("debug");
792 rad_assert(c != NULL);
795 xlat_register("md5", xlat_md5, &xlat_inst[0]);
796 c = xlat_find("md5");
797 rad_assert(c != NULL);
800 xlat_register("tolower", xlat_lc, &xlat_inst[0]);
801 c = xlat_find("tolower");
802 rad_assert(c != NULL);
805 xlat_register("toupper", xlat_uc, &xlat_inst[0]);
806 c = xlat_find("toupper");
807 rad_assert(c != NULL);
812 * If it already exists, replace the instance.
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);
819 DEBUG("xlat_register: Cannot re-define internal xlat");
824 c->instance = instance;
829 * Doesn't exist. Create it.
831 c = rad_malloc(sizeof(*c));
832 memset(c, 0, sizeof(*c));
835 strlcpy(c->module, module, sizeof(c->module));
836 c->length = strlen(c->module);
837 c->instance = instance;
839 rbtree_insert(xlat_root, c);
845 * Unregister an xlat function.
847 * We can only have one function to call per name, so the
848 * passing of "func" here is extraneous.
850 void xlat_unregister(const char *module, RAD_XLAT_FUNC func, void *instance)
855 func = func; /* -Wunused */
859 strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
860 my_xlat.length = strlen(my_xlat.module);
862 c = rbtree_finddata(xlat_root, &my_xlat);
865 if (c->instance != instance) return;
867 rbtree_deletebydata(xlat_root, c);
871 * De-register all xlat functions,
872 * used mainly for debugging.
876 rbtree_free(xlat_root);
881 * Decode an attribute name into a string.
883 static int decode_attribute(const char **from, char **to, int freespace,
885 RADIUS_ESCAPE_STRING func)
888 char *module_name, *xlat_str;
889 char *p, *q, *l, *next = NULL;
900 * Copy the input string to an intermediate buffer where
903 varlen = rad_copy_variable(buffer, *from);
905 RDEBUG2("ERROR: Badly formatted variable: %s", *from);
911 * Kill the %{} around the data we are looking for.
914 p[varlen - 1] = '\0'; /* */
922 * Handle %{%{foo}:-%{bar}}, which is useful, too.
924 * Did I mention that this parser is garbage?
926 if ((p[0] == '%') && (p[1] == '{')) {
931 * 'p' is after the start of 'buffer', so we can
934 len1 = rad_copy_variable(buffer, p);
936 RDEBUG2("ERROR: Badly formatted variable: %s", p);
941 * They did %{%{foo}}, which is stupid, but allowed.
944 RDEBUG2("Improperly nested variable; %%{%s}", p);
949 * It SHOULD be %{%{foo}:-%{bar}}. If not, it's
952 if ((p[len1] != ':') || (p[len1 + 1] != '-')) {
953 RDEBUG2("No trailing :- after variable at %s", p);
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
963 l = buffer + len1 + 1;
965 if ((p[0] == '%') && (p[1] == '{')) {
966 len2 = rad_copy_variable(l, p);
969 RDEBUG2("ERROR: Invalid text after :- at %s", p);
975 } else if ((p[0] == '"') || p[0] == '\'') {
976 getstring(&p, l, strlen(l));
983 * Expand the first one. If we did, exit the
986 retlen = radius_xlat(q, freespace, buffer, request, func);
992 RDEBUG2("\t... expanding second conditional");
994 * Expand / copy the second string if required.
997 retlen = radius_xlat(q, freespace, l,
1003 strlcpy(q, l, freespace);
1008 * Else the output is an empty string.
1014 * See if we're supposed to expand a module name.
1017 for (l = p; *l != '\0'; l++) {
1025 RDEBUG2("WARNING: Deprecated conditional expansion \":-\". See \"man unlang\" for details");
1026 module_name = internal_xlat[1];
1033 if (isdigit(l[1])) break;
1035 module_name = p; /* start of name */
1042 * Module names can't have spaces.
1044 if ((*l == ' ') || (*l == '\t')) break;
1048 * %{name} is a simple attribute reference,
1049 * or regex reference.
1053 module_name = xlat_str = p;
1055 module_name = internal_xlat[1];
1062 * FIXME: For backwards "WTF" compatibility, check for
1063 * {...}, (after the :), and copy that, too.
1066 /* module name, followed by (possibly) per-module string */
1070 c = xlat_find(module_name);
1072 if (module_name == internal_xlat[1]) {
1073 RDEBUG2("WARNING: Unknown Attribute \"%s\" in string expansion \"%%%s\"", module_name, *from);
1075 RDEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", module_name, *from);
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);
1086 snprintf(q, freespace, "%d", retlen);
1092 * Expand the second bit.
1094 RDEBUG2("\t... expanding second conditional");
1095 retlen = radius_xlat(q, freespace, next, request, func);
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.
1109 static size_t xlat_copy(char *out, size_t outlen, const char *in)
1111 int freespace = outlen;
1113 if (outlen < 1) return 0;
1115 while ((*in) && (freespace > 1)) {
1119 * FIXME: Do escaping of bad stuff!
1127 return (outlen - freespace); /* count does not include NUL */
1131 * Replace %<whatever> in a string.
1133 * See 'doc/variables.txt' for more information.
1135 int radius_xlat(char *out, int outlen, const char *fmt,
1136 REQUEST *request, RADIUS_ESCAPE_STRING func)
1138 int c, len, freespace;
1143 struct tm *TM, s_TM;
1144 char tmpdt[40]; /* For temporary storing of dates */
1148 * Catch bad modules.
1150 if (!fmt || !out || !request) return 0;
1153 * Ensure that we always have an escaping function.
1162 /* Calculate freespace in output */
1163 freespace = outlen - (q - out);
1168 if ((c != '%') && (c != '$') && (c != '\\')) {
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
1174 if ((c == '}') && openbraces) {
1184 * There's nothing after this character, copy
1185 * the last '%' or "$' or '\\' over to the output
1211 } else if (c == '%') switch(*p) {
1214 if (decode_attribute(&p, &q, freespace, request, func) < 0) return 0;
1220 case 'a': /* Protocol: */
1221 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_PROTOCOL),PW_TYPE_INTEGER, func);
1224 case 'c': /* Callback-Number */
1225 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_CALLBACK_NUMBER),PW_TYPE_STRING, func);
1228 case 'd': /* request day */
1229 TM = localtime_r(&request->timestamp, &s_TM);
1230 len = strftime(tmpdt, sizeof(tmpdt), "%d", TM);
1232 strlcpy(q, tmpdt, freespace);
1237 case 'f': /* Framed IP address */
1238 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_IP_ADDRESS),PW_TYPE_IPADDR, func);
1241 case 'i': /* Calling station ID */
1242 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CALLING_STATION_ID),PW_TYPE_STRING, func);
1245 case 'l': /* request timestamp */
1246 snprintf(tmpdt, sizeof(tmpdt), "%lu",
1247 (unsigned long) request->timestamp);
1248 strlcpy(q,tmpdt,freespace);
1252 case 'm': /* request month */
1253 TM = localtime_r(&request->timestamp, &s_TM);
1254 len = strftime(tmpdt, sizeof(tmpdt), "%m", TM);
1256 strlcpy(q, tmpdt, freespace);
1261 case 'n': /* NAS IP address */
1262 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_IP_ADDRESS),PW_TYPE_IPADDR, func);
1265 case 'p': /* Port number */
1266 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_PORT),PW_TYPE_INTEGER, func);
1269 case 's': /* Speed */
1270 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CONNECT_INFO),PW_TYPE_STRING, func);
1273 case 't': /* request timestamp */
1274 CTIME_R(&request->timestamp, tmpdt, sizeof(tmpdt));
1275 nl = strchr(tmpdt, '\n');
1277 strlcpy(q, tmpdt, freespace);
1281 case 'u': /* User name */
1282 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_USER_NAME),PW_TYPE_STRING, func);
1285 case 'A': /* radacct_dir */
1286 strlcpy(q,radacct_dir,freespace);
1290 case 'C': /* ClientName */
1291 strlcpy(q,request->client->shortname,freespace);
1295 case 'D': /* request date */
1296 TM = localtime_r(&request->timestamp, &s_TM);
1297 len = strftime(tmpdt, sizeof(tmpdt), "%Y%m%d", TM);
1299 strlcpy(q, tmpdt, freespace);
1304 case 'H': /* request hour */
1305 TM = localtime_r(&request->timestamp, &s_TM);
1306 len = strftime(tmpdt, sizeof(tmpdt), "%H", TM);
1308 strlcpy(q, tmpdt, freespace);
1313 case 'I': /* Request ID */
1314 snprintf(tmpdt, sizeof(tmpdt), "%i", request->packet->id);
1315 strlcpy(q, tmpdt, freespace);
1319 case 'L': /* radlog_dir */
1320 strlcpy(q,radlog_dir,freespace);
1324 case 'G': /* request minute */
1325 TM = localtime_r(&request->timestamp, &s_TM);
1326 len = strftime(tmpdt, sizeof(tmpdt), "%M", TM);
1328 strlcpy(q, tmpdt, freespace);
1334 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_MTU),PW_TYPE_INTEGER, func);
1337 case 'R': /* radius_dir */
1338 strlcpy(q,radius_dir,freespace);
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);
1346 strlcpy(q, tmpdt, freespace);
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);
1355 strlcpy(q, tmpdt, freespace);
1360 case 'U': /* Stripped User name */
1361 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_STRIPPED_USER_NAME),PW_TYPE_STRING, func);
1364 case 'V': /* Request-Authenticator */
1365 strlcpy(q,"Verified",freespace);
1369 case 'Y': /* request year */
1370 TM = localtime_r(&request->timestamp, &s_TM);
1371 len = strftime(tmpdt, sizeof(tmpdt), "%Y", TM);
1373 strlcpy(q, tmpdt, freespace);
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) {
1383 len = vp_prints(q, freespace - 2, tmp);
1385 freespace -= (len + 2);
1393 RDEBUG2("WARNING: Unknown variable '%%%c': See 'doc/variables.txt'", *p);
1394 if (freespace > 2) {
1403 RDEBUG2("\texpand: %s -> %s", fmt, out);