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