Fixes from clang / scan-build
[freeradius.git] / src / lib / print.c
1 /*
2  * print.c      Routines to print stuff.
3  *
4  * Version:     $Id$
5  *
6  *   This library is free software; you can redistribute it and/or
7  *   modify it under the terms of the GNU Lesser General Public
8  *   License as published by the Free Software Foundation; either
9  *   version 2.1 of the License, or (at your option) any later version.
10  *
11  *   This library is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  *   Lesser General Public License for more details.
15  *
16  *   You should have received a copy of the GNU Lesser General Public
17  *   License along with this library; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  */
22
23 #include        <freeradius-devel/ident.h>
24 RCSID("$Id$")
25
26 #include        <freeradius-devel/libradius.h>
27
28 #include        <ctype.h>
29
30 /*
31  *      Checks for utf-8, taken from:
32  *
33  *  http://www.w3.org/International/questions/qa-forms-utf-8
34  *
35  *      Note that we don't care about the length of the input string,
36  *      because '\0' is an invalid UTF-8 character.
37  */
38 int fr_utf8_char(const uint8_t *str)
39 {
40         if (*str < 0x20) return 0;
41
42         if (*str <= 0x7e) return 1; /* 1 */
43
44         if (*str <= 0xc1) return 0;
45
46         if ((str[0] >= 0xc2) && /* 2 */
47             (str[0] <= 0xdf) &&
48             (str[1] >= 0x80) &&
49             (str[1] <= 0xbf)) {
50                 return 2;
51         }
52
53         if ((str[0] == 0xe0) && /* 3 */
54             (str[1] >= 0xa0) &&
55             (str[1] <= 0xbf) &&
56             (str[2] >= 0x80) &&
57             (str[2] <= 0xbf)) {
58                 return 3;
59         }
60
61         if ((str[0] >= 0xe1) && /* 4a */
62             (str[0] <= 0xec) &&
63             (str[1] >= 0x80) &&
64             (str[1] <= 0xbf) &&
65             (str[2] >= 0x80) &&
66             (str[2] <= 0xbf)) {
67                 return 3;
68         }
69
70         if ((str[0] >= 0xee) && /* 4b */
71             (str[0] <= 0xef) &&
72             (str[1] >= 0x80) &&
73             (str[1] <= 0xbf) &&
74             (str[2] >= 0x80) &&
75             (str[2] <= 0xbf)) {
76                 return 3;
77         }
78
79         if ((str[0] == 0xed) && /* 5 */
80             (str[1] >= 0x80) &&
81             (str[1] <= 0x9f) &&
82             (str[2] >= 0x80) &&
83             (str[2] <= 0xbf)) {
84                 return 3;
85         }
86
87         if ((str[0] == 0xf0) && /* 6 */
88             (str[1] >= 0x90) &&
89             (str[1] <= 0xbf) &&
90             (str[2] >= 0x80) &&
91             (str[2] <= 0xbf) &&
92             (str[3] >= 0x80) &&
93             (str[3] <= 0xbf)) {
94                 return 4;
95         }
96
97         if ((str[0] >= 0xf1) && /* 6 */
98             (str[1] <= 0xf3) &&
99             (str[1] >= 0x80) &&
100             (str[1] <= 0xbf) &&
101             (str[2] >= 0x80) &&
102             (str[2] <= 0xbf) &&
103             (str[3] >= 0x80) &&
104             (str[3] <= 0xbf)) {
105                 return 4;
106         }
107
108
109         if ((str[0] == 0xf4) && /* 7 */
110             (str[1] >= 0x80) &&
111             (str[1] <= 0x8f) &&
112             (str[2] >= 0x80) &&
113             (str[2] <= 0xbf) &&
114             (str[3] >= 0x80) &&
115             (str[3] <= 0xbf)) {
116                 return 4;
117         }
118
119         /*
120          *      Invalid UTF-8 Character
121          */
122         return 0;
123 }
124
125 /*
126  *      Convert a string to something printable.  The output string
127  *      has to be larger than the input string by at least 5 bytes.
128  *      If not, the output is silently truncated...
129  */
130 void fr_print_string(const char *in, size_t inlen, char *out, size_t outlen)
131 {
132         const uint8_t   *str = (const uint8_t *) in;
133         int             sp = 0;
134         int             utf8 = 0;
135
136         if (inlen == 0) inlen = strlen(in);
137
138         /*
139          *      
140          */
141         while ((inlen > 0) && (outlen > 4)) {
142                 /*
143                  *      Hack: never print trailing zero.
144                  *      Some clients send strings with an off-by-one
145                  *      length (confused with strings in C).
146                  */
147                 if ((inlen == 1) && (*str == 0)) break;
148
149                 switch (*str) {
150                         case '\\':
151                                 sp = '\\';
152                                 break;
153                         case '\r':
154                                 sp = 'r';
155                                 break;
156                         case '\n':
157                                 sp = 'n';
158                                 break;
159                         case '\t':
160                                 sp = 't';
161                                 break;
162                         case '"':
163                                 sp = '"';
164                                 break;
165                         default:
166                                 sp = 0;
167                                 break;
168                 }
169
170                 if (sp) {
171                         *out++ = '\\';
172                         *out++ = sp;
173                         outlen -= 2;
174                         str++;
175                         inlen--;
176                         continue;
177                 }
178
179                 utf8 = fr_utf8_char(str);
180                 if (!utf8) {
181                         snprintf(out, outlen, "\\%03o", *str);
182                         out  += 4;
183                         outlen -= 4;
184                         str++;
185                         inlen--;
186                         continue;
187                 }
188
189                 do {
190                         *out++ = *str++;
191                         outlen--;
192                         inlen--;
193                 } while (--utf8 > 0);
194         }
195         *out = 0;
196 }
197
198
199 /*
200  *  Print one value into a string.
201  *  delimitst will define if strings and dates will be delimited by '"'
202  */
203 int vp_prints_value(char * out, size_t outlen, VALUE_PAIR *vp, int delimitst)
204 {
205         DICT_VALUE  *v;
206         char        buf[1024];
207         const char  *a = NULL;
208         size_t      len;
209         time_t      t;
210         struct tm   s_tm;
211
212         out[0] = '\0';
213         if (!vp) return 0;
214
215         switch (vp->type) {
216                 case PW_TYPE_STRING:
217                         if ((delimitst == 1) && vp->flags.has_tag) {
218                                 /* Tagged attribute: print delimter and ignore tag */
219                                 buf[0] = '"';
220                                 fr_print_string(vp->vp_strvalue,
221                                                  vp->length, buf + 1, sizeof(buf) - 2);
222                                 strcat(buf, "\"");
223                         } else if (delimitst == 1) {
224                                 /* Non-tagged attribute: print delimter */
225                                 buf[0] = '"';
226                                 fr_print_string(vp->vp_strvalue,
227                                                  vp->length, buf + 1, sizeof(buf) - 2);
228                                 strcat(buf, "\"");
229
230                         } else if (delimitst < 0) { /* xlat.c */
231                                 strlcpy(out, vp->vp_strvalue, outlen);
232                                 return strlen(out);
233
234                         } else {
235                                 /* Non-tagged attribute: no delimiter */
236                                 fr_print_string(vp->vp_strvalue,
237                                                  vp->length, buf, sizeof(buf));
238                         }
239                         a = buf;
240                         break;
241                 case PW_TYPE_INTEGER:
242                         if ( vp->flags.has_tag ) {
243                                 /* Attribute value has a tag, need to ignore it */
244                                 if ((v = dict_valbyattr(vp->attribute, vp->vendor, (vp->vp_integer & 0xffffff)))
245                                     != NULL)
246                                         a = v->name;
247                                 else {
248                                         snprintf(buf, sizeof(buf), "%u", (vp->vp_integer & 0xffffff));
249                                         a = buf;
250                                 }
251                         } else {
252                 case PW_TYPE_BYTE:
253                 case PW_TYPE_SHORT:
254                                 /* Normal, non-tagged attribute */
255                                 if ((v = dict_valbyattr(vp->attribute, vp->vendor, vp->vp_integer))
256                                     != NULL)
257                                         a = v->name;
258                                 else {
259                                         snprintf(buf, sizeof(buf), "%u", vp->vp_integer);
260                                         a = buf;
261                                 }
262                         }
263                         break;
264                 case PW_TYPE_DATE:
265                         t = vp->vp_date;
266                         if (delimitst == 1) {
267                           len = strftime(buf, sizeof(buf), "\"%b %e %Y %H:%M:%S %Z\"",
268                                          localtime_r(&t, &s_tm));
269                         } else {
270                           len = strftime(buf, sizeof(buf), "%b %e %Y %H:%M:%S %Z",
271                                          localtime_r(&t, &s_tm));
272                         }
273                         if (len > 0) a = buf;
274                         break;
275                 case PW_TYPE_SIGNED: /* Damned code for 1 WiMAX attribute */
276                         snprintf(buf, sizeof(buf), "%d", vp->vp_signed);
277                         a = buf;
278                         break;
279                 case PW_TYPE_IPADDR:
280                         a = inet_ntop(AF_INET, &(vp->vp_ipaddr),
281                                       buf, sizeof(buf));
282                         break;
283                 case PW_TYPE_ABINARY:
284 #ifdef ASCEND_BINARY
285                         a = buf;
286                         print_abinary(vp, buf, sizeof(buf));
287                         break;
288 #else
289                   /* FALL THROUGH */
290 #endif
291                 case PW_TYPE_OCTETS:
292                         if (outlen <= (2 * (vp->length + 1))) return 0;
293
294                         strcpy(buf, "0x");
295
296                         fr_bin2hex(vp->vp_octets, buf + 2, vp->length);
297                         a = buf;
298                   break;
299
300                 case PW_TYPE_IFID:
301                         a = ifid_ntoa(buf, sizeof(buf), vp->vp_octets);
302                         break;
303
304                 case PW_TYPE_IPV6ADDR:
305                         a = inet_ntop(AF_INET6,
306                                       (const struct in6_addr *) vp->vp_strvalue,
307                                       buf, sizeof(buf));
308                         break;
309
310                 case PW_TYPE_IPV6PREFIX:
311                 {
312                         struct in6_addr addr;
313
314                         /*
315                          *      Alignment issues.
316                          */
317                         memcpy(&addr, vp->vp_strvalue + 2, sizeof(addr));
318
319                         a = inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
320                         if (a) {
321                                 char *p = buf + strlen(buf);
322                                 snprintf(p, buf + sizeof(buf) - p - 1, "/%u",
323                                          (unsigned int) vp->vp_octets[1]);
324                         }
325                 }
326                         break;
327
328                 case PW_TYPE_ETHERNET:
329                         snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
330                                  vp->vp_ether[0], vp->vp_ether[1],
331                                  vp->vp_ether[2], vp->vp_ether[3],
332                                  vp->vp_ether[4], vp->vp_ether[5]);
333                         a = buf;
334                         break;
335
336                 case PW_TYPE_TLV:
337                         if (outlen <= (2 * (vp->length + 1))) return 0;
338
339                         strcpy(buf, "0x");
340
341                         fr_bin2hex(vp->vp_tlv, buf + 2, vp->length);
342                         a = buf;
343                   break;
344
345                 default:
346                         a = "UNKNOWN-TYPE";
347                         break;
348         }
349
350         if (a != NULL) strlcpy(out, a, outlen);
351
352         return strlen(out);
353 }
354
355 /*
356  *  This is a hack, and has to be kept in sync with tokens.h
357  */
358 static const char *vp_tokens[] = {
359   "?",                          /* T_OP_INVALID */
360   "EOL",                        /* T_EOL */
361   "{",
362   "}",
363   "(",
364   ")",
365   ",",
366   ";",
367   "+=",
368   "-=",
369   ":=",
370   "=",
371   "!=",
372   ">=",
373   ">",
374   "<=",
375   "<",
376   "=~",
377   "!~",
378   "=*",
379   "!*",
380   "==",
381   "#",
382   "<BARE-WORD>",
383   "<\"STRING\">",
384   "<'STRING'>",
385   "<`STRING`>"
386 };
387
388 const char *vp_print_name(char *buffer, size_t bufsize, int attr, int vendor)
389 {
390         size_t len = 0;
391
392         if (!buffer) return NULL;
393
394         if (vendor) {
395                 DICT_VENDOR *v;
396                 
397                 v = dict_vendorbyvalue(vendor);
398                 if (v) {
399                         snprintf(buffer, bufsize, "%s-", v->name);
400                 } else {
401                         snprintf(buffer, bufsize, "Vendor-%u-", vendor);
402                 }
403
404                 len = strlen(buffer);
405                 if (len == bufsize) {
406                         return NULL;
407                 }
408         }
409
410         snprintf(buffer + len, bufsize - len, "Attr-%u", attr & 0xffff);
411         len += strlen(buffer + len);
412         if (len == bufsize) {
413                 return NULL;
414         }
415
416         return buffer;
417 }
418
419
420 /*
421  *      Print one attribute and value into a string.
422  */
423 int vp_prints(char *out, size_t outlen, VALUE_PAIR *vp)
424 {
425         size_t          len;
426         const char      *token = NULL;
427         const char      *name;
428         char            namebuf[128];
429
430         out[0] = 0;
431         if (!vp) return 0;
432
433         name = vp->name;
434
435         if (!name || !*name) {
436                 if (!vp_print_name(namebuf, sizeof(namebuf), vp->attribute, vp->attribute)) {
437                         return 0;
438                 }
439                 name = namebuf;
440         }
441
442         if ((vp->operator > T_OP_INVALID) &&
443             (vp->operator < T_TOKEN_LAST)) {
444                 token = vp_tokens[vp->operator];
445         } else {
446                 token = "<INVALID-TOKEN>";
447         }
448
449         if( vp->flags.has_tag ) {
450                 snprintf(out, outlen, "%s:%d %s ",
451                          name, vp->flags.tag, token);
452
453                 len = strlen(out);
454                 vp_prints_value(out + len, outlen - len, vp, 1);
455
456         } else {
457                 snprintf(out, outlen, "%s %s ", name, token);
458                 len = strlen(out);
459                 vp_prints_value(out + len, outlen - len, vp, 1);
460
461         }
462
463         return len + strlen(out + len);
464 }
465
466
467 /*
468  *      Print one attribute and value.
469  */
470 void vp_print(FILE *fp, VALUE_PAIR *vp)
471 {
472         char    buf[1024];
473
474         vp_prints(buf, sizeof(buf), vp);
475         fputs(buf, fp);
476 }
477
478
479 /*
480  *      Print a whole list of attributes, indented by a TAB
481  *      and with a newline at the end.
482  */
483 void vp_printlist(FILE *fp, VALUE_PAIR *vp)
484 {
485         for (; vp; vp = vp->next) {
486                 fprintf(fp, "\t");
487                 vp_print(fp, vp);
488                 fprintf(fp, "\n");
489         }
490 }
491