cd54420e8e2656268a1556c455c51a73992d7a72
[sfrench/samba-autobuild/.git] / lib / util / charset / codepoints.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Character set conversion Extensions
4    Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001
5    Copyright (C) Andrew Tridgell 2001
6    Copyright (C) Simo Sorce 2001
7    Copyright (C) Jelmer Vernooij 2007
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; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22 */
23 #include "includes.h"
24 #include "lib/util/charset/charset.h"
25 #include "system/locale.h"
26 #include "dynconfig.h"
27
28 #ifdef strcasecmp
29 #undef strcasecmp
30 #endif
31
32 /**
33  * @file
34  * @brief Unicode string manipulation
35  */
36
37 /* these 2 tables define the unicode case handling.  They are loaded
38    at startup either via mmap() or read() from the lib directory */
39 static void *upcase_table;
40 static void *lowcase_table;
41
42
43 /*******************************************************************
44 load the case handling tables
45
46 This is the function that should be called from library code.
47 ********************************************************************/
48 void load_case_tables_library(void)
49 {
50         TALLOC_CTX *mem_ctx;
51
52         mem_ctx = talloc_init("load_case_tables");
53         if (!mem_ctx) {
54                 smb_panic("No memory for case_tables");
55         }
56         upcase_table = map_file(talloc_asprintf(mem_ctx, "%s/upcase.dat", get_dyn_CODEPAGEDIR()), 0x20000);
57         lowcase_table = map_file(talloc_asprintf(mem_ctx, "%s/lowcase.dat", get_dyn_CODEPAGEDIR()), 0x20000);
58         talloc_free(mem_ctx);
59         if (upcase_table == NULL) {
60                 DEBUG(1, ("Failed to load upcase.dat, will use lame ASCII-only case sensitivity rules\n"));
61                 upcase_table = (void *)-1;
62         }
63         if (lowcase_table == NULL) {
64                 DEBUG(1, ("Failed to load lowcase.dat, will use lame ASCII-only case sensitivity rules\n"));
65                 lowcase_table = (void *)-1;
66         }
67 }
68
69 /*******************************************************************
70 load the case handling tables
71
72 This MUST only be called from main() in application code, never from a
73 library.  We don't know if the calling program has already done
74 setlocale() to another value, and can't tell if they have.
75 ********************************************************************/
76 void load_case_tables(void)
77 {
78         /* This is a useful global hook where we can ensure that the
79          * locale is set from the environment.  This is needed so that
80          * we can use LOCALE as a codepage */
81 #ifdef HAVE_SETLOCALE
82         setlocale(LC_ALL, "");
83 #endif
84         load_case_tables_library();
85 }
86
87 /**
88  Convert a codepoint_t to upper case.
89 **/
90 _PUBLIC_ codepoint_t toupper_m(codepoint_t val)
91 {
92         if (val < 128) {
93                 return toupper(val);
94         }
95         if (upcase_table == NULL) {
96                 load_case_tables_library();
97         }
98         if (upcase_table == (void *)-1) {
99                 return val;
100         }
101         if (val & 0xFFFF0000) {
102                 return val;
103         }
104         return SVAL(upcase_table, val*2);
105 }
106
107 /**
108  Convert a codepoint_t to lower case.
109 **/
110 _PUBLIC_ codepoint_t tolower_m(codepoint_t val)
111 {
112         if (val < 128) {
113                 return tolower(val);
114         }
115         if (lowcase_table == NULL) {
116                 load_case_tables_library();
117         }
118         if (lowcase_table == (void *)-1) {
119                 return val;
120         }
121         if (val & 0xFFFF0000) {
122                 return val;
123         }
124         return SVAL(lowcase_table, val*2);
125 }
126
127 /**
128  If we upper cased this character, would we get the same character?
129 **/
130 _PUBLIC_ bool islower_m(codepoint_t val)
131 {
132         return (toupper_m(val) != val);
133 }
134
135 /**
136  If we lower cased this character, would we get the same character?
137 **/
138 _PUBLIC_ bool isupper_m(codepoint_t val)
139 {
140         return (tolower_m(val) != val);
141 }
142
143 /**
144   compare two codepoints case insensitively
145 */
146 _PUBLIC_ int codepoint_cmpi(codepoint_t c1, codepoint_t c2)
147 {
148         if (c1 == c2 ||
149             toupper_m(c1) == toupper_m(c2)) {
150                 return 0;
151         }
152         return c1 - c2;
153 }
154
155
156 struct smb_iconv_handle {
157         TALLOC_CTX *child_ctx;
158         const char *unix_charset;
159         const char *dos_charset;
160         const char *display_charset;
161         bool native_iconv;
162         smb_iconv_t conv_handles[NUM_CHARSETS][NUM_CHARSETS];
163 };
164
165 struct smb_iconv_handle *global_iconv_handle = NULL;
166
167 struct smb_iconv_handle *get_iconv_handle(void)
168 {
169         if (global_iconv_handle == NULL)
170                 global_iconv_handle = smb_iconv_handle_reinit(talloc_autofree_context(),
171                                                                         "ASCII", "UTF-8", "ASCII", true, NULL);
172         return global_iconv_handle;
173 }
174
175 struct smb_iconv_handle *get_iconv_testing_handle(TALLOC_CTX *mem_ctx, 
176                                                   const char *dos_charset, 
177                                                   const char *unix_charset, 
178                                                   const char *display_charset)
179 {
180         return smb_iconv_handle_reinit(mem_ctx,
181                                        dos_charset, unix_charset, display_charset, true, NULL);
182 }
183
184 /**
185  * Return the name of a charset to give to iconv().
186  **/
187 const char *charset_name(struct smb_iconv_handle *ic, charset_t ch)
188 {
189         switch (ch) {
190         case CH_UTF16: return "UTF-16LE";
191         case CH_UNIX: return ic->unix_charset;
192         case CH_DOS: return ic->dos_charset;
193         case CH_DISPLAY: return ic->display_charset;
194         case CH_UTF8: return "UTF8";
195         case CH_UTF16BE: return "UTF-16BE";
196         case CH_UTF16MUNGED: return "UTF16_MUNGED";
197         default:
198         return "ASCII";
199         }
200 }
201
202 /**
203  re-initialize iconv conversion descriptors
204 **/
205 static int close_iconv_handle(struct smb_iconv_handle *data)
206 {
207         unsigned c1, c2;
208         for (c1=0;c1<NUM_CHARSETS;c1++) {
209                 for (c2=0;c2<NUM_CHARSETS;c2++) {
210                         if (data->conv_handles[c1][c2] != NULL) {
211                                 if (data->conv_handles[c1][c2] != (smb_iconv_t)-1) {
212                                         smb_iconv_close(data->conv_handles[c1][c2]);
213                                 }
214                                 data->conv_handles[c1][c2] = NULL;
215                         }
216                 }
217         }
218
219         return 0;
220 }
221
222 static const char *map_locale(const char *charset)
223 {
224         if (strcmp(charset, "LOCALE") != 0) {
225                 return charset;
226         }
227 #if defined(HAVE_NL_LANGINFO) && defined(CODESET)
228         {
229                 const char *ln;
230                 smb_iconv_t handle;
231
232                 ln = nl_langinfo(CODESET);
233                 if (ln == NULL) {
234                         DEBUG(1,("Unable to determine charset for LOCALE - using ASCII\n"));
235                         return "ASCII";
236                 }
237                 /* Check whether the charset name is supported
238                    by iconv */
239                 handle = smb_iconv_open(ln, "UCS-2LE");
240                 if (handle == (smb_iconv_t) -1) {
241                         DEBUG(5,("Locale charset '%s' unsupported, using ASCII instead\n", ln));
242                         return "ASCII";
243                 } else {
244                         DEBUG(5,("Substituting charset '%s' for LOCALE\n", ln));
245                         smb_iconv_close(handle);
246                 }
247                 return ln;
248         }
249 #endif
250         return "ASCII";
251 }
252
253 /*
254   the old_ic is passed in here as the smb_iconv_handle structure
255   is used as a global pointer in some places (eg. python modules). We
256   don't want to invalidate those global pointers, but we do want to
257   update them with the right charset information when loadparm
258   runs. To do that we need to re-use the structure pointer, but
259   re-fill the elements in the structure with the updated values
260  */
261 _PUBLIC_ struct smb_iconv_handle *smb_iconv_handle_reinit(TALLOC_CTX *mem_ctx,
262                                                                     const char *dos_charset,
263                                                                     const char *unix_charset,
264                                                                     const char *display_charset,
265                                                                     bool native_iconv,
266                                                                     struct smb_iconv_handle *old_ic)
267 {
268         struct smb_iconv_handle *ret;
269
270         display_charset = map_locale(display_charset);
271
272         if (old_ic != NULL) {
273                 ret = old_ic;
274                 close_iconv_handle(ret);
275                 talloc_free(ret->child_ctx);
276                 ZERO_STRUCTP(ret);
277         } else {
278                 ret = talloc_zero(mem_ctx, struct smb_iconv_handle);
279         }
280         if (ret == NULL) {
281                 return NULL;
282         }
283
284         /* we use a child context to allow us to free all ptrs without
285            freeing the structure itself */
286         ret->child_ctx = talloc_new(ret);
287         if (ret->child_ctx == NULL) {
288                 return NULL;
289         }
290
291         talloc_set_destructor(ret, close_iconv_handle);
292
293         ret->dos_charset = talloc_strdup(ret->child_ctx, dos_charset);
294         ret->unix_charset = talloc_strdup(ret->child_ctx, unix_charset);
295         ret->display_charset = talloc_strdup(ret->child_ctx, display_charset);
296         ret->native_iconv = native_iconv;
297
298         return ret;
299 }
300
301 /*
302   on-demand initialisation of conversion handles
303 */
304 smb_iconv_t get_conv_handle(struct smb_iconv_handle *ic,
305                             charset_t from, charset_t to)
306 {
307         const char *n1, *n2;
308
309         if (ic->conv_handles[from][to]) {
310                 return ic->conv_handles[from][to];
311         }
312
313         n1 = charset_name(ic, from);
314         n2 = charset_name(ic, to);
315
316         ic->conv_handles[from][to] = smb_iconv_open_ex(ic, n2, n1,
317                                                        ic->native_iconv);
318
319         if (ic->conv_handles[from][to] == (smb_iconv_t)-1) {
320                 if ((from == CH_DOS || to == CH_DOS) &&
321                     strcasecmp(charset_name(ic, CH_DOS), "ASCII") != 0) {
322                         DEBUG(0,("dos charset '%s' unavailable - using ASCII\n",
323                                  charset_name(ic, CH_DOS)));
324                         ic->dos_charset = "ASCII";
325
326                         n1 = charset_name(ic, from);
327                         n2 = charset_name(ic, to);
328
329                         ic->conv_handles[from][to] =
330                                 smb_iconv_open_ex(ic, n2, n1, ic->native_iconv);
331                 }
332         }
333
334         return ic->conv_handles[from][to];
335 }
336
337 /**
338  * Return the unicode codepoint for the next character in the input
339  * string in the given src_charset.
340  * The unicode codepoint (codepoint_t) is an unsinged 32 bit value.
341  *
342  * Also return the number of bytes consumed (which tells the caller
343  * how many bytes to skip to get to the next src_charset-character).
344  *
345  * This is implemented (in the non-ascii-case) by first converting the
346  * next character in the input string to UTF16_LE and then calculating
347  * the unicode codepoint from that.
348  *
349  * Return INVALID_CODEPOINT if the next character cannot be converted.
350  */
351 _PUBLIC_ codepoint_t next_codepoint_handle_ext(
352                         struct smb_iconv_handle *ic,
353                         const char *str, charset_t src_charset,
354                         size_t *bytes_consumed)
355 {
356         /* it cannot occupy more than 4 bytes in UTF16 format */
357         uint8_t buf[4];
358         smb_iconv_t descriptor;
359         size_t ilen_orig;
360         size_t ilen;
361         size_t olen;
362         char *outbuf;
363
364         if ((str[0] & 0x80) == 0) {
365                 *bytes_consumed = 1;
366                 return (codepoint_t)str[0];
367         }
368
369         /*
370          * we assume that no multi-byte character can take more than 5 bytes.
371          * This is OK as we only support codepoints up to 1M (U+100000)
372          */
373         ilen_orig = strnlen(str, 5);
374         ilen = ilen_orig;
375
376         descriptor = get_conv_handle(ic, src_charset, CH_UTF16);
377         if (descriptor == (smb_iconv_t)-1) {
378                 *bytes_consumed = 1;
379                 return INVALID_CODEPOINT;
380         }
381
382         /*
383          * this looks a little strange, but it is needed to cope with
384          * codepoints above 64k (U+1000) which are encoded as per RFC2781.
385          */
386         olen = 2;
387         outbuf = (char *)buf;
388         smb_iconv(descriptor, &str, &ilen, &outbuf, &olen);
389         if (olen == 2) {
390                 olen = 4;
391                 outbuf = (char *)buf;
392                 smb_iconv(descriptor,  &str, &ilen, &outbuf, &olen);
393                 if (olen == 4) {
394                         /* we didn't convert any bytes */
395                         *bytes_consumed = 1;
396                         return INVALID_CODEPOINT;
397                 }
398                 olen = 4 - olen;
399         } else {
400                 olen = 2 - olen;
401         }
402
403         *bytes_consumed = ilen_orig - ilen;
404
405         if (olen == 2) {
406                 return (codepoint_t)SVAL(buf, 0);
407         }
408         if (olen == 4) {
409                 /* decode a 4 byte UTF16 character manually */
410                 return (codepoint_t)0x10000 +
411                         (buf[2] | ((buf[3] & 0x3)<<8) |
412                          (buf[0]<<10) | ((buf[1] & 0x3)<<18));
413         }
414
415         /* no other length is valid */
416         return INVALID_CODEPOINT;
417 }
418
419 /*
420   return the unicode codepoint for the next multi-byte CH_UNIX character
421   in the string
422
423   also return the number of bytes consumed (which tells the caller
424   how many bytes to skip to get to the next CH_UNIX character)
425
426   return INVALID_CODEPOINT if the next character cannot be converted
427 */
428 _PUBLIC_ codepoint_t next_codepoint_handle(struct smb_iconv_handle *ic,
429                                     const char *str, size_t *size)
430 {
431         return next_codepoint_handle_ext(ic, str, CH_UNIX, size);
432 }
433
434 /*
435   push a single codepoint into a CH_UNIX string the target string must
436   be able to hold the full character, which is guaranteed if it is at
437   least 5 bytes in size. The caller may pass less than 5 bytes if they
438   are sure the character will fit (for example, you can assume that
439   uppercase/lowercase of a character will not add more than 1 byte)
440
441   return the number of bytes occupied by the CH_UNIX character, or
442   -1 on failure
443 */
444 _PUBLIC_ ssize_t push_codepoint_handle(struct smb_iconv_handle *ic,
445                                 char *str, codepoint_t c)
446 {
447         smb_iconv_t descriptor;
448         uint8_t buf[4];
449         size_t ilen, olen;
450         const char *inbuf;
451
452         if (c < 128) {
453                 *str = c;
454                 return 1;
455         }
456
457         descriptor = get_conv_handle(ic,
458                                      CH_UTF16, CH_UNIX);
459         if (descriptor == (smb_iconv_t)-1) {
460                 return -1;
461         }
462
463         if (c < 0x10000) {
464                 ilen = 2;
465                 olen = 5;
466                 inbuf = (char *)buf;
467                 SSVAL(buf, 0, c);
468                 smb_iconv(descriptor, &inbuf, &ilen, &str, &olen);
469                 if (ilen != 0) {
470                         return -1;
471                 }
472                 return 5 - olen;
473         }
474
475         c -= 0x10000;
476
477         buf[0] = (c>>10) & 0xFF;
478         buf[1] = (c>>18) | 0xd8;
479         buf[2] = c & 0xFF;
480         buf[3] = ((c>>8) & 0x3) | 0xdc;
481
482         ilen = 4;
483         olen = 5;
484         inbuf = (char *)buf;
485
486         smb_iconv(descriptor, &inbuf, &ilen, &str, &olen);
487         if (ilen != 0) {
488                 return -1;
489         }
490         return 5 - olen;
491 }
492
493 _PUBLIC_ codepoint_t next_codepoint_ext(const char *str, charset_t src_charset,
494                                         size_t *size)
495 {
496         return next_codepoint_handle_ext(get_iconv_handle(), str,
497                                               src_charset, size);
498 }
499
500 _PUBLIC_ codepoint_t next_codepoint(const char *str, size_t *size)
501 {
502         return next_codepoint_handle(get_iconv_handle(), str, size);
503 }
504
505 _PUBLIC_ ssize_t push_codepoint(char *str, codepoint_t c)
506 {
507         return push_codepoint_handle(get_iconv_handle(), str, c);
508 }