Fix.
[libidn.git] / stringprep.c
blob86fc54a507896d8528cf56901d1fd07580681486
1 /* stringprep.c Core stringprep implementation.
2 * Copyright (C) 2002, 2003 Simon Josefsson
4 * This file is part of GNU Libidn.
6 * GNU Libidn is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * GNU Libidn is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with GNU Libidn; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "internal.h"
24 static int
25 stringprep_find_character_in_table (uint32_t ucs4,
26 Stringprep_table_element * table)
28 int i;
30 for (i = 0; table[i].start; i++)
31 if (ucs4 >= table[i].start &&
32 ucs4 <= (table[i].end ? table[i].end : table[i].start))
33 return i;
35 return -1;
38 static ssize_t
39 stringprep_find_string_in_table (uint32_t *ucs4,
40 size_t ucs4len,
41 int *tablepos,
42 Stringprep_table_element * table)
44 size_t j;
45 int pos;
47 for (j = 0; j < ucs4len; j++)
48 if ((pos = stringprep_find_character_in_table (ucs4[j], table)) != -1)
50 if (tablepos)
51 *tablepos = pos;
52 return j;
55 return -1;
58 static int
59 stringprep_apply_table_to_string (uint32_t *ucs4,
60 size_t * ucs4len,
61 size_t maxucs4len,
62 Stringprep_table_element * table,
63 const char *tablename)
65 int i;
66 ssize_t pos;
67 size_t maplen;
69 while ((pos = stringprep_find_string_in_table
70 (ucs4, *ucs4len, &i, table)) != -1)
72 for (maplen = STRINGPREP_MAX_MAP_CHARS;
73 maplen > 0 && table[i].map[maplen - 1] == 0; maplen--)
76 if (*ucs4len - 1 + maplen >= maxucs4len)
77 return STRINGPREP_TOO_SMALL_BUFFER;
79 memmove (&ucs4[pos + maplen], &ucs4[pos + 1],
80 *ucs4len * sizeof (uint32_t) - (&ucs4[pos + 1] - ucs4));
81 memcpy (&ucs4[pos], table[i].map, sizeof (uint32_t) * maplen);
82 *ucs4len = *ucs4len - 1 + maplen;
85 return STRINGPREP_OK;
88 #define INVERTED(x) ((x) & ((~0UL) >> 1))
89 #define UNAPPLICAPLEFLAGS(flags, profileflags) \
90 ((!INVERTED(profileflags) && !(profileflags & flags) && profileflags) || \
91 ( INVERTED(profileflags) && (profileflags & flags)))
93 /**
94 * stringprep:
95 * @in: input/ouput array with string to prepare.
96 * @maxlen: maximum length of input/output array.
97 * @flags: optional stringprep profile flags.
98 * @profile: pointer to stringprep profile to use.
100 * Prepare the input UTF-8 string according to the stringprep profile.
101 * Normally application programmers use stringprep profile macros such
102 * as stringprep_nameprep(), stringprep_kerberos5() etc instead of
103 * calling this function directly.
105 * Since the stringprep operation can expand the string, @maxlen
106 * indicate how large the buffer holding the string is. The @flags
107 * are one of Stringprep_profile_flags, or 0. The profile indicates
108 * processing details, see the profile header files, such as
109 * stringprep_generic.h and stringprep_nameprep.h for two examples.
110 * Your application can define new profiles, possibly re-using the
111 * generic stringprep tables that always will be part of the library.
112 * Note that you must convert strings entered in the systems locale
113 * into UTF-8 before using this function.
115 * Return value: Returns 0 iff successful, or an error code.
118 stringprep (char *in, size_t maxlen, int flags, Stringprep_profile * profile)
120 int i, j;
121 int rc;
122 char *p = 0;
123 uint32_t *q = 0;
124 uint32_t *ucs4;
125 size_t ucs4len, maxucs4len;
127 ucs4 = stringprep_utf8_to_ucs4 (in, -1, &ucs4len);
128 maxucs4len = 4 * ucs4len + 10; /* XXX */
129 ucs4 = realloc (ucs4, 1 + maxucs4len * sizeof (uint32_t));
130 if (!ucs4)
132 rc = STRINGPREP_MALLOC_ERROR;
133 goto done;
136 for (i = 0; profile[i].operation; i++)
138 switch (profile[i].operation)
140 case STRINGPREP_NFKC:
141 if (UNAPPLICAPLEFLAGS (flags, profile[i].flags))
143 break;
146 if (flags & STRINGPREP_NO_NFKC && !profile[i].flags)
148 /* Profile requires NFKC, but callee asked for no NFKC. */
149 rc = STRINGPREP_FLAG_ERROR;
150 goto done;
153 q = stringprep_ucs4_nfkc_normalize (ucs4, ucs4len);
155 if (!q)
157 rc = STRINGPREP_NFKC_FAILED;
158 goto done;
161 for (j = 0; q[j]; j++)
164 free (ucs4);
165 ucs4 = q;
166 ucs4len = j;
167 q = 0;
168 break;
170 case STRINGPREP_PROHIBIT_TABLE:
171 j = stringprep_find_string_in_table (ucs4, ucs4len,
172 NULL, profile[i].table);
173 if (j != -1)
175 rc = STRINGPREP_CONTAINS_PROHIBITED;
176 goto done;
178 break;
180 case STRINGPREP_UNASSIGNED_TABLE:
181 if (UNAPPLICAPLEFLAGS (flags, profile[i].flags))
182 break;
183 if (flags & STRINGPREP_NO_UNASSIGNED)
185 j = stringprep_find_string_in_table
186 (ucs4, ucs4len, NULL, profile[i].table);
187 if (j != -1)
189 rc = STRINGPREP_CONTAINS_UNASSIGNED;
190 goto done;
193 break;
195 case STRINGPREP_MAP_TABLE:
196 if (UNAPPLICAPLEFLAGS (flags, profile[i].flags))
197 break;
198 rc = stringprep_apply_table_to_string
199 (ucs4, &ucs4len, maxucs4len, profile[i].table, profile[i].name);
200 if (rc != STRINGPREP_OK)
201 goto done;
202 break;
204 case STRINGPREP_BIDI_PROHIBIT_TABLE:
205 case STRINGPREP_BIDI_RAL_TABLE:
206 case STRINGPREP_BIDI_L_TABLE:
207 break;
209 case STRINGPREP_BIDI:
211 int done_prohibited = 0;
212 int done_ral = 0;
213 int done_l = 0;
214 int contains_ral = -1;
215 int contains_l = -1;
216 int k;
218 for (j = 0; profile[j].operation; j++)
219 if (profile[j].operation == STRINGPREP_BIDI_PROHIBIT_TABLE)
221 done_prohibited = 1;
222 k = stringprep_find_string_in_table (ucs4, ucs4len,
223 NULL,
224 profile[j].table);
225 if (k != -1)
227 rc = STRINGPREP_BIDI_CONTAINS_PROHIBITED;
228 goto done;
231 else if (profile[j].operation == STRINGPREP_BIDI_RAL_TABLE)
233 done_ral = 1;
234 if (stringprep_find_string_in_table
235 (ucs4, ucs4len, NULL, profile[j].table) != -1)
236 contains_ral = j;
238 else if (profile[j].operation == STRINGPREP_BIDI_L_TABLE)
240 done_l = 1;
241 if (stringprep_find_string_in_table
242 (ucs4, ucs4len, NULL, profile[j].table) != -1)
243 contains_l = j;
246 if (!done_prohibited || !done_ral || !done_l)
248 rc = STRINGPREP_PROFILE_ERROR;
249 goto done;
252 if (contains_ral != -1 && contains_l != -1)
254 rc = STRINGPREP_BIDI_BOTH_L_AND_RAL;
255 goto done;
258 if (contains_ral != -1)
260 if (!(stringprep_find_character_in_table
261 (ucs4[0], profile[contains_ral].table) != -1 &&
262 stringprep_find_character_in_table
263 (ucs4[ucs4len - 1], profile[contains_ral].table) != -1))
265 rc = STRINGPREP_BIDI_LEADTRAIL_NOT_RAL;
266 goto done;
270 break;
272 default:
273 rc = STRINGPREP_PROFILE_ERROR;
274 goto done;
275 break;
279 p = stringprep_ucs4_to_utf8 (ucs4, ucs4len, 0, 0);
281 if (strlen (p) >= maxlen)
283 rc = STRINGPREP_TOO_SMALL_BUFFER;
284 goto done;
287 strcpy (in, p); /* flawfinder: ignore */
289 rc = STRINGPREP_OK;
291 done:
292 if (p)
293 free (p);
294 if (q)
295 free (q);
296 if (ucs4)
297 free (ucs4);
298 return rc;
302 * stringprep_profile:
303 * @in: input/ouput array with string to prepare.
304 * @out: output variable with newly allocate string.
305 * @flags: optional stringprep profile flags.
306 * @profile: name of stringprep profile to use.
308 * Prepare the input UTF-8 string according to the stringprep profile.
309 * Normally application programmers use stringprep profile macros such
310 * as stringprep_nameprep(), stringprep_kerberos5() etc instead of
311 * calling this function directly.
313 * Note that you must convert strings entered in the systems locale
314 * into UTF-8 before using this function.
316 * The output @out variable must be deallocated by the caller.
318 * Return value: Returns 0 iff successful, or an error code.
321 stringprep_profile (char *in, char **out, char *profile, int flags)
323 Stringprep_profiles *p;
324 char *str;
325 size_t len;
326 int rc;
328 for (p = &stringprep_profiles[0]; p->name; p++)
329 if (strcmp (p->name, profile) == 0)
330 break;
332 if (!p || !p->name || !p->tables)
333 return STRINGPREP_UNKNOWN_PROFILE;
335 len = strlen (in) + BUFSIZ;
336 str = (char *) malloc (len);
337 if (str == NULL)
338 return STRINGPREP_MALLOC_ERROR;
340 strcpy (str, in);
342 rc = stringprep (str, len, flags, p->tables);
344 if (rc == STRINGPREP_OK)
345 *out = str;
346 else
347 free (str);
349 return rc;