/* Unix SMB/CIFS implementation. Character set conversion Extensions Copyright (C) Igor Vergeichik 2001 Copyright (C) Andrew Tridgell 2001 Copyright (C) Simo Sorce 2001 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "includes.h" /** * @file * * @brief Character-set conversion routines built on our iconv. * * @note Samba's internal character set (at least in the 3.0 series) * is always the same as the one for the Unix filesystem. It is * not necessarily UTF-8 and may be different on machines that * need i18n filenames to be compatible with Unix software. It does * have to be a superset of ASCII. All multibyte sequences must start * with a byte with the high bit set. * * @sa lib/iconv.c */ /** * Return the name of a charset to give to iconv(). **/ static const char *charset_name(charset_t ch) { const char *ret = NULL; if (ch == CH_UTF16) ret = "UTF-16LE"; else if (ch == CH_UNIX) ret = lp_unix_charset(); else if (ch == CH_DOS) ret = lp_dos_charset(); else if (ch == CH_DISPLAY) ret = lp_display_charset(); else if (ch == CH_UTF8) ret = "UTF8"; else if (ch == CH_UTF16BE) ret = "UTF-16BE"; if (!ret || !*ret) ret = "ASCII"; return ret; } static void lazy_initialize_conv(void) { static int initialized = False; if (!initialized) { initialized = True; load_case_tables(); init_iconv(); } } static smb_iconv_t conv_handles[NUM_CHARSETS][NUM_CHARSETS]; /* on-demand initialisation of conversion handles */ static smb_iconv_t get_conv_handle(charset_t from, charset_t to) { const char *n1, *n2; if (conv_handles[from][to]) { return conv_handles[from][to]; } n1 = charset_name(from); n2 = charset_name(to); conv_handles[from][to] = smb_iconv_open(n2,n1); return conv_handles[from][to]; } /** re-initialize iconv conversion descriptors **/ void init_iconv(void) { charset_t c1, c2; for (c1=0;c1 *
STR_TERMINATE
means include the null termination
*
STR_UPPER
means uppercase in the destination
* * * @param dest_len the maximum length in bytes allowed in the * destination. If @p dest_len is -1 then no maximum is used. **/ ssize_t push_ascii(void *dest, const char *src, size_t dest_len, int flags) { size_t src_len; ssize_t ret; char *tmpbuf = NULL; /* treat a pstring as "unlimited" length */ if (dest_len == (size_t)-1) dest_len = sizeof(pstring); if (flags & STR_UPPER) { tmpbuf = strupper_talloc(NULL, src); if (!tmpbuf) { return -1; } src = tmpbuf; } src_len = strlen(src); if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) src_len++; ret = convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len); talloc_free(tmpbuf); return ret; } /** * Copy a string from a unix char* src to an ASCII destination, * allocating a buffer using talloc(). * * @param dest always set at least to NULL * * @returns The number of bytes occupied by the string in the destination * or -1 in case of error. **/ ssize_t push_ascii_talloc(TALLOC_CTX *ctx, char **dest, const char *src) { size_t src_len = strlen(src)+1; *dest = NULL; return convert_string_talloc(ctx, CH_UNIX, CH_DOS, src, src_len, (void **)dest); } /** * Copy a string from a dos codepage source to a unix char* destination. * * The resulting string in "dest" is always null terminated. * * @param flags can have: *
*
STR_TERMINATE
*
STR_TERMINATE means the string in @p src * is null terminated, and src_len is ignored.
*
* * @param src_len is the length of the source area in bytes. * @returns the number of bytes occupied by the string in @p src. **/ ssize_t pull_ascii(char *dest, const void *src, size_t dest_len, size_t src_len, int flags) { size_t ret; if (dest_len == (size_t)-1) dest_len = sizeof(pstring); if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) { if (src_len == (size_t)-1) { src_len = strlen(src) + 1; } else { size_t len = strnlen(src, src_len); if (len < src_len) len++; src_len = len; } } ret = convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len); if (dest_len) dest[MIN(ret, dest_len-1)] = 0; return src_len; } /** * Copy a string from a char* src to a unicode destination. * * @returns the number of bytes occupied by the string in the destination. * * @param flags can have: * *
*
STR_TERMINATE
means include the null termination. *
STR_UPPER
means uppercase in the destination. *
STR_NOALIGN
means don't do alignment. *
* * @param dest_len is the maximum length allowed in the * destination. If dest_len is -1 then no maxiumum is used. **/ ssize_t push_ucs2(void *dest, const char *src, size_t dest_len, int flags) { size_t len=0; size_t src_len = strlen(src); size_t ret; /* treat a pstring as "unlimited" length */ if (dest_len == (size_t)-1) dest_len = sizeof(pstring); if (flags & STR_TERMINATE) src_len++; if (ucs2_align(NULL, dest, flags)) { *(char *)dest = 0; dest = (void *)((char *)dest + 1); if (dest_len) dest_len--; len++; } /* ucs2 is always a multiple of 2 bytes */ dest_len &= ~1; ret = convert_string(CH_UNIX, CH_UTF16, src, src_len, dest, dest_len); if (ret == (size_t)-1) { return 0; } len += ret; if (flags & STR_UPPER) { smb_ucs2_t *dest_ucs2 = dest; size_t i; for (i = 0; i < (dest_len / 2) && dest_ucs2[i]; i++) { smb_ucs2_t v = toupper_w(dest_ucs2[i]); if (v != dest_ucs2[i]) { dest_ucs2[i] = v; } } } return len; } /** * Copy a string from a unix char* src to a UCS2 destination, * allocating a buffer using talloc(). * * @param dest always set at least to NULL * * @returns The number of bytes occupied by the string in the destination * or -1 in case of error. **/ ssize_t push_ucs2_talloc(TALLOC_CTX *ctx, smb_ucs2_t **dest, const char *src) { size_t src_len = strlen(src)+1; *dest = NULL; return convert_string_talloc(ctx, CH_UNIX, CH_UTF16, src, src_len, (void **)dest); } /** * Copy a string from a unix char* src to a UTF-8 destination, allocating a buffer using talloc * * @param dest always set at least to NULL * * @returns The number of bytes occupied by the string in the destination **/ ssize_t push_utf8_talloc(TALLOC_CTX *ctx, char **dest, const char *src) { size_t src_len = strlen(src)+1; *dest = NULL; return convert_string_talloc(ctx, CH_UNIX, CH_UTF8, src, src_len, (void **)dest); } /** Copy a string from a ucs2 source to a unix char* destination. Flags can have: STR_TERMINATE means the string in src is null terminated. STR_NOALIGN means don't try to align. if STR_TERMINATE is set then src_len is ignored if it is -1. src_len is the length of the source area in bytes Return the number of bytes occupied by the string in src. The resulting string in "dest" is always null terminated. **/ size_t pull_ucs2(char *dest, const void *src, size_t dest_len, size_t src_len, int flags) { size_t ret; if (dest_len == (size_t)-1) dest_len = sizeof(pstring); if (ucs2_align(NULL, src, flags)) { src = (const void *)((const char *)src + 1); if (src_len > 0) src_len--; } if (flags & STR_TERMINATE) { if (src_len == (size_t)-1) { src_len = strlen_w(src)*2 + 2; } else { size_t len = strnlen_w(src, src_len/2); if (len < src_len/2) len++; src_len = len*2; } } /* ucs2 is always a multiple of 2 bytes */ if (src_len != (size_t)-1) src_len &= ~1; ret = convert_string(CH_UTF16, CH_UNIX, src, src_len, dest, dest_len); if (dest_len) dest[MIN(ret, dest_len-1)] = 0; return src_len; } ssize_t pull_ucs2_pstring(char *dest, const void *src) { return pull_ucs2(dest, src, sizeof(pstring), -1, STR_TERMINATE); } /** * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer using talloc * * @param dest always set at least to NULL * * @returns The number of bytes occupied by the string in the destination **/ ssize_t pull_ucs2_talloc(TALLOC_CTX *ctx, char **dest, const smb_ucs2_t *src) { size_t src_len = (strlen_w(src)+1) * sizeof(smb_ucs2_t); *dest = NULL; return convert_string_talloc(ctx, CH_UTF16, CH_UNIX, src, src_len, (void **)dest); } /** * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer using talloc * * @param dest always set at least to NULL * * @returns The number of bytes occupied by the string in the destination **/ ssize_t pull_utf8_talloc(TALLOC_CTX *ctx, char **dest, const char *src) { size_t src_len = strlen(src)+1; *dest = NULL; return convert_string_talloc(ctx, CH_UTF8, CH_UNIX, src, src_len, (void **)dest); } /** Copy a string from a char* src to a unicode or ascii dos codepage destination choosing unicode or ascii based on the flags in the SMB buffer starting at base_ptr. Return the number of bytes occupied by the string in the destination. flags can have: STR_TERMINATE means include the null termination. STR_UPPER means uppercase in the destination. STR_ASCII use ascii even with unicode packet. STR_NOALIGN means don't do alignment. dest_len is the maximum length allowed in the destination. If dest_len is -1 then no maxiumum is used. **/ ssize_t push_string(void *dest, const char *src, size_t dest_len, int flags) { if (flags & STR_ASCII) { return push_ascii(dest, src, dest_len, flags); } else if (flags & STR_UNICODE) { return push_ucs2(dest, src, dest_len, flags); } else { smb_panic("push_string requires either STR_ASCII or STR_UNICODE flag to be set"); return -1; } } /** Copy a string from a unicode or ascii source (depending on the packet flags) to a char* destination. Flags can have: STR_TERMINATE means the string in src is null terminated. STR_UNICODE means to force as unicode. STR_ASCII use ascii even with unicode packet. STR_NOALIGN means don't do alignment. if STR_TERMINATE is set then src_len is ignored is it is -1 src_len is the length of the source area in bytes. Return the number of bytes occupied by the string in src. The resulting string in "dest" is always null terminated. **/ ssize_t pull_string(char *dest, const void *src, size_t dest_len, size_t src_len, int flags) { if (flags & STR_ASCII) { return pull_ascii(dest, src, dest_len, src_len, flags); } else if (flags & STR_UNICODE) { return pull_ucs2(dest, src, dest_len, src_len, flags); } else { smb_panic("pull_string requires either STR_ASCII or STR_UNICODE flag to be set"); return -1; } }