88f464c2c239406fd813508141b91c2838b0061e
[nivanova/samba-autobuild/.git] / lib / util / charset / 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) 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 "system/iconv.h"
25 #include "libcli/smb/smb_common.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  * Convert string from one encoding to another, making error checking etc
44  *
45  * @param mem_ctx Memory context
46  * @param cd Iconv handle
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  * @returns the number of bytes occupied in the destination
52  **/
53 _PUBLIC_ ssize_t iconv_talloc(TALLOC_CTX *ctx, 
54                                        smb_iconv_t cd,
55                                        void const *src, size_t srclen, 
56                                        void *dst)
57 {
58         size_t i_len, o_len, destlen;
59         void **dest = (void **)dst;
60         size_t retval;
61         const char *inbuf = (const char *)src;
62         char *outbuf, *ob;
63
64         *dest = NULL;
65
66         /* it is _very_ rare that a conversion increases the size by
67            more than 3x */
68         destlen = srclen;
69         outbuf = NULL;
70 convert:
71         destlen = 2 + (destlen*3);
72         ob = talloc_realloc(ctx, outbuf, char, destlen);
73         if (!ob) {
74                 DEBUG(0, ("iconv_talloc: realloc failed!\n"));
75                 talloc_free(outbuf);
76                 return (size_t)-1;
77         } else {
78                 outbuf = ob;
79         }
80
81         /* we give iconv 2 less bytes to allow us to terminate at the
82            end */
83         i_len = srclen;
84         o_len = destlen-2;
85         retval = smb_iconv(cd,
86                            &inbuf, &i_len,
87                            &outbuf, &o_len);
88         if(retval == (size_t)-1)                {
89                 const char *reason="unknown error";
90                 switch(errno) {
91                         case EINVAL:
92                                 reason="Incomplete multibyte sequence";
93                                 break;
94                         case E2BIG:
95                                 goto convert;           
96                         case EILSEQ:
97                                 reason="Illegal multibyte sequence";
98                                 break;
99                 }
100                 DEBUG(0,("Conversion error: %s - ",reason));
101                 dump_data(0, (const uint8_t *) inbuf, i_len);
102                 talloc_free(ob);
103                 return (size_t)-1;
104         }
105         
106         destlen = (destlen-2) - o_len;
107
108         /* guarantee null termination in all charsets */
109         SSVAL(ob, destlen, 0);
110
111         *dest = ob;
112
113         return destlen;
114
115 }
116
117 /**
118  * Convert string from one encoding to another, making error checking etc
119  *
120  * @param src pointer to source string (multibyte or singlebyte)
121  * @param srclen length of the source string in bytes
122  * @param dest pointer to destination string (multibyte or singlebyte)
123  * @param destlen maximal length allowed for string
124  * @returns the number of bytes occupied in the destination
125  * on error, returns -1, and sets errno
126  **/
127 _PUBLIC_ ssize_t convert_string_error(struct smb_iconv_convenience *ic,
128                                       charset_t from, charset_t to,
129                                       void const *src, size_t srclen,
130                                       void *dest, size_t destlen, size_t *converted_size)
131 {
132         size_t i_len, o_len;
133         ssize_t retval;
134         const char* inbuf = (const char*)src;
135         char* outbuf = (char*)dest;
136         smb_iconv_t descriptor;
137
138         if (srclen == (size_t)-1)
139                 srclen = strlen(inbuf)+1;
140
141         descriptor = get_conv_handle(ic, from, to);
142         if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
143                 if (converted_size) {
144                         *converted_size = 0;
145                 }
146                 errno = EINVAL;
147                 return -1;
148         }
149
150         i_len=srclen;
151         o_len=destlen;
152
153         retval = smb_iconv(descriptor,  &inbuf, &i_len, &outbuf, &o_len);
154
155         if (converted_size != NULL)
156                 *converted_size = destlen-o_len;
157         return retval;
158 }
159
160
161 /**
162  * Convert string from one encoding to another, making error checking etc
163  *
164  * @param src pointer to source string (multibyte or singlebyte)
165  * @param srclen length of the source string in bytes
166  * @param dest pointer to destination string (multibyte or singlebyte)
167  * @param destlen maximal length allowed for string
168  * @returns the number of bytes occupied in the destination
169  **/
170 _PUBLIC_ bool convert_string_convenience(struct smb_iconv_convenience *ic,
171                                          charset_t from, charset_t to,
172                                          void const *src, size_t srclen,
173                                          void *dest, size_t destlen, size_t *converted_size)
174 {
175         ssize_t retval;
176
177         retval = convert_string_error(ic, from, to, src, srclen, dest, destlen, converted_size);
178         if(retval==(size_t)-1) {
179                 const char *reason;
180                 switch(errno) {
181                 case EINVAL:
182                         reason="Incomplete multibyte sequence";
183                         return false;
184                 case E2BIG:
185                         reason="No more room";
186                         if (from == CH_UNIX) {
187                                 DEBUG(0,("E2BIG: convert_string(%s,%s): srclen=%d destlen=%d - '%s'\n",
188                                          charset_name(ic, from), charset_name(ic, to),
189                                          (int)srclen, (int)destlen,
190                                          (const char *)src));
191                         } else {
192                                 DEBUG(0,("E2BIG: convert_string(%s,%s): srclen=%d destlen=%d\n",
193                                          charset_name(ic, from), charset_name(ic, to),
194                                          (int)srclen, (int)destlen));
195                         }
196                         return false;
197                 case EILSEQ:
198                         reason="Illegal multibyte sequence";
199                         return false;
200                 default:
201                         return false;
202                 }
203         }
204         return true;
205 }
206         
207 /**
208  * Convert between character sets, allocating a new buffer using talloc for the result.
209  *
210  * @param srclen length of source buffer.
211  * @param dest always set at least to NULL
212  * @note -1 is not accepted for srclen.
213  *
214  * @returns Size in bytes of the converted string; or -1 in case of error.
215  **/
216
217 _PUBLIC_ bool convert_string_talloc_convenience(TALLOC_CTX *ctx, 
218                                                 struct smb_iconv_convenience *ic, 
219                                                 charset_t from, charset_t to, 
220                                                 void const *src, size_t srclen, 
221                                                 void *dst, size_t *converted_size)
222 {
223         void **dest = (void **)dst;
224         smb_iconv_t descriptor;
225         ssize_t ret;
226
227         *dest = NULL;
228
229         if (src == NULL || srclen == (size_t)-1 || srclen == 0)
230                 return false;
231
232         descriptor = get_conv_handle(ic, from, to);
233
234         if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
235                 /* conversion not supported, return -1*/
236                 DEBUG(3, ("convert_string_talloc: conversion from %s to %s not supported!\n",
237                           charset_name(ic, from), 
238                           charset_name(ic, to)));
239                 return false;
240         }
241
242         ret = iconv_talloc(ctx, descriptor, src, srclen, dest);
243         if (ret == -1)
244                 return false;
245         if (converted_size != NULL)
246                 *converted_size = ret;
247         return true;
248 }
249