[kdbus] KDBUS_ITEM_PAYLOAD_OFF items are (once again) relative to msg header
[platform/upstream/glib.git] / glib / gbase64.c
index 08c1c24..a6cbea4 100644 (file)
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * This is based on code in camel, written by:
  *    Michael Zucchi <notzed@ximian.com>
 #include <string.h>
 
 #include "gbase64.h"
-#include "glib.h"
+#include "gtestutils.h"
 #include "glibintl.h"
 
-#include "galias.h"
+
+/**
+ * SECTION:base64
+ * @title: Base64 Encoding
+ * @short_description: encodes and decodes data in Base64 format
+ *
+ * Base64 is an encoding that allows a sequence of arbitrary bytes to be
+ * encoded as a sequence of printable ASCII characters. For the definition
+ * of Base64, see 
+ * [RFC 1421](http://www.ietf.org/rfc/rfc1421.txt)
+ * or
+ * [RFC 2045](http://www.ietf.org/rfc/rfc2045.txt).
+ * Base64 is most commonly used as a MIME transfer encoding
+ * for email.
+ *
+ * GLib supports incremental encoding using g_base64_encode_step() and
+ * g_base64_encode_close(). Incremental decoding can be done with
+ * g_base64_decode_step(). To encode or decode data in one go, use
+ * g_base64_encode() or g_base64_decode(). To avoid memory allocation when
+ * decoding, you can use g_base64_decode_inplace().
+ *
+ * Support for Base64 encoding has been added in GLib 2.12.
+ */
 
 static const char base64_alphabet[] =
-       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 /**
  * g_base64_encode_step:
- * @in: the binary data to encode.
- * @len: the length of @in.
+ * @in: (array length=len) (element-type guint8): the binary data to encode
+ * @len: the length of @in
  * @break_lines: whether to break long lines
- * @out: pointer to destination buffer
- * @state: Saved state between steps, initialize to 0
- * @save: Saved state between steps, initialize to 0
+ * @out: (out) (array) (element-type guint8): pointer to destination buffer
+ * @state: (inout): Saved state between steps, initialize to 0
+ * @save: (inout): Saved state between steps, initialize to 0
  *
- * Incrementally encode a sequence of binary data into it's Base-64 stringified
- * representation. By calling this functions multiple times you can convert data
- * in chunks to avoid having to have the full encoded data in memory.
+ * Incrementally encode a sequence of binary data into its Base-64 stringified
+ * representation. By calling this function multiple times you can convert
+ * data in chunks to avoid having to have the full encoded data in memory.
  *
- * When all the data has been converted you must call g_base64_encode_close()
- * to flush the saved state.
+ * When all of the data has been converted you must call
+ * g_base64_encode_close() to flush the saved state.
  *
  * The output buffer must be large enough to fit all the data that will
  * be written to it. Due to the way base64 encodes you will need
- * at least: @len * 4 / 3 + 6 bytes. If you enable line-breaking you will
- * need at least: @len * 4 / 3 + @len * 4 / (3 * 72) + 7 bytes.
+ * at least: (@len / 3 + 1) * 4 + 4 bytes (+ 4 may be needed in case of
+ * non-zero state). If you enable line-breaking you will need at least:
+ * ((@len / 3 + 1) * 4 + 4) / 72 + 1 bytes of extra space.
  *
  * @break_lines is typically used when putting base64-encoded data in emails.
- * It breaks the lines at 72 columns instead of putting all text on the same
- * line. This avoids problems with long lines in the email system.
+ * It breaks the lines at 72 columns instead of putting all of the text on
+ * the same line. This avoids problems with long lines in the email system.
+ * Note however that it breaks the lines with `LF` characters, not
+ * `CR LF` sequences, so the result cannot be passed directly to SMTP
+ * or certain other protocols.
  *
- * Return value: The number of bytes of output that was written
+ * Returns: The number of bytes of output that was written
  *
  * Since: 2.12
  */
 gsize
-g_base64_encode_step (const guchar *in, 
-                     gsize         len, 
-                     gboolean      break_lines, 
-                     gchar        *out, 
-                     gint         *state, 
-                     gint         *save)
+g_base64_encode_step (const guchar *in,
+                      gsize         len,
+                      gboolean      break_lines,
+                      gchar        *out,
+                      gint         *state,
+                      gint         *save)
 {
   char *outptr;
   const guchar *inptr;
-  
+
+  g_return_val_if_fail (in != NULL, 0);
+  g_return_val_if_fail (out != NULL, 0);
+  g_return_val_if_fail (state != NULL, 0);
+  g_return_val_if_fail (save != NULL, 0);
+
   if (len <= 0)
     return 0;
-  
+
   inptr = in;
   outptr = out;
-  
+
   if (len + ((char *) save) [0] > 2)
     {
       const guchar *inend = in+len-2;
       int c1, c2, c3;
       int already;
-      
+
       already = *state;
-      
+
       switch (((char *) save) [0])
-       {       
-       case 1: 
-         c1 = ((unsigned char *) save) [1]; 
+        {
+        case 1:
+          c1 = ((unsigned char *) save) [1];
           goto skip1;
-       case 2: 
+        case 2:
           c1 = ((unsigned char *) save) [1];
-         c2 = ((unsigned char *) save) [2]; 
+          c2 = ((unsigned char *) save) [2];
           goto skip2;
-       }
-      
-      /* 
-       * yes, we jump into the loop, no i'm not going to change it, 
-       * it's beautiful! 
+        }
+
+      /*
+       * yes, we jump into the loop, no i'm not going to change it,
+       * it's beautiful!
        */
       while (inptr < inend)
-       {
-         c1 = *inptr++;
-       skip1:
-         c2 = *inptr++;
-       skip2:
-         c3 = *inptr++;
-         *outptr++ = base64_alphabet [ c1 >> 2 ];
-         *outptr++ = base64_alphabet [ c2 >> 4 | 
-                                       ((c1&0x3) << 4) ];
-         *outptr++ = base64_alphabet [ ((c2 &0x0f) << 2) | 
-                                       (c3 >> 6) ];
-         *outptr++ = base64_alphabet [ c3 & 0x3f ];
-         /* this is a bit ugly ... */
-         if (break_lines && (++already) >= 19)
-           {
-             *outptr++ = '\n';
-             already = 0;
-           }
-       }
-      
+        {
+          c1 = *inptr++;
+        skip1:
+          c2 = *inptr++;
+        skip2:
+          c3 = *inptr++;
+          *outptr++ = base64_alphabet [ c1 >> 2 ];
+          *outptr++ = base64_alphabet [ c2 >> 4 |
+                                        ((c1&0x3) << 4) ];
+          *outptr++ = base64_alphabet [ ((c2 &0x0f) << 2) |
+                                        (c3 >> 6) ];
+          *outptr++ = base64_alphabet [ c3 & 0x3f ];
+          /* this is a bit ugly ... */
+          if (break_lines && (++already) >= 19)
+            {
+              *outptr++ = '\n';
+              already = 0;
+            }
+        }
+
       ((char *)save)[0] = 0;
       len = 2 - (inptr - inend);
       *state = already;
     }
-  
+
   if (len>0)
     {
       char *saveout;
-      
+
       /* points to the slot for the next char to save */
       saveout = & (((char *)save)[1]) + ((char *)save)[0];
-      
+
       /* len can only be 0 1 or 2 */
       switch(len)
-       {
-       case 2: *saveout++ = *inptr++;
-       case 1: *saveout++ = *inptr++;
-       }
+        {
+        case 2: *saveout++ = *inptr++;
+        case 1: *saveout++ = *inptr++;
+        }
       ((char *)save)[0] += len;
     }
-  
+
   return outptr - out;
 }
 
 /**
  * g_base64_encode_close:
  * @break_lines: whether to break long lines
- * @out: pointer to destination buffer
- * @state: Saved state from g_base64_encode_step()
- * @save: Saved state from g_base64_encode_step()
+ * @out: (out) (array) (element-type guint8): pointer to destination buffer
+ * @state: (inout): Saved state from g_base64_encode_step()
+ * @save: (inout): Saved state from g_base64_encode_step()
  *
  * Flush the status from a sequence of calls to g_base64_encode_step().
  *
- * Return value: The number of bytes of output that was written
+ * The output buffer must be large enough to fit all the data that will
+ * be written to it. It will need up to 4 bytes, or up to 5 bytes if
+ * line-breaking is enabled.
+ *
+ * Returns: The number of bytes of output that was written
  *
  * Since: 2.12
  */
 gsize
 g_base64_encode_close (gboolean  break_lines,
-                      gchar    *out, 
-                      gint     *state, 
-                      gint     *save)
+                       gchar    *out,
+                       gint     *state,
+                       gint     *save)
 {
   int c1, c2;
   char *outptr = out;
 
+  g_return_val_if_fail (out != NULL, 0);
+  g_return_val_if_fail (state != NULL, 0);
+  g_return_val_if_fail (save != NULL, 0);
+
   c1 = ((unsigned char *) save) [1];
   c2 = ((unsigned char *) save) [2];
-  
+
   switch (((char *) save) [0])
     {
     case 2:
@@ -192,42 +229,49 @@ g_base64_encode_close (gboolean  break_lines,
     }
   if (break_lines)
     *outptr++ = '\n';
-  
+
   *save = 0;
   *state = 0;
-  
+
   return outptr - out;
 }
 
 /**
  * g_base64_encode:
- * @data: the binary data to encode.
- * @len: the length of @data.
+ * @data: (array length=len) (element-type guint8): the binary data to encode
+ * @len: the length of @data
  *
- * Encode a sequence of binary data into it's Base-64 stringified
+ * Encode a sequence of binary data into its Base-64 stringified
  * representation.
  *
- * Return value: a newly allocated, zero-terminated Base-64 encoded
- *               string representing @data.
+ * Returns: (transfer full): a newly allocated, zero-terminated Base-64
+ *               encoded string representing @data. The returned string must
+ *               be freed with g_free().
  *
  * Since: 2.12
  */
 gchar *
-g_base64_encode (const guchar *data, 
+g_base64_encode (const guchar *data,
                  gsize         len)
 {
   gchar *out;
   gint state = 0, outlen;
   gint save = 0;
 
-  /* We can use a smaller limit here, since we know the saved state is 0 */
-  out = g_malloc (len * 4 / 3 + 4);
+  g_return_val_if_fail (data != NULL || len == 0, NULL);
+
+  /* We can use a smaller limit here, since we know the saved state is 0,
+     +1 is needed for trailing \0, also check for unlikely integer overflow */
+  if (len >= ((G_MAXSIZE - 1) / 4 - 1) * 3)
+    g_error("%s: input too large for Base64 encoding (%"G_GSIZE_FORMAT" chars)",
+        G_STRLOC, len);
+
+  out = g_malloc ((len / 3 + 1) * 4 + 1);
+
   outlen = g_base64_encode_step (data, len, FALSE, out, &state, &save);
-  outlen += g_base64_encode_close (FALSE,
-                                  out + outlen, 
-                                  &state, 
-                                  &save);
+  outlen += g_base64_encode_close (FALSE, out + outlen, &state, &save);
   out[outlen] = '\0';
+
   return (gchar *) out;
 }
 
@@ -251,31 +295,32 @@ static const unsigned char mime_base64_rank[256] = {
 };
 
 /**
- * g_base64_decode_step: 
- * @in: binary input data
+ * g_base64_decode_step:
+ * @in: (array length=len) (element-type guint8): binary input data
  * @len: max length of @in data to decode
- * @out: output buffer
- * @state: Saved state between steps, initialize to 0
- * @save: Saved state between steps, initialize to 0
+ * @out: (out) (array) (element-type guint8): output buffer
+ * @state: (inout): Saved state between steps, initialize to 0
+ * @save: (inout): Saved state between steps, initialize to 0
  *
- * Incrementally decode a sequence of binary data from it's Base-64 stringified
- * representation. By calling this functions multiple times you can convert data
- * in chunks to avoid having to have the full encoded data in memory.
+ * Incrementally decode a sequence of binary data from its Base-64 stringified
+ * representation. By calling this function multiple times you can convert
+ * data in chunks to avoid having to have the full encoded data in memory.
  *
  * The output buffer must be large enough to fit all the data that will
  * be written to it. Since base64 encodes 3 bytes in 4 chars you need
- * at least: @len * 3 / 4 bytes.
- * 
- * Return value: The number of bytes of output that was written
+ * at least: (@len / 4) * 3 + 3 bytes (+ 3 may be needed in case of non-zero
+ * state).
+ *
+ * Returns: The number of bytes of output that was written
  *
  * Since: 2.12
  **/
 gsize
-g_base64_decode_step (const gchar  *in, 
-                     gsize         len, 
-                     guchar       *out, 
-                     gint         *state, 
-                     guint        *save)
+g_base64_decode_step (const gchar  *in,
+                      gsize         len,
+                      guchar       *out,
+                      gint         *state,
+                      guint        *save)
 {
   const guchar *inptr;
   guchar *outptr;
@@ -284,70 +329,129 @@ g_base64_decode_step (const gchar  *in,
   guchar last[2];
   unsigned int v;
   int i;
-  
+
+  g_return_val_if_fail (in != NULL, 0);
+  g_return_val_if_fail (out != NULL, 0);
+  g_return_val_if_fail (state != NULL, 0);
+  g_return_val_if_fail (save != NULL, 0);
+
+  if (len <= 0)
+    return 0;
+
   inend = (const guchar *)in+len;
   outptr = out;
-  
+
   /* convert 4 base64 bytes to 3 normal bytes */
   v=*save;
   i=*state;
-  inptr = (const guchar *)in;
+
   last[0] = last[1] = 0;
+
+  /* we use the sign in the state to determine if we got a padding character
+     in the previous sequence */
+  if (i < 0)
+    {
+      i = -i;
+      last[0] = '=';
+    }
+
+  inptr = (const guchar *)in;
   while (inptr < inend)
     {
       c = *inptr++;
       rank = mime_base64_rank [c];
       if (rank != 0xff)
-       {
-         last[1] = last[0];
-         last[0] = c;
-         v = (v<<6) | rank;
-         i++;
-         if (i==4)
-           {
-             *outptr++ = v>>16;
-             if (last[1] != '=')
-               *outptr++ = v>>8;
-             if (last[0] != '=')
-               *outptr++ = v;
-             i=0;
-           }
-       }
+        {
+          last[1] = last[0];
+          last[0] = c;
+          v = (v<<6) | rank;
+          i++;
+          if (i==4)
+            {
+              *outptr++ = v>>16;
+              if (last[1] != '=')
+                *outptr++ = v>>8;
+              if (last[0] != '=')
+                *outptr++ = v;
+              i=0;
+            }
+        }
     }
-  
+
   *save = v;
-  *state = i;
-  
+  *state = last[0] == '=' ? -i : i;
+
   return outptr - out;
 }
 
 /**
  * g_base64_decode:
- * @text: zero-terminated string with base64 text to decode.
- * @out_len: The lenght of the decoded data is written here.
+ * @text: zero-terminated string with base64 text to decode
+ * @out_len: (out): The length of the decoded data is written here
  *
- * Decode a sequence of Base-64 encoded text into binary data
+ * Decode a sequence of Base-64 encoded text into binary data.  Note
+ * that the returned binary data is not necessarily zero-terminated,
+ * so it should not be used as a character string.
  *
- * Return value: a newly allocated, buffer containing the binary data
- *               that @text represents
+ * Returns: (transfer full) (array length=out_len) (element-type guint8):
+ *               newly allocated buffer containing the binary data
+ *               that @text represents. The returned buffer must
+ *               be freed with g_free().
  *
  * Since: 2.12
  */
 guchar *
 g_base64_decode (const gchar *text,
-                gsize       *out_len)
+                 gsize       *out_len)
 {
   guchar *ret;
-  gint inlen, state = 0;
+  gsize input_length;
+  gint state = 0;
   guint save = 0;
-  
-  inlen = strlen (text);
-  ret = g_malloc0 (inlen * 3 / 4);
-  
-  *out_len = g_base64_decode_step (text, inlen, ret, &state, &save);
-  
-  return ret; 
+
+  g_return_val_if_fail (text != NULL, NULL);
+  g_return_val_if_fail (out_len != NULL, NULL);
+
+  input_length = strlen (text);
+
+  /* We can use a smaller limit here, since we know the saved state is 0,
+     +1 used to avoid calling g_malloc0(0), and hence returning NULL */
+  ret = g_malloc0 ((input_length / 4) * 3 + 1);
+
+  *out_len = g_base64_decode_step (text, input_length, ret, &state, &save);
+
+  return ret;
 }
 
-#define __G_BASE64_C__
-#include "galiasdef.c"
+/**
+ * g_base64_decode_inplace:
+ * @text: (inout) (array length=out_len) (element-type guint8): zero-terminated
+ *        string with base64 text to decode
+ * @out_len: (inout): The length of the decoded data is written here
+ *
+ * Decode a sequence of Base-64 encoded text into binary data
+ * by overwriting the input data.
+ *
+ * Returns: (transfer none): The binary data that @text responds. This pointer
+ *               is the same as the input @text.
+ *
+ * Since: 2.20
+ */
+guchar *
+g_base64_decode_inplace (gchar *text,
+                         gsize *out_len)
+{
+  gint input_length, state = 0;
+  guint save = 0;
+
+  g_return_val_if_fail (text != NULL, NULL);
+  g_return_val_if_fail (out_len != NULL, NULL);
+
+  input_length = strlen (text);
+
+  g_return_val_if_fail (input_length > 1, NULL);
+
+  *out_len = g_base64_decode_step (text, input_length, (guchar *) text, &state, &save);
+
+  return (guchar *) text;
+}