Branch data Line data Source code
1 : : /* gbase64.c - Base64 encoding/decoding
2 : : *
3 : : * Copyright (C) 2006 Alexander Larsson <alexl@redhat.com>
4 : : * Copyright (C) 2000-2003 Ximian Inc.
5 : : *
6 : : * SPDX-License-Identifier: LGPL-2.1-or-later
7 : : *
8 : : * This library is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU Lesser General Public
10 : : * License as published by the Free Software Foundation; either
11 : : * version 2.1 of the License, or (at your option) any later version.
12 : : *
13 : : * This library is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : : * Lesser General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU Lesser General Public License
19 : : * along with this library; if not, see <http://www.gnu.org/licenses/>.
20 : : *
21 : : * This is based on code in camel, written by:
22 : : * Michael Zucchi <notzed@ximian.com>
23 : : * Jeffrey Stedfast <fejj@ximian.com>
24 : : */
25 : :
26 : : #include "config.h"
27 : :
28 : : #include <string.h>
29 : :
30 : : #include "gbase64.h"
31 : : #include "gtestutils.h"
32 : : #include "glibintl.h"
33 : :
34 : : static const char base64_alphabet[] =
35 : : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
36 : :
37 : : /**
38 : : * g_base64_encode_step:
39 : : * @in: (array length=len) (element-type guint8): the binary data to encode
40 : : * @len: the length of @in
41 : : * @break_lines: whether to break long lines
42 : : * @out: (out) (array) (element-type guint8): pointer to destination buffer
43 : : * @state: (inout): Saved state between steps, initialize to 0
44 : : * @save: (inout): Saved state between steps, initialize to 0
45 : : *
46 : : * Incrementally encode a sequence of binary data into its Base-64 stringified
47 : : * representation. By calling this function multiple times you can convert
48 : : * data in chunks to avoid having to have the full encoded data in memory.
49 : : *
50 : : * When all of the data has been converted you must call
51 : : * g_base64_encode_close() to flush the saved state.
52 : : *
53 : : * The output buffer must be large enough to fit all the data that will
54 : : * be written to it. Due to the way base64 encodes you will need
55 : : * at least: (@len / 3 + 1) * 4 + 4 bytes (+ 4 may be needed in case of
56 : : * non-zero state). If you enable line-breaking you will need at least:
57 : : * ((@len / 3 + 1) * 4 + 4) / 76 + 1 bytes of extra space.
58 : : *
59 : : * @break_lines is typically used when putting base64-encoded data in emails.
60 : : * It breaks the lines at 76 columns instead of putting all of the text on
61 : : * the same line. This avoids problems with long lines in the email system.
62 : : * Note however that it breaks the lines with `LF` characters, not
63 : : * `CR LF` sequences, so the result cannot be passed directly to SMTP
64 : : * or certain other protocols.
65 : : *
66 : : * Returns: The number of bytes of output that was written
67 : : *
68 : : * Since: 2.12
69 : : */
70 : : gsize
71 : 12349 : g_base64_encode_step (const guchar *in,
72 : : gsize len,
73 : : gboolean break_lines,
74 : : gchar *out,
75 : : gint *state,
76 : : gint *save)
77 : : {
78 : : char *outptr;
79 : : const guchar *inptr;
80 : :
81 : 12349 : g_return_val_if_fail (in != NULL || len == 0, 0);
82 : 12349 : g_return_val_if_fail (out != NULL, 0);
83 : 12349 : g_return_val_if_fail (state != NULL, 0);
84 : 12349 : g_return_val_if_fail (save != NULL, 0);
85 : :
86 : 12349 : if (len == 0)
87 : 3 : return 0;
88 : :
89 : 12346 : inptr = in;
90 : 12346 : outptr = out;
91 : :
92 : 12346 : if (len + ((char *) save) [0] > 2)
93 : : {
94 : 7935 : const guchar *inend = in+len-2;
95 : : int c1, c2, c3;
96 : : int already;
97 : :
98 : 7935 : already = *state;
99 : :
100 : 7935 : switch (((char *) save) [0])
101 : : {
102 : 1284 : case 1:
103 : 1284 : c1 = ((unsigned char *) save) [1];
104 : 1284 : goto skip1;
105 : 2966 : case 2:
106 : 2966 : c1 = ((unsigned char *) save) [1];
107 : 2966 : c2 = ((unsigned char *) save) [2];
108 : 2966 : goto skip2;
109 : : }
110 : :
111 : : /*
112 : : * yes, we jump into the loop, no i'm not going to change it,
113 : : * it's beautiful!
114 : : */
115 : 195519 : while (inptr < inend)
116 : : {
117 : 187584 : c1 = *inptr++;
118 : 188868 : skip1:
119 : 188868 : c2 = *inptr++;
120 : 191834 : skip2:
121 : 191834 : c3 = *inptr++;
122 : 191834 : *outptr++ = base64_alphabet [ c1 >> 2 ];
123 : 191834 : *outptr++ = base64_alphabet [ c2 >> 4 |
124 : 191834 : ((c1&0x3) << 4) ];
125 : 191834 : *outptr++ = base64_alphabet [ ((c2 &0x0f) << 2) |
126 : 191834 : (c3 >> 6) ];
127 : 191834 : *outptr++ = base64_alphabet [ c3 & 0x3f ];
128 : : /* this is a bit ugly ... */
129 : 191834 : if (break_lines && (++already) >= 19)
130 : : {
131 : 51 : *outptr++ = '\n';
132 : 51 : already = 0;
133 : : }
134 : : }
135 : :
136 : 7935 : ((char *)save)[0] = 0;
137 : 7935 : len = 2 - (inptr - inend);
138 : 7935 : *state = already;
139 : : }
140 : :
141 : 12346 : g_assert (len == 0 || len == 1 || len == 2);
142 : :
143 : : {
144 : : char *saveout;
145 : :
146 : : /* points to the slot for the next char to save */
147 : 12346 : saveout = & (((char *)save)[1]) + ((char *)save)[0];
148 : :
149 : : /* len can only be 0 1 or 2 */
150 : 12346 : switch(len)
151 : : {
152 : 1903 : case 2:
153 : 1903 : *saveout++ = *inptr++;
154 : : G_GNUC_FALLTHROUGH;
155 : 7256 : case 1:
156 : 7256 : *saveout++ = *inptr++;
157 : : }
158 : 12346 : ((char *)save)[0] += len;
159 : : }
160 : :
161 : 12346 : return outptr - out;
162 : : }
163 : :
164 : : /**
165 : : * g_base64_encode_close:
166 : : * @break_lines: whether to break long lines
167 : : * @out: (out) (array) (element-type guint8): pointer to destination buffer
168 : : * @state: (inout): Saved state from g_base64_encode_step()
169 : : * @save: (inout): Saved state from g_base64_encode_step()
170 : : *
171 : : * Flush the status from a sequence of calls to g_base64_encode_step().
172 : : *
173 : : * The output buffer must be large enough to fit all the data that will
174 : : * be written to it. It will need up to 4 bytes, or up to 5 bytes if
175 : : * line-breaking is enabled.
176 : : *
177 : : * The @out array will not be automatically nul-terminated.
178 : : *
179 : : * Returns: The number of bytes of output that was written
180 : : *
181 : : * Since: 2.12
182 : : */
183 : : gsize
184 : 1946 : g_base64_encode_close (gboolean break_lines,
185 : : gchar *out,
186 : : gint *state,
187 : : gint *save)
188 : : {
189 : : int c1, c2;
190 : 1946 : char *outptr = out;
191 : :
192 : 1946 : g_return_val_if_fail (out != NULL, 0);
193 : 1946 : g_return_val_if_fail (state != NULL, 0);
194 : 1946 : g_return_val_if_fail (save != NULL, 0);
195 : :
196 : 1946 : c1 = ((unsigned char *) save) [1];
197 : 1946 : c2 = ((unsigned char *) save) [2];
198 : :
199 : 1946 : switch (((char *) save) [0])
200 : : {
201 : 644 : case 2:
202 : 644 : outptr [2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
203 : 644 : g_assert (outptr [2] != 0);
204 : 644 : goto skip;
205 : 655 : case 1:
206 : 655 : outptr[2] = '=';
207 : 655 : c2 = 0; /* saved state here is not relevant */
208 : 1299 : skip:
209 : 1299 : outptr [0] = base64_alphabet [ c1 >> 2 ];
210 : 1299 : outptr [1] = base64_alphabet [ c2 >> 4 | ( (c1&0x3) << 4 )];
211 : 1299 : outptr [3] = '=';
212 : 1299 : outptr += 4;
213 : 1299 : break;
214 : : }
215 : 1946 : if (break_lines)
216 : 3 : *outptr++ = '\n';
217 : :
218 : 1946 : *save = 0;
219 : 1946 : *state = 0;
220 : :
221 : 1946 : return outptr - out;
222 : : }
223 : :
224 : : /**
225 : : * g_base64_encode:
226 : : * @data: (array length=len) (element-type guint8) (nullable): the binary data to encode
227 : : * @len: the length of @data
228 : : *
229 : : * Encode a sequence of binary data into its Base-64 stringified
230 : : * representation.
231 : : *
232 : : * Returns: (transfer full): a newly allocated, zero-terminated Base-64
233 : : * encoded string representing @data. The returned string must
234 : : * be freed with g_free().
235 : : *
236 : : * Since: 2.12
237 : : */
238 : : gchar *
239 : 1537 : g_base64_encode (const guchar *data,
240 : : gsize len)
241 : : {
242 : : gchar *out;
243 : 1537 : gint state = 0, outlen;
244 : 1537 : gint save = 0;
245 : :
246 : 1537 : g_return_val_if_fail (data != NULL || len == 0, NULL);
247 : :
248 : : /* We can use a smaller limit here, since we know the saved state is 0,
249 : : +1 is needed for trailing \0, also check for unlikely integer overflow */
250 : 1537 : g_return_val_if_fail (len < ((G_MAXSIZE - 1) / 4 - 1) * 3, NULL);
251 : :
252 : 1537 : out = g_malloc ((len / 3 + 1) * 4 + 1);
253 : :
254 : 1537 : outlen = g_base64_encode_step (data, len, FALSE, out, &state, &save);
255 : 1537 : outlen += g_base64_encode_close (FALSE, out + outlen, &state, &save);
256 : 1537 : out[outlen] = '\0';
257 : :
258 : 1537 : return (gchar *) out;
259 : : }
260 : :
261 : : static const unsigned char mime_base64_rank[256] = {
262 : : 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
263 : : 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
264 : : 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
265 : : 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
266 : : 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
267 : : 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
268 : : 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
269 : : 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
270 : : 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
271 : : 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
272 : : 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
273 : : 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
274 : : 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
275 : : 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
276 : : 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
277 : : 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
278 : : };
279 : :
280 : : /**
281 : : * g_base64_decode_step: (skip)
282 : : * @in: (array length=len) (element-type guint8): binary input data
283 : : * @len: max length of @in data to decode
284 : : * @out: (out caller-allocates) (array) (element-type guint8): output buffer
285 : : * @state: (inout): Saved state between steps, initialize to 0
286 : : * @save: (inout): Saved state between steps, initialize to 0
287 : : *
288 : : * Incrementally decode a sequence of binary data from its Base-64 stringified
289 : : * representation. By calling this function multiple times you can convert
290 : : * data in chunks to avoid having to have the full encoded data in memory.
291 : : *
292 : : * The output buffer must be large enough to fit all the data that will
293 : : * be written to it. Since base64 encodes 3 bytes in 4 chars you need
294 : : * at least: (@len / 4) * 3 + 3 bytes (+ 3 may be needed in case of non-zero
295 : : * state).
296 : : *
297 : : * Returns: The number of bytes of output that was written
298 : : *
299 : : * Since: 2.12
300 : : **/
301 : : gsize
302 : 16242 : g_base64_decode_step (const gchar *in,
303 : : gsize len,
304 : : guchar *out,
305 : : gint *state,
306 : : guint *save)
307 : : {
308 : : const guchar *inptr;
309 : : guchar *outptr;
310 : : const guchar *inend;
311 : : guchar c, rank;
312 : : guchar last[2];
313 : : unsigned int v;
314 : : int i;
315 : :
316 : 16242 : g_return_val_if_fail (in != NULL || len == 0, 0);
317 : 16242 : g_return_val_if_fail (out != NULL, 0);
318 : 16242 : g_return_val_if_fail (state != NULL, 0);
319 : 16242 : g_return_val_if_fail (save != NULL, 0);
320 : :
321 : 16242 : if (len == 0)
322 : 2 : return 0;
323 : :
324 : 16240 : inend = (const guchar *)in+len;
325 : 16240 : outptr = out;
326 : :
327 : : /* convert 4 base64 bytes to 3 normal bytes */
328 : 16240 : v=*save;
329 : 16240 : i=*state;
330 : :
331 : 16240 : last[0] = last[1] = 0;
332 : :
333 : : /* we use the sign in the state to determine if we got a padding character
334 : : in the previous sequence */
335 : 16240 : if (i < 0)
336 : : {
337 : 46 : i = -i;
338 : 46 : last[0] = '=';
339 : : }
340 : :
341 : 16240 : inptr = (const guchar *)in;
342 : 795694 : while (inptr < inend)
343 : : {
344 : 779454 : c = *inptr++;
345 : 779454 : rank = mime_base64_rank [c];
346 : 779454 : if (rank != 0xff)
347 : : {
348 : 779400 : last[1] = last[0];
349 : 779400 : last[0] = c;
350 : 779400 : v = (v<<6) | rank;
351 : 779400 : i++;
352 : 779400 : if (i==4)
353 : : {
354 : 194850 : *outptr++ = v>>16;
355 : 194850 : if (last[1] != '=')
356 : 194161 : *outptr++ = v>>8;
357 : 194850 : if (last[0] != '=')
358 : 193484 : *outptr++ = v;
359 : 194850 : i=0;
360 : : }
361 : : }
362 : : }
363 : :
364 : 16240 : *save = v;
365 : 16240 : *state = last[0] == '=' ? -i : i;
366 : :
367 : 16240 : return outptr - out;
368 : : }
369 : :
370 : : /**
371 : : * g_base64_decode:
372 : : * @text: (not nullable): zero-terminated string with base64 text to decode
373 : : * @out_len: (out): The length of the decoded data is written here
374 : : *
375 : : * Decode a sequence of Base-64 encoded text into binary data. Note
376 : : * that the returned binary data is not necessarily zero-terminated,
377 : : * so it should not be used as a character string.
378 : : *
379 : : * Returns: (transfer full) (array length=out_len) (element-type guint8):
380 : : * newly allocated buffer containing the binary data
381 : : * that @text represents. The returned buffer must
382 : : * be freed with g_free().
383 : : *
384 : : * Since: 2.12
385 : : */
386 : : guchar *
387 : 1536 : g_base64_decode (const gchar *text,
388 : : gsize *out_len)
389 : : {
390 : : guchar *ret;
391 : : gsize input_length;
392 : 1536 : gint state = 0;
393 : 1536 : guint save = 0;
394 : :
395 : 1536 : g_return_val_if_fail (text != NULL, NULL);
396 : 1536 : g_return_val_if_fail (out_len != NULL, NULL);
397 : :
398 : 1536 : input_length = strlen (text);
399 : :
400 : : /* We can use a smaller limit here, since we know the saved state is 0,
401 : : +1 used to avoid calling g_malloc0(0), and hence returning NULL */
402 : 1536 : ret = g_malloc0 ((input_length / 4) * 3 + 1);
403 : :
404 : 1536 : *out_len = g_base64_decode_step (text, input_length, ret, &state, &save);
405 : :
406 : 1536 : return ret;
407 : : }
408 : :
409 : : /**
410 : : * g_base64_decode_inplace:
411 : : * @text: (inout) (array length=out_len) (element-type guint8): zero-terminated
412 : : * string with base64 text to decode
413 : : * @out_len: (inout): The length of the decoded data is written here
414 : : *
415 : : * Decode a sequence of Base-64 encoded text into binary data
416 : : * by overwriting the input data.
417 : : *
418 : : * Returns: (transfer none): The binary data that @text responds. This pointer
419 : : * is the same as the input @text.
420 : : *
421 : : * Since: 2.20
422 : : */
423 : : guchar *
424 : 100 : g_base64_decode_inplace (gchar *text,
425 : : gsize *out_len)
426 : : {
427 : 100 : gint state = 0;
428 : : size_t input_length;
429 : 100 : guint save = 0;
430 : :
431 : 100 : g_return_val_if_fail (text != NULL, NULL);
432 : 100 : g_return_val_if_fail (out_len != NULL, NULL);
433 : :
434 : 100 : input_length = strlen (text);
435 : :
436 : 100 : g_return_val_if_fail (input_length > 1, NULL);
437 : :
438 : 100 : *out_len = g_base64_decode_step (text, input_length, (guchar *) text, &state, &save);
439 : :
440 : 100 : return (guchar *) text;
441 : : }
|