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