2 **********************************************************************
3 * Copyright (C) 2002-2004, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 **********************************************************************
8 * tab size: 8 (not used)
11 * created on: 2002jul01
12 * created by: Markus W. Scherer
14 * UTF-7 converter implementation. Used to be in ucnv_utf.c.
17 #include "unicode/utypes.h"
19 #if !UCONFIG_NO_CONVERSION
21 #include "unicode/ucnv.h"
25 /* UTF-7 -------------------------------------------------------------------- */
28 * UTF-7 is a stateful encoding of Unicode.
29 * It is defined in RFC 2152. (http://www.ietf.org/rfc/rfc2152.txt)
30 * It was intended for use in Internet email systems, using in its bytewise
31 * encoding only a subset of 7-bit US-ASCII.
32 * UTF-7 is deprecated in favor of UTF-8/16/32 and SCSU, but still
35 * For converting Unicode to UTF-7, the RFC allows to encode some US-ASCII
36 * characters directly or in base64. Especially, the characters in set O
37 * as defined in the RFC (see below) may be encoded directly but are not
38 * allowed in, e.g., email headers.
39 * By default, the ICU UTF-7 converter encodes set O directly.
40 * By choosing the option "version=1", set O will be escaped instead.
42 * utf7Converter=ucnv_open("UTF-7,version=1");
44 * For details about email headers see RFC 2047.
48 * Tests for US-ASCII characters belonging to character classes
51 * Set D (directly encoded characters) consists of the following
52 * characters: the upper and lower case letters A through Z
53 * and a through z, the 10 digits 0-9, and the following nine special
54 * characters (note that "+" and "=" are omitted):
57 * Set O (optional direct characters) consists of the following
58 * characters (note that "\" and "~" are omitted):
59 * !"#$%&*;<=>@[]^_`{|}
61 * According to the rules in RFC 2152, the byte values for the following
62 * US-ASCII characters are not used in UTF-7 and are therefore illegal:
63 * - all C0 control codes except for CR LF TAB
67 * - all codes beyond US-ASCII, i.e. all >127
70 ((uint8_t)((c)-97)<26 || (uint8_t)((c)-65)<26 || /* letters */ \
71 (uint8_t)((c)-48)<10 || /* digits */ \
72 (uint8_t)((c)-39)<3 || /* '() */ \
73 (uint8_t)((c)-44)<4 || /* ,-./ */ \
74 (c)==58 || (c)==63 /* :? */ \
78 ((uint8_t)((c)-33)<6 || /* !"#$%& */ \
79 (uint8_t)((c)-59)<4 || /* ;<=> */ \
80 (uint8_t)((c)-93)<4 || /* ]^_` */ \
81 (uint8_t)((c)-123)<3 || /* {|} */ \
82 (c)==42 || (c)==64 || (c)==91 /* *@[ */ \
85 #define isCRLFTAB(c) ((c)==13 || (c)==10 || (c)==9)
86 #define isCRLFSPTAB(c) ((c)==32 || (c)==13 || (c)==10 || (c)==9)
93 /* legal byte values: all US-ASCII graphic characters from space to before tilde, and CR LF TAB */
94 #define isLegalUTF7(c) (((uint8_t)((c)-32)<94 && (c)!=BACKSLASH) || isCRLFTAB(c))
96 /* encode directly sets D and O and CR LF SP TAB */
97 static const UBool encodeDirectlyMaximum
[128]={
98 /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
99 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
100 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
102 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
103 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
105 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
106 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
108 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
109 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
112 /* encode directly set D and CR LF SP TAB but not set O */
113 static const UBool encodeDirectlyRestricted
[128]={
114 /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
115 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
116 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1,
119 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
121 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
122 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
124 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
125 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
131 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
132 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
134 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
135 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
137 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
144 /* C0 controls, -1 for legal ones (CR LF TAB), -3 for illegal ones */
145 -3, -3, -3, -3, -3, -3, -3, -3, -3, -1, -1, -3, -3, -1, -3, -3,
146 -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
148 /* general punctuation with + and / and a special value (-2) for - */
149 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -2, -1, 63,
151 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
154 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
155 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -3, -1, -1, -1,
158 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
159 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -3, -3
163 * converter status values:
166 * 24 inDirectMode (boolean)
167 * 23..16 base64Counter (-1..7)
168 * 15..0 bits (up to 14 bits incoming base64)
171 * 31..28 version (0: set O direct 1: set O escaped)
172 * 24 inDirectMode (boolean)
173 * 23..16 base64Counter (0..2)
174 * 7..0 bits (6 bits outgoing base64)
179 _UTF7Reset(UConverter
*cnv
, UConverterResetChoice choice
) {
180 if(choice
<=UCNV_RESET_TO_UNICODE
) {
181 /* reset toUnicode */
182 cnv
->toUnicodeStatus
=0x1000000; /* inDirectMode=TRUE */
185 if(choice
!=UCNV_RESET_TO_UNICODE
) {
186 /* reset fromUnicode */
187 cnv
->fromUnicodeStatus
=(cnv
->fromUnicodeStatus
&0xf0000000)|0x1000000; /* keep version, inDirectMode=TRUE */
192 _UTF7Open(UConverter
*cnv
,
196 UErrorCode
*pErrorCode
) {
197 if((options
&0xf)<=1) {
198 cnv
->fromUnicodeStatus
=(options
&0xf)<<28;
199 _UTF7Reset(cnv
, UCNV_RESET_BOTH
);
201 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
206 _UTF7ToUnicodeWithOffsets(UConverterToUnicodeArgs
*pArgs
,
207 UErrorCode
*pErrorCode
) {
209 const uint8_t *source
, *sourceLimit
;
211 const UChar
*targetLimit
;
217 int32_t length
, targetCapacity
;
221 int8_t base64Counter
;
226 int32_t sourceIndex
, nextSourceIndex
;
229 /* set up the local pointers */
230 cnv
=pArgs
->converter
;
232 source
=(const uint8_t *)pArgs
->source
;
233 sourceLimit
=(const uint8_t *)pArgs
->sourceLimit
;
234 target
=pArgs
->target
;
235 targetLimit
=pArgs
->targetLimit
;
236 offsets
=pArgs
->offsets
;
237 /* get the state machine state */
239 uint32_t status
=cnv
->toUnicodeStatus
;
240 inDirectMode
=(UBool
)((status
>>24)&1);
241 base64Counter
=(int8_t)(status
>>16);
242 bits
=(uint16_t)status
;
245 byteIndex
=cnv
->toULength
;
247 /* sourceIndex=-1 if the current character began in the previous buffer */
248 sourceIndex
=byteIndex
==0 ? 0 : -1;
254 * In Direct Mode, most US-ASCII characters are encoded directly, i.e.,
255 * with their US-ASCII byte values.
256 * Backslash and Tilde and most control characters are not allowed in UTF-7.
257 * A plus sign starts Unicode (or "escape") Mode.
259 * In Direct Mode, only the sourceIndex is used.
262 length
=sourceLimit
-source
;
263 targetCapacity
=targetLimit
-target
;
264 if(length
>targetCapacity
) {
265 length
=targetCapacity
;
269 if(!isLegalUTF7(b
)) {
273 *pErrorCode
=U_ILLEGAL_CHAR_FOUND
;
276 /* write directly encoded character */
279 *offsets
++=sourceIndex
++;
282 /* switch to Unicode mode */
283 nextSourceIndex
=++sourceIndex
;
292 if(source
<sourceLimit
&& target
>=targetLimit
) {
294 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
299 * In Unicode (or "escape") Mode, UTF-16BE is base64-encoded.
300 * The base64 sequence ends with any character that is not in the base64 alphabet.
301 * A terminating minus sign is consumed.
303 * In Unicode Mode, the sourceIndex has the index to the start of the current
304 * base64 bytes, while nextSourceIndex is precisely parallel to source,
305 * keeping the index to the following byte.
306 * Note that in 2 out of 3 cases, UChars overlap within a base64 byte.
308 while(source
<sourceLimit
) {
309 if(target
<targetLimit
) {
310 bytes
[byteIndex
++]=b
=*source
++;
313 /* illegal - test other illegal US-ASCII values by base64Value==-3 */
315 *pErrorCode
=U_ILLEGAL_CHAR_FOUND
;
317 } else if((base64Value
=fromBase64
[b
])>=0) {
318 /* collect base64 bytes into UChars */
319 switch(base64Counter
) {
320 case -1: /* -1 is immediately after the + */
329 bits
=(uint16_t)((bits
<<6)|base64Value
);
333 *target
++=(UChar
)((bits
<<4)|(base64Value
>>2));
335 *offsets
++=sourceIndex
;
336 sourceIndex
=nextSourceIndex
-1;
338 bytes
[0]=b
; /* keep this byte in case an error occurs */
340 bits
=(uint16_t)(base64Value
&3);
344 *target
++=(UChar
)((bits
<<2)|(base64Value
>>4));
346 *offsets
++=sourceIndex
;
347 sourceIndex
=nextSourceIndex
-1;
349 bytes
[0]=b
; /* keep this byte in case an error occurs */
351 bits
=(uint16_t)(base64Value
&15);
355 *target
++=(UChar
)((bits
<<6)|base64Value
);
357 *offsets
++=sourceIndex
;
358 sourceIndex
=nextSourceIndex
;
365 /* will never occur */
368 } else if(base64Value
==-2) {
369 /* minus sign terminates the base64 sequence */
371 if(base64Counter
==-1) {
372 /* +- i.e. a minus immediately following a plus */
375 *offsets
++=sourceIndex
-1;
378 /* absorb the minus and leave the Unicode Mode */
380 /* bits are illegally left over, a UChar is incomplete */
381 *pErrorCode
=U_ILLEGAL_CHAR_FOUND
;
385 sourceIndex
=nextSourceIndex
;
387 } else if(base64Value
==-1) /* for any legal character except base64 and minus sign */ {
388 /* leave the Unicode Mode */
390 if(base64Counter
==-1) {
391 /* illegal: + immediately followed by something other than base64 or minus sign */
392 /* include the plus sign in the reported sequence */
397 *pErrorCode
=U_ILLEGAL_CHAR_FOUND
;
400 /* un-read the character in case it is a plus sign */
402 sourceIndex
=nextSourceIndex
-1;
405 /* bits are illegally left over, a UChar is incomplete */
406 *pErrorCode
=U_ILLEGAL_CHAR_FOUND
;
409 } else /* base64Value==-3 for illegal characters */ {
412 *pErrorCode
=U_ILLEGAL_CHAR_FOUND
;
417 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
423 if(U_SUCCESS(*pErrorCode
) && pArgs
->flush
&& source
==sourceLimit
&& bits
==0) {
425 * if we are in Unicode mode, then the byteIndex might not be 0,
426 * but that is ok if bits==0
427 * -> we set byteIndex=0 at the end of the stream to avoid a truncated error
428 * (not true for IMAP-mailbox-name where we must end in direct mode)
433 /* set the converter state back into UConverter */
434 cnv
->toUnicodeStatus
=((uint32_t)inDirectMode
<<24)|((uint32_t)((uint8_t)base64Counter
)<<16)|(uint32_t)bits
;
435 cnv
->toULength
=byteIndex
;
437 /* write back the updated pointers */
438 pArgs
->source
=(const char *)source
;
439 pArgs
->target
=target
;
440 pArgs
->offsets
=offsets
;
445 _UTF7FromUnicodeWithOffsets(UConverterFromUnicodeArgs
*pArgs
,
446 UErrorCode
*pErrorCode
) {
448 const UChar
*source
, *sourceLimit
;
449 uint8_t *target
, *targetLimit
;
452 int32_t length
, targetCapacity
, sourceIndex
;
456 const UBool
*encodeDirectly
;
458 int8_t base64Counter
;
461 /* set up the local pointers */
462 cnv
=pArgs
->converter
;
464 /* set up the local pointers */
465 source
=pArgs
->source
;
466 sourceLimit
=pArgs
->sourceLimit
;
467 target
=(uint8_t *)pArgs
->target
;
468 targetLimit
=(uint8_t *)pArgs
->targetLimit
;
469 offsets
=pArgs
->offsets
;
471 /* get the state machine state */
473 uint32_t status
=cnv
->fromUnicodeStatus
;
474 encodeDirectly
= status
<0x10000000 ? encodeDirectlyMaximum
: encodeDirectlyRestricted
;
475 inDirectMode
=(UBool
)((status
>>24)&1);
476 base64Counter
=(int8_t)(status
>>16);
477 bits
=(uint8_t)status
;
480 /* UTF-7 always encodes UTF-16 code units, therefore we need only a simple sourceIndex */
485 length
=sourceLimit
-source
;
486 targetCapacity
=targetLimit
-target
;
487 if(length
>targetCapacity
) {
488 length
=targetCapacity
;
492 /* currently always encode CR LF SP TAB directly */
493 if(c
<=127 && encodeDirectly
[c
]) {
494 /* encode directly */
495 *target
++=(uint8_t)c
;
497 *offsets
++=sourceIndex
++;
500 /* output +- for + */
502 if(target
<targetLimit
) {
505 *offsets
++=sourceIndex
;
506 *offsets
++=sourceIndex
++;
508 /* realign length and targetCapacity */
512 *offsets
++=sourceIndex
++;
514 cnv
->charErrorBuffer
[0]=MINUS
;
515 cnv
->charErrorBufferLength
=1;
516 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
520 /* un-read this character and switch to Unicode Mode */
524 *offsets
++=sourceIndex
;
532 if(source
<sourceLimit
&& target
>=targetLimit
) {
534 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
538 while(source
<sourceLimit
) {
539 if(target
<targetLimit
) {
541 if(c
<=127 && encodeDirectly
[c
]) {
542 /* encode directly */
545 /* trick: back out this character to make this easier */
548 /* terminate the base64 sequence */
549 if(base64Counter
!=0) {
550 /* write remaining bits for the previous character */
551 *target
++=toBase64
[bits
];
553 *offsets
++=sourceIndex
-1;
556 if(fromBase64
[c
]!=-1) {
557 /* need to terminate with a minus */
558 if(target
<targetLimit
) {
561 *offsets
++=sourceIndex
-1;
564 cnv
->charErrorBuffer
[0]=MINUS
;
565 cnv
->charErrorBufferLength
=1;
566 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
573 * base64 this character:
574 * Output 2 or 3 base64 bytes for the remaining bits of the previous character
575 * and the bits of this character, each implicitly in UTF-16BE.
577 * Here, bits is an 8-bit variable because only 6 bits need to be kept from one
578 * character to the next. The actual 2 or 4 bits are shifted to the left edge
579 * of the 6-bits field 5..0 to make the termination of the base64 sequence easier.
581 switch(base64Counter
) {
583 *target
++=toBase64
[c
>>10];
584 if(target
<targetLimit
) {
585 *target
++=toBase64
[(c
>>4)&0x3f];
587 *offsets
++=sourceIndex
;
588 *offsets
++=sourceIndex
++;
592 *offsets
++=sourceIndex
++;
594 cnv
->charErrorBuffer
[0]=toBase64
[(c
>>4)&0x3f];
595 cnv
->charErrorBufferLength
=1;
596 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
598 bits
=(uint8_t)((c
&15)<<2);
602 *target
++=toBase64
[bits
|(c
>>14)];
603 if(target
<targetLimit
) {
604 *target
++=toBase64
[(c
>>8)&0x3f];
605 if(target
<targetLimit
) {
606 *target
++=toBase64
[(c
>>2)&0x3f];
608 *offsets
++=sourceIndex
;
609 *offsets
++=sourceIndex
;
610 *offsets
++=sourceIndex
++;
614 *offsets
++=sourceIndex
;
615 *offsets
++=sourceIndex
++;
617 cnv
->charErrorBuffer
[0]=toBase64
[(c
>>2)&0x3f];
618 cnv
->charErrorBufferLength
=1;
619 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
623 *offsets
++=sourceIndex
++;
625 cnv
->charErrorBuffer
[0]=toBase64
[(c
>>8)&0x3f];
626 cnv
->charErrorBuffer
[1]=toBase64
[(c
>>2)&0x3f];
627 cnv
->charErrorBufferLength
=2;
628 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
630 bits
=(uint8_t)((c
&3)<<4);
634 *target
++=toBase64
[bits
|(c
>>12)];
635 if(target
<targetLimit
) {
636 *target
++=toBase64
[(c
>>6)&0x3f];
637 if(target
<targetLimit
) {
638 *target
++=toBase64
[c
&0x3f];
640 *offsets
++=sourceIndex
;
641 *offsets
++=sourceIndex
;
642 *offsets
++=sourceIndex
++;
646 *offsets
++=sourceIndex
;
647 *offsets
++=sourceIndex
++;
649 cnv
->charErrorBuffer
[0]=toBase64
[c
&0x3f];
650 cnv
->charErrorBufferLength
=1;
651 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
655 *offsets
++=sourceIndex
++;
657 cnv
->charErrorBuffer
[0]=toBase64
[(c
>>6)&0x3f];
658 cnv
->charErrorBuffer
[1]=toBase64
[c
&0x3f];
659 cnv
->charErrorBufferLength
=2;
660 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
666 /* will never occur */
672 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
678 if(pArgs
->flush
&& source
>=sourceLimit
) {
679 /* flush remaining bits to the target */
680 if(!inDirectMode
&& base64Counter
!=0) {
681 if(target
<targetLimit
) {
682 *target
++=toBase64
[bits
];
684 *offsets
++=sourceIndex
-1;
687 cnv
->charErrorBuffer
[cnv
->charErrorBufferLength
++]=toBase64
[bits
];
688 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
691 /* reset the state for the next conversion */
692 cnv
->fromUnicodeStatus
=(cnv
->fromUnicodeStatus
&0xf0000000)|0x1000000; /* keep version, inDirectMode=TRUE */
694 /* set the converter state back into UConverter */
695 cnv
->fromUnicodeStatus
=
696 (cnv
->fromUnicodeStatus
&0xf0000000)| /* keep version*/
697 ((uint32_t)inDirectMode
<<24)|((uint32_t)base64Counter
<<16)|(uint32_t)bits
;
700 /* write back the updated pointers */
701 pArgs
->source
=source
;
702 pArgs
->target
=(char *)target
;
703 pArgs
->offsets
=offsets
;
708 _UTF7GetName(const UConverter
*cnv
) {
709 switch(cnv
->fromUnicodeStatus
>>28) {
711 return "UTF-7,version=1";
717 static const UConverterImpl _UTF7Impl
={
727 _UTF7ToUnicodeWithOffsets
,
728 _UTF7ToUnicodeWithOffsets
,
729 _UTF7FromUnicodeWithOffsets
,
730 _UTF7FromUnicodeWithOffsets
,
735 NULL
, /* we don't need writeSub() because we never call a callback at fromUnicode() */
737 ucnv_getCompleteUnicodeSet
740 static const UConverterStaticData _UTF7StaticData
={
741 sizeof(UConverterStaticData
),
743 0, /* TODO CCSID for UTF-7 */
746 { 0x3f, 0, 0, 0 }, 1, /* the subchar is not used */
750 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */
753 const UConverterSharedData _UTF7Data
={
754 sizeof(UConverterSharedData
), ~((uint32_t)0),
755 NULL
, NULL
, &_UTF7StaticData
, FALSE
, &_UTF7Impl
,
759 /* IMAP mailbox name encoding ----------------------------------------------- */
762 * RFC 2060: INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1
763 * http://www.ietf.org/rfc/rfc2060.txt
765 * 5.1.3. Mailbox International Naming Convention
767 * By convention, international mailbox names are specified using a
768 * modified version of the UTF-7 encoding described in [UTF-7]. The
769 * purpose of these modifications is to correct the following problems
772 * 1) UTF-7 uses the "+" character for shifting; this conflicts with
773 * the common use of "+" in mailbox names, in particular USENET
776 * 2) UTF-7's encoding is BASE64 which uses the "/" character; this
777 * conflicts with the use of "/" as a popular hierarchy delimiter.
779 * 3) UTF-7 prohibits the unencoded usage of "\"; this conflicts with
780 * the use of "\" as a popular hierarchy delimiter.
782 * 4) UTF-7 prohibits the unencoded usage of "~"; this conflicts with
783 * the use of "~" in some servers as a home directory indicator.
785 * 5) UTF-7 permits multiple alternate forms to represent the same
786 * string; in particular, printable US-ASCII chararacters can be
787 * represented in encoded form.
789 * In modified UTF-7, printable US-ASCII characters except for "&"
790 * represent themselves; that is, characters with octet values 0x20-0x25
791 * and 0x27-0x7e. The character "&" (0x26) is represented by the two-
792 * octet sequence "&-".
794 * All other characters (octet values 0x00-0x1f, 0x7f-0xff, and all
795 * Unicode 16-bit octets) are represented in modified BASE64, with a
796 * further modification from [UTF-7] that "," is used instead of "/".
797 * Modified BASE64 MUST NOT be used to represent any printing US-ASCII
798 * character which can represent itself.
800 * "&" is used to shift to modified BASE64 and "-" to shift back to US-
801 * ASCII. All names start in US-ASCII, and MUST end in US-ASCII (that
802 * is, a name that ends with a Unicode 16-bit octet MUST end with a "-
805 * For example, here is a mailbox name which mixes English, Japanese,
806 * and Chinese text: ~peter/mail/&ZeVnLIqe-/&U,BTFw-
810 * Tests for US-ASCII characters belonging to character classes
813 * Set D (directly encoded characters) consists of the following
814 * characters: the upper and lower case letters A through Z
815 * and a through z, the 10 digits 0-9, and the following nine special
816 * characters (note that "+" and "=" are omitted):
819 * Set O (optional direct characters) consists of the following
820 * characters (note that "\" and "~" are omitted):
821 * !"#$%&*;<=>@[]^_`{|}
823 * According to the rules in RFC 2152, the byte values for the following
824 * US-ASCII characters are not used in UTF-7 and are therefore illegal:
825 * - all C0 control codes except for CR LF TAB
829 * - all codes beyond US-ASCII, i.e. all >127
832 /* uses '&' not '+' to start a base64 sequence */
833 #define AMPERSAND 0x26
837 /* legal byte values: all US-ASCII graphic characters 0x20..0x7e */
838 #define isLegalIMAP(c) (0x20<=(c) && (c)<=0x7e)
840 /* direct-encode all of printable ASCII 0x20..0x7e except '&' 0x26 */
841 #define inSetDIMAP(c) (isLegalIMAP(c) && c!=AMPERSAND)
843 #define TO_BASE64_IMAP(n) ((n)<63 ? toBase64[n] : COMMA)
844 #define FROM_BASE64_IMAP(c) ((c)==COMMA ? 63 : (c)==SLASH ? -1 : fromBase64[c])
847 * converter status values:
850 * 24 inDirectMode (boolean)
851 * 23..16 base64Counter (-1..7)
852 * 15..0 bits (up to 14 bits incoming base64)
855 * 24 inDirectMode (boolean)
856 * 23..16 base64Counter (0..2)
857 * 7..0 bits (6 bits outgoing base64)
863 _IMAPToUnicodeWithOffsets(UConverterToUnicodeArgs
*pArgs
,
864 UErrorCode
*pErrorCode
) {
866 const uint8_t *source
, *sourceLimit
;
868 const UChar
*targetLimit
;
874 int32_t length
, targetCapacity
;
878 int8_t base64Counter
;
883 int32_t sourceIndex
, nextSourceIndex
;
888 /* set up the local pointers */
889 cnv
=pArgs
->converter
;
891 source
=(const uint8_t *)pArgs
->source
;
892 sourceLimit
=(const uint8_t *)pArgs
->sourceLimit
;
893 target
=pArgs
->target
;
894 targetLimit
=pArgs
->targetLimit
;
895 offsets
=pArgs
->offsets
;
896 /* get the state machine state */
898 uint32_t status
=cnv
->toUnicodeStatus
;
899 inDirectMode
=(UBool
)((status
>>24)&1);
900 base64Counter
=(int8_t)(status
>>16);
901 bits
=(uint16_t)status
;
904 byteIndex
=cnv
->toULength
;
906 /* sourceIndex=-1 if the current character began in the previous buffer */
907 sourceIndex
=byteIndex
==0 ? 0 : -1;
913 * In Direct Mode, US-ASCII characters are encoded directly, i.e.,
914 * with their US-ASCII byte values.
915 * An ampersand starts Unicode (or "escape") Mode.
917 * In Direct Mode, only the sourceIndex is used.
920 length
=sourceLimit
-source
;
921 targetCapacity
=targetLimit
-target
;
922 if(length
>targetCapacity
) {
923 length
=targetCapacity
;
927 if(!isLegalIMAP(b
)) {
931 *pErrorCode
=U_ILLEGAL_CHAR_FOUND
;
933 } else if(b
!=AMPERSAND
) {
934 /* write directly encoded character */
937 *offsets
++=sourceIndex
++;
939 } else /* AMPERSAND */ {
940 /* switch to Unicode mode */
941 nextSourceIndex
=++sourceIndex
;
950 if(source
<sourceLimit
&& target
>=targetLimit
) {
952 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
957 * In Unicode (or "escape") Mode, UTF-16BE is base64-encoded.
958 * The base64 sequence ends with any character that is not in the base64 alphabet.
959 * A terminating minus sign is consumed.
960 * US-ASCII must not be base64-ed.
962 * In Unicode Mode, the sourceIndex has the index to the start of the current
963 * base64 bytes, while nextSourceIndex is precisely parallel to source,
964 * keeping the index to the following byte.
965 * Note that in 2 out of 3 cases, UChars overlap within a base64 byte.
967 while(source
<sourceLimit
) {
968 if(target
<targetLimit
) {
969 bytes
[byteIndex
++]=b
=*source
++;
972 /* illegal - test other illegal US-ASCII values by base64Value==-3 */
974 *pErrorCode
=U_ILLEGAL_CHAR_FOUND
;
976 } else if((base64Value
=FROM_BASE64_IMAP(b
))>=0) {
977 /* collect base64 bytes into UChars */
978 switch(base64Counter
) {
979 case -1: /* -1 is immediately after the & */
988 bits
=(uint16_t)((bits
<<6)|base64Value
);
992 c
=(UChar
)((bits
<<4)|(base64Value
>>2));
996 *pErrorCode
=U_ILLEGAL_CHAR_FOUND
;
1001 *offsets
++=sourceIndex
;
1002 sourceIndex
=nextSourceIndex
-1;
1004 bytes
[0]=b
; /* keep this byte in case an error occurs */
1006 bits
=(uint16_t)(base64Value
&3);
1010 c
=(UChar
)((bits
<<2)|(base64Value
>>4));
1011 if(isLegalIMAP(c
)) {
1014 *pErrorCode
=U_ILLEGAL_CHAR_FOUND
;
1019 *offsets
++=sourceIndex
;
1020 sourceIndex
=nextSourceIndex
-1;
1022 bytes
[0]=b
; /* keep this byte in case an error occurs */
1024 bits
=(uint16_t)(base64Value
&15);
1028 c
=(UChar
)((bits
<<6)|base64Value
);
1029 if(isLegalIMAP(c
)) {
1032 *pErrorCode
=U_ILLEGAL_CHAR_FOUND
;
1037 *offsets
++=sourceIndex
;
1038 sourceIndex
=nextSourceIndex
;
1045 /* will never occur */
1048 } else if(base64Value
==-2) {
1049 /* minus sign terminates the base64 sequence */
1051 if(base64Counter
==-1) {
1052 /* &- i.e. a minus immediately following an ampersand */
1053 *target
++=AMPERSAND
;
1055 *offsets
++=sourceIndex
-1;
1058 /* absorb the minus and leave the Unicode Mode */
1059 if(bits
!=0 || (base64Counter
!=0 && base64Counter
!=3 && base64Counter
!=6)) {
1060 /* bits are illegally left over, a UChar is incomplete */
1061 /* base64Counter other than 0, 3, 6 means non-minimal zero-padding, also illegal */
1062 *pErrorCode
=U_ILLEGAL_CHAR_FOUND
;
1066 sourceIndex
=nextSourceIndex
;
1069 if(base64Counter
==-1) {
1070 /* illegal: & immediately followed by something other than base64 or minus sign */
1071 /* include the ampersand in the reported sequence */
1077 /* base64Value==-1 for characters that are illegal only in Unicode mode */
1078 /* base64Value==-3 for illegal characters */
1081 *pErrorCode
=U_ILLEGAL_CHAR_FOUND
;
1085 /* target is full */
1086 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
1094 * the end of the input stream and detection of truncated input
1095 * are handled by the framework, but here we must check if we are in Unicode
1096 * mode and byteIndex==0 because we must end in direct mode
1100 * in Unicode mode and byteIndex==0
1101 * end of input and no truncated input
1103 if( U_SUCCESS(*pErrorCode
) &&
1104 !inDirectMode
&& byteIndex
==0 &&
1105 pArgs
->flush
&& source
>=sourceLimit
1107 if(base64Counter
==-1) {
1108 /* & at the very end of the input */
1109 /* make the ampersand the reported sequence */
1113 /* else if(base64Counter!=-1) byteIndex remains 0 because there is no particular byte sequence */
1115 inDirectMode
=TRUE
; /* avoid looping */
1116 *pErrorCode
=U_TRUNCATED_CHAR_FOUND
;
1119 /* set the converter state back into UConverter */
1120 cnv
->toUnicodeStatus
=((uint32_t)inDirectMode
<<24)|((uint32_t)((uint8_t)base64Counter
)<<16)|(uint32_t)bits
;
1121 cnv
->toULength
=byteIndex
;
1123 /* write back the updated pointers */
1124 pArgs
->source
=(const char *)source
;
1125 pArgs
->target
=target
;
1126 pArgs
->offsets
=offsets
;
1131 _IMAPFromUnicodeWithOffsets(UConverterFromUnicodeArgs
*pArgs
,
1132 UErrorCode
*pErrorCode
) {
1134 const UChar
*source
, *sourceLimit
;
1135 uint8_t *target
, *targetLimit
;
1138 int32_t length
, targetCapacity
, sourceIndex
;
1144 int8_t base64Counter
;
1147 /* set up the local pointers */
1148 cnv
=pArgs
->converter
;
1150 /* set up the local pointers */
1151 source
=pArgs
->source
;
1152 sourceLimit
=pArgs
->sourceLimit
;
1153 target
=(uint8_t *)pArgs
->target
;
1154 targetLimit
=(uint8_t *)pArgs
->targetLimit
;
1155 offsets
=pArgs
->offsets
;
1157 /* get the state machine state */
1159 uint32_t status
=cnv
->fromUnicodeStatus
;
1160 inDirectMode
=(UBool
)((status
>>24)&1);
1161 base64Counter
=(int8_t)(status
>>16);
1162 bits
=(uint8_t)status
;
1165 /* UTF-7 always encodes UTF-16 code units, therefore we need only a simple sourceIndex */
1170 length
=sourceLimit
-source
;
1171 targetCapacity
=targetLimit
-target
;
1172 if(length
>targetCapacity
) {
1173 length
=targetCapacity
;
1177 /* encode 0x20..0x7e except '&' directly */
1179 /* encode directly */
1180 *target
++=(uint8_t)c
;
1182 *offsets
++=sourceIndex
++;
1184 } else if(c
==AMPERSAND
) {
1185 /* output &- for & */
1186 *target
++=AMPERSAND
;
1187 if(target
<targetLimit
) {
1190 *offsets
++=sourceIndex
;
1191 *offsets
++=sourceIndex
++;
1193 /* realign length and targetCapacity */
1197 *offsets
++=sourceIndex
++;
1199 cnv
->charErrorBuffer
[0]=MINUS
;
1200 cnv
->charErrorBufferLength
=1;
1201 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
1205 /* un-read this character and switch to Unicode Mode */
1207 *target
++=AMPERSAND
;
1209 *offsets
++=sourceIndex
;
1217 if(source
<sourceLimit
&& target
>=targetLimit
) {
1218 /* target is full */
1219 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
1223 while(source
<sourceLimit
) {
1224 if(target
<targetLimit
) {
1226 if(isLegalIMAP(c
)) {
1227 /* encode directly */
1230 /* trick: back out this character to make this easier */
1233 /* terminate the base64 sequence */
1234 if(base64Counter
!=0) {
1235 /* write remaining bits for the previous character */
1236 *target
++=TO_BASE64_IMAP(bits
);
1238 *offsets
++=sourceIndex
-1;
1241 /* need to terminate with a minus */
1242 if(target
<targetLimit
) {
1245 *offsets
++=sourceIndex
-1;
1248 cnv
->charErrorBuffer
[0]=MINUS
;
1249 cnv
->charErrorBufferLength
=1;
1250 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
1256 * base64 this character:
1257 * Output 2 or 3 base64 bytes for the remaining bits of the previous character
1258 * and the bits of this character, each implicitly in UTF-16BE.
1260 * Here, bits is an 8-bit variable because only 6 bits need to be kept from one
1261 * character to the next. The actual 2 or 4 bits are shifted to the left edge
1262 * of the 6-bits field 5..0 to make the termination of the base64 sequence easier.
1264 switch(base64Counter
) {
1267 *target
++=TO_BASE64_IMAP(b
);
1268 if(target
<targetLimit
) {
1269 b
=(uint8_t)((c
>>4)&0x3f);
1270 *target
++=TO_BASE64_IMAP(b
);
1272 *offsets
++=sourceIndex
;
1273 *offsets
++=sourceIndex
++;
1277 *offsets
++=sourceIndex
++;
1279 b
=(uint8_t)((c
>>4)&0x3f);
1280 cnv
->charErrorBuffer
[0]=TO_BASE64_IMAP(b
);
1281 cnv
->charErrorBufferLength
=1;
1282 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
1284 bits
=(uint8_t)((c
&15)<<2);
1288 b
=(uint8_t)(bits
|(c
>>14));
1289 *target
++=TO_BASE64_IMAP(b
);
1290 if(target
<targetLimit
) {
1291 b
=(uint8_t)((c
>>8)&0x3f);
1292 *target
++=TO_BASE64_IMAP(b
);
1293 if(target
<targetLimit
) {
1294 b
=(uint8_t)((c
>>2)&0x3f);
1295 *target
++=TO_BASE64_IMAP(b
);
1297 *offsets
++=sourceIndex
;
1298 *offsets
++=sourceIndex
;
1299 *offsets
++=sourceIndex
++;
1303 *offsets
++=sourceIndex
;
1304 *offsets
++=sourceIndex
++;
1306 b
=(uint8_t)((c
>>2)&0x3f);
1307 cnv
->charErrorBuffer
[0]=TO_BASE64_IMAP(b
);
1308 cnv
->charErrorBufferLength
=1;
1309 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
1313 *offsets
++=sourceIndex
++;
1315 b
=(uint8_t)((c
>>8)&0x3f);
1316 cnv
->charErrorBuffer
[0]=TO_BASE64_IMAP(b
);
1317 b
=(uint8_t)((c
>>2)&0x3f);
1318 cnv
->charErrorBuffer
[1]=TO_BASE64_IMAP(b
);
1319 cnv
->charErrorBufferLength
=2;
1320 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
1322 bits
=(uint8_t)((c
&3)<<4);
1326 b
=(uint8_t)(bits
|(c
>>12));
1327 *target
++=TO_BASE64_IMAP(b
);
1328 if(target
<targetLimit
) {
1329 b
=(uint8_t)((c
>>6)&0x3f);
1330 *target
++=TO_BASE64_IMAP(b
);
1331 if(target
<targetLimit
) {
1332 b
=(uint8_t)(c
&0x3f);
1333 *target
++=TO_BASE64_IMAP(b
);
1335 *offsets
++=sourceIndex
;
1336 *offsets
++=sourceIndex
;
1337 *offsets
++=sourceIndex
++;
1341 *offsets
++=sourceIndex
;
1342 *offsets
++=sourceIndex
++;
1344 b
=(uint8_t)(c
&0x3f);
1345 cnv
->charErrorBuffer
[0]=TO_BASE64_IMAP(b
);
1346 cnv
->charErrorBufferLength
=1;
1347 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
1351 *offsets
++=sourceIndex
++;
1353 b
=(uint8_t)((c
>>6)&0x3f);
1354 cnv
->charErrorBuffer
[0]=TO_BASE64_IMAP(b
);
1355 b
=(uint8_t)(c
&0x3f);
1356 cnv
->charErrorBuffer
[1]=TO_BASE64_IMAP(b
);
1357 cnv
->charErrorBufferLength
=2;
1358 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
1364 /* will never occur */
1369 /* target is full */
1370 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
1376 if(pArgs
->flush
&& source
>=sourceLimit
) {
1377 /* flush remaining bits to the target */
1379 if(base64Counter
!=0) {
1380 if(target
<targetLimit
) {
1381 *target
++=TO_BASE64_IMAP(bits
);
1383 *offsets
++=sourceIndex
-1;
1386 cnv
->charErrorBuffer
[cnv
->charErrorBufferLength
++]=TO_BASE64_IMAP(bits
);
1387 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
1390 /* need to terminate with a minus */
1391 if(target
<targetLimit
) {
1394 *offsets
++=sourceIndex
-1;
1397 cnv
->charErrorBuffer
[cnv
->charErrorBufferLength
++]=MINUS
;
1398 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
1401 /* reset the state for the next conversion */
1402 cnv
->fromUnicodeStatus
=(cnv
->fromUnicodeStatus
&0xf0000000)|0x1000000; /* keep version, inDirectMode=TRUE */
1404 /* set the converter state back into UConverter */
1405 cnv
->fromUnicodeStatus
=
1406 (cnv
->fromUnicodeStatus
&0xf0000000)| /* keep version*/
1407 ((uint32_t)inDirectMode
<<24)|((uint32_t)base64Counter
<<16)|(uint32_t)bits
;
1410 /* write back the updated pointers */
1411 pArgs
->source
=source
;
1412 pArgs
->target
=(char *)target
;
1413 pArgs
->offsets
=offsets
;
1417 static const UConverterImpl _IMAPImpl
={
1427 _IMAPToUnicodeWithOffsets
,
1428 _IMAPToUnicodeWithOffsets
,
1429 _IMAPFromUnicodeWithOffsets
,
1430 _IMAPFromUnicodeWithOffsets
,
1435 NULL
, /* we don't need writeSub() because we never call a callback at fromUnicode() */
1437 ucnv_getCompleteUnicodeSet
1440 static const UConverterStaticData _IMAPStaticData
={
1441 sizeof(UConverterStaticData
),
1442 "IMAP-mailbox-name",
1443 0, /* TODO CCSID for IMAP-mailbox-name */
1444 UCNV_IBM
, UCNV_IMAP_MAILBOX
,
1446 { 0x3f, 0, 0, 0 }, 1, /* the subchar is not used */
1450 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */
1453 const UConverterSharedData _IMAPData
={
1454 sizeof(UConverterSharedData
), ~((uint32_t)0),
1455 NULL
, NULL
, &_IMAPStaticData
, FALSE
, &_IMAPImpl
,