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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * Copyright 2000 The FreeRADIUS server project
22 * Copyright 2000 Alan DeKok <aland@ox.org>
25 static const char rcsid[] =
29 #include "libradius.h"
39 char module[MAX_STRING_LEN];
42 RAD_XLAT_FUNC do_xlat;
43 struct xlat_cmp *next;
46 static struct xlat_cmp *cmp = NULL;
49 * Register an xlat function.
51 int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance)
55 if (module == NULL || strlen(module) == 0){
56 DEBUG("xlat_register: Invalid module name");
60 xlat_unregister(module, func);
62 c = rad_malloc(sizeof(struct xlat_cmp));
65 strNcpy(c->module, module, sizeof(c->module));
66 c->length = strlen(c->module);
67 c->instance = instance;
75 * Unregister an xlat function.
77 void xlat_unregister(const char *module, RAD_XLAT_FUNC func)
79 struct xlat_cmp *c, *last;
82 for (c = cmp; c; c = c->next) {
83 if (strncmp(c->module,module,c->length) == 0 && c->do_xlat == func)
88 if (c == NULL) return;
99 * find the appropriate registered xlat function.
101 static struct xlat_cmp *find_xlat_func(const char *module)
105 for (c = cmp; c; c = c->next){
106 if (strncmp(c->module,module,c->length) == 0 && *(module+c->length) == ':')
115 Convert the value on a VALUE_PAIR to string
117 static int valuepair2str(char * out,int outlen,VALUE_PAIR * pair,
118 int type, RADIUS_ESCAPE_STRING func)
120 char buffer[MAX_STRING_LEN * 4];
123 vp_prints_value(buffer, sizeof(buffer), pair, 0);
124 return func(out, outlen, buffer);
128 case PW_TYPE_STRING :
129 strNcpy(out,"_",outlen);
131 case PW_TYPE_INTEGER :
132 strNcpy(out,"0",outlen);
134 case PW_TYPE_IPADDR :
135 strNcpy(out,"?.?.?.?",outlen);
138 strNcpy(out,"0",outlen);
141 strNcpy(out,"unknown_type",outlen);
147 * Decode an attribute name from a particular RADIUS_PACKET
150 static int decode_attr_packet(const char *from, char **to, int freespace,
151 RADIUS_PACKET *packet,
152 RADIUS_ESCAPE_STRING func)
158 tmpda = dict_attrbyname(from);
159 if (!tmpda) return 0;
162 * See if the VP is defined.
164 vp = pairfind(packet->vps, tmpda->attr);
166 *to += valuepair2str(*to, freespace, vp,
172 * Non-protocol attributes.
174 switch (tmpda->attr) {
179 dval = dict_valbyattr(tmpda->attr, packet->code);
181 snprintf(*to, freespace, "%s", dval->name);
183 snprintf(*to, freespace, "%d", packet->code);
198 * Decode an attribute name from a particular VALUE_PAIR*
201 static int decode_attr_vps(const char *from, char **to, int freespace,
203 RADIUS_ESCAPE_STRING func)
209 tmpda = dict_attrbyname(from);
210 if (!tmpda) return 0;
213 * See if the VP is defined.
215 vp = pairfind(vps, tmpda->attr);
217 *to += valuepair2str(*to, freespace, vp,
226 * Decode an attribute name into a string.
228 static void decode_attribute(const char **from, char **to, int freespace,
229 int *open, REQUEST *request,
230 RADIUS_ESCAPE_STRING func)
235 int stop=0, found=0, retlen=0;
236 int openbraces = *open;
246 * Skip the '{' at the front of 'p'
247 * Increment open braces
253 * Copy over the rest of the string.
255 while ((*p) && (!stop)) {
258 * Allow braces inside things, too.
272 if (openbraces == *open) {
281 * Attr-Name1:-Attr-Name2
283 * Use Attr-Name1, and if not found,
292 /* else FALL-THROUGH */
302 * Find an attribute from the reply.
304 if (strncasecmp(attrname,"reply:",6) == 0) {
305 found = decode_attr_packet(&attrname[6], &q, freespace,
306 request->reply, func);
309 * Find an attribute from the request.
311 } else if (strncasecmp(attrname,"request:",8) == 0) {
312 found = decode_attr_packet(&attrname[8], &q, freespace,
313 request->packet, func);
316 * Find an attribute from the config items.
318 } else if (strncasecmp(attrname,"check:",6) == 0) {
319 found = decode_attr_vps(&attrname[6], &q, freespace,
320 request->config_items, func);
323 * Find an attribute from the proxy request.
325 } else if ((strncasecmp(attrname,"proxy-request:",14) == 0) &&
326 (request->proxy_reply != NULL)) {
327 found = decode_attr_packet(&attrname[14], &q, freespace,
328 request->proxy, func);
331 * Find an attribute from the proxy reply.
333 } else if ((strncasecmp(attrname,"proxy-reply:",12) == 0) &&
334 (request->proxy_reply != NULL)) {
335 found = decode_attr_packet(&attrname[12], &q, freespace,
336 request->proxy_reply, func);
339 * Find a string from a registered function.
341 } else if ((c = find_xlat_func(attrname)) != NULL) {
342 DEBUG("radius_xlat: Running registered xlat function of module %s for string \'%s\'",
343 c->module, attrname+ c->length + 1);
344 retlen = c->do_xlat(c->instance, request, attrname+(c->length+1), q, freespace, func);
345 /* If retlen is 0, treat it as not found */
354 * Nothing else, it MUST be a bare attribute name.
356 } else if (decode_attr_packet(attrname, &q, freespace, request->packet, func)) {
359 } else if (dict_attrbyname(attrname) == NULL) {
361 * No attribute by that name, return an error.
363 DEBUG2("WARNING: Attempt to use unknown xlat function, or non-existent attribute in string %%{%s}", attrname);
365 } /* else the attribute is known, but not in the request */
368 * Skip to last '}' if attr is found
369 * The rest of the stuff within the braces is
370 * useless if we found what we need
373 while((*p != '\0') && (openbraces > 0)) {
379 * Ensure that escaped braces are allowed.
382 p++; /* skip the escaped character */
396 p++; /* skip the character */
406 * If the caller doesn't pass xlat an escape function, then
407 * we use this one. It simplifies the coding, as the check for
408 * func == NULL only happens once.
410 static int xlat_copy(char *out, int outlen, const char *in)
416 * Truncate, if too much.
425 * FIXME: Do escaping of bad stuff!
439 * Replace %<whatever> in a string.
441 * See 'doc/variables.txt' for more information.
443 int radius_xlat(char *out, int outlen, const char *fmt,
444 REQUEST *request, RADIUS_ESCAPE_STRING func)
451 char tmpdt[40]; /* For temporary storing of dates */
455 * Ensure that we always have an escaping function.
464 /* Calculate freespace in output */
465 freespace = outlen - (q - out);
470 if ((c != '%') && (c != '$') && (c != '\\')) {
472 * We check if we're inside an open brace. If we are
473 * then we assume this brace is NOT literal, but is
474 * a closing brace and apply it
476 if ((c == '}') && openbraces) {
486 * There's nothing after this character, copy
487 * the last '%' or "$' or '\\' over to the output
512 } else if (c == '$') switch(*p) {
513 case '{': /* Attribute by Name */
514 decode_attribute(&p, &q, freespace, &openbraces, request, func);
521 } else if (c == '%') switch(*p) {
523 decode_attribute(&p, &q, freespace, &openbraces, request, func);
529 case 'a': /* Protocol: */
530 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_PROTOCOL),PW_TYPE_INTEGER, func);
533 case 'c': /* Callback-Number */
534 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_CALLBACK_NUMBER),PW_TYPE_STRING, func);
537 case 'd': /* request day */
538 TM = localtime_r(&request->timestamp, &s_TM);
539 strftime(tmpdt,sizeof(tmpdt),"%d",TM);
540 strNcpy(q,tmpdt,freespace);
544 case 'f': /* Framed IP address */
545 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_IP_ADDRESS),PW_TYPE_IPADDR, func);
548 case 'i': /* Calling station ID */
549 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CALLING_STATION_ID),PW_TYPE_STRING, func);
552 case 'l': /* request timestamp */
553 snprintf(tmpdt, sizeof(tmpdt), "%lu",
554 (unsigned long) request->timestamp);
555 strNcpy(q,tmpdt,freespace);
559 case 'm': /* request month */
560 TM = localtime_r(&request->timestamp, &s_TM);
561 strftime(tmpdt,sizeof(tmpdt),"%m",TM);
562 strNcpy(q,tmpdt,freespace);
566 case 'n': /* NAS IP address */
567 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_IP_ADDRESS),PW_TYPE_IPADDR, func);
570 case 'p': /* Port number */
571 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_PORT),PW_TYPE_INTEGER, func);
574 case 's': /* Speed */
575 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CONNECT_INFO),PW_TYPE_STRING, func);
578 case 't': /* request timestamp */
579 CTIME_R(&request->timestamp, q, freespace);
583 case 'u': /* User name */
584 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_USER_NAME),PW_TYPE_STRING, func);
587 case 'A': /* radacct_dir */
588 strNcpy(q,radacct_dir,freespace-1);
592 case 'C': /* ClientName */
593 strNcpy(q,client_name(request->packet->src_ipaddr),freespace-1);
597 case 'D': /* request date */
598 TM = localtime_r(&request->timestamp, &s_TM);
599 strftime(tmpdt,sizeof(tmpdt),"%Y%m%d",TM);
600 strNcpy(q,tmpdt,freespace);
604 case 'H': /* request hour */
605 TM = localtime_r(&request->timestamp, &s_TM);
606 strftime(tmpdt,sizeof(tmpdt),"%H",TM);
607 strNcpy(q,tmpdt,freespace);
611 case 'L': /* radlog_dir */
612 strNcpy(q,radlog_dir,freespace-1);
617 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_MTU),PW_TYPE_INTEGER, func);
620 case 'R': /* radius_dir */
621 strNcpy(q,radius_dir,freespace-1);
625 case 'S': /* request timestamp in SQL format*/
626 TM = localtime_r(&request->timestamp, &s_TM);
627 strftime(tmpdt,sizeof(tmpdt),"%Y-%m-%d %H:%M:%S",TM);
628 strNcpy(q,tmpdt,freespace);
632 case 'T': /* request timestamp */
633 TM = localtime_r(&request->timestamp, &s_TM);
634 strftime(tmpdt,sizeof(tmpdt),"%Y-%m-%d-%H.%M.%S.000000",TM);
635 strNcpy(q,tmpdt,freespace);
639 case 'U': /* Stripped User name */
640 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_STRIPPED_USER_NAME),PW_TYPE_STRING, func);
643 case 'V': /* Request-Authenticator */
644 if (request->packet->verified)
645 strNcpy(q,"Verified",freespace-1);
647 strNcpy(q,"None",freespace-1);
651 case 'Y': /* request year */
652 TM = localtime_r(&request->timestamp, &s_TM);
653 strftime(tmpdt,sizeof(tmpdt),"%Y",TM);
654 strNcpy(q,tmpdt,freespace);
658 case 'Z': /* Full request pairs except password */
659 tmp = request->packet->vps;
660 while (tmp && (freespace > 3)) {
661 if (tmp->attribute != PW_PASSWORD) {
663 i = vp_prints(q,freespace-2,tmp);
673 DEBUG2("WARNING: Unknown variable '%%%c': See 'doc/variables.txt'", *p);
683 DEBUG2("radius_xlat: '%s'", out);