add convert_string_allocate() function
[samba.git] / source3 / lib / charcnv.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    Character set conversion Extensions
5    Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001
6    Copyright (C) Andrew Tridgell 2001
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 */
23 #include "includes.h"
24
25 static pstring cvtbuf;
26
27 static smb_iconv_t conv_handles[NUM_CHARSETS][NUM_CHARSETS];
28
29 static int initialized;
30
31 /****************************************************************************
32 return the name of a charset to give to iconv()
33 ****************************************************************************/
34 static char *charset_name(charset_t ch)
35 {
36         char *ret = NULL;
37
38         if (ch == CH_UCS2) ret = "UCS-2LE";
39         else if (ch == CH_UNIX) ret = lp_unix_charset();
40         else if (ch == CH_DOS) ret = lp_dos_charset();
41         else if (ch == CH_DISPLAY) ret = lp_display_charset();
42
43         if (!ret || !*ret) ret = "ASCII";
44         return ret;
45 }
46
47 /****************************************************************************
48  Initialize iconv conversion descriptors 
49 ****************************************************************************/
50 void init_iconv(void)
51 {
52         int c1, c2;
53
54         /* so that charset_name() works we need to get the UNIX<->UCS2 going
55            first */
56         if (!conv_handles[CH_UNIX][CH_UCS2]) {
57                 conv_handles[CH_UNIX][CH_UCS2] = smb_iconv_open("UCS-2LE", "ASCII");
58         }
59         if (!conv_handles[CH_UCS2][CH_UNIX]) {
60                 conv_handles[CH_UCS2][CH_UNIX] = smb_iconv_open("ASCII", "UCS-2LE");
61         }
62         
63
64         for (c1=0;c1<NUM_CHARSETS;c1++) {
65                 for (c2=0;c2<NUM_CHARSETS;c2++) {
66                         char *n1 = charset_name((charset_t)c1);
67                         char *n2 = charset_name((charset_t)c2);
68                         if (conv_handles[c1][c2]) {
69                                 smb_iconv_close(conv_handles[c1][c2]);
70                         }
71                         conv_handles[c1][c2] = smb_iconv_open(n2,n1);
72                         if (conv_handles[c1][c2] == (smb_iconv_t)-1) {
73                                 DEBUG(0,("Conversion from %s to %s not supported\n",
74                                          charset_name((charset_t)c1), charset_name((charset_t)c2)));
75                                 conv_handles[c1][c2] = NULL;
76                         }
77                 }
78         }
79 }
80
81 /****************************************************************************
82  Convert string from one encoding to another, making error checking etc
83  Parameters:
84         descriptor - conversion descriptor, created in init_iconv
85         src - pointer to source string (multibyte or singlebyte)
86         srclen - length of the source string in bytes
87         dest - pointer to destination string (multibyte or singlebyte)
88         destlen - maximal length allowed for string
89 return the number of bytes occupied in the destination
90 ****************************************************************************/
91 size_t convert_string(charset_t from, charset_t to,
92                       void const *src, size_t srclen, 
93                       void *dest, size_t destlen)
94 {
95         size_t i_len, o_len;
96         size_t retval;
97         char* inbuf = (char*)src;
98         char* outbuf = (char*)dest;
99         smb_iconv_t descriptor;
100
101         if (srclen == -1) srclen = strlen(src)+1;
102
103         if (!initialized) {
104                 initialized = 1;
105                 load_case_tables();
106                 init_iconv();
107         }
108
109         descriptor = conv_handles[from][to];
110
111         if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
112                 /* conversion not supported, use as is */
113                 int len = MIN(srclen,destlen);
114                 memcpy(dest,src,len);
115                 return len;
116         }
117
118         i_len=srclen;
119         o_len=destlen;
120         retval=smb_iconv(descriptor,&inbuf, &i_len, &outbuf, &o_len);
121         if(retval==-1)          
122         {
123                 char *reason="unknown error";
124                 switch(errno)
125                 { case EINVAL: reason="Incomplete multibyte sequence"; break;
126                   case E2BIG:  reason="No more room"; 
127                                DEBUG(0, ("Required %d, available %d\n",
128                                srclen, destlen));
129                                /* we are not sure we need srclen bytes,
130                                   may be more, may be less.
131                                   We only know we need more than destlen
132                                   bytes ---simo */
133                                 
134                                                 
135                                break;
136                   case EILSEQ: reason="Illegal myltibyte sequence"; break;
137                 }
138                 DEBUG(0,("Conversion error: %s(%s)\n",reason,inbuf));
139                 /* smb_panic(reason); */
140         }
141         return destlen-o_len;
142 }
143
144 /* you must provide source lenght -1 is not accepted as lenght.
145    this function will return the size in bytes of the converted string
146    or -1 in case of error.
147  */
148 size_t convert_string_allocate(charset_t from, charset_t to,
149                                 void const *src, size_t srclen, void **dest)
150 {
151         size_t i_len, o_len, destlen;
152         size_t retval;
153         char* inbuf = (char *)src;
154         char *outbuf, *ob;
155         smb_iconv_t descriptor;
156
157         *dest = NULL;
158
159         if (src == NULL || srclen == -1) return -1;
160
161         if (!initialized) {
162                 initialized = 1;
163                 load_case_tables();
164                 init_iconv();
165         }
166
167         descriptor = conv_handles[from][to];
168
169         if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
170                 /* conversion not supported, return -1*/
171                 return -1;
172         }
173
174         destlen = MAX(srclen, 1024);
175         outbuf = NULL;
176 convert:
177         destlen = destlen * 2;
178         ob = (char *)realloc(outbuf, destlen);
179         if (!ob) {
180                 DEBUG(0, ("convert_string_allocate: realloc failed!\n"));
181                 free(outbuf);
182                 return -1;
183         }
184         else outbuf = ob;
185         i_len = srclen;
186         o_len = destlen;
187         retval = smb_iconv(descriptor,
188                                 &inbuf, &i_len,
189                                 &outbuf, &o_len);
190         if(retval == -1)                
191         {
192                 char *reason="unknown error";
193                 switch(errno)
194                 {
195                 case EINVAL:
196                         reason="Incomplete multibyte sequence";
197                         break;
198                 case E2BIG:
199                         goto convert;           
200                         break;
201                 case EILSEQ:
202                         reason="Illegal myltibyte sequence";
203                         break;
204                 }
205                 DEBUG(0,("Conversion error: %s(%s)\n",reason,inbuf));
206                 /* smb_panic(reason); */
207                 return -1;
208         }
209         
210         destlen = destlen - o_len;
211         *dest = (char *)malloc(destlen);
212         if (!*dest) {
213                 DEBUG(0, ("convert_string_allocate: out of memory!\n"));
214                 free(outbuf);
215                 return -1;
216         }
217         memcpy(*dest, outbuf, destlen);
218         free(outbuf);
219         
220         return destlen;
221 }
222
223 int unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
224 {
225         int size;
226         smb_ucs2_t *buffer=(smb_ucs2_t*)cvtbuf;
227         size=convert_string(CH_UNIX, CH_UCS2, src, srclen, buffer, sizeof(cvtbuf));
228         if (!strupper_w(buffer) && (dest == src)) return srclen;
229         return convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen);
230 }
231
232 int unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
233 {
234         int size;
235         smb_ucs2_t *buffer=(smb_ucs2_t*)cvtbuf;
236         size=convert_string(CH_UNIX, CH_UCS2, src, srclen, buffer, sizeof(cvtbuf));
237         if (!strlower_w(buffer) && (dest == src)) return srclen;
238         return convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen);
239 }
240
241
242 int ucs2_align(const void *base_ptr, const void *p, int flags)
243 {
244         if (flags & (STR_NOALIGN|STR_ASCII)) return 0;
245         return PTR_DIFF(p, base_ptr) & 1;
246 }
247
248
249 /****************************************************************************
250 copy a string from a char* unix src to a dos codepage string destination
251 return the number of bytes occupied by the string in the destination
252 flags can have:
253   STR_TERMINATE means include the null termination
254   STR_UPPER     means uppercase in the destination
255 dest_len is the maximum length allowed in the destination. If dest_len
256 is -1 then no maxiumum is used
257 ****************************************************************************/
258 int push_ascii(void *dest, const char *src, int dest_len, int flags)
259 {
260         int src_len = strlen(src);
261         pstring tmpbuf;
262
263         /* treat a pstring as "unlimited" length */
264         if (dest_len == -1) {
265                 dest_len = sizeof(pstring);
266         }
267
268         if (flags & STR_UPPER) {
269                 pstrcpy(tmpbuf, src);
270                 strupper(tmpbuf);
271                 src = tmpbuf;
272         }
273
274         if (flags & STR_TERMINATE) {
275                 src_len++;
276         }
277
278         return convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len);
279 }
280
281 int push_ascii_fstring(void *dest, const char *src)
282 {
283         return push_ascii(dest, src, sizeof(fstring), STR_TERMINATE);
284 }
285
286 int push_ascii_pstring(void *dest, const char *src)
287 {
288         return push_ascii(dest, src, sizeof(pstring), STR_TERMINATE);
289 }
290
291 int push_pstring(void *dest, const char *src)
292 {
293         return push_ascii(dest, src, sizeof(pstring), STR_TERMINATE);
294 }
295
296
297 /****************************************************************************
298 copy a string from a dos codepage source to a unix char* destination
299 flags can have:
300   STR_TERMINATE means the string in src is null terminated
301 if STR_TERMINATE is set then src_len is ignored
302 src_len is the length of the source area in bytes
303 return the number of bytes occupied by the string in src
304 the resulting string in "dest" is always null terminated
305 ****************************************************************************/
306 int pull_ascii(char *dest, const void *src, int dest_len, int src_len, int flags)
307 {
308         int ret;
309
310         if (dest_len == -1) {
311                 dest_len = sizeof(pstring);
312         }
313
314         if (flags & STR_TERMINATE) src_len = strlen(src)+1;
315
316         ret = convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len);
317
318         if (dest_len) dest[MIN(ret, dest_len-1)] = 0;
319
320         return src_len;
321 }
322
323 int pull_ascii_pstring(char *dest, const void *src)
324 {
325         return pull_ascii(dest, src, sizeof(pstring), -1, STR_TERMINATE);
326 }
327
328 int pull_ascii_fstring(char *dest, const void *src)
329 {
330         return pull_ascii(dest, src, sizeof(fstring), -1, STR_TERMINATE);
331 }
332
333 /****************************************************************************
334 copy a string from a char* src to a unicode destination
335 return the number of bytes occupied by the string in the destination
336 flags can have:
337   STR_TERMINATE means include the null termination
338   STR_UPPER     means uppercase in the destination
339   STR_NOALIGN   means don't do alignment
340 dest_len is the maximum length allowed in the destination. If dest_len
341 is -1 then no maxiumum is used
342 ****************************************************************************/
343 int push_ucs2(const void *base_ptr, void *dest, const char *src, int dest_len, int flags)
344 {
345         int len=0;
346         int src_len = strlen(src);
347         pstring tmpbuf;
348
349         /* treat a pstring as "unlimited" length */
350         if (dest_len == -1) {
351                 dest_len = sizeof(pstring);
352         }
353
354         if (flags & STR_UPPER) {
355                 pstrcpy(tmpbuf, src);
356                 strupper(tmpbuf);
357                 src = tmpbuf;
358         }
359
360         if (flags & STR_TERMINATE) {
361                 src_len++;
362         }
363
364         if (ucs2_align(base_ptr, dest, flags)) {
365                 *(char *)dest = 0;
366                 dest = (void *)((char *)dest + 1);
367                 if (dest_len) dest_len--;
368                 len++;
369         }
370
371         /* ucs2 is always a multiple of 2 bytes */
372         dest_len &= ~1;
373
374         len += convert_string(CH_UNIX, CH_UCS2, src, src_len, dest, dest_len);
375         return len;
376 }
377
378
379 /****************************************************************************
380 copy a string from a ucs2 source to a unix char* destination
381 flags can have:
382   STR_TERMINATE means the string in src is null terminated
383   STR_NOALIGN   means don't try to align
384 if STR_TERMINATE is set then src_len is ignored
385 src_len is the length of the source area in bytes
386 return the number of bytes occupied by the string in src
387 the resulting string in "dest" is always null terminated
388 ****************************************************************************/
389 int pull_ucs2(const void *base_ptr, char *dest, const void *src, int dest_len, int src_len, int flags)
390 {
391         int ret;
392
393         if (dest_len == -1) {
394                 dest_len = sizeof(pstring);
395         }
396
397         if (ucs2_align(base_ptr, src, flags)) {
398                 src = (const void *)((const char *)src + 1);
399                 if (src_len > 0) src_len--;
400         }
401
402         if (flags & STR_TERMINATE) src_len = strlen_w(src)*2+2;
403
404         /* ucs2 is always a multiple of 2 bytes */
405         src_len &= ~1;
406         
407         ret = convert_string(CH_UCS2, CH_UNIX, src, src_len, dest, dest_len);
408         if (dest_len) dest[MIN(ret, dest_len-1)] = 0;
409
410         return src_len;
411 }
412
413 int pull_ucs2_pstring(char *dest, const void *src)
414 {
415         return pull_ucs2(NULL, dest, src, sizeof(pstring), -1, STR_TERMINATE);
416 }
417
418 int pull_ucs2_fstring(char *dest, const void *src)
419 {
420         return pull_ucs2(NULL, dest, src, sizeof(fstring), -1, STR_TERMINATE);
421 }
422
423
424 /****************************************************************************
425 copy a string from a char* src to a unicode or ascii
426 dos codepage destination choosing unicode or ascii based on the 
427 flags in the SMB buffer starting at base_ptr
428 return the number of bytes occupied by the string in the destination
429 flags can have:
430   STR_TERMINATE means include the null termination
431   STR_UPPER     means uppercase in the destination
432   STR_ASCII     use ascii even with unicode packet
433   STR_NOALIGN   means don't do alignment
434 dest_len is the maximum length allowed in the destination. If dest_len
435 is -1 then no maxiumum is used
436 ****************************************************************************/
437 int push_string(const void *base_ptr, void *dest, const char *src, int dest_len, int flags)
438 {
439         if (!(flags & STR_ASCII) && \
440             ((flags & STR_UNICODE || \
441               (SVAL(base_ptr, smb_flg2) & FLAGS2_UNICODE_STRINGS)))) {
442                 return push_ucs2(base_ptr, dest, src, dest_len, flags);
443         }
444         return push_ascii(dest, src, dest_len, flags);
445 }
446
447
448 /****************************************************************************
449 copy a string from a unicode or ascii source (depending on
450 the packet flags) to a char* destination
451 flags can have:
452   STR_TERMINATE means the string in src is null terminated
453   STR_UNICODE   means to force as unicode
454   STR_ASCII     use ascii even with unicode packet
455   STR_NOALIGN   means don't do alignment
456 if STR_TERMINATE is set then src_len is ignored
457 src_len is the length of the source area in bytes
458 return the number of bytes occupied by the string in src
459 the resulting string in "dest" is always null terminated
460 ****************************************************************************/
461 int pull_string(const void *base_ptr, char *dest, const void *src, int dest_len, int src_len, 
462                 int flags)
463 {
464         if (!(flags & STR_ASCII) && \
465             ((flags & STR_UNICODE || \
466               (SVAL(base_ptr, smb_flg2) & FLAGS2_UNICODE_STRINGS)))) {
467                 return pull_ucs2(base_ptr, dest, src, dest_len, src_len, flags);
468         }
469         return pull_ascii(dest, src, dest_len, src_len, flags);
470 }
471
472 int align_string(const void *base_ptr, const char *p, int flags)
473 {
474         if (!(flags & STR_ASCII) && \
475             ((flags & STR_UNICODE || \
476               (SVAL(base_ptr, smb_flg2) & FLAGS2_UNICODE_STRINGS)))) {
477                 return ucs2_align(base_ptr, p, flags);
478         }
479         return 0;
480 }