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

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

E-mail: