lib/util/charset: Remove autodetection of charset from LOCALE
[samba.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/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", 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 {
179         return smb_iconv_handle_reinit(mem_ctx,
180                                        dos_charset, unix_charset, true, NULL);
181 }
182
183 /**
184  * Return the name of a charset to give to iconv().
185  **/
186 const char *charset_name(struct smb_iconv_handle *ic, charset_t ch)
187 {
188         switch (ch) {
189         case CH_UTF16: return "UTF-16LE";
190         case CH_UNIX: return ic->unix_charset;
191         case CH_DOS: return ic->dos_charset;
192         case CH_UTF8: return "UTF8";
193         case CH_UTF16BE: return "UTF-16BE";
194         case CH_UTF16MUNGED: return "UTF16_MUNGED";
195         default:
196         return "ASCII";
197         }
198 }
199
200 /**
201  re-initialize iconv conversion descriptors
202 **/
203 static int close_iconv_handle(struct smb_iconv_handle *data)
204 {
205         unsigned c1, c2;
206         for (c1=0;c1<NUM_CHARSETS;c1++) {
207                 for (c2=0;c2<NUM_CHARSETS;c2++) {
208                         if (data->conv_handles[c1][c2] != NULL) {
209                                 if (data->conv_handles[c1][c2] != (smb_iconv_t)-1) {
210                                         smb_iconv_close(data->conv_handles[c1][c2]);
211                                 }
212                                 data->conv_handles[c1][c2] = NULL;
213                         }
214                 }
215         }
216
217         return 0;
218 }
219
220 /*
221   the old_ic is passed in here as the smb_iconv_handle structure
222   is used as a global pointer in some places (eg. python modules). We
223   don't want to invalidate those global pointers, but we do want to
224   update them with the right charset information when loadparm
225   runs. To do that we need to re-use the structure pointer, but
226   re-fill the elements in the structure with the updated values
227  */
228 _PUBLIC_ struct smb_iconv_handle *smb_iconv_handle_reinit(TALLOC_CTX *mem_ctx,
229                                                                     const char *dos_charset,
230                                                                     const char *unix_charset,
231                                                                     bool native_iconv,
232                                                                     struct smb_iconv_handle *old_ic)
233 {
234         struct smb_iconv_handle *ret;
235
236         if (old_ic != NULL) {
237                 ret = old_ic;
238                 close_iconv_handle(ret);
239                 talloc_free(ret->child_ctx);
240                 ZERO_STRUCTP(ret);
241         } else {
242                 ret = talloc_zero(mem_ctx, struct smb_iconv_handle);
243         }
244         if (ret == NULL) {
245                 return NULL;
246         }
247
248         /* we use a child context to allow us to free all ptrs without
249            freeing the structure itself */
250         ret->child_ctx = talloc_new(ret);
251         if (ret->child_ctx == NULL) {
252                 return NULL;
253         }
254
255         talloc_set_destructor(ret, close_iconv_handle);
256
257         if (strcasecmp(dos_charset, "UTF8") == 0 || strcasecmp(dos_charset, "UTF-8") == 0) {
258                 DEBUG(0,("ERROR: invalid DOS charset: 'dos charset' must not be UTF8, using (default value) CP850 instead\n"));
259                 dos_charset = "CP850";
260         }
261
262         ret->dos_charset = talloc_strdup(ret->child_ctx, dos_charset);
263         ret->unix_charset = talloc_strdup(ret->child_ctx, unix_charset);
264         ret->native_iconv = native_iconv;
265
266         return ret;
267 }
268
269 /*
270   on-demand initialisation of conversion handles
271 */
272 smb_iconv_t get_conv_handle(struct smb_iconv_handle *ic,
273                             charset_t from, charset_t to)
274 {
275         const char *n1, *n2;
276
277         if (ic->conv_handles[from][to]) {
278                 return ic->conv_handles[from][to];
279         }
280
281         n1 = charset_name(ic, from);
282         n2 = charset_name(ic, to);
283
284         ic->conv_handles[from][to] = smb_iconv_open_ex(ic, n2, n1,
285                                                        ic->native_iconv);
286
287         if (ic->conv_handles[from][to] == (smb_iconv_t)-1) {
288                 if ((from == CH_DOS || to == CH_DOS) &&
289                     strcasecmp(charset_name(ic, CH_DOS), "ASCII") != 0) {
290                         DEBUG(0,("dos charset '%s' unavailable - using ASCII\n",
291                                  charset_name(ic, CH_DOS)));
292                         ic->dos_charset = "ASCII";
293
294                         n1 = charset_name(ic, from);
295                         n2 = charset_name(ic, to);
296
297                         ic->conv_handles[from][to] =
298                                 smb_iconv_open_ex(ic, n2, n1, ic->native_iconv);
299                 }
300         }
301
302         return ic->conv_handles[from][to];
303 }
304
305 /**
306  * Return the unicode codepoint for the next character in the input
307  * string in the given src_charset.
308  * The unicode codepoint (codepoint_t) is an unsinged 32 bit value.
309  *
310  * Also return the number of bytes consumed (which tells the caller
311  * how many bytes to skip to get to the next src_charset-character).
312  *
313  * This is implemented (in the non-ascii-case) by first converting the
314  * next character in the input string to UTF16_LE and then calculating
315  * the unicode codepoint from that.
316  *
317  * Return INVALID_CODEPOINT if the next character cannot be converted.
318  */
319 _PUBLIC_ codepoint_t next_codepoint_handle_ext(
320                         struct smb_iconv_handle *ic,
321                         const char *str, charset_t src_charset,
322                         size_t *bytes_consumed)
323 {
324         /* it cannot occupy more than 4 bytes in UTF16 format */
325         uint8_t buf[4];
326         smb_iconv_t descriptor;
327         size_t ilen_orig;
328         size_t ilen;
329         size_t olen;
330         char *outbuf;
331
332         if ((str[0] & 0x80) == 0) {
333                 *bytes_consumed = 1;
334                 return (codepoint_t)str[0];
335         }
336
337         /*
338          * we assume that no multi-byte character can take more than 5 bytes.
339          * This is OK as we only support codepoints up to 1M (U+100000)
340          */
341         ilen_orig = strnlen(str, 5);
342         ilen = ilen_orig;
343
344         descriptor = get_conv_handle(ic, src_charset, CH_UTF16);
345         if (descriptor == (smb_iconv_t)-1) {
346                 *bytes_consumed = 1;
347                 return INVALID_CODEPOINT;
348         }
349
350         /*
351          * this looks a little strange, but it is needed to cope with
352          * codepoints above 64k (U+1000) which are encoded as per RFC2781.
353          */
354         olen = 2;
355         outbuf = (char *)buf;
356         smb_iconv(descriptor, &str, &ilen, &outbuf, &olen);
357         if (olen == 2) {
358                 olen = 4;
359                 outbuf = (char *)buf;
360                 smb_iconv(descriptor,  &str, &ilen, &outbuf, &olen);
361                 if (olen == 4) {
362                         /* we didn't convert any bytes */
363                         *bytes_consumed = 1;
364                         return INVALID_CODEPOINT;
365                 }
366                 olen = 4 - olen;
367         } else {
368                 olen = 2 - olen;
369         }
370
371         *bytes_consumed = ilen_orig - ilen;
372
373         if (olen == 2) {
374                 return (codepoint_t)SVAL(buf, 0);
375         }
376         if (olen == 4) {
377                 /* decode a 4 byte UTF16 character manually */
378                 return (codepoint_t)0x10000 +
379                         (buf[2] | ((buf[3] & 0x3)<<8) |
380                          (buf[0]<<10) | ((buf[1] & 0x3)<<18));
381         }
382
383         /* no other length is valid */
384         return INVALID_CODEPOINT;
385 }
386
387 /*
388   return the unicode codepoint for the next multi-byte CH_UNIX character
389   in the string
390
391   also return the number of bytes consumed (which tells the caller
392   how many bytes to skip to get to the next CH_UNIX character)
393
394   return INVALID_CODEPOINT if the next character cannot be converted
395 */
396 _PUBLIC_ codepoint_t next_codepoint_handle(struct smb_iconv_handle *ic,
397                                     const char *str, size_t *size)
398 {
399         return next_codepoint_handle_ext(ic, str, CH_UNIX, size);
400 }
401
402 /*
403   push a single codepoint into a CH_UNIX string the target string must
404   be able to hold the full character, which is guaranteed if it is at
405   least 5 bytes in size. The caller may pass less than 5 bytes if they
406   are sure the character will fit (for example, you can assume that
407   uppercase/lowercase of a character will not add more than 1 byte)
408
409   return the number of bytes occupied by the CH_UNIX character, or
410   -1 on failure
411 */
412 _PUBLIC_ ssize_t push_codepoint_handle(struct smb_iconv_handle *ic,
413                                 char *str, codepoint_t c)
414 {
415         smb_iconv_t descriptor;
416         uint8_t buf[4];
417         size_t ilen, olen;
418         const char *inbuf;
419
420         if (c < 128) {
421                 *str = c;
422                 return 1;
423         }
424
425         descriptor = get_conv_handle(ic,
426                                      CH_UTF16, CH_UNIX);
427         if (descriptor == (smb_iconv_t)-1) {
428                 return -1;
429         }
430
431         if (c < 0x10000) {
432                 ilen = 2;
433                 olen = 5;
434                 inbuf = (char *)buf;
435                 SSVAL(buf, 0, c);
436                 smb_iconv(descriptor, &inbuf, &ilen, &str, &olen);
437                 if (ilen != 0) {
438                         return -1;
439                 }
440                 return 5 - olen;
441         }
442
443         c -= 0x10000;
444
445         buf[0] = (c>>10) & 0xFF;
446         buf[1] = (c>>18) | 0xd8;
447         buf[2] = c & 0xFF;
448         buf[3] = ((c>>8) & 0x3) | 0xdc;
449
450         ilen = 4;
451         olen = 5;
452         inbuf = (char *)buf;
453
454         smb_iconv(descriptor, &inbuf, &ilen, &str, &olen);
455         if (ilen != 0) {
456                 return -1;
457         }
458         return 5 - olen;
459 }
460
461 _PUBLIC_ codepoint_t next_codepoint_ext(const char *str, charset_t src_charset,
462                                         size_t *size)
463 {
464         return next_codepoint_handle_ext(get_iconv_handle(), str,
465                                               src_charset, size);
466 }
467
468 _PUBLIC_ codepoint_t next_codepoint(const char *str, size_t *size)
469 {
470         return next_codepoint_handle(get_iconv_handle(), str, size);
471 }
472
473 _PUBLIC_ ssize_t push_codepoint(char *str, codepoint_t c)
474 {
475         return push_codepoint_handle(get_iconv_handle(), str, c);
476 }