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)
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 q += c->do_xlat(c->instance, request, attrname+(c->length+1), q, freespace, func);
348 * Nothing else, it MUST be a bare attribute name.
350 } else if (decode_attr_packet(attrname, &q, freespace, request->packet, func)) {
353 } else if (dict_attrbyname(attrname) == NULL) {
355 * No attribute by that name, return an error.
357 DEBUG2("WARNING: Attempt to use unknown xlat function, or non-existent attribute in string %%{%s}", attrname);
359 } /* else the attribute is known, but not in the request */
362 * Skip to last '}' if attr is found
363 * The rest of the stuff within the braces is
364 * useless if we found what we need
367 while((*p != '\0') && (openbraces > 0)) {
373 * Ensure that escaped braces are allowed.
376 p++; /* skip the escaped character */
390 p++; /* skip the character */
400 * If the caller doesn't pass xlat an escape function, then
401 * we use this one. It simplifies the coding, as the check for
402 * func == NULL only happens once.
404 static int xlat_copy(char *out, int outlen, const char *in)
410 * Truncate, if too much.
419 * FIXME: Do escaping of bad stuff!
433 * Replace %<whatever> in a string.
435 * See 'doc/variables.txt' for more information.
437 int radius_xlat(char *out, int outlen, const char *fmt,
438 REQUEST *request, RADIUS_ESCAPE_STRING func)
445 char tmpdt[40]; /* For temporary storing of dates */
449 * Ensure that we always have an escaping function.
458 /* Calculate freespace in output */
459 freespace = outlen - (q - out);
464 if ((c != '%') && (c != '$') && (c != '\\')) {
466 * We check if we're inside an open brace. If we are
467 * then we assume this brace is NOT literal, but is
468 * a closing brace and apply it
470 if ((c == '}') && openbraces) {
480 * There's nothing after this character, copy
481 * the last '%' or "$' or '\\' over to the output
506 } else if (c == '$') switch(*p) {
507 case '{': /* Attribute by Name */
508 decode_attribute(&p, &q, freespace, &openbraces, request, func);
515 } else if (c == '%') switch(*p) {
517 decode_attribute(&p, &q, freespace, &openbraces, request, func);
523 case 'a': /* Protocol: */
524 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_PROTOCOL),PW_TYPE_INTEGER, func);
527 case 'c': /* Callback-Number */
528 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_CALLBACK_NUMBER),PW_TYPE_STRING, func);
531 case 'd': /* request day */
532 TM = localtime_r(&request->timestamp, &s_TM);
533 strftime(tmpdt,sizeof(tmpdt),"%d",TM);
534 strNcpy(q,tmpdt,freespace);
538 case 'f': /* Framed IP address */
539 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_IP_ADDRESS),PW_TYPE_IPADDR, func);
542 case 'i': /* Calling station ID */
543 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CALLING_STATION_ID),PW_TYPE_STRING, func);
546 case 'l': /* request timestamp */
547 snprintf(tmpdt, sizeof(tmpdt), "%lu",
548 (unsigned long) request->timestamp);
549 strNcpy(q,tmpdt,freespace);
553 case 'm': /* request month */
554 TM = localtime_r(&request->timestamp, &s_TM);
555 strftime(tmpdt,sizeof(tmpdt),"%m",TM);
556 strNcpy(q,tmpdt,freespace);
560 case 'n': /* NAS IP address */
561 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_IP_ADDRESS),PW_TYPE_IPADDR, func);
564 case 'p': /* Port number */
565 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_NAS_PORT),PW_TYPE_INTEGER, func);
568 case 's': /* Speed */
569 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_CONNECT_INFO),PW_TYPE_STRING, func);
572 case 't': /* request timestamp */
573 CTIME_R(&request->timestamp, q, freespace);
577 case 'u': /* User name */
578 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_USER_NAME),PW_TYPE_STRING, func);
581 case 'A': /* radacct_dir */
582 strNcpy(q,radacct_dir,freespace-1);
586 case 'C': /* ClientName */
587 strNcpy(q,client_name(request->packet->src_ipaddr),freespace-1);
591 case 'D': /* request date */
592 TM = localtime_r(&request->timestamp, &s_TM);
593 strftime(tmpdt,sizeof(tmpdt),"%Y%m%d",TM);
594 strNcpy(q,tmpdt,freespace);
598 case 'H': /* request hour */
599 TM = localtime_r(&request->timestamp, &s_TM);
600 strftime(tmpdt,sizeof(tmpdt),"%H",TM);
601 strNcpy(q,tmpdt,freespace);
605 case 'L': /* radlog_dir */
606 strNcpy(q,radlog_dir,freespace-1);
611 q += valuepair2str(q,freespace,pairfind(request->reply->vps,PW_FRAMED_MTU),PW_TYPE_INTEGER, func);
614 case 'R': /* radius_dir */
615 strNcpy(q,radius_dir,freespace-1);
619 case 'S': /* request timestamp in SQL format*/
620 TM = localtime_r(&request->timestamp, &s_TM);
621 strftime(tmpdt,sizeof(tmpdt),"%Y-%m-%d %H:%M:%S",TM);
622 strNcpy(q,tmpdt,freespace);
626 case 'T': /* request timestamp */
627 TM = localtime_r(&request->timestamp, &s_TM);
628 strftime(tmpdt,sizeof(tmpdt),"%Y-%m-%d-%H.%M.%S.000000",TM);
629 strNcpy(q,tmpdt,freespace);
633 case 'U': /* Stripped User name */
634 q += valuepair2str(q,freespace,pairfind(request->packet->vps,PW_STRIPPED_USER_NAME),PW_TYPE_STRING, func);
637 case 'V': /* Request-Authenticator */
638 if (request->packet->verified)
639 strNcpy(q,"Verified",freespace-1);
641 strNcpy(q,"None",freespace-1);
645 case 'Y': /* request year */
646 TM = localtime_r(&request->timestamp, &s_TM);
647 strftime(tmpdt,sizeof(tmpdt),"%Y",TM);
648 strNcpy(q,tmpdt,freespace);
652 case 'Z': /* Full request pairs except password */
653 tmp = request->packet->vps;
654 while (tmp && (freespace > 3)) {
655 if (tmp->attribute != PW_PASSWORD) {
657 i = vp_prints(q,freespace-2,tmp);
667 DEBUG2("WARNING: Unknown variable '%%%c': See 'doc/variables.txt'", *p);
677 DEBUG2("radius_xlat: '%s'", out);