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