Blame glib/gbase64.c

Packit ae235b
/* gbase64.c - Base64 encoding/decoding
Packit ae235b
 *
Packit ae235b
 *  Copyright (C) 2006 Alexander Larsson <alexl@redhat.com>
Packit ae235b
 *  Copyright (C) 2000-2003 Ximian Inc.
Packit ae235b
 *
Packit ae235b
 * This library is free software; you can redistribute it and/or
Packit ae235b
 * modify it under the terms of the GNU Lesser General Public
Packit ae235b
 * License as published by the Free Software Foundation; either
Packit ae235b
 * version 2.1 of the License, or (at your option) any later version.
Packit ae235b
 *
Packit ae235b
 * This library is distributed in the hope that it will be useful,
Packit ae235b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit ae235b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit ae235b
 * Lesser General Public License for more details.
Packit ae235b
 *
Packit ae235b
 * You should have received a copy of the GNU Lesser General Public License
Packit ae235b
 * along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit ae235b
 *
Packit ae235b
 * This is based on code in camel, written by:
Packit ae235b
 *    Michael Zucchi <notzed@ximian.com>
Packit ae235b
 *    Jeffrey Stedfast <fejj@ximian.com>
Packit ae235b
 */
Packit ae235b
Packit ae235b
#include "config.h"
Packit ae235b
Packit ae235b
#include <string.h>
Packit ae235b
Packit ae235b
#include "gbase64.h"
Packit ae235b
#include "gtestutils.h"
Packit ae235b
#include "glibintl.h"
Packit ae235b
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * SECTION:base64
Packit ae235b
 * @title: Base64 Encoding
Packit ae235b
 * @short_description: encodes and decodes data in Base64 format
Packit ae235b
 *
Packit ae235b
 * Base64 is an encoding that allows a sequence of arbitrary bytes to be
Packit ae235b
 * encoded as a sequence of printable ASCII characters. For the definition
Packit ae235b
 * of Base64, see 
Packit ae235b
 * [RFC 1421](http://www.ietf.org/rfc/rfc1421.txt)
Packit ae235b
 * or
Packit ae235b
 * [RFC 2045](http://www.ietf.org/rfc/rfc2045.txt).
Packit ae235b
 * Base64 is most commonly used as a MIME transfer encoding
Packit ae235b
 * for email.
Packit ae235b
 *
Packit ae235b
 * GLib supports incremental encoding using g_base64_encode_step() and
Packit ae235b
 * g_base64_encode_close(). Incremental decoding can be done with
Packit ae235b
 * g_base64_decode_step(). To encode or decode data in one go, use
Packit ae235b
 * g_base64_encode() or g_base64_decode(). To avoid memory allocation when
Packit ae235b
 * decoding, you can use g_base64_decode_inplace().
Packit ae235b
 *
Packit ae235b
 * Support for Base64 encoding has been added in GLib 2.12.
Packit ae235b
 */
Packit ae235b
Packit ae235b
static const char base64_alphabet[] =
Packit ae235b
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_base64_encode_step:
Packit ae235b
 * @in: (array length=len) (element-type guint8): the binary data to encode
Packit ae235b
 * @len: the length of @in
Packit ae235b
 * @break_lines: whether to break long lines
Packit ae235b
 * @out: (out) (array) (element-type guint8): pointer to destination buffer
Packit ae235b
 * @state: (inout): Saved state between steps, initialize to 0
Packit ae235b
 * @save: (inout): Saved state between steps, initialize to 0
Packit ae235b
 *
Packit ae235b
 * Incrementally encode a sequence of binary data into its Base-64 stringified
Packit ae235b
 * representation. By calling this function multiple times you can convert
Packit ae235b
 * data in chunks to avoid having to have the full encoded data in memory.
Packit ae235b
 *
Packit ae235b
 * When all of the data has been converted you must call
Packit ae235b
 * g_base64_encode_close() to flush the saved state.
Packit ae235b
 *
Packit ae235b
 * The output buffer must be large enough to fit all the data that will
Packit ae235b
 * be written to it. Due to the way base64 encodes you will need
Packit ae235b
 * at least: (@len / 3 + 1) * 4 + 4 bytes (+ 4 may be needed in case of
Packit ae235b
 * non-zero state). If you enable line-breaking you will need at least:
Packit ae235b
 * ((@len / 3 + 1) * 4 + 4) / 72 + 1 bytes of extra space.
Packit ae235b
 *
Packit ae235b
 * @break_lines is typically used when putting base64-encoded data in emails.
Packit ae235b
 * It breaks the lines at 72 columns instead of putting all of the text on
Packit ae235b
 * the same line. This avoids problems with long lines in the email system.
Packit ae235b
 * Note however that it breaks the lines with `LF` characters, not
Packit ae235b
 * `CR LF` sequences, so the result cannot be passed directly to SMTP
Packit ae235b
 * or certain other protocols.
Packit ae235b
 *
Packit ae235b
 * Returns: The number of bytes of output that was written
Packit ae235b
 *
Packit ae235b
 * Since: 2.12
Packit ae235b
 */
Packit ae235b
gsize
Packit ae235b
g_base64_encode_step (const guchar *in,
Packit ae235b
                      gsize         len,
Packit ae235b
                      gboolean      break_lines,
Packit ae235b
                      gchar        *out,
Packit ae235b
                      gint         *state,
Packit ae235b
                      gint         *save)
Packit ae235b
{
Packit ae235b
  char *outptr;
Packit ae235b
  const guchar *inptr;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (in != NULL, 0);
Packit ae235b
  g_return_val_if_fail (out != NULL, 0);
Packit ae235b
  g_return_val_if_fail (state != NULL, 0);
Packit ae235b
  g_return_val_if_fail (save != NULL, 0);
Packit ae235b
Packit ae235b
  if (len <= 0)
Packit ae235b
    return 0;
Packit ae235b
Packit ae235b
  inptr = in;
Packit ae235b
  outptr = out;
Packit ae235b
Packit ae235b
  if (len + ((char *) save) [0] > 2)
Packit ae235b
    {
Packit ae235b
      const guchar *inend = in+len-2;
Packit ae235b
      int c1, c2, c3;
Packit ae235b
      int already;
Packit ae235b
Packit ae235b
      already = *state;
Packit ae235b
Packit ae235b
      switch (((char *) save) [0])
Packit ae235b
        {
Packit ae235b
        case 1:
Packit ae235b
          c1 = ((unsigned char *) save) [1];
Packit ae235b
          goto skip1;
Packit ae235b
        case 2:
Packit ae235b
          c1 = ((unsigned char *) save) [1];
Packit ae235b
          c2 = ((unsigned char *) save) [2];
Packit ae235b
          goto skip2;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      /*
Packit ae235b
       * yes, we jump into the loop, no i'm not going to change it,
Packit ae235b
       * it's beautiful!
Packit ae235b
       */
Packit ae235b
      while (inptr < inend)
Packit ae235b
        {
Packit ae235b
          c1 = *inptr++;
Packit ae235b
        skip1:
Packit ae235b
          c2 = *inptr++;
Packit ae235b
        skip2:
Packit ae235b
          c3 = *inptr++;
Packit ae235b
          *outptr++ = base64_alphabet [ c1 >> 2 ];
Packit ae235b
          *outptr++ = base64_alphabet [ c2 >> 4 |
Packit ae235b
                                        ((c1&0x3) << 4) ];
Packit ae235b
          *outptr++ = base64_alphabet [ ((c2 &0x0f) << 2) |
Packit ae235b
                                        (c3 >> 6) ];
Packit ae235b
          *outptr++ = base64_alphabet [ c3 & 0x3f ];
Packit ae235b
          /* this is a bit ugly ... */
Packit ae235b
          if (break_lines && (++already) >= 19)
Packit ae235b
            {
Packit ae235b
              *outptr++ = '\n';
Packit ae235b
              already = 0;
Packit ae235b
            }
Packit ae235b
        }
Packit ae235b
Packit ae235b
      ((char *)save)[0] = 0;
Packit ae235b
      len = 2 - (inptr - inend);
Packit ae235b
      *state = already;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (len>0)
Packit ae235b
    {
Packit ae235b
      char *saveout;
Packit ae235b
Packit ae235b
      /* points to the slot for the next char to save */
Packit ae235b
      saveout = & (((char *)save)[1]) + ((char *)save)[0];
Packit ae235b
Packit ae235b
      /* len can only be 0 1 or 2 */
Packit ae235b
      switch(len)
Packit ae235b
        {
Packit ae235b
        case 2: *saveout++ = *inptr++;
Packit ae235b
        case 1: *saveout++ = *inptr++;
Packit ae235b
        }
Packit ae235b
      ((char *)save)[0] += len;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return outptr - out;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_base64_encode_close:
Packit ae235b
 * @break_lines: whether to break long lines
Packit ae235b
 * @out: (out) (array) (element-type guint8): pointer to destination buffer
Packit ae235b
 * @state: (inout): Saved state from g_base64_encode_step()
Packit ae235b
 * @save: (inout): Saved state from g_base64_encode_step()
Packit ae235b
 *
Packit ae235b
 * Flush the status from a sequence of calls to g_base64_encode_step().
Packit ae235b
 *
Packit ae235b
 * The output buffer must be large enough to fit all the data that will
Packit ae235b
 * be written to it. It will need up to 4 bytes, or up to 5 bytes if
Packit ae235b
 * line-breaking is enabled.
Packit ae235b
 *
Packit ae235b
 * The @out array will not be automatically nul-terminated.
Packit ae235b
 *
Packit ae235b
 * Returns: The number of bytes of output that was written
Packit ae235b
 *
Packit ae235b
 * Since: 2.12
Packit ae235b
 */
Packit ae235b
gsize
Packit ae235b
g_base64_encode_close (gboolean  break_lines,
Packit ae235b
                       gchar    *out,
Packit ae235b
                       gint     *state,
Packit ae235b
                       gint     *save)
Packit ae235b
{
Packit ae235b
  int c1, c2;
Packit ae235b
  char *outptr = out;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (out != NULL, 0);
Packit ae235b
  g_return_val_if_fail (state != NULL, 0);
Packit ae235b
  g_return_val_if_fail (save != NULL, 0);
Packit ae235b
Packit ae235b
  c1 = ((unsigned char *) save) [1];
Packit ae235b
  c2 = ((unsigned char *) save) [2];
Packit ae235b
Packit ae235b
  switch (((char *) save) [0])
Packit ae235b
    {
Packit ae235b
    case 2:
Packit ae235b
      outptr [2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
Packit ae235b
      g_assert (outptr [2] != 0);
Packit ae235b
      goto skip;
Packit ae235b
    case 1:
Packit ae235b
      outptr[2] = '=';
Packit ae235b
      c2 = 0;  /* saved state here is not relevant */
Packit ae235b
    skip:
Packit ae235b
      outptr [0] = base64_alphabet [ c1 >> 2 ];
Packit ae235b
      outptr [1] = base64_alphabet [ c2 >> 4 | ( (c1&0x3) << 4 )];
Packit ae235b
      outptr [3] = '=';
Packit ae235b
      outptr += 4;
Packit ae235b
      break;
Packit ae235b
    }
Packit ae235b
  if (break_lines)
Packit ae235b
    *outptr++ = '\n';
Packit ae235b
Packit ae235b
  *save = 0;
Packit ae235b
  *state = 0;
Packit ae235b
Packit ae235b
  return outptr - out;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_base64_encode:
Packit ae235b
 * @data: (array length=len) (element-type guint8): the binary data to encode
Packit ae235b
 * @len: the length of @data
Packit ae235b
 *
Packit ae235b
 * Encode a sequence of binary data into its Base-64 stringified
Packit ae235b
 * representation.
Packit ae235b
 *
Packit ae235b
 * Returns: (transfer full): a newly allocated, zero-terminated Base-64
Packit ae235b
 *               encoded string representing @data. The returned string must
Packit ae235b
 *               be freed with g_free().
Packit ae235b
 *
Packit ae235b
 * Since: 2.12
Packit ae235b
 */
Packit ae235b
gchar *
Packit ae235b
g_base64_encode (const guchar *data,
Packit ae235b
                 gsize         len)
Packit ae235b
{
Packit ae235b
  gchar *out;
Packit ae235b
  gint state = 0, outlen;
Packit ae235b
  gint save = 0;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (data != NULL || len == 0, NULL);
Packit ae235b
Packit ae235b
  /* We can use a smaller limit here, since we know the saved state is 0,
Packit ae235b
     +1 is needed for trailing \0, also check for unlikely integer overflow */
Packit ae235b
  if (len >= ((G_MAXSIZE - 1) / 4 - 1) * 3)
Packit ae235b
    g_error("%s: input too large for Base64 encoding (%"G_GSIZE_FORMAT" chars)",
Packit ae235b
        G_STRLOC, len);
Packit ae235b
Packit ae235b
  out = g_malloc ((len / 3 + 1) * 4 + 1);
Packit ae235b
Packit ae235b
  outlen = g_base64_encode_step (data, len, FALSE, out, &state, &save);
Packit ae235b
  outlen += g_base64_encode_close (FALSE, out + outlen, &state, &save);
Packit ae235b
  out[outlen] = '\0';
Packit ae235b
Packit ae235b
  return (gchar *) out;
Packit ae235b
}
Packit ae235b
Packit ae235b
static const unsigned char mime_base64_rank[256] = {
Packit ae235b
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
Packit ae235b
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
Packit ae235b
  255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
Packit ae235b
   52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
Packit ae235b
  255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
Packit ae235b
   15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
Packit ae235b
  255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
Packit ae235b
   41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
Packit ae235b
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
Packit ae235b
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
Packit ae235b
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
Packit ae235b
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
Packit ae235b
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
Packit ae235b
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
Packit ae235b
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
Packit ae235b
  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
Packit ae235b
};
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_base64_decode_step: (skip)
Packit ae235b
 * @in: (array length=len) (element-type guint8): binary input data
Packit ae235b
 * @len: max length of @in data to decode
Packit ae235b
 * @out: (out caller-allocates) (array) (element-type guint8): output buffer
Packit ae235b
 * @state: (inout): Saved state between steps, initialize to 0
Packit ae235b
 * @save: (inout): Saved state between steps, initialize to 0
Packit ae235b
 *
Packit ae235b
 * Incrementally decode a sequence of binary data from its Base-64 stringified
Packit ae235b
 * representation. By calling this function multiple times you can convert
Packit ae235b
 * data in chunks to avoid having to have the full encoded data in memory.
Packit ae235b
 *
Packit ae235b
 * The output buffer must be large enough to fit all the data that will
Packit ae235b
 * be written to it. Since base64 encodes 3 bytes in 4 chars you need
Packit ae235b
 * at least: (@len / 4) * 3 + 3 bytes (+ 3 may be needed in case of non-zero
Packit ae235b
 * state).
Packit ae235b
 *
Packit ae235b
 * Returns: The number of bytes of output that was written
Packit ae235b
 *
Packit ae235b
 * Since: 2.12
Packit ae235b
 **/
Packit ae235b
gsize
Packit ae235b
g_base64_decode_step (const gchar  *in,
Packit ae235b
                      gsize         len,
Packit ae235b
                      guchar       *out,
Packit ae235b
                      gint         *state,
Packit ae235b
                      guint        *save)
Packit ae235b
{
Packit ae235b
  const guchar *inptr;
Packit ae235b
  guchar *outptr;
Packit ae235b
  const guchar *inend;
Packit ae235b
  guchar c, rank;
Packit ae235b
  guchar last[2];
Packit ae235b
  unsigned int v;
Packit ae235b
  int i;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (in != NULL, 0);
Packit ae235b
  g_return_val_if_fail (out != NULL, 0);
Packit ae235b
  g_return_val_if_fail (state != NULL, 0);
Packit ae235b
  g_return_val_if_fail (save != NULL, 0);
Packit ae235b
Packit ae235b
  if (len <= 0)
Packit ae235b
    return 0;
Packit ae235b
Packit ae235b
  inend = (const guchar *)in+len;
Packit ae235b
  outptr = out;
Packit ae235b
Packit ae235b
  /* convert 4 base64 bytes to 3 normal bytes */
Packit ae235b
  v=*save;
Packit ae235b
  i=*state;
Packit ae235b
Packit ae235b
  last[0] = last[1] = 0;
Packit ae235b
Packit ae235b
  /* we use the sign in the state to determine if we got a padding character
Packit ae235b
     in the previous sequence */
Packit ae235b
  if (i < 0)
Packit ae235b
    {
Packit ae235b
      i = -i;
Packit ae235b
      last[0] = '=';
Packit ae235b
    }
Packit ae235b
Packit ae235b
  inptr = (const guchar *)in;
Packit ae235b
  while (inptr < inend)
Packit ae235b
    {
Packit ae235b
      c = *inptr++;
Packit ae235b
      rank = mime_base64_rank [c];
Packit ae235b
      if (rank != 0xff)
Packit ae235b
        {
Packit ae235b
          last[1] = last[0];
Packit ae235b
          last[0] = c;
Packit ae235b
          v = (v<<6) | rank;
Packit ae235b
          i++;
Packit ae235b
          if (i==4)
Packit ae235b
            {
Packit ae235b
              *outptr++ = v>>16;
Packit ae235b
              if (last[1] != '=')
Packit ae235b
                *outptr++ = v>>8;
Packit ae235b
              if (last[0] != '=')
Packit ae235b
                *outptr++ = v;
Packit ae235b
              i=0;
Packit ae235b
            }
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
Packit ae235b
  *save = v;
Packit ae235b
  *state = last[0] == '=' ? -i : i;
Packit ae235b
Packit ae235b
  return outptr - out;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_base64_decode:
Packit ae235b
 * @text: zero-terminated string with base64 text to decode
Packit ae235b
 * @out_len: (out): The length of the decoded data is written here
Packit ae235b
 *
Packit ae235b
 * Decode a sequence of Base-64 encoded text into binary data.  Note
Packit ae235b
 * that the returned binary data is not necessarily zero-terminated,
Packit ae235b
 * so it should not be used as a character string.
Packit ae235b
 *
Packit ae235b
 * Returns: (transfer full) (array length=out_len) (element-type guint8):
Packit ae235b
 *               newly allocated buffer containing the binary data
Packit ae235b
 *               that @text represents. The returned buffer must
Packit ae235b
 *               be freed with g_free().
Packit ae235b
 *
Packit ae235b
 * Since: 2.12
Packit ae235b
 */
Packit ae235b
guchar *
Packit ae235b
g_base64_decode (const gchar *text,
Packit ae235b
                 gsize       *out_len)
Packit ae235b
{
Packit ae235b
  guchar *ret;
Packit ae235b
  gsize input_length;
Packit ae235b
  gint state = 0;
Packit ae235b
  guint save = 0;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (text != NULL, NULL);
Packit ae235b
  g_return_val_if_fail (out_len != NULL, NULL);
Packit ae235b
Packit ae235b
  input_length = strlen (text);
Packit ae235b
Packit ae235b
  /* We can use a smaller limit here, since we know the saved state is 0,
Packit ae235b
     +1 used to avoid calling g_malloc0(0), and hence returning NULL */
Packit ae235b
  ret = g_malloc0 ((input_length / 4) * 3 + 1);
Packit ae235b
Packit ae235b
  *out_len = g_base64_decode_step (text, input_length, ret, &state, &save);
Packit ae235b
Packit ae235b
  return ret;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_base64_decode_inplace:
Packit ae235b
 * @text: (inout) (array length=out_len) (element-type guint8): zero-terminated
Packit ae235b
 *        string with base64 text to decode
Packit ae235b
 * @out_len: (inout): The length of the decoded data is written here
Packit ae235b
 *
Packit ae235b
 * Decode a sequence of Base-64 encoded text into binary data
Packit ae235b
 * by overwriting the input data.
Packit ae235b
 *
Packit ae235b
 * Returns: (transfer none): The binary data that @text responds. This pointer
Packit ae235b
 *               is the same as the input @text.
Packit ae235b
 *
Packit ae235b
 * Since: 2.20
Packit ae235b
 */
Packit ae235b
guchar *
Packit ae235b
g_base64_decode_inplace (gchar *text,
Packit ae235b
                         gsize *out_len)
Packit ae235b
{
Packit ae235b
  gint input_length, state = 0;
Packit ae235b
  guint save = 0;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (text != NULL, NULL);
Packit ae235b
  g_return_val_if_fail (out_len != NULL, NULL);
Packit ae235b
Packit ae235b
  input_length = strlen (text);
Packit ae235b
Packit ae235b
  g_return_val_if_fail (input_length > 1, NULL);
Packit ae235b
Packit ae235b
  *out_len = g_base64_decode_step (text, input_length, (guchar *) text, &state, &save);
Packit ae235b
Packit ae235b
  return (guchar *) text;
Packit ae235b
}