xref: /aosp_15_r20/external/icu/icu4c/source/common/ucnv_u7.cpp (revision 0e209d3975ff4a8c132096b14b0e9364a753506e)
1*0e209d39SAndroid Build Coastguard Worker // © 2016 and later: Unicode, Inc. and others.
2*0e209d39SAndroid Build Coastguard Worker // License & terms of use: http://www.unicode.org/copyright.html
3*0e209d39SAndroid Build Coastguard Worker /*
4*0e209d39SAndroid Build Coastguard Worker **********************************************************************
5*0e209d39SAndroid Build Coastguard Worker *   Copyright (C) 2002-2016, International Business Machines
6*0e209d39SAndroid Build Coastguard Worker *   Corporation and others.  All Rights Reserved.
7*0e209d39SAndroid Build Coastguard Worker **********************************************************************
8*0e209d39SAndroid Build Coastguard Worker *   file name:  ucnv_u7.c
9*0e209d39SAndroid Build Coastguard Worker *   encoding:   UTF-8
10*0e209d39SAndroid Build Coastguard Worker *   tab size:   8 (not used)
11*0e209d39SAndroid Build Coastguard Worker *   indentation:4
12*0e209d39SAndroid Build Coastguard Worker *
13*0e209d39SAndroid Build Coastguard Worker *   created on: 2002jul01
14*0e209d39SAndroid Build Coastguard Worker *   created by: Markus W. Scherer
15*0e209d39SAndroid Build Coastguard Worker *
16*0e209d39SAndroid Build Coastguard Worker *   UTF-7 converter implementation. Used to be in ucnv_utf.c.
17*0e209d39SAndroid Build Coastguard Worker */
18*0e209d39SAndroid Build Coastguard Worker 
19*0e209d39SAndroid Build Coastguard Worker #include "unicode/utypes.h"
20*0e209d39SAndroid Build Coastguard Worker 
21*0e209d39SAndroid Build Coastguard Worker #if !UCONFIG_NO_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION
22*0e209d39SAndroid Build Coastguard Worker 
23*0e209d39SAndroid Build Coastguard Worker #include "cmemory.h"
24*0e209d39SAndroid Build Coastguard Worker #include "unicode/ucnv.h"
25*0e209d39SAndroid Build Coastguard Worker #include "ucnv_bld.h"
26*0e209d39SAndroid Build Coastguard Worker #include "ucnv_cnv.h"
27*0e209d39SAndroid Build Coastguard Worker #include "uassert.h"
28*0e209d39SAndroid Build Coastguard Worker 
29*0e209d39SAndroid Build Coastguard Worker /* UTF-7 -------------------------------------------------------------------- */
30*0e209d39SAndroid Build Coastguard Worker 
31*0e209d39SAndroid Build Coastguard Worker /*
32*0e209d39SAndroid Build Coastguard Worker  * UTF-7 is a stateful encoding of Unicode.
33*0e209d39SAndroid Build Coastguard Worker  * It is defined in RFC 2152. (http://www.ietf.org/rfc/rfc2152.txt)
34*0e209d39SAndroid Build Coastguard Worker  * It was intended for use in Internet email systems, using in its bytewise
35*0e209d39SAndroid Build Coastguard Worker  * encoding only a subset of 7-bit US-ASCII.
36*0e209d39SAndroid Build Coastguard Worker  * UTF-7 is deprecated in favor of UTF-8/16/32 and SCSU, but still
37*0e209d39SAndroid Build Coastguard Worker  * occasionally used.
38*0e209d39SAndroid Build Coastguard Worker  *
39*0e209d39SAndroid Build Coastguard Worker  * For converting Unicode to UTF-7, the RFC allows to encode some US-ASCII
40*0e209d39SAndroid Build Coastguard Worker  * characters directly or in base64. Especially, the characters in set O
41*0e209d39SAndroid Build Coastguard Worker  * as defined in the RFC (see below) may be encoded directly but are not
42*0e209d39SAndroid Build Coastguard Worker  * allowed in, e.g., email headers.
43*0e209d39SAndroid Build Coastguard Worker  * By default, the ICU UTF-7 converter encodes set O directly.
44*0e209d39SAndroid Build Coastguard Worker  * By choosing the option "version=1", set O will be escaped instead.
45*0e209d39SAndroid Build Coastguard Worker  * For example:
46*0e209d39SAndroid Build Coastguard Worker  *     utf7Converter=ucnv_open("UTF-7,version=1");
47*0e209d39SAndroid Build Coastguard Worker  *
48*0e209d39SAndroid Build Coastguard Worker  * For details about email headers see RFC 2047.
49*0e209d39SAndroid Build Coastguard Worker  */
50*0e209d39SAndroid Build Coastguard Worker 
51*0e209d39SAndroid Build Coastguard Worker /*
52*0e209d39SAndroid Build Coastguard Worker  * Tests for US-ASCII characters belonging to character classes
53*0e209d39SAndroid Build Coastguard Worker  * defined in UTF-7.
54*0e209d39SAndroid Build Coastguard Worker  *
55*0e209d39SAndroid Build Coastguard Worker  * Set D (directly encoded characters) consists of the following
56*0e209d39SAndroid Build Coastguard Worker  * characters: the upper and lower case letters A through Z
57*0e209d39SAndroid Build Coastguard Worker  * and a through z, the 10 digits 0-9, and the following nine special
58*0e209d39SAndroid Build Coastguard Worker  * characters (note that "+" and "=" are omitted):
59*0e209d39SAndroid Build Coastguard Worker  *     '(),-./:?
60*0e209d39SAndroid Build Coastguard Worker  *
61*0e209d39SAndroid Build Coastguard Worker  * Set O (optional direct characters) consists of the following
62*0e209d39SAndroid Build Coastguard Worker  * characters (note that "\" and "~" are omitted):
63*0e209d39SAndroid Build Coastguard Worker  *     !"#$%&*;<=>@[]^_`{|}
64*0e209d39SAndroid Build Coastguard Worker  *
65*0e209d39SAndroid Build Coastguard Worker  * According to the rules in RFC 2152, the byte values for the following
66*0e209d39SAndroid Build Coastguard Worker  * US-ASCII characters are not used in UTF-7 and are therefore illegal:
67*0e209d39SAndroid Build Coastguard Worker  * - all C0 control codes except for CR LF TAB
68*0e209d39SAndroid Build Coastguard Worker  * - BACKSLASH
69*0e209d39SAndroid Build Coastguard Worker  * - TILDE
70*0e209d39SAndroid Build Coastguard Worker  * - DEL
71*0e209d39SAndroid Build Coastguard Worker  * - all codes beyond US-ASCII, i.e. all >127
72*0e209d39SAndroid Build Coastguard Worker  */
73*0e209d39SAndroid Build Coastguard Worker #define inSetD(c) \
74*0e209d39SAndroid Build Coastguard Worker     ((uint8_t)((c)-97)<26 || (uint8_t)((c)-65)<26 || /* letters */ \
75*0e209d39SAndroid Build Coastguard Worker      (uint8_t)((c)-48)<10 ||    /* digits */ \
76*0e209d39SAndroid Build Coastguard Worker      (uint8_t)((c)-39)<3 ||     /* '() */ \
77*0e209d39SAndroid Build Coastguard Worker      (uint8_t)((c)-44)<4 ||     /* ,-./ */ \
78*0e209d39SAndroid Build Coastguard Worker      (c)==58 || (c)==63         /* :? */ \
79*0e209d39SAndroid Build Coastguard Worker     )
80*0e209d39SAndroid Build Coastguard Worker 
81*0e209d39SAndroid Build Coastguard Worker #define inSetO(c) \
82*0e209d39SAndroid Build Coastguard Worker     ((uint8_t)((c)-33)<6 ||         /* !"#$%& */ \
83*0e209d39SAndroid Build Coastguard Worker      (uint8_t)((c)-59)<4 ||         /* ;<=> */ \
84*0e209d39SAndroid Build Coastguard Worker      (uint8_t)((c)-93)<4 ||         /* ]^_` */ \
85*0e209d39SAndroid Build Coastguard Worker      (uint8_t)((c)-123)<3 ||        /* {|} */ \
86*0e209d39SAndroid Build Coastguard Worker      (c)==42 || (c)==64 || (c)==91  /* *@[ */ \
87*0e209d39SAndroid Build Coastguard Worker     )
88*0e209d39SAndroid Build Coastguard Worker 
89*0e209d39SAndroid Build Coastguard Worker #define isCRLFTAB(c) ((c)==13 || (c)==10 || (c)==9)
90*0e209d39SAndroid Build Coastguard Worker #define isCRLFSPTAB(c) ((c)==32 || (c)==13 || (c)==10 || (c)==9)
91*0e209d39SAndroid Build Coastguard Worker 
92*0e209d39SAndroid Build Coastguard Worker #define PLUS  43
93*0e209d39SAndroid Build Coastguard Worker #define MINUS 45
94*0e209d39SAndroid Build Coastguard Worker #define BACKSLASH 92
95*0e209d39SAndroid Build Coastguard Worker #define TILDE 126
96*0e209d39SAndroid Build Coastguard Worker 
97*0e209d39SAndroid Build Coastguard Worker /* legal byte values: all US-ASCII graphic characters from space to before tilde, and CR LF TAB */
98*0e209d39SAndroid Build Coastguard Worker #define isLegalUTF7(c) (((uint8_t)((c)-32)<94 && (c)!=BACKSLASH) || isCRLFTAB(c))
99*0e209d39SAndroid Build Coastguard Worker 
100*0e209d39SAndroid Build Coastguard Worker /* encode directly sets D and O and CR LF SP TAB */
101*0e209d39SAndroid Build Coastguard Worker static const UBool encodeDirectlyMaximum[128]={
102*0e209d39SAndroid Build Coastguard Worker  /* 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f */
103*0e209d39SAndroid Build Coastguard Worker     0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
104*0e209d39SAndroid Build Coastguard Worker     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
105*0e209d39SAndroid Build Coastguard Worker 
106*0e209d39SAndroid Build Coastguard Worker     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
107*0e209d39SAndroid Build Coastguard Worker     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
108*0e209d39SAndroid Build Coastguard Worker 
109*0e209d39SAndroid Build Coastguard Worker     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
110*0e209d39SAndroid Build Coastguard Worker     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
111*0e209d39SAndroid Build Coastguard Worker 
112*0e209d39SAndroid Build Coastguard Worker     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
113*0e209d39SAndroid Build Coastguard Worker     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
114*0e209d39SAndroid Build Coastguard Worker };
115*0e209d39SAndroid Build Coastguard Worker 
116*0e209d39SAndroid Build Coastguard Worker /* encode directly set D and CR LF SP TAB but not set O */
117*0e209d39SAndroid Build Coastguard Worker static const UBool encodeDirectlyRestricted[128]={
118*0e209d39SAndroid Build Coastguard Worker  /* 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f */
119*0e209d39SAndroid Build Coastguard Worker     0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
120*0e209d39SAndroid Build Coastguard Worker     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121*0e209d39SAndroid Build Coastguard Worker 
122*0e209d39SAndroid Build Coastguard Worker     1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1,
123*0e209d39SAndroid Build Coastguard Worker     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
124*0e209d39SAndroid Build Coastguard Worker 
125*0e209d39SAndroid Build Coastguard Worker     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
126*0e209d39SAndroid Build Coastguard Worker     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
127*0e209d39SAndroid Build Coastguard Worker 
128*0e209d39SAndroid Build Coastguard Worker     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
129*0e209d39SAndroid Build Coastguard Worker     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
130*0e209d39SAndroid Build Coastguard Worker };
131*0e209d39SAndroid Build Coastguard Worker 
132*0e209d39SAndroid Build Coastguard Worker static const uint8_t
133*0e209d39SAndroid Build Coastguard Worker toBase64[64]={
134*0e209d39SAndroid Build Coastguard Worker     /* A-Z */
135*0e209d39SAndroid Build Coastguard Worker     65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
136*0e209d39SAndroid Build Coastguard Worker     78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
137*0e209d39SAndroid Build Coastguard Worker     /* a-z */
138*0e209d39SAndroid Build Coastguard Worker     97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
139*0e209d39SAndroid Build Coastguard Worker     110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
140*0e209d39SAndroid Build Coastguard Worker     /* 0-9 */
141*0e209d39SAndroid Build Coastguard Worker     48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
142*0e209d39SAndroid Build Coastguard Worker     /* +/ */
143*0e209d39SAndroid Build Coastguard Worker     43, 47
144*0e209d39SAndroid Build Coastguard Worker };
145*0e209d39SAndroid Build Coastguard Worker 
146*0e209d39SAndroid Build Coastguard Worker static const int8_t
147*0e209d39SAndroid Build Coastguard Worker fromBase64[128]={
148*0e209d39SAndroid Build Coastguard Worker     /* C0 controls, -1 for legal ones (CR LF TAB), -3 for illegal ones */
149*0e209d39SAndroid Build Coastguard Worker     -3, -3, -3, -3, -3, -3, -3, -3, -3, -1, -1, -3, -3, -1, -3, -3,
150*0e209d39SAndroid Build Coastguard Worker     -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
151*0e209d39SAndroid Build Coastguard Worker 
152*0e209d39SAndroid Build Coastguard Worker     /* general punctuation with + and / and a special value (-2) for - */
153*0e209d39SAndroid Build Coastguard Worker     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -2, -1, 63,
154*0e209d39SAndroid Build Coastguard Worker     /* digits */
155*0e209d39SAndroid Build Coastguard Worker     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
156*0e209d39SAndroid Build Coastguard Worker 
157*0e209d39SAndroid Build Coastguard Worker     /* A-Z */
158*0e209d39SAndroid Build Coastguard Worker     -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
159*0e209d39SAndroid Build Coastguard Worker     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -3, -1, -1, -1,
160*0e209d39SAndroid Build Coastguard Worker 
161*0e209d39SAndroid Build Coastguard Worker     /* a-z */
162*0e209d39SAndroid Build Coastguard Worker     -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
163*0e209d39SAndroid Build Coastguard Worker     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -3, -3
164*0e209d39SAndroid Build Coastguard Worker };
165*0e209d39SAndroid Build Coastguard Worker 
166*0e209d39SAndroid Build Coastguard Worker /*
167*0e209d39SAndroid Build Coastguard Worker  * converter status values:
168*0e209d39SAndroid Build Coastguard Worker  *
169*0e209d39SAndroid Build Coastguard Worker  * toUnicodeStatus:
170*0e209d39SAndroid Build Coastguard Worker  *     24 inDirectMode (boolean)
171*0e209d39SAndroid Build Coastguard Worker  * 23..16 base64Counter (-1..7)
172*0e209d39SAndroid Build Coastguard Worker  * 15..0  bits (up to 14 bits incoming base64)
173*0e209d39SAndroid Build Coastguard Worker  *
174*0e209d39SAndroid Build Coastguard Worker  * fromUnicodeStatus:
175*0e209d39SAndroid Build Coastguard Worker  * 31..28 version (0: set O direct  1: set O escaped)
176*0e209d39SAndroid Build Coastguard Worker  *     24 inDirectMode (boolean)
177*0e209d39SAndroid Build Coastguard Worker  * 23..16 base64Counter (0..2)
178*0e209d39SAndroid Build Coastguard Worker  *  7..0  bits (6 bits outgoing base64)
179*0e209d39SAndroid Build Coastguard Worker  *
180*0e209d39SAndroid Build Coastguard Worker  */
181*0e209d39SAndroid Build Coastguard Worker 
182*0e209d39SAndroid Build Coastguard Worker U_CDECL_BEGIN
183*0e209d39SAndroid Build Coastguard Worker static void U_CALLCONV
_UTF7Reset(UConverter * cnv,UConverterResetChoice choice)184*0e209d39SAndroid Build Coastguard Worker _UTF7Reset(UConverter *cnv, UConverterResetChoice choice) {
185*0e209d39SAndroid Build Coastguard Worker     if(choice<=UCNV_RESET_TO_UNICODE) {
186*0e209d39SAndroid Build Coastguard Worker         /* reset toUnicode */
187*0e209d39SAndroid Build Coastguard Worker         cnv->toUnicodeStatus=0x1000000; /* inDirectMode=true */
188*0e209d39SAndroid Build Coastguard Worker         cnv->toULength=0;
189*0e209d39SAndroid Build Coastguard Worker     }
190*0e209d39SAndroid Build Coastguard Worker     if(choice!=UCNV_RESET_TO_UNICODE) {
191*0e209d39SAndroid Build Coastguard Worker         /* reset fromUnicode */
192*0e209d39SAndroid Build Coastguard Worker         cnv->fromUnicodeStatus=(cnv->fromUnicodeStatus&0xf0000000)|0x1000000; /* keep version, inDirectMode=true */
193*0e209d39SAndroid Build Coastguard Worker     }
194*0e209d39SAndroid Build Coastguard Worker }
195*0e209d39SAndroid Build Coastguard Worker 
196*0e209d39SAndroid Build Coastguard Worker static void U_CALLCONV
_UTF7Open(UConverter * cnv,UConverterLoadArgs * pArgs,UErrorCode * pErrorCode)197*0e209d39SAndroid Build Coastguard Worker _UTF7Open(UConverter *cnv,
198*0e209d39SAndroid Build Coastguard Worker           UConverterLoadArgs *pArgs,
199*0e209d39SAndroid Build Coastguard Worker           UErrorCode *pErrorCode) {
200*0e209d39SAndroid Build Coastguard Worker     (void)pArgs;
201*0e209d39SAndroid Build Coastguard Worker     if(UCNV_GET_VERSION(cnv)<=1) {
202*0e209d39SAndroid Build Coastguard Worker         /* TODO(markus): Should just use cnv->options rather than copying the version number. */
203*0e209d39SAndroid Build Coastguard Worker         cnv->fromUnicodeStatus=UCNV_GET_VERSION(cnv)<<28;
204*0e209d39SAndroid Build Coastguard Worker         _UTF7Reset(cnv, UCNV_RESET_BOTH);
205*0e209d39SAndroid Build Coastguard Worker     } else {
206*0e209d39SAndroid Build Coastguard Worker         *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
207*0e209d39SAndroid Build Coastguard Worker     }
208*0e209d39SAndroid Build Coastguard Worker }
209*0e209d39SAndroid Build Coastguard Worker 
210*0e209d39SAndroid Build Coastguard Worker static void U_CALLCONV
_UTF7ToUnicodeWithOffsets(UConverterToUnicodeArgs * pArgs,UErrorCode * pErrorCode)211*0e209d39SAndroid Build Coastguard Worker _UTF7ToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs,
212*0e209d39SAndroid Build Coastguard Worker                           UErrorCode *pErrorCode) {
213*0e209d39SAndroid Build Coastguard Worker     UConverter *cnv;
214*0e209d39SAndroid Build Coastguard Worker     const uint8_t *source, *sourceLimit;
215*0e209d39SAndroid Build Coastguard Worker     char16_t *target;
216*0e209d39SAndroid Build Coastguard Worker     const char16_t *targetLimit;
217*0e209d39SAndroid Build Coastguard Worker     int32_t *offsets;
218*0e209d39SAndroid Build Coastguard Worker 
219*0e209d39SAndroid Build Coastguard Worker     uint8_t *bytes;
220*0e209d39SAndroid Build Coastguard Worker     uint8_t byteIndex;
221*0e209d39SAndroid Build Coastguard Worker 
222*0e209d39SAndroid Build Coastguard Worker     int32_t length, targetCapacity;
223*0e209d39SAndroid Build Coastguard Worker 
224*0e209d39SAndroid Build Coastguard Worker     /* UTF-7 state */
225*0e209d39SAndroid Build Coastguard Worker     uint16_t bits;
226*0e209d39SAndroid Build Coastguard Worker     int8_t base64Counter;
227*0e209d39SAndroid Build Coastguard Worker     UBool inDirectMode;
228*0e209d39SAndroid Build Coastguard Worker 
229*0e209d39SAndroid Build Coastguard Worker     int8_t base64Value;
230*0e209d39SAndroid Build Coastguard Worker 
231*0e209d39SAndroid Build Coastguard Worker     int32_t sourceIndex, nextSourceIndex;
232*0e209d39SAndroid Build Coastguard Worker 
233*0e209d39SAndroid Build Coastguard Worker     uint8_t b;
234*0e209d39SAndroid Build Coastguard Worker     /* set up the local pointers */
235*0e209d39SAndroid Build Coastguard Worker     cnv=pArgs->converter;
236*0e209d39SAndroid Build Coastguard Worker 
237*0e209d39SAndroid Build Coastguard Worker     source=(const uint8_t *)pArgs->source;
238*0e209d39SAndroid Build Coastguard Worker     sourceLimit=(const uint8_t *)pArgs->sourceLimit;
239*0e209d39SAndroid Build Coastguard Worker     target=pArgs->target;
240*0e209d39SAndroid Build Coastguard Worker     targetLimit=pArgs->targetLimit;
241*0e209d39SAndroid Build Coastguard Worker     offsets=pArgs->offsets;
242*0e209d39SAndroid Build Coastguard Worker     /* get the state machine state */
243*0e209d39SAndroid Build Coastguard Worker     {
244*0e209d39SAndroid Build Coastguard Worker         uint32_t status=cnv->toUnicodeStatus;
245*0e209d39SAndroid Build Coastguard Worker         inDirectMode=(UBool)((status>>24)&1);
246*0e209d39SAndroid Build Coastguard Worker         base64Counter=(int8_t)(status>>16);
247*0e209d39SAndroid Build Coastguard Worker         bits=(uint16_t)status;
248*0e209d39SAndroid Build Coastguard Worker     }
249*0e209d39SAndroid Build Coastguard Worker     bytes=cnv->toUBytes;
250*0e209d39SAndroid Build Coastguard Worker     byteIndex=cnv->toULength;
251*0e209d39SAndroid Build Coastguard Worker 
252*0e209d39SAndroid Build Coastguard Worker     /* sourceIndex=-1 if the current character began in the previous buffer */
253*0e209d39SAndroid Build Coastguard Worker     sourceIndex=byteIndex==0 ? 0 : -1;
254*0e209d39SAndroid Build Coastguard Worker     nextSourceIndex=0;
255*0e209d39SAndroid Build Coastguard Worker 
256*0e209d39SAndroid Build Coastguard Worker     if(inDirectMode) {
257*0e209d39SAndroid Build Coastguard Worker directMode:
258*0e209d39SAndroid Build Coastguard Worker         /*
259*0e209d39SAndroid Build Coastguard Worker          * In Direct Mode, most US-ASCII characters are encoded directly, i.e.,
260*0e209d39SAndroid Build Coastguard Worker          * with their US-ASCII byte values.
261*0e209d39SAndroid Build Coastguard Worker          * Backslash and Tilde and most control characters are not allowed in UTF-7.
262*0e209d39SAndroid Build Coastguard Worker          * A plus sign starts Unicode (or "escape") Mode.
263*0e209d39SAndroid Build Coastguard Worker          *
264*0e209d39SAndroid Build Coastguard Worker          * In Direct Mode, only the sourceIndex is used.
265*0e209d39SAndroid Build Coastguard Worker          */
266*0e209d39SAndroid Build Coastguard Worker         byteIndex=0;
267*0e209d39SAndroid Build Coastguard Worker         length=(int32_t)(sourceLimit-source);
268*0e209d39SAndroid Build Coastguard Worker         targetCapacity=(int32_t)(targetLimit-target);
269*0e209d39SAndroid Build Coastguard Worker         if(length>targetCapacity) {
270*0e209d39SAndroid Build Coastguard Worker             length=targetCapacity;
271*0e209d39SAndroid Build Coastguard Worker         }
272*0e209d39SAndroid Build Coastguard Worker         while(length>0) {
273*0e209d39SAndroid Build Coastguard Worker             b=*source++;
274*0e209d39SAndroid Build Coastguard Worker             if(!isLegalUTF7(b)) {
275*0e209d39SAndroid Build Coastguard Worker                 /* illegal */
276*0e209d39SAndroid Build Coastguard Worker                 bytes[0]=b;
277*0e209d39SAndroid Build Coastguard Worker                 byteIndex=1;
278*0e209d39SAndroid Build Coastguard Worker                 *pErrorCode=U_ILLEGAL_CHAR_FOUND;
279*0e209d39SAndroid Build Coastguard Worker                 break;
280*0e209d39SAndroid Build Coastguard Worker             } else if(b!=PLUS) {
281*0e209d39SAndroid Build Coastguard Worker                 /* write directly encoded character */
282*0e209d39SAndroid Build Coastguard Worker                 *target++=b;
283*0e209d39SAndroid Build Coastguard Worker                 if(offsets!=nullptr) {
284*0e209d39SAndroid Build Coastguard Worker                     *offsets++=sourceIndex++;
285*0e209d39SAndroid Build Coastguard Worker                 }
286*0e209d39SAndroid Build Coastguard Worker             } else /* PLUS */ {
287*0e209d39SAndroid Build Coastguard Worker                 /* switch to Unicode mode */
288*0e209d39SAndroid Build Coastguard Worker                 nextSourceIndex=++sourceIndex;
289*0e209d39SAndroid Build Coastguard Worker                 inDirectMode=false;
290*0e209d39SAndroid Build Coastguard Worker                 byteIndex=0;
291*0e209d39SAndroid Build Coastguard Worker                 bits=0;
292*0e209d39SAndroid Build Coastguard Worker                 base64Counter=-1;
293*0e209d39SAndroid Build Coastguard Worker                 goto unicodeMode;
294*0e209d39SAndroid Build Coastguard Worker             }
295*0e209d39SAndroid Build Coastguard Worker             --length;
296*0e209d39SAndroid Build Coastguard Worker         }
297*0e209d39SAndroid Build Coastguard Worker         if(source<sourceLimit && target>=targetLimit) {
298*0e209d39SAndroid Build Coastguard Worker             /* target is full */
299*0e209d39SAndroid Build Coastguard Worker             *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
300*0e209d39SAndroid Build Coastguard Worker         }
301*0e209d39SAndroid Build Coastguard Worker     } else {
302*0e209d39SAndroid Build Coastguard Worker unicodeMode:
303*0e209d39SAndroid Build Coastguard Worker         /*
304*0e209d39SAndroid Build Coastguard Worker          * In Unicode (or "escape") Mode, UTF-16BE is base64-encoded.
305*0e209d39SAndroid Build Coastguard Worker          * The base64 sequence ends with any character that is not in the base64 alphabet.
306*0e209d39SAndroid Build Coastguard Worker          * A terminating minus sign is consumed.
307*0e209d39SAndroid Build Coastguard Worker          *
308*0e209d39SAndroid Build Coastguard Worker          * In Unicode Mode, the sourceIndex has the index to the start of the current
309*0e209d39SAndroid Build Coastguard Worker          * base64 bytes, while nextSourceIndex is precisely parallel to source,
310*0e209d39SAndroid Build Coastguard Worker          * keeping the index to the following byte.
311*0e209d39SAndroid Build Coastguard Worker          * Note that in 2 out of 3 cases, UChars overlap within a base64 byte.
312*0e209d39SAndroid Build Coastguard Worker          */
313*0e209d39SAndroid Build Coastguard Worker         while(source<sourceLimit) {
314*0e209d39SAndroid Build Coastguard Worker             if(target<targetLimit) {
315*0e209d39SAndroid Build Coastguard Worker                 bytes[byteIndex++]=b=*source++;
316*0e209d39SAndroid Build Coastguard Worker                 ++nextSourceIndex;
317*0e209d39SAndroid Build Coastguard Worker                 base64Value = -3; /* initialize as illegal */
318*0e209d39SAndroid Build Coastguard Worker                 if(b>=126 || (base64Value=fromBase64[b])==-3 || base64Value==-1) {
319*0e209d39SAndroid Build Coastguard Worker                     /* either
320*0e209d39SAndroid Build Coastguard Worker                      * base64Value==-1 for any legal character except base64 and minus sign, or
321*0e209d39SAndroid Build Coastguard Worker                      * base64Value==-3 for illegal characters:
322*0e209d39SAndroid Build Coastguard Worker                      * 1. In either case, leave Unicode mode.
323*0e209d39SAndroid Build Coastguard Worker                      * 2.1. If we ended with an incomplete char16_t or none after the +, then
324*0e209d39SAndroid Build Coastguard Worker                      *      generate an error for the preceding erroneous sequence and deal with
325*0e209d39SAndroid Build Coastguard Worker                      *      the current (possibly illegal) character next time through.
326*0e209d39SAndroid Build Coastguard Worker                      * 2.2. Else the current char comes after a complete char16_t, which was already
327*0e209d39SAndroid Build Coastguard Worker                      *      pushed to the output buf, so:
328*0e209d39SAndroid Build Coastguard Worker                      * 2.2.1. If the current char is legal, just save it for processing next time.
329*0e209d39SAndroid Build Coastguard Worker                      *        It may be for example, a plus which we need to deal with in direct mode.
330*0e209d39SAndroid Build Coastguard Worker                      * 2.2.2. Else if the current char is illegal, we might as well deal with it here.
331*0e209d39SAndroid Build Coastguard Worker                      */
332*0e209d39SAndroid Build Coastguard Worker                     inDirectMode=true;
333*0e209d39SAndroid Build Coastguard Worker                     if(base64Counter==-1) {
334*0e209d39SAndroid Build Coastguard Worker                         /* illegal: + immediately followed by something other than base64 or minus sign */
335*0e209d39SAndroid Build Coastguard Worker                         /* include the plus sign in the reported sequence, but not the subsequent char */
336*0e209d39SAndroid Build Coastguard Worker                         --source;
337*0e209d39SAndroid Build Coastguard Worker                         bytes[0]=PLUS;
338*0e209d39SAndroid Build Coastguard Worker                         byteIndex=1;
339*0e209d39SAndroid Build Coastguard Worker                         *pErrorCode=U_ILLEGAL_CHAR_FOUND;
340*0e209d39SAndroid Build Coastguard Worker                         break;
341*0e209d39SAndroid Build Coastguard Worker                     } else if(bits!=0) {
342*0e209d39SAndroid Build Coastguard Worker                         /* bits are illegally left over, a char16_t is incomplete */
343*0e209d39SAndroid Build Coastguard Worker                         /* don't include current char (legal or illegal) in error seq */
344*0e209d39SAndroid Build Coastguard Worker                         --source;
345*0e209d39SAndroid Build Coastguard Worker                         --byteIndex;
346*0e209d39SAndroid Build Coastguard Worker                         *pErrorCode=U_ILLEGAL_CHAR_FOUND;
347*0e209d39SAndroid Build Coastguard Worker                         break;
348*0e209d39SAndroid Build Coastguard Worker                     } else {
349*0e209d39SAndroid Build Coastguard Worker                         /* previous char16_t was complete */
350*0e209d39SAndroid Build Coastguard Worker                         if(base64Value==-3) {
351*0e209d39SAndroid Build Coastguard Worker                             /* current character is illegal, deal with it here */
352*0e209d39SAndroid Build Coastguard Worker                             *pErrorCode=U_ILLEGAL_CHAR_FOUND;
353*0e209d39SAndroid Build Coastguard Worker                             break;
354*0e209d39SAndroid Build Coastguard Worker                         } else {
355*0e209d39SAndroid Build Coastguard Worker                             /* un-read the current character in case it is a plus sign */
356*0e209d39SAndroid Build Coastguard Worker                             --source;
357*0e209d39SAndroid Build Coastguard Worker                             sourceIndex=nextSourceIndex-1;
358*0e209d39SAndroid Build Coastguard Worker                             goto directMode;
359*0e209d39SAndroid Build Coastguard Worker                         }
360*0e209d39SAndroid Build Coastguard Worker                     }
361*0e209d39SAndroid Build Coastguard Worker                 } else if(base64Value>=0) {
362*0e209d39SAndroid Build Coastguard Worker                     /* collect base64 bytes into UChars */
363*0e209d39SAndroid Build Coastguard Worker                     switch(base64Counter) {
364*0e209d39SAndroid Build Coastguard Worker                     case -1: /* -1 is immediately after the + */
365*0e209d39SAndroid Build Coastguard Worker                     case 0:
366*0e209d39SAndroid Build Coastguard Worker                         bits=base64Value;
367*0e209d39SAndroid Build Coastguard Worker                         base64Counter=1;
368*0e209d39SAndroid Build Coastguard Worker                         break;
369*0e209d39SAndroid Build Coastguard Worker                     case 1:
370*0e209d39SAndroid Build Coastguard Worker                     case 3:
371*0e209d39SAndroid Build Coastguard Worker                     case 4:
372*0e209d39SAndroid Build Coastguard Worker                     case 6:
373*0e209d39SAndroid Build Coastguard Worker                         bits=(uint16_t)((bits<<6)|base64Value);
374*0e209d39SAndroid Build Coastguard Worker                         ++base64Counter;
375*0e209d39SAndroid Build Coastguard Worker                         break;
376*0e209d39SAndroid Build Coastguard Worker                     case 2:
377*0e209d39SAndroid Build Coastguard Worker                         *target++=(char16_t)((bits<<4)|(base64Value>>2));
378*0e209d39SAndroid Build Coastguard Worker                         if(offsets!=nullptr) {
379*0e209d39SAndroid Build Coastguard Worker                             *offsets++=sourceIndex;
380*0e209d39SAndroid Build Coastguard Worker                             sourceIndex=nextSourceIndex-1;
381*0e209d39SAndroid Build Coastguard Worker                         }
382*0e209d39SAndroid Build Coastguard Worker                         bytes[0]=b; /* keep this byte in case an error occurs */
383*0e209d39SAndroid Build Coastguard Worker                         byteIndex=1;
384*0e209d39SAndroid Build Coastguard Worker                         bits=(uint16_t)(base64Value&3);
385*0e209d39SAndroid Build Coastguard Worker                         base64Counter=3;
386*0e209d39SAndroid Build Coastguard Worker                         break;
387*0e209d39SAndroid Build Coastguard Worker                     case 5:
388*0e209d39SAndroid Build Coastguard Worker                         *target++=(char16_t)((bits<<2)|(base64Value>>4));
389*0e209d39SAndroid Build Coastguard Worker                         if(offsets!=nullptr) {
390*0e209d39SAndroid Build Coastguard Worker                             *offsets++=sourceIndex;
391*0e209d39SAndroid Build Coastguard Worker                             sourceIndex=nextSourceIndex-1;
392*0e209d39SAndroid Build Coastguard Worker                         }
393*0e209d39SAndroid Build Coastguard Worker                         bytes[0]=b; /* keep this byte in case an error occurs */
394*0e209d39SAndroid Build Coastguard Worker                         byteIndex=1;
395*0e209d39SAndroid Build Coastguard Worker                         bits=(uint16_t)(base64Value&15);
396*0e209d39SAndroid Build Coastguard Worker                         base64Counter=6;
397*0e209d39SAndroid Build Coastguard Worker                         break;
398*0e209d39SAndroid Build Coastguard Worker                     case 7:
399*0e209d39SAndroid Build Coastguard Worker                         *target++=(char16_t)((bits<<6)|base64Value);
400*0e209d39SAndroid Build Coastguard Worker                         if(offsets!=nullptr) {
401*0e209d39SAndroid Build Coastguard Worker                             *offsets++=sourceIndex;
402*0e209d39SAndroid Build Coastguard Worker                             sourceIndex=nextSourceIndex;
403*0e209d39SAndroid Build Coastguard Worker                         }
404*0e209d39SAndroid Build Coastguard Worker                         byteIndex=0;
405*0e209d39SAndroid Build Coastguard Worker                         bits=0;
406*0e209d39SAndroid Build Coastguard Worker                         base64Counter=0;
407*0e209d39SAndroid Build Coastguard Worker                         break;
408*0e209d39SAndroid Build Coastguard Worker                     default:
409*0e209d39SAndroid Build Coastguard Worker                         /* will never occur */
410*0e209d39SAndroid Build Coastguard Worker                         break;
411*0e209d39SAndroid Build Coastguard Worker                     }
412*0e209d39SAndroid Build Coastguard Worker                 } else /*base64Value==-2*/ {
413*0e209d39SAndroid Build Coastguard Worker                     /* minus sign terminates the base64 sequence */
414*0e209d39SAndroid Build Coastguard Worker                     inDirectMode=true;
415*0e209d39SAndroid Build Coastguard Worker                     if(base64Counter==-1) {
416*0e209d39SAndroid Build Coastguard Worker                         /* +- i.e. a minus immediately following a plus */
417*0e209d39SAndroid Build Coastguard Worker                         *target++=PLUS;
418*0e209d39SAndroid Build Coastguard Worker                         if(offsets!=nullptr) {
419*0e209d39SAndroid Build Coastguard Worker                             *offsets++=sourceIndex-1;
420*0e209d39SAndroid Build Coastguard Worker                         }
421*0e209d39SAndroid Build Coastguard Worker                     } else {
422*0e209d39SAndroid Build Coastguard Worker                         /* absorb the minus and leave the Unicode Mode */
423*0e209d39SAndroid Build Coastguard Worker                         if(bits!=0) {
424*0e209d39SAndroid Build Coastguard Worker                             /* bits are illegally left over, a char16_t is incomplete */
425*0e209d39SAndroid Build Coastguard Worker                             *pErrorCode=U_ILLEGAL_CHAR_FOUND;
426*0e209d39SAndroid Build Coastguard Worker                             break;
427*0e209d39SAndroid Build Coastguard Worker                         }
428*0e209d39SAndroid Build Coastguard Worker                     }
429*0e209d39SAndroid Build Coastguard Worker                     sourceIndex=nextSourceIndex;
430*0e209d39SAndroid Build Coastguard Worker                     goto directMode;
431*0e209d39SAndroid Build Coastguard Worker                 }
432*0e209d39SAndroid Build Coastguard Worker             } else {
433*0e209d39SAndroid Build Coastguard Worker                 /* target is full */
434*0e209d39SAndroid Build Coastguard Worker                 *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
435*0e209d39SAndroid Build Coastguard Worker                 break;
436*0e209d39SAndroid Build Coastguard Worker             }
437*0e209d39SAndroid Build Coastguard Worker         }
438*0e209d39SAndroid Build Coastguard Worker     }
439*0e209d39SAndroid Build Coastguard Worker 
440*0e209d39SAndroid Build Coastguard Worker     if(U_SUCCESS(*pErrorCode) && pArgs->flush && source==sourceLimit && bits==0) {
441*0e209d39SAndroid Build Coastguard Worker         /*
442*0e209d39SAndroid Build Coastguard Worker          * if we are in Unicode mode, then the byteIndex might not be 0,
443*0e209d39SAndroid Build Coastguard Worker          * but that is ok if bits==0
444*0e209d39SAndroid Build Coastguard Worker          * -> we set byteIndex=0 at the end of the stream to avoid a truncated error
445*0e209d39SAndroid Build Coastguard Worker          * (not true for IMAP-mailbox-name where we must end in direct mode)
446*0e209d39SAndroid Build Coastguard Worker          */
447*0e209d39SAndroid Build Coastguard Worker         byteIndex=0;
448*0e209d39SAndroid Build Coastguard Worker     }
449*0e209d39SAndroid Build Coastguard Worker 
450*0e209d39SAndroid Build Coastguard Worker     /* set the converter state back into UConverter */
451*0e209d39SAndroid Build Coastguard Worker     cnv->toUnicodeStatus=((uint32_t)inDirectMode<<24)|((uint32_t)((uint8_t)base64Counter)<<16)|(uint32_t)bits;
452*0e209d39SAndroid Build Coastguard Worker     cnv->toULength=byteIndex;
453*0e209d39SAndroid Build Coastguard Worker 
454*0e209d39SAndroid Build Coastguard Worker     /* write back the updated pointers */
455*0e209d39SAndroid Build Coastguard Worker     pArgs->source=(const char *)source;
456*0e209d39SAndroid Build Coastguard Worker     pArgs->target=target;
457*0e209d39SAndroid Build Coastguard Worker     pArgs->offsets=offsets;
458*0e209d39SAndroid Build Coastguard Worker }
459*0e209d39SAndroid Build Coastguard Worker 
460*0e209d39SAndroid Build Coastguard Worker static void U_CALLCONV
_UTF7FromUnicodeWithOffsets(UConverterFromUnicodeArgs * pArgs,UErrorCode * pErrorCode)461*0e209d39SAndroid Build Coastguard Worker _UTF7FromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs,
462*0e209d39SAndroid Build Coastguard Worker                             UErrorCode *pErrorCode) {
463*0e209d39SAndroid Build Coastguard Worker     UConverter *cnv;
464*0e209d39SAndroid Build Coastguard Worker     const char16_t *source, *sourceLimit;
465*0e209d39SAndroid Build Coastguard Worker     uint8_t *target, *targetLimit;
466*0e209d39SAndroid Build Coastguard Worker     int32_t *offsets;
467*0e209d39SAndroid Build Coastguard Worker 
468*0e209d39SAndroid Build Coastguard Worker     int32_t length, targetCapacity, sourceIndex;
469*0e209d39SAndroid Build Coastguard Worker     char16_t c;
470*0e209d39SAndroid Build Coastguard Worker 
471*0e209d39SAndroid Build Coastguard Worker     /* UTF-7 state */
472*0e209d39SAndroid Build Coastguard Worker     const UBool *encodeDirectly;
473*0e209d39SAndroid Build Coastguard Worker     uint8_t bits;
474*0e209d39SAndroid Build Coastguard Worker     int8_t base64Counter;
475*0e209d39SAndroid Build Coastguard Worker     UBool inDirectMode;
476*0e209d39SAndroid Build Coastguard Worker 
477*0e209d39SAndroid Build Coastguard Worker     /* set up the local pointers */
478*0e209d39SAndroid Build Coastguard Worker     cnv=pArgs->converter;
479*0e209d39SAndroid Build Coastguard Worker 
480*0e209d39SAndroid Build Coastguard Worker     /* set up the local pointers */
481*0e209d39SAndroid Build Coastguard Worker     source=pArgs->source;
482*0e209d39SAndroid Build Coastguard Worker     sourceLimit=pArgs->sourceLimit;
483*0e209d39SAndroid Build Coastguard Worker     target=(uint8_t *)pArgs->target;
484*0e209d39SAndroid Build Coastguard Worker     targetLimit=(uint8_t *)pArgs->targetLimit;
485*0e209d39SAndroid Build Coastguard Worker     offsets=pArgs->offsets;
486*0e209d39SAndroid Build Coastguard Worker 
487*0e209d39SAndroid Build Coastguard Worker     /* get the state machine state */
488*0e209d39SAndroid Build Coastguard Worker     {
489*0e209d39SAndroid Build Coastguard Worker         uint32_t status=cnv->fromUnicodeStatus;
490*0e209d39SAndroid Build Coastguard Worker         encodeDirectly= status<0x10000000 ? encodeDirectlyMaximum : encodeDirectlyRestricted;
491*0e209d39SAndroid Build Coastguard Worker         inDirectMode=(UBool)((status>>24)&1);
492*0e209d39SAndroid Build Coastguard Worker         base64Counter=(int8_t)(status>>16);
493*0e209d39SAndroid Build Coastguard Worker         bits=(uint8_t)status;
494*0e209d39SAndroid Build Coastguard Worker         U_ASSERT(bits<=UPRV_LENGTHOF(toBase64));
495*0e209d39SAndroid Build Coastguard Worker     }
496*0e209d39SAndroid Build Coastguard Worker 
497*0e209d39SAndroid Build Coastguard Worker     /* UTF-7 always encodes UTF-16 code units, therefore we need only a simple sourceIndex */
498*0e209d39SAndroid Build Coastguard Worker     sourceIndex=0;
499*0e209d39SAndroid Build Coastguard Worker 
500*0e209d39SAndroid Build Coastguard Worker     if(inDirectMode) {
501*0e209d39SAndroid Build Coastguard Worker directMode:
502*0e209d39SAndroid Build Coastguard Worker         length=(int32_t)(sourceLimit-source);
503*0e209d39SAndroid Build Coastguard Worker         targetCapacity=(int32_t)(targetLimit-target);
504*0e209d39SAndroid Build Coastguard Worker         if(length>targetCapacity) {
505*0e209d39SAndroid Build Coastguard Worker             length=targetCapacity;
506*0e209d39SAndroid Build Coastguard Worker         }
507*0e209d39SAndroid Build Coastguard Worker         while(length>0) {
508*0e209d39SAndroid Build Coastguard Worker             c=*source++;
509*0e209d39SAndroid Build Coastguard Worker             /* currently always encode CR LF SP TAB directly */
510*0e209d39SAndroid Build Coastguard Worker             if(c<=127 && encodeDirectly[c]) {
511*0e209d39SAndroid Build Coastguard Worker                 /* encode directly */
512*0e209d39SAndroid Build Coastguard Worker                 *target++=(uint8_t)c;
513*0e209d39SAndroid Build Coastguard Worker                 if(offsets!=nullptr) {
514*0e209d39SAndroid Build Coastguard Worker                     *offsets++=sourceIndex++;
515*0e209d39SAndroid Build Coastguard Worker                 }
516*0e209d39SAndroid Build Coastguard Worker             } else if(c==PLUS) {
517*0e209d39SAndroid Build Coastguard Worker                 /* output +- for + */
518*0e209d39SAndroid Build Coastguard Worker                 *target++=PLUS;
519*0e209d39SAndroid Build Coastguard Worker                 if(target<targetLimit) {
520*0e209d39SAndroid Build Coastguard Worker                     *target++=MINUS;
521*0e209d39SAndroid Build Coastguard Worker                     if(offsets!=nullptr) {
522*0e209d39SAndroid Build Coastguard Worker                         *offsets++=sourceIndex;
523*0e209d39SAndroid Build Coastguard Worker                         *offsets++=sourceIndex++;
524*0e209d39SAndroid Build Coastguard Worker                     }
525*0e209d39SAndroid Build Coastguard Worker                     /* realign length and targetCapacity */
526*0e209d39SAndroid Build Coastguard Worker                     goto directMode;
527*0e209d39SAndroid Build Coastguard Worker                 } else {
528*0e209d39SAndroid Build Coastguard Worker                     if(offsets!=nullptr) {
529*0e209d39SAndroid Build Coastguard Worker                         *offsets++=sourceIndex++;
530*0e209d39SAndroid Build Coastguard Worker                     }
531*0e209d39SAndroid Build Coastguard Worker                     cnv->charErrorBuffer[0]=MINUS;
532*0e209d39SAndroid Build Coastguard Worker                     cnv->charErrorBufferLength=1;
533*0e209d39SAndroid Build Coastguard Worker                     *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
534*0e209d39SAndroid Build Coastguard Worker                     break;
535*0e209d39SAndroid Build Coastguard Worker                 }
536*0e209d39SAndroid Build Coastguard Worker             } else {
537*0e209d39SAndroid Build Coastguard Worker                 /* un-read this character and switch to Unicode Mode */
538*0e209d39SAndroid Build Coastguard Worker                 --source;
539*0e209d39SAndroid Build Coastguard Worker                 *target++=PLUS;
540*0e209d39SAndroid Build Coastguard Worker                 if(offsets!=nullptr) {
541*0e209d39SAndroid Build Coastguard Worker                     *offsets++=sourceIndex;
542*0e209d39SAndroid Build Coastguard Worker                 }
543*0e209d39SAndroid Build Coastguard Worker                 inDirectMode=false;
544*0e209d39SAndroid Build Coastguard Worker                 base64Counter=0;
545*0e209d39SAndroid Build Coastguard Worker                 goto unicodeMode;
546*0e209d39SAndroid Build Coastguard Worker             }
547*0e209d39SAndroid Build Coastguard Worker             --length;
548*0e209d39SAndroid Build Coastguard Worker         }
549*0e209d39SAndroid Build Coastguard Worker         if(source<sourceLimit && target>=targetLimit) {
550*0e209d39SAndroid Build Coastguard Worker             /* target is full */
551*0e209d39SAndroid Build Coastguard Worker             *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
552*0e209d39SAndroid Build Coastguard Worker         }
553*0e209d39SAndroid Build Coastguard Worker     } else {
554*0e209d39SAndroid Build Coastguard Worker unicodeMode:
555*0e209d39SAndroid Build Coastguard Worker         while(source<sourceLimit) {
556*0e209d39SAndroid Build Coastguard Worker             if(target<targetLimit) {
557*0e209d39SAndroid Build Coastguard Worker                 c=*source++;
558*0e209d39SAndroid Build Coastguard Worker                 if(c<=127 && encodeDirectly[c]) {
559*0e209d39SAndroid Build Coastguard Worker                     /* encode directly */
560*0e209d39SAndroid Build Coastguard Worker                     inDirectMode=true;
561*0e209d39SAndroid Build Coastguard Worker 
562*0e209d39SAndroid Build Coastguard Worker                     /* trick: back out this character to make this easier */
563*0e209d39SAndroid Build Coastguard Worker                     --source;
564*0e209d39SAndroid Build Coastguard Worker 
565*0e209d39SAndroid Build Coastguard Worker                     /* terminate the base64 sequence */
566*0e209d39SAndroid Build Coastguard Worker                     if(base64Counter!=0) {
567*0e209d39SAndroid Build Coastguard Worker                         /* write remaining bits for the previous character */
568*0e209d39SAndroid Build Coastguard Worker                         *target++=toBase64[bits];
569*0e209d39SAndroid Build Coastguard Worker                         if(offsets!=nullptr) {
570*0e209d39SAndroid Build Coastguard Worker                             *offsets++=sourceIndex-1;
571*0e209d39SAndroid Build Coastguard Worker                         }
572*0e209d39SAndroid Build Coastguard Worker                     }
573*0e209d39SAndroid Build Coastguard Worker                     if(fromBase64[c]!=-1) {
574*0e209d39SAndroid Build Coastguard Worker                         /* need to terminate with a minus */
575*0e209d39SAndroid Build Coastguard Worker                         if(target<targetLimit) {
576*0e209d39SAndroid Build Coastguard Worker                             *target++=MINUS;
577*0e209d39SAndroid Build Coastguard Worker                             if(offsets!=nullptr) {
578*0e209d39SAndroid Build Coastguard Worker                                 *offsets++=sourceIndex-1;
579*0e209d39SAndroid Build Coastguard Worker                             }
580*0e209d39SAndroid Build Coastguard Worker                         } else {
581*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBuffer[0]=MINUS;
582*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBufferLength=1;
583*0e209d39SAndroid Build Coastguard Worker                             *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
584*0e209d39SAndroid Build Coastguard Worker                             break;
585*0e209d39SAndroid Build Coastguard Worker                         }
586*0e209d39SAndroid Build Coastguard Worker                     }
587*0e209d39SAndroid Build Coastguard Worker                     goto directMode;
588*0e209d39SAndroid Build Coastguard Worker                 } else {
589*0e209d39SAndroid Build Coastguard Worker                     /*
590*0e209d39SAndroid Build Coastguard Worker                      * base64 this character:
591*0e209d39SAndroid Build Coastguard Worker                      * Output 2 or 3 base64 bytes for the remaining bits of the previous character
592*0e209d39SAndroid Build Coastguard Worker                      * and the bits of this character, each implicitly in UTF-16BE.
593*0e209d39SAndroid Build Coastguard Worker                      *
594*0e209d39SAndroid Build Coastguard Worker                      * Here, bits is an 8-bit variable because only 6 bits need to be kept from one
595*0e209d39SAndroid Build Coastguard Worker                      * character to the next. The actual 2 or 4 bits are shifted to the left edge
596*0e209d39SAndroid Build Coastguard Worker                      * of the 6-bits field 5..0 to make the termination of the base64 sequence easier.
597*0e209d39SAndroid Build Coastguard Worker                      */
598*0e209d39SAndroid Build Coastguard Worker                     switch(base64Counter) {
599*0e209d39SAndroid Build Coastguard Worker                     case 0:
600*0e209d39SAndroid Build Coastguard Worker                         *target++=toBase64[c>>10];
601*0e209d39SAndroid Build Coastguard Worker                         if(target<targetLimit) {
602*0e209d39SAndroid Build Coastguard Worker                             *target++=toBase64[(c>>4)&0x3f];
603*0e209d39SAndroid Build Coastguard Worker                             if(offsets!=nullptr) {
604*0e209d39SAndroid Build Coastguard Worker                                 *offsets++=sourceIndex;
605*0e209d39SAndroid Build Coastguard Worker                                 *offsets++=sourceIndex++;
606*0e209d39SAndroid Build Coastguard Worker                             }
607*0e209d39SAndroid Build Coastguard Worker                         } else {
608*0e209d39SAndroid Build Coastguard Worker                             if(offsets!=nullptr) {
609*0e209d39SAndroid Build Coastguard Worker                                 *offsets++=sourceIndex++;
610*0e209d39SAndroid Build Coastguard Worker                             }
611*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBuffer[0]=toBase64[(c>>4)&0x3f];
612*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBufferLength=1;
613*0e209d39SAndroid Build Coastguard Worker                             *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
614*0e209d39SAndroid Build Coastguard Worker                         }
615*0e209d39SAndroid Build Coastguard Worker                         bits=(uint8_t)((c&15)<<2);
616*0e209d39SAndroid Build Coastguard Worker                         base64Counter=1;
617*0e209d39SAndroid Build Coastguard Worker                         break;
618*0e209d39SAndroid Build Coastguard Worker                     case 1:
619*0e209d39SAndroid Build Coastguard Worker                         *target++=toBase64[bits|(c>>14)];
620*0e209d39SAndroid Build Coastguard Worker                         if(target<targetLimit) {
621*0e209d39SAndroid Build Coastguard Worker                             *target++=toBase64[(c>>8)&0x3f];
622*0e209d39SAndroid Build Coastguard Worker                             if(target<targetLimit) {
623*0e209d39SAndroid Build Coastguard Worker                                 *target++=toBase64[(c>>2)&0x3f];
624*0e209d39SAndroid Build Coastguard Worker                                 if(offsets!=nullptr) {
625*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex;
626*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex;
627*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex++;
628*0e209d39SAndroid Build Coastguard Worker                                 }
629*0e209d39SAndroid Build Coastguard Worker                             } else {
630*0e209d39SAndroid Build Coastguard Worker                                 if(offsets!=nullptr) {
631*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex;
632*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex++;
633*0e209d39SAndroid Build Coastguard Worker                                 }
634*0e209d39SAndroid Build Coastguard Worker                                 cnv->charErrorBuffer[0]=toBase64[(c>>2)&0x3f];
635*0e209d39SAndroid Build Coastguard Worker                                 cnv->charErrorBufferLength=1;
636*0e209d39SAndroid Build Coastguard Worker                                 *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
637*0e209d39SAndroid Build Coastguard Worker                             }
638*0e209d39SAndroid Build Coastguard Worker                         } else {
639*0e209d39SAndroid Build Coastguard Worker                             if(offsets!=nullptr) {
640*0e209d39SAndroid Build Coastguard Worker                                 *offsets++=sourceIndex++;
641*0e209d39SAndroid Build Coastguard Worker                             }
642*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBuffer[0]=toBase64[(c>>8)&0x3f];
643*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBuffer[1]=toBase64[(c>>2)&0x3f];
644*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBufferLength=2;
645*0e209d39SAndroid Build Coastguard Worker                             *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
646*0e209d39SAndroid Build Coastguard Worker                         }
647*0e209d39SAndroid Build Coastguard Worker                         bits=(uint8_t)((c&3)<<4);
648*0e209d39SAndroid Build Coastguard Worker                         base64Counter=2;
649*0e209d39SAndroid Build Coastguard Worker                         break;
650*0e209d39SAndroid Build Coastguard Worker                     case 2:
651*0e209d39SAndroid Build Coastguard Worker                         *target++=toBase64[bits|(c>>12)];
652*0e209d39SAndroid Build Coastguard Worker                         if(target<targetLimit) {
653*0e209d39SAndroid Build Coastguard Worker                             *target++=toBase64[(c>>6)&0x3f];
654*0e209d39SAndroid Build Coastguard Worker                             if(target<targetLimit) {
655*0e209d39SAndroid Build Coastguard Worker                                 *target++=toBase64[c&0x3f];
656*0e209d39SAndroid Build Coastguard Worker                                 if(offsets!=nullptr) {
657*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex;
658*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex;
659*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex++;
660*0e209d39SAndroid Build Coastguard Worker                                 }
661*0e209d39SAndroid Build Coastguard Worker                             } else {
662*0e209d39SAndroid Build Coastguard Worker                                 if(offsets!=nullptr) {
663*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex;
664*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex++;
665*0e209d39SAndroid Build Coastguard Worker                                 }
666*0e209d39SAndroid Build Coastguard Worker                                 cnv->charErrorBuffer[0]=toBase64[c&0x3f];
667*0e209d39SAndroid Build Coastguard Worker                                 cnv->charErrorBufferLength=1;
668*0e209d39SAndroid Build Coastguard Worker                                 *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
669*0e209d39SAndroid Build Coastguard Worker                             }
670*0e209d39SAndroid Build Coastguard Worker                         } else {
671*0e209d39SAndroid Build Coastguard Worker                             if(offsets!=nullptr) {
672*0e209d39SAndroid Build Coastguard Worker                                 *offsets++=sourceIndex++;
673*0e209d39SAndroid Build Coastguard Worker                             }
674*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBuffer[0]=toBase64[(c>>6)&0x3f];
675*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBuffer[1]=toBase64[c&0x3f];
676*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBufferLength=2;
677*0e209d39SAndroid Build Coastguard Worker                             *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
678*0e209d39SAndroid Build Coastguard Worker                         }
679*0e209d39SAndroid Build Coastguard Worker                         bits=0;
680*0e209d39SAndroid Build Coastguard Worker                         base64Counter=0;
681*0e209d39SAndroid Build Coastguard Worker                         break;
682*0e209d39SAndroid Build Coastguard Worker                     default:
683*0e209d39SAndroid Build Coastguard Worker                         /* will never occur */
684*0e209d39SAndroid Build Coastguard Worker                         break;
685*0e209d39SAndroid Build Coastguard Worker                     }
686*0e209d39SAndroid Build Coastguard Worker                 }
687*0e209d39SAndroid Build Coastguard Worker             } else {
688*0e209d39SAndroid Build Coastguard Worker                 /* target is full */
689*0e209d39SAndroid Build Coastguard Worker                 *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
690*0e209d39SAndroid Build Coastguard Worker                 break;
691*0e209d39SAndroid Build Coastguard Worker             }
692*0e209d39SAndroid Build Coastguard Worker         }
693*0e209d39SAndroid Build Coastguard Worker     }
694*0e209d39SAndroid Build Coastguard Worker 
695*0e209d39SAndroid Build Coastguard Worker     if(pArgs->flush && source>=sourceLimit) {
696*0e209d39SAndroid Build Coastguard Worker         /* flush remaining bits to the target */
697*0e209d39SAndroid Build Coastguard Worker         if(!inDirectMode) {
698*0e209d39SAndroid Build Coastguard Worker             if (base64Counter!=0) {
699*0e209d39SAndroid Build Coastguard Worker                 if(target<targetLimit) {
700*0e209d39SAndroid Build Coastguard Worker                     *target++=toBase64[bits];
701*0e209d39SAndroid Build Coastguard Worker                     if(offsets!=nullptr) {
702*0e209d39SAndroid Build Coastguard Worker                         *offsets++=sourceIndex-1;
703*0e209d39SAndroid Build Coastguard Worker                     }
704*0e209d39SAndroid Build Coastguard Worker                 } else {
705*0e209d39SAndroid Build Coastguard Worker                     cnv->charErrorBuffer[cnv->charErrorBufferLength++]=toBase64[bits];
706*0e209d39SAndroid Build Coastguard Worker                     *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
707*0e209d39SAndroid Build Coastguard Worker                 }
708*0e209d39SAndroid Build Coastguard Worker             }
709*0e209d39SAndroid Build Coastguard Worker             /* Add final MINUS to terminate unicodeMode */
710*0e209d39SAndroid Build Coastguard Worker             if(target<targetLimit) {
711*0e209d39SAndroid Build Coastguard Worker                 *target++=MINUS;
712*0e209d39SAndroid Build Coastguard Worker                 if(offsets!=nullptr) {
713*0e209d39SAndroid Build Coastguard Worker                     *offsets++=sourceIndex-1;
714*0e209d39SAndroid Build Coastguard Worker                 }
715*0e209d39SAndroid Build Coastguard Worker             } else {
716*0e209d39SAndroid Build Coastguard Worker                 cnv->charErrorBuffer[cnv->charErrorBufferLength++]=MINUS;
717*0e209d39SAndroid Build Coastguard Worker                 *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
718*0e209d39SAndroid Build Coastguard Worker             }
719*0e209d39SAndroid Build Coastguard Worker         }
720*0e209d39SAndroid Build Coastguard Worker         /* reset the state for the next conversion */
721*0e209d39SAndroid Build Coastguard Worker         cnv->fromUnicodeStatus=(cnv->fromUnicodeStatus&0xf0000000)|0x1000000; /* keep version, inDirectMode=true */
722*0e209d39SAndroid Build Coastguard Worker     } else {
723*0e209d39SAndroid Build Coastguard Worker         /* set the converter state back into UConverter */
724*0e209d39SAndroid Build Coastguard Worker         cnv->fromUnicodeStatus=
725*0e209d39SAndroid Build Coastguard Worker             (cnv->fromUnicodeStatus&0xf0000000)|    /* keep version*/
726*0e209d39SAndroid Build Coastguard Worker             ((uint32_t)inDirectMode<<24)|((uint32_t)base64Counter<<16)|(uint32_t)bits;
727*0e209d39SAndroid Build Coastguard Worker     }
728*0e209d39SAndroid Build Coastguard Worker 
729*0e209d39SAndroid Build Coastguard Worker     /* write back the updated pointers */
730*0e209d39SAndroid Build Coastguard Worker     pArgs->source=source;
731*0e209d39SAndroid Build Coastguard Worker     pArgs->target=(char *)target;
732*0e209d39SAndroid Build Coastguard Worker     pArgs->offsets=offsets;
733*0e209d39SAndroid Build Coastguard Worker }
734*0e209d39SAndroid Build Coastguard Worker 
735*0e209d39SAndroid Build Coastguard Worker static const char * U_CALLCONV
_UTF7GetName(const UConverter * cnv)736*0e209d39SAndroid Build Coastguard Worker _UTF7GetName(const UConverter *cnv) {
737*0e209d39SAndroid Build Coastguard Worker     switch(cnv->fromUnicodeStatus>>28) {
738*0e209d39SAndroid Build Coastguard Worker     case 1:
739*0e209d39SAndroid Build Coastguard Worker         return "UTF-7,version=1";
740*0e209d39SAndroid Build Coastguard Worker     default:
741*0e209d39SAndroid Build Coastguard Worker         return "UTF-7";
742*0e209d39SAndroid Build Coastguard Worker     }
743*0e209d39SAndroid Build Coastguard Worker }
744*0e209d39SAndroid Build Coastguard Worker U_CDECL_END
745*0e209d39SAndroid Build Coastguard Worker 
746*0e209d39SAndroid Build Coastguard Worker static const UConverterImpl _UTF7Impl={
747*0e209d39SAndroid Build Coastguard Worker     UCNV_UTF7,
748*0e209d39SAndroid Build Coastguard Worker 
749*0e209d39SAndroid Build Coastguard Worker     nullptr,
750*0e209d39SAndroid Build Coastguard Worker     nullptr,
751*0e209d39SAndroid Build Coastguard Worker 
752*0e209d39SAndroid Build Coastguard Worker     _UTF7Open,
753*0e209d39SAndroid Build Coastguard Worker     nullptr,
754*0e209d39SAndroid Build Coastguard Worker     _UTF7Reset,
755*0e209d39SAndroid Build Coastguard Worker 
756*0e209d39SAndroid Build Coastguard Worker     _UTF7ToUnicodeWithOffsets,
757*0e209d39SAndroid Build Coastguard Worker     _UTF7ToUnicodeWithOffsets,
758*0e209d39SAndroid Build Coastguard Worker     _UTF7FromUnicodeWithOffsets,
759*0e209d39SAndroid Build Coastguard Worker     _UTF7FromUnicodeWithOffsets,
760*0e209d39SAndroid Build Coastguard Worker     nullptr,
761*0e209d39SAndroid Build Coastguard Worker 
762*0e209d39SAndroid Build Coastguard Worker     nullptr,
763*0e209d39SAndroid Build Coastguard Worker     _UTF7GetName,
764*0e209d39SAndroid Build Coastguard Worker     nullptr, /* we don't need writeSub() because we never call a callback at fromUnicode() */
765*0e209d39SAndroid Build Coastguard Worker     nullptr,
766*0e209d39SAndroid Build Coastguard Worker     ucnv_getCompleteUnicodeSet,
767*0e209d39SAndroid Build Coastguard Worker 
768*0e209d39SAndroid Build Coastguard Worker     nullptr,
769*0e209d39SAndroid Build Coastguard Worker     nullptr
770*0e209d39SAndroid Build Coastguard Worker };
771*0e209d39SAndroid Build Coastguard Worker 
772*0e209d39SAndroid Build Coastguard Worker static const UConverterStaticData _UTF7StaticData={
773*0e209d39SAndroid Build Coastguard Worker     sizeof(UConverterStaticData),
774*0e209d39SAndroid Build Coastguard Worker     "UTF-7",
775*0e209d39SAndroid Build Coastguard Worker     0, /* TODO CCSID for UTF-7 */
776*0e209d39SAndroid Build Coastguard Worker     UCNV_IBM, UCNV_UTF7,
777*0e209d39SAndroid Build Coastguard Worker     1, 4,
778*0e209d39SAndroid Build Coastguard Worker     { 0x3f, 0, 0, 0 }, 1, /* the subchar is not used */
779*0e209d39SAndroid Build Coastguard Worker     false, false,
780*0e209d39SAndroid Build Coastguard Worker     0,
781*0e209d39SAndroid Build Coastguard Worker     0,
782*0e209d39SAndroid Build Coastguard Worker     { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */
783*0e209d39SAndroid Build Coastguard Worker };
784*0e209d39SAndroid Build Coastguard Worker 
785*0e209d39SAndroid Build Coastguard Worker const UConverterSharedData _UTF7Data=
786*0e209d39SAndroid Build Coastguard Worker         UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_UTF7StaticData, &_UTF7Impl);
787*0e209d39SAndroid Build Coastguard Worker 
788*0e209d39SAndroid Build Coastguard Worker /* IMAP mailbox name encoding ----------------------------------------------- */
789*0e209d39SAndroid Build Coastguard Worker 
790*0e209d39SAndroid Build Coastguard Worker /*
791*0e209d39SAndroid Build Coastguard Worker  * RFC 2060: INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1
792*0e209d39SAndroid Build Coastguard Worker  * http://www.ietf.org/rfc/rfc2060.txt
793*0e209d39SAndroid Build Coastguard Worker  *
794*0e209d39SAndroid Build Coastguard Worker  * 5.1.3.  Mailbox International Naming Convention
795*0e209d39SAndroid Build Coastguard Worker  *
796*0e209d39SAndroid Build Coastguard Worker  * By convention, international mailbox names are specified using a
797*0e209d39SAndroid Build Coastguard Worker  * modified version of the UTF-7 encoding described in [UTF-7].  The
798*0e209d39SAndroid Build Coastguard Worker  * purpose of these modifications is to correct the following problems
799*0e209d39SAndroid Build Coastguard Worker  * with UTF-7:
800*0e209d39SAndroid Build Coastguard Worker  *
801*0e209d39SAndroid Build Coastguard Worker  *    1) UTF-7 uses the "+" character for shifting; this conflicts with
802*0e209d39SAndroid Build Coastguard Worker  *       the common use of "+" in mailbox names, in particular USENET
803*0e209d39SAndroid Build Coastguard Worker  *       newsgroup names.
804*0e209d39SAndroid Build Coastguard Worker  *
805*0e209d39SAndroid Build Coastguard Worker  *    2) UTF-7's encoding is BASE64 which uses the "/" character; this
806*0e209d39SAndroid Build Coastguard Worker  *       conflicts with the use of "/" as a popular hierarchy delimiter.
807*0e209d39SAndroid Build Coastguard Worker  *
808*0e209d39SAndroid Build Coastguard Worker  *    3) UTF-7 prohibits the unencoded usage of "\"; this conflicts with
809*0e209d39SAndroid Build Coastguard Worker  *       the use of "\" as a popular hierarchy delimiter.
810*0e209d39SAndroid Build Coastguard Worker  *
811*0e209d39SAndroid Build Coastguard Worker  *    4) UTF-7 prohibits the unencoded usage of "~"; this conflicts with
812*0e209d39SAndroid Build Coastguard Worker  *       the use of "~" in some servers as a home directory indicator.
813*0e209d39SAndroid Build Coastguard Worker  *
814*0e209d39SAndroid Build Coastguard Worker  *    5) UTF-7 permits multiple alternate forms to represent the same
815*0e209d39SAndroid Build Coastguard Worker  *       string; in particular, printable US-ASCII characters can be
816*0e209d39SAndroid Build Coastguard Worker  *       represented in encoded form.
817*0e209d39SAndroid Build Coastguard Worker  *
818*0e209d39SAndroid Build Coastguard Worker  * In modified UTF-7, printable US-ASCII characters except for "&"
819*0e209d39SAndroid Build Coastguard Worker  * represent themselves; that is, characters with octet values 0x20-0x25
820*0e209d39SAndroid Build Coastguard Worker  * and 0x27-0x7e.  The character "&" (0x26) is represented by the two-
821*0e209d39SAndroid Build Coastguard Worker  * octet sequence "&-".
822*0e209d39SAndroid Build Coastguard Worker  *
823*0e209d39SAndroid Build Coastguard Worker  * All other characters (octet values 0x00-0x1f, 0x7f-0xff, and all
824*0e209d39SAndroid Build Coastguard Worker  * Unicode 16-bit octets) are represented in modified BASE64, with a
825*0e209d39SAndroid Build Coastguard Worker  * further modification from [UTF-7] that "," is used instead of "/".
826*0e209d39SAndroid Build Coastguard Worker  * Modified BASE64 MUST NOT be used to represent any printing US-ASCII
827*0e209d39SAndroid Build Coastguard Worker  * character which can represent itself.
828*0e209d39SAndroid Build Coastguard Worker  *
829*0e209d39SAndroid Build Coastguard Worker  * "&" is used to shift to modified BASE64 and "-" to shift back to US-
830*0e209d39SAndroid Build Coastguard Worker  * ASCII.  All names start in US-ASCII, and MUST end in US-ASCII (that
831*0e209d39SAndroid Build Coastguard Worker  * is, a name that ends with a Unicode 16-bit octet MUST end with a "-
832*0e209d39SAndroid Build Coastguard Worker  * ").
833*0e209d39SAndroid Build Coastguard Worker  *
834*0e209d39SAndroid Build Coastguard Worker  * For example, here is a mailbox name which mixes English, Japanese,
835*0e209d39SAndroid Build Coastguard Worker  * and Chinese text: ~peter/mail/&ZeVnLIqe-/&U,BTFw-
836*0e209d39SAndroid Build Coastguard Worker  */
837*0e209d39SAndroid Build Coastguard Worker 
838*0e209d39SAndroid Build Coastguard Worker /*
839*0e209d39SAndroid Build Coastguard Worker  * Tests for US-ASCII characters belonging to character classes
840*0e209d39SAndroid Build Coastguard Worker  * defined in UTF-7.
841*0e209d39SAndroid Build Coastguard Worker  *
842*0e209d39SAndroid Build Coastguard Worker  * Set D (directly encoded characters) consists of the following
843*0e209d39SAndroid Build Coastguard Worker  * characters: the upper and lower case letters A through Z
844*0e209d39SAndroid Build Coastguard Worker  * and a through z, the 10 digits 0-9, and the following nine special
845*0e209d39SAndroid Build Coastguard Worker  * characters (note that "+" and "=" are omitted):
846*0e209d39SAndroid Build Coastguard Worker  *     '(),-./:?
847*0e209d39SAndroid Build Coastguard Worker  *
848*0e209d39SAndroid Build Coastguard Worker  * Set O (optional direct characters) consists of the following
849*0e209d39SAndroid Build Coastguard Worker  * characters (note that "\" and "~" are omitted):
850*0e209d39SAndroid Build Coastguard Worker  *     !"#$%&*;<=>@[]^_`{|}
851*0e209d39SAndroid Build Coastguard Worker  *
852*0e209d39SAndroid Build Coastguard Worker  * According to the rules in RFC 2152, the byte values for the following
853*0e209d39SAndroid Build Coastguard Worker  * US-ASCII characters are not used in UTF-7 and are therefore illegal:
854*0e209d39SAndroid Build Coastguard Worker  * - all C0 control codes except for CR LF TAB
855*0e209d39SAndroid Build Coastguard Worker  * - BACKSLASH
856*0e209d39SAndroid Build Coastguard Worker  * - TILDE
857*0e209d39SAndroid Build Coastguard Worker  * - DEL
858*0e209d39SAndroid Build Coastguard Worker  * - all codes beyond US-ASCII, i.e. all >127
859*0e209d39SAndroid Build Coastguard Worker  */
860*0e209d39SAndroid Build Coastguard Worker 
861*0e209d39SAndroid Build Coastguard Worker /* uses '&' not '+' to start a base64 sequence */
862*0e209d39SAndroid Build Coastguard Worker #define AMPERSAND 0x26
863*0e209d39SAndroid Build Coastguard Worker #define COMMA 0x2c
864*0e209d39SAndroid Build Coastguard Worker #define SLASH 0x2f
865*0e209d39SAndroid Build Coastguard Worker 
866*0e209d39SAndroid Build Coastguard Worker /* legal byte values: all US-ASCII graphic characters 0x20..0x7e */
867*0e209d39SAndroid Build Coastguard Worker #define isLegalIMAP(c) (0x20<=(c) && (c)<=0x7e)
868*0e209d39SAndroid Build Coastguard Worker 
869*0e209d39SAndroid Build Coastguard Worker /* direct-encode all of printable ASCII 0x20..0x7e except '&' 0x26 */
870*0e209d39SAndroid Build Coastguard Worker #define inSetDIMAP(c) (isLegalIMAP(c) && c!=AMPERSAND)
871*0e209d39SAndroid Build Coastguard Worker 
872*0e209d39SAndroid Build Coastguard Worker #define TO_BASE64_IMAP(n) ((n)<63 ? toBase64[n] : COMMA)
873*0e209d39SAndroid Build Coastguard Worker #define FROM_BASE64_IMAP(c) ((c)==COMMA ? 63 : (c)==SLASH ? -1 : fromBase64[c])
874*0e209d39SAndroid Build Coastguard Worker 
875*0e209d39SAndroid Build Coastguard Worker /*
876*0e209d39SAndroid Build Coastguard Worker  * converter status values:
877*0e209d39SAndroid Build Coastguard Worker  *
878*0e209d39SAndroid Build Coastguard Worker  * toUnicodeStatus:
879*0e209d39SAndroid Build Coastguard Worker  *     24 inDirectMode (boolean)
880*0e209d39SAndroid Build Coastguard Worker  * 23..16 base64Counter (-1..7)
881*0e209d39SAndroid Build Coastguard Worker  * 15..0  bits (up to 14 bits incoming base64)
882*0e209d39SAndroid Build Coastguard Worker  *
883*0e209d39SAndroid Build Coastguard Worker  * fromUnicodeStatus:
884*0e209d39SAndroid Build Coastguard Worker  *     24 inDirectMode (boolean)
885*0e209d39SAndroid Build Coastguard Worker  * 23..16 base64Counter (0..2)
886*0e209d39SAndroid Build Coastguard Worker  *  7..0  bits (6 bits outgoing base64)
887*0e209d39SAndroid Build Coastguard Worker  *
888*0e209d39SAndroid Build Coastguard Worker  * ignore bits 31..25
889*0e209d39SAndroid Build Coastguard Worker  */
890*0e209d39SAndroid Build Coastguard Worker 
891*0e209d39SAndroid Build Coastguard Worker U_CDECL_BEGIN
892*0e209d39SAndroid Build Coastguard Worker static void U_CALLCONV
_IMAPToUnicodeWithOffsets(UConverterToUnicodeArgs * pArgs,UErrorCode * pErrorCode)893*0e209d39SAndroid Build Coastguard Worker _IMAPToUnicodeWithOffsets(UConverterToUnicodeArgs *pArgs,
894*0e209d39SAndroid Build Coastguard Worker                           UErrorCode *pErrorCode) {
895*0e209d39SAndroid Build Coastguard Worker     UConverter *cnv;
896*0e209d39SAndroid Build Coastguard Worker     const uint8_t *source, *sourceLimit;
897*0e209d39SAndroid Build Coastguard Worker     char16_t *target;
898*0e209d39SAndroid Build Coastguard Worker     const char16_t *targetLimit;
899*0e209d39SAndroid Build Coastguard Worker     int32_t *offsets;
900*0e209d39SAndroid Build Coastguard Worker 
901*0e209d39SAndroid Build Coastguard Worker     uint8_t *bytes;
902*0e209d39SAndroid Build Coastguard Worker     uint8_t byteIndex;
903*0e209d39SAndroid Build Coastguard Worker 
904*0e209d39SAndroid Build Coastguard Worker     int32_t length, targetCapacity;
905*0e209d39SAndroid Build Coastguard Worker 
906*0e209d39SAndroid Build Coastguard Worker     /* UTF-7 state */
907*0e209d39SAndroid Build Coastguard Worker     uint16_t bits;
908*0e209d39SAndroid Build Coastguard Worker     int8_t base64Counter;
909*0e209d39SAndroid Build Coastguard Worker     UBool inDirectMode;
910*0e209d39SAndroid Build Coastguard Worker 
911*0e209d39SAndroid Build Coastguard Worker     int8_t base64Value;
912*0e209d39SAndroid Build Coastguard Worker 
913*0e209d39SAndroid Build Coastguard Worker     int32_t sourceIndex, nextSourceIndex;
914*0e209d39SAndroid Build Coastguard Worker 
915*0e209d39SAndroid Build Coastguard Worker     char16_t c;
916*0e209d39SAndroid Build Coastguard Worker     uint8_t b;
917*0e209d39SAndroid Build Coastguard Worker 
918*0e209d39SAndroid Build Coastguard Worker     /* set up the local pointers */
919*0e209d39SAndroid Build Coastguard Worker     cnv=pArgs->converter;
920*0e209d39SAndroid Build Coastguard Worker 
921*0e209d39SAndroid Build Coastguard Worker     source=(const uint8_t *)pArgs->source;
922*0e209d39SAndroid Build Coastguard Worker     sourceLimit=(const uint8_t *)pArgs->sourceLimit;
923*0e209d39SAndroid Build Coastguard Worker     target=pArgs->target;
924*0e209d39SAndroid Build Coastguard Worker     targetLimit=pArgs->targetLimit;
925*0e209d39SAndroid Build Coastguard Worker     offsets=pArgs->offsets;
926*0e209d39SAndroid Build Coastguard Worker     /* get the state machine state */
927*0e209d39SAndroid Build Coastguard Worker     {
928*0e209d39SAndroid Build Coastguard Worker         uint32_t status=cnv->toUnicodeStatus;
929*0e209d39SAndroid Build Coastguard Worker         inDirectMode=(UBool)((status>>24)&1);
930*0e209d39SAndroid Build Coastguard Worker         base64Counter=(int8_t)(status>>16);
931*0e209d39SAndroid Build Coastguard Worker         bits=(uint16_t)status;
932*0e209d39SAndroid Build Coastguard Worker     }
933*0e209d39SAndroid Build Coastguard Worker     bytes=cnv->toUBytes;
934*0e209d39SAndroid Build Coastguard Worker     byteIndex=cnv->toULength;
935*0e209d39SAndroid Build Coastguard Worker 
936*0e209d39SAndroid Build Coastguard Worker     /* sourceIndex=-1 if the current character began in the previous buffer */
937*0e209d39SAndroid Build Coastguard Worker     sourceIndex=byteIndex==0 ? 0 : -1;
938*0e209d39SAndroid Build Coastguard Worker     nextSourceIndex=0;
939*0e209d39SAndroid Build Coastguard Worker 
940*0e209d39SAndroid Build Coastguard Worker     if(inDirectMode) {
941*0e209d39SAndroid Build Coastguard Worker directMode:
942*0e209d39SAndroid Build Coastguard Worker         /*
943*0e209d39SAndroid Build Coastguard Worker          * In Direct Mode, US-ASCII characters are encoded directly, i.e.,
944*0e209d39SAndroid Build Coastguard Worker          * with their US-ASCII byte values.
945*0e209d39SAndroid Build Coastguard Worker          * An ampersand starts Unicode (or "escape") Mode.
946*0e209d39SAndroid Build Coastguard Worker          *
947*0e209d39SAndroid Build Coastguard Worker          * In Direct Mode, only the sourceIndex is used.
948*0e209d39SAndroid Build Coastguard Worker          */
949*0e209d39SAndroid Build Coastguard Worker         byteIndex=0;
950*0e209d39SAndroid Build Coastguard Worker         length=(int32_t)(sourceLimit-source);
951*0e209d39SAndroid Build Coastguard Worker         targetCapacity=(int32_t)(targetLimit-target);
952*0e209d39SAndroid Build Coastguard Worker         if(length>targetCapacity) {
953*0e209d39SAndroid Build Coastguard Worker             length=targetCapacity;
954*0e209d39SAndroid Build Coastguard Worker         }
955*0e209d39SAndroid Build Coastguard Worker         while(length>0) {
956*0e209d39SAndroid Build Coastguard Worker             b=*source++;
957*0e209d39SAndroid Build Coastguard Worker             if(!isLegalIMAP(b)) {
958*0e209d39SAndroid Build Coastguard Worker                 /* illegal */
959*0e209d39SAndroid Build Coastguard Worker                 bytes[0]=b;
960*0e209d39SAndroid Build Coastguard Worker                 byteIndex=1;
961*0e209d39SAndroid Build Coastguard Worker                 *pErrorCode=U_ILLEGAL_CHAR_FOUND;
962*0e209d39SAndroid Build Coastguard Worker                 break;
963*0e209d39SAndroid Build Coastguard Worker             } else if(b!=AMPERSAND) {
964*0e209d39SAndroid Build Coastguard Worker                 /* write directly encoded character */
965*0e209d39SAndroid Build Coastguard Worker                 *target++=b;
966*0e209d39SAndroid Build Coastguard Worker                 if(offsets!=nullptr) {
967*0e209d39SAndroid Build Coastguard Worker                     *offsets++=sourceIndex++;
968*0e209d39SAndroid Build Coastguard Worker                 }
969*0e209d39SAndroid Build Coastguard Worker             } else /* AMPERSAND */ {
970*0e209d39SAndroid Build Coastguard Worker                 /* switch to Unicode mode */
971*0e209d39SAndroid Build Coastguard Worker                 nextSourceIndex=++sourceIndex;
972*0e209d39SAndroid Build Coastguard Worker                 inDirectMode=false;
973*0e209d39SAndroid Build Coastguard Worker                 byteIndex=0;
974*0e209d39SAndroid Build Coastguard Worker                 bits=0;
975*0e209d39SAndroid Build Coastguard Worker                 base64Counter=-1;
976*0e209d39SAndroid Build Coastguard Worker                 goto unicodeMode;
977*0e209d39SAndroid Build Coastguard Worker             }
978*0e209d39SAndroid Build Coastguard Worker             --length;
979*0e209d39SAndroid Build Coastguard Worker         }
980*0e209d39SAndroid Build Coastguard Worker         if(source<sourceLimit && target>=targetLimit) {
981*0e209d39SAndroid Build Coastguard Worker             /* target is full */
982*0e209d39SAndroid Build Coastguard Worker             *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
983*0e209d39SAndroid Build Coastguard Worker         }
984*0e209d39SAndroid Build Coastguard Worker     } else {
985*0e209d39SAndroid Build Coastguard Worker unicodeMode:
986*0e209d39SAndroid Build Coastguard Worker         /*
987*0e209d39SAndroid Build Coastguard Worker          * In Unicode (or "escape") Mode, UTF-16BE is base64-encoded.
988*0e209d39SAndroid Build Coastguard Worker          * The base64 sequence ends with any character that is not in the base64 alphabet.
989*0e209d39SAndroid Build Coastguard Worker          * A terminating minus sign is consumed.
990*0e209d39SAndroid Build Coastguard Worker          * US-ASCII must not be base64-ed.
991*0e209d39SAndroid Build Coastguard Worker          *
992*0e209d39SAndroid Build Coastguard Worker          * In Unicode Mode, the sourceIndex has the index to the start of the current
993*0e209d39SAndroid Build Coastguard Worker          * base64 bytes, while nextSourceIndex is precisely parallel to source,
994*0e209d39SAndroid Build Coastguard Worker          * keeping the index to the following byte.
995*0e209d39SAndroid Build Coastguard Worker          * Note that in 2 out of 3 cases, UChars overlap within a base64 byte.
996*0e209d39SAndroid Build Coastguard Worker          */
997*0e209d39SAndroid Build Coastguard Worker         while(source<sourceLimit) {
998*0e209d39SAndroid Build Coastguard Worker             if(target<targetLimit) {
999*0e209d39SAndroid Build Coastguard Worker                 bytes[byteIndex++]=b=*source++;
1000*0e209d39SAndroid Build Coastguard Worker                 ++nextSourceIndex;
1001*0e209d39SAndroid Build Coastguard Worker                 if(b>0x7e) {
1002*0e209d39SAndroid Build Coastguard Worker                     /* illegal - test other illegal US-ASCII values by base64Value==-3 */
1003*0e209d39SAndroid Build Coastguard Worker                     inDirectMode=true;
1004*0e209d39SAndroid Build Coastguard Worker                     *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1005*0e209d39SAndroid Build Coastguard Worker                     break;
1006*0e209d39SAndroid Build Coastguard Worker                 } else if((base64Value=FROM_BASE64_IMAP(b))>=0) {
1007*0e209d39SAndroid Build Coastguard Worker                     /* collect base64 bytes into UChars */
1008*0e209d39SAndroid Build Coastguard Worker                     switch(base64Counter) {
1009*0e209d39SAndroid Build Coastguard Worker                     case -1: /* -1 is immediately after the & */
1010*0e209d39SAndroid Build Coastguard Worker                     case 0:
1011*0e209d39SAndroid Build Coastguard Worker                         bits=base64Value;
1012*0e209d39SAndroid Build Coastguard Worker                         base64Counter=1;
1013*0e209d39SAndroid Build Coastguard Worker                         break;
1014*0e209d39SAndroid Build Coastguard Worker                     case 1:
1015*0e209d39SAndroid Build Coastguard Worker                     case 3:
1016*0e209d39SAndroid Build Coastguard Worker                     case 4:
1017*0e209d39SAndroid Build Coastguard Worker                     case 6:
1018*0e209d39SAndroid Build Coastguard Worker                         bits=(uint16_t)((bits<<6)|base64Value);
1019*0e209d39SAndroid Build Coastguard Worker                         ++base64Counter;
1020*0e209d39SAndroid Build Coastguard Worker                         break;
1021*0e209d39SAndroid Build Coastguard Worker                     case 2:
1022*0e209d39SAndroid Build Coastguard Worker                         c=(char16_t)((bits<<4)|(base64Value>>2));
1023*0e209d39SAndroid Build Coastguard Worker                         if(isLegalIMAP(c)) {
1024*0e209d39SAndroid Build Coastguard Worker                             /* illegal */
1025*0e209d39SAndroid Build Coastguard Worker                             inDirectMode=true;
1026*0e209d39SAndroid Build Coastguard Worker                             *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1027*0e209d39SAndroid Build Coastguard Worker                             goto endloop;
1028*0e209d39SAndroid Build Coastguard Worker                         }
1029*0e209d39SAndroid Build Coastguard Worker                         *target++=c;
1030*0e209d39SAndroid Build Coastguard Worker                         if(offsets!=nullptr) {
1031*0e209d39SAndroid Build Coastguard Worker                             *offsets++=sourceIndex;
1032*0e209d39SAndroid Build Coastguard Worker                             sourceIndex=nextSourceIndex-1;
1033*0e209d39SAndroid Build Coastguard Worker                         }
1034*0e209d39SAndroid Build Coastguard Worker                         bytes[0]=b; /* keep this byte in case an error occurs */
1035*0e209d39SAndroid Build Coastguard Worker                         byteIndex=1;
1036*0e209d39SAndroid Build Coastguard Worker                         bits=(uint16_t)(base64Value&3);
1037*0e209d39SAndroid Build Coastguard Worker                         base64Counter=3;
1038*0e209d39SAndroid Build Coastguard Worker                         break;
1039*0e209d39SAndroid Build Coastguard Worker                     case 5:
1040*0e209d39SAndroid Build Coastguard Worker                         c=(char16_t)((bits<<2)|(base64Value>>4));
1041*0e209d39SAndroid Build Coastguard Worker                         if(isLegalIMAP(c)) {
1042*0e209d39SAndroid Build Coastguard Worker                             /* illegal */
1043*0e209d39SAndroid Build Coastguard Worker                             inDirectMode=true;
1044*0e209d39SAndroid Build Coastguard Worker                             *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1045*0e209d39SAndroid Build Coastguard Worker                             goto endloop;
1046*0e209d39SAndroid Build Coastguard Worker                         }
1047*0e209d39SAndroid Build Coastguard Worker                         *target++=c;
1048*0e209d39SAndroid Build Coastguard Worker                         if(offsets!=nullptr) {
1049*0e209d39SAndroid Build Coastguard Worker                             *offsets++=sourceIndex;
1050*0e209d39SAndroid Build Coastguard Worker                             sourceIndex=nextSourceIndex-1;
1051*0e209d39SAndroid Build Coastguard Worker                         }
1052*0e209d39SAndroid Build Coastguard Worker                         bytes[0]=b; /* keep this byte in case an error occurs */
1053*0e209d39SAndroid Build Coastguard Worker                         byteIndex=1;
1054*0e209d39SAndroid Build Coastguard Worker                         bits=(uint16_t)(base64Value&15);
1055*0e209d39SAndroid Build Coastguard Worker                         base64Counter=6;
1056*0e209d39SAndroid Build Coastguard Worker                         break;
1057*0e209d39SAndroid Build Coastguard Worker                     case 7:
1058*0e209d39SAndroid Build Coastguard Worker                         c=(char16_t)((bits<<6)|base64Value);
1059*0e209d39SAndroid Build Coastguard Worker                         if(isLegalIMAP(c)) {
1060*0e209d39SAndroid Build Coastguard Worker                             /* illegal */
1061*0e209d39SAndroid Build Coastguard Worker                             inDirectMode=true;
1062*0e209d39SAndroid Build Coastguard Worker                             *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1063*0e209d39SAndroid Build Coastguard Worker                             goto endloop;
1064*0e209d39SAndroid Build Coastguard Worker                         }
1065*0e209d39SAndroid Build Coastguard Worker                         *target++=c;
1066*0e209d39SAndroid Build Coastguard Worker                         if(offsets!=nullptr) {
1067*0e209d39SAndroid Build Coastguard Worker                             *offsets++=sourceIndex;
1068*0e209d39SAndroid Build Coastguard Worker                             sourceIndex=nextSourceIndex;
1069*0e209d39SAndroid Build Coastguard Worker                         }
1070*0e209d39SAndroid Build Coastguard Worker                         byteIndex=0;
1071*0e209d39SAndroid Build Coastguard Worker                         bits=0;
1072*0e209d39SAndroid Build Coastguard Worker                         base64Counter=0;
1073*0e209d39SAndroid Build Coastguard Worker                         break;
1074*0e209d39SAndroid Build Coastguard Worker                     default:
1075*0e209d39SAndroid Build Coastguard Worker                         /* will never occur */
1076*0e209d39SAndroid Build Coastguard Worker                         break;
1077*0e209d39SAndroid Build Coastguard Worker                     }
1078*0e209d39SAndroid Build Coastguard Worker                 } else if(base64Value==-2) {
1079*0e209d39SAndroid Build Coastguard Worker                     /* minus sign terminates the base64 sequence */
1080*0e209d39SAndroid Build Coastguard Worker                     inDirectMode=true;
1081*0e209d39SAndroid Build Coastguard Worker                     if(base64Counter==-1) {
1082*0e209d39SAndroid Build Coastguard Worker                         /* &- i.e. a minus immediately following an ampersand */
1083*0e209d39SAndroid Build Coastguard Worker                         *target++=AMPERSAND;
1084*0e209d39SAndroid Build Coastguard Worker                         if(offsets!=nullptr) {
1085*0e209d39SAndroid Build Coastguard Worker                             *offsets++=sourceIndex-1;
1086*0e209d39SAndroid Build Coastguard Worker                         }
1087*0e209d39SAndroid Build Coastguard Worker                     } else {
1088*0e209d39SAndroid Build Coastguard Worker                         /* absorb the minus and leave the Unicode Mode */
1089*0e209d39SAndroid Build Coastguard Worker                         if(bits!=0 || (base64Counter!=0 && base64Counter!=3 && base64Counter!=6)) {
1090*0e209d39SAndroid Build Coastguard Worker                             /* bits are illegally left over, a char16_t is incomplete */
1091*0e209d39SAndroid Build Coastguard Worker                             /* base64Counter other than 0, 3, 6 means non-minimal zero-padding, also illegal */
1092*0e209d39SAndroid Build Coastguard Worker                             *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1093*0e209d39SAndroid Build Coastguard Worker                             break;
1094*0e209d39SAndroid Build Coastguard Worker                         }
1095*0e209d39SAndroid Build Coastguard Worker                     }
1096*0e209d39SAndroid Build Coastguard Worker                     sourceIndex=nextSourceIndex;
1097*0e209d39SAndroid Build Coastguard Worker                     goto directMode;
1098*0e209d39SAndroid Build Coastguard Worker                 } else {
1099*0e209d39SAndroid Build Coastguard Worker                     if(base64Counter==-1) {
1100*0e209d39SAndroid Build Coastguard Worker                         /* illegal: & immediately followed by something other than base64 or minus sign */
1101*0e209d39SAndroid Build Coastguard Worker                         /* include the ampersand in the reported sequence */
1102*0e209d39SAndroid Build Coastguard Worker                         --sourceIndex;
1103*0e209d39SAndroid Build Coastguard Worker                         bytes[0]=AMPERSAND;
1104*0e209d39SAndroid Build Coastguard Worker                         bytes[1]=b;
1105*0e209d39SAndroid Build Coastguard Worker                         byteIndex=2;
1106*0e209d39SAndroid Build Coastguard Worker                     }
1107*0e209d39SAndroid Build Coastguard Worker                     /* base64Value==-1 for characters that are illegal only in Unicode mode */
1108*0e209d39SAndroid Build Coastguard Worker                     /* base64Value==-3 for illegal characters */
1109*0e209d39SAndroid Build Coastguard Worker                     /* illegal */
1110*0e209d39SAndroid Build Coastguard Worker                     inDirectMode=true;
1111*0e209d39SAndroid Build Coastguard Worker                     *pErrorCode=U_ILLEGAL_CHAR_FOUND;
1112*0e209d39SAndroid Build Coastguard Worker                     break;
1113*0e209d39SAndroid Build Coastguard Worker                 }
1114*0e209d39SAndroid Build Coastguard Worker             } else {
1115*0e209d39SAndroid Build Coastguard Worker                 /* target is full */
1116*0e209d39SAndroid Build Coastguard Worker                 *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1117*0e209d39SAndroid Build Coastguard Worker                 break;
1118*0e209d39SAndroid Build Coastguard Worker             }
1119*0e209d39SAndroid Build Coastguard Worker         }
1120*0e209d39SAndroid Build Coastguard Worker     }
1121*0e209d39SAndroid Build Coastguard Worker endloop:
1122*0e209d39SAndroid Build Coastguard Worker 
1123*0e209d39SAndroid Build Coastguard Worker     /*
1124*0e209d39SAndroid Build Coastguard Worker      * the end of the input stream and detection of truncated input
1125*0e209d39SAndroid Build Coastguard Worker      * are handled by the framework, but here we must check if we are in Unicode
1126*0e209d39SAndroid Build Coastguard Worker      * mode and byteIndex==0 because we must end in direct mode
1127*0e209d39SAndroid Build Coastguard Worker      *
1128*0e209d39SAndroid Build Coastguard Worker      * conditions:
1129*0e209d39SAndroid Build Coastguard Worker      *   successful
1130*0e209d39SAndroid Build Coastguard Worker      *   in Unicode mode and byteIndex==0
1131*0e209d39SAndroid Build Coastguard Worker      *   end of input and no truncated input
1132*0e209d39SAndroid Build Coastguard Worker      */
1133*0e209d39SAndroid Build Coastguard Worker     if( U_SUCCESS(*pErrorCode) &&
1134*0e209d39SAndroid Build Coastguard Worker         !inDirectMode && byteIndex==0 &&
1135*0e209d39SAndroid Build Coastguard Worker         pArgs->flush && source>=sourceLimit
1136*0e209d39SAndroid Build Coastguard Worker     ) {
1137*0e209d39SAndroid Build Coastguard Worker         if(base64Counter==-1) {
1138*0e209d39SAndroid Build Coastguard Worker             /* & at the very end of the input */
1139*0e209d39SAndroid Build Coastguard Worker             /* make the ampersand the reported sequence */
1140*0e209d39SAndroid Build Coastguard Worker             bytes[0]=AMPERSAND;
1141*0e209d39SAndroid Build Coastguard Worker             byteIndex=1;
1142*0e209d39SAndroid Build Coastguard Worker         }
1143*0e209d39SAndroid Build Coastguard Worker         /* else if(base64Counter!=-1) byteIndex remains 0 because there is no particular byte sequence */
1144*0e209d39SAndroid Build Coastguard Worker 
1145*0e209d39SAndroid Build Coastguard Worker         inDirectMode=true; /* avoid looping */
1146*0e209d39SAndroid Build Coastguard Worker         *pErrorCode=U_TRUNCATED_CHAR_FOUND;
1147*0e209d39SAndroid Build Coastguard Worker     }
1148*0e209d39SAndroid Build Coastguard Worker 
1149*0e209d39SAndroid Build Coastguard Worker     /* set the converter state back into UConverter */
1150*0e209d39SAndroid Build Coastguard Worker     cnv->toUnicodeStatus=((uint32_t)inDirectMode<<24)|((uint32_t)((uint8_t)base64Counter)<<16)|(uint32_t)bits;
1151*0e209d39SAndroid Build Coastguard Worker     cnv->toULength=byteIndex;
1152*0e209d39SAndroid Build Coastguard Worker 
1153*0e209d39SAndroid Build Coastguard Worker     /* write back the updated pointers */
1154*0e209d39SAndroid Build Coastguard Worker     pArgs->source=(const char *)source;
1155*0e209d39SAndroid Build Coastguard Worker     pArgs->target=target;
1156*0e209d39SAndroid Build Coastguard Worker     pArgs->offsets=offsets;
1157*0e209d39SAndroid Build Coastguard Worker }
1158*0e209d39SAndroid Build Coastguard Worker 
1159*0e209d39SAndroid Build Coastguard Worker static void U_CALLCONV
_IMAPFromUnicodeWithOffsets(UConverterFromUnicodeArgs * pArgs,UErrorCode * pErrorCode)1160*0e209d39SAndroid Build Coastguard Worker _IMAPFromUnicodeWithOffsets(UConverterFromUnicodeArgs *pArgs,
1161*0e209d39SAndroid Build Coastguard Worker                             UErrorCode *pErrorCode) {
1162*0e209d39SAndroid Build Coastguard Worker     UConverter *cnv;
1163*0e209d39SAndroid Build Coastguard Worker     const char16_t *source, *sourceLimit;
1164*0e209d39SAndroid Build Coastguard Worker     uint8_t *target, *targetLimit;
1165*0e209d39SAndroid Build Coastguard Worker     int32_t *offsets;
1166*0e209d39SAndroid Build Coastguard Worker 
1167*0e209d39SAndroid Build Coastguard Worker     int32_t length, targetCapacity, sourceIndex;
1168*0e209d39SAndroid Build Coastguard Worker     char16_t c;
1169*0e209d39SAndroid Build Coastguard Worker     uint8_t b;
1170*0e209d39SAndroid Build Coastguard Worker 
1171*0e209d39SAndroid Build Coastguard Worker     /* UTF-7 state */
1172*0e209d39SAndroid Build Coastguard Worker     uint8_t bits;
1173*0e209d39SAndroid Build Coastguard Worker     int8_t base64Counter;
1174*0e209d39SAndroid Build Coastguard Worker     UBool inDirectMode;
1175*0e209d39SAndroid Build Coastguard Worker 
1176*0e209d39SAndroid Build Coastguard Worker     /* set up the local pointers */
1177*0e209d39SAndroid Build Coastguard Worker     cnv=pArgs->converter;
1178*0e209d39SAndroid Build Coastguard Worker 
1179*0e209d39SAndroid Build Coastguard Worker     /* set up the local pointers */
1180*0e209d39SAndroid Build Coastguard Worker     source=pArgs->source;
1181*0e209d39SAndroid Build Coastguard Worker     sourceLimit=pArgs->sourceLimit;
1182*0e209d39SAndroid Build Coastguard Worker     target=(uint8_t *)pArgs->target;
1183*0e209d39SAndroid Build Coastguard Worker     targetLimit=(uint8_t *)pArgs->targetLimit;
1184*0e209d39SAndroid Build Coastguard Worker     offsets=pArgs->offsets;
1185*0e209d39SAndroid Build Coastguard Worker 
1186*0e209d39SAndroid Build Coastguard Worker     /* get the state machine state */
1187*0e209d39SAndroid Build Coastguard Worker     {
1188*0e209d39SAndroid Build Coastguard Worker         uint32_t status=cnv->fromUnicodeStatus;
1189*0e209d39SAndroid Build Coastguard Worker         inDirectMode=(UBool)((status>>24)&1);
1190*0e209d39SAndroid Build Coastguard Worker         base64Counter=(int8_t)(status>>16);
1191*0e209d39SAndroid Build Coastguard Worker         bits=(uint8_t)status;
1192*0e209d39SAndroid Build Coastguard Worker     }
1193*0e209d39SAndroid Build Coastguard Worker 
1194*0e209d39SAndroid Build Coastguard Worker     /* UTF-7 always encodes UTF-16 code units, therefore we need only a simple sourceIndex */
1195*0e209d39SAndroid Build Coastguard Worker     sourceIndex=0;
1196*0e209d39SAndroid Build Coastguard Worker 
1197*0e209d39SAndroid Build Coastguard Worker     if(inDirectMode) {
1198*0e209d39SAndroid Build Coastguard Worker directMode:
1199*0e209d39SAndroid Build Coastguard Worker         length=(int32_t)(sourceLimit-source);
1200*0e209d39SAndroid Build Coastguard Worker         targetCapacity=(int32_t)(targetLimit-target);
1201*0e209d39SAndroid Build Coastguard Worker         if(length>targetCapacity) {
1202*0e209d39SAndroid Build Coastguard Worker             length=targetCapacity;
1203*0e209d39SAndroid Build Coastguard Worker         }
1204*0e209d39SAndroid Build Coastguard Worker         while(length>0) {
1205*0e209d39SAndroid Build Coastguard Worker             c=*source++;
1206*0e209d39SAndroid Build Coastguard Worker             /* encode 0x20..0x7e except '&' directly */
1207*0e209d39SAndroid Build Coastguard Worker             if(inSetDIMAP(c)) {
1208*0e209d39SAndroid Build Coastguard Worker                 /* encode directly */
1209*0e209d39SAndroid Build Coastguard Worker                 *target++=(uint8_t)c;
1210*0e209d39SAndroid Build Coastguard Worker                 if(offsets!=nullptr) {
1211*0e209d39SAndroid Build Coastguard Worker                     *offsets++=sourceIndex++;
1212*0e209d39SAndroid Build Coastguard Worker                 }
1213*0e209d39SAndroid Build Coastguard Worker             } else if(c==AMPERSAND) {
1214*0e209d39SAndroid Build Coastguard Worker                 /* output &- for & */
1215*0e209d39SAndroid Build Coastguard Worker                 *target++=AMPERSAND;
1216*0e209d39SAndroid Build Coastguard Worker                 if(target<targetLimit) {
1217*0e209d39SAndroid Build Coastguard Worker                     *target++=MINUS;
1218*0e209d39SAndroid Build Coastguard Worker                     if(offsets!=nullptr) {
1219*0e209d39SAndroid Build Coastguard Worker                         *offsets++=sourceIndex;
1220*0e209d39SAndroid Build Coastguard Worker                         *offsets++=sourceIndex++;
1221*0e209d39SAndroid Build Coastguard Worker                     }
1222*0e209d39SAndroid Build Coastguard Worker                     /* realign length and targetCapacity */
1223*0e209d39SAndroid Build Coastguard Worker                     goto directMode;
1224*0e209d39SAndroid Build Coastguard Worker                 } else {
1225*0e209d39SAndroid Build Coastguard Worker                     if(offsets!=nullptr) {
1226*0e209d39SAndroid Build Coastguard Worker                         *offsets++=sourceIndex++;
1227*0e209d39SAndroid Build Coastguard Worker                     }
1228*0e209d39SAndroid Build Coastguard Worker                     cnv->charErrorBuffer[0]=MINUS;
1229*0e209d39SAndroid Build Coastguard Worker                     cnv->charErrorBufferLength=1;
1230*0e209d39SAndroid Build Coastguard Worker                     *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1231*0e209d39SAndroid Build Coastguard Worker                     break;
1232*0e209d39SAndroid Build Coastguard Worker                 }
1233*0e209d39SAndroid Build Coastguard Worker             } else {
1234*0e209d39SAndroid Build Coastguard Worker                 /* un-read this character and switch to Unicode Mode */
1235*0e209d39SAndroid Build Coastguard Worker                 --source;
1236*0e209d39SAndroid Build Coastguard Worker                 *target++=AMPERSAND;
1237*0e209d39SAndroid Build Coastguard Worker                 if(offsets!=nullptr) {
1238*0e209d39SAndroid Build Coastguard Worker                     *offsets++=sourceIndex;
1239*0e209d39SAndroid Build Coastguard Worker                 }
1240*0e209d39SAndroid Build Coastguard Worker                 inDirectMode=false;
1241*0e209d39SAndroid Build Coastguard Worker                 base64Counter=0;
1242*0e209d39SAndroid Build Coastguard Worker                 goto unicodeMode;
1243*0e209d39SAndroid Build Coastguard Worker             }
1244*0e209d39SAndroid Build Coastguard Worker             --length;
1245*0e209d39SAndroid Build Coastguard Worker         }
1246*0e209d39SAndroid Build Coastguard Worker         if(source<sourceLimit && target>=targetLimit) {
1247*0e209d39SAndroid Build Coastguard Worker             /* target is full */
1248*0e209d39SAndroid Build Coastguard Worker             *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1249*0e209d39SAndroid Build Coastguard Worker         }
1250*0e209d39SAndroid Build Coastguard Worker     } else {
1251*0e209d39SAndroid Build Coastguard Worker unicodeMode:
1252*0e209d39SAndroid Build Coastguard Worker         while(source<sourceLimit) {
1253*0e209d39SAndroid Build Coastguard Worker             if(target<targetLimit) {
1254*0e209d39SAndroid Build Coastguard Worker                 c=*source++;
1255*0e209d39SAndroid Build Coastguard Worker                 if(isLegalIMAP(c)) {
1256*0e209d39SAndroid Build Coastguard Worker                     /* encode directly */
1257*0e209d39SAndroid Build Coastguard Worker                     inDirectMode=true;
1258*0e209d39SAndroid Build Coastguard Worker 
1259*0e209d39SAndroid Build Coastguard Worker                     /* trick: back out this character to make this easier */
1260*0e209d39SAndroid Build Coastguard Worker                     --source;
1261*0e209d39SAndroid Build Coastguard Worker 
1262*0e209d39SAndroid Build Coastguard Worker                     /* terminate the base64 sequence */
1263*0e209d39SAndroid Build Coastguard Worker                     if(base64Counter!=0) {
1264*0e209d39SAndroid Build Coastguard Worker                         /* write remaining bits for the previous character */
1265*0e209d39SAndroid Build Coastguard Worker                         *target++=TO_BASE64_IMAP(bits);
1266*0e209d39SAndroid Build Coastguard Worker                         if(offsets!=nullptr) {
1267*0e209d39SAndroid Build Coastguard Worker                             *offsets++=sourceIndex-1;
1268*0e209d39SAndroid Build Coastguard Worker                         }
1269*0e209d39SAndroid Build Coastguard Worker                     }
1270*0e209d39SAndroid Build Coastguard Worker                     /* need to terminate with a minus */
1271*0e209d39SAndroid Build Coastguard Worker                     if(target<targetLimit) {
1272*0e209d39SAndroid Build Coastguard Worker                         *target++=MINUS;
1273*0e209d39SAndroid Build Coastguard Worker                         if(offsets!=nullptr) {
1274*0e209d39SAndroid Build Coastguard Worker                             *offsets++=sourceIndex-1;
1275*0e209d39SAndroid Build Coastguard Worker                         }
1276*0e209d39SAndroid Build Coastguard Worker                     } else {
1277*0e209d39SAndroid Build Coastguard Worker                         cnv->charErrorBuffer[0]=MINUS;
1278*0e209d39SAndroid Build Coastguard Worker                         cnv->charErrorBufferLength=1;
1279*0e209d39SAndroid Build Coastguard Worker                         *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1280*0e209d39SAndroid Build Coastguard Worker                         break;
1281*0e209d39SAndroid Build Coastguard Worker                     }
1282*0e209d39SAndroid Build Coastguard Worker                     goto directMode;
1283*0e209d39SAndroid Build Coastguard Worker                 } else {
1284*0e209d39SAndroid Build Coastguard Worker                     /*
1285*0e209d39SAndroid Build Coastguard Worker                      * base64 this character:
1286*0e209d39SAndroid Build Coastguard Worker                      * Output 2 or 3 base64 bytes for the remaining bits of the previous character
1287*0e209d39SAndroid Build Coastguard Worker                      * and the bits of this character, each implicitly in UTF-16BE.
1288*0e209d39SAndroid Build Coastguard Worker                      *
1289*0e209d39SAndroid Build Coastguard Worker                      * Here, bits is an 8-bit variable because only 6 bits need to be kept from one
1290*0e209d39SAndroid Build Coastguard Worker                      * character to the next. The actual 2 or 4 bits are shifted to the left edge
1291*0e209d39SAndroid Build Coastguard Worker                      * of the 6-bits field 5..0 to make the termination of the base64 sequence easier.
1292*0e209d39SAndroid Build Coastguard Worker                      */
1293*0e209d39SAndroid Build Coastguard Worker                     switch(base64Counter) {
1294*0e209d39SAndroid Build Coastguard Worker                     case 0:
1295*0e209d39SAndroid Build Coastguard Worker                         b=(uint8_t)(c>>10);
1296*0e209d39SAndroid Build Coastguard Worker                         *target++=TO_BASE64_IMAP(b);
1297*0e209d39SAndroid Build Coastguard Worker                         if(target<targetLimit) {
1298*0e209d39SAndroid Build Coastguard Worker                             b=(uint8_t)((c>>4)&0x3f);
1299*0e209d39SAndroid Build Coastguard Worker                             *target++=TO_BASE64_IMAP(b);
1300*0e209d39SAndroid Build Coastguard Worker                             if(offsets!=nullptr) {
1301*0e209d39SAndroid Build Coastguard Worker                                 *offsets++=sourceIndex;
1302*0e209d39SAndroid Build Coastguard Worker                                 *offsets++=sourceIndex++;
1303*0e209d39SAndroid Build Coastguard Worker                             }
1304*0e209d39SAndroid Build Coastguard Worker                         } else {
1305*0e209d39SAndroid Build Coastguard Worker                             if(offsets!=nullptr) {
1306*0e209d39SAndroid Build Coastguard Worker                                 *offsets++=sourceIndex++;
1307*0e209d39SAndroid Build Coastguard Worker                             }
1308*0e209d39SAndroid Build Coastguard Worker                             b=(uint8_t)((c>>4)&0x3f);
1309*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBuffer[0]=TO_BASE64_IMAP(b);
1310*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBufferLength=1;
1311*0e209d39SAndroid Build Coastguard Worker                             *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1312*0e209d39SAndroid Build Coastguard Worker                         }
1313*0e209d39SAndroid Build Coastguard Worker                         bits=(uint8_t)((c&15)<<2);
1314*0e209d39SAndroid Build Coastguard Worker                         base64Counter=1;
1315*0e209d39SAndroid Build Coastguard Worker                         break;
1316*0e209d39SAndroid Build Coastguard Worker                     case 1:
1317*0e209d39SAndroid Build Coastguard Worker                         b=(uint8_t)(bits|(c>>14));
1318*0e209d39SAndroid Build Coastguard Worker                         *target++=TO_BASE64_IMAP(b);
1319*0e209d39SAndroid Build Coastguard Worker                         if(target<targetLimit) {
1320*0e209d39SAndroid Build Coastguard Worker                             b=(uint8_t)((c>>8)&0x3f);
1321*0e209d39SAndroid Build Coastguard Worker                             *target++=TO_BASE64_IMAP(b);
1322*0e209d39SAndroid Build Coastguard Worker                             if(target<targetLimit) {
1323*0e209d39SAndroid Build Coastguard Worker                                 b=(uint8_t)((c>>2)&0x3f);
1324*0e209d39SAndroid Build Coastguard Worker                                 *target++=TO_BASE64_IMAP(b);
1325*0e209d39SAndroid Build Coastguard Worker                                 if(offsets!=nullptr) {
1326*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex;
1327*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex;
1328*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex++;
1329*0e209d39SAndroid Build Coastguard Worker                                 }
1330*0e209d39SAndroid Build Coastguard Worker                             } else {
1331*0e209d39SAndroid Build Coastguard Worker                                 if(offsets!=nullptr) {
1332*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex;
1333*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex++;
1334*0e209d39SAndroid Build Coastguard Worker                                 }
1335*0e209d39SAndroid Build Coastguard Worker                                 b=(uint8_t)((c>>2)&0x3f);
1336*0e209d39SAndroid Build Coastguard Worker                                 cnv->charErrorBuffer[0]=TO_BASE64_IMAP(b);
1337*0e209d39SAndroid Build Coastguard Worker                                 cnv->charErrorBufferLength=1;
1338*0e209d39SAndroid Build Coastguard Worker                                 *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1339*0e209d39SAndroid Build Coastguard Worker                             }
1340*0e209d39SAndroid Build Coastguard Worker                         } else {
1341*0e209d39SAndroid Build Coastguard Worker                             if(offsets!=nullptr) {
1342*0e209d39SAndroid Build Coastguard Worker                                 *offsets++=sourceIndex++;
1343*0e209d39SAndroid Build Coastguard Worker                             }
1344*0e209d39SAndroid Build Coastguard Worker                             b=(uint8_t)((c>>8)&0x3f);
1345*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBuffer[0]=TO_BASE64_IMAP(b);
1346*0e209d39SAndroid Build Coastguard Worker                             b=(uint8_t)((c>>2)&0x3f);
1347*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBuffer[1]=TO_BASE64_IMAP(b);
1348*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBufferLength=2;
1349*0e209d39SAndroid Build Coastguard Worker                             *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1350*0e209d39SAndroid Build Coastguard Worker                         }
1351*0e209d39SAndroid Build Coastguard Worker                         bits=(uint8_t)((c&3)<<4);
1352*0e209d39SAndroid Build Coastguard Worker                         base64Counter=2;
1353*0e209d39SAndroid Build Coastguard Worker                         break;
1354*0e209d39SAndroid Build Coastguard Worker                     case 2:
1355*0e209d39SAndroid Build Coastguard Worker                         b=(uint8_t)(bits|(c>>12));
1356*0e209d39SAndroid Build Coastguard Worker                         *target++=TO_BASE64_IMAP(b);
1357*0e209d39SAndroid Build Coastguard Worker                         if(target<targetLimit) {
1358*0e209d39SAndroid Build Coastguard Worker                             b=(uint8_t)((c>>6)&0x3f);
1359*0e209d39SAndroid Build Coastguard Worker                             *target++=TO_BASE64_IMAP(b);
1360*0e209d39SAndroid Build Coastguard Worker                             if(target<targetLimit) {
1361*0e209d39SAndroid Build Coastguard Worker                                 b=(uint8_t)(c&0x3f);
1362*0e209d39SAndroid Build Coastguard Worker                                 *target++=TO_BASE64_IMAP(b);
1363*0e209d39SAndroid Build Coastguard Worker                                 if(offsets!=nullptr) {
1364*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex;
1365*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex;
1366*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex++;
1367*0e209d39SAndroid Build Coastguard Worker                                 }
1368*0e209d39SAndroid Build Coastguard Worker                             } else {
1369*0e209d39SAndroid Build Coastguard Worker                                 if(offsets!=nullptr) {
1370*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex;
1371*0e209d39SAndroid Build Coastguard Worker                                     *offsets++=sourceIndex++;
1372*0e209d39SAndroid Build Coastguard Worker                                 }
1373*0e209d39SAndroid Build Coastguard Worker                                 b=(uint8_t)(c&0x3f);
1374*0e209d39SAndroid Build Coastguard Worker                                 cnv->charErrorBuffer[0]=TO_BASE64_IMAP(b);
1375*0e209d39SAndroid Build Coastguard Worker                                 cnv->charErrorBufferLength=1;
1376*0e209d39SAndroid Build Coastguard Worker                                 *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1377*0e209d39SAndroid Build Coastguard Worker                             }
1378*0e209d39SAndroid Build Coastguard Worker                         } else {
1379*0e209d39SAndroid Build Coastguard Worker                             if(offsets!=nullptr) {
1380*0e209d39SAndroid Build Coastguard Worker                                 *offsets++=sourceIndex++;
1381*0e209d39SAndroid Build Coastguard Worker                             }
1382*0e209d39SAndroid Build Coastguard Worker                             b=(uint8_t)((c>>6)&0x3f);
1383*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBuffer[0]=TO_BASE64_IMAP(b);
1384*0e209d39SAndroid Build Coastguard Worker                             b=(uint8_t)(c&0x3f);
1385*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBuffer[1]=TO_BASE64_IMAP(b);
1386*0e209d39SAndroid Build Coastguard Worker                             cnv->charErrorBufferLength=2;
1387*0e209d39SAndroid Build Coastguard Worker                             *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1388*0e209d39SAndroid Build Coastguard Worker                         }
1389*0e209d39SAndroid Build Coastguard Worker                         bits=0;
1390*0e209d39SAndroid Build Coastguard Worker                         base64Counter=0;
1391*0e209d39SAndroid Build Coastguard Worker                         break;
1392*0e209d39SAndroid Build Coastguard Worker                     default:
1393*0e209d39SAndroid Build Coastguard Worker                         /* will never occur */
1394*0e209d39SAndroid Build Coastguard Worker                         break;
1395*0e209d39SAndroid Build Coastguard Worker                     }
1396*0e209d39SAndroid Build Coastguard Worker                 }
1397*0e209d39SAndroid Build Coastguard Worker             } else {
1398*0e209d39SAndroid Build Coastguard Worker                 /* target is full */
1399*0e209d39SAndroid Build Coastguard Worker                 *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1400*0e209d39SAndroid Build Coastguard Worker                 break;
1401*0e209d39SAndroid Build Coastguard Worker             }
1402*0e209d39SAndroid Build Coastguard Worker         }
1403*0e209d39SAndroid Build Coastguard Worker     }
1404*0e209d39SAndroid Build Coastguard Worker 
1405*0e209d39SAndroid Build Coastguard Worker     if(pArgs->flush && source>=sourceLimit) {
1406*0e209d39SAndroid Build Coastguard Worker         /* flush remaining bits to the target */
1407*0e209d39SAndroid Build Coastguard Worker         if(!inDirectMode) {
1408*0e209d39SAndroid Build Coastguard Worker             if(base64Counter!=0) {
1409*0e209d39SAndroid Build Coastguard Worker                 if(target<targetLimit) {
1410*0e209d39SAndroid Build Coastguard Worker                     *target++=TO_BASE64_IMAP(bits);
1411*0e209d39SAndroid Build Coastguard Worker                     if(offsets!=nullptr) {
1412*0e209d39SAndroid Build Coastguard Worker                         *offsets++=sourceIndex-1;
1413*0e209d39SAndroid Build Coastguard Worker                     }
1414*0e209d39SAndroid Build Coastguard Worker                 } else {
1415*0e209d39SAndroid Build Coastguard Worker                     cnv->charErrorBuffer[cnv->charErrorBufferLength++]=TO_BASE64_IMAP(bits);
1416*0e209d39SAndroid Build Coastguard Worker                     *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1417*0e209d39SAndroid Build Coastguard Worker                 }
1418*0e209d39SAndroid Build Coastguard Worker             }
1419*0e209d39SAndroid Build Coastguard Worker             /* need to terminate with a minus */
1420*0e209d39SAndroid Build Coastguard Worker             if(target<targetLimit) {
1421*0e209d39SAndroid Build Coastguard Worker                 *target++=MINUS;
1422*0e209d39SAndroid Build Coastguard Worker                 if(offsets!=nullptr) {
1423*0e209d39SAndroid Build Coastguard Worker                     *offsets++=sourceIndex-1;
1424*0e209d39SAndroid Build Coastguard Worker                 }
1425*0e209d39SAndroid Build Coastguard Worker             } else {
1426*0e209d39SAndroid Build Coastguard Worker                 cnv->charErrorBuffer[cnv->charErrorBufferLength++]=MINUS;
1427*0e209d39SAndroid Build Coastguard Worker                 *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
1428*0e209d39SAndroid Build Coastguard Worker             }
1429*0e209d39SAndroid Build Coastguard Worker         }
1430*0e209d39SAndroid Build Coastguard Worker         /* reset the state for the next conversion */
1431*0e209d39SAndroid Build Coastguard Worker         cnv->fromUnicodeStatus=(cnv->fromUnicodeStatus&0xf0000000)|0x1000000; /* keep version, inDirectMode=true */
1432*0e209d39SAndroid Build Coastguard Worker     } else {
1433*0e209d39SAndroid Build Coastguard Worker         /* set the converter state back into UConverter */
1434*0e209d39SAndroid Build Coastguard Worker         cnv->fromUnicodeStatus=
1435*0e209d39SAndroid Build Coastguard Worker             (cnv->fromUnicodeStatus&0xf0000000)|    /* keep version*/
1436*0e209d39SAndroid Build Coastguard Worker             ((uint32_t)inDirectMode<<24)|((uint32_t)base64Counter<<16)|(uint32_t)bits;
1437*0e209d39SAndroid Build Coastguard Worker     }
1438*0e209d39SAndroid Build Coastguard Worker 
1439*0e209d39SAndroid Build Coastguard Worker     /* write back the updated pointers */
1440*0e209d39SAndroid Build Coastguard Worker     pArgs->source=source;
1441*0e209d39SAndroid Build Coastguard Worker     pArgs->target=(char *)target;
1442*0e209d39SAndroid Build Coastguard Worker     pArgs->offsets=offsets;
1443*0e209d39SAndroid Build Coastguard Worker }
1444*0e209d39SAndroid Build Coastguard Worker U_CDECL_END
1445*0e209d39SAndroid Build Coastguard Worker 
1446*0e209d39SAndroid Build Coastguard Worker static const UConverterImpl _IMAPImpl={
1447*0e209d39SAndroid Build Coastguard Worker     UCNV_IMAP_MAILBOX,
1448*0e209d39SAndroid Build Coastguard Worker 
1449*0e209d39SAndroid Build Coastguard Worker     nullptr,
1450*0e209d39SAndroid Build Coastguard Worker     nullptr,
1451*0e209d39SAndroid Build Coastguard Worker 
1452*0e209d39SAndroid Build Coastguard Worker     _UTF7Open,
1453*0e209d39SAndroid Build Coastguard Worker     nullptr,
1454*0e209d39SAndroid Build Coastguard Worker     _UTF7Reset,
1455*0e209d39SAndroid Build Coastguard Worker 
1456*0e209d39SAndroid Build Coastguard Worker     _IMAPToUnicodeWithOffsets,
1457*0e209d39SAndroid Build Coastguard Worker     _IMAPToUnicodeWithOffsets,
1458*0e209d39SAndroid Build Coastguard Worker     _IMAPFromUnicodeWithOffsets,
1459*0e209d39SAndroid Build Coastguard Worker     _IMAPFromUnicodeWithOffsets,
1460*0e209d39SAndroid Build Coastguard Worker     nullptr,
1461*0e209d39SAndroid Build Coastguard Worker 
1462*0e209d39SAndroid Build Coastguard Worker     nullptr,
1463*0e209d39SAndroid Build Coastguard Worker     nullptr,
1464*0e209d39SAndroid Build Coastguard Worker     nullptr, /* we don't need writeSub() because we never call a callback at fromUnicode() */
1465*0e209d39SAndroid Build Coastguard Worker     nullptr,
1466*0e209d39SAndroid Build Coastguard Worker     ucnv_getCompleteUnicodeSet,
1467*0e209d39SAndroid Build Coastguard Worker     nullptr,
1468*0e209d39SAndroid Build Coastguard Worker     nullptr
1469*0e209d39SAndroid Build Coastguard Worker };
1470*0e209d39SAndroid Build Coastguard Worker 
1471*0e209d39SAndroid Build Coastguard Worker static const UConverterStaticData _IMAPStaticData={
1472*0e209d39SAndroid Build Coastguard Worker     sizeof(UConverterStaticData),
1473*0e209d39SAndroid Build Coastguard Worker     "IMAP-mailbox-name",
1474*0e209d39SAndroid Build Coastguard Worker     0, /* TODO CCSID for IMAP-mailbox-name */
1475*0e209d39SAndroid Build Coastguard Worker     UCNV_IBM, UCNV_IMAP_MAILBOX,
1476*0e209d39SAndroid Build Coastguard Worker     1, 4,
1477*0e209d39SAndroid Build Coastguard Worker     { 0x3f, 0, 0, 0 }, 1, /* the subchar is not used */
1478*0e209d39SAndroid Build Coastguard Worker     false, false,
1479*0e209d39SAndroid Build Coastguard Worker     0,
1480*0e209d39SAndroid Build Coastguard Worker     0,
1481*0e209d39SAndroid Build Coastguard Worker     { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* reserved */
1482*0e209d39SAndroid Build Coastguard Worker };
1483*0e209d39SAndroid Build Coastguard Worker 
1484*0e209d39SAndroid Build Coastguard Worker const UConverterSharedData _IMAPData=
1485*0e209d39SAndroid Build Coastguard Worker         UCNV_IMMUTABLE_SHARED_DATA_INITIALIZER(&_IMAPStaticData, &_IMAPImpl);
1486*0e209d39SAndroid Build Coastguard Worker 
1487*0e209d39SAndroid Build Coastguard Worker #endif
1488