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
ucstooem(char * oem,const smb_wchar_t * ucs,size_t nbytes,uint32_t cpid)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
oemtoucs(smb_wchar_t * ucs,const char * oem,size_t nwchars,uint32_t cpid)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 *
oem_get_oempage(uint32_t cpid)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 *
oem_get_ucspage(uint32_t cpid)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
oem_codepage_init(uint32_t cpid)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
oem_codepage_setup(uint32_t cpid)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