Annotation of parser3/src/main/pa_base64.C, revision 1.2

1.1       moko        1: /**    @file
                      2:        Parser: base64 functions impl.
                      3: 
                      4:        Copyright (c) 2001-2017 Art. Lebedev Studio (http://www.artlebedev.com)
                      5:        Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
                      6: */
                      7: 
                      8: #include "pa_base64.h"
                      9: #include "pa_common.h"
                     10: 
1.2     ! moko       11: volatile const char * IDENT_PA_BASE64_C="$Id: pa_base64.C,v 1.1 2019/11/12 21:13:31 moko Exp $" IDENT_PA_BASE64_H;
1.1       moko       12: 
                     13: /*
                     14:  * BASE64 part
                     15:  *  Authors: Michael Zucchi <notzed@ximian.com>
                     16:  *           Jeffrey Stedfast <fejj@ximian.com>
                     17:  *
                     18:  *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
                     19:  *
                     20:  *  This program is free software; you can redistribute it and/or modify
                     21:  *  it under the terms of the GNU General Public License as published by
                     22:  *  the Free Software Foundation; either version 2 of the License, or
                     23:  *  (at your option) any later version.
                     24:  *
                     25:  *  This program is distributed in the hope that it will be useful,
                     26:  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
                     27:  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     28:  *  GNU General Public License for more details.
                     29:  *
                     30:  *  You should have received a copy of the GNU General Public License
                     31:  *  along with this program; if not, write to the Free Software
                     32:  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
                     33:  *
                     34:  */
                     35: 
1.2     ! moko       36: static const char *base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1.1       moko       37: 
                     38: /**
                     39:  * g_mime_utils_base64_encode_step:
                     40:  * @in: input stream
                     41:  * @inlen: length of the input
                     42:  * @out: output string
                     43:  * @state: holds the number of bits that are stored in @save
                     44:  * @save: leftover bits that have not yet been encoded
                     45:  *
                     46:  * Base64 encodes a chunk of data. Performs an 'encode step', only
                     47:  * encodes blocks of 3 characters to the output at a time, saves
                     48:  * left-over state in state and save (initialise to 0 on first
                     49:  * invocation).
                     50:  *
                     51:  * Returns the number of bytes encoded.
                     52:  **/
                     53: 
                     54: #define BASE64_GROUPS_IN_LINE 19
                     55: 
1.2     ! moko       56: static size_t g_mime_utils_base64_encode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save) {
1.1       moko       57:        register const unsigned char *inptr;
                     58:        register unsigned char *outptr;
                     59:        
                     60:        if (inlen <= 0)
                     61:                return 0;
                     62:        
                     63:        inptr = in;
                     64:        outptr = out;
                     65:        
                     66:        if (inlen + ((unsigned char *)save)[0] > 2) {
                     67:                const unsigned char *inend = in + inlen - 2;
                     68:                register int c1 = 0, c2 = 0, c3 = 0;
                     69:                register int already;
                     70:                
                     71:                already = *state;
                     72:                
                     73:                switch (((char *)save)[0]) {
                     74:                case 1: c1 = ((unsigned char *)save)[1]; goto skip1;
                     75:                case 2: c1 = ((unsigned char *)save)[1];
                     76:                        c2 = ((unsigned char *)save)[2]; goto skip2;
                     77:                }
                     78:                
                     79:                /* yes, we jump into the loop, no i'm not going to change it, its beautiful! */
                     80:                while (inptr < inend) {
                     81:                        c1 = *inptr++;
                     82:                skip1:
                     83:                        c2 = *inptr++;
                     84:                skip2:
                     85:                        c3 = *inptr++;
                     86:                        *outptr++ = base64_alphabet [c1 >> 2];
                     87:                        *outptr++ = base64_alphabet [(c2 >> 4) | ((c1 & 0x3) << 4)];
                     88:                        *outptr++ = base64_alphabet [((c2 & 0x0f) << 2) | (c3 >> 6)];
                     89:                        *outptr++ = base64_alphabet [c3 & 0x3f];
                     90:                        /* this is a bit ugly ... */
                     91:                        if ((++already) >= BASE64_GROUPS_IN_LINE) {
                     92:                                *outptr++ = '\n';
                     93:                                already = 0;
                     94:                        }
                     95:                }
                     96:                
                     97:                ((unsigned char *)save)[0] = 0;
                     98:                inlen = 2 - (inptr - inend);
                     99:                *state = already;
                    100:        }
                    101:        
                    102:        //d(printf ("state = %d, inlen = %d\n", (int)((char *)save)[0], inlen));
                    103:        
                    104:        if (inlen > 0) {
                    105:                register char *saveout;
                    106:                
                    107:                /* points to the slot for the next char to save */
                    108:                saveout = & (((char *)save)[1]) + ((char *)save)[0];
                    109:                
                    110:                /* inlen can only be 0 1 or 2 */
                    111:                switch (inlen) {
                    112:                case 2: *saveout++ = *inptr++;
                    113:                case 1: *saveout++ = *inptr++;
                    114:                }
                    115:                *(char *)save = *(char *)save+(char)inlen;
                    116:        }
                    117:        
                    118:        /*d(printf ("mode = %d\nc1 = %c\nc2 = %c\n",
                    119:                  (int)((char *)save)[0],
                    120:                  (int)((char *)save)[1],
                    121:                  (int)((char *)save)[2]));*/
                    122:        
                    123:        return (outptr - out);
                    124: }
                    125: 
                    126: /**
                    127:  * g_mime_utils_base64_encode_close:
                    128:  * @in: input stream
                    129:  * @inlen: length of the input
                    130:  * @out: output string
                    131:  * @state: holds the number of bits that are stored in @save
                    132:  * @save: leftover bits that have not yet been encoded
                    133:  *
                    134:  * Base64 encodes the input stream to the output stream. Call this
                    135:  * when finished encoding data with g_mime_utils_base64_encode_step to
                    136:  * flush off the last little bit.
                    137:  *
                    138:  * Returns the number of bytes encoded.
                    139:  **/
1.2     ! moko      140: static size_t g_mime_utils_base64_encode_close (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save) {
1.1       moko      141:        unsigned char *outptr = out;
                    142:        int c1, c2;
                    143:        
                    144:        if (inlen > 0)
                    145:                outptr += g_mime_utils_base64_encode_step (in, inlen, outptr, state, save);
                    146:        
                    147:        c1 = ((unsigned char *)save)[1];
                    148:        c2 = ((unsigned char *)save)[2];
                    149:        
                    150:        switch (((unsigned char *)save)[0]) {
                    151:        case 2:
                    152:                outptr[2] = base64_alphabet [(c2 & 0x0f) << 2];
                    153:                goto skip;
                    154:        case 1:
                    155:                outptr[2] = '=';
                    156:        skip:
                    157:                outptr[0] = base64_alphabet [c1 >> 2];
                    158:                outptr[1] = base64_alphabet [c2 >> 4 | ((c1 & 0x3) << 4)];
                    159:                outptr[3] = '=';
                    160:                outptr += 4;
                    161:                break;
                    162:        }
                    163:        
                    164:        *outptr++ = 0;
                    165:        
                    166:        *save = 0;
                    167:        *state = 0;
                    168:        
                    169:        return (outptr - out);
                    170: }
                    171: 
                    172: static unsigned char gmime_base64_rank[256] = {
                    173:        255,255,255,255,255,255,255,255,255,254,254,255,255,254,255,255,
                    174:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
                    175:        254,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
                    176:         52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
                    177:        255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
                    178:         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
                    179:        255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
                    180:         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
                    181:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
                    182:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
                    183:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
                    184:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
                    185:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
                    186:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
                    187:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
                    188:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
                    189: };
                    190: 
                    191: /**
                    192:  * g_mime_utils_base64_decode_step:
                    193:  * @in: input stream
                    194:  * @inlen: max length of data to decode
                    195:  * @out: output stream
                    196:  * @state: holds the number of bits that are stored in @save
                    197:  * @save: leftover bits that have not yet been decoded
                    198:  * @strict: only base64 and whitespace chars are allowed
                    199:  *
                    200:  * Decodes a chunk of base64 encoded data.
                    201:  *
                    202:  * Returns the number of bytes decoded (which have been dumped in @out).
                    203:  **/
1.2     ! moko      204: size_t g_mime_utils_base64_decode_step(const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save, bool strict=false) {
1.1       moko      205:        const unsigned char *inptr;
                    206:        unsigned char *outptr;
                    207:        const unsigned char *inend;
                    208:        int saved;
                    209:        unsigned char c;
                    210:        int i;
                    211:        
                    212:        inend = in + inlen;
                    213:        outptr = out;
                    214:        
                    215:        /* convert 4 base64 bytes to 3 normal bytes */
                    216:        saved = *save;
                    217:        i = *state;
                    218:        inptr = in;
                    219:        while (inptr < inend) {
                    220:                c = gmime_base64_rank[*inptr++];
                    221:                switch(c) {
                    222:                        case 0xff: // non-base64 and non-whitespace chars. not allowed in strict mode
                    223:                                if(strict)
                    224:                                        throw Exception(BASE64_FORMAT, 0, "Invalid base64 char on position %d is detected", inptr-in-1);
                    225:                        case 0xfe: // whitespace chars 0x09, 0x0A, 0x0D, 0x20 are allowed in any mode
                    226:                                break;
                    227:                        default:
                    228:                                saved = (saved << 6) | c;
                    229:                                i++;
                    230:                                if (i == 4) {
                    231:                                        *outptr++ = (unsigned char)(saved >> 16);
                    232:                                        *outptr++ = (unsigned char)(saved >> 8);
                    233:                                        *outptr++ = (unsigned char)(saved);
                    234:                                        i = 0;
                    235:                                }
                    236:                }
                    237:        }
                    238:        
                    239:        *save = saved;
                    240:        *state = i;
                    241:        
                    242:        /* quick scan back for '=' on the end somewhere */
                    243:        /* fortunately we can drop 1 output char for each trailing = (upto 2) */
                    244:        i = 2;
                    245:        while (inptr > in && i) {
                    246:                inptr--;
                    247:                if (gmime_base64_rank[*inptr] <= 0xfe) {
                    248:                        if (*inptr == '=' && outptr > out)
                    249:                                outptr--;
                    250:                        i--;
                    251:                }
                    252:        }
                    253:        
                    254:        /* if i != 0 then there is a truncation error! */
                    255:        return (outptr - out);
                    256: }
                    257: 
                    258: 
1.2     ! moko      259: char* pa_base64_encode(const char *in, size_t in_size) {
1.1       moko      260:        size_t new_size = ((in_size / 3 + 1) * 4);
                    261:        new_size += new_size / (BASE64_GROUPS_IN_LINE * 4)/*new lines*/ + 1/*zero terminator*/;
                    262:        char* result = new(PointerFreeGC) char[new_size];
                    263:        int state=0;
                    264:        int save=0;
                    265: #ifndef NDEBUG
                    266:        size_t filled=
                    267: #endif
                    268:                g_mime_utils_base64_encode_close ((const unsigned char*)in, in_size, (unsigned char*)result, &state, &save);
                    269: 
                    270:        //throw Exception(PARSER_RUNTIME, 0, "%d %d %d", in_size, new_size, filled);
                    271:        assert(filled <= new_size);
                    272: 
                    273:        return result;
                    274: }
                    275: 
                    276: struct File_base64_action_info {
                    277:        unsigned char** base64;
                    278: }; 
                    279: 
                    280: static void file_base64_file_action(struct stat& finfo, int f, const String& file_spec, void *context) {
                    281: 
                    282:        if(finfo.st_size) { 
                    283:                File_base64_action_info& info=*static_cast<File_base64_action_info *>(context);
                    284:                *info.base64=new(PointerFreeGC) unsigned char[check_file_size(finfo.st_size, file_spec) * 2 + 6]; 
                    285:                unsigned char* base64 = *info.base64;
                    286:                int state=0;
                    287:                int save=0;
                    288:                int nCount;
                    289:                do {
                    290:                        unsigned char buffer[FILE_BUFFER_SIZE];
                    291:                        nCount = file_block_read(f, buffer, sizeof(buffer));
                    292:                        if( nCount ){
                    293:                                size_t filled=g_mime_utils_base64_encode_step ((const unsigned char*)buffer, nCount, base64, &state, &save);
                    294:                                base64+=filled;
                    295:                        }
                    296:                } while(nCount > 0);
                    297:                g_mime_utils_base64_encode_close (0, 0, base64, &state, &save);
                    298:        }
                    299: }
                    300: 
                    301: char* pa_base64_encode(const String& file_spec){
                    302:        unsigned char* base64=0;
                    303:        File_base64_action_info info={&base64}; 
                    304: 
                    305:        file_read_action_under_lock(file_spec, "pa_base64_encode", file_base64_file_action, &info);
                    306: 
                    307:        return (char*)base64; 
                    308: }
                    309: 
                    310: void pa_base64_decode(const char *in, size_t in_size, char*& result, size_t& result_size, bool strict) {
                    311:        // every 4 base64 bytes are converted into 3 normal bytes
                    312:        // not full set (tail) of 4-bytes set is ignored
                    313:        size_t new_size=in_size/4*3;
                    314:        result=new(PointerFreeGC) char[new_size+1/*terminator*/];
                    315: 
                    316:        int state=0;
                    317:        int save=0;
                    318:        result_size=g_mime_utils_base64_decode_step ((const unsigned char*)in, in_size, (unsigned char*)result, &state, &save, strict);
                    319:        assert(result_size <= new_size);
                    320:        result[result_size]=0; // for text files
                    321: 
                    322:        if(strict && state!=0)
                    323:                throw Exception(BASE64_FORMAT, 0, "Unexpected end of chars");
                    324: }
                    325: 

E-mail: