Annotation of parser3/src/main/pa_base64.C, revision 1.9
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.9 ! moko 11: volatile const char * IDENT_PA_BASE64_C="$Id: pa_base64.C,v 1.8 2019/11/24 22:25:54 moko Exp $" IDENT_PA_BASE64_H;
1.1 moko 12:
13: /*
1.9 ! moko 14: * BASE64 part inspired by g_mime_utils
1.1 moko 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:
1.6 moko 39: Base64Options::Base64Options(bool awrap): strict(false), wrap(awrap), pad(true), abc(base64_alphabet) {}
1.3 moko 40:
41: void Base64Options::set_url_safe_abc() {
1.4 moko 42: abc = base64_alphabet_url_safe;
1.3 moko 43: }
1.1 moko 44:
1.9 ! moko 45: #define BASE64_GROUPS_IN_LINE 19
! 46:
! 47: static size_t pa_base64_encode(const unsigned char *in, size_t inlen, unsigned char *out, Base64Options options) {
! 48: const unsigned char *inptr = in;
! 49: unsigned char *outptr = out;
1.1 moko 50:
1.9 ! moko 51: const unsigned char *abc = (const unsigned char *)options.abc;
1.1 moko 52:
1.9 ! moko 53: if (inlen > 2) {
1.1 moko 54: const unsigned char *inend = in + inlen - 2;
1.9 ! moko 55: int already=0;
1.1 moko 56:
57: while (inptr < inend) {
1.9 ! moko 58: int c1 = *inptr++;
! 59: int c2 = *inptr++;
! 60: int c3 = *inptr++;
! 61: *outptr++ = abc[c1 >> 2];
! 62: *outptr++ = abc[(c2 >> 4) | ((c1 & 0x3) << 4)];
! 63: *outptr++ = abc[((c2 & 0x0f) << 2) | (c3 >> 6)];
! 64: *outptr++ = abc[c3 & 0x3f];
! 65:
! 66: if ((++already) >= BASE64_GROUPS_IN_LINE && options.wrap) {
1.1 moko 67: *outptr++ = '\n';
68: already = 0;
69: }
70: }
71:
72: inlen = 2 - (inptr - inend);
73: }
1.9 ! moko 74:
! 75: if (inlen == 2) {
! 76: int c1 = *inptr++;
! 77: int c2 = *inptr++;
! 78: outptr[0] = abc[c1 >> 2];
! 79: outptr[1] = abc[c2 >> 4 | ((c1 & 0x3) << 4)];
! 80: outptr[2] = abc[(c2 & 0x0f) << 2];
! 81: if(options.pad) {
! 82: outptr[3] = '=';
! 83: outptr += 4;
! 84: } else {
! 85: outptr += 3;
! 86: }
! 87: } else if (inlen == 1) {
! 88: int c1 = *inptr++;
! 89: outptr[0] = abc[c1 >> 2];
! 90: outptr[1] = abc[(c1 & 0x3) << 4];
! 91: if(options.pad) {
! 92: outptr[2] = '=';
! 93: outptr[3] = '=';
! 94: outptr += 4;
! 95: } else {
! 96: outptr += 2;
1.1 moko 97: }
98: }
99:
1.9 ! moko 100: *outptr='\0';
! 101: return outptr - out;
1.1 moko 102: }
103:
104: static unsigned char gmime_base64_rank[256] = {
105: 255,255,255,255,255,255,255,255,255,254,254,255,255,254,255,255,
106: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
107: 254,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
1.6 moko 108: 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,253,255,255,
1.1 moko 109: 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
110: 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
111: 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
112: 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
113: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
114: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
115: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
116: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
117: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
118: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
119: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
120: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
121: };
122:
1.5 moko 123: static unsigned char gmime_base64_rank_url_safe[256] = {
124: 255,255,255,255,255,255,255,255,255,254,254,255,255,254,255,255,
125: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
126: 254,255,255,255,255,255,255,255,255,255,255,255,255, 62,255,255,
1.6 moko 127: 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,253,255,255,
1.5 moko 128: 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
129: 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255, 63,
130: 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
131: 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
132: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
133: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
134: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
135: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
136: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
137: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
138: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
139: 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
140: };
141:
1.9 ! moko 142: size_t pa_base64_decode(const unsigned char *in, size_t inlen, unsigned char *out, Base64Options options) {
1.5 moko 143: const unsigned char *inptr = in;
144: unsigned char *outptr = out;
145: const unsigned char *inend = in + inlen;
146:
147: int saved = 0;
148: int state = 0;
1.1 moko 149:
1.5 moko 150: unsigned char *abc_rank = options.abc == base64_alphabet ? gmime_base64_rank : gmime_base64_rank_url_safe;
151:
1.1 moko 152: /* convert 4 base64 bytes to 3 normal bytes */
153: while (inptr < inend) {
1.5 moko 154: unsigned char c = abc_rank[*inptr++];
1.1 moko 155: switch(c) {
1.6 moko 156: case 255: // non-base64 and non-whitespace chars. not allowed in strict mode
1.5 moko 157: if(options.strict)
1.4 moko 158: throw Exception(BASE64_FORMAT, 0, "Invalid base64 char on position %d is detected", inptr - in - 1);
1.6 moko 159: case 254: // whitespace chars 0x09, 0x0A, 0x0D, 0x20 are allowed in any mode
160: break;
161: case 253: // =
162: if(state < 2) {
163: if(options.strict)
164: throw Exception(BASE64_FORMAT, 0, "Unexpected '=' on position %d is detected", inptr - in - 1);
165: break;
166: }
167: if(state == 2) { // double '='
168: if(inptr == inend) {
169: if(options.strict)
170: throw Exception(BASE64_FORMAT, 0, "Unexpected end of chars");
171: break;
172: }
173: if(*inptr != '=') {
174: if(options.strict)
175: throw Exception(BASE64_FORMAT, 0, "Unexpected '=' on position %d is detected", inptr - in - 1);
176: break;
177: }
178: inptr++;
179: *outptr++ = (unsigned char)(saved >> 4);
180: } else { // single '='
181: *outptr++ = (unsigned char)(saved >> 10);
182: *outptr++ = (unsigned char)(saved >> 2);
183: }
184: state = 0;
1.1 moko 185: break;
186: default:
187: saved = (saved << 6) | c;
1.5 moko 188: state++;
189: if (state == 4) {
1.1 moko 190: *outptr++ = (unsigned char)(saved >> 16);
191: *outptr++ = (unsigned char)(saved >> 8);
192: *outptr++ = (unsigned char)(saved);
1.5 moko 193: state = 0;
1.1 moko 194: }
195: }
196: }
1.5 moko 197:
1.6 moko 198: if(state > 0) {
199: if(state > 1) {
200: if(options.pad && options.strict)
201: throw Exception(BASE64_FORMAT, 0, "Unexpected end of chars");
202: if(state == 2) {
203: *outptr++ = (unsigned char)(saved >> 4);
204: } else {
205: *outptr++ = (unsigned char)(saved >> 10);
206: *outptr++ = (unsigned char)(saved >> 2);
207: }
208: } else {
209: if(options.strict)
210: throw Exception(BASE64_FORMAT, 0, "Unexpected end of chars");
1.1 moko 211: }
212: }
1.5 moko 213:
1.7 moko 214: *outptr='\0'; // for text files
1.9 ! moko 215: return outptr - out;
1.1 moko 216: }
217:
1.8 moko 218: char* pa_base64_encode(const char *in, size_t in_size, Base64Options options) {
1.9 ! moko 219: size_t new_size = ((in_size / 3 + 1) * 4);
! 220: if (options.wrap)
! 221: new_size += new_size / (BASE64_GROUPS_IN_LINE * 4) /*new lines*/;
1.1 moko 222:
1.9 ! moko 223: char* result = new(PointerFreeGC) char[new_size + 1 /*zero terminator*/];
1.1 moko 224:
1.9 ! moko 225: size_t filled = pa_base64_encode((const unsigned char*)in, in_size, (unsigned char*)result, options);
1.1 moko 226: assert(filled <= new_size);
227:
228: return result;
229: }
230:
1.7 moko 231: size_t pa_base64_decode(const char *in, size_t in_size, char*& result, Base64Options options) {
1.1 moko 232: // every 4 base64 bytes are converted into 3 normal bytes
1.4 moko 233: size_t new_size = (in_size + 3) / 4 * 3;
234: result = new(PointerFreeGC) char[new_size + 1 /*terminator*/];
1.1 moko 235:
1.9 ! moko 236: return pa_base64_decode((const unsigned char*)in, in_size, (unsigned char*)result, options);
1.1 moko 237: }
E-mail: