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