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: