8f46c8803919b32476be14b68b4cf036414bac0a
[ddiss/samba.git] / lib / util / charset / convert_string.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-2011
6    Copyright (C) Andrew Bartlett 2011
7    Copyright (C) Simo Sorce 2001
8    Copyright (C) Martin Pool 2003
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
23 */
24 #include "includes.h"
25 #include "system/iconv.h"
26
27 /**
28  * @file
29  *
30  * @brief Character-set conversion routines built on our iconv.
31  *
32  * @note Samba's internal character set (at least in the 3.0 series)
33  * is always the same as the one for the Unix filesystem.  It is
34  * <b>not</b> necessarily UTF-8 and may be different on machines that
35  * need i18n filenames to be compatible with Unix software.  It does
36  * have to be a superset of ASCII.  All multibyte sequences must start
37  * with a byte with the high bit set.
38  *
39  * @sa lib/iconv.c
40  */
41
42
43 /**
44  * Convert string from one encoding to another, making error checking etc
45  * Slow path version - uses (slow) iconv.
46  *
47  * @param src pointer to source string (multibyte or singlebyte)
48  * @param srclen length of the source string in bytes
49  * @param dest pointer to destination string (multibyte or singlebyte)
50  * @param destlen maximal length allowed for string
51  * @param converted size is the number of bytes occupied in the destination
52  *
53  * @returns false and sets errno on fail, true on success.
54  *
55  * Ensure the srclen contains the terminating zero.
56  *
57  **/
58
59 static bool convert_string_internal(struct smb_iconv_handle *ic,
60                                     charset_t from, charset_t to,
61                                     void const *src, size_t srclen,
62                                     void *dest, size_t destlen, size_t *converted_size)
63 {
64         size_t i_len, o_len;
65         size_t retval;
66         const char* inbuf = (const char*)src;
67         char* outbuf = (char*)dest;
68         smb_iconv_t descriptor;
69
70         descriptor = get_conv_handle(ic, from, to);
71
72         if (srclen == (size_t)-1) {
73                 if (from == CH_UTF16LE || from == CH_UTF16BE) {
74                         srclen = (strlen_w((const smb_ucs2_t *)src)+1) * 2;
75                 } else {
76                         srclen = strlen((const char *)src)+1;
77                 }
78         }
79
80
81         if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
82                 errno = EINVAL;
83                 return false;
84         }
85
86         i_len=srclen;
87         o_len=destlen;
88
89         retval = smb_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len);
90         *converted_size = destlen-o_len;
91
92         return (retval != (size_t)-1);
93 }
94
95 /**
96  * Convert string from one encoding to another, making error checking etc
97  * Fast path version - handles ASCII first.
98  *
99  * @param src pointer to source string (multibyte or singlebyte)
100  * @param srclen length of the source string in bytes, or -1 for nul terminated.
101  * @param dest pointer to destination string (multibyte or singlebyte)
102  * @param destlen maximal length allowed for string - *NEVER* -1.
103  * @param converted size is the number of bytes occupied in the destination
104  *
105  * @returns false and sets errno on fail, true on success.
106  *
107  * Ensure the srclen contains the terminating zero.
108  *
109  * This function has been hand-tuned to provide a fast path.
110  * Don't change unless you really know what you are doing. JRA.
111  **/
112
113 bool convert_string_error_handle(struct smb_iconv_handle *ic,
114                                  charset_t from, charset_t to,
115                                  void const *src, size_t srclen,
116                                  void *dest, size_t destlen,
117                                  size_t *converted_size)
118 {
119         /*
120          * NB. We deliberately don't do a strlen here if srclen == -1.
121          * This is very expensive over millions of calls and is taken
122          * care of in the slow path in convert_string_internal. JRA.
123          */
124
125 #ifdef DEVELOPER
126         SMB_ASSERT(destlen != (size_t)-1);
127 #endif
128
129         if (srclen == 0) {
130                 *converted_size = 0;
131                 return true;
132         }
133
134         if (from != CH_UTF16LE && from != CH_UTF16BE && to != CH_UTF16LE && to != CH_UTF16BE) {
135                 const unsigned char *p = (const unsigned char *)src;
136                 unsigned char *q = (unsigned char *)dest;
137                 size_t slen = srclen;
138                 size_t dlen = destlen;
139                 unsigned char lastp = '\0';
140                 size_t retval = 0;
141
142                 /* If all characters are ascii, fast path here. */
143                 while (slen && dlen) {
144                         if ((lastp = *p) <= 0x7f) {
145                                 *q++ = *p++;
146                                 if (slen != (size_t)-1) {
147                                         slen--;
148                                 }
149                                 dlen--;
150                                 retval++;
151                                 if (!lastp)
152                                         break;
153                         } else {
154 #ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
155                                 goto general_case;
156 #else
157                                 bool ret = convert_string_internal(ic, from, to, p, slen, q, dlen, converted_size);
158                                 *converted_size += retval;
159                                 return ret;
160 #endif
161                         }
162                 }
163
164                 *converted_size = retval;
165
166                 if (!dlen) {
167                         /* Even if we fast path we should note if we ran out of room. */
168                         if (((slen != (size_t)-1) && slen) ||
169                                         ((slen == (size_t)-1) && lastp)) {
170                                 errno = E2BIG;
171                                 return false;
172                         }
173                 }
174                 return true;
175         } else if (from == CH_UTF16LE && to != CH_UTF16LE) {
176                 const unsigned char *p = (const unsigned char *)src;
177                 unsigned char *q = (unsigned char *)dest;
178                 size_t retval = 0;
179                 size_t slen = srclen;
180                 size_t dlen = destlen;
181                 unsigned char lastp = '\0';
182                 bool ret;
183
184                 if (slen == (size_t)-1) {
185                         while (dlen &&
186                                ((lastp = *p) <= 0x7f) && (p[1] == 0)) {
187                                 *q++ = *p;
188                                 p += 2;
189                                 dlen--;
190                                 retval++;
191                                 if (!lastp)
192                                         break;
193                         }
194                         if (lastp != 0) goto slow_path;
195                 } else {
196                         while (slen >= 2 && dlen &&
197                                (*p <= 0x7f) && (p[1] == 0)) {
198                                 *q++ = *p;
199                                 slen -= 2;
200                                 p += 2;
201                                 dlen--;
202                                 retval++;
203                         }
204                         if (slen != 0) goto slow_path;
205                 }
206
207                 *converted_size = retval;
208
209                 if (!dlen) {
210                         /* Even if we fast path we should note if we ran out of room. */
211                         if (((slen != (size_t)-1) && slen) ||
212                                         ((slen == (size_t)-1) && lastp)) {
213                                 errno = E2BIG;
214                                 return false;
215                         }
216                 }
217                 return true;
218
219         slow_path:
220                 /* come here when we hit a character we can't deal
221                  * with in the fast path
222                  */
223 #ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
224                 goto general_case;
225 #else
226                 ret = convert_string_internal(ic, from, to, p, slen, q, dlen, converted_size);
227                 *converted_size += retval;
228                 return ret;
229 #endif
230
231         } else if (from != CH_UTF16LE && from != CH_UTF16BE && to == CH_UTF16LE) {
232                 const unsigned char *p = (const unsigned char *)src;
233                 unsigned char *q = (unsigned char *)dest;
234                 size_t retval = 0;
235                 size_t slen = srclen;
236                 size_t dlen = destlen;
237                 unsigned char lastp = '\0';
238
239                 /* If all characters are ascii, fast path here. */
240                 while (slen && (dlen >= 1)) {
241                         if (dlen >=2 && (lastp = *p) <= 0x7F) {
242                                 *q++ = *p++;
243                                 *q++ = '\0';
244                                 if (slen != (size_t)-1) {
245                                         slen--;
246                                 }
247                                 dlen -= 2;
248                                 retval += 2;
249                                 if (!lastp)
250                                         break;
251                         } else {
252 #ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
253                                 goto general_case;
254 #else
255                                 bool ret = convert_string_internal(ic, from, to, p, slen, q, dlen, converted_size);
256                                 *converted_size += retval;
257                                 return ret;
258 #endif
259                         }
260                 }
261
262                 *converted_size = retval;
263
264                 if (!dlen) {
265                         /* Even if we fast path we should note if we ran out of room. */
266                         if (((slen != (size_t)-1) && slen) ||
267                                         ((slen == (size_t)-1) && lastp)) {
268                                 errno = E2BIG;
269                                 return false;
270                         }
271                 }
272                 return true;
273         }
274
275 #ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
276   general_case:
277 #endif
278         return convert_string_internal(ic, from, to, src, srclen, dest, destlen, converted_size);
279 }
280
281 bool convert_string_handle(struct smb_iconv_handle *ic,
282                            charset_t from, charset_t to,
283                            void const *src, size_t srclen,
284                            void *dest, size_t destlen,
285                            size_t *converted_size)
286 {
287         bool ret = convert_string_error_handle(ic, from, to, src, srclen, dest, destlen, converted_size);
288
289         if(ret==false) {
290                 const char *reason="unknown error";
291                 switch(errno) {
292                         case EINVAL:
293                                 reason="Incomplete multibyte sequence";
294                                 DEBUG(3,("convert_string_internal: Conversion error: %s(%s)\n",
295                                          reason, (const char *)src));
296                                 break;
297                         case E2BIG:
298                         {
299                                 reason="No more room";
300                                 if (from == CH_UNIX) {
301                                         DEBUG(3,("E2BIG: convert_string(%s,%s): srclen=%u destlen=%u - '%s'\n",
302                                                  charset_name(ic, from), charset_name(ic, to),
303                                                  (unsigned int)srclen, (unsigned int)destlen, (const char *)src));
304                                 } else {
305                                         DEBUG(3,("E2BIG: convert_string(%s,%s): srclen=%u destlen=%u\n",
306                                                  charset_name(ic, from), charset_name(ic, to),
307                                                  (unsigned int)srclen, (unsigned int)destlen));
308                                 }
309                                 break;
310                         }
311                         case EILSEQ:
312                                 reason="Illegal multibyte sequence";
313                                 DEBUG(3,("convert_string_internal: Conversion error: %s(%s)\n",
314                                          reason, (const char *)src));
315                                 break;
316                         default:
317                                 DEBUG(0,("convert_string_internal: Conversion error: %s(%s)\n",
318                                          reason, (const char *)src));
319                                 break;
320                 }
321                 /* smb_panic(reason); */
322         }
323         return ret;
324 }
325
326
327 /**
328  * Convert between character sets, allocating a new buffer using talloc for the result.
329  *
330  * @param srclen length of source buffer.
331  * @param dest always set at least to NULL
332  * @parm converted_size set to the number of bytes occupied by the string in
333  * the destination on success.
334  * @note -1 is not accepted for srclen.
335  *
336  * @return true if new buffer was correctly allocated, and string was
337  * converted.
338  *
339  * Ensure the srclen contains the terminating zero.
340  *
341  * I hate the goto's in this function. It's embarressing.....
342  * There has to be a cleaner way to do this. JRA.
343  */
344 bool convert_string_talloc_handle(TALLOC_CTX *ctx, struct smb_iconv_handle *ic,
345                                   charset_t from, charset_t to,
346                                   void const *src, size_t srclen, void *dst,
347                                   size_t *converted_size)
348
349 {
350         size_t i_len, o_len, destlen = (srclen * 3) / 2;
351         size_t retval;
352         const char *inbuf = (const char *)src;
353         char *outbuf = NULL, *ob = NULL;
354         smb_iconv_t descriptor;
355         void **dest = (void **)dst;
356
357         *dest = NULL;
358
359         if (src == NULL || srclen == (size_t)-1) {
360                 errno = EINVAL;
361                 return false;
362         }
363
364         if (srclen == 0) {
365                 /* We really should treat this as an error, but
366                    there are too many callers that need this to
367                    return a NULL terminated string in the correct
368                    character set. */
369                 if (to == CH_UTF16LE|| to == CH_UTF16BE || to == CH_UTF16MUNGED) {
370                         destlen = 2;
371                 } else {
372                         destlen = 1;
373                 }
374                 ob = talloc_zero_array(ctx, char, destlen);
375                 if (ob == NULL) {
376                         errno = ENOMEM;
377                         return false;
378                 }
379                 if (converted_size != NULL) {
380                         *converted_size = destlen;
381                 }
382                 *dest = ob;
383                 return true;
384         }
385
386         descriptor = get_conv_handle(ic, from, to);
387
388         if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
389                 DEBUG(0,("convert_string_talloc: Conversion not supported.\n"));
390                 errno = EOPNOTSUPP;
391                 return false;
392         }
393
394   convert:
395
396         /* +2 is for ucs2 null termination. */
397         if ((destlen*2)+2 < destlen) {
398                 /* wrapped ! abort. */
399                 DEBUG(0, ("convert_string_talloc: destlen wrapped !\n"));
400                 TALLOC_FREE(outbuf);
401                 errno = EOPNOTSUPP;
402                 return false;
403         } else {
404                 destlen = destlen * 2;
405         }
406
407         /* +2 is for ucs2 null termination. */
408         ob = talloc_realloc(ctx, ob, char, destlen + 2);
409
410         if (!ob) {
411                 DEBUG(0, ("convert_string_talloc: realloc failed!\n"));
412                 errno = ENOMEM;
413                 return false;
414         }
415         outbuf = ob;
416         i_len = srclen;
417         o_len = destlen;
418
419         retval = smb_iconv(descriptor,
420                            &inbuf, &i_len,
421                            &outbuf, &o_len);
422         if(retval == (size_t)-1)                {
423                 const char *reason="unknown error";
424                 switch(errno) {
425                         case EINVAL:
426                                 reason="Incomplete multibyte sequence";
427                                 DEBUG(3,("convert_string_talloc: Conversion error: %s(%s)\n",reason,inbuf));
428                                 break;
429                         case E2BIG:
430                                 goto convert;
431                         case EILSEQ:
432                                 reason="Illegal multibyte sequence";
433                                 DEBUG(3,("convert_string_talloc: Conversion error: %s(%s)\n",reason,inbuf));
434                                 break;
435                 }
436                 DEBUG(0,("Conversion error: %s(%s)\n",reason,inbuf));
437                 /* smb_panic(reason); */
438                 TALLOC_FREE(ob);
439                 return false;
440         }
441
442         destlen = destlen - o_len;
443         /* Don't shrink unless we're reclaiming a lot of
444          * space. This is in the hot codepath and these
445          * reallocs *cost*. JRA.
446          */
447         if (o_len > 1024) {
448                 /* We're shrinking here so we know the +2 is safe from wrap. */
449                 ob = talloc_realloc(ctx,ob, char, destlen + 2);
450         }
451
452         if (destlen && !ob) {
453                 DEBUG(0, ("convert_string_talloc: out of memory!\n"));
454                 errno = ENOMEM;
455                 return false;
456         }
457
458         *dest = ob;
459
460         /* Must ucs2 null terminate in the extra space we allocated. */
461         ob[destlen] = '\0';
462         ob[destlen+1] = '\0';
463
464         /* Ensure we can never return a *converted_size of zero. */
465         if (destlen == 0) {
466                 /* As we're now returning false on a bad smb_iconv call,
467                    this should never happen. But be safe anyway. */
468                 if (to == CH_UTF16LE|| to == CH_UTF16BE || to == CH_UTF16MUNGED) {
469                         destlen = 2;
470                 } else {
471                         destlen = 1;
472                 }
473         }
474
475         if (converted_size != NULL) {
476                 *converted_size = destlen;
477         }
478         return true;
479 }
480
481 /**
482  * Convert string from one encoding to another, making error checking etc
483  *
484  * @param src pointer to source string (multibyte or singlebyte)
485  * @param srclen length of the source string in bytes
486  * @param dest pointer to destination string (multibyte or singlebyte)
487  * @param destlen maximal length allowed for string
488  * @param converted_size the number of bytes occupied in the destination
489  *
490  * @returns true on success, false on fail.
491  **/
492 _PUBLIC_ bool convert_string(charset_t from, charset_t to,
493                                void const *src, size_t srclen,
494                                void *dest, size_t destlen,
495                                size_t *converted_size)
496 {
497         return convert_string_handle(get_iconv_handle(), from, to,
498                                         src, srclen,
499                                         dest, destlen, converted_size);
500 }
501
502 /**
503  * Convert string from one encoding to another, making error checking etc
504  *
505  * @param src pointer to source string (multibyte or singlebyte)
506  * @param srclen length of the source string in bytes
507  * @param dest pointer to destination string (multibyte or singlebyte)
508  * @param destlen maximal length allowed for string
509  * @param converted_size the number of bytes occupied in the destination
510  *
511  * @returns true on success, false on fail.
512  **/
513 _PUBLIC_ bool convert_string_error(charset_t from, charset_t to,
514                                    void const *src, size_t srclen,
515                                    void *dest, size_t destlen,
516                                    size_t *converted_size)
517 {
518         return convert_string_error_handle(get_iconv_handle(), from, to,
519                                            src, srclen,
520                                            dest, destlen, converted_size);
521 }
522
523 /**
524  * Convert between character sets, allocating a new buffer using talloc for the result.
525  *
526  * @param srclen length of source buffer.
527  * @param dest always set at least to NULL
528  * @param converted_size Size in bytes of the converted string
529  * @note -1 is not accepted for srclen.
530  *
531  * @returns boolean indication whether the conversion succeeded
532  **/
533
534 _PUBLIC_ bool convert_string_talloc(TALLOC_CTX *ctx,
535                                     charset_t from, charset_t to,
536                                     void const *src, size_t srclen,
537                                     void *dest, size_t *converted_size)
538 {
539         return convert_string_talloc_handle(ctx, get_iconv_handle(),
540                                                  from, to, src, srclen, dest,
541                                                  converted_size);
542 }