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