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