/***********************************************************************
* _ _ __ _ _
* __ _ _ __ ___| |_ | |__ __ _ ___ ___ / /_ | || |
* / _` | '_ \ / _ \ __| | '_ \ / _` / __|/ _ \ '_ \| || |_
* | (_| | | | | __/ |_ | |_) | (_| \__ \ __/ (_) |__ _|
* \__, |_| |_|\___|\__| |_.__/ \__,_|___/\___|\___/ |_|
* |___/
*
* created by Alfred Reibenschuh <alfredreibenschuh@gmx.net>,
* under the ``GNU Library General Public License´´ (see below).
*
***********************************************************************
*
* Copyright (C) 2003 Free Software Foundation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
***********************************************************************/
#include "base64.h"
#include <string.h>
#include <ctype.h>
static gchar gnet_Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#define gnet_Pad64 '='
static guchar gnet_Base64_rank[256] = {
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, /* 0x00-0x0f */
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, /* 0x10-0x1f */
255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, /* 0x20-0x2f */
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,255,255,255, /* 0x30-0x3f */
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4f */
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, /* 0x50-0x5f */
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6f */
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255, /* 0x70-0x7f */
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, /* 0x80-0x8f */
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, /* 0x90-0x9f */
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, /* 0xa0-0xaf */
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, /* 0xb0-0xbf */
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, /* 0xc0-0xcf */
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, /* 0xd0-0xdf */
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, /* 0xe0-0xef */
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, /* 0xf0-0xff */
};
/**
* gnet_base64_encode
* @src: source buffer
* @srclen: length of the source buffer
* @dstlenp: length of the buffer returned (including the terminating \0)
* @strict: insert new lines as required by RFC 2045
*
* Convert a buffer from binary to base64 representation. Set
* @strict to TRUE to insert a newline every 72th character. This is
* required by RFC 2045, but some applications don't require this.
*
* Returns: caller-owned buffer.
*
**/
gchar*
gnet_base64_encode (gchar* src, gint srclen, gint* dstlenp, gboolean strict)
{
gchar* dst;
gint dstpos;
guchar input[3];
guchar output[4];
gint ocnt;
gint i;
if (srclen == 0)
return NULL; /* FIX: Or return ""? */
/* Calculate required length of dst. 4 bytes of dst are needed for
every 3 bytes of src. */
*dstlenp = (((srclen + 2) / 3) * 4)+5;
if (strict)
*dstlenp += (*dstlenp / 72); /* Handle trailing \n */
dst = g_new(gchar, *dstlenp );
/* bulk encoding */
dstpos = 0;
ocnt = 0;
while (srclen >= 3)
{
/*
Convert 3 bytes of src to 4 bytes of output
output[0] = input[0] 7:2
output[1] = input[0] 1:0 input[1] 7:4
output[2] = input[1] 3:0 input[2] 7:6
output[3] = input[1] 5:0
*/
input[0] = *src++;
input[1] = *src++;
input[2] = *src++;
srclen -= 3;
output[0] = (input[0] >> 2);
output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
output[3] = (input[2] & 0x3f);
g_assert ((dstpos + 4) < *dstlenp);
/* Map output to the Base64 alphabet */
dst[dstpos++] = gnet_Base64[(guint) output[0]];
dst[dstpos++] = gnet_Base64[(guint) output[1]];
dst[dstpos++] = gnet_Base64[(guint) output[2]];
dst[dstpos++] = gnet_Base64[(guint) output[3]];
/* Add a newline if strict and */
if (strict)
if ((++ocnt % (72/4)) == 0)
dst[dstpos++] = '\n';
}
/* Now worry about padding with remaining 1 or 2 bytes */
if (srclen != 0)
{
input[0] = input[1] = input[2] = '\0';
for (i = 0; i < srclen; i++)
input[i] = *src++;
output[0] = (input[0] >> 2);
output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
g_assert ((dstpos + 4) < *dstlenp);
dst[dstpos++] = gnet_Base64[(guint) output[0]];
dst[dstpos++] = gnet_Base64[(guint) output[1]];
if (srclen == 1)
dst[dstpos++] = gnet_Pad64;
else
dst[dstpos++] = gnet_Base64[(guint) output[2]];
dst[dstpos++] = gnet_Pad64;
}
g_assert (dstpos <= *dstlenp);
dst[dstpos] = '\0';
*dstlenp = dstpos + 1;
return dst;
}
/**
* gnet_base64_decode
* @src: the source buffer
* @srclen: the length of the source buffer
* @dstlenp: pointer to length of the destination buffer
*
* Convert a buffer from base64 to binary representation. This
* function is liberal in what it will accept. It ignores non-base64
* symbols.
*
* Returns: caller-owned buffer. The integer pointed to by @dstlenp
* is set to the length of that buffer.
*
**/
gchar*
gnet_base64_decode (gchar* src, gint srclen, gint* dstlenp)
{
gchar* dst;
gint dstidx, state, ch = 0;
gchar res;
guchar pos;
if (srclen == 0)
srclen = strlen(src);
state = 0;
dstidx = 0;
res = 0;
dst = g_new(gchar, srclen+1);
*dstlenp = srclen+1;
while (srclen-- > 0)
{
ch = *src++;
if (gnet_Base64_rank[ch]==255) /* Skip any non-base64 anywhere */
continue;
if (ch == gnet_Pad64)
break;
pos = gnet_Base64_rank[ch];
switch (state)
{
case 0:
if (dst != NULL)
{
dst[dstidx] = (pos << 2);
}
state = 1;
break;
case 1:
if (dst != NULL)
{
dst[dstidx] |= (pos >> 4);
res = ((pos & 0x0f) << 4);
}
dstidx++;
state = 2;
break;
case 2:
if (dst != NULL)
{
dst[dstidx] = res | (pos >> 2);
res = (pos & 0x03) << 6;
}
dstidx++;
state = 3;
break;
case 3:
if (dst != NULL)
{
dst[dstidx] = res | pos;
}
dstidx++;
state = 0;
break;
default:
break;
}
}
/*
* We are done decoding Base-64 chars. Let's see if we ended
* on a byte boundary, and/or with erroneous trailing characters.
*/
if (ch == gnet_Pad64) /* We got a pad char. */
{
switch (state)
{
case 0: /* Invalid = in first position */
case 1: /* Invalid = in second position */
return NULL;
case 2: /* Valid, means one byte of info */
/* Skip any number of spaces. */
while (srclen-- > 0)
{
ch = *src++;
if (gnet_Base64_rank[ch] != 255) break;
}
/* Make sure there is another trailing = sign. */
if (ch != gnet_Pad64)
{
g_free(dst);
*dstlenp = 0;
return NULL;
}
/* FALLTHROUGH */
case 3: /* Valid, means two bytes of info */
/*
* We know this char is an =. Is there anything but
* whitespace after it?
*/
while (srclen-- > 0)
{
ch = *src++;
if (gnet_Base64_rank[ch] != 255)
{
g_free(dst);
*dstlenp = 0;
return NULL;
}
}
/*
* Now make sure for cases 2 and 3 that the "extra"
* bits that slopped past the last full byte were
* zeros. If we don't check them, they become a
* subliminal channel.
*/
if (dst != NULL && res != 0)
{
g_free(dst);
*dstlenp = 0;
return NULL;
}
default:
break;
}
} else
{
/*
* We ended by seeing the end of the string. Make sure we
* have no partial bytes lying around.
*/
if (state != 0)
{
g_free(dst);
*dstlenp = 0;
return NULL;
}
}
dst[dstidx]=0;
*dstlenp = dstidx;
return dst;
}
syntax highlighted by Code2HTML, v. 0.9.1