X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgbase64.c;h=a6cbea459bca7368383ef476461360fe4d09ed21;hb=2a53b4d0e2c98a14aedf31e38f0ad1fb2e8fe26f;hp=c4654c8fb9d085a3a9901a82f2c434f15a4024e4;hpb=5f4e467f336600e24348fb38b89c41b53926a159;p=platform%2Fupstream%2Fglib.git diff --git a/glib/gbase64.c b/glib/gbase64.c index c4654c8..a6cbea4 100644 --- a/glib/gbase64.c +++ b/glib/gbase64.c @@ -10,13 +10,11 @@ * * 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 . * * This is based on code in camel, written by: * Michael Zucchi @@ -28,153 +26,192 @@ #include #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 function multiple times you can convert + * 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 of the data has been converted you must call + * 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 of the text on + * 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 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 its Base-64 stringified - * representation. By calling this function multiple times you can convert + * 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 length 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; +}