Coverage Report

Created: 2026-05-23 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/glib/glib/gbase64.c
Line
Count
Source
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
g_base64_encode_step (const guchar *in,
72
                      gsize         len,
73
                      gboolean      break_lines,
74
                      gchar        *out,
75
                      gint         *state,
76
                      gint         *save)
77
0
{
78
0
  char *outptr;
79
0
  const guchar *inptr;
80
81
0
  g_return_val_if_fail (in != NULL || len == 0, 0);
82
0
  g_return_val_if_fail (out != NULL, 0);
83
0
  g_return_val_if_fail (state != NULL, 0);
84
0
  g_return_val_if_fail (save != NULL, 0);
85
86
0
  if (len == 0)
87
0
    return 0;
88
89
0
  inptr = in;
90
0
  outptr = out;
91
92
0
  if (len + ((char *) save) [0] > 2)
93
0
    {
94
0
      const guchar *inend = in+len-2;
95
0
      int c1, c2, c3;
96
0
      int already;
97
98
0
      already = *state;
99
100
0
      switch (((char *) save) [0])
101
0
        {
102
0
        case 1:
103
0
          c1 = ((unsigned char *) save) [1];
104
0
          goto skip1;
105
0
        case 2:
106
0
          c1 = ((unsigned char *) save) [1];
107
0
          c2 = ((unsigned char *) save) [2];
108
0
          goto skip2;
109
0
        }
110
111
      /*
112
       * yes, we jump into the loop, no i'm not going to change it,
113
       * it's beautiful!
114
       */
115
0
      while (inptr < inend)
116
0
        {
117
0
          c1 = *inptr++;
118
0
        skip1:
119
0
          c2 = *inptr++;
120
0
        skip2:
121
0
          c3 = *inptr++;
122
0
          *outptr++ = base64_alphabet [ c1 >> 2 ];
123
0
          *outptr++ = base64_alphabet [ c2 >> 4 |
124
0
                                        ((c1&0x3) << 4) ];
125
0
          *outptr++ = base64_alphabet [ ((c2 &0x0f) << 2) |
126
0
                                        (c3 >> 6) ];
127
0
          *outptr++ = base64_alphabet [ c3 & 0x3f ];
128
          /* this is a bit ugly ... */
129
0
          if (break_lines && (++already) >= 19)
130
0
            {
131
0
              *outptr++ = '\n';
132
0
              already = 0;
133
0
            }
134
0
        }
135
136
0
      ((char *)save)[0] = 0;
137
0
      len = 2 - (inptr - inend);
138
0
      *state = already;
139
0
    }
140
141
0
  g_assert (len == 0 || len == 1 || len == 2);
142
143
0
    {
144
0
      char *saveout;
145
146
      /* points to the slot for the next char to save */
147
0
      saveout = & (((char *)save)[1]) + ((char *)save)[0];
148
149
      /* len can only be 0 1 or 2 */
150
0
      switch(len)
151
0
        {
152
0
        case 2:
153
0
          *saveout++ = *inptr++;
154
0
          G_GNUC_FALLTHROUGH;
155
0
        case 1:
156
0
          *saveout++ = *inptr++;
157
0
        }
158
0
      ((char *)save)[0] += len;
159
0
    }
160
161
0
  return outptr - out;
162
0
}
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
g_base64_encode_close (gboolean  break_lines,
185
                       gchar    *out,
186
                       gint     *state,
187
                       gint     *save)
188
0
{
189
0
  int c1, c2;
190
0
  char *outptr = out;
191
192
0
  g_return_val_if_fail (out != NULL, 0);
193
0
  g_return_val_if_fail (state != NULL, 0);
194
0
  g_return_val_if_fail (save != NULL, 0);
195
196
0
  c1 = ((unsigned char *) save) [1];
197
0
  c2 = ((unsigned char *) save) [2];
198
199
0
  switch (((char *) save) [0])
200
0
    {
201
0
    case 2:
202
0
      outptr [2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
203
0
      g_assert (outptr [2] != 0);
204
0
      goto skip;
205
0
    case 1:
206
0
      outptr[2] = '=';
207
0
      c2 = 0;  /* saved state here is not relevant */
208
0
    skip:
209
0
      outptr [0] = base64_alphabet [ c1 >> 2 ];
210
0
      outptr [1] = base64_alphabet [ c2 >> 4 | ( (c1&0x3) << 4 )];
211
0
      outptr [3] = '=';
212
0
      outptr += 4;
213
0
      break;
214
0
    }
215
0
  if (break_lines)
216
0
    *outptr++ = '\n';
217
218
0
  *save = 0;
219
0
  *state = 0;
220
221
0
  return outptr - out;
222
0
}
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
g_base64_encode (const guchar *data,
240
                 gsize         len)
241
0
{
242
0
  gchar *out;
243
0
  gint state = 0;
244
0
  gint save = 0;
245
0
  gsize outlen;
246
0
  gsize allocsize;
247
248
0
  g_return_val_if_fail (data != NULL || len == 0, NULL);
249
250
  /* We can use a smaller limit here, since we know the saved state is 0,
251
     +1 is needed for trailing \0, also check for unlikely integer overflow */
252
0
  g_return_val_if_fail (len < ((G_MAXSIZE - 1) / 4 - 1) * 3, NULL);
253
254
0
  allocsize = (len / 3 + 1) * 4 + 1;
255
0
  out = g_malloc (allocsize);
256
257
0
  outlen = g_base64_encode_step (data, len, FALSE, out, &state, &save);
258
0
  g_assert (outlen <= allocsize);
259
260
0
  outlen += g_base64_encode_close (FALSE, out + outlen, &state, &save);
261
0
  g_assert (outlen <= allocsize);
262
263
0
  out[outlen] = '\0';
264
265
0
  return (gchar *) out;
266
0
}
267
268
static const unsigned char mime_base64_rank[256] = {
269
  255,255,255,255,255,255,255,255,255,255,255,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, 62,255,255,255, 63,
272
   52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
273
  255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
274
   15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
275
  255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
276
   41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
277
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
278
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
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,255,255,255,255,255,
282
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
283
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
284
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
285
};
286
287
/**
288
 * g_base64_decode_step: (skip)
289
 * @in: (array length=len): base-64 encoded input data
290
 * @len: max length of @in data to decode
291
 * @out: (out caller-allocates) (array) (element-type guint8): output buffer
292
 * @state: (inout): Saved state between steps, initialize to 0
293
 * @save: (inout): Saved state between steps, initialize to 0
294
 *
295
 * Incrementally decode a sequence of binary data from its Base-64 stringified
296
 * representation. By calling this function multiple times you can convert
297
 * data in chunks to avoid having to have the full encoded data in memory.
298
 *
299
 * The output buffer must be large enough to fit all the data that will
300
 * be written to it. Since base64 encodes 3 bytes in 4 chars you need
301
 * at least: (@len / 4) * 3 + 3 bytes (+ 3 may be needed in case of non-zero
302
 * state).
303
 *
304
 * Returns: The number of bytes of output that was written
305
 *
306
 * Since: 2.12
307
 **/
308
gsize
309
g_base64_decode_step (const gchar  *in,
310
                      gsize         len,
311
                      guchar       *out,
312
                      gint         *state,
313
                      guint        *save)
314
11.2k
{
315
11.2k
  const guchar *inptr;
316
11.2k
  guchar *outptr;
317
11.2k
  const guchar *inend;
318
11.2k
  guchar c, rank;
319
11.2k
  guchar last[2];
320
11.2k
  unsigned int v;
321
11.2k
  int i;
322
323
11.2k
  g_return_val_if_fail (in != NULL || len == 0, 0);
324
11.2k
  g_return_val_if_fail (out != NULL, 0);
325
11.2k
  g_return_val_if_fail (state != NULL, 0);
326
11.2k
  g_return_val_if_fail (save != NULL, 0);
327
328
11.2k
  if (len == 0)
329
3.07k
    return 0;
330
331
8.16k
  inend = (const guchar *)in+len;
332
8.16k
  outptr = out;
333
334
  /* convert 4 base64 bytes to 3 normal bytes */
335
8.16k
  v=*save;
336
8.16k
  i=*state;
337
338
8.16k
  last[0] = last[1] = 0;
339
340
  /* we use the sign in the state to determine if we got a padding character
341
     in the previous sequence */
342
8.16k
  if (i < 0)
343
0
    {
344
0
      i = -i;
345
0
      last[0] = '=';
346
0
    }
347
348
8.16k
  inptr = (const guchar *)in;
349
129k
  while (inptr < inend)
350
121k
    {
351
121k
      c = *inptr++;
352
121k
      rank = mime_base64_rank [c];
353
121k
      if (rank != 0xff)
354
112k
        {
355
112k
          last[1] = last[0];
356
112k
          last[0] = c;
357
112k
          v = (v<<6) | rank;
358
112k
          i++;
359
112k
          if (i==4)
360
24.9k
            {
361
24.9k
              *outptr++ = v>>16;
362
24.9k
              if (last[1] != '=')
363
21.9k
                *outptr++ = v>>8;
364
24.9k
              if (last[0] != '=')
365
21.3k
                *outptr++ = v;
366
24.9k
              i=0;
367
24.9k
            }
368
112k
        }
369
121k
    }
370
371
8.16k
  *save = v;
372
8.16k
  *state = last[0] == '=' ? -i : i;
373
374
8.16k
  return outptr - out;
375
11.2k
}
376
377
/**
378
 * g_base64_decode:
379
 * @text: (not nullable): zero-terminated string with base64 text to decode
380
 * @out_len: (out): The length of the decoded data is written here
381
 *
382
 * Decode a sequence of Base-64 encoded text into binary data.  Note
383
 * that the returned binary data is not necessarily zero-terminated,
384
 * so it should not be used as a character string.
385
 *
386
 * Returns: (transfer full) (array length=out_len) (element-type guint8):
387
 *               newly allocated buffer containing the binary data
388
 *               that @text represents. The returned buffer must
389
 *               be freed with g_free().
390
 *
391
 * Since: 2.12
392
 */
393
guchar *
394
g_base64_decode (const gchar *text,
395
                 gsize       *out_len)
396
11.2k
{
397
11.2k
  guchar *ret;
398
11.2k
  gsize input_length;
399
11.2k
  gint state = 0;
400
11.2k
  guint save = 0;
401
402
11.2k
  g_return_val_if_fail (text != NULL, NULL);
403
11.2k
  g_return_val_if_fail (out_len != NULL, NULL);
404
405
11.2k
  input_length = strlen (text);
406
407
  /* We can use a smaller limit here, since we know the saved state is 0,
408
     +1 used to avoid calling g_malloc0(0), and hence returning NULL */
409
11.2k
  ret = g_malloc0 ((input_length / 4) * 3 + 1);
410
411
11.2k
  *out_len = g_base64_decode_step (text, input_length, ret, &state, &save);
412
413
11.2k
  return ret;
414
11.2k
}
415
416
/**
417
 * g_base64_decode_inplace:
418
 * @text: (inout) (array length=out_len) (element-type guint8): zero-terminated
419
 *        string with base64 text to decode
420
 * @out_len: (inout): The length of the decoded data is written here
421
 *
422
 * Decode a sequence of Base-64 encoded text into binary data
423
 * by overwriting the input data.
424
 *
425
 * Returns: (transfer none): The binary data that @text responds. This pointer
426
 *               is the same as the input @text.
427
 *
428
 * Since: 2.20
429
 */
430
guchar *
431
g_base64_decode_inplace (gchar *text,
432
                         gsize *out_len)
433
0
{
434
0
  gint state = 0;
435
0
  size_t input_length;
436
0
  guint save = 0;
437
438
0
  g_return_val_if_fail (text != NULL, NULL);
439
0
  g_return_val_if_fail (out_len != NULL, NULL);
440
441
0
  input_length = strlen (text);
442
443
0
  g_return_val_if_fail (input_length > 1, NULL);
444
445
0
  *out_len = g_base64_decode_step (text, input_length, (guchar *) text, &state, &save);
446
447
0
  return (guchar *) text;
448
0
}