48b9cad7319bbe02444ac190aa200f9ecf112b17
[kai/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    Copyright (C) Simo Sorce 2001
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 #include "includes.h"
25
26 static pstring cvtbuf;
27
28 static smb_iconv_t conv_handles[NUM_CHARSETS][NUM_CHARSETS];
29
30 /****************************************************************************
31 return the name of a charset to give to iconv()
32 ****************************************************************************/
33 static char *charset_name(charset_t ch)
34 {
35         char *ret = NULL;
36
37         if (ch == CH_UCS2) ret = "UCS-2LE";
38         else if (ch == CH_UNIX) ret = lp_unix_charset();
39         else if (ch == CH_DOS) ret = lp_dos_charset();
40         else if (ch == CH_DISPLAY) ret = lp_display_charset();
41
42         if (!ret || !*ret) ret = "ASCII";
43         return ret;
44 }
45
46
47 static void lazy_initialize_conv(void)
48 {
49         static int initialized = False;
50
51         if (!initialized) {
52                 initialized = True;
53                 load_case_tables();
54                 init_iconv();
55                 init_valid_table();
56         }
57 }
58
59 /****************************************************************************
60  Initialize iconv conversion descriptors 
61 ****************************************************************************/
62 void init_iconv(void)
63 {
64         int c1, c2;
65         BOOL did_reload = False;
66
67         /* so that charset_name() works we need to get the UNIX<->UCS2 going
68            first */
69         if (!conv_handles[CH_UNIX][CH_UCS2]) {
70                 conv_handles[CH_UNIX][CH_UCS2] = smb_iconv_open("UCS-2LE", "ASCII");
71         }
72         if (!conv_handles[CH_UCS2][CH_UNIX]) {
73                 conv_handles[CH_UCS2][CH_UNIX] = smb_iconv_open("ASCII", "UCS-2LE");
74         }
75         
76
77         for (c1=0;c1<NUM_CHARSETS;c1++) {
78                 for (c2=0;c2<NUM_CHARSETS;c2++) {
79                         char *n1 = charset_name((charset_t)c1);
80                         char *n2 = charset_name((charset_t)c2);
81                         if (conv_handles[c1][c2] &&
82                             strcmp(n1, conv_handles[c1][c2]->from_name) == 0 &&
83                             strcmp(n2, conv_handles[c1][c2]->to_name) == 0) continue;
84
85                         did_reload = True;
86
87                         if (conv_handles[c1][c2]) {
88                                 smb_iconv_close(conv_handles[c1][c2]);
89                         }
90                         conv_handles[c1][c2] = smb_iconv_open(n2,n1);
91                         if (conv_handles[c1][c2] == (smb_iconv_t)-1) {
92                                 DEBUG(0,("Conversion from %s to %s not supported\n",
93                                          charset_name((charset_t)c1), charset_name((charset_t)c2)));
94                                 conv_handles[c1][c2] = NULL;
95                         }
96                 }
97         }
98
99         if (did_reload) {
100                 init_valid_table();
101         }
102 }
103
104 /**
105  * Convert string from one encoding to another, making error checking etc
106  *
107  * @param descriptor conversion descriptor, created in init_iconv()
108  * @param src pointer to source string (multibyte or singlebyte)
109  * @param srclen length of the source string in bytes
110  * @param dest pointer to destination string (multibyte or singlebyte)
111  * @param destlen maximal length allowed for string
112  * @retval the number of bytes occupied in the destination
113  **/
114 size_t convert_string(charset_t from, charset_t to,
115                       void const *src, size_t srclen, 
116                       void *dest, size_t destlen)
117 {
118         size_t i_len, o_len;
119         size_t retval;
120         const char* inbuf = (const char*)src;
121         char* outbuf = (char*)dest;
122         smb_iconv_t descriptor;
123
124         if (srclen == -1) srclen = strlen(src)+1;
125
126         lazy_initialize_conv();
127
128         descriptor = conv_handles[from][to];
129
130         if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
131                 /* conversion not supported, use as is */
132                 int len = MIN(srclen,destlen);
133                 memcpy(dest,src,len);
134                 return len;
135         }
136
137         i_len=srclen;
138         o_len=destlen;
139         retval = smb_iconv(descriptor,  &inbuf, &i_len, &outbuf, &o_len);
140         if(retval==-1)          
141         {
142                 char *reason="unknown error";
143                 switch(errno)
144                 { case EINVAL: reason="Incomplete multibyte sequence"; break;
145                   case E2BIG:  reason="No more room"; 
146                                DEBUG(0, ("Required %d, available %d\n",
147                                srclen, destlen));
148                                /* we are not sure we need srclen bytes,
149                                   may be more, may be less.
150                                   We only know we need more than destlen
151                                   bytes ---simo */
152                                 
153                                                 
154                                break;
155                   case EILSEQ: reason="Illegal myltibyte sequence"; break;
156                 }
157                 /* smb_panic(reason); */
158         }
159         return destlen-o_len;
160 }
161
162 /**
163  * Convert between character sets, allocating a new buffer for the result.
164  *
165  * @param srclen length of source buffer.
166  * @note -1 is not accepted for srclen.
167  *
168  * @retval Size in bytes of the converted string; or -1 in case of error.
169  **/
170 size_t convert_string_allocate(charset_t from, charset_t to,
171                                 void const *src, size_t srclen, void **dest)
172 {
173         size_t i_len, o_len, destlen;
174         size_t retval;
175         const char *inbuf = (const char *)src;
176         char *outbuf, *ob;
177         smb_iconv_t descriptor;
178
179         *dest = NULL;
180
181         if (src == NULL || srclen == -1) return -1;
182
183         lazy_initialize_conv();
184
185         descriptor = conv_handles[from][to];
186
187         if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
188                 /* conversion not supported, return -1*/
189                 return -1;
190         }
191
192         destlen = MAX(srclen, 512);
193         outbuf = NULL;
194 convert:
195         destlen = destlen * 2;
196         ob = (char *)realloc(outbuf, destlen);
197         if (!ob) {
198                 DEBUG(0, ("convert_string_allocate: realloc failed!\n"));
199                 SAFE_FREE(outbuf);
200                 return -1;
201         }
202         else outbuf = ob;
203         i_len = srclen;
204         o_len = destlen;
205         retval = smb_iconv(descriptor,
206                            &inbuf, &i_len,
207                            &outbuf, &o_len);
208         if(retval == -1)                
209         {
210                 char *reason="unknown error";
211                 switch(errno)
212                 {
213                 case EINVAL:
214                         reason="Incomplete multibyte sequence";
215                         break;
216                 case E2BIG:
217                         goto convert;           
218                 case EILSEQ:
219                         reason="Illegal myltibyte sequence";
220                         break;
221                 }
222                 DEBUG(0,("Conversion error: %s(%s)\n",reason,inbuf));
223                 /* smb_panic(reason); */
224                 return -1;
225         }
226         
227         destlen = destlen - o_len;
228         *dest = (char *)Realloc(ob,destlen);
229         if (!*dest) {
230                 DEBUG(0, ("convert_string_allocate: out of memory!\n"));
231                 SAFE_FREE(ob);
232                 return -1;
233         }
234
235         return destlen;
236 }
237
238 int unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
239 {
240         int size;
241         smb_ucs2_t *buffer=(smb_ucs2_t*)cvtbuf;
242         size=convert_string(CH_UNIX, CH_UCS2, src, srclen, buffer, sizeof(cvtbuf));
243         if (!strupper_w(buffer) && (dest == src)) return srclen;
244         return convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen);
245 }
246
247 int unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
248 {
249         int size;
250         smb_ucs2_t *buffer=(smb_ucs2_t*)cvtbuf;
251         size=convert_string(CH_UNIX, CH_UCS2, src, srclen, buffer, sizeof(cvtbuf));
252         if (!strlower_w(buffer) && (dest == src)) return srclen;
253         return convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen);
254 }
255
256
257 int ucs2_align(const void *base_ptr, const void *p, int flags)
258 {
259         if (flags & (STR_NOALIGN|STR_ASCII)) return 0;
260         return PTR_DIFF(p, base_ptr) & 1;
261 }
262
263
264 /****************************************************************************
265 copy a string from a char* unix src to a dos codepage string destination
266 return the number of bytes occupied by the string in the destination
267 flags can have:
268   STR_TERMINATE means include the null termination
269   STR_UPPER     means uppercase in the destination
270 dest_len is the maximum length allowed in the destination. If dest_len
271 is -1 then no maxiumum is used
272 ****************************************************************************/
273 int push_ascii(void *dest, const char *src, int dest_len, int flags)
274 {
275         int src_len = strlen(src);
276         pstring tmpbuf;
277
278         /* treat a pstring as "unlimited" length */
279         if (dest_len == -1) {
280                 dest_len = sizeof(pstring);
281         }
282
283         if (flags & STR_UPPER) {
284                 pstrcpy(tmpbuf, src);
285                 strupper(tmpbuf);
286                 src = tmpbuf;
287         }
288
289         if (flags & STR_TERMINATE) {
290                 src_len++;
291         }
292
293         return convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len);
294 }
295
296 int push_ascii_fstring(void *dest, const char *src)
297 {
298         return push_ascii(dest, src, sizeof(fstring), STR_TERMINATE);
299 }
300
301 int push_ascii_pstring(void *dest, const char *src)
302 {
303         return push_ascii(dest, src, sizeof(pstring), STR_TERMINATE);
304 }
305
306 int push_pstring(void *dest, const char *src)
307 {
308         return push_ascii(dest, src, sizeof(pstring), STR_TERMINATE);
309 }
310
311
312 /****************************************************************************
313 copy a string from a dos codepage source to a unix char* destination
314 flags can have:
315   STR_TERMINATE means the string in src is null terminated
316 if STR_TERMINATE is set then src_len is ignored
317 src_len is the length of the source area in bytes
318 return the number of bytes occupied by the string in src
319 the resulting string in "dest" is always null terminated
320 ****************************************************************************/
321 int pull_ascii(char *dest, const void *src, int dest_len, int src_len, int flags)
322 {
323         int ret;
324
325         if (dest_len == -1) {
326                 dest_len = sizeof(pstring);
327         }
328
329         if (flags & STR_TERMINATE) src_len = strlen(src)+1;
330
331         ret = convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len);
332
333         if (dest_len) dest[MIN(ret, dest_len-1)] = 0;
334
335         return src_len;
336 }
337
338 int pull_ascii_pstring(char *dest, const void *src)
339 {
340         return pull_ascii(dest, src, sizeof(pstring), -1, STR_TERMINATE);
341 }
342
343 int pull_ascii_fstring(char *dest, const void *src)
344 {
345         return pull_ascii(dest, src, sizeof(fstring), -1, STR_TERMINATE);
346 }
347
348 /****************************************************************************
349 copy a string from a char* src to a unicode destination
350 return the number of bytes occupied by the string in the destination
351 flags can have:
352   STR_TERMINATE means include the null termination
353   STR_UPPER     means uppercase in the destination
354   STR_NOALIGN   means don't do alignment
355 dest_len is the maximum length allowed in the destination. If dest_len
356 is -1 then no maxiumum is used
357 ****************************************************************************/
358 int push_ucs2(const void *base_ptr, void *dest, const char *src, int dest_len, int flags)
359 {
360         int len=0;
361         int src_len = strlen(src);
362         pstring tmpbuf;
363
364         /* treat a pstring as "unlimited" length */
365         if (dest_len == -1) {
366                 dest_len = sizeof(pstring);
367         }
368
369         if (flags & STR_UPPER) {
370                 pstrcpy(tmpbuf, src);
371                 strupper(tmpbuf);
372                 src = tmpbuf;
373         }
374
375         if (flags & STR_TERMINATE) {
376                 src_len++;
377         }
378
379         if (ucs2_align(base_ptr, dest, flags)) {
380                 *(char *)dest = 0;
381                 dest = (void *)((char *)dest + 1);
382                 if (dest_len) dest_len--;
383                 len++;
384         }
385
386         /* ucs2 is always a multiple of 2 bytes */
387         dest_len &= ~1;
388
389         len += convert_string(CH_UNIX, CH_UCS2, src, src_len, dest, dest_len);
390         return len;
391 }
392
393
394 /****************************************************************************
395 copy a string from a ucs2 source to a unix char* destination
396 flags can have:
397   STR_TERMINATE means the string in src is null terminated
398   STR_NOALIGN   means don't try to align
399 if STR_TERMINATE is set then src_len is ignored
400 src_len is the length of the source area in bytes
401 return the number of bytes occupied by the string in src
402 the resulting string in "dest" is always null terminated
403 ****************************************************************************/
404 int pull_ucs2(const void *base_ptr, char *dest, const void *src, int dest_len, int src_len, int flags)
405 {
406         int ret;
407
408         if (dest_len == -1) {
409                 dest_len = sizeof(pstring);
410         }
411
412         if (ucs2_align(base_ptr, src, flags)) {
413                 src = (const void *)((const char *)src + 1);
414                 if (src_len > 0) src_len--;
415         }
416
417         if (flags & STR_TERMINATE) src_len = strlen_w(src)*2+2;
418
419         /* ucs2 is always a multiple of 2 bytes */
420         src_len &= ~1;
421         
422         ret = convert_string(CH_UCS2, CH_UNIX, src, src_len, dest, dest_len);
423         if (dest_len) dest[MIN(ret, dest_len-1)] = 0;
424
425         return src_len;
426 }
427
428 int pull_ucs2_pstring(char *dest, const void *src)
429 {
430         return pull_ucs2(NULL, dest, src, sizeof(pstring), -1, STR_TERMINATE);
431 }
432
433 int pull_ucs2_fstring(char *dest, const void *src)
434 {
435         return pull_ucs2(NULL, dest, src, sizeof(fstring), -1, STR_TERMINATE);
436 }
437
438
439 /****************************************************************************
440 copy a string from a char* src to a unicode or ascii
441 dos codepage destination choosing unicode or ascii based on the 
442 flags in the SMB buffer starting at base_ptr
443 return the number of bytes occupied by the string in the destination
444 flags can have:
445   STR_TERMINATE means include the null termination
446   STR_UPPER     means uppercase in the destination
447   STR_ASCII     use ascii even with unicode packet
448   STR_NOALIGN   means don't do alignment
449 dest_len is the maximum length allowed in the destination. If dest_len
450 is -1 then no maxiumum is used
451 ****************************************************************************/
452 int push_string(const void *base_ptr, void *dest, const char *src, int dest_len, int flags)
453 {
454         if (!(flags & STR_ASCII) && \
455             ((flags & STR_UNICODE || \
456               (SVAL(base_ptr, smb_flg2) & FLAGS2_UNICODE_STRINGS)))) {
457                 return push_ucs2(base_ptr, dest, src, dest_len, flags);
458         }
459         return push_ascii(dest, src, dest_len, flags);
460 }
461
462
463 /****************************************************************************
464 copy a string from a unicode or ascii source (depending on
465 the packet flags) to a char* destination
466 flags can have:
467   STR_TERMINATE means the string in src is null terminated
468   STR_UNICODE   means to force as unicode
469   STR_ASCII     use ascii even with unicode packet
470   STR_NOALIGN   means don't do alignment
471 if STR_TERMINATE is set then src_len is ignored
472 src_len is the length of the source area in bytes
473 return the number of bytes occupied by the string in src
474 the resulting string in "dest" is always null terminated
475 ****************************************************************************/
476 int pull_string(const void *base_ptr, char *dest, const void *src, int dest_len, int src_len, 
477                 int flags)
478 {
479         if (!(flags & STR_ASCII) && \
480             ((flags & STR_UNICODE || \
481               (SVAL(base_ptr, smb_flg2) & FLAGS2_UNICODE_STRINGS)))) {
482                 return pull_ucs2(base_ptr, dest, src, dest_len, src_len, flags);
483         }
484         return pull_ascii(dest, src, dest_len, src_len, flags);
485 }
486
487 int align_string(const void *base_ptr, const char *p, int flags)
488 {
489         if (!(flags & STR_ASCII) && \
490             ((flags & STR_UNICODE || \
491               (SVAL(base_ptr, smb_flg2) & FLAGS2_UNICODE_STRINGS)))) {
492                 return ucs2_align(base_ptr, p, flags);
493         }
494         return 0;
495 }
496
497
498
499 /****************************************************************************
500 convert from ucs2 to unix charset and return the
501 allocated and converted string or NULL if an error occurred.
502 you must provide a zero terminated string.
503 the returning string will be zero terminated.
504 ****************************************************************************/
505 char *acnv_u2ux(const smb_ucs2_t *src)
506 {
507         size_t slen;
508         size_t dlen;
509         void *dest;
510         
511         slen = (strlen_w(src) + 1) * sizeof(smb_ucs2_t);
512         dlen = convert_string_allocate(CH_UCS2, CH_UNIX, src, slen, &dest);
513         if (dlen == -1) return NULL;
514         else return dest;
515 }
516
517 /****************************************************************************
518 convert from unix to ucs2 charset and return the
519 allocated and converted string or NULL if an error occurred.
520 you must provide a zero terminated string.
521 the returning string will be zero terminated.
522 ****************************************************************************/
523 smb_ucs2_t *acnv_uxu2(const char *src)
524 {
525         size_t slen;
526         size_t dlen;
527         void *dest;
528         
529         slen = strlen(src) + 1;
530         dlen = convert_string_allocate(CH_UNIX, CH_UCS2, src, slen, &dest);
531         if (dlen == -1) return NULL;
532         else return dest;
533 }
534
535 /****************************************************************************
536 convert from ucs2 to dos charset and return the
537 allocated and converted string or NULL if an error occurred.
538 you must provide a zero terminated string.
539 the returning string will be zero terminated.
540 ****************************************************************************/
541 char *acnv_u2dos(const smb_ucs2_t *src)
542 {
543         size_t slen;
544         size_t dlen;
545         void *dest;
546         
547         slen = (strlen_w(src) + 1) * sizeof(smb_ucs2_t);
548         dlen = convert_string_allocate(CH_UCS2, CH_DOS, src, slen, &dest);
549         if (dlen == -1) return NULL;
550         else return dest;
551 }
552
553 /****************************************************************************
554 convert from dos to ucs2 charset and return the
555 allocated and converted string or NULL if an error occurred.
556 you must provide a zero terminated string.
557 the returning string will be zero terminated.
558 ****************************************************************************/
559 smb_ucs2_t *acnv_dosu2(const char *src)
560 {
561         size_t slen;
562         size_t dlen;
563         void *dest;
564         
565         slen = strlen(src) + 1;
566         dlen = convert_string_allocate(CH_DOS, CH_UCS2, src, slen, &dest);
567         if (dlen == -1) return NULL;
568         else return dest;
569 }