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

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: 
        !            11: volatile const char * IDENT_PA_BASE64_C="$Id: pa_base64.C,v 1.1 2017/02/07 22:00:42 moko Exp $" IDENT_PA_BASE64_H;
        !            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: 
        !            36: static const char *base64_alphabet =
        !            37:        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        !            38: 
        !            39: /**
        !            40:  * g_mime_utils_base64_encode_step:
        !            41:  * @in: input stream
        !            42:  * @inlen: length of the input
        !            43:  * @out: output string
        !            44:  * @state: holds the number of bits that are stored in @save
        !            45:  * @save: leftover bits that have not yet been encoded
        !            46:  *
        !            47:  * Base64 encodes a chunk of data. Performs an 'encode step', only
        !            48:  * encodes blocks of 3 characters to the output at a time, saves
        !            49:  * left-over state in state and save (initialise to 0 on first
        !            50:  * invocation).
        !            51:  *
        !            52:  * Returns the number of bytes encoded.
        !            53:  **/
        !            54: 
        !            55: #define BASE64_GROUPS_IN_LINE 19
        !            56: 
        !            57: static size_t
        !            58: g_mime_utils_base64_encode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)
        !            59: {
        !            60:        register const unsigned char *inptr;
        !            61:        register unsigned char *outptr;
        !            62:        
        !            63:        if (inlen <= 0)
        !            64:                return 0;
        !            65:        
        !            66:        inptr = in;
        !            67:        outptr = out;
        !            68:        
        !            69:        if (inlen + ((unsigned char *)save)[0] > 2) {
        !            70:                const unsigned char *inend = in + inlen - 2;
        !            71:                register int c1 = 0, c2 = 0, c3 = 0;
        !            72:                register int already;
        !            73:                
        !            74:                already = *state;
        !            75:                
        !            76:                switch (((char *)save)[0]) {
        !            77:                case 1: c1 = ((unsigned char *)save)[1]; goto skip1;
        !            78:                case 2: c1 = ((unsigned char *)save)[1];
        !            79:                        c2 = ((unsigned char *)save)[2]; goto skip2;
        !            80:                }
        !            81:                
        !            82:                /* yes, we jump into the loop, no i'm not going to change it, its beautiful! */
        !            83:                while (inptr < inend) {
        !            84:                        c1 = *inptr++;
        !            85:                skip1:
        !            86:                        c2 = *inptr++;
        !            87:                skip2:
        !            88:                        c3 = *inptr++;
        !            89:                        *outptr++ = base64_alphabet [c1 >> 2];
        !            90:                        *outptr++ = base64_alphabet [(c2 >> 4) | ((c1 & 0x3) << 4)];
        !            91:                        *outptr++ = base64_alphabet [((c2 & 0x0f) << 2) | (c3 >> 6)];
        !            92:                        *outptr++ = base64_alphabet [c3 & 0x3f];
        !            93:                        /* this is a bit ugly ... */
        !            94:                        if ((++already) >= BASE64_GROUPS_IN_LINE) {
        !            95:                                *outptr++ = '\n';
        !            96:                                already = 0;
        !            97:                        }
        !            98:                }
        !            99:                
        !           100:                ((unsigned char *)save)[0] = 0;
        !           101:                inlen = 2 - (inptr - inend);
        !           102:                *state = already;
        !           103:        }
        !           104:        
        !           105:        //d(printf ("state = %d, inlen = %d\n", (int)((char *)save)[0], inlen));
        !           106:        
        !           107:        if (inlen > 0) {
        !           108:                register char *saveout;
        !           109:                
        !           110:                /* points to the slot for the next char to save */
        !           111:                saveout = & (((char *)save)[1]) + ((char *)save)[0];
        !           112:                
        !           113:                /* inlen can only be 0 1 or 2 */
        !           114:                switch (inlen) {
        !           115:                case 2: *saveout++ = *inptr++;
        !           116:                case 1: *saveout++ = *inptr++;
        !           117:                }
        !           118:                *(char *)save = *(char *)save+(char)inlen;
        !           119:        }
        !           120:        
        !           121:        /*d(printf ("mode = %d\nc1 = %c\nc2 = %c\n",
        !           122:                  (int)((char *)save)[0],
        !           123:                  (int)((char *)save)[1],
        !           124:                  (int)((char *)save)[2]));*/
        !           125:        
        !           126:        return (outptr - out);
        !           127: }
        !           128: 
        !           129: /**
        !           130:  * g_mime_utils_base64_encode_close:
        !           131:  * @in: input stream
        !           132:  * @inlen: length of the input
        !           133:  * @out: output string
        !           134:  * @state: holds the number of bits that are stored in @save
        !           135:  * @save: leftover bits that have not yet been encoded
        !           136:  *
        !           137:  * Base64 encodes the input stream to the output stream. Call this
        !           138:  * when finished encoding data with g_mime_utils_base64_encode_step to
        !           139:  * flush off the last little bit.
        !           140:  *
        !           141:  * Returns the number of bytes encoded.
        !           142:  **/
        !           143: static size_t
        !           144: g_mime_utils_base64_encode_close (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)
        !           145: {
        !           146:        unsigned char *outptr = out;
        !           147:        int c1, c2;
        !           148:        
        !           149:        if (inlen > 0)
        !           150:                outptr += g_mime_utils_base64_encode_step (in, inlen, outptr, state, save);
        !           151:        
        !           152:        c1 = ((unsigned char *)save)[1];
        !           153:        c2 = ((unsigned char *)save)[2];
        !           154:        
        !           155:        switch (((unsigned char *)save)[0]) {
        !           156:        case 2:
        !           157:                outptr[2] = base64_alphabet [(c2 & 0x0f) << 2];
        !           158:                goto skip;
        !           159:        case 1:
        !           160:                outptr[2] = '=';
        !           161:        skip:
        !           162:                outptr[0] = base64_alphabet [c1 >> 2];
        !           163:                outptr[1] = base64_alphabet [c2 >> 4 | ((c1 & 0x3) << 4)];
        !           164:                outptr[3] = '=';
        !           165:                outptr += 4;
        !           166:                break;
        !           167:        }
        !           168:        
        !           169:        *outptr++ = 0;
        !           170:        
        !           171:        *save = 0;
        !           172:        *state = 0;
        !           173:        
        !           174:        return (outptr - out);
        !           175: }
        !           176: 
        !           177: static unsigned char gmime_base64_rank[256] = {
        !           178:        255,255,255,255,255,255,255,255,255,254,254,255,255,254,255,255,
        !           179:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
        !           180:        254,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
        !           181:         52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
        !           182:        255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
        !           183:         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
        !           184:        255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
        !           185:         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,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:        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: };
        !           195: 
        !           196: /**
        !           197:  * g_mime_utils_base64_decode_step:
        !           198:  * @in: input stream
        !           199:  * @inlen: max length of data to decode
        !           200:  * @out: output stream
        !           201:  * @state: holds the number of bits that are stored in @save
        !           202:  * @save: leftover bits that have not yet been decoded
        !           203:  * @strict: only base64 and whitespace chars are allowed
        !           204:  *
        !           205:  * Decodes a chunk of base64 encoded data.
        !           206:  *
        !           207:  * Returns the number of bytes decoded (which have been dumped in @out).
        !           208:  **/
        !           209: size_t
        !           210: g_mime_utils_base64_decode_step(const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save, bool strict=false)
        !           211: {
        !           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: 
        !           266: char* pa_base64_encode(const char *in, size_t in_size){
        !           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: 
        !           308: char* pa_base64_encode(const String& file_spec){
        !           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: 
        !           317: void pa_base64_decode(const char *in, size_t in_size, char*& result, size_t& result_size, bool strict) {
        !           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;
        !           325:        result_size=g_mime_utils_base64_decode_step ((const unsigned char*)in, in_size, (unsigned char*)result, &state, &save, strict);
        !           326:        assert(result_size <= new_size);
        !           327:        result[result_size]=0; // for text files
        !           328: 
        !           329:        if(strict && state!=0)
        !           330:                throw Exception(BASE64_FORMAT, 0, "Unexpected end of chars");
        !           331: }
        !           332: 
        !           333: 

E-mail: