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: