Enrik Berkhan wrote:
- I'd add a length parameter and check for str to utf8_char().
Not needed. The C string termination character '\0' is not a valid UTF-8 character.
- line 167(?) of src/lib/print.c should read
((utf8 = utf8_char((uint8_t *)str)) == 0)) {
Yes.
- for testing with HEAD I had to disbable the special case
delimitst < 0
in vp_prints_value(). May be this can be eliminated then.
OK.
And when testing, with a simple test setup like the following, remember that the escaped stuff may show up in the debug output only, as packet values will be translated back before being sent (is this correct? At least looks so ... )
Yes. The server will send exactly what you tell it to send, even if it's garbage.
Test setup, very simple again, thanks to unlang:
<g> I like that.
(Don't know how the two strings will show up in the mail. Suppose they are valid UTF-8 and Latin1 ... :-)
They both show up, but both as UTF-8.
I think with a little fixing this can make it into HEAD and 1.1.x to be tested by others.
Please try the following patch. It's a little clearer. Alan DeKok. Index: src/lib/print.c =================================================================== RCS file: /source/radiusd/src/lib/print.c,v retrieving revision 1.49 diff -u -r1.49 print.c --- src/lib/print.c 29 May 2007 14:33:26 -0000 1.49 +++ src/lib/print.c 28 Aug 2007 08:47:28 -0000 @@ -28,28 +28,122 @@ #include <ctype.h> /* - * Convert a string to something printable. - * The output string has to be _at least_ 4x the size - * of the input string! + * Checks for utf-8, + * taken from: + * + * http://www.w3.org/International/questions/qa-forms-utf-8 + */ +static int utf8_char(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 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) { unsigned char *str = (unsigned char *)in; int done = 0; int sp = 0; + int utf8 = 0; if (inlen < 0) inlen = strlen(in); - while (inlen-- > 0 && (done + 3) < outlen) { + /* + * + */ + while ((inlen > 0) && (done + 5) < outlen) { /* * 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 == 0) && (*str == 0)) break; switch (*str) { case '\\': @@ -68,24 +162,37 @@ 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; + } + + utf8 = utf8_char((uint8_t *)str); + if (!utf8) { + snprintf(out, outlen, "\\%03o", *str); + done += 4; + out += 4; + outlen -= 4; + str++; + inlen--; + continue; } - str++; + + do { + *out++ = *str++; + outlen--; + inlen--; + done++; + } while (--utf8 > 0); } *out = 0; } @@ -122,10 +229,6 @@ vp->length, buf + 1, sizeof(buf) - 2); strcat(buf, "\""); - } else if (delimitst < 0) { - strlcpy(out, vp->vp_strvalue, outlen); - return strlen(out); - } else { /* Non-tagged attribute: no delimiter */ librad_safeprint((char *)vp->vp_strvalue, @@ -137,20 +240,21 @@ if ( vp->flags.has_tag ) { /* Attribute value has a tag, need to ignore it */ if ((v = dict_valbyattr(vp->attribute, (vp->vp_integer & 0xffffff))) - != NULL) + != NULL) { a = v->name; + } else { snprintf(buf, sizeof(buf), "%u", (vp->vp_integer & 0xffffff)); a = buf; } } else { - case PW_TYPE_BYTE: - case PW_TYPE_SHORT: + case PW_TYPE_BYTE: + case PW_TYPE_SHORT: /* Normal, non-tagged attribute */ if ((v = dict_valbyattr(vp->attribute, vp->vp_integer)) - != NULL) + != NULL) { a = v->name; - else { + }else { snprintf(buf, sizeof(buf), "%u", vp->vp_integer); a = buf; }