Add base64 encode/decode functions
[platform/upstream/glib.git] / glib / gbase64.c
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, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
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 "glib.h"
32 #include "glibintl.h"
33
34 #include "galias.h"
35
36 static const char base64_alphabet[] =
37         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
38
39 /**
40  * g_base64_encode_step:
41  * @in: the binary data to encode.
42  * @len: the length of @in.
43  * @break_lines: whether to break long lines
44  * @out: pointer to destination buffer
45  * @state: Saved state between steps, initialize to 0
46  * @save: Saved state between steps, initialize to 0
47  *
48  * Incrementally encode a sequence of binary data into it's Base-64 stringified
49  * representation. By calling this functions multiple times you can convert data
50  * in chunks to avoid having to have the full encoded data in memory.
51  *
52  * When all the data has been converted you must call g_base64_encode_close()
53  * to flush the saved state.
54  *
55  * The output buffer must be large enough to fit all the data that will
56  * be written to it. Due to the way base64 encodes you will need
57  * at least: @len * 4 / 3 + 6 bytes. If you enable line-breaking you will
58  * need at least: @len * 4 / 3 + @len * 4 / (3 * 72) + 7 bytes.
59  *
60  * @break_lines is typically used when putting base64-encoded data in emails.
61  * It breaks the lines at 72 columns instead of putting all text on the same
62  * line. This avoids problems with long lines in the email system.
63  *
64  * Return value: The number of bytes of output that was written
65  *
66  * Since: 2.12
67  */
68 gsize
69 g_base64_encode_step (const guchar  *in, 
70                       gsize          len, 
71                       gboolean       break_lines, 
72                       char          *out, 
73                       int           *state, 
74                       int           *save)
75 {
76   char *outptr;
77   const guchar *inptr;
78   
79   if (len <= 0)
80     return 0;
81   
82   inptr = in;
83   outptr = out;
84   
85   if (len + ((char *) save) [0] > 2)
86     {
87       const guchar *inend = in+len-2;
88       int c1, c2, c3;
89       int already;
90       
91       already = *state;
92       
93       switch (((char *) save) [0])
94         {
95         case 1: c1 = ((unsigned char *) save) [1]; goto skip1;
96         case 2: c1 = ((unsigned char *) save) [1];
97           c2 = ((unsigned char *) save) [2]; goto skip2;
98         }
99       
100       /* 
101        * yes, we jump into the loop, no i'm not going to change it, 
102        * it's beautiful! 
103        */
104       while (inptr < inend)
105         {
106           c1 = *inptr++;
107         skip1:
108           c2 = *inptr++;
109         skip2:
110           c3 = *inptr++;
111           *outptr++ = base64_alphabet [ c1 >> 2 ];
112           *outptr++ = base64_alphabet [ c2 >> 4 | 
113                                         ((c1&0x3) << 4) ];
114           *outptr++ = base64_alphabet [ ((c2 &0x0f) << 2) | 
115                                         (c3 >> 6) ];
116           *outptr++ = base64_alphabet [ c3 & 0x3f ];
117           /* this is a bit ugly ... */
118           if (break_lines && (++already)>=19)
119             {
120               *outptr++='\n';
121               already = 0;
122             }
123         }
124       
125       ((char *)save)[0] = 0;
126       len = 2-(inptr-inend);
127       *state = already;
128     }
129   
130   if (len>0)
131     {
132       char *saveout;
133       
134       /* points to the slot for the next char to save */
135       saveout = & (((char *)save)[1]) + ((char *)save)[0];
136       
137       /* len can only be 0 1 or 2 */
138       switch(len)
139         {
140         case 2: *saveout++ = *inptr++;
141         case 1: *saveout++ = *inptr++;
142         }
143       ((char *)save)[0]+=len;
144     }
145   
146   return outptr-out;
147 }
148
149 /**
150  * g_base64_encode_close:
151  * @break_lines: whether to break long lines
152  * @out: pointer to destination buffer
153  * @state: Saved state from g_base64_encode_step()
154  * @save: Saved state from g_base64_encode_step()
155  *
156  * Flush the status from a sequence of calls to g_base64_encode_step().
157  *
158  * Return value: The number of bytes of output that was written
159  *
160  * Since: 2.12
161  */
162 gsize
163 g_base64_encode_close (gboolean       break_lines,
164                        char          *out, 
165                        int           *state, 
166                        int           *save)
167 {
168   int c1, c2;
169   char *outptr = out;
170
171   c1 = ((unsigned char *) save) [1];
172   c2 = ((unsigned char *) save) [2];
173   
174   switch (((char *) save) [0])
175     {
176     case 2:
177       outptr [2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
178       g_assert (outptr [2] != 0);
179       goto skip;
180     case 1:
181       outptr[2] = '=';
182     skip:
183       outptr [0] = base64_alphabet [ c1 >> 2 ];
184       outptr [1] = base64_alphabet [ c2 >> 4 | ( (c1&0x3) << 4 )];
185       outptr [3] = '=';
186       outptr += 4;
187       break;
188     }
189   if (break_lines)
190     *outptr++ = '\n';
191   
192   *save = 0;
193   *state = 0;
194   
195   return outptr-out;
196 }
197
198 /**
199  * g_base64_encode:
200  * @data: the binary data to encode.
201  * @len: the length of @data.
202  *
203  * Encode a sequence of binary data into it's Base-64 stringified
204  * representation.
205  *
206  * Return value: a newly allocated, zero-terminated Base-64 encoded
207  *               string representing @data.
208  *
209  * Since: 2.12
210  */
211 char *
212 g_base64_encode (const guchar *data, gsize len)
213 {
214   char *out;
215   int state = 0, outlen;
216   int save = 0;
217
218   /* We can use a smaller limit here, since we know the saved state is 0 */
219   out = g_malloc (len * 4 / 3 + 4);
220   outlen = g_base64_encode_step (data, len, FALSE, out, &state, &save);
221   outlen += g_base64_encode_close (FALSE,
222                                    out + outlen, 
223                                    &state, 
224                                    &save);
225   out[outlen] = '\0';
226   return (char *) out;
227 }
228
229 static const unsigned char mime_base64_rank[256] = {
230   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
231   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
232   255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
233    52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
234   255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
235    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
236   255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
237    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
238   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
239   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
240   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
241   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
242   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
243   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
244   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
245   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
246 };
247
248 /**
249  * g_base64_decode_step: decode a chunk of base64 encoded data
250  * @in: binary input data
251  * @len: max length of @in data to decode
252  * @out: output buffer
253  * @state: Saved state between steps, initialize to 0
254  * @save: Saved state between steps, initialize to 0
255  *
256  * Incrementally decode a sequence of binary data from it's Base-64 stringified
257  * representation. By calling this functions multiple times you can convert data
258  * in chunks to avoid having to have the full encoded data in memory.
259  *
260  * The output buffer must be large enough to fit all the data that will
261  * be written to it. Since base64 encodes 3 bytes in 4 chars you need
262  * at least: @len * 3 / 4 bytes.
263  * 
264  * Return value: The number of bytes of output that was written
265  *
266  * Since: 2.12
267  **/
268 gsize
269 g_base64_decode_step (const char  *in, 
270                       gsize        len, 
271                       guchar      *out, 
272                       int         *state, 
273                       guint       *save)
274 {
275   const guchar *inptr;
276   guchar *outptr;
277   const guchar *inend;
278   guchar c;
279   unsigned int v;
280   int i;
281   
282   inend = (const guchar *)in+len;
283   outptr = out;
284   
285   /* convert 4 base64 bytes to 3 normal bytes */
286   v=*save;
287   i=*state;
288   inptr = (const guchar *)in;
289   while (inptr < inend)
290     {
291       c = mime_base64_rank [*inptr++];
292       if (c != 0xff)
293         {
294           v = (v<<6) | c;
295           i++;
296           if (i==4)
297             {
298               *outptr++ = v>>16;
299               *outptr++ = v>>8;
300               *outptr++ = v;
301               i=0;
302             }
303         }
304     }
305   
306   *save = v;
307   *state = i;
308   
309   /* quick scan back for '=' on the end somewhere */
310   /* fortunately we can drop 1 output char for each trailing = (upto 2) */
311   i=2;
312   while (inptr > (const guchar *)in && i)
313     {
314       inptr--;
315       if (mime_base64_rank [*inptr] != 0xff)
316         {
317           if (*inptr == '=')
318             outptr--;
319           i--;
320         }
321     }
322
323   /* if i!= 0 then there is a truncation error! */
324   return outptr - out;
325 }
326
327 /**
328  * g_base64_decode:
329  * @text: zero-terminated string with base64 text to decode.
330  * @out_len: The lenght of the decoded data is written here.
331  *
332  * Decode a sequence of Base-64 encoded text into binary data
333  *
334  * Return value: a newly allocated, buffer containing the binary data
335  *               that @text represents
336  *
337  * Since: 2.12
338  */
339 guchar *
340 g_base64_decode (const char   *text,
341                  gsize        *out_len)
342 {
343   guchar *ret;
344   int inlen, state = 0;
345   guint save = 0;
346   
347   inlen = strlen (text);
348   ret = g_malloc0 (inlen * 3 / 4);
349   
350   *out_len = g_base64_decode_step (text, inlen, ret, &state, &save);
351   
352   return ret; 
353 }
354
355 #define __G_BASE64_C__
356 #include "galiasdef.c"