1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Support for oem <-> unicode translations. 28 */ 29 30 #ifndef _KERNEL 31 #include <stdlib.h> 32 #include <thread.h> 33 #include <synch.h> 34 #include <string.h> 35 #else 36 #include <sys/ksynch.h> 37 #endif /* _KERNEL */ 38 39 #include <sys/byteorder.h> 40 #include <smbsrv/alloc.h> 41 #include <smbsrv/string.h> 42 43 /* 44 * cpid The oemcpg_table index for this oempage. 45 * value The conversion values. 46 */ 47 typedef struct oempage { 48 uint32_t cpid; 49 smb_wchar_t *value; 50 } oempage_t; 51 52 /* 53 * filename The actual filename contains the codepage. 54 * bytesperchar The codepage uses double or single bytes per char. 55 * oempage The oempage is used to convert Unicode characters to 56 * OEM characters. Memory needs to be allocated for 57 * the value field of oempage to store the table. 58 * ucspage The unicode page is used to convert OEM characters 59 * to Unicode characters. Memory needs to be allocated 60 * for the value field of ucspage to store the table. 61 * valid True if the codepage has been initialized. 62 */ 63 typedef struct oem_codepage { 64 char *filename; 65 uint32_t bytesperchar; 66 oempage_t oempage; 67 oempage_t ucspage; 68 boolean_t valid; 69 } oem_codepage_t; 70 71 static oem_codepage_t oemcpg_table[] = { 72 {"850.cpg", 1, {0, 0}, {0, 0}, 0}, /* Multilingual Latin1 */ 73 {"950.cpg", 2, {1, 0}, {1, 0}, 0}, /* Chinese Traditional */ 74 {"1252.cpg", 1, {2, 0}, {2, 0}, 0}, /* MS Latin1 */ 75 {"949.cpg", 2, {3, 0}, {3, 0}, 0}, /* Korean */ 76 {"936.cpg", 2, {4, 0}, {4, 0}, 0}, /* Chinese Simplified */ 77 {"932.cpg", 2, {5, 0}, {5, 0}, 0}, /* Japanese */ 78 {"852.cpg", 1, {6, 0}, {6, 0}, 0}, /* Multilingual Latin2 */ 79 {"1250.cpg", 1, {7, 0}, {7, 0}, 0}, /* MS Latin2 */ 80 {"1253.cpg", 1, {8, 0}, {8, 0}, 0}, /* MS Greek */ 81 {"737.cpg", 1, {9, 0}, {9, 0}, 0}, /* Greek */ 82 {"1254.cpg", 1, {10, 0}, {10, 0}, 0}, /* MS Turkish */ 83 {"857.cpg", 1, {11, 0}, {11, 0}, 0}, /* Multilingual Latin5 */ 84 {"1251.cpg", 1, {12, 0}, {12, 0}, 0}, /* MS Cyrillic */ 85 {"866.cpg", 1, {13, 0}, {13, 0}, 0}, /* Cyrillic II */ 86 {"1255.cpg", 1, {14, 0}, {14, 0}, 0}, /* MS Hebrew */ 87 {"862.cpg", 1, {15, 0}, {15, 0}, 0}, /* Hebrew */ 88 {"1256.cpg", 1, {16, 0}, {16, 0}, 0}, /* MS Arabic */ 89 {"720.cpg", 1, {17, 0}, {17, 0}, 0} /* Arabic */ 90 }; 91 92 #define MAX_OEMPAGES (sizeof (oemcpg_table) / sizeof (oemcpg_table[0])) 93 #define MAX_UNICODE_IDX 65536 94 95 /* 96 * The default SMB OEM codepage for English is codepage 850. 97 */ 98 smb_wchar_t oem_codepage_850[256] = { 99 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 100 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 101 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 102 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 103 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 104 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 105 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 106 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 107 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 108 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 109 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 110 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 111 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 112 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 113 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 114 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, 115 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 116 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 117 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 118 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, 119 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 120 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 121 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 122 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, 123 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 124 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 125 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 126 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, 127 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 128 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, 129 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 130 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 131 }; 132 133 /* 134 * The default telnet OEM codepage for English is codepage 1252. 135 */ 136 smb_wchar_t oem_codepage_1252[256] = { 137 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 138 0x9, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x10, 139 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 140 0x19, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x20, 141 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 142 0x29, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x30, 143 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 144 0x39, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x40, 145 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 146 0x49, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x50, 147 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 148 0x59, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x60, 149 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 150 0x69, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x70, 151 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 152 0x79, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, 0x20AC, 153 0x81, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 154 0x2030, 0x160, 0x2039, 0x152, 0x8D, 0x017D, 0x8F, 0x90, 155 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 156 0x2122, 0x161, 0x203A, 0x153, 0x9D, 0x017E, 0x178, 0x00A0, 157 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 158 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 159 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 160 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 161 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 162 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 163 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 164 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 165 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 166 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 167 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 168 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF 169 }; 170 171 static oempage_t *oem_get_oempage(uint32_t); 172 static oempage_t *oem_get_ucspage(uint32_t); 173 static void oem_codepage_init(uint32_t); 174 static void oem_codepage_setup(uint32_t); 175 176 /* 177 * Convert a unicode string to an oem string. 178 * 179 * The conversion will stop at the end of the unicode string 180 * or when (nbytes - 1) oem characters have been stored. 181 * 182 * The number of converted unicode characters is returned, 183 * or 0 on error. 184 */ 185 size_t 186 ucstooem(char *oem, const smb_wchar_t *ucs, size_t nbytes, uint32_t cpid) 187 { 188 oempage_t *ucspage; 189 uint32_t count = 0; 190 smb_wchar_t oemchar; 191 192 if (ucs == NULL || oem == NULL) 193 return (0); 194 195 if ((ucspage = oem_get_ucspage(cpid)) == NULL) 196 return (0); 197 198 while (nbytes != 0 && (oemchar = ucspage->value[*ucs]) != 0) { 199 if (oemchar & 0xff00 && nbytes >= MTS_MB_CHAR_MAX) { 200 *oem++ = oemchar >> 8; 201 *oem++ = (char)oemchar; 202 nbytes -= 2; 203 } else if (nbytes > 1) { 204 *oem++ = (char)oemchar; 205 nbytes--; 206 } else { 207 break; 208 } 209 210 count++; 211 ucs++; 212 } 213 214 *oem = '\0'; 215 return (count); 216 } 217 218 /* 219 * Convert an oem string to a unicode string. 220 * 221 * The conversion will stop at the end of the oem string or 222 * when nwchars - 1 have been converted. 223 * 224 * The number of converted oem chars is returned, or 0 on error. 225 * An oem char may be either 1 or 2 bytes. 226 */ 227 size_t 228 oemtoucs(smb_wchar_t *ucs, const char *oem, size_t nwchars, uint32_t cpid) 229 { 230 oempage_t *oempage; 231 size_t count = nwchars; 232 smb_wchar_t oemchar; 233 234 if (ucs == NULL || oem == NULL) 235 return (0); 236 237 if ((oempage = oem_get_oempage(cpid)) == NULL) 238 return (0); 239 240 while ((oemchar = (smb_wchar_t)*oem++ & 0xff) != 0) { 241 /* 242 * Cannot find one byte oemchar in table. 243 * Must be a lead byte. Try two bytes. 244 */ 245 if ((oempage->value[oemchar] == 0) && (oemchar != 0)) { 246 oemchar = oemchar << 8 | (*oem++ & 0xff); 247 if (oempage->value[oemchar] == 0) { 248 *ucs = 0; 249 break; 250 } 251 } 252 #ifdef _BIG_ENDIAN 253 *ucs = LE_IN16(&oempage->value[oemchar]); 254 #else 255 *ucs = oempage->value[oemchar]; 256 #endif 257 count--; 258 ucs++; 259 } 260 261 *ucs = 0; 262 return (nwchars - count); 263 } 264 265 /* 266 * Get a pointer to the oem page for the specific codepage id. 267 */ 268 static oempage_t * 269 oem_get_oempage(uint32_t cpid) 270 { 271 if (cpid >= MAX_OEMPAGES) 272 return (NULL); 273 274 if (!oemcpg_table[cpid].valid) { 275 oem_codepage_init(cpid); 276 277 if (!oemcpg_table[cpid].valid) 278 return (NULL); 279 } 280 281 return (&oemcpg_table[cpid].oempage); 282 } 283 284 /* 285 * Get a pointer to the ucs page for the specific codepage id. 286 */ 287 static oempage_t * 288 oem_get_ucspage(uint32_t cpid) 289 { 290 if (cpid >= MAX_OEMPAGES) 291 return (NULL); 292 293 if (!oemcpg_table[cpid].valid) { 294 oem_codepage_init(cpid); 295 296 if (!oemcpg_table[cpid].valid) 297 return (NULL); 298 } 299 300 return (&oemcpg_table[cpid].ucspage); 301 } 302 303 /* 304 * Initialize the oem page in the oem table. 305 */ 306 static void 307 oem_codepage_init(uint32_t cpid) 308 { 309 #ifndef _KERNEL 310 static mutex_t mutex; 311 312 (void) mutex_lock(&mutex); 313 oem_codepage_setup(cpid); 314 (void) mutex_unlock(&mutex); 315 #else 316 static kmutex_t mutex; 317 318 mutex_enter(&mutex); 319 oem_codepage_setup(cpid); 320 mutex_exit(&mutex); 321 #endif /* _KERNEL */ 322 } 323 324 static void 325 oem_codepage_setup(uint32_t cpid) 326 { 327 smb_wchar_t *default_oem_cp; 328 oem_codepage_t *oemcpg; 329 uint32_t bytesperchar; 330 uint32_t max_oem_index; 331 int i; 332 333 switch (cpid) { 334 case OEM_CPG_850: 335 default_oem_cp = oem_codepage_850; 336 break; 337 case OEM_CPG_1252: 338 default_oem_cp = oem_codepage_1252; 339 default: 340 return; 341 } 342 343 oemcpg = &oemcpg_table[cpid]; 344 if (oemcpg->valid) 345 return; 346 347 /* 348 * max_oem_index will be 256 or 65536 dependent 349 * on the OEM codepage. 350 */ 351 bytesperchar = oemcpg_table[cpid].bytesperchar; 352 max_oem_index = 1 << (bytesperchar * 8); 353 354 oemcpg->oempage.value = 355 MEM_ZALLOC("oem", max_oem_index * sizeof (smb_wchar_t)); 356 if (oemcpg->oempage.value == NULL) 357 return; 358 359 oemcpg->ucspage.value = 360 MEM_ZALLOC("oem", MAX_UNICODE_IDX * sizeof (smb_wchar_t)); 361 if (oemcpg->ucspage.value == NULL) { 362 MEM_FREE("oem", oemcpg->oempage.value); 363 oemcpg->oempage.value = NULL; 364 return; 365 } 366 367 for (i = 0; i < max_oem_index; i++) { 368 oemcpg->oempage.value[i] = default_oem_cp[i]; 369 oemcpg->ucspage.value[default_oem_cp[i]] = (smb_wchar_t)i; 370 } 371 372 oemcpg->valid = B_TRUE; 373 } 374