Created SILC Runtime Toolkit git repository Part II.
[runtime.git] / lib / silcutil / silcstringprep.c
1 /*
2
3   silcstringprep.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 #include "stringprep.h"
22
23 /* We use GNU Libidn which has stringprep to do the magic.  Only bad thing
24    is that its interface is idiotic.  We have our own API here in case
25    we'll implement it ourselves later. */
26
27 /* Prohibited characters as defined by the protocol in Appendix C */
28 const Stringprep_table_element silc_appendix_c[] =
29 {
30   {0x000021}, {0x00002A}, {0x00002C}, {0x00003F}, {0x000040},
31   {0}
32 };
33
34 /* Prohibited characters as defined by the protocol in Appendix D */
35 const Stringprep_table_element silc_appendix_d[] =
36 {
37   {0x0000A2, 0x0000A9},
38   {0x0000AC}, {0x0000AE}, {0x0000AF}, {0x0000B0}, {0x0000B1}, {0x0000B4},
39   {0x0000B6}, {0x0000B8}, {0x0000D7}, {0x0000F7},
40   {0x0002C2, 0x0002C5}, {0x0002D2, 0x0002FF},
41   {0x000374}, {0x000375}, {0x000384}, {0x000385}, {0x0003F6}, {0x000482},
42   {0x00060E}, {0x00060F}, {0x0006E9}, {0x0006FD}, {0x0006FE}, {0x0009F2},
43   {0x0009F3}, {0x0009FA}, {0x000AF1}, {0x000B70},
44   {0x000BF3, 0x000BFA}, {0x000E3F},
45   {0x000F01, 0x000F03}, {0x000F13, 0x000F17}, {0x000F1A, 0x000F1F},
46   {0x000F34}, {0x000F36}, {0x000F38}, {0x000FBE}, {0x000FBF},
47   {0x000FC0, 0x000FC5}, {0x000FC7, 0x000FCF}, {0x0017DB}, {0x001940},
48   {0x0019E0, 0x0019FF}, {0x001FBD}, {0x001FBF, 0x001FC1},
49   {0x001FCD, 0x001FCF}, {0x001FDD, 0x001FDF}, {0x001FED, 0x001FEF},
50   {0x001FFD}, {0x001FFE}, {0x002044}, {0x002052}, {0x00207A, 0x00207C},
51   {0x00208A, 0x00208C}, {0x0020A0, 0x0020B1}, {0x002100, 0x00214F},
52   {0x002150, 0x00218F}, {0x002190, 0x0021FF}, {0x002200, 0x0022FF},
53   {0x002300, 0x0023FF}, {0x002400, 0x00243F}, {0x002440, 0x00245F},
54   {0x002460, 0x0024FF}, {0x002500, 0x00257F}, {0x002580, 0x00259F},
55   {0x0025A0, 0x0025FF}, {0x002600, 0x0026FF}, {0x002700, 0x0027BF},
56   {0x0027C0, 0x0027EF}, {0x0027F0, 0x0027FF}, {0x002800, 0x0028FF},
57   {0x002900, 0x00297F}, {0x002980, 0x0029FF}, {0x002A00, 0x002AFF},
58   {0x002B00, 0x002BFF}, {0x002E9A}, {0x002EF4, 0x002EFF},
59   {0x002FF0, 0x002FFF}, {0x00303B, 0x00303D}, {0x003040},
60   {0x003095, 0x003098}, {0x00309F, 0x0030A0}, {0x0030FF, 0x003104},
61   {0x00312D, 0x003130}, {0x00318F}, {0x0031B8, 0x0031FF},
62   {0x00321D, 0x00321F}, {0x003244, 0x00325F}, {0x00327C, 0x00327E},
63   {0x0032B1, 0x0032BF}, {0x0032CC, 0x0032CF}, {0x0032FF},
64   {0x003377, 0x00337A}, {0x0033DE, 0x0033DF}, {0x0033FF},
65   {0x004DB6, 0x004DFF},
66   {0x009FA6, 0x009FFF}, {0x00A48D, 0x00A48F}, {0x00A4A2, 0x00A4A3},
67   {0x00A4B4}, {0x00A4C1}, {0x00A4C5}, {0x00A4C7, 0x00ABFF},
68   {0x00D7A4, 0x00D7FF}, {0x00FA2E, 0x00FAFF}, {0x00FFE0, 0x00FFEE},
69   {0x00FFFC}, {0x010000, 0x01007F}, {0x010080, 0x0100FF},
70   {0x010100, 0x01013F}, {0x01D000, 0x01D0FF}, {0x01D100, 0x01D1FF},
71   {0x01D300, 0x01D35F}, {0x01D400, 0x01D7FF},
72   {0x0E0100, 0x0E01EF},
73   {0}
74 };
75
76 /* Default SILC Identifier String profile defined by the protocol */
77 const Stringprep_profile stringprep_silc_identifier_prep[] =
78 {
79   {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_1},
80   {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_2},
81   {STRINGPREP_NFKC, 0, 0},
82   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_1_1},
83   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_1_2},
84   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_1},
85   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_2},
86   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_3},
87   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_4},
88   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_5},
89   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_6},
90   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_7},
91   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8},
92   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_9},
93   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_9},
94   {STRINGPREP_PROHIBIT_TABLE, 0, silc_appendix_c},
95   {STRINGPREP_PROHIBIT_TABLE, 0, silc_appendix_d},
96   {STRINGPREP_UNASSIGNED_TABLE, 0, stringprep_rfc3454_A_1},
97   {0}
98 };
99
100 /* Default channel name string profile defined by the protocol */
101 const Stringprep_profile stringprep_silc_identifier_ch_prep[] =
102 {
103   {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_1},
104   {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_2},
105   {STRINGPREP_NFKC, 0, 0},
106   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_1_1},
107   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_1_2},
108   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_1},
109   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_2},
110   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_3},
111   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_4},
112   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_5},
113   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_6},
114   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_7},
115   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8},
116   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_9},
117   {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_9},
118   {STRINGPREP_PROHIBIT_TABLE, 0, silc_appendix_d},
119   {STRINGPREP_UNASSIGNED_TABLE, 0, stringprep_rfc3454_A_1},
120   {0}
121 };
122
123 /* Identifier string case folding and normalizing */
124 const Stringprep_profile stringprep_silc_identifierc_prep[] =
125 {
126   {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_1},
127   {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_2},
128   {STRINGPREP_NFKC, 0, 0},
129   {0}
130 };
131
132 /* Case folding and normalizing */
133 const Stringprep_profile stringprep_silc_casefold_prep[] =
134 {
135   {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_2},
136   {STRINGPREP_NFKC, 0, 0},
137   {0}
138 };
139
140
141 /* Prepares string according to the profile */
142
143 SilcStringprepStatus
144 silc_stringprep(const unsigned char *bin, SilcUInt32 bin_len,
145                 SilcStringEncoding bin_encoding,
146                 const char *profile_name,
147                 SilcStringprepFlags flags,
148                 unsigned char **out, SilcUInt32 *out_len,
149                 SilcStringEncoding out_encoding)
150 {
151   Stringprep_profile_flags f = 0;
152   const Stringprep_profile *profile;
153   unsigned char *utf8s;
154   SilcUInt32 utf8s_len;
155   int ret;
156
157   SILC_LOG_DEBUG(("Preparing string"));
158
159   if (!bin || !bin_len || !profile_name)
160     return SILC_STRINGPREP_ERR;
161
162   /* Convert string to UTF-8 */
163   utf8s_len = silc_utf8_encoded_len(bin, bin_len, bin_encoding);
164   if (!utf8s_len)
165     return SILC_STRINGPREP_ERR_ENCODING;
166   utf8s = silc_calloc(utf8s_len + 1, sizeof(*utf8s));
167   if (!utf8s)
168     return SILC_STRINGPREP_ERR_OUT_OF_MEMORY;
169   silc_utf8_encode(bin, bin_len, bin_encoding, utf8s, utf8s_len);
170
171   /* Check profile. */
172   if (!strcmp(profile_name, SILC_IDENTIFIER_PREP))
173     profile = stringprep_silc_identifier_prep;
174   else if (!strcmp(profile_name, SILC_IDENTIFIER_CH_PREP))
175     profile = stringprep_silc_identifier_ch_prep;
176   else if (!strcmp(profile_name, SILC_IDENTIFIERC_PREP))
177     profile = stringprep_silc_identifierc_prep;
178   else if (!strcmp(profile_name, SILC_CASEFOLD_PREP))
179     profile = stringprep_silc_casefold_prep;
180   else
181     return SILC_STRINGPREP_ERR_UNSUP_PROFILE;
182
183   /* Translate flags */
184   if (!(flags & SILC_STRINGPREP_ALLOW_UNASSIGNED))
185     f |= STRINGPREP_NO_UNASSIGNED;
186
187   /* Prepare */
188   ret = stringprep((char *)utf8s, utf8s_len, f, profile);
189   SILC_LOG_DEBUG(("stringprep() return %d", ret));
190
191   /* Since the stringprep() doesn't allocate returned buffer, and
192      stringprep_profile() doesn't do it correctly, we can't know how
193      much space we must have for the conversion.  Allocate more if it
194      fails, and try again. */
195   if (ret == STRINGPREP_TOO_SMALL_BUFFER) {
196     utf8s = silc_realloc(utf8s, sizeof(*utf8s) * ((utf8s_len * 2) + 1));
197     if (!utf8s)
198       return SILC_STRINGPREP_ERR_OUT_OF_MEMORY;
199     memset(utf8s + utf8s_len + 1, 0, utf8s_len);
200     ret = stringprep((char *)utf8s, utf8s_len * 2, f, profile);
201     SILC_LOG_DEBUG(("stringprep() return %d", ret));
202   }
203
204   switch (ret) {
205   case STRINGPREP_OK:
206     ret = SILC_STRINGPREP_OK;
207     break;
208
209   case STRINGPREP_CONTAINS_UNASSIGNED:
210     ret = SILC_STRINGPREP_ERR_UNASSIGNED;
211     break;
212
213   case STRINGPREP_CONTAINS_PROHIBITED:
214     ret = SILC_STRINGPREP_ERR_PROHIBITED;
215     break;
216
217   case STRINGPREP_BIDI_BOTH_L_AND_RAL:
218     ret = SILC_STRINGPREP_ERR_BIDI_RAL_WITH_L;
219     break;
220
221   case STRINGPREP_BIDI_LEADTRAIL_NOT_RAL:
222     ret = SILC_STRINGPREP_ERR_BIDI_RAL;
223     break;
224
225   case STRINGPREP_BIDI_CONTAINS_PROHIBITED:
226     ret = SILC_STRINGPREP_ERR_BIDI_PROHIBITED;
227     break;
228
229   case STRINGPREP_UNKNOWN_PROFILE:
230     ret = SILC_STRINGPREP_ERR_UNSUP_PROFILE;
231     break;
232
233   case STRINGPREP_MALLOC_ERROR:
234     ret = SILC_STRINGPREP_ERR_OUT_OF_MEMORY;
235     break;
236
237   default:
238     ret = SILC_STRINGPREP_ERR;
239     break;
240   }
241
242   /* Convert to desired output character encoding */
243   if (ret == SILC_STRINGPREP_OK) {
244     if (out && out_len) {
245       if (out_encoding != SILC_STRING_UTF8) {
246         *out_len = silc_utf8_decoded_len(utf8s, strlen(utf8s), out_encoding);
247         if (*out_len) {
248           *out = silc_calloc(*out_len + 1, sizeof(**out));
249           if (*out) {
250             silc_utf8_decode(utf8s, strlen(utf8s), out_encoding, *out,
251                              *out_len);
252           } else {
253             ret = SILC_STRINGPREP_ERR_OUT_OF_MEMORY;
254           }
255         } else {
256           ret = SILC_STRINGPREP_ERR_ENCODING;
257         }
258       } else {
259         *out_len = strlen(utf8s);
260         *out = silc_memdup(utf8s, *out_len);
261       }
262     }
263   }
264
265   silc_free(utf8s);
266
267   return (SilcStringprepStatus)ret;
268 }