Fixes from clang / scan-build
[freeradius.git] / src / lib / print.c
index 1257531..2c14c22 100644 (file)
  *
  *   You should have received a copy of the GNU Lesser General Public
  *   License along with this library; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
- * Copyright 2000  The FreeRADIUS server project
+ * Copyright 2000,2006  The FreeRADIUS server project
  */
 
-static const char rcsid[] = "$Id$";
+#include       <freeradius-devel/ident.h>
+RCSID("$Id$")
 
-#include       "autoconf.h"
+#include       <freeradius-devel/libradius.h>
 
-#include       <stdio.h>
-#include       <stdlib.h>
-#include       <sys/types.h>
 #include       <ctype.h>
-#include       <string.h>
 
-#include       "libradius.h"
+/*
+ *     Checks for utf-8, taken from:
+ *
+ *  http://www.w3.org/International/questions/qa-forms-utf-8
+ *
+ *     Note that we don't care about the length of the input string,
+ *     because '\0' is an invalid UTF-8 character.
+ */
+int fr_utf8_char(const uint8_t *str)
+{
+       if (*str < 0x20) return 0;
+
+       if (*str <= 0x7e) return 1; /* 1 */
+
+       if (*str <= 0xc1) return 0;
+
+       if ((str[0] >= 0xc2) && /* 2 */
+           (str[0] <= 0xdf) &&
+           (str[1] >= 0x80) &&
+           (str[1] <= 0xbf)) {
+               return 2;
+       }
+
+       if ((str[0] == 0xe0) && /* 3 */
+           (str[1] >= 0xa0) &&
+           (str[1] <= 0xbf) &&
+           (str[2] >= 0x80) &&
+           (str[2] <= 0xbf)) {
+               return 3;
+       }
+
+       if ((str[0] >= 0xe1) && /* 4a */
+           (str[0] <= 0xec) &&
+           (str[1] >= 0x80) &&
+           (str[1] <= 0xbf) &&
+           (str[2] >= 0x80) &&
+           (str[2] <= 0xbf)) {
+               return 3;
+       }
+
+       if ((str[0] >= 0xee) && /* 4b */
+           (str[0] <= 0xef) &&
+           (str[1] >= 0x80) &&
+           (str[1] <= 0xbf) &&
+           (str[2] >= 0x80) &&
+           (str[2] <= 0xbf)) {
+               return 3;
+       }
+
+       if ((str[0] == 0xed) && /* 5 */
+           (str[1] >= 0x80) &&
+           (str[1] <= 0x9f) &&
+           (str[2] >= 0x80) &&
+           (str[2] <= 0xbf)) {
+               return 3;
+       }
+
+       if ((str[0] == 0xf0) && /* 6 */
+           (str[1] >= 0x90) &&
+           (str[1] <= 0xbf) &&
+           (str[2] >= 0x80) &&
+           (str[2] <= 0xbf) &&
+           (str[3] >= 0x80) &&
+           (str[3] <= 0xbf)) {
+               return 4;
+       }
+
+       if ((str[0] >= 0xf1) && /* 6 */
+           (str[1] <= 0xf3) &&
+           (str[1] >= 0x80) &&
+           (str[1] <= 0xbf) &&
+           (str[2] >= 0x80) &&
+           (str[2] <= 0xbf) &&
+           (str[3] >= 0x80) &&
+           (str[3] <= 0xbf)) {
+               return 4;
+       }
+
+
+       if ((str[0] == 0xf4) && /* 7 */
+           (str[1] >= 0x80) &&
+           (str[1] <= 0x8f) &&
+           (str[2] >= 0x80) &&
+           (str[2] <= 0xbf) &&
+           (str[3] >= 0x80) &&
+           (str[3] <= 0xbf)) {
+               return 4;
+       }
+
+       /*
+        *      Invalid UTF-8 Character
+        */
+       return 0;
+}
 
 /*
- *     Convert a string to something printable.
- *     The output string has to be _at least_ 4x the size
- *     of the input string!
+ *     Convert a string to something printable.  The output string
+ *     has to be larger than the input string by at least 5 bytes.
+ *     If not, the output is silently truncated...
  */
-void librad_safeprint(char *in, int inlen, char *out, int outlen)
+void fr_print_string(const char *in, size_t inlen, char *out, size_t outlen)
 {
-       unsigned char   *str = (unsigned char *)in;
-       int             done = 0;
+       const uint8_t   *str = (const uint8_t *) in;
        int             sp = 0;
+       int             utf8 = 0;
 
-       if (inlen < 0) inlen = strlen(in);
+       if (inlen == 0) inlen = strlen(in);
 
-       while (inlen-- > 0 && (done + 3) < outlen) {
+       /*
+        *      
+        */
+       while ((inlen > 0) && (outlen > 4)) {
                /*
                 *      Hack: never print trailing zero.
                 *      Some clients send strings with an off-by-one
                 *      length (confused with strings in C).
                 */
-               if (inlen == 0 && *str == 0)
-                       break;
-
-               sp = 0;
+               if ((inlen == 1) && (*str == 0)) break;
 
                switch (*str) {
                        case '\\':
@@ -69,25 +159,38 @@ void librad_safeprint(char *in, int inlen, char *out, int outlen)
                        case '\t':
                                sp = 't';
                                break;
+                       case '"':
+                               sp = '"';
+                               break;
                        default:
-                               if (*str < 32 || (*str >= 128)){
-                                       snprintf(out, outlen, "\\%03o", *str);
-                                       done += 4;
-                                       out  += 4;
-                                       outlen -= 4;
-                               } else {
-                                       *out++ = *str;
-                                       outlen--;
-                                       done++;
-                               }
+                               sp = 0;
+                               break;
                }
+
                if (sp) {
                        *out++ = '\\';
                        *out++ = sp;
                        outlen -= 2;
-                       done += 2;
+                       str++;
+                       inlen--;
+                       continue;
                }
-               str++;
+
+               utf8 = fr_utf8_char(str);
+               if (!utf8) {
+                       snprintf(out, outlen, "\\%03o", *str);
+                       out  += 4;
+                       outlen -= 4;
+                       str++;
+                       inlen--;
+                       continue;
+               }
+
+               do {
+                       *out++ = *str++;
+                       outlen--;
+                       inlen--;
+               } while (--utf8 > 0);
        }
        *out = 0;
 }
@@ -97,104 +200,155 @@ void librad_safeprint(char *in, int inlen, char *out, int outlen)
  *  Print one value into a string.
  *  delimitst will define if strings and dates will be delimited by '"'
  */
-int vp_prints_value(char * out, int outlen, VALUE_PAIR *vp, int delimitst)
+int vp_prints_value(char * out, size_t outlen, VALUE_PAIR *vp, int delimitst)
 {
        DICT_VALUE  *v;
        char        buf[1024];
-       char        *a;
+       const char  *a = NULL;
+       size_t      len;
        time_t      t;
        struct tm   s_tm;
 
-       out[0] = 0;
+       out[0] = '\0';
        if (!vp) return 0;
 
        switch (vp->type) {
                case PW_TYPE_STRING:
-                       if (vp->attribute == PW_NAS_PORT_ID)
-                               a = (char *)vp->strvalue;
-                       else {
-                               if (delimitst && vp->flags.has_tag) {
-                                       /* Tagged attribute: print delimter and ignore tag */
-                                       buf[0] = '"';
-                                       librad_safeprint((char *)(vp->strvalue),
-                                                        vp->length, buf + 1, sizeof(buf) - 2);
-                                       strcat(buf, "\"");
-                               } else if (delimitst) {
-                                       /* Non-tagged attribute: print delimter */
-                                       buf[0] = '"';
-                                       librad_safeprint((char *)vp->strvalue,
-                                                        vp->length, buf + 1, sizeof(buf) - 2);
-                                       strcat(buf, "\"");
-                               } else {
-                                       /* Non-tagged attribute: no delimiter */
-                                       librad_safeprint((char *)vp->strvalue,
-                                                        vp->length, buf, sizeof(buf));
-                               }
-                               a = buf;
+                       if ((delimitst == 1) && vp->flags.has_tag) {
+                               /* Tagged attribute: print delimter and ignore tag */
+                               buf[0] = '"';
+                               fr_print_string(vp->vp_strvalue,
+                                                vp->length, buf + 1, sizeof(buf) - 2);
+                               strcat(buf, "\"");
+                       } else if (delimitst == 1) {
+                               /* Non-tagged attribute: print delimter */
+                               buf[0] = '"';
+                               fr_print_string(vp->vp_strvalue,
+                                                vp->length, buf + 1, sizeof(buf) - 2);
+                               strcat(buf, "\"");
+
+                       } else if (delimitst < 0) { /* xlat.c */
+                               strlcpy(out, vp->vp_strvalue, outlen);
+                               return strlen(out);
+
+                       } else {
+                               /* Non-tagged attribute: no delimiter */
+                               fr_print_string(vp->vp_strvalue,
+                                                vp->length, buf, sizeof(buf));
                        }
+                       a = buf;
                        break;
                case PW_TYPE_INTEGER:
                        if ( vp->flags.has_tag ) {
                                /* Attribute value has a tag, need to ignore it */
-                               if ((v = dict_valbyattr(vp->attribute, (vp->lvalue & 0xffffff)))
+                               if ((v = dict_valbyattr(vp->attribute, vp->vendor, (vp->vp_integer & 0xffffff)))
                                    != NULL)
                                        a = v->name;
                                else {
-                                       snprintf(buf, sizeof(buf), "%u", (vp->lvalue & 0xffffff));
+                                       snprintf(buf, sizeof(buf), "%u", (vp->vp_integer & 0xffffff));
                                        a = buf;
                                }
                        } else {
+               case PW_TYPE_BYTE:
+               case PW_TYPE_SHORT:
                                /* Normal, non-tagged attribute */
-                               if ((v = dict_valbyattr(vp->attribute, vp->lvalue))
+                               if ((v = dict_valbyattr(vp->attribute, vp->vendor, vp->vp_integer))
                                    != NULL)
                                        a = v->name;
                                else {
-                                       snprintf(buf, sizeof(buf), "%u", vp->lvalue);
+                                       snprintf(buf, sizeof(buf), "%u", vp->vp_integer);
                                        a = buf;
                                }
                        }
                        break;
                case PW_TYPE_DATE:
-                       t = vp->lvalue;
-                       if (delimitst) {
-                         strftime(buf, sizeof(buf), "\"%b %e %Y\"", gmtime_r(&t, &s_tm));
+                       t = vp->vp_date;
+                       if (delimitst == 1) {
+                         len = strftime(buf, sizeof(buf), "\"%b %e %Y %H:%M:%S %Z\"",
+                                        localtime_r(&t, &s_tm));
                        } else {
-                         strftime(buf, sizeof(buf), "%b %e %Y", gmtime_r(&t, &s_tm));
+                         len = strftime(buf, sizeof(buf), "%b %e %Y %H:%M:%S %Z",
+                                        localtime_r(&t, &s_tm));
                        }
+                       if (len > 0) a = buf;
+                       break;
+               case PW_TYPE_SIGNED: /* Damned code for 1 WiMAX attribute */
+                       snprintf(buf, sizeof(buf), "%d", vp->vp_signed);
                        a = buf;
                        break;
                case PW_TYPE_IPADDR:
-                       if (vp->strvalue[0])
-                               a = (char *)vp->strvalue;
-                       else
-                               a = ip_hostname((char *)vp->strvalue,
-                                               sizeof(vp->strvalue),
-                                               vp->lvalue);
+                       a = inet_ntop(AF_INET, &(vp->vp_ipaddr),
+                                     buf, sizeof(buf));
                        break;
                case PW_TYPE_ABINARY:
 #ifdef ASCEND_BINARY
-                 a = buf;
-                 print_abinary(vp, (unsigned char *)buf, sizeof(buf));
-                 break;
+                       a = buf;
+                       print_abinary(vp, buf, sizeof(buf));
+                       break;
 #else
                  /* FALL THROUGH */
 #endif
                case PW_TYPE_OCTETS:
-                 strcpy(buf, "0x");
-                 a = buf + 2;
-                 for (t = 0; t < vp->length; t++) {
-                       sprintf(a, "%02x", vp->strvalue[t]);
-                       a += 2;
-                 }
-                 a = buf;
+                       if (outlen <= (2 * (vp->length + 1))) return 0;
+
+                       strcpy(buf, "0x");
+
+                       fr_bin2hex(vp->vp_octets, buf + 2, vp->length);
+                       a = buf;
+                 break;
+
+               case PW_TYPE_IFID:
+                       a = ifid_ntoa(buf, sizeof(buf), vp->vp_octets);
+                       break;
+
+               case PW_TYPE_IPV6ADDR:
+                       a = inet_ntop(AF_INET6,
+                                     (const struct in6_addr *) vp->vp_strvalue,
+                                     buf, sizeof(buf));
+                       break;
+
+               case PW_TYPE_IPV6PREFIX:
+               {
+                       struct in6_addr addr;
+
+                       /*
+                        *      Alignment issues.
+                        */
+                       memcpy(&addr, vp->vp_strvalue + 2, sizeof(addr));
+
+                       a = inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
+                       if (a) {
+                               char *p = buf + strlen(buf);
+                               snprintf(p, buf + sizeof(buf) - p - 1, "/%u",
+                                        (unsigned int) vp->vp_octets[1]);
+                       }
+               }
+                       break;
+
+               case PW_TYPE_ETHERNET:
+                       snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
+                                vp->vp_ether[0], vp->vp_ether[1],
+                                vp->vp_ether[2], vp->vp_ether[3],
+                                vp->vp_ether[4], vp->vp_ether[5]);
+                       a = buf;
+                       break;
+
+               case PW_TYPE_TLV:
+                       if (outlen <= (2 * (vp->length + 1))) return 0;
+
+                       strcpy(buf, "0x");
+
+                       fr_bin2hex(vp->vp_tlv, buf + 2, vp->length);
+                       a = buf;
                  break;
 
                default:
-                       a = 0;
+                       a = "UNKNOWN-TYPE";
                        break;
        }
-       strNcpy(out, a?a:"UNKNOWN-TYPE", outlen);
-       
+
+       if (a != NULL) strlcpy(out, a, outlen);
+
        return strlen(out);
 }
 
@@ -202,7 +356,7 @@ int vp_prints_value(char * out, int outlen, VALUE_PAIR *vp, int delimitst)
  *  This is a hack, and has to be kept in sync with tokens.h
  */
 static const char *vp_tokens[] = {
-  "?",                         /* T_INVALID */
+  "?",                         /* T_OP_INVALID */
   "EOL",                       /* T_EOL */
   "{",
   "}",
@@ -222,7 +376,7 @@ static const char *vp_tokens[] = {
   "=~",
   "!~",
   "=*",
-  "~*",
+  "!*",
   "==",
   "#",
   "<BARE-WORD>",
@@ -231,23 +385,61 @@ static const char *vp_tokens[] = {
   "<`STRING`>"
 };
 
+const char *vp_print_name(char *buffer, size_t bufsize, int attr, int vendor)
+{
+       size_t len = 0;
+
+       if (!buffer) return NULL;
+
+       if (vendor) {
+               DICT_VENDOR *v;
+               
+               v = dict_vendorbyvalue(vendor);
+               if (v) {
+                       snprintf(buffer, bufsize, "%s-", v->name);
+               } else {
+                       snprintf(buffer, bufsize, "Vendor-%u-", vendor);
+               }
+
+               len = strlen(buffer);
+               if (len == bufsize) {
+                       return NULL;
+               }
+       }
+
+       snprintf(buffer + len, bufsize - len, "Attr-%u", attr & 0xffff);
+       len += strlen(buffer + len);
+       if (len == bufsize) {
+               return NULL;
+       }
+
+       return buffer;
+}
+
 
 /*
  *     Print one attribute and value into a string.
  */
-int vp_prints(char *out, int outlen, VALUE_PAIR *vp)
+int vp_prints(char *out, size_t outlen, VALUE_PAIR *vp)
 {
-       int             len;
+       size_t          len;
        const char      *token = NULL;
+       const char      *name;
+       char            namebuf[128];
 
        out[0] = 0;
        if (!vp) return 0;
 
-       if (strlen(vp->name) + 3 > (size_t)outlen) {
-               return 0;
+       name = vp->name;
+
+       if (!name || !*name) {
+               if (!vp_print_name(namebuf, sizeof(namebuf), vp->attribute, vp->attribute)) {
+                       return 0;
+               }
+               name = namebuf;
        }
 
-       if ((vp->operator > T_INVALID) &&
+       if ((vp->operator > T_OP_INVALID) &&
            (vp->operator < T_TOKEN_LAST)) {
                token = vp_tokens[vp->operator];
        } else {
@@ -255,22 +447,20 @@ int vp_prints(char *out, int outlen, VALUE_PAIR *vp)
        }
 
        if( vp->flags.has_tag ) {
+               snprintf(out, outlen, "%s:%d %s ",
+                        name, vp->flags.tag, token);
 
-               snprintf(out, outlen, "%s:%d %s ", vp->name, vp->flags.tag,
-                        token);
-               
                len = strlen(out);
                vp_prints_value(out + len, outlen - len, vp, 1);
 
        } else {
-
-               snprintf(out, outlen, "%s %s ", vp->name, token);
+               snprintf(out, outlen, "%s %s ", name, token);
                len = strlen(out);
                vp_prints_value(out + len, outlen - len, vp, 1);
 
-       }         
+       }
 
-       return strlen(out);
+       return len + strlen(out + len);
 }