VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/asn1/asn1-ut-string.cpp

Last change on this file was 106061, checked in by vboxsync, 6 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 69.1 KB
Line 
1/* $Id: asn1-ut-string.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - ASN.1, XXX STRING Types.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include "internal/iprt.h"
42#include <iprt/asn1.h>
43
44#include <iprt/ctype.h>
45#include <iprt/err.h>
46#include <iprt/string.h>
47#include <iprt/uni.h>
48
49#include <iprt/formats/asn1.h>
50
51
52/*********************************************************************************************************************************
53* Global Variables *
54*********************************************************************************************************************************/
55static uint8_t const g_acbStringTags[] =
56{
57 /* [ASN1_TAG_EOC] = */ 0,
58 /* [ASN1_TAG_BOOLEAN] = */ 0,
59 /* [ASN1_TAG_INTEGER] = */ 0,
60 /* [ASN1_TAG_BIT_STRING] = */ 0,
61 /* [ASN1_TAG_OCTET_STRING] = */ 0,
62 /* [ASN1_TAG_NULL] = */ 0,
63 /* [ASN1_TAG_OID] = */ 0,
64 /* [ASN1_TAG_OBJECT_DESCRIPTOR] = */ 0,
65 /* [ASN1_TAG_EXTERNAL] = */ 0,
66 /* [ASN1_TAG_REAL] = */ 0,
67 /* [ASN1_TAG_ENUMERATED] = */ 0,
68 /* [ASN1_TAG_EMBEDDED_PDV] = */ 0,
69 /* [ASN1_TAG_UTF8_STRING] = */ 1,
70 /* [ASN1_TAG_RELATIVE_OID] = */ 0,
71 /* [ASN1_TAG_RESERVED_14] = */ 0,
72 /* [ASN1_TAG_RESERVED_15] = */ 0,
73 /* [ASN1_TAG_SEQUENCE] = */ 0,
74 /* [ASN1_TAG_SET] = */ 0,
75 /* [ASN1_TAG_NUMERIC_STRING] = */ 1,
76 /* [ASN1_TAG_PRINTABLE_STRING] = */ 1,
77 /* [ASN1_TAG_T61_STRING] = */ 1,
78 /* [ASN1_TAG_VIDEOTEX_STRING] = */ 1,
79 /* [ASN1_TAG_IA5_STRING] = */ 1,
80 /* [ASN1_TAG_UTC_TIME] = */ 0,
81 /* [ASN1_TAG_GENERALIZED_TIME] = */ 0,
82 /* [ASN1_TAG_GRAPHIC_STRING] = */ 1,
83 /* [ASN1_TAG_VISIBLE_STRING] = */ 1,
84 /* [ASN1_TAG_GENERAL_STRING] = */ 1,
85 /* [ASN1_TAG_UNIVERSAL_STRING] = */ 4,
86 /* [ASN1_TAG_CHARACTER_STRING] = */ 1,
87 /* [ASN1_TAG_BMP_STRING] = */ 2,
88};
89
90
91
92
93/*
94 * ISO/IEC-2022 + TeletexString mess.
95 */
96
97/**
98 * ISO-2022 codepoint mappings.
99 */
100typedef struct RTISO2022MAP
101{
102 /** The number of bytes per character. */
103 uint8_t cb;
104 /** The registration number. */
105 uint16_t uRegistration;
106 /** The size of the pauToUni table. */
107 uint16_t cToUni;
108 /** Pointer to the convertion table from ISO-2022 to Unicode.
109 * ASSUMES that unicode chars above 0xffff won't be required. */
110 uint16_t const *pauToUni;
111
112 /** Escape sequence for loading into G0 or C0 or C1 depending on the type (sans
113 * ESC). */
114 uint8_t abEscLoadXX[6];
115 /** Escape sequence for loading into G1 (sans ESC). */
116 uint8_t abEscLoadG1[6];
117 /** Escape sequence for loading into G2 (sans ESC). */
118 uint8_t abEscLoadG2[6];
119 /** Escape sequence for loading into G3 (sans ESC). */
120 uint8_t abEscLoadG3[6];
121} RTISO2022MAP;
122/** Pointer to const ISO-2022 mappings. */
123typedef RTISO2022MAP const *PCRTISO2022MAP;
124
125/** Unused codepoint value. */
126#define RTISO2022_UNUSED UINT16_C(0xffff)
127
128
129/** Dummy mappings to avoid dealing with NULL pointers in the decoder
130 * registers. */
131static const RTISO2022MAP g_DummyMap =
132{
133 1, UINT16_MAX, 0, NULL,
134 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* No escape into G0 */,
135 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* No escape into G1 */,
136 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* No escape into G2 */,
137 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* No escape into G3 */
138};
139
140
141/** GL mappings for ISO-IR-168 (Japanese, update of #87), with space and
142 * delete. */
143static const RTISO2022MAP g_IsoIr168Map =
144{
145 //2, 168, RT_ELEMENTS(g_awcIsoIr168Decode), g_awcIsoIr168Decode,
146 2, 168, 0, NULL,
147 { 0x26, 0x40, 0x2b, 0x24, 0x42, 0xff } /* Esc into G0 */,
148 { 0x26, 0x40, 0x2b, 0x24, 0x29, 0x42 } /* Esc into G1 */,
149 { 0x26, 0x40, 0x2b, 0x24, 0x2a, 0x42 } /* Esc into G2 */,
150 { 0x26, 0x40, 0x2b, 0x24, 0x2b, 0x42 } /* Esc into G3 */,
151};
152
153
154/** GL mappings for ISO-IR-165 (Chinese), with space and delete. */
155static const RTISO2022MAP g_IsoIr165Map =
156{
157 //2, 165, RT_ELEMENTS(g_awcIsoIr165Decode), g_awcIsoIr165Decode,
158 2, 165, 0, NULL,
159 { 0x24, 0x28, 0x45, 0xff, 0xff, 0xff } /* Esc into G0 */,
160 { 0x24, 0x29, 0x45, 0xff, 0xff, 0xff } /* Esc into G1 */,
161 { 0x24, 0x2a, 0x45, 0xff, 0xff, 0xff } /* Esc into G2 */,
162 { 0x24, 0x2b, 0x45, 0xff, 0xff, 0xff } /* Esc into G3 */,
163};
164
165
166/** GL mappings for ISO-IR-150 (Greek), with space and delete. */
167static const RTISO2022MAP g_IsoIr150Map =
168{
169 //1, 150, RT_ELEMENTS(g_awcIsoIr150Decode), g_awcIsoIr150Decode,
170 1, 150, 0, NULL,
171 { 0x28, 0x21, 0x40, 0xff, 0xff, 0xff } /* Esc into G0 */,
172 { 0x29, 0x21, 0x40, 0xff, 0xff, 0xff } /* Esc into G1 */,
173 { 0x2a, 0x21, 0x40, 0xff, 0xff, 0xff } /* Esc into G2 */,
174 { 0x2b, 0x21, 0x40, 0xff, 0xff, 0xff } /* Esc into G3 */,
175};
176
177
178/** GL mappings for ISO-IR-103 (Teletex supplementary), with space and
179 * delete. */
180static const RTISO2022MAP g_IsoIr103Map =
181{
182 //1, 103, RT_ELEMENTS(g_awcIsoIr103Decode), g_awcIsoIr103Decode,
183 1, 103, 0, NULL,
184 { 0x28, 0x76, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
185 { 0x29, 0x76, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
186 { 0x2a, 0x76, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
187 { 0x2b, 0x76, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
188};
189
190
191/**
192 * GL mapping from ISO-IR-102 (Teletex primary) to unicode, with space and
193 * delete.
194 *
195 * Mostly 1:1, except that (a) what would be dollar is currency sign, (b)
196 * positions 0x5c, 0x5e, 0x7b, 0x7d and 0x7e are defined not to be used.
197 */
198static uint16_t const g_awcIsoIr102Decode[0x60] =
199{
200 0x0020, 0x0021, 0x0022, 0x0023, 0x00A4, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
201 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
202 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
203 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0xffff, 0x005d, 0xffff, 0x005f,
204 0xffff, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
205 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0xffff, 0x007c, 0xffff, 0xffff, 0x007f,
206};
207
208/** GL mappings for ISO-IR-102, with space and delete. */
209static const RTISO2022MAP g_IsoIr102Map =
210{
211 1, 102, RT_ELEMENTS(g_awcIsoIr102Decode), g_awcIsoIr102Decode,
212 { 0x28, 0x75, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
213 { 0x29, 0x75, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
214 { 0x2a, 0x75, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
215 { 0x2b, 0x75, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
216};
217
218
219#if 0 /* unused */
220/** GL mappings for ISO-IR-87 (Japanese), with space and delete. */
221static const RTISO2022MAP g_IsoIr87Map =
222{
223 //1, 87, RT_ELEMENTS(g_awcIsoIr87Decode), g_awcIsoIr97Decode,
224 1, 87, 0, NULL,
225 { 0x24, 0x42, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
226 { 0x24, 0x29, 0x42, 0xff, 0xff, 0xff } /* Esc into G1 */,
227 { 0x24, 0x2a, 0x42, 0xff, 0xff, 0xff } /* Esc into G2 */,
228 { 0x24, 0x2b, 0x42, 0xff, 0xff, 0xff } /* Esc into G3 */,
229};
230#endif
231
232
233/**
234 * GL mapping from ISO-IR-6 (ASCII) to unicode, with space and delete.
235 *
236 * Completely 1:1.
237 */
238static uint16_t const g_awcIsoIr6Decode[0x60] =
239{
240 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
241 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
242 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
243 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
244 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
245 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x006b, 0x007c, 0x007d, 0x007e, 0x007f,
246};
247
248/** GL mappings for ISO-IR-6 (ASCII), with space and delete. */
249static const RTISO2022MAP g_IsoIr6Map =
250{
251 1, 6, RT_ELEMENTS(g_awcIsoIr6Decode), g_awcIsoIr6Decode,
252 { 0x28, 0x42, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
253 { 0x29, 0x42, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
254 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
255 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
256};
257
258
259/** GL maps. */
260static PCRTISO2022MAP g_paGLMaps[] =
261{
262 &g_IsoIr6Map,
263 &g_IsoIr102Map,
264 &g_IsoIr103Map,
265 &g_IsoIr150Map,
266 &g_IsoIr165Map,
267 &g_IsoIr168Map,
268};
269
270
271
272/** GR mappings for ISO-IR-164 (Hebrew supplementary). */
273static const RTISO2022MAP g_IsoIr164Map =
274{
275 //1, 164, RT_ELEMENTS(g_awcIsoIr164Decode), g_awcIsoIr164Decode
276 1, 164, 0, NULL,
277 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
278 { 0x2d, 0x53, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
279 { 0x2e, 0x53, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
280 { 0x2f, 0x53, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
281};
282
283
284/** GR mappings for ISO-IR-156 (Supplementary for ASCII (#6)). */
285static const RTISO2022MAP g_IsoIr156Map =
286{
287 //1, 156, RT_ELEMENTS(g_awcIsoIr156Decode), g_awcIsoIr156Decode
288 1, 156, 0, NULL,
289 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
290 { 0x2d, 0x52, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
291 { 0x2e, 0x52, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
292 { 0x2f, 0x52, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
293};
294
295
296/** GR mappings for ISO-IR-153 (Basic Cyrillic). */
297static const RTISO2022MAP g_IsoIr153Map =
298{
299 //1, 153, RT_ELEMENTS(g_awcIsoIr153Decode), g_awcIsoIr153Decode
300 1, 153, 0, NULL,
301 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
302 { 0x2d, 0x4f, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
303 { 0x2e, 0x4f, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
304 { 0x2f, 0x4f, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
305};
306
307
308/** GR mappings for ISO-IR-144 (Cryllic part of Latin/Cyrillic). */
309static const RTISO2022MAP g_IsoIr144Map =
310{
311 //1, 144, RT_ELEMENTS(g_awcIsoIr144Decode), g_awcIsoIr144Decode
312 1, 144, 0, NULL,
313 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
314 { 0x2d, 0x4f, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
315 { 0x2e, 0x4f, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
316 { 0x2f, 0x4f, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
317};
318
319
320/** GR mappings for ISO-IR-126 (Latin/Greek). */
321static const RTISO2022MAP g_IsoIr126Map =
322{
323 //1, 126, RT_ELEMENTS(g_awcIsoIr126Decode), g_awcIsoIr126Decode
324 1, 126, 0, NULL,
325 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
326 { 0x2d, 0x46, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
327 { 0x2e, 0x46, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
328 { 0x2f, 0x46, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
329};
330
331
332/** GR maps. */
333static PCRTISO2022MAP g_paGRMaps[] =
334{
335 &g_IsoIr126Map,
336 &g_IsoIr144Map,
337 &g_IsoIr153Map,
338 &g_IsoIr156Map,
339 &g_IsoIr164Map,
340};
341
342
343
344/** C0 mapping from ISO-IR-106 to unicode. */
345static uint16_t g_awcIsoIr106Decode[0x20] =
346{
347 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0008, 0xffff, 0x000a, 0xffff, 0x000c, 0x000d, 0x000e, 0x000f,
348 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x008e, 0x000a, 0x001b, 0xffff, 0x008f, 0xffff, 0xffff,
349};
350
351/** C0 mappings for ISO-IR-106. */
352static const RTISO2022MAP g_IsoIr106Map =
353{
354 1, 106, RT_ELEMENTS(g_awcIsoIr106Decode), g_awcIsoIr106Decode,
355 { 0x21, 0x45, 0xff, 0xff, 0xff, 0xff } /* Esc into C0 */,
356 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* N/A */,
357 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* N/A */,
358 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* N/A */,
359};
360
361/** C0 maps. */
362static PCRTISO2022MAP g_paC0Maps[] =
363{
364 &g_IsoIr106Map,
365};
366
367
368
369/** C1 mapping from ISO-IR-107 to unicode. */
370static uint16_t g_awcIsoIr107Decode[0x20] =
371{
372 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x008b, 0x008c, 0xffff, 0xffff, 0xffff,
373 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x009b, 0xffff, 0xffff, 0xffff, 0xffff,
374};
375
376/** C1 mappings for ISO-IR-107. */
377static const RTISO2022MAP g_IsoIr107Map =
378{
379 1, 107, RT_ELEMENTS(g_awcIsoIr107Decode), g_awcIsoIr107Decode,
380 { 0x22, 0x48, 0xff, 0xff, 0xff, 0xff } /* Esc into C1 */,
381 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* N/A */,
382 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* N/A */,
383 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* N/A */,
384};
385
386/** C1 maps. */
387static PCRTISO2022MAP g_paC1Maps[] =
388{
389 &g_IsoIr107Map,
390};
391
392
393static int rtIso2022Decoder_LookupAndSet(PCRTISO2022MAP *ppMapRet, uint16_t uRegistration, PCRTISO2022MAP *papMaps, uint32_t cMaps)
394{
395 uint32_t i = cMaps;
396 while (i-- > 0)
397 if (papMaps[i]->uRegistration == uRegistration)
398 {
399 /** @todo skip non-Teletex codesets if we ever add more than we need for it. */
400 *ppMapRet = papMaps[i];
401 return VINF_SUCCESS;
402 }
403 return VERR_ASN1_INVALID_T61_STRING_ENCODING;
404}
405
406
407/**
408 * ISO-2022 decoder state.
409 */
410typedef struct RTISO2022DECODERSTATE
411{
412 /** Pointer to the string */
413 uint8_t const *pabString;
414 /** The string size. */
415 uint32_t cbString;
416 /** The current string position. */
417 uint32_t offString;
418
419 /** The GL mapping. */
420 PCRTISO2022MAP pMapGL;
421 /** The GR mapping. */
422 PCRTISO2022MAP pMapGR;
423 /** The lower control set (C0) mapping. */
424 PCRTISO2022MAP pMapC0;
425 /** The higher control set (C1) mapping. */
426 PCRTISO2022MAP pMapC1;
427 /** The G0, G1, G2, and G3 mappings. */
428 PCRTISO2022MAP apMapGn[4];
429 /** Used by SS2 & SS3 to store the orignal GL value that is to be restored. */
430 PCRTISO2022MAP pRestoreGL;
431 /** Pointer to extended error info buffer, optional. */
432 PRTERRINFO pErrInfo;
433} RTISO2022DECODERSTATE;
434/** Pointer to a const ISO-2022 decoder state. */
435typedef RTISO2022DECODERSTATE *PRTISO2022DECODERSTATE;
436
437
438static int rtIso2022Decoder_SetGL(PRTISO2022DECODERSTATE pThis, PCRTISO2022MAP pNewMap)
439{
440 pThis->pMapGL = pNewMap;
441 return VINF_SUCCESS;
442}
443
444
445static int rtIso2022Decoder_SetGR(PRTISO2022DECODERSTATE pThis, PCRTISO2022MAP pNewMap)
446{
447 pThis->pMapGR = pNewMap;
448 return VINF_SUCCESS;
449}
450
451
452static int rtIso2022Decoder_SetGLForOneChar(PRTISO2022DECODERSTATE pThis, PCRTISO2022MAP pTmpMap)
453{
454 pThis->pRestoreGL = pThis->pMapGL;
455 pThis->pMapGL = pTmpMap;
456 return VINF_SUCCESS;
457}
458
459
460static int rtIso2022Decoder_SetC0(PRTISO2022DECODERSTATE pThis, uint16_t uRegistration)
461{
462 return rtIso2022Decoder_LookupAndSet(&pThis->pMapC0, uRegistration, g_paC0Maps, RT_ELEMENTS(g_paC0Maps));
463}
464
465
466static int rtIso2022Decoder_SetC1(PRTISO2022DECODERSTATE pThis, uint16_t uRegistration)
467{
468 return rtIso2022Decoder_LookupAndSet(&pThis->pMapC1, uRegistration, g_paC1Maps, RT_ELEMENTS(g_paC1Maps));
469}
470
471
472/**
473 * Worker for rtIso2022Decoder_FindEscAndSet.
474 *
475 * @returns true if match, false if not.
476 * @param pabLeft Pointer to the first string byte after the ESC.
477 * @param cbLeft The number of bytes left in the string.
478 * @param pabRight Pointer to the abEscLoad* byte array to match with.
479 * @param cbRight Size of the mapping sequence (fixed).
480 * @param pcchMatch Where to return the length of the escape sequence (sans
481 * ESC) on success.
482 */
483static bool rtIso2022Decoder_MatchEscSeqFrom2ndByte(uint8_t const *pabLeft, uint32_t cbLeft,
484 uint8_t const *pabRight, uint32_t cbRight,
485 uint32_t *pcchMatch)
486{
487 Assert(cbRight == 6);
488 uint32_t i = 1;
489 while (i < cbRight)
490 {
491 if (pabRight[i] == 0xff)
492 break;
493 if (cbLeft <= i || pabLeft[i] != pabRight[i])
494 return false;
495 i++;
496 }
497 *pcchMatch = i;
498 return true;
499}
500
501
502/**
503 * Finds a the set with a matching abEscLoad* escape sequence and loads it into
504 * the designated register.
505 *
506 * @returns The length of the sequence on success, negative error status code on
507 * failure.
508 * @param pThis The decoder instance.
509 * @param ppMapRet Used to specify C0 or C1 maps when processing
510 * escape sequences for loading these. Only the
511 * abEscLoadXX arrays will be searched if this is
512 * not NULL. For loading {G0,...,G3} pass NULL.
513 * @param pb Pointer to the start of the escape sequence.
514 * @param cb The number of bytes remaining in the string.
515 * @param papMaps The maps to search.
516 * @param cMaps The number of maps @a papMaps points to.
517 */
518static int rtIso2022Decoder_FindEscAndSet(PRTISO2022DECODERSTATE pThis,
519 PCRTISO2022MAP *ppMapRet, PCRTISO2022MAP *papMaps, uint32_t cMaps)
520{
521 /* Skip the ESC.*/
522 uint8_t const *pb = &pThis->pabString[pThis->offString + 1];
523 uint32_t cb = pThis->cbString - (pThis->offString + 1);
524
525 /* Cache the first char. */
526 uint8_t const b0 = pb[0];
527
528 /* Scan the array of maps for matching sequences. */
529 uint32_t i = cMaps;
530 while (i-- > 0)
531 {
532 uint32_t cchMatch = 0; /* (MSC maybe used uninitialized) */
533 PCRTISO2022MAP pMap = papMaps[i];
534 /** @todo skip non-Teletex codesets if we ever add more than we need for it. */
535 if ( pMap->abEscLoadXX[0] == b0
536 && rtIso2022Decoder_MatchEscSeqFrom2ndByte(pb, cb, pMap->abEscLoadXX, sizeof(pMap->abEscLoadXX), &cchMatch) )
537 {
538 if (ppMapRet)
539 *ppMapRet = pMap;
540 else
541 pThis->apMapGn[0] = pMap;
542 return cchMatch + 1;
543 }
544
545 if (!ppMapRet) /* ppMapRet is NULL if Gn. */
546 {
547 uint32_t iGn;
548 if ( pMap->abEscLoadG1[0] == b0
549 && rtIso2022Decoder_MatchEscSeqFrom2ndByte(pb, cb, pMap->abEscLoadG1, sizeof(pMap->abEscLoadG1), &cchMatch))
550 iGn = 1;
551 else if ( pMap->abEscLoadG2[0] == b0
552 && rtIso2022Decoder_MatchEscSeqFrom2ndByte(pb, cb, pMap->abEscLoadG2, sizeof(pMap->abEscLoadG2), &cchMatch))
553 iGn = 2;
554 else if ( pMap->abEscLoadG3[0] == b0
555 && rtIso2022Decoder_MatchEscSeqFrom2ndByte(pb, cb, pMap->abEscLoadG3, sizeof(pMap->abEscLoadG3), &cchMatch))
556 iGn = 3;
557 else
558 iGn = UINT32_MAX;
559 if (iGn != UINT32_MAX)
560 {
561 pThis->apMapGn[iGn] = pMap;
562 return cchMatch + 1;
563 }
564 }
565 }
566 return VERR_ASN1_TELETEX_UNSUPPORTED_CHARSET;
567}
568
569
570/**
571 * Interprets an escape sequence.
572 *
573 * @returns The length of the sequence on success, negative error status code on
574 * failure.
575 * @param pThis The decoder instance. The offString must be
576 * pointing to the escape byte.
577 */
578static int rtIso2022Decoder_InterpretEsc(PRTISO2022DECODERSTATE pThis)
579{
580 /* the first escape byte. */
581 uint32_t offString = pThis->offString;
582 if (offString + 1 >= pThis->cbString)
583 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
584 "@%u: Unexpected EOS parsing ESC...", offString);
585 int rc;
586 switch (pThis->pabString[offString + 1])
587 {
588 /*
589 * GL selection:
590 */
591 case 0x6e: /* Lock shift two: G2 -> GL */
592 rc = rtIso2022Decoder_SetGL(pThis, pThis->apMapGn[2]);
593 break;
594 case 0x6f: /* Lock shift three: G3 -> GL */
595 rc = rtIso2022Decoder_SetGL(pThis, pThis->apMapGn[3]);
596 break;
597 case 0x4e: /* Single shift two: G2 -> GL for one char. */
598 rc = rtIso2022Decoder_SetGLForOneChar(pThis, pThis->apMapGn[2]);
599 break;
600 case 0x4f: /* Single shift three: G3 -> GL for one char. */
601 rc = rtIso2022Decoder_SetGLForOneChar(pThis, pThis->apMapGn[3]);
602 break;
603
604 /*
605 * GR selection:
606 */
607 case 0x7e: /* Locking shift one right: G1 -> GR. */
608 rc = rtIso2022Decoder_SetGR(pThis, pThis->apMapGn[1]);
609 break;
610 case 0x7d: /* Locking shift two right: G2 -> GR. */
611 rc = rtIso2022Decoder_SetGR(pThis, pThis->apMapGn[2]);
612 break;
613 case 0x7c: /* Locking shift three right: G3 -> GR. */
614 rc = rtIso2022Decoder_SetGR(pThis, pThis->apMapGn[3]);
615 break;
616
617 /*
618 * Cx selection:
619 */
620 case 0x21: /* C0-designate */
621 return rtIso2022Decoder_FindEscAndSet(pThis, &pThis->pMapC0, g_paC0Maps, RT_ELEMENTS(g_paC0Maps));
622 case 0x22: /* C1-designate */
623 return rtIso2022Decoder_FindEscAndSet(pThis, &pThis->pMapC1, g_paC1Maps, RT_ELEMENTS(g_paC1Maps));
624
625 /*
626 * Single-byte character set selection.
627 */
628 case 0x28: /* G0-designate, 94 chars. */
629 case 0x29: /* G1-designate, 94 chars. */
630 case 0x2a: /* G2-designate, 94 chars. */
631 case 0x2b: /* G3-designate, 94 chars. */
632 return rtIso2022Decoder_FindEscAndSet(pThis, NULL, g_paGLMaps, RT_ELEMENTS(g_paGLMaps));
633
634 case 0x2c: /* G0-designate, 96 chars. */
635 case 0x2d: /* G1-designate, 96 chars. */
636 case 0x2e: /* G2-designate, 96 chars. */
637 case 0x2f: /* G3-designate, 96 chars. */
638 return rtIso2022Decoder_FindEscAndSet(pThis, NULL, g_paGRMaps, RT_ELEMENTS(g_paGRMaps));
639
640 /*
641 * Multibyte character set selection.
642 */
643 case 0x24:
644 if (offString + 2 >= pThis->cbString)
645 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
646 "@%u: Unexpected EOS parsing ESC %#x...", offString, pThis->pabString[offString + 1]);
647 switch (pThis->pabString[offString + 2])
648 {
649 case 0x28: /* G0-designate, 94^n chars. */
650 case 0x29: /* G1-designate, 94^n chars. */
651 case 0x2a: /* G2-designate, 94^n chars. */
652 case 0x2b: /* G3-designate, 94^n chars. */
653 default: /* G0-designate that skips the 0x28? (See japanese ones.) */
654 return rtIso2022Decoder_FindEscAndSet(pThis, NULL, g_paGLMaps, RT_ELEMENTS(g_paGLMaps));
655
656 case 0x2c: /* G0-designate, 96^n chars. */
657 case 0x2d: /* G1-designate, 96^n chars. */
658 case 0x2e: /* G2-designate, 96^n chars. */
659 case 0x2f: /* G3-designate, 96^n chars. */
660 return rtIso2022Decoder_FindEscAndSet(pThis, NULL, g_paGRMaps, RT_ELEMENTS(g_paGRMaps));
661 } \
662 break;
663
664 case 0x26: /* Special escape prefix for #168. */
665 return rtIso2022Decoder_FindEscAndSet(pThis, NULL, g_paGLMaps, RT_ELEMENTS(g_paGLMaps));
666
667 /*
668 * Unknown/unsupported/unimplemented.
669 */
670 case 0x25: /* Designate other coding system. */
671 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_TELETEX_UNSUPPORTED_ESC_SEQ,
672 "@%u: ESC DOCS not supported\n", offString);
673 default:
674 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_TELETEX_UNKNOWN_ESC_SEQ,
675 "@%u: Unknown escape sequence: ESC %#x...\n", offString, pThis->pabString[offString + 1]);
676 }
677
678 /* Only single byte escapes sequences for shifting ends up here. */
679 if (RT_SUCCESS(rc))
680 return 1;
681 return rc;
682}
683
684
685static int rtIso2022Decoder_ControlCharHook(PRTISO2022DECODERSTATE pThis, uint16_t wcControl)
686{
687 int rc;
688 switch (wcControl)
689 {
690 case 0x000e: /* Locking shift zero: G0 -> GL. */
691 rc = rtIso2022Decoder_SetGL(pThis, pThis->apMapGn[0]);
692 break;
693
694 case 0x000f: /* Locking shift one: G1 -> GL. */
695 rc = rtIso2022Decoder_SetGL(pThis, pThis->apMapGn[1]);
696 break;
697
698 case 0x008e: /* Single shift two: G2 -> GL for one char. */
699 rc = rtIso2022Decoder_SetGLForOneChar(pThis, pThis->apMapGn[2]);
700 break;
701
702 case 0x008f: /* Single shift three: G3 -> GL for one char. */
703 rc = rtIso2022Decoder_SetGLForOneChar(pThis, pThis->apMapGn[3]);
704 break;
705
706 case 0x002b: /* Escape should be handled by the caller. */
707 rc = rtIso2022Decoder_InterpretEsc(pThis);
708 break;
709
710 default:
711 return 0;
712 }
713
714 return RT_SUCCESS(rc) ? 1 : rc;
715}
716
717
718static int rtIso2022Decoder_Init(PRTISO2022DECODERSTATE pThis, const char *pchString, uint32_t cchString,
719 uint32_t uGL, uint32_t uC0, uint32_t uC1, uint32_t uG0,
720 PRTERRINFO pErrInfo)
721{
722 pThis->pabString = (uint8_t const *)pchString;
723 pThis->cbString = cchString;
724 pThis->offString = 0;
725
726 pThis->pMapGL = &g_DummyMap;
727 pThis->pMapGR = &g_DummyMap;
728 pThis->pMapC0 = &g_DummyMap;
729 pThis->pMapC1 = &g_DummyMap;
730 pThis->pRestoreGL = NULL;
731 pThis->apMapGn[0] = &g_DummyMap;
732 pThis->apMapGn[1] = &g_DummyMap;
733 pThis->apMapGn[2] = &g_DummyMap;
734 pThis->apMapGn[3] = &g_DummyMap;
735 pThis->pErrInfo = pErrInfo;
736
737 int rc = VINF_SUCCESS;
738 if (uGL != UINT32_MAX)
739 rc = rtIso2022Decoder_LookupAndSet(&pThis->pMapGL, uGL, g_paGLMaps, RT_ELEMENTS(g_paGLMaps));
740 if (RT_SUCCESS(rc) && uG0 != UINT32_MAX)
741 rc = rtIso2022Decoder_LookupAndSet(&pThis->apMapGn[0], uG0, g_paGLMaps, RT_ELEMENTS(g_paGLMaps));
742 if (RT_SUCCESS(rc) && uC0 != UINT32_MAX)
743 rc = rtIso2022Decoder_SetC0(pThis, uC0);
744 if (RT_SUCCESS(rc) && uC1 != UINT32_MAX)
745 rc = rtIso2022Decoder_SetC1(pThis, uC1);
746 return rc;
747}
748
749
750static int rtIso2022Decoder_GetNextUniCpSlow(PRTISO2022DECODERSTATE pThis, PRTUNICP pUniCp)
751{
752 while (pThis->offString < pThis->cbString)
753 {
754 uint8_t b = pThis->pabString[pThis->offString];
755 if (!(b & 0x80))
756 {
757 if (b >= 0x20)
758 {
759 /*
760 * GL range.
761 */
762 b -= 0x20;
763 PCRTISO2022MAP pMap = pThis->pMapGL;
764
765 /* Single byte character map. */
766 if (pMap->cb == 1)
767 {
768 if (RT_LIKELY(b < pMap->cToUni))
769 {
770 uint16_t wc = pMap->pauToUni[b];
771 if (RT_LIKELY(wc != RTISO2022_UNUSED))
772 {
773 *pUniCp = wc;
774 pThis->offString += 1;
775 return VINF_SUCCESS;
776 }
777 *pUniCp = RTUNICP_INVALID;
778 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
779 "@%u: GL b=%#x is marked unused in map #%u range %u.",
780 pThis->offString, b + 0x20, pMap->uRegistration, pMap->cToUni);
781 }
782 *pUniCp = RTUNICP_INVALID;
783 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
784 "@%u: GL b=%#x is outside map #%u range %u.",
785 pThis->offString, b + 0x20, pMap->uRegistration, pMap->cToUni);
786 }
787
788 /* Double byte character set. */
789 Assert(pMap->cb == 2);
790 if (pThis->offString + 1 < pThis->cbString)
791 {
792 uint8_t b2 = pThis->pabString[pThis->offString + 1];
793 b2 -= 0x20;
794 if (RT_LIKELY(b2 < 0x60))
795 {
796 uint16_t u16 = ((uint16_t)b << 8) | b2;
797 if (RT_LIKELY(u16 < pMap->cToUni))
798 {
799 uint16_t wc = pMap->pauToUni[b];
800 if (RT_LIKELY(wc != RTISO2022_UNUSED))
801 {
802 *pUniCp = wc;
803 pThis->offString += 2;
804 return VINF_SUCCESS;
805 }
806 *pUniCp = RTUNICP_INVALID;
807 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
808 "@%u: GL b=%#x is marked unused in map #%u.",
809 pThis->offString, b + 0x20, pMap->uRegistration);
810 }
811 if (u16 >= 0x7f00)
812 {
813 *pUniCp = 0x7f; /* delete */
814 pThis->offString += 2;
815 return VINF_SUCCESS;
816 }
817 *pUniCp = RTUNICP_INVALID;
818 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
819 "@%u: GL u16=%#x (b0=%#x b1=%#x) is outside map #%u range %u.",
820 pThis->offString, u16, b + 0x20, b2 + 0x20, pMap->uRegistration, pMap->cToUni);
821 }
822 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
823 "@%u: 2nd GL byte outside GL range: b0=%#x b1=%#x (map #%u)",
824 pThis->offString, b + 0x20, b2 + 0x20, pMap->uRegistration);
825
826 }
827 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
828 "@%u: EOS reading 2nd byte for GL b=%#x (map #%u).",
829 pThis->offString, b + 0x20, pMap->uRegistration);
830 }
831 else
832 {
833 /*
834 * C0 range.
835 */
836 Assert(pThis->pMapC0->cb == 0x20);
837 uint16_t wc = pThis->pMapC0->pauToUni[b];
838 if (wc != RTISO2022_UNUSED)
839 {
840 int rc;
841 if (b == 0x1b || wc == 0x1b) /* ESC is hardcoded, or so they say. */
842 rc = rtIso2022Decoder_InterpretEsc(pThis);
843 else
844 rc = rtIso2022Decoder_ControlCharHook(pThis, wc);
845 if (RT_SUCCESS(rc))
846 {
847 if (rc == 0)
848 {
849 pThis->offString += 1;
850 *pUniCp = wc;
851 return VINF_SUCCESS;
852 }
853 pThis->offString += rc;
854 }
855 else
856 return rc;
857 }
858 else
859 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
860 "@%u: C0 b=%#x is marked unused in map #%u.",
861 pThis->offString, b, pThis->pMapC0->uRegistration);
862 }
863 }
864 else
865 {
866 if (b >= 0xa0)
867 {
868 /*
869 * GR range.
870 */
871 b -= 0xa0;
872 PCRTISO2022MAP pMap = pThis->pMapGR;
873
874 /* Single byte character map. */
875 if (pMap->cb == 1)
876 {
877 /** @todo 0xa0 = SPACE and 0xff = DELETE if it's a 94 charater map... */
878 if (RT_LIKELY(b < pMap->cToUni))
879 {
880 uint16_t wc = pMap->pauToUni[b];
881 if (RT_LIKELY(wc != RTISO2022_UNUSED))
882 {
883 *pUniCp = wc;
884 pThis->offString += 1;
885 return VINF_SUCCESS;
886 }
887
888 *pUniCp = RTUNICP_INVALID;
889 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
890 "@%u: GR b=%#x is marked unused in map #%u.",
891 pThis->offString, b + 0xa0, pMap->uRegistration);
892 }
893 *pUniCp = RTUNICP_INVALID;
894 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
895 "@%u: GR b=%#x is outside map #%u range %u",
896 pThis->offString, b + 0xa0, pMap->uRegistration, pMap->cToUni);
897 }
898
899 /* Double byte character set. */
900 Assert(pMap->cb == 2);
901 if (pThis->offString + 1 < pThis->cbString)
902 {
903 uint8_t b2 = pThis->pabString[pThis->offString + 1];
904 b2 -= 0xa0;
905 if (RT_LIKELY(b2 < 0x60))
906 {
907 uint16_t u16 = ((uint16_t)b << 8) | b2;
908 if (RT_LIKELY(u16 < pMap->cToUni))
909 {
910 uint16_t wc = pMap->pauToUni[b];
911 if (RT_LIKELY(wc != RTISO2022_UNUSED))
912 {
913 *pUniCp = wc;
914 pThis->offString += 2;
915 return VINF_SUCCESS;
916 }
917
918 *pUniCp = RTUNICP_INVALID;
919 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
920 "@%u: GR b=%#x is marked unused in map #%u.",
921 pThis->offString, b + 0xa0, pMap->uRegistration);
922 }
923 *pUniCp = RTUNICP_INVALID;
924 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
925 "@%u: GR u16=%#x (b0=%#x b1=%#x) is outside map #%u range %u.",
926 pThis->offString, u16, b + 0xa0, b2 + 0xa0, pMap->uRegistration, pMap->cToUni);
927 }
928 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
929 "@%u: 2nd GR byte outside GR range: b0=%#x b1=%#x (map #%u).",
930 pThis->offString, b + 0xa0, b2 + 0xa0, pMap->uRegistration);
931
932 }
933 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
934 "@%u: EOS reading 2nd byte for GR b=%#x (map #%u).",
935 pThis->offString, b + 0xa0, pMap->uRegistration);
936 }
937 else
938 {
939 /*
940 * C2 range.
941 */
942 Assert(pThis->pMapC1->cb == 0x20);
943 b -= 0x80;
944 uint16_t wc = pThis->pMapC1->pauToUni[b];
945 if (wc != RTISO2022_UNUSED)
946 {
947 int rc = rtIso2022Decoder_ControlCharHook(pThis, wc);
948 if (RT_SUCCESS(rc))
949 {
950 if (rc == 0)
951 {
952 pThis->offString += 1;
953 *pUniCp = wc;
954 return VINF_SUCCESS;
955 }
956 pThis->offString += rc;
957 }
958 else
959 return rc;
960 }
961 else
962 return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
963 "@%u: C1 b=%#x is marked unused in map #%u.",
964 pThis->offString, b + 0x80, pThis->pMapC1->uRegistration);
965 }
966 }
967 }
968
969 /* End of string. */
970 *pUniCp = RTUNICP_INVALID;
971 return VERR_END_OF_STRING;
972}
973
974DECLINLINE(int) rtIso2022Decoder_GetNextUniCp(PRTISO2022DECODERSTATE pThis, PRTUNICP pUniCp)
975{
976 /*
977 * Deal with single byte GL.
978 */
979 uint32_t const offString = pThis->offString;
980 if (pThis->offString < pThis->cbString)
981 {
982 PCRTISO2022MAP const pMapGL = pThis->pMapGL;
983 if (pMapGL->cb == 1)
984 {
985 uint8_t const b = pThis->pabString[offString] - (uint8_t)0x20;
986 if (b < pMapGL->cToUni)
987 {
988 uint16_t wc = pMapGL->pauToUni[b];
989 if (wc != RTISO2022_UNUSED)
990 {
991 pThis->offString = offString + 1;
992 *pUniCp = wc;
993 return VINF_SUCCESS;
994 }
995 }
996 }
997
998 /*
999 * Deal with complications in the non-inline function.
1000 */
1001 return rtIso2022Decoder_GetNextUniCpSlow(pThis, pUniCp);
1002 }
1003
1004 *pUniCp = RTUNICP_INVALID;
1005 return VERR_END_OF_STRING;
1006}
1007
1008
1009static int rtIso2022ValidateString(uint32_t uProfile, const char *pch, uint32_t cch, size_t *pcchUtf8, PRTERRINFO pErrInfo)
1010{
1011 AssertReturn(uProfile == ASN1_TAG_T61_STRING, VERR_INVALID_PARAMETER); /* just a place holder for now. */
1012
1013 RTISO2022DECODERSTATE Decoder;
1014 int rc = rtIso2022Decoder_Init(&Decoder, pch, cch, 102, 106, 107, 102, pErrInfo);
1015 if (RT_SUCCESS(rc))
1016 {
1017 size_t cchUtf8 = 0;
1018 for (;;)
1019 {
1020 RTUNICP uc;
1021 rc = rtIso2022Decoder_GetNextUniCp(&Decoder, &uc);
1022 if (RT_SUCCESS(rc))
1023 cchUtf8 += RTStrCpSize(uc);
1024 else
1025 {
1026 if (RT_LIKELY(rc == VERR_END_OF_STRING))
1027 {
1028 *pcchUtf8 = cchUtf8;
1029 return VINF_SUCCESS;
1030 }
1031 return rc;
1032 }
1033 }
1034 }
1035 return rc;
1036}
1037
1038
1039static int rtIso2022RecodeAsUtf8(uint32_t uProfile, const char *pchSrc, uint32_t cchSrc, char *pszDst, size_t cbDst)
1040{
1041 AssertReturn(uProfile == ASN1_TAG_T61_STRING, VERR_INVALID_PARAMETER); /* just a place holder for now. */
1042 AssertReturn(cbDst > 0, VERR_INVALID_PARAMETER);
1043
1044 RTISO2022DECODERSTATE Decoder;
1045 int rc = rtIso2022Decoder_Init(&Decoder, pchSrc, cchSrc, 102, 106, 107, 102, NULL /*pErrInfo*/);
1046 if (RT_SUCCESS(rc))
1047 {
1048 for (;;)
1049 {
1050 RTUNICP uc;
1051 rc = rtIso2022Decoder_GetNextUniCp(&Decoder, &uc);
1052 if (RT_SUCCESS(rc))
1053 {
1054 if (uc < 0x80 && cbDst > 1)
1055 {
1056 *pszDst++ = (char)uc;
1057 cbDst--;
1058 }
1059 else
1060 {
1061 size_t cchUniCp = RTStrCpSize(uc);
1062 if (cbDst > cchUniCp)
1063 {
1064 cbDst -= cchUniCp;
1065 pszDst = RTStrPutCp(pszDst, uc);
1066 }
1067 else
1068 {
1069 *pszDst = '\0';
1070 return VERR_BUFFER_OVERFLOW;
1071 }
1072 }
1073 }
1074 else if (RT_LIKELY(rc == VERR_END_OF_STRING))
1075 {
1076 *pszDst = '\0';
1077 return VINF_SUCCESS;
1078 }
1079 else
1080 return rc;
1081 }
1082 }
1083 return rc;
1084}
1085
1086
1087
1088/** The unicode mapping of the C1 area of windows codepage 1252.
1089 * The rest of the code page is 1:1 with unicode. */
1090static uint16_t g_awcWin1252_C1[0x20] =
1091{
1092 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
1093 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178,
1094};
1095
1096
1097static size_t rtWin1252CalcUtf8Length(const char *pch, uint32_t cch)
1098{
1099 size_t cchUtf8 = 0;
1100 while (cch-- > 0)
1101 {
1102 uint8_t const b = *pch++;
1103 if (b < 0x80)
1104 cchUtf8 += 1;
1105 else if (b >= 0xa0)
1106 cchUtf8 += 2;
1107 else
1108 {
1109 uint16_t const wc = g_awcWin1252_C1[b - 0x80];
1110 cchUtf8 += RTStrCpSize(wc);
1111 }
1112 }
1113 return cchUtf8;
1114}
1115
1116
1117static int rtWin1252RecodeAsUtf8(const char *pchSrc, uint32_t cchSrc, char *pszDst, size_t cbDst)
1118{
1119 while (cchSrc-- > 0)
1120 {
1121 uint8_t b = *pchSrc++;
1122 if (b < 0x80)
1123 {
1124 if (cbDst <= 1)
1125 return VERR_BUFFER_OVERFLOW;
1126 *pszDst++ = (char)b;
1127 }
1128 else
1129 {
1130 uint16_t const wc = b >= 0xa0 ? b : g_awcWin1252_C1[b - 0x80];
1131 size_t cchCp = RTStrCpSize(wc);
1132 if (cbDst <= cchCp)
1133 return VERR_BUFFER_OVERFLOW;
1134 pszDst = RTStrPutCp(pszDst, wc);
1135 }
1136 }
1137
1138 if (!cbDst)
1139 return VERR_BUFFER_OVERFLOW;
1140 *pszDst = '\0';
1141 return VINF_SUCCESS;
1142}
1143
1144
1145
1146/*
1147 * ASN.1 STRING - Specific Methods.
1148 */
1149
1150/** rtAsn1String_IsTeletexLatin1 results. */
1151typedef enum RTASN1TELETEXVARIANT
1152{
1153 /** Couldn't find hard evidence of either. */
1154 RTASN1TELETEXVARIANT_UNDECIDED = 1,
1155 /** Pretty certain that it's real teletex. */
1156 RTASN1TELETEXVARIANT_TELETEX,
1157 /** Pretty sure it's latin-1 or Windows-1252. */
1158 RTASN1TELETEXVARIANT_LATIN1,
1159 /** Pretty sure it's Windows-1252. */
1160 RTASN1TELETEXVARIANT_WIN_1252
1161} RTASN1TELETEXVARIANT;
1162
1163/**
1164 * Takes a guess as whether TELETEX STRING (T61 STRING) is actually is Latin-1
1165 * or the real thing.
1166 *
1167 * According to RFC-2459, section 4.1.2.4, various libraries, certificate
1168 * authorities and others have perverted the TeletexString/T61String tag by
1169 * ISO-8859-1 (aka latin-1) strings (more probably these are actually Windows
1170 * CP-1252 rather than latin-1). We'll try detect incompatible latin-1
1171 * perversions by:
1172 * - The use of GR (0xf0-0xff) chars.
1173 * - The lack of ESC sequences and shifts (LS0,LS1,SS2,SS3)
1174 *
1175 * An ASSUMTION here is that GR is not loaded with anything at the start of a
1176 * teletex string, as per table 3 in section 8.23.5.2 in T-REC-X.590.200811.
1177 *
1178 * @retval @c true if chances are good that it's LATIN-1.
1179 * @retval @c false if changes are very good that it's real teletex.
1180 * @param pch The first char in the string.
1181 * @param cch The string length.
1182 *
1183 * @remarks Useful info on Teletex and ISO/IEC-2022:
1184 * https://www.mail-archive.com/asn1@asn1.org/msg00460.html
1185 * http://en.wikipedia.org/wiki/ISO/IEC_2022
1186 * http://www.open-std.org/cen/tc304/guide/GCONCEPT.HTM
1187 * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-035.pdf
1188 */
1189static RTASN1TELETEXVARIANT rtAsn1String_IsTeletexLatin1(const char *pch, uint32_t cch)
1190{
1191 RTASN1TELETEXVARIANT enmVariant = RTASN1TELETEXVARIANT_UNDECIDED;
1192 while (cch-- > 0)
1193 {
1194 uint8_t const b = *pch;
1195 if (b >= 0x20 && b <= 0x7f)
1196 {
1197 if (g_awcIsoIr102Decode[b - 0x20] == RTISO2022_UNUSED)
1198 enmVariant = RTASN1TELETEXVARIANT_LATIN1;
1199 }
1200 else
1201 {
1202 if ( b == 0x1b /* ESC */
1203 || b == 0x0e /* LS0 / SI */
1204 || b == 0x0f /* LS1 / SO */
1205 || b == 0x19 /* SS2 */
1206 || b == 0x1d /* SS3 */ )
1207 return RTASN1TELETEXVARIANT_TELETEX;
1208
1209 if (b >= 0xa0)
1210 enmVariant = RTASN1TELETEXVARIANT_LATIN1;
1211 else if (b >= 0x80 && b <= 0x9f)
1212 {
1213 /* Any use of C1 characters defined by windows cp-1252 will
1214 lead us to believe it's the windows code rather than the
1215 ISO/IEC standard that is being used. (Not that it makes
1216 much of a difference, because we're gonna treat it as the
1217 windows codepage, anyways.) */
1218 if ( b != 0x81
1219 && b != 0x8d
1220 && b != 0x8f
1221 && b != 0x90
1222 && b != 0x9d)
1223 return RTASN1TELETEXVARIANT_WIN_1252;
1224 }
1225 }
1226 }
1227 return RTASN1TELETEXVARIANT_UNDECIDED;
1228}
1229
1230
1231/**
1232 * Checks the encoding of an ASN.1 string according to it's tag.
1233 *
1234 * @returns IPRT status code.
1235 * @param pThis The string to santity check.
1236 * @param pErrInfo Where to store extra error info. Optional.
1237 * @param pcchUtf8 Where to return the UTF-8 string length. Optional.
1238 */
1239static int rtAsn1String_CheckSanity(PCRTASN1STRING pThis, PRTERRINFO pErrInfo, const char *pszErrorTag, size_t *pcchUtf8)
1240{
1241 int rc;
1242 uint32_t cch = pThis->Asn1Core.cb;
1243 size_t cchUtf8 = cch;
1244 const char *pch = pThis->Asn1Core.uData.pch;
1245 uint32_t uTag = RTASN1CORE_GET_TAG(&pThis->Asn1Core);
1246 switch (uTag)
1247 {
1248 case ASN1_TAG_UTF8_STRING:
1249 rc = RTStrValidateEncodingEx(pch, cch, 0);
1250 if (RT_SUCCESS(rc))
1251 break;
1252 return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_UTF8_STRING_ENCODING, "%s: Bad UTF-8 encoding (%Rrc, %.*Rhxs)",
1253 pszErrorTag, rc, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
1254
1255 case ASN1_TAG_NUMERIC_STRING:
1256 while (cch-- > 0)
1257 {
1258 char ch = *pch++;
1259 if ( !RT_C_IS_DIGIT(ch)
1260 && ch != ' ')
1261 return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_NUMERIC_STRING_ENCODING,
1262 "%s: Bad numeric string: ch=%#x (pos %u in %.*Rhxs)", pszErrorTag, ch,
1263 pThis->Asn1Core.cb - cch + 1, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
1264 }
1265 break;
1266
1267 case ASN1_TAG_PRINTABLE_STRING:
1268 while (cch-- > 0)
1269 {
1270 char ch = *pch++;
1271 if ( !RT_C_IS_ALNUM(ch)
1272 && ch != ' '
1273 && ch != '\''
1274 && ch != '('
1275 && ch != ')'
1276 && ch != '+'
1277 && ch != ','
1278 && ch != '-'
1279 && ch != '.'
1280 && ch != '/'
1281 && ch != ':'
1282 && ch != '='
1283 && ch != '?'
1284 )
1285 return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_PRINTABLE_STRING_ENCODING,
1286 "%s: Bad printable string: ch=%#x (pos %u in %.*Rhxs)", pszErrorTag, ch,
1287 pThis->Asn1Core.cb - cch + 1, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
1288 }
1289 break;
1290
1291 case ASN1_TAG_IA5_STRING: /* ASCII */
1292 while (cch-- > 0)
1293 {
1294 unsigned char ch = *pch++;
1295 if (ch == 0 || ch >= 0x80)
1296 {
1297 /* Ignore C-style zero terminator as the "Microsoft ECC Product Root Certificate Authority 2018"
1298 for instance, has a policy qualifier string "http://www.microsoft.com/pkiops/Docs/Repository.htm\0" */
1299 /** @todo should '\0' really be excluded above? */
1300 if (ch != 0 || cch != 0)
1301 return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_IA5_STRING_ENCODING,
1302 "%s: Bad IA5 string: ch=%#x (pos %u in %.*Rhxs)", pszErrorTag, ch,
1303 pThis->Asn1Core.cb - cch + 1, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
1304 break;
1305 }
1306 }
1307 break;
1308
1309 case ASN1_TAG_T61_STRING:
1310 switch (rtAsn1String_IsTeletexLatin1(pch, cch))
1311 {
1312 default:
1313 rc = rtIso2022ValidateString(ASN1_TAG_T61_STRING, pch, cch, &cchUtf8, pErrInfo);
1314 if (RT_FAILURE(rc))
1315 return rc;
1316 break;
1317 case RTASN1TELETEXVARIANT_UNDECIDED:
1318 case RTASN1TELETEXVARIANT_LATIN1:
1319 case RTASN1TELETEXVARIANT_WIN_1252:
1320 cchUtf8 = rtWin1252CalcUtf8Length(pch, cch);
1321 break;
1322 }
1323 break;
1324
1325 case ASN1_TAG_VIDEOTEX_STRING:
1326 case ASN1_TAG_GRAPHIC_STRING:
1327 return VERR_ASN1_STRING_TYPE_NOT_IMPLEMENTED;
1328
1329 case ASN1_TAG_VISIBLE_STRING:
1330 while (cch-- > 0)
1331 {
1332 unsigned char ch = *pch++;
1333 if (ch < 0x20 || ch >= 0x7f)
1334 return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_VISIBLE_STRING_ENCODING,
1335 "%s: Bad visible string: ch=%#x (pos %u in %.*Rhxs)", pszErrorTag, ch,
1336 pThis->Asn1Core.cb - cch + 1, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
1337 }
1338 break;
1339
1340 case ASN1_TAG_GENERAL_STRING:
1341 return VERR_ASN1_STRING_TYPE_NOT_IMPLEMENTED;
1342
1343 case ASN1_TAG_UNIVERSAL_STRING:
1344 if (!(cch & 3))
1345 {
1346 uint8_t const *pb = (uint8_t const *)pch;
1347 cchUtf8 = 0;
1348 while (cch > 0)
1349 {
1350 RTUNICP uc = RT_MAKE_U32_FROM_U8(pb[3], pb[2], pb[1], pb[0]); /* big endian */
1351 if (!RTUniCpIsValid(uc))
1352 return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_UNIVERSAL_STRING_ENCODING,
1353 "%s: Bad universal string: uc=%#x (pos %u in %.*Rhxs)", pszErrorTag, uc,
1354 pThis->Asn1Core.cb - cch + 1, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
1355 cchUtf8 += RTUniCpCalcUtf8Len(uc);
1356
1357 /* next */
1358 pb += 4;
1359 cch -= 4;
1360 }
1361 break;
1362 }
1363 return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_UNIVERSAL_STRING_ENCODING,
1364 "%s: Bad universal string: size not a multiple of 4: cch=%#x (%.*Rhxs)",
1365 pszErrorTag, cch, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
1366
1367 case ASN1_TAG_BMP_STRING:
1368 if (!(cch & 1))
1369 {
1370 uint8_t const *pb = (uint8_t const *)pch;
1371 cchUtf8 = 0;
1372 while (cch > 0)
1373 {
1374 RTUNICP uc = RT_MAKE_U32_FROM_U8(pb[1], pb[0], 0, 0); /* big endian */
1375 if (!RTUniCpIsValid(uc))
1376 return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_BMP_STRING_ENCODING,
1377 "%s: Bad BMP string: uc=%#x (pos %u in %.*Rhxs)", pszErrorTag, uc,
1378 pThis->Asn1Core.cb - cch + 1, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
1379 cchUtf8 += RTUniCpCalcUtf8Len(uc);
1380
1381 /* next */
1382 pb += 2;
1383 cch -= 2;
1384 }
1385 break;
1386 }
1387 return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_BMP_STRING_ENCODING,
1388 "%s: Bad BMP string: odd number of bytes cch=%#x (%.*Rhxs)",
1389 pszErrorTag, cch, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
1390
1391 default:
1392 AssertMsgFailedReturn(("uTag=%#x\n", uTag), VERR_INTERNAL_ERROR_3);
1393 }
1394
1395 if (pcchUtf8)
1396 *pcchUtf8 = cchUtf8;
1397 return VINF_SUCCESS;
1398}
1399
1400
1401RTDECL(int) RTAsn1String_CompareValues(PCRTASN1STRING pLeft, PCRTASN1STRING pRight)
1402{
1403 return RTAsn1String_CompareEx(pLeft, pRight, false /*fTypeToo*/);
1404}
1405
1406
1407RTDECL(int) RTAsn1String_CompareEx(PCRTASN1STRING pLeft, PCRTASN1STRING pRight, bool fTypeToo)
1408{
1409 Assert(pLeft && (!RTAsn1String_IsPresent(pLeft) || pLeft->Asn1Core.pOps == &g_RTAsn1String_Vtable));
1410 Assert(pRight && (!RTAsn1String_IsPresent(pRight) || pRight->Asn1Core.pOps == &g_RTAsn1String_Vtable));
1411
1412 int iDiff;
1413 if (RTAsn1String_IsPresent(pLeft))
1414 {
1415 if (RTAsn1String_IsPresent(pRight))
1416 {
1417 if (!fTypeToo || RTASN1CORE_GET_TAG(&pLeft->Asn1Core) == RTASN1CORE_GET_TAG(&pRight->Asn1Core))
1418 iDiff = RTAsn1Core_CompareEx(&pLeft->Asn1Core, &pRight->Asn1Core, true /*fIgnoreTagAndClass*/);
1419 else
1420 iDiff = RTASN1CORE_GET_TAG(&pLeft->Asn1Core) < RTASN1CORE_GET_TAG(&pRight->Asn1Core) ? -1 : 1;
1421 }
1422 else
1423 iDiff = 1;
1424 }
1425 else
1426 iDiff = 0 - RTAsn1String_IsPresent(pRight);
1427 return iDiff;
1428}
1429
1430
1431RTDECL(int) RTAsn1String_CompareWithString(PCRTASN1STRING pThis, const char *pszString, size_t cchString)
1432{
1433 Assert(pThis && (!RTAsn1String_IsPresent(pThis) || pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable));
1434 AssertPtr(pszString);
1435
1436 int iDiff;
1437 if (RTAsn1String_IsPresent(pThis))
1438 {
1439 if (cchString == RTSTR_MAX)
1440 cchString = strlen(pszString);
1441
1442 /*
1443 * If there is a UTF-8 conversion available already, use it.
1444 */
1445 if (pThis->pszUtf8)
1446 {
1447 iDiff = strncmp(pThis->pszUtf8, pszString, cchString);
1448 if (!iDiff && pThis->cchUtf8 != cchString)
1449 iDiff = pThis->cchUtf8 < cchString ? -1 : 1;
1450 }
1451 else
1452 {
1453 /*
1454 * Some types are UTF-8 compatible, so try do the compare without
1455 * RTAsn1String_QueryUtf8.
1456 */
1457 uint32_t cch = pThis->Asn1Core.cb;
1458 const char *pch = pThis->Asn1Core.uData.pch;
1459 switch (RTASN1CORE_GET_TAG(&pThis->Asn1Core))
1460 {
1461 case ASN1_TAG_UTF8_STRING:
1462 case ASN1_TAG_NUMERIC_STRING:
1463 case ASN1_TAG_IA5_STRING:
1464 case ASN1_TAG_PRINTABLE_STRING:
1465 iDiff = strncmp(pch, pszString, RT_MIN(cch, cchString));
1466 if (iDiff && cch != cchString)
1467 iDiff = cch < cchString ? - 1 : 1;
1468 break;
1469
1470 /** @todo Implement comparing ASN1_TAG_BMP_STRING, ASN1_TAG_UNIVERSAL_STRING and
1471 * ASN1_TAG_T61_STRING with UTF-8 strings without conversion. */
1472
1473 default:
1474 {
1475 int rc = RTAsn1String_QueryUtf8(pThis, NULL, NULL);
1476 if (RT_SUCCESS(rc))
1477 {
1478 iDiff = strncmp(pThis->pszUtf8, pszString, cchString);
1479 if (!iDiff && pThis->cchUtf8 != cchString)
1480 iDiff = pThis->cchUtf8 < cchString ? -1 : 1;
1481 }
1482 else
1483 iDiff = -1;
1484 break;
1485 }
1486 }
1487 }
1488
1489 /* Reduce the strcmp return value. */
1490 if (iDiff != 0)
1491 iDiff = iDiff < 0 ? -1 : 1;
1492 }
1493 else
1494 iDiff = -1;
1495 return iDiff;
1496}
1497
1498
1499RTDECL(int) RTAsn1String_QueryUtf8(PCRTASN1STRING pThis, const char **ppsz, size_t *pcch)
1500{
1501 Assert(pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable);
1502
1503 char *psz = (char *)pThis->pszUtf8;
1504 size_t cch = pThis->cchUtf8;
1505 if (!psz)
1506 {
1507
1508 /*
1509 * Convert the first time around. Start by validating the encoding and
1510 * calculating the length.
1511 */
1512 int rc = rtAsn1String_CheckSanity(pThis, NULL, NULL, &cch);
1513 if (RT_SUCCESS(rc))
1514 {
1515 PRTASN1STRING pThisNC = (PRTASN1STRING)pThis;
1516 rc = RTAsn1MemAllocZ(&pThisNC->Allocation, (void **)&psz, cch + 1);
1517 if (RT_SUCCESS(rc))
1518 {
1519 /*
1520 * Got memory, now do the actual convertion to UTF-8 / copying.
1521 */
1522 switch (RTASN1CORE_GET_TAG(&pThis->Asn1Core))
1523 {
1524 case ASN1_TAG_UTF8_STRING:
1525 case ASN1_TAG_NUMERIC_STRING:
1526 case ASN1_TAG_PRINTABLE_STRING:
1527 case ASN1_TAG_IA5_STRING:
1528 case ASN1_TAG_VISIBLE_STRING:
1529 Assert(cch == pThis->Asn1Core.cb);
1530 memcpy(psz, pThis->Asn1Core.uData.pch, cch);
1531 psz[cch] = '\0';
1532 break;
1533
1534 case ASN1_TAG_T61_STRING:
1535 switch (rtAsn1String_IsTeletexLatin1(pThis->Asn1Core.uData.pch, pThis->Asn1Core.cb))
1536 {
1537 default:
1538 rc = rtIso2022RecodeAsUtf8(ASN1_TAG_T61_STRING, pThis->Asn1Core.uData.pch, pThis->Asn1Core.cb,
1539 psz, cch + 1);
1540 break;
1541 case RTASN1TELETEXVARIANT_UNDECIDED:
1542 case RTASN1TELETEXVARIANT_LATIN1:
1543 case RTASN1TELETEXVARIANT_WIN_1252:
1544 rc = rtWin1252RecodeAsUtf8(pThis->Asn1Core.uData.pch, pThis->Asn1Core.cb, psz, cch + 1);
1545 break;
1546 }
1547 AssertReturnStmt(RT_SUCCESS(rc), RTAsn1MemFree(&pThisNC->Allocation, psz), VERR_INTERNAL_ERROR_3);
1548 break;
1549
1550 /* case ASN1_TAG_VIDEOTEX_STRING: */
1551 /* case ASN1_TAG_GRAPHIC_STRING: */
1552 /* case ASN1_TAG_GENERAL_STRING: */
1553
1554 case ASN1_TAG_UNIVERSAL_STRING:
1555 {
1556 char *pszDst = psz;
1557 size_t cchSrc = pThis->Asn1Core.cb;
1558 uint8_t const *pbSrc = pThis->Asn1Core.uData.pu8;
1559 while (cchSrc > 0)
1560 {
1561 RTUNICP uc = RT_MAKE_U32_FROM_U8(pbSrc[3], pbSrc[2], pbSrc[1], pbSrc[0]); /* big endian */
1562 AssertReturnStmt(RTUniCpIsValid(uc), RTAsn1MemFree(&pThisNC->Allocation, psz), VERR_INTERNAL_ERROR_2);
1563 pszDst = RTStrPutCp(pszDst, uc);
1564
1565 /* next */
1566 pbSrc += 4;
1567 cchSrc -= 4;
1568 }
1569 Assert((size_t)(pszDst - psz) == cch);
1570 break;
1571 }
1572
1573 case ASN1_TAG_BMP_STRING:
1574 {
1575 char *pszDst = psz;
1576 size_t cchSrc = pThis->Asn1Core.cb;
1577 uint8_t const *pbSrc = pThis->Asn1Core.uData.pu8;
1578 while (cchSrc > 0)
1579 {
1580 RTUNICP uc = RT_MAKE_U32_FROM_U8(pbSrc[1], pbSrc[0], 0, 0); /* big endian */
1581 AssertReturnStmt(RTUniCpIsValid(uc), RTAsn1MemFree(&pThisNC->Allocation, psz), VERR_INTERNAL_ERROR_2);
1582 pszDst = RTStrPutCp(pszDst, uc);
1583
1584 /* next */
1585 pbSrc += 2;
1586 cchSrc -= 2;
1587 }
1588 Assert((size_t)(pszDst - psz) == cch);
1589 break;
1590 }
1591
1592 default:
1593 RTAsn1MemFree(&pThisNC->Allocation, psz);
1594 AssertMsgFailedReturn(("uTag=%#x\n", RTASN1CORE_GET_TAG(&pThis->Asn1Core)), VERR_INTERNAL_ERROR_3);
1595 }
1596
1597 /*
1598 * Successfully produced UTF-8. Save it in the object.
1599 */
1600 pThisNC->pszUtf8 = psz;
1601 pThisNC->cchUtf8 = (uint32_t)cch;
1602 }
1603 else
1604 return rc;
1605 }
1606 else
1607 return rc;
1608 }
1609
1610 /*
1611 * Success.
1612 */
1613 if (ppsz)
1614 *ppsz = psz;
1615 if (pcch)
1616 *pcch = cch;
1617 return VINF_SUCCESS;
1618}
1619
1620
1621
1622RTDECL(int) RTAsn1String_QueryUtf8Len(PCRTASN1STRING pThis, size_t *pcch)
1623{
1624 Assert(pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable);
1625
1626 size_t cch = pThis->cchUtf8;
1627 if (!cch && !pThis->pszUtf8)
1628 {
1629 int rc = rtAsn1String_CheckSanity(pThis, NULL, NULL, &cch);
1630 if (RT_FAILURE(rc))
1631 return rc;
1632 }
1633
1634 *pcch = cch;
1635 return VINF_SUCCESS;
1636}
1637
1638
1639
1640
1641RTDECL(int) RTAsn1String_InitEx(PRTASN1STRING pThis, uint32_t uTag, void const *pvValue, size_t cbValue,
1642 PCRTASN1ALLOCATORVTABLE pAllocator)
1643{
1644 RT_ZERO(*pThis);
1645 AssertMsgReturn(uTag < RT_ELEMENTS(g_acbStringTags) && g_acbStringTags[uTag] > 0, ("uTag=%#x\n", uTag),
1646 VERR_INVALID_PARAMETER);
1647
1648 RTAsn1MemInitAllocation(&pThis->Allocation, pAllocator);
1649 RTAsn1Core_InitEx(&pThis->Asn1Core,
1650 uTag,
1651 ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
1652 &g_RTAsn1String_Vtable,
1653 RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
1654
1655 if (cbValue > 0)
1656 {
1657 int rc = RTAsn1ContentDup(&pThis->Asn1Core, pvValue, cbValue, pAllocator);
1658 if (RT_FAILURE(rc))
1659 return rc;
1660 }
1661
1662 return VINF_SUCCESS;
1663}
1664
1665
1666RTDECL(int) RTAsn1String_InitWithValue(PRTASN1STRING pThis, const char *pszUtf8Value, PCRTASN1ALLOCATORVTABLE pAllocator)
1667{
1668 Assert(RTStrValidateEncoding(pszUtf8Value));
1669 return RTAsn1String_InitEx(pThis, ASN1_TAG_UTF8_STRING, pszUtf8Value, strlen(pszUtf8Value), pAllocator);
1670}
1671
1672
1673RTDECL(int) RTAsn1String_RecodeAsUtf8(PRTASN1STRING pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
1674{
1675 /*
1676 * Query the UTF-8 string. Do this even if it's already an
1677 * ASN1_TAG_UTF8_STRING object as it makes sure we've got a valid UTF-8
1678 * string upon successful return.
1679 */
1680 int rc = RTAsn1String_QueryUtf8(pThis, NULL, NULL);
1681 if (RT_SUCCESS(rc))
1682 {
1683 if (RTASN1CORE_GET_TAG(&pThis->Asn1Core) != ASN1_TAG_UTF8_STRING)
1684 {
1685 /*
1686 * Resize the content, copy the UTF-8 bytes in there, and change
1687 * the tag.
1688 */
1689 rc = RTAsn1ContentReallocZ(&pThis->Asn1Core, pThis->cchUtf8, pAllocator);
1690 if (RT_SUCCESS(rc))
1691 {
1692 memcpy((void *)pThis->Asn1Core.uData.pv, pThis->pszUtf8, pThis->cchUtf8);
1693 rc = RTAsn1Core_ChangeTag(&pThis->Asn1Core, ASN1_TAG_UTF8_STRING);
1694 }
1695 }
1696 }
1697 return rc;
1698}
1699
1700
1701
1702/*
1703 * ASN.1 STRING - Standard Methods.
1704 */
1705
1706RT_DECL_DATA_CONST(RTASN1COREVTABLE const) g_RTAsn1String_Vtable =
1707{
1708 "RTAsn1String",
1709 sizeof(RTASN1STRING),
1710 UINT8_MAX,
1711 ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
1712 0,
1713 (PFNRTASN1COREVTDTOR)RTAsn1String_Delete,
1714 NULL,
1715 (PFNRTASN1COREVTCLONE)RTAsn1String_Clone,
1716 (PFNRTASN1COREVTCOMPARE)RTAsn1String_Compare,
1717 (PFNRTASN1COREVTCHECKSANITY)RTAsn1String_CheckSanity,
1718 NULL,
1719 NULL
1720};
1721
1722
1723RTDECL(int) RTAsn1String_Init(PRTASN1STRING pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
1724{
1725 return RTAsn1String_InitEx(pThis, ASN1_TAG_UTF8_STRING, NULL /*pvValue*/, 0 /*cbValue*/, pAllocator);
1726}
1727
1728
1729RTDECL(int) RTAsn1String_Clone(PRTASN1STRING pThis, PCRTASN1STRING pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
1730{
1731 AssertPtr(pSrc); AssertPtr(pThis); AssertPtr(pAllocator);
1732 RT_ZERO(*pThis);
1733 if (RTAsn1String_IsPresent(pSrc))
1734 {
1735 AssertReturn(pSrc->Asn1Core.pOps == &g_RTAsn1String_Vtable, VERR_INTERNAL_ERROR_3);
1736 int rc = RTAsn1Core_CloneContent(&pThis->Asn1Core, &pSrc->Asn1Core, pAllocator);
1737 if (RT_SUCCESS(rc))
1738 {
1739 /* Don't copy the UTF-8 representation, decode it when queried. */
1740 RTAsn1MemInitAllocation(&pThis->Allocation, pAllocator);
1741 return VINF_SUCCESS;
1742 }
1743 }
1744 return VINF_SUCCESS;
1745}
1746
1747
1748RTDECL(void) RTAsn1String_Delete(PRTASN1STRING pThis)
1749{
1750 if ( pThis
1751 && RTAsn1String_IsPresent(pThis))
1752 {
1753 Assert(pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable);
1754
1755 if (pThis->Allocation.cbAllocated)
1756 RTAsn1MemFree(&pThis->Allocation, (char *)pThis->pszUtf8);
1757 RTAsn1ContentFree(&pThis->Asn1Core);
1758 RT_ZERO(*pThis);
1759 }
1760}
1761
1762
1763RTDECL(int) RTAsn1String_Enum(PRTASN1STRING pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser)
1764{
1765 RT_NOREF_PV(pThis); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(uDepth); RT_NOREF_PV(pvUser);
1766 Assert(pThis && (!RTAsn1String_IsPresent(pThis) || pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable));
1767
1768 /* No children to enumerate. */
1769 return VINF_SUCCESS;
1770}
1771
1772
1773RTDECL(int) RTAsn1String_Compare(PCRTASN1STRING pLeft, PCRTASN1STRING pRight)
1774{
1775 /* Compare tag and binary value. */
1776 return RTAsn1String_CompareEx(pLeft, pRight, true /*fTypeToo*/);
1777}
1778
1779
1780RTDECL(int) RTAsn1String_CheckSanity(PCRTASN1STRING pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
1781{
1782 RT_NOREF_PV(fFlags);
1783 if (RT_UNLIKELY(!RTAsn1String_IsPresent(pThis)))
1784 return RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Missing (STRING).", pszErrorTag);
1785 return rtAsn1String_CheckSanity(pThis, pErrInfo, pszErrorTag, NULL /*pcchUtf8*/);
1786}
1787
1788
1789/*
1790 * Generate code for the tag specific methods.
1791 * Note! This is very similar to what we're doing in asn1-ut-time.cpp.
1792 */
1793#define RTASN1STRING_IMPL(a_uTag, a_szTag, a_Api) \
1794 \
1795 RTDECL(int) RT_CONCAT(a_Api,_Init)(PRTASN1STRING pThis, PCRTASN1ALLOCATORVTABLE pAllocator) \
1796 { \
1797 return RTAsn1String_InitEx(pThis, a_uTag, NULL /*pvValue*/, 0 /*cbValue*/, pAllocator); \
1798 } \
1799 \
1800 RTDECL(int) RT_CONCAT(a_Api,_Clone)(PRTASN1STRING pThis, PCRTASN1STRING pSrc, PCRTASN1ALLOCATORVTABLE pAllocator) \
1801 { \
1802 AssertReturn(RTASN1CORE_GET_TAG(&pSrc->Asn1Core) == a_uTag || !RTAsn1String_IsPresent(pSrc), \
1803 VERR_ASN1_STRING_TAG_MISMATCH); \
1804 return RTAsn1String_Clone(pThis, pSrc, pAllocator); \
1805 } \
1806 \
1807 RTDECL(void) RT_CONCAT(a_Api,_Delete)(PRTASN1STRING pThis) \
1808 { \
1809 Assert( !pThis \
1810 || !RTAsn1String_IsPresent(pThis) \
1811 || ( pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable \
1812 && RTASN1CORE_GET_TAG(&pThis->Asn1Core) == a_uTag) ); \
1813 RTAsn1String_Delete(pThis); \
1814 } \
1815 \
1816 RTDECL(int) RT_CONCAT(a_Api,_Enum)(PRTASN1STRING pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser) \
1817 { \
1818 RT_NOREF_PV(pThis); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(uDepth); RT_NOREF_PV(pvUser); \
1819 Assert( pThis \
1820 && ( !RTAsn1String_IsPresent(pThis) \
1821 || ( pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable \
1822 && RTASN1CORE_GET_TAG(&pThis->Asn1Core) == a_uTag) ) ); \
1823 /* No children to enumerate. */ \
1824 return VINF_SUCCESS; \
1825 } \
1826 \
1827 RTDECL(int) RT_CONCAT(a_Api,_Compare)(PCRTASN1STRING pLeft, PCRTASN1STRING pRight) \
1828 { \
1829 int iDiff = RTAsn1String_CompareEx(pLeft, pRight, true /*fTypeToo*/); \
1830 if (!iDiff && RTASN1CORE_GET_TAG(&pLeft->Asn1Core) != a_uTag && RTAsn1String_IsPresent(pLeft)) \
1831 iDiff = RTASN1CORE_GET_TAG(&pLeft->Asn1Core) < a_uTag ? -1 : 1; \
1832 return iDiff; \
1833 } \
1834 \
1835 RTDECL(int) RT_CONCAT(a_Api,_CheckSanity)(PCRTASN1STRING pThis, uint32_t fFlags, \
1836 PRTERRINFO pErrInfo, const char *pszErrorTag) \
1837 { \
1838 if (RTASN1CORE_GET_TAG(&pThis->Asn1Core) != a_uTag && RTAsn1String_IsPresent(pThis)) \
1839 return RTErrInfoSetF(pErrInfo, VERR_ASN1_STRING_TAG_MISMATCH, "%s: uTag=%#x, expected %#x (%s)", \
1840 pszErrorTag, RTASN1CORE_GET_TAG(&pThis->Asn1Core), a_uTag, a_szTag); \
1841 return RTAsn1String_CheckSanity(pThis, fFlags, pErrInfo, pszErrorTag); \
1842 }
1843
1844#include "asn1-ut-string-template2.h"
1845
1846
1847/*
1848 * Generate code for the associated collection types.
1849 */
1850#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-string-template.h"
1851#include <iprt/asn1-generator-internal-header.h>
1852#include <iprt/asn1-generator-core.h>
1853#include <iprt/asn1-generator-init.h>
1854#include <iprt/asn1-generator-sanity.h>
1855
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette