Home | History | Annotate | Download | only in common
      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
     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
   1485