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