3 * @brief String expansion ("translation"). Implements %Attribute -> value
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/rad_assert.h>
30 #include <freeradius-devel/base64.h>
31 #include <freeradius-devel/dhcp.h>
35 typedef struct xlat_t {
36 char module[MAX_STRING_LEN];
39 RAD_XLAT_FUNC do_xlat;
40 int internal; /* not allowed to re-define these */
43 static rbtree_t *xlat_root = NULL;
46 * Define all xlat's in the structure.
48 static const char * const internal_xlat[] = {"check",
58 static const char * const xlat_foreach_names[] = {"Foreach-Variable-0",
71 #if REQUEST_MAX_REGEX > 8
72 #error Please fix the following line
74 static int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; /* up to 8 for regex */
77 * @brief Convert the value on a VALUE_PAIR to string
79 static int valuepair2str(char * out,int outlen,VALUE_PAIR * pair, int type)
82 vp_prints_value(out, outlen, pair, -1);
88 strlcpy(out,"_",outlen);
90 case PW_TYPE_INTEGER64:
93 strlcpy(out,"0",outlen);
96 strlcpy(out,"?.?.?.?",outlen);
98 case PW_TYPE_IPV6ADDR :
99 strlcpy(out,":?:",outlen);
102 strlcpy(out,"0",outlen);
105 strlcpy(out,"unknown_type",outlen);
110 static VALUE_PAIR *pairfind_tag(VALUE_PAIR *vps, const DICT_ATTR *da, int tag)
112 VALUE_PAIR *vp = vps;
115 vp = pairfind(vp, da->attr, da->vendor);
118 if (!vp) return NULL;
120 if (!vp->flags.has_tag) return NULL;
122 if (vp->flags.tag == tag) return vp;
129 * Dynamically translate for check:, request:, reply:, etc.
131 static size_t xlat_packet(void *instance, REQUEST *request,
132 const char *fmt, char *out, size_t outlen)
136 VALUE_PAIR *vps = NULL;
137 RADIUS_PACKET *packet = NULL;
139 switch (*(int*) instance) {
141 vps = request->config_items;
145 vps = request->packet->vps;
146 packet = request->packet;
150 vps = request->reply->vps;
151 packet = request->reply;
156 if (request->proxy) vps = request->proxy->vps;
157 packet = request->proxy;
163 if (request->proxy_reply) vps = request->proxy_reply->vps;
164 packet = request->proxy_reply;
169 if (request->parent) {
170 vps = request->parent->packet->vps;
171 packet = request->parent->packet;
176 if (request->parent && request->parent->reply) {
177 vps = request->parent->reply->vps;
178 packet = request->parent->reply;
183 if (request->parent) {
184 vps = request->parent->config_items;
193 * The "format" string is the attribute name.
195 da = dict_attrbyname(fmt);
197 int do_number = FALSE;
198 int do_array = FALSE;
199 int do_count = FALSE;
202 size_t count = 0, total;
206 if (strlen(fmt) > sizeof(buffer)) return 0;
208 strlcpy(buffer, fmt, sizeof(buffer));
211 * %{Attribute-name#} - print integer version of it.
213 p = buffer + strlen(buffer) - 1;
220 * %{Attribute-Name:tag} - get the name with the specified
223 p = strchr(buffer, ':');
231 * Allow %{Attribute-Name:tag[...]}
237 * %{Attribute-Name[...] does more stuff
245 } else if (p[1] == '*') {
249 p += 1 + strspn(p + 1, "0123456789");
251 RDEBUG2("xlat: Invalid array reference in string at %s %s",
259 * We COULD argue about %{Attribute-Name[#]#} etc.
260 * But that looks like more work than it's worth.
263 da = dict_attrbyname(buffer);
267 * No array, print the tagged attribute.
270 vp = pairfind_tag(vps, da, tag);
277 * Array[#] - return the total
280 for (vp = pairfind_tag(vps, da, tag);
282 vp = pairfind_tag(vp->next, da, tag)) {
286 snprintf(out, outlen, "%d", (int) total);
291 * %{Attribute-Name[*]} returns ALL of the
292 * the attributes, separated by a newline.
295 for (vp = pairfind_tag(vps, da, tag);
297 vp = pairfind_tag(vp->next, da, tag)) {
298 count = valuepair2str(out, outlen - 1, vp, da->type);
299 rad_assert(count <= outlen);
301 outlen -= (count + 1);
306 if (outlen <= 1) break;
314 * Find the N'th value.
316 for (vp = pairfind_tag(vps, da, tag);
318 vp = pairfind_tag(vp->next, da, tag)) {
319 if (total == count) break;
328 * Non-existent array reference.
334 if ((vp->type != PW_TYPE_IPADDR) &&
335 (vp->type != PW_TYPE_INTEGER) &&
336 (vp->type != PW_TYPE_SHORT) &&
337 (vp->type != PW_TYPE_BYTE) &&
338 (vp->type != PW_TYPE_DATE)) {
343 return snprintf(out, outlen, "%u", vp->vp_integer);
346 return valuepair2str(out, outlen, vp, da->type);
349 vp = pairfind(vps, da->attr, da->vendor);
352 * Some "magic" handlers, which are never in VP's, but
353 * which are in the packet.
355 * @bug FIXME: We should really do this in a more
361 memset(&localvp, 0, sizeof(localvp));
368 dval = dict_valbyattr(da->attr, da->vendor, packet->code);
370 snprintf(out, outlen, "%s", dval->name);
372 snprintf(out, outlen, "%d", packet->code);
378 case PW_CLIENT_SHORTNAME:
379 if (request->client && request->client->shortname) {
380 strlcpy(out, request->client->shortname, outlen);
382 strlcpy(out, "<UNKNOWN-CLIENT>", outlen);
386 case PW_CLIENT_IP_ADDRESS: /* the same as below */
387 case PW_PACKET_SRC_IP_ADDRESS:
388 if (packet->src_ipaddr.af != AF_INET) {
391 localvp.attribute = da->attr;
392 localvp.vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
395 case PW_PACKET_DST_IP_ADDRESS:
396 if (packet->dst_ipaddr.af != AF_INET) {
399 localvp.attribute = da->attr;
400 localvp.vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
403 case PW_PACKET_SRC_PORT:
404 localvp.attribute = da->attr;
405 localvp.vp_integer = packet->src_port;
408 case PW_PACKET_DST_PORT:
409 localvp.attribute = da->attr;
410 localvp.vp_integer = packet->dst_port;
413 case PW_PACKET_AUTHENTICATION_VECTOR:
414 localvp.attribute = da->attr;
415 memcpy(localvp.vp_strvalue, packet->vector,
416 sizeof(packet->vector));
417 localvp.length = sizeof(packet->vector);
421 * Authorization, accounting, etc.
423 case PW_REQUEST_PROCESSING_STAGE:
424 if (request->component) {
425 strlcpy(out, request->component, outlen);
427 strlcpy(out, "server_core", outlen);
431 case PW_PACKET_SRC_IPV6_ADDRESS:
432 if (packet->src_ipaddr.af != AF_INET6) {
435 localvp.attribute = da->attr;
436 memcpy(localvp.vp_strvalue,
437 &packet->src_ipaddr.ipaddr.ip6addr,
438 sizeof(packet->src_ipaddr.ipaddr.ip6addr));
441 case PW_PACKET_DST_IPV6_ADDRESS:
442 if (packet->dst_ipaddr.af != AF_INET6) {
445 localvp.attribute = da->attr;
446 memcpy(localvp.vp_strvalue,
447 &packet->dst_ipaddr.ipaddr.ip6addr,
448 sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
451 case PW_VIRTUAL_SERVER:
452 if (!request->server) return 0;
454 snprintf(out, outlen, "%s", request->server);
458 case PW_MODULE_RETURN_CODE:
459 localvp.attribute = da->attr;
462 * See modcall.c for a bit of a hack.
464 localvp.vp_integer = request->simul_max;
468 return 0; /* not found */
472 localvp.type = da->type;
473 return valuepair2str(out, outlen, &localvp, da->type);
482 if (!vps) return 0; /* silently fail */
485 * Convert the VP to a string, and return it.
487 return valuepair2str(out, outlen, vp, da->type);
491 * @brief Print data as integer, not as VALUE.
493 static size_t xlat_integer(UNUSED void *instance, REQUEST *request,
494 const char *fmt, char *out, size_t outlen)
498 while (isspace((int) *fmt)) fmt++;
500 if (!radius_get_vp(request, fmt, &vp) || !vp) {
505 if ((vp->type != PW_TYPE_IPADDR) &&
506 (vp->type != PW_TYPE_INTEGER) &&
507 (vp->type != PW_TYPE_INTEGER64) &&
508 (vp->type != PW_TYPE_SHORT) &&
509 (vp->type != PW_TYPE_BYTE) &&
510 (vp->type != PW_TYPE_DATE)) {
515 if (vp->type == PW_TYPE_INTEGER64) {
516 return snprintf(out, outlen, "%llu", vp->vp_integer64);
519 return snprintf(out, outlen, "%u", vp->vp_integer);
523 * @brief Print data as hex, not as VALUE.
525 static size_t xlat_hex(UNUSED void *instance, REQUEST *request,
526 const char *fmt, char *out, size_t outlen)
530 uint8_t buffer[MAX_STRING_LEN];
534 while (isspace((int) *fmt)) fmt++;
536 if (!radius_get_vp(request, fmt, &vp) || !vp) {
541 ret = rad_vp2data(vp, buffer, sizeof(buffer));
545 * Don't truncate the data.
547 if ((ret < 0 ) || (outlen < (len * 2))) {
552 for (i = 0; i < len; i++) {
553 snprintf(out + 2*i, 3, "%02x", buffer[i]);
560 * @brief Print data as base64, not as VALUE
562 static size_t xlat_base64(UNUSED void *instance, REQUEST *request,
563 const char *fmt, char *out, size_t outlen)
566 uint8_t buffer[MAX_STRING_LEN];
571 while (isspace((int) *fmt)) fmt++;
573 if (!radius_get_vp(request, fmt, &vp) || !vp) {
578 ret = rad_vp2data(vp, buffer, sizeof(buffer));
586 enc = FR_BASE64_ENC_LENGTH(len);
589 * Don't truncate the data.
591 if (outlen < (enc + 1)) {
596 fr_base64_encode(buffer, len, out, outlen);
601 static size_t xlat_dhcp_options(UNUSED void *instance, REQUEST *request,
602 const char *fmt, char *out, size_t outlen)
604 VALUE_PAIR *vp, *head = NULL, *next;
607 while (isspace((int) *fmt)) fmt++;
609 if (!radius_get_vp(request, fmt, &vp) || !vp) {
615 if ((fr_dhcp_decode_options(vp->vp_octets, vp->length, &head) < 0) ||
617 RDEBUG("WARNING: DHCP option decoding failed");
628 pairmove(&(request->packet->vps), &head);
632 snprintf(out, outlen, "%i", decoded);
638 * @brief Prints the current module processing the request
640 static size_t xlat_module(UNUSED void *instance, REQUEST *request,
641 UNUSED const char *fmt, char *out, size_t outlen)
643 strlcpy(out, request->module, outlen);
650 * @brief Implements the Foreach-Variable-X
654 static size_t xlat_foreach(void *instance, REQUEST *request,
655 UNUSED const char *fmt, char *out, size_t outlen)
660 * See modcall, "FOREACH" for how this works.
662 pvp = (VALUE_PAIR **) request_data_reference(request, radius_get_vp,
669 return valuepair2str(out, outlen, (*pvp), (*pvp)->type);
674 * @brief Print data as string, if possible.
676 * If attribute "Foo" is defined as "octets" it will normally
677 * be printed as 0x0a0a0a. The xlat "%{string:Foo}" will instead
680 static size_t xlat_string(UNUSED void *instance, REQUEST *request,
681 const char *fmt, char *out, size_t outlen)
686 while (isspace((int) *fmt)) fmt++;
694 if (!radius_get_vp(request, fmt, &vp)) goto nothing;
696 if (!vp) goto nothing;
698 if (vp->type != PW_TYPE_OCTETS) goto nothing;
700 len = fr_print_string(vp->vp_strvalue, vp->length, out, outlen);
708 * @brief Expand regexp matches %{0} to %{8}
710 static size_t xlat_regex(void *instance, REQUEST *request,
711 const char *fmt, char *out, size_t outlen)
716 * We cheat: fmt is "0" to "8", but those numbers
717 * are already in the "instance".
719 fmt = fmt; /* -Wunused */
721 regex = request_data_reference(request, request,
722 REQUEST_DATA_REGEX | *(int *)instance);
723 if (!regex) return 0;
726 * Copy UP TO "freespace" bytes, including
729 strlcpy(out, regex, outlen);
732 #endif /* HAVE_REGEX_H */
735 * @brief Dynamically change the debugging level for the current request
739 static size_t xlat_debug(UNUSED void *instance, REQUEST *request,
740 const char *fmt, char *out, size_t outlen)
745 * Expand to previous (or current) level
747 snprintf(out, outlen, "%d", request->options & RAD_REQUEST_OPTION_DEBUG4);
750 * Assume we just want to get the current value and NOT set it to 0
757 request->options = RAD_REQUEST_OPTION_NONE;
758 request->radlog = NULL;
760 if (level > 4) level = 4;
762 request->options = level;
763 request->radlog = radlog_request;
771 * Compare two xlat_t structs, based ONLY on the module name.
773 static int xlat_cmp(const void *a, const void *b)
775 if (((const xlat_t *)a)->length != ((const xlat_t *)b)->length) {
776 return ((const xlat_t *)a)->length - ((const xlat_t *)b)->length;
779 return memcmp(((const xlat_t *)a)->module,
780 ((const xlat_t *)b)->module,
781 ((const xlat_t *)a)->length);
786 * find the appropriate registered xlat function.
788 static xlat_t *xlat_find(const char *module)
792 strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
793 my_xlat.length = strlen(my_xlat.module);
795 return rbtree_finddata(xlat_root, &my_xlat);
800 * @brief Register an xlat function.
802 * @param module xlat name
803 * @param func xlat function to be called
804 * @param instance argument to xlat function
805 * @return 0 on success, -1 on failure
807 int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance)
812 if (!module || !*module) {
813 DEBUG("xlat_register: Invalid module name");
818 * First time around, build up the tree...
820 * FIXME: This code should be hoisted out of this function,
821 * and into a global "initialization". But it isn't critical...
829 xlat_root = rbtree_create(xlat_cmp, free, 0);
831 DEBUG("xlat_register: Failed to create tree.");
836 * Register the internal packet xlat's.
838 for (i = 0; internal_xlat[i] != NULL; i++) {
839 xlat_register(internal_xlat[i], xlat_packet, &xlat_inst[i]);
840 c = xlat_find(internal_xlat[i]);
841 rad_assert(c != NULL);
846 for (i = 0; xlat_foreach_names[i] != NULL; i++) {
847 xlat_register(xlat_foreach_names[i],
848 xlat_foreach, &xlat_inst[i]);
849 c = xlat_find(xlat_foreach_names[i]);
850 rad_assert(c != NULL);
856 * New name: "control"
858 xlat_register("control", xlat_packet, &xlat_inst[0]);
859 c = xlat_find("control");
860 rad_assert(c != NULL);
863 #define XLAT_REGISTER(_x) xlat_register(Stringify(_x), xlat_ ## _x, NULL); \
864 c = xlat_find(Stringify(_x)); \
865 rad_assert(c != NULL); \
868 XLAT_REGISTER(integer);
870 XLAT_REGISTER(base64);
871 XLAT_REGISTER(dhcp_options);
872 XLAT_REGISTER(string);
873 XLAT_REGISTER(module);
877 * Register xlat's for regexes.
880 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
882 xlat_register(buffer, xlat_regex, &xlat_inst[i]);
883 c = xlat_find(buffer);
884 rad_assert(c != NULL);
887 #endif /* HAVE_REGEX_H */
890 xlat_register("debug", xlat_debug, &xlat_inst[0]);
891 c = xlat_find("debug");
892 rad_assert(c != NULL);
897 * If it already exists, replace the instance.
899 strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
900 my_xlat.length = strlen(my_xlat.module);
901 c = rbtree_finddata(xlat_root, &my_xlat);
904 DEBUG("xlat_register: Cannot re-define internal xlat");
909 c->instance = instance;
914 * Doesn't exist. Create it.
916 c = rad_malloc(sizeof(*c));
917 memset(c, 0, sizeof(*c));
920 strlcpy(c->module, module, sizeof(c->module));
921 c->length = strlen(c->module);
922 c->instance = instance;
924 rbtree_insert(xlat_root, c);
930 * @brief Unregister an xlat function.
932 * We can only have one function to call per name, so the
933 * passing of "func" here is extraneous.
935 * @param module xlat to unregister
939 void xlat_unregister(const char *module, RAD_XLAT_FUNC func, void *instance)
944 func = func; /* -Wunused */
948 strlcpy(my_xlat.module, module, sizeof(my_xlat.module));
949 my_xlat.length = strlen(my_xlat.module);
951 c = rbtree_finddata(xlat_root, &my_xlat);
954 if (c->instance != instance) return;
956 rbtree_deletebydata(xlat_root, c);
960 * @brief De-register all xlat functions, used mainly for debugging.
964 rbtree_free(xlat_root);
969 * @brief Decode an attribute name into a string.
971 * This expands the various formats:
976 * calls radius_xlat() to do most of the work
978 * @param from string to expand
979 * @param to buffer for output
980 * @param freespace space remaining in output buffer
981 * @param request current server request
982 * @param func optional function to escape output; passed to radius_xlat()
983 * @return 0 on success, -1 on failure
985 static int decode_attribute(const char **from, char **to, int freespace,
987 RADIUS_ESCAPE_STRING func, void *funcarg)
990 const char *module_name, *xlat_str;
991 char *p, *q, *l, *next = NULL;
1002 * Copy the input string to an intermediate buffer where
1005 varlen = rad_copy_variable(buffer, *from);
1007 RDEBUG2("ERROR: Badly formatted variable: %s", *from);
1013 * Kill the %{} around the data we are looking for.
1016 p[varlen - 1] = '\0'; /* */
1024 * Handle %{%{foo}:-%{bar}}, which is useful, too.
1026 * Did I mention that this parser is garbage?
1028 if ((p[0] == '%') && (p[1] == '{')) {
1030 int expand2 = FALSE;
1033 * 'p' is after the start of 'buffer', so we can
1036 len1 = rad_copy_variable(buffer, p);
1038 RDEBUG2("ERROR: Badly formatted variable: %s", p);
1043 * They did %{%{foo}}, which is stupid, but allowed.
1046 RDEBUG2("Improperly nested variable; %%{%s}", p);
1051 * It SHOULD be %{%{foo}:-%{bar}}. If not, it's
1054 if ((p[len1] != ':') || (p[len1 + 1] != '-')) {
1055 RDEBUG2("No trailing :- after variable at %s", p);
1060 * Parse the second bit. The second bit can be
1061 * either %{foo}, or a string "foo", or a string
1062 * 'foo', or just a bare word: foo
1065 l = buffer + len1 + 1;
1067 if ((p[0] == '%') && (p[1] == '{')) {
1068 len2 = rad_copy_variable(l, p);
1071 RDEBUG2("ERROR: Invalid text after :- at %s", p);
1077 } else if ((p[0] == '"') || p[0] == '\'') {
1078 getstring((const char **) &p, l, strlen(l));
1085 * Expand the first one. If we did, exit the
1088 retlen = radius_xlat(q, freespace, buffer, request, func, funcarg);
1094 RDEBUG2("\t... expanding second conditional");
1096 * Expand / copy the second string if required.
1099 retlen = radius_xlat(q, freespace, l,
1100 request, func, funcarg);
1105 strlcpy(q, l, freespace);
1110 * Else the output is an empty string.
1116 * See if we're supposed to expand a module name.
1119 for (l = p; *l != '\0'; l++) {
1124 module_name = p; /* start of name */
1131 * Module names can't have spaces.
1133 if ((*l == ' ') || (*l == '\t')) break;
1137 * %{name} is a simple attribute reference,
1138 * or regex reference.
1141 if (isdigit(*p) && !p[1]) { /* regex 0..8 */
1142 module_name = xlat_str = p;
1150 * Maybe it's the old-style %{foo:-bar}
1153 RDEBUG2("WARNING: Deprecated conditional expansion \":-\". See \"man unlang\" for details");
1156 xlat_str = module_name;
1162 * FIXME: For backwards "WTF" compatibility, check for
1163 * {...}, (after the :), and copy that, too.
1166 /* module name, followed by (possibly) per-module string */
1171 * Just "foo". Maybe it's a magic attr, which doesn't
1174 * If we can't find that, then assume it's a dictionary
1175 * attribute in the request.
1177 * Else if it's module:foo, look for module, and pass it "foo".
1180 c = xlat_find(xlat_str);
1181 if (!c) c = xlat_find("request");
1183 c = xlat_find(module_name);
1187 RDEBUG2("WARNING: Unknown Attribute \"%s\" in string expansion \"%%%s\"", xlat_str, *from);
1189 RDEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", module_name, *from);
1194 if (!c->internal) RDEBUG3("radius_xlat: Running registered xlat function of module %s for string \'%s\'",
1195 c->module, xlat_str);
1197 /* xlat to a temporary buffer, then escape */
1199 retlen = c->do_xlat(c->instance, request, xlat_str, tmpbuf, sizeof(tmpbuf));
1201 retlen = func(request, q, freespace, tmpbuf, funcarg);
1203 RDEBUG2("string escaped from \'%s\' to \'%s\'", tmpbuf, q);
1204 } else if (retlen < 0) {
1205 RDEBUG2("string escape failed");
1209 retlen = c->do_xlat(c->instance, request, xlat_str, q, freespace);
1213 snprintf(q, freespace, "%d", retlen);
1219 * Expand the second bit.
1221 RDEBUG2("\t... expanding second conditional");
1222 retlen = radius_xlat(q, freespace, next, request, func, funcarg);
1232 * @brief Replace %whatever in a string.
1234 * See 'doc/variables.txt' for more information.
1236 * @param out output buffer
1237 * @param outlen size of output buffer
1238 * @param fmt string to expand
1239 * @param request current request
1240 * @param func function to escape final value e.g. SQL quoting
1241 * @return length of string written @bug should really have -1 for failure
1243 int radius_xlat(char *out, int outlen, const char *fmt,
1245 RADIUS_ESCAPE_STRING func, void *funcarg)
1247 int c, len, freespace;
1252 struct tm *TM, s_TM;
1253 char tmpdt[40]; /* For temporary storing of dates */
1256 * Catch bad modules.
1258 if (!fmt || !out || !request) return 0;
1263 /* Calculate freespace in output */
1264 freespace = outlen - (q - out);
1269 if ((c != '%') && (c != '$') && (c != '\\')) {
1271 * We check if we're inside an open brace. If we are
1272 * then we assume this brace is NOT literal, but is
1273 * a closing brace and apply it
1280 * There's nothing after this character, copy
1281 * the last '%' or "$' or '\\' over to the output
1307 } else if (c == '%') switch(*p) {
1310 if (decode_attribute(&p, &q, freespace, request, func, funcarg) < 0) return 0;
1316 case 'd': /* request day */
1317 TM = localtime_r(&request->timestamp, &s_TM);
1318 len = strftime(tmpdt, sizeof(tmpdt), "%d", TM);
1320 strlcpy(q, tmpdt, freespace);
1325 case 'l': /* request timestamp */
1326 snprintf(tmpdt, sizeof(tmpdt), "%lu",
1327 (unsigned long) request->timestamp);
1328 strlcpy(q,tmpdt,freespace);
1332 case 'm': /* request month */
1333 TM = localtime_r(&request->timestamp, &s_TM);
1334 len = strftime(tmpdt, sizeof(tmpdt), "%m", TM);
1336 strlcpy(q, tmpdt, freespace);
1341 case 't': /* request timestamp */
1342 CTIME_R(&request->timestamp, tmpdt, sizeof(tmpdt));
1343 nl = strchr(tmpdt, '\n');
1345 strlcpy(q, tmpdt, freespace);
1349 case 'C': /* ClientName */
1350 strlcpy(q,request->client->shortname,freespace);
1354 case 'D': /* request date */
1355 TM = localtime_r(&request->timestamp, &s_TM);
1356 len = strftime(tmpdt, sizeof(tmpdt), "%Y%m%d", TM);
1358 strlcpy(q, tmpdt, freespace);
1363 case 'G': /* request minute */
1364 TM = localtime_r(&request->timestamp, &s_TM);
1365 len = strftime(tmpdt, sizeof(tmpdt), "%M", TM);
1367 strlcpy(q, tmpdt, freespace);
1372 case 'H': /* request hour */
1373 TM = localtime_r(&request->timestamp, &s_TM);
1374 len = strftime(tmpdt, sizeof(tmpdt), "%H", TM);
1376 strlcpy(q, tmpdt, freespace);
1381 case 'I': /* Request ID */
1382 snprintf(tmpdt, sizeof(tmpdt), "%i", request->packet->id);
1383 strlcpy(q, tmpdt, freespace);
1387 case 'S': /* request timestamp in SQL format*/
1388 TM = localtime_r(&request->timestamp, &s_TM);
1389 len = strftime(tmpdt, sizeof(tmpdt), "%Y-%m-%d %H:%M:%S", TM);
1391 strlcpy(q, tmpdt, freespace);
1396 case 'T': /* request timestamp */
1397 TM = localtime_r(&request->timestamp, &s_TM);
1398 len = strftime(tmpdt, sizeof(tmpdt), "%Y-%m-%d-%H.%M.%S.000000", TM);
1400 strlcpy(q, tmpdt, freespace);
1405 case 'V': /* Request-Authenticator */
1406 strlcpy(q,"Verified",freespace);
1410 case 'Y': /* request year */
1411 TM = localtime_r(&request->timestamp, &s_TM);
1412 len = strftime(tmpdt, sizeof(tmpdt), "%Y", TM);
1414 strlcpy(q, tmpdt, freespace);
1419 case 'Z': /* Full request pairs except password */
1420 tmp = request->packet->vps;
1421 while (tmp && (freespace > 3)) {
1422 if (tmp->attribute != PW_USER_PASSWORD) {
1424 len = vp_prints(q, freespace - 2, tmp);
1426 freespace -= (len + 2);
1434 RDEBUG2("WARNING: Unknown variable '%%%c': See 'doc/variables.txt'", *p);
1435 if (freespace > 2) {
1444 RDEBUG2("\texpand: %s -> %s", fmt, out);