]> arthur.barton.de Git - netatalk.git/blob - libatalk/unicode/charcnv.c
Merge 2-2
[netatalk.git] / libatalk / unicode / charcnv.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) Martin Pool 2003
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 2 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, write to the Free Software
21   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
23 */
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif /* HAVE_CONFIG_H */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <sys/stat.h>
35 #include <sys/param.h>
36 #ifdef HAVE_USABLE_ICONV
37 #include <iconv.h>
38 #endif
39 #include <arpa/inet.h>
40
41 #include <atalk/logger.h>
42 #include <atalk/unicode.h>
43 #include <atalk/util.h>
44 #include <atalk/compat.h>
45
46 #include "byteorder.h"
47
48
49 /**
50  * @file
51  *
52  * @brief Character-set conversion routines built on our iconv.
53  *
54  * @note Samba's internal character set (at least in the 3.0 series)
55  * is always the same as the one for the Unix filesystem.  It is
56  * <b>not</b> necessarily UTF-8 and may be different on machines that
57  * need i18n filenames to be compatible with Unix software.  It does
58  * have to be a superset of ASCII.  All multibyte sequences must start
59  * with a byte with the high bit set.
60  *
61  * @sa lib/iconv.c
62  */
63
64
65 #define MAX_CHARSETS 20
66
67 #define CHECK_FLAGS(a,b) (((a)!=NULL) ? (*(a) & (b)) : 0 )
68
69 static atalk_iconv_t conv_handles[MAX_CHARSETS][MAX_CHARSETS];
70 char* charset_names[MAX_CHARSETS];
71 static struct charset_functions* charsets[MAX_CHARSETS];
72 static char hexdig[] = "0123456789abcdef";
73 #define hextoint( c )   ( isdigit( c ) ? c - '0' : c + 10 - 'a' )
74
75
76 /**
77  * Return the name of a charset to give to iconv().
78  **/
79 static const char *charset_name(charset_t ch)
80 {
81     const char *ret = NULL;
82
83     if (ch == CH_UCS2) ret = "UCS-2";
84     else if (ch == CH_UTF8) ret = "UTF8";
85     else if (ch == CH_UTF8_MAC) ret = "UTF8-MAC";
86     else ret = charset_names[ch];
87     return ret;
88 }
89
90 static struct charset_functions* get_charset_functions (charset_t ch)
91 {
92     if (charsets[ch] != NULL)
93         return charsets[ch];
94
95     charsets[ch] = find_charset_functions(charset_name(ch));
96
97     return charsets[ch];
98 }
99
100
101 static void lazy_initialize_conv(void)
102 {
103     static int initialized = 0;
104
105     if (!initialized) {
106         initialized = 1;
107         init_iconv();
108     }
109 }
110
111 charset_t add_charset(const char* name)
112 {
113     static charset_t max_charset_t = NUM_CHARSETS-1;
114     charset_t cur_charset_t = max_charset_t+1;
115     unsigned int c1;
116
117     lazy_initialize_conv();
118
119     for (c1=0; c1<=max_charset_t;c1++) {
120         if ( strcasecmp(name, charset_name(c1)) == 0)
121             return (c1);
122     }
123
124     if ( cur_charset_t >= MAX_CHARSETS )  {
125         LOG (log_debug, logtype_default, "Adding charset %s failed, too many charsets (max. %u allowed)",
126              name, MAX_CHARSETS);
127         return (charset_t) -1;
128     }
129
130     /* First try to setup the required conversions */
131
132     conv_handles[cur_charset_t][CH_UCS2] = atalk_iconv_open( charset_name(CH_UCS2), name);
133     if (conv_handles[cur_charset_t][CH_UCS2] == (atalk_iconv_t)-1) {
134         LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
135             name,  charset_name(CH_UCS2));
136         conv_handles[cur_charset_t][CH_UCS2] = NULL;
137         return (charset_t) -1;
138     }
139
140     conv_handles[CH_UCS2][cur_charset_t] = atalk_iconv_open( name, charset_name(CH_UCS2));
141     if (conv_handles[CH_UCS2][cur_charset_t] == (atalk_iconv_t)-1) {
142         LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
143             charset_name(CH_UCS2), name);
144         conv_handles[CH_UCS2][cur_charset_t] = NULL;
145         return (charset_t) -1;
146     }
147
148     /* register the new charset_t name */
149     charset_names[cur_charset_t] = strdup(name);
150
151     charsets[cur_charset_t] = get_charset_functions (cur_charset_t);
152     max_charset_t++;
153
154 #ifdef DEBUG
155     LOG(log_debug9, logtype_default, "Added charset %s with handle %u", name, cur_charset_t);
156 #endif
157     return (cur_charset_t);
158 }
159
160 /**
161  * Initialize iconv conversion descriptors.
162  *
163  * This is called the first time it is needed, and also called again
164  * every time the configuration is reloaded, because the charset or
165  * codepage might have changed.
166  **/
167 void init_iconv(void)
168 {
169     int c1;
170
171     for (c1=0;c1<NUM_CHARSETS;c1++) {
172         const char *name = charset_name((charset_t)c1);
173
174         conv_handles[c1][CH_UCS2] = atalk_iconv_open( charset_name(CH_UCS2), name);
175         if (conv_handles[c1][CH_UCS2] == (atalk_iconv_t)-1) {
176             LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
177                 name,  charset_name(CH_UCS2));
178             conv_handles[c1][CH_UCS2] = NULL;
179         }
180
181         if (c1 != CH_UCS2) { /* avoid lost memory, make valgrind happy */
182             conv_handles[CH_UCS2][c1] = atalk_iconv_open( name, charset_name(CH_UCS2));
183             if (conv_handles[CH_UCS2][c1] == (atalk_iconv_t)-1) {
184                 LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
185                     charset_name(CH_UCS2), name);
186                 conv_handles[CH_UCS2][c1] = NULL;
187             }
188         }
189
190         charsets[c1] = get_charset_functions (c1);
191     }
192 }
193
194 /**
195  *
196  **/
197 static size_t add_null(charset_t to, char *buf, size_t bytesleft, size_t len)
198 {
199     /* Terminate the string */
200     if (to == CH_UCS2 && bytesleft >= 2) {
201         buf[len]   = 0;
202         buf[len+1] = 0;
203
204     }
205     else if ( to != CH_UCS2 && bytesleft > 0 )
206         buf[len]   = 0;
207     else {
208         errno = E2BIG;
209         return (size_t)(-1);
210     }
211
212     return len;
213 }
214
215
216 /**
217  * Convert string from one encoding to another, making error checking etc
218  *
219  * @param src pointer to source string (multibyte or singlebyte)
220  * @param srclen length of the source string in bytes
221  * @param dest pointer to destination string (multibyte or singlebyte)
222  * @param destlen maximal length allowed for string
223  * @returns the number of bytes occupied in the destination
224  **/
225 static size_t convert_string_internal(charset_t from, charset_t to,
226                                       void const *src, size_t srclen,
227                                       void *dest, size_t destlen)
228 {
229     size_t i_len, o_len;
230     size_t retval;
231     const char* inbuf = (const char*)src;
232     char* outbuf = (char*)dest;
233     char* o_save = outbuf;
234     atalk_iconv_t descriptor;
235
236     /* Fixed based on Samba 3.0.6 */
237     if (srclen == (size_t)-1) {
238         if (from == CH_UCS2) {
239             srclen = (strlen_w((const ucs2_t *)src)) * 2;
240         } else {
241             srclen = strlen((const char *)src);
242         }
243     }
244
245
246     lazy_initialize_conv();
247
248     descriptor = conv_handles[from][to];
249
250     if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
251         return (size_t) -1;
252     }
253
254     i_len=srclen;
255     o_len=destlen;
256     retval = atalk_iconv(descriptor,  &inbuf, &i_len, &outbuf, &o_len);
257     if(retval==(size_t)-1) {
258         const char *reason="unknown error";
259         switch(errno) {
260         case EINVAL:
261             reason="Incomplete multibyte sequence";
262             break;
263         case E2BIG:
264             reason="No more room";
265             break;
266         case EILSEQ:
267             reason="Illegal multibyte sequence";
268             break;
269         }
270         LOG(log_debug, logtype_default,"Conversion error: %s",reason);
271         return (size_t)-1;
272     }
273
274     /* Terminate the string */
275     return add_null( to, o_save, o_len, destlen -o_len);
276 }
277
278
279 size_t convert_string(charset_t from, charset_t to,
280                       void const *src, size_t srclen,
281                       void *dest, size_t destlen)
282 {
283     size_t i_len, o_len;
284     ucs2_t *u;
285     ucs2_t buffer[MAXPATHLEN];
286     ucs2_t buffer2[MAXPATHLEN];
287
288     /* convert from_set to UCS2 */
289     if ((size_t)-1 == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen,
290                                                            (char*) buffer, sizeof(buffer))) ) {
291         LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from));
292         return (size_t) -1;
293     }
294
295     /* Do pre/decomposition */
296     i_len = sizeof(buffer2);
297     u = buffer2;
298     if (charsets[to] && (charsets[to]->flags & CHARSET_DECOMPOSED) ) {
299         if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
300             return (size_t)-1;
301     }
302     else if (!charsets[from] || (charsets[from]->flags & CHARSET_DECOMPOSED)) {
303         if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
304             return (size_t)-1;
305     }
306     else {
307         u = buffer;
308         i_len = o_len;
309     }
310     /* Convert UCS2 to to_set */
311     if ((size_t)(-1) == ( o_len = convert_string_internal( CH_UCS2, to, (char*) u, i_len, dest, destlen)) ) {
312         LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
313         return (size_t) -1;
314     }
315
316     return o_len;
317 }
318
319
320
321 /**
322  * Convert between character sets, allocating a new buffer for the result.
323  *
324  * @param srclen length of source buffer.
325  * @param dest always set at least to NULL
326  * @note -1 is not accepted for srclen.
327  *
328  * @returns Size in bytes of the converted string; or -1 in case of error.
329  **/
330
331 static size_t convert_string_allocate_internal(charset_t from, charset_t to,
332                                                void const *src, size_t srclen, char **dest)
333 {
334     size_t i_len, o_len, destlen;
335     size_t retval;
336     const char *inbuf = (const char *)src;
337     char *outbuf = NULL, *ob = NULL;
338     atalk_iconv_t descriptor;
339
340     *dest = NULL;
341
342     if (src == NULL || srclen == (size_t)-1)
343         return (size_t)-1;
344
345     lazy_initialize_conv();
346
347     descriptor = conv_handles[from][to];
348
349     if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
350         /* conversion not supported, return -1*/
351         LOG(log_debug, logtype_default, "convert_string_allocate: conversion not supported!");
352         return -1;
353     }
354
355     destlen = MAX(srclen, 512);
356 convert:
357     destlen = destlen * 2;
358     outbuf = (char *)realloc(ob, destlen);
359     if (!outbuf) {
360         LOG(log_debug, logtype_default,"convert_string_allocate: realloc failed!");
361         SAFE_FREE(ob);
362         return (size_t)-1;
363     } else {
364         ob = outbuf;
365     }
366     inbuf = src;   /* this restarts the whole conversion if buffer needed to be increased */
367     i_len = srclen;
368     o_len = destlen;
369     retval = atalk_iconv(descriptor,
370                          &inbuf, &i_len,
371                          &outbuf, &o_len);
372     if(retval == (size_t)-1)        {
373         const char *reason="unknown error";
374         switch(errno) {
375         case EINVAL:
376             reason="Incomplete multibyte sequence";
377             break;
378         case E2BIG:
379             goto convert;
380         case EILSEQ:
381             reason="Illegal multibyte sequence";
382             break;
383         }
384         LOG(log_debug, logtype_default,"Conversion error: %s(%s)",reason,inbuf);
385         SAFE_FREE(ob);
386         return (size_t)-1;
387     }
388
389
390     destlen = destlen - o_len;
391
392     /* Terminate the string */
393     if (to == CH_UCS2 && o_len >= 2) {
394         ob[destlen] = 0;
395         ob[destlen+1] = 0;
396         *dest = (char *)realloc(ob,destlen+2);
397     }
398     else if ( to != CH_UCS2 && o_len > 0 ) {
399         ob[destlen] = 0;
400         *dest = (char *)realloc(ob,destlen+1);
401     }
402     else {
403         goto convert; /* realloc */
404     }
405
406     if (destlen && !*dest) {
407         LOG(log_debug, logtype_default, "convert_string_allocate: out of memory!");
408         SAFE_FREE(ob);
409         return (size_t)-1;
410     }
411
412     return destlen;
413 }
414
415
416 size_t convert_string_allocate(charset_t from, charset_t to,
417                                void const *src, size_t srclen,
418                                char ** dest)
419 {
420     size_t i_len, o_len;
421     ucs2_t *u;
422     ucs2_t buffer[MAXPATHLEN];
423     ucs2_t buffer2[MAXPATHLEN];
424
425     *dest = NULL;
426
427     /* convert from_set to UCS2 */
428     if ((size_t)(-1) == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen,
429                                                            buffer, sizeof(buffer))) ) {
430         LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from));
431         return (size_t) -1;
432     }
433
434     /* Do pre/decomposition */
435     i_len = sizeof(buffer2);
436     u = buffer2;
437     if (charsets[to] && (charsets[to]->flags & CHARSET_DECOMPOSED) ) {
438         if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
439             return (size_t)-1;
440     }
441     else if ( !charsets[from] || (charsets[from]->flags & CHARSET_DECOMPOSED) ) {
442         if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
443             return (size_t)-1;
444     }
445     else {
446         u = buffer;
447         i_len = o_len;
448     }
449
450     /* Convert UCS2 to to_set */
451     if ((size_t)-1 == ( o_len = convert_string_allocate_internal( CH_UCS2, to, (char*)u, i_len, dest)) )
452         LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
453
454     return o_len;
455
456 }
457
458 size_t charset_strupper(charset_t ch, const char *src, size_t srclen, char *dest, size_t destlen)
459 {
460     size_t size;
461     char *buffer;
462
463     size = convert_string_allocate_internal(ch, CH_UCS2, src, srclen,
464                                             (char**) &buffer);
465     if (size == (size_t)-1) {
466         SAFE_FREE(buffer);
467         return size;
468     }
469     if (!strupper_w((ucs2_t *)buffer) && (dest == src)) {
470         free(buffer);
471         return srclen;
472     }
473
474     size = convert_string_internal(CH_UCS2, ch, buffer, size, dest, destlen);
475     free(buffer);
476     return size;
477 }
478
479 size_t charset_strlower(charset_t ch, const char *src, size_t srclen, char *dest, size_t destlen)
480 {
481     size_t size;
482     char *buffer;
483
484     size = convert_string_allocate_internal(ch, CH_UCS2, src, srclen,
485                                             (char **) &buffer);
486     if (size == (size_t)-1) {
487         SAFE_FREE(buffer);
488         return size;
489     }
490     if (!strlower_w((ucs2_t *)buffer) && (dest == src)) {
491         free(buffer);
492         return srclen;
493     }
494
495     size = convert_string_internal(CH_UCS2, ch, buffer, size, dest, destlen);
496     free(buffer);
497     return size;
498 }
499
500
501 size_t unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
502 {
503     return charset_strupper( CH_UNIX, src, srclen, dest, destlen);
504 }
505
506 size_t unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
507 {
508     return charset_strlower( CH_UNIX, src, srclen, dest, destlen);
509 }
510
511 size_t utf8_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
512 {
513     return charset_strupper( CH_UTF8, src, srclen, dest, destlen);
514 }
515
516 size_t utf8_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
517 {
518     return charset_strlower( CH_UTF8, src, srclen, dest, destlen);
519 }
520
521 /**
522  * Copy a string from a charset_t char* src to a UCS2 destination, allocating a buffer
523  *
524  * @param dest always set at least to NULL
525  *
526  * @returns The number of bytes occupied by the string in the destination
527  *         or -1 in case of error.
528  **/
529
530 size_t charset_to_ucs2_allocate(charset_t ch, ucs2_t **dest, const char *src)
531 {
532     size_t src_len = strlen(src);
533
534     *dest = NULL;
535     return convert_string_allocate(ch, CH_UCS2, src, src_len, (char**) dest);
536 }
537
538 /** -----------------------------------
539  * Copy a string from a charset_t char* src to a UTF-8 destination, allocating a buffer
540  *
541  * @param dest always set at least to NULL
542  *
543  * @returns The number of bytes occupied by the string in the destination
544  **/
545
546 size_t charset_to_utf8_allocate(charset_t ch, char **dest, const char *src)
547 {
548     size_t src_len = strlen(src);
549
550     *dest = NULL;
551     return convert_string_allocate(ch, CH_UTF8, src, src_len, dest);
552 }
553
554 /** -----------------------------------
555  * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer
556  *
557  * @param dest always set at least to NULL
558  *
559  * @returns The number of bytes occupied by the string in the destination
560  **/
561
562 size_t ucs2_to_charset(charset_t ch, const ucs2_t *src, char *dest, size_t destlen)
563 {
564     size_t src_len = (strlen_w(src)) * sizeof(ucs2_t);
565     return convert_string(CH_UCS2, ch, src, src_len, dest, destlen);
566 }
567
568 /* --------------------------------- */
569 size_t ucs2_to_charset_allocate(charset_t ch, char **dest, const ucs2_t *src)
570 {
571     size_t src_len = (strlen_w(src)) * sizeof(ucs2_t);
572     *dest = NULL;
573     return convert_string_allocate(CH_UCS2, ch, src, src_len, dest);
574 }
575
576 /** ---------------------------------
577  * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer
578  *
579  * @param dest always set at least to NULL
580  *
581  * @returns The number of bytes occupied by the string in the destination
582  **/
583
584 size_t utf8_to_charset_allocate(charset_t ch, char **dest, const char *src)
585 {
586     size_t src_len = strlen(src);
587     *dest = NULL;
588     return convert_string_allocate(CH_UTF8, ch, src, src_len, dest);
589 }
590
591 size_t charset_precompose ( charset_t ch, char * src, size_t inlen, char * dst, size_t outlen)
592 {
593     char *buffer;
594     ucs2_t u[MAXPATHLEN];
595     size_t len;
596     size_t ilen;
597
598     if ((size_t)(-1) == (len = convert_string_allocate_internal(ch, CH_UCS2, src, inlen, &buffer)) )
599         return len;
600
601     ilen=sizeof(u);
602
603     if ( (size_t)-1 == (ilen = precompose_w((ucs2_t *)buffer, len, u, &ilen)) ) {
604         free (buffer);
605         return (size_t)(-1);
606     }
607
608     if ((size_t)(-1) == (len = convert_string_internal( CH_UCS2, ch, (char*)u, ilen, dst, outlen)) ) {
609         free (buffer);
610         return (size_t)(-1);
611     }
612
613     free(buffer);
614     return (len);
615 }
616
617 size_t charset_decompose ( charset_t ch, char * src, size_t inlen, char * dst, size_t outlen)
618 {
619     char *buffer;
620     ucs2_t u[MAXPATHLEN];
621     size_t len;
622     size_t ilen;
623
624     if ((size_t)(-1) == (len = convert_string_allocate_internal(ch, CH_UCS2, src, inlen, &buffer)) )
625         return len;
626
627     ilen=sizeof(u);
628
629     if ( (size_t)-1 == (ilen = decompose_w((ucs2_t *)buffer, len, u, &ilen)) ) {
630         free (buffer);
631         return (size_t)(-1);
632     }
633
634     if ((size_t)(-1) == (len = convert_string_internal( CH_UCS2, ch, (char*)u, ilen, dst, outlen)) ) {
635         free (buffer);
636         return (size_t)(-1);
637     }
638
639     free(buffer);
640     return (len);
641 }
642
643 size_t utf8_precompose ( char * src, size_t inlen, char * dst, size_t outlen)
644 {
645     return charset_precompose ( CH_UTF8, src, inlen, dst, outlen);
646 }
647
648 size_t utf8_decompose ( char * src, size_t inlen, char * dst, size_t outlen)
649 {
650     return charset_decompose ( CH_UTF8, src, inlen, dst, outlen);
651 }
652
653 #if 0
654 static char  debugbuf[ MAXPATHLEN +1 ];
655 char * debug_out ( char * seq, size_t len)
656 {
657     size_t i = 0;
658     unsigned char *p;
659     char *q;
660
661     p = (unsigned char*) seq;
662     q = debugbuf;
663
664     for ( i = 0; i<=(len-1); i++)
665     {
666         sprintf(q, "%2.2x.", *p);
667         q += 3;
668         p++;
669     }
670     *q=0;
671     q = debugbuf;
672     return q;
673 }
674 #endif
675
676 /*
677  * Convert from MB to UCS2 charset
678  * Flags:
679  *      CONV_UNESCAPEHEX:    ':XX' will be converted to an UCS2 character
680  *      CONV_IGNORE:         return the first convertable characters.
681  *      CONV_FORCE:  force convertion
682  * FIXME:
683  *      This will *not* work if the destination charset is not multibyte, i.e. UCS2->UCS2 will fail
684  *      The (un)escape scheme is not compatible to the old cap style escape. This is bad, we need it
685  *      for e.g. HFS cdroms.
686  */
687
688 static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const char *src, size_t srclen, char* dest, size_t destlen, uint16_t *flags)
689 {
690     const uint16_t option = (flags ? *flags : 0);
691     size_t i_len, o_len;
692     size_t j = 0;
693     const char* inbuf = (const char*)src;
694     char* outbuf = dest;
695     atalk_iconv_t descriptor;
696     atalk_iconv_t descriptor_cap;
697
698     if (srclen == (size_t)-1)
699         srclen = strlen(src) + 1;
700
701     descriptor = conv_handles[from_set][CH_UCS2];
702     descriptor_cap = conv_handles[cap_set][CH_UCS2];
703
704     if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
705         errno = EINVAL;
706         return (size_t)-1;
707     }
708
709     i_len=srclen;
710     o_len=destlen;
711
712     while (i_len > 0) {
713         for (j = 0; j < i_len; ++j)
714             if (inbuf[j] == ':')
715                 break;
716         j = i_len - j;
717         i_len -= j;
718
719         if (i_len > 0 &&
720             atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len) == (size_t)-1) {
721             if (errno == EILSEQ || errno == EINVAL) {
722                 errno = EILSEQ;
723                 if ((option & CONV_IGNORE)) {
724                     *flags |= CONV_REQMANGLE;
725                     return destlen - o_len;
726                 }
727                 if ((option & CONV__EILSEQ)) {
728                     if (o_len < 2) {
729                         errno = E2BIG;
730                         goto end;
731                     }
732                     *((ucs2_t *)outbuf) = (ucs2_t) IGNORE_CHAR; /**inbuf */
733                     inbuf++;
734                     i_len--;
735                     outbuf += 2;
736                     o_len -= 2;
737                     /* FIXME reset stat ? */
738                     continue;
739                 }
740             }
741             goto end;
742         }
743
744         if (j) {
745             /* we have a ':' */
746             i_len = j, j = 0;
747
748             if ((option & CONV_UNESCAPEHEX)) {
749                 /* treat it as a CAP hex encoded char */
750                 char h[MAXPATHLEN];
751                 size_t hlen = 0;
752
753                 while (i_len >= 3 && inbuf[0] == ':' &&
754                        isxdigit(inbuf[1]) && isxdigit(inbuf[2])) {
755                     h[hlen++] = (hextoint(inbuf[1]) << 4) | hextoint(inbuf[2]);
756                     inbuf += 3;
757                     i_len -= 3;
758                 }
759                 if (hlen) {
760                     const char *h_buf = h;
761                     if (atalk_iconv(descriptor_cap, &h_buf, &hlen, &outbuf, &o_len) == (size_t)-1) {
762                         i_len += hlen * 3;
763                         inbuf -= hlen * 3;
764                         if (errno == EILSEQ && (option & CONV_IGNORE)) {
765                             *flags |= CONV_REQMANGLE;
766                             return destlen - o_len;
767                         }
768                         goto end;
769                     }
770                 } else {
771                     /* We have an invalid :xx sequence */
772                     errno = EILSEQ;
773                     if ((option & CONV_IGNORE)) {
774                         *flags |= CONV_REQMANGLE;
775                         return destlen - o_len;
776                     }
777                     goto end;
778                 }
779             } else {
780                 /* a ':' that we just convert to a '/' */
781                 ucs2_t slash = 0x002f;
782                 memcpy(outbuf, &slash, sizeof(ucs2_t));
783                 outbuf += 2;
784                 o_len -= 2;
785                 inbuf++;
786                 i_len--;
787             }
788         }
789     }
790 end:
791     return (i_len + j == 0 || (option & CONV_FORCE)) ? destlen - o_len : (size_t)-1;
792 }
793
794 /*
795  * Convert from UCS2 to MB charset
796  * Flags:
797  *      CONV_ESCAPEDOTS: escape leading dots
798  *      CONV_ESCAPEHEX:  unconvertable characters and '/' will be escaped to :XX
799  *      CONV_IGNORE:     return the first convertable characters.
800  *      CONV__EILSEQ:    unconvertable characters will be replaced with '_'
801  *      CONV_FORCE:  force convertion
802  * FIXME:
803  *      CONV_IGNORE and CONV_ESCAPEHEX can't work together. Should we check this ?
804  *      This will *not* work if the destination charset is not multibyte, i.e. UCS2->UCS2 will fail
805  *      The escape scheme is not compatible to the old cap style escape. This is bad, we need it
806  *      for e.g. HFS cdroms.
807  */
808
809
810 static size_t push_charset_flags (charset_t to_set, charset_t cap_set, char* src, size_t srclen, char* dest, size_t destlen, uint16_t *flags)
811 {
812     const uint16_t option = (flags ? *flags : 0);
813     size_t i_len, o_len, i;
814     size_t j = 0;
815     const char* inbuf = (const char*)src;
816     char* outbuf = (char*)dest;
817     atalk_iconv_t descriptor;
818     atalk_iconv_t descriptor_cap;
819     char escch;                 /* 150210: uninitialized OK, depends on j */
820
821     descriptor = conv_handles[CH_UCS2][to_set];
822     descriptor_cap = conv_handles[CH_UCS2][cap_set];
823
824     if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
825         errno = EINVAL;
826         return (size_t) -1;
827     }
828
829     i_len=srclen;
830     o_len=destlen;
831
832     if ((option & CONV_ESCAPEDOTS) &&
833         i_len >= 2 && SVAL(inbuf, 0) == 0x002e) { /* 0x002e = . */
834         if (o_len < 3) {
835             errno = E2BIG;
836             goto end;
837         }
838         *outbuf++ = ':';
839         *outbuf++ = '2';
840         *outbuf++ = 'e';
841         o_len -= 3;
842         inbuf += 2;
843         i_len -= 2;
844         *flags |= CONV_REQESCAPE;
845     }
846
847     while (i_len >= 2) {
848         for (i = 0; i < i_len; i += 2) {
849             ucs2_t c = SVAL(inbuf, i);
850             switch (c) {
851             case 0x003a: /* 0x003a = ':' */
852                 if ( ! (option & CONV_ALLOW_COLON)) {
853                     errno = EILSEQ;
854                     goto end;
855                 }
856                 escch = c;
857                 j = i_len - i;
858                 i_len = i;
859                 break;
860             case 0x002f: /* 0x002f = '/' */
861                 escch = c;
862                 j = i_len - i;
863                 i_len = i;
864                 break;
865             }
866         }
867         while (i_len > 0 &&
868                atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len) == (size_t)-1) {
869             if (errno == EILSEQ) {
870                 if ((option & CONV_IGNORE)) {
871                     *flags |= CONV_REQMANGLE;
872                     return destlen - o_len;
873                 }
874                 if ((option & CONV_ESCAPEHEX)) {
875                     const size_t bufsiz = o_len / 3 + 1;
876                     char *buf = malloc(bufsiz);
877                     size_t buflen;
878
879                     if (!buf)
880                         goto end;
881                     i = i_len;
882                     for (buflen = 1; buflen <= bufsiz; ++buflen) {
883                         char *b = buf;
884                         size_t o = buflen;
885                         if (atalk_iconv(descriptor_cap, &inbuf, &i, &b, &o) != (size_t)-1) {
886                             buflen -= o;
887                             break;
888                         } else if (errno != E2BIG) {
889                             SAFE_FREE(buf);
890                             goto end;
891                         } else if (o < buflen) {
892                             buflen -= o;
893                             break;
894                         }
895                     }
896                     if (o_len < buflen * 3) {
897                         SAFE_FREE(buf);
898                         errno = E2BIG;
899                         goto end;
900                     }
901                     o_len -= buflen * 3;
902                     i_len = i;
903                     for (i = 0; i < buflen; ++i) {
904                         *outbuf++ = ':';
905                         *outbuf++ = hexdig[(buf[i] >> 4) & 0x0f];
906                         *outbuf++ = hexdig[buf[i] & 0x0f];
907                     }
908                     SAFE_FREE(buf);
909                     *flags |= CONV_REQESCAPE;
910                     continue;
911                 }
912             }
913             goto end;
914         }
915
916         if (j) {
917             /* we have a ':' or '/' */
918             i_len = j, j = 0;
919
920             if ((option & CONV_ESCAPEHEX)) {
921                 /* CAP hex encode it */
922                 if (o_len < 3) {
923                     errno = E2BIG;
924                     goto end;
925                 }
926                 switch (escch) {
927                 case '/':
928                     *outbuf++ = ':';
929                     *outbuf++ = '2';
930                     *outbuf++ = 'f';
931                     break;
932                 case ':':
933                     *outbuf++ = ':';
934                     *outbuf++ = '3';
935                     *outbuf++ = 'a';
936                     break;
937                 default:
938                     /*
939                      *  THIS SHOULD NEVER BE REACHED !!!
940                      *  As a safety net I put in a ' ' here
941                      */
942                     *outbuf++ = ':';
943                     *outbuf++ = '2';
944                     *outbuf++ = '0';
945                     break;
946                 }
947                 o_len -= 3;
948                 inbuf += 2;
949                 i_len -= 2;
950             } else {
951                 switch (escch) {
952                 case '/':
953                 case ':':
954                     *outbuf++ = ':';
955                     break;
956                 default: /* should never be reached */
957                     *outbuf++ = ' ';
958                     break;
959                 }
960                 o_len--;
961                 inbuf += 2;
962                 i_len -= 2;
963             }
964         }
965     }
966     if (i_len > 0) errno = EINVAL;
967 end:
968     return (i_len + j == 0 || (option & CONV_FORCE)) ? destlen - o_len : (size_t)-1;
969 }
970
971 /*
972  * FIXME the size is a mess we really need a malloc/free logic
973  *`dest size must be dest_len +2
974  */
975 size_t convert_charset ( charset_t from_set, charset_t to_set, charset_t cap_charset, const char *src, size_t src_len, char *dest, size_t dest_len, uint16_t *flags)
976 {
977     size_t i_len, o_len;
978     ucs2_t *u;
979     ucs2_t buffer[MAXPATHLEN +2];
980     ucs2_t buffer2[MAXPATHLEN +2];
981
982     lazy_initialize_conv();
983
984     /* convert from_set to UCS2 */
985     if ((size_t)(-1) == ( o_len = pull_charset_flags( from_set, cap_charset, src, src_len,
986                                                       (char *) buffer, sizeof(buffer) -2, flags)) ) {
987         LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from_set));
988         return (size_t) -1;
989     }
990
991     if ( o_len == 0)
992         return o_len;
993
994     /* Do pre/decomposition */
995     i_len = sizeof(buffer2) -2;
996     u = buffer2;
997     if (CHECK_FLAGS(flags, CONV_DECOMPOSE) || (charsets[to_set] && (charsets[to_set]->flags & CHARSET_DECOMPOSED)) ) {
998         if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
999             return (size_t)(-1);
1000     }
1001     else if (CHECK_FLAGS(flags, CONV_PRECOMPOSE) || !charsets[from_set] || (charsets[from_set]->flags & CHARSET_DECOMPOSED)) {
1002         if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
1003             return (size_t)(-1);
1004     }
1005     else {
1006         u = buffer;
1007         i_len = o_len;
1008     }
1009     /* null terminate */
1010     u[i_len] = 0;
1011     u[i_len +1] = 0;
1012
1013     /* Do case conversions */
1014     if (CHECK_FLAGS(flags, CONV_TOUPPER)) {
1015         strupper_w(u);
1016     }
1017     else if (CHECK_FLAGS(flags, CONV_TOLOWER)) {
1018         strlower_w(u);
1019     }
1020
1021     /* Convert UCS2 to to_set */
1022     if ((size_t)(-1) == ( o_len = push_charset_flags( to_set, cap_charset, (char *)u, i_len, dest, dest_len, flags )) ) {
1023         LOG(log_error, logtype_default,
1024             "Conversion failed (CH_UCS2 to %s):%s", charset_name(to_set), strerror(errno));
1025         return (size_t) -1;
1026     }
1027     /* null terminate */
1028     dest[o_len] = 0;
1029     dest[o_len +1] = 0;
1030
1031     return o_len;
1032 }