Merge branch 'topic/mm-fixes' of git://208.110.73.182/silc into silc.1.1.branch
[silc.git] / lib / silcutil / silcutf8.c
1 /*
2
3   silcutf8.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2004 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silc.h"
21 #include "silcutf8.h"
22
23 /* Encodes the string `bin' of which encoding is `bin_encoding' to the
24    UTF-8 encoding into the buffer `utf8' which is of size of `utf8_size'.
25    Returns the length of the UTF-8 encoded string, or zero (0) on error.
26    By default `bin_encoding' is ASCII, and the caller needs to know the
27    encoding of the input string if it is anything else. */
28
29 SilcUInt32 silc_utf8_encode(const unsigned char *bin, SilcUInt32 bin_len,
30                             SilcStringEncoding bin_encoding,
31                             unsigned char *utf8, SilcUInt32 utf8_size)
32 {
33   SilcUInt32 enclen = 0, i, charval = 0;
34
35   if (!bin || !bin_len)
36     return 0;
37
38   if (bin_encoding == SILC_STRING_UTF8) {
39     if (!silc_utf8_valid(bin, bin_len))
40       return 0;
41     if (!utf8)
42       return bin_len;
43     if (bin_len > utf8_size)
44       return 0;
45     memcpy(utf8, bin, bin_len);
46     return bin_len;
47   }
48
49   /* The SILC_STRING_LDAP_DN is alredy UTF-8 but it may be escaped.  We
50      remove the escaping and we're done. */
51   if (bin_encoding == SILC_STRING_LDAP_DN ||
52       bin_encoding == SILC_STRING_UTF8_ESCAPE) {
53     unsigned char cv;
54
55     for (i = 0; i < bin_len; i++) {
56       if (bin[i] == '\\') {
57         if (i + 1 >= bin_len)
58           return 0;
59
60         /* If escaped character is any of the following no processing is
61            needed, otherwise it is a hex value and we need to read it. */
62         cv = bin[i + 1];
63         if (cv != ',' && cv != '+' && cv != '"' && cv != '\\' && cv != '<' &&
64             cv != '>' && cv != ';' && cv != ' ' && cv != '#') {
65           unsigned int hexval;
66           if (i + 2 >= bin_len)
67             return 0;
68           if (sscanf(&bin[i + 1], "%02X", &hexval) != 1)
69             return 0;
70           if (utf8) {
71             if (enclen + 1 > utf8_size)
72               return 0;
73             utf8[enclen] = (unsigned char)hexval;
74           }
75
76           i += 2;
77           enclen++;
78           continue;
79         }
80         i++;
81       }
82
83       if (utf8) {
84         if (enclen + 1 > utf8_size)
85           return 0;
86         utf8[enclen] = bin[i];
87       }
88       enclen++;
89     }
90
91     return enclen;
92   }
93
94   if (bin_encoding == SILC_STRING_LOCALE) {
95 #if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) && defined(CODESET)
96     char *fromconv, *icp, *ocp;
97     iconv_t icd;
98     size_t inlen, outlen;
99
100     setlocale(LC_CTYPE, "");
101     fromconv = nl_langinfo(CODESET);
102     if (fromconv && strlen(fromconv)) {
103       icd = iconv_open("UTF-8", fromconv);
104       icp = (char *)bin;
105       ocp = (char *)utf8;
106       inlen = bin_len;
107       outlen = utf8_size;
108       if (icp && ocp && icd != (iconv_t)-1) {
109         if (iconv(icd, &icp, &inlen, &ocp, &outlen) != -1) {
110           utf8_size -= outlen;
111           iconv_close(icd);
112           return utf8_size;
113         }
114       }
115       if (icd != (iconv_t)-1)
116         iconv_close(icd);
117     }
118 #endif
119
120     /* Fallback to 8-bit ASCII */
121     bin_encoding = SILC_STRING_ASCII;
122   }
123
124   for (i = 0; i < bin_len; i++) {
125     switch (bin_encoding) {
126     case SILC_STRING_ASCII:
127     case SILC_STRING_TELETEX:
128       charval = bin[i];
129       break;
130     case SILC_STRING_ASCII_ESC:
131       SILC_NOT_IMPLEMENTED("SILC_STRING_ASCII_ESC");
132       return 0;
133       break;
134     case SILC_STRING_BMP:
135       if (i + 1 >= bin_len)
136         return 0;
137       SILC_GET16_MSB(charval, bin + i);
138       i += 1;
139       break;
140     case SILC_STRING_BMP_LSB:
141       if (i + 1 >= bin_len)
142         return 0;
143       SILC_GET16_LSB(charval, bin + i);
144       i += 1;
145       break;
146     case SILC_STRING_UNIVERSAL:
147       if (i + 3 >= bin_len)
148         return 0;
149       SILC_GET32_MSB(charval, bin + i);
150       i += 3;
151       break;
152     case SILC_STRING_UNIVERSAL_LSB:
153       if (i + 3 >= bin_len)
154         return 0;
155       SILC_GET32_LSB(charval, bin + i);
156       i += 3;
157       break;
158     case SILC_STRING_PRINTABLE:
159     case SILC_STRING_VISIBLE:
160       if (!isprint(bin[i]))
161         return 0;
162       charval = bin[i];
163       break;
164     case SILC_STRING_NUMERICAL:
165       if (bin[i] != 0x20 && !isdigit(bin[i]))
166         return 0;
167       charval = bin[i];
168       break;
169     default:
170       return 0;
171       break;
172     }
173
174     if (charval < 0x80) {
175       if (utf8) {
176         if (enclen > utf8_size)
177           return 0;
178
179         utf8[enclen] = (unsigned char)charval;
180       }
181       enclen++;
182     } else if (charval < 0x800) {
183       if (utf8) {
184         if (enclen + 2 > utf8_size)
185           return 0;
186
187         utf8[enclen    ] = (unsigned char )(((charval >> 6)  & 0x1f) | 0xc0);
188         utf8[enclen + 1] = (unsigned char )((charval & 0x3f) | 0x80);
189       }
190       enclen += 2;
191     } else if (charval < 0x10000) {
192       if (utf8) {
193         if (enclen + 3 > utf8_size)
194           return 0;
195
196         utf8[enclen    ] = (unsigned char )(((charval >> 12) & 0xf)  | 0xe0);
197         utf8[enclen + 1] = (unsigned char )(((charval >> 6)  & 0x3f) | 0x80);
198         utf8[enclen + 2] = (unsigned char )((charval & 0x3f) | 0x80);
199       }
200       enclen += 3;
201     } else if (charval < 0x200000) {
202       if (utf8) {
203         if (enclen + 4 > utf8_size)
204           return 0;
205
206         utf8[enclen    ] = (unsigned char )(((charval >> 18) & 0x7)  | 0xf0);
207         utf8[enclen + 1] = (unsigned char )(((charval >> 12) & 0x3f) | 0x80);
208         utf8[enclen + 2] = (unsigned char )(((charval >> 6)  & 0x3f) | 0x80);
209         utf8[enclen + 3] = (unsigned char )((charval & 0x3f) | 0x80);
210       }
211       enclen += 4;
212     } else if (charval < 0x4000000) {
213       if (utf8) {
214         if (enclen + 5 > utf8_size)
215           return 0;
216
217         utf8[enclen    ] = (unsigned char )(((charval >> 24) & 0x3)  | 0xf8);
218         utf8[enclen + 1] = (unsigned char )(((charval >> 18) & 0x3f) | 0x80);
219         utf8[enclen + 2] = (unsigned char )(((charval >> 12) & 0x3f) | 0x80);
220         utf8[enclen + 3] = (unsigned char )(((charval >> 6)  & 0x3f) | 0x80);
221         utf8[enclen + 4] = (unsigned char )((charval & 0x3f) | 0x80);
222       }
223       enclen += 5;
224     } else {
225       if (utf8) {
226         if (enclen + 6 > utf8_size)
227           return 0;
228
229         utf8[enclen    ] = (unsigned char )(((charval >> 30) & 0x1)  | 0xfc);
230         utf8[enclen + 1] = (unsigned char )(((charval >> 24) & 0x3f) | 0x80);
231         utf8[enclen + 2] = (unsigned char )(((charval >> 18) & 0x3f) | 0x80);
232         utf8[enclen + 3] = (unsigned char )(((charval >> 12) & 0x3f) | 0x80);
233         utf8[enclen + 4] = (unsigned char )(((charval >> 6)  & 0x3f) | 0x80);
234         utf8[enclen + 5] = (unsigned char )((charval & 0x3f) | 0x80);
235       }
236       enclen += 6;
237     }
238   }
239
240   return enclen;
241 }
242
243 /* Decodes UTF-8 encoded string `utf8' to string of which encoding is
244    to be `bin_encoding', into the `bin' buffer of size of `bin_size'.
245    Returns the length of the decoded buffer, or zero (0) on error.
246    By default `bin_encoding' is ASCII, and the caller needs to know to
247    which encoding the output string is to be encoded if ASCII is not
248    desired. */
249
250 SilcUInt32 silc_utf8_decode(const unsigned char *utf8, SilcUInt32 utf8_len,
251                             SilcStringEncoding bin_encoding,
252                             unsigned char *bin, SilcUInt32 bin_size)
253 {
254   SilcUInt32 enclen = 0, i, charval, bytes;
255
256   if (!utf8 || !utf8_len)
257     return 0;
258
259   if (bin_encoding == SILC_STRING_UTF8) {
260     if (!silc_utf8_valid(utf8, utf8_len) ||
261         utf8_len > bin_size)
262       return 0;
263     memcpy(bin, utf8, utf8_len);
264     return utf8_len;
265   }
266
267   if (bin_encoding == SILC_STRING_LOCALE) {
268 #if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) && defined(CODESET)
269     char *toconv, *icp, *ocp;
270     iconv_t icd;
271     size_t inlen, outlen;
272
273     setlocale(LC_CTYPE, "");
274     toconv = nl_langinfo(CODESET);
275     if (toconv && strlen(toconv)) {
276       icd = iconv_open(toconv, "UTF-8");
277       icp = (char *)utf8;
278       ocp = (char *)bin;
279       inlen = utf8_len;
280       outlen = bin_size;
281       if (icp && ocp && icd != (iconv_t)-1) {
282         if (iconv(icd, &icp, &inlen, &ocp, &outlen) != -1) {
283           bin_size -= outlen;
284           iconv_close(icd);
285           return bin_size;
286         }
287       }
288       if (icd != (iconv_t)-1)
289         iconv_close(icd);
290     }
291 #endif
292
293     /* Fallback to 8-bit ASCII */
294     bin_encoding = SILC_STRING_ASCII;
295   }
296
297   for (i = 0; i < utf8_len; i++) {
298     if ((utf8[i] & 0x80) == 0x00) {
299       charval = utf8[i] & 0x7f;
300       bytes = 1;
301     } else if ((utf8[i] & 0xe0) == 0xc0) {
302       if (i + 1 >= utf8_len)
303         return 0;
304
305       if ((utf8[i + 1] & 0xc0) != 0x80)
306         return 0;
307
308       charval = (utf8[i++] & 0x1f) << 6;
309       charval |= utf8[i] & 0x3f;
310       if (charval < 0x80)
311         return 0;
312       bytes = 2;
313     } else if ((utf8[i] & 0xf0) == 0xe0) {
314       if (i + 2 >= utf8_len)
315         return 0;
316
317       if (((utf8[i + 1] & 0xc0) != 0x80) ||
318           ((utf8[i + 2] & 0xc0) != 0x80))
319         return 0;
320
321       /* Surrogates not allowed (D800-DFFF) */
322       if (utf8[i] == 0xed &&
323           utf8[i + 1] >= 0xa0 && utf8[i + 1] <= 0xbf &&
324           utf8[i + 2] >= 0x80 && utf8[i + 2] <= 0xbf)
325         return 0;
326
327       charval = (utf8[i++]  & 0xf)  << 12;
328       charval |= (utf8[i++] & 0x3f) << 6;
329       charval |= utf8[i] & 0x3f;
330       if (charval < 0x800)
331         return 0;
332       bytes = 3;
333     } else if ((utf8[i] & 0xf8) == 0xf0) {
334       if (i + 3 >= utf8_len)
335         return 0;
336
337       if (((utf8[i + 1] & 0xc0) != 0x80) ||
338           ((utf8[i + 2] & 0xc0) != 0x80) ||
339           ((utf8[i + 3] & 0xc0) != 0x80))
340         return 0;
341
342       charval = ((SilcUInt32)(utf8[i++] & 0x7)) << 18;
343       charval |= (utf8[i++] & 0x3f) << 12;
344       charval |= (utf8[i++] & 0x3f) << 6;
345       charval |= utf8[i] & 0x3f;
346       if (charval < 0x10000)
347         return 0;
348       bytes = 4;
349     } else if ((utf8[i] & 0xfc) == 0xf8) {
350       if (i + 4 >= utf8_len)
351         return 0;
352
353       if (((utf8[i + 1] & 0xc0) != 0x80) ||
354           ((utf8[i + 2] & 0xc0) != 0x80) ||
355           ((utf8[i + 3] & 0xc0) != 0x80) ||
356           ((utf8[i + 4] & 0xc0) != 0x80))
357         return 0;
358
359       charval = ((SilcUInt32)(utf8[i++]  & 0x3))  << 24;
360       charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 18;
361       charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 12;
362       charval |= (utf8[i++] & 0x3f) << 6;
363       charval |= utf8[i] & 0x3f;
364       if (charval < 0x200000)
365         return 0;
366       bytes = 5;
367     } else if ((utf8[i] & 0xfe) == 0xfc) {
368       if (i + 5 >= utf8_len)
369         return 0;
370
371       if (((utf8[i + 1] & 0xc0) != 0x80) ||
372           ((utf8[i + 2] & 0xc0) != 0x80) ||
373           ((utf8[i + 3] & 0xc0) != 0x80) ||
374           ((utf8[i + 4] & 0xc0) != 0x80) ||
375           ((utf8[i + 5] & 0xc0) != 0x80))
376         return 0;
377
378       charval = ((SilcUInt32)(utf8[i++]  & 0x1))  << 30;
379       charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 24;
380       charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 18;
381       charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 12;
382       charval |= (utf8[i++] & 0x3f) << 6;
383       charval |= utf8[i] & 0x3f;
384       if (charval < 0x4000000)
385         return 0;
386       bytes = 6;
387     } else {
388       return 0;
389     }
390
391     switch (bin_encoding) {
392     case SILC_STRING_ASCII:
393     case SILC_STRING_PRINTABLE:
394     case SILC_STRING_VISIBLE:
395     case SILC_STRING_TELETEX:
396     case SILC_STRING_NUMERICAL:
397       if (bin) {
398         if (enclen + 1 > bin_size)
399           return 0;
400
401         bin[enclen] = (unsigned char)charval;
402       }
403       enclen++;
404       break;
405     case SILC_STRING_ASCII_ESC:
406       SILC_NOT_IMPLEMENTED("SILC_STRING_ASCII_ESC");
407       return 0;
408       break;
409     case SILC_STRING_BMP:
410       if (bin) {
411         if (enclen + 2 > bin_size)
412           return 0;
413         SILC_PUT16_MSB(charval, bin + enclen);
414       }
415       enclen += 2;
416       break;
417     case SILC_STRING_BMP_LSB:
418       if (bin) {
419         if (enclen + 2 > bin_size)
420           return 0;
421         SILC_PUT16_LSB(charval, bin + enclen);
422       }
423       enclen += 2;
424       break;
425     case SILC_STRING_UNIVERSAL:
426       if (bin) {
427         if (enclen + 4 > bin_size)
428           return 0;
429         SILC_PUT32_MSB(charval, bin + enclen);
430       }
431       enclen += 4;
432       break;
433     case SILC_STRING_UNIVERSAL_LSB:
434       if (bin) {
435         if (enclen + 4 > bin_size)
436           return 0;
437         SILC_PUT32_LSB(charval, bin + enclen);
438       }
439       enclen += 4;
440       break;
441     case SILC_STRING_LDAP_DN:
442       {
443         int k;
444         unsigned char cv;
445
446         /* Non-printable UTF-8 characters will be escaped, printable will
447            be as is.  We take the bytes directly from the original data. */
448         for (k = 0; k < bytes; k++) {
449           cv = utf8[(i - (bytes - 1)) + k];
450
451           /* If string starts with space or # escape it */
452           if (!enclen && (cv == '#' || cv == ' ')) {
453             if (bin) {
454               if (enclen + 2 > bin_size)
455                 return 0;
456               bin[enclen] = '\\';
457               bin[enclen + 1] = cv;
458             }
459             enclen += 2;
460             continue;
461           }
462
463           /* If string ends with space escape it */
464           if (i == utf8_len - 1 && cv == ' ') {
465             if (bin) {
466               if (enclen + 2 > bin_size)
467                 return 0;
468               bin[enclen] = '\\';
469               bin[enclen + 1] = cv;
470             }
471             enclen += 2;
472             continue;
473           }
474
475           /* If character is any of following then escape */
476           if (cv == ',' || cv == '+' || cv == '"' || cv == '\\' || cv == '<' ||
477               cv == '>' || cv == ';') {
478             if (bin) {
479               if (enclen + 2 > bin_size)
480                 return 0;
481               bin[enclen] = '\\';
482               bin[enclen + 1] = cv;
483             }
484             enclen += 2;
485             continue;
486           }
487
488           /* If character is not printable escape it with hex character */
489           if (!isprint((int)cv)) {
490             if (bin) {
491               if (enclen + 3 > bin_size)
492                 return 0;
493               bin[enclen] = '\\';
494               silc_snprintf(bin + enclen + 1, 3, "%02X", cv);
495             }
496             enclen += 3;
497             continue;
498           }
499
500           if (bin) {
501             if (enclen + 1 > bin_size)
502               return 0;
503             bin[enclen] = cv;
504           }
505           enclen++;
506         }
507       }
508       break;
509     default:
510       return 0;
511       break;
512     }
513   }
514
515   return enclen;
516 }
517
518 /* UTF-8 to wide characters */
519
520 SilcUInt32 silc_utf8_c2w(const unsigned char *utf8, SilcUInt32 utf8_len,
521                          SilcUInt16 *utf8_wide, SilcUInt32 utf8_wide_size)
522 {
523   unsigned char *tmp;
524   SilcUInt32 tmp_len;
525   int i, k;
526
527   tmp_len = silc_utf8_decoded_len(utf8, utf8_len, SILC_STRING_BMP);
528   if (!tmp_len)
529     return 0;
530
531   if (utf8_wide_size < tmp_len / 2)
532     return 0;
533
534   memset(utf8_wide, 0, utf8_wide_size * 2);
535
536   tmp = silc_malloc(tmp_len);
537   if (!tmp)
538     return 0;
539
540   silc_utf8_decode(utf8, utf8_len, SILC_STRING_BMP, tmp, tmp_len);
541
542   for (i = 0, k = 0; i < tmp_len; i += 2, k++)
543     SILC_GET16_MSB(utf8_wide[k], tmp + i);
544
545   silc_free(tmp);
546   return k + 1;
547 }
548
549 /* Wide characters to UTF-8 */
550
551 SilcUInt32 silc_utf8_w2c(const SilcUInt16 *wide_str,
552                          SilcUInt32 wide_str_len,
553                          unsigned char *utf8, SilcUInt32 utf8_size)
554
555 {
556   unsigned char *tmp;
557   SilcUInt32 tmp_len;
558   int i, k;
559
560   if (utf8_size < wide_str_len * 2)
561     return 0;
562
563   memset(utf8, 0, utf8_size);
564
565   tmp = silc_malloc(wide_str_len * 2);
566   if (!tmp)
567     return 0;
568
569   for (i = 0, k = 0; i < wide_str_len; i += 2, k++)
570     SILC_PUT16_MSB(wide_str[k], tmp + i);
571
572   tmp_len = silc_utf8_encode(tmp, wide_str_len * 2, SILC_STRING_BMP,
573                              utf8, utf8_size);
574
575   silc_free(tmp);
576   return tmp_len;
577 }
578
579 /* Returns the length of UTF-8 encoded string if the `bin' of
580    encoding of `bin_encoding' is encoded with silc_utf8_encode. */
581
582 SilcUInt32 silc_utf8_encoded_len(const unsigned char *bin, SilcUInt32 bin_len,
583                                  SilcStringEncoding bin_encoding)
584 {
585   return silc_utf8_encode(bin, bin_len, bin_encoding, NULL, 0);
586 }
587
588 /* Returns the length of decoded string if the `bin' of encoding of
589    `bin_encoding' is decoded with silc_utf8_decode. */
590
591 SilcUInt32 silc_utf8_decoded_len(const unsigned char *bin, SilcUInt32 bin_len,
592                                  SilcStringEncoding bin_encoding)
593 {
594   return silc_utf8_decode(bin, bin_len, bin_encoding, NULL, 0);
595 }
596
597 /* Returns TRUE if the `utf8' string of length of `utf8_len' is valid
598    UTF-8 encoded string, FALSE if it is not UTF-8 encoded string. */
599
600 SilcBool silc_utf8_valid(const unsigned char *utf8, SilcUInt32 utf8_len)
601 {
602   return silc_utf8_decode(utf8, utf8_len, 0, NULL, 0) != 0;
603 }
604
605 /* Pretty close strcasecmp */
606
607 SilcBool silc_utf8_strcasecmp(const char *s1, const char *s2)
608 {
609   if (s1 == s2)
610     return TRUE;
611   if (strlen(s1) != strlen(s2))
612     return FALSE;
613
614   return silc_utf8_strncasecmp(s1, s2, strlen(s1));
615 }
616
617 /* Pretty close strcasecmp */
618
619 SilcBool silc_utf8_strncasecmp(const char *s1, const char *s2, SilcUInt32 n)
620 {
621   unsigned char *s1u, *s2u;
622   SilcUInt32 s1u_len, s2u_len;
623   SilcStringprepStatus status;
624   SilcBool ret;
625
626   if (s1 == s2)
627     return TRUE;
628
629   /* Casefold and normalize */
630   status = silc_stringprep(s1, n, SILC_STRING_UTF8,
631                            SILC_IDENTIFIERC_PREP, 0, &s1u,
632                            &s1u_len, SILC_STRING_UTF8);
633   if (status != SILC_STRINGPREP_OK)
634     return FALSE;
635
636   /* Casefold and normalize */
637   status = silc_stringprep(s2, n, SILC_STRING_UTF8,
638                            SILC_IDENTIFIERC_PREP, 0, &s2u,
639                            &s2u_len, SILC_STRING_UTF8);
640   if (status != SILC_STRINGPREP_OK)
641     return FALSE;
642
643   ret = !memcmp(s1u, s2u, n);
644
645   silc_free(s1u);
646   silc_free(s2u);
647
648   return ret;
649 }