wxWidgets/src/common/base64.cpp
2007-07-20 17:23:30 +00:00

227 lines
6.9 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/common/base64.cpp
// Purpose: implementation of BASE64 encoding/decoding functions
// Author: Charles Reimers, Vadim Zeitlin
// Created: 2007-06-18
// RCS-ID: $Id$
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
#include "wx/wxprec.h"
#if wxUSE_BASE64
#include "wx/base64.h"
size_t
wxBase64Encode(char *dst, size_t dstLen, const void *src_, size_t srcLen)
{
wxCHECK_MSG( src_, wxCONV_FAILED, _T("NULL input buffer") );
const unsigned char *src = wx_static_cast(const unsigned char *, src_);
static const char b64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
size_t encLen = 0;
// encode blocks of 3 bytes into 4 base64 characters
for ( ; srcLen >= 3; srcLen -= 3, src += 3 )
{
encLen += 4;
if ( dst )
{
if ( encLen > dstLen )
return wxCONV_FAILED;
*dst++ = b64[src[0] >> 2];
*dst++ = b64[((src[0] & 0x03) << 4) | ((src[1] & 0xf0) >> 4)];
*dst++ = b64[((src[1] & 0x0f) << 2) | ((src[2] & 0xc0) >> 6)];
*dst++ = b64[src[2] & 0x3f];
}
}
// finish with the remaining characters
if ( srcLen )
{
encLen += 4;
if ( dst )
{
if ( encLen > dstLen )
return wxCONV_FAILED;
// we have definitely one and maybe two bytes remaining
unsigned char next = srcLen == 2 ? src[1] : 0;
*dst++ = b64[src[0] >> 2];
*dst++ = b64[((src[0] & 0x03) << 4) | ((next & 0xf0) >> 4)];
*dst++ = srcLen == 2 ? b64[((next & 0x0f) << 2)] : '=';
*dst = '=';
}
}
return encLen;
}
size_t
wxBase64Decode(void *dst_, size_t dstLen,
const char *src, size_t srcLen,
wxBase64DecodeMode mode,
size_t *posErr)
{
wxCHECK_MSG( src, wxCONV_FAILED, _T("NULL input buffer") );
unsigned char *dst = wx_static_cast(unsigned char *, dst_);
size_t decLen = 0;
if ( srcLen == wxNO_LEN )
srcLen = strlen(src);
// this table contains the values, in base 64, of all valid characters and
// special values WSP or INV for white space and invalid characters
// respectively as well as a special PAD value for '='
enum
{
WSP = 200,
INV,
PAD
};
static const unsigned char decode[256] =
{
WSP,INV,INV,INV,INV,INV,INV,INV,INV,WSP,WSP,INV,WSP,WSP,INV,INV,
INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,
WSP,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,076,INV,INV,INV,077,
064,065,066,067,070,071,072,073,074,075,INV,INV,INV,PAD,INV,INV,
INV,000,001,002,003,004,005,006,007,010,011,012,013,014,015,016,
017,020,021,022,023,024,025,026,027,030,031,INV,INV,INV,INV,INV,
INV,032,033,034,035,036,037,040,041,042,043,044,045,046,047,050,
051,052,053,054,055,056,057,060,061,062,063,INV,INV,INV,INV,INV,
INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,
INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,
INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,
INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,
INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,
INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,
INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,
INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,INV,
};
// we decode input by groups of 4 characters but things are complicated by
// the fact that there can be whitespace and other junk in it too so keep
// record of where exactly we're inside the current quartet in this var
int n = 0;
unsigned char in[4]; // current quartet
bool end = false; // set when we find padding
size_t padLen = 0; // length lost to padding
const char *p;
for ( p = src; srcLen; p++, srcLen-- )
{
const unsigned char c = decode[wx_static_cast(unsigned char, *p)];
switch ( c )
{
case WSP:
if ( mode == wxBase64DecodeMode_SkipWS )
continue;
// fall through
case INV:
if ( mode == wxBase64DecodeMode_Relaxed )
continue;
// force the loop to stop and an error to be returned
n = -1;
srcLen = 0;
break;
case PAD:
// set the flag telling us that we're past the end now
end = true;
// there can be either a single '=' at the end of a quartet or
// "==" in positions 2 and 3
if ( n == 3 )
{
padLen = 1;
in[n++] = '\0';
}
else if ( (n == 2) && (--srcLen && *++p == '=') )
{
padLen = 2;
in[n++] = '\0';
in[n++] = '\0';
}
else // invalid padding
{
// force the loop terminate with an error
n = -1;
srcLen = 0;
}
break;
default:
if ( end )
{
// nothing is allowed after the end so provoke error return
n = -1;
srcLen = 0;
break;
}
in[n++] = c;
}
if ( n == 4 )
{
// got entire block, decode
decLen += 3 - padLen;
if ( dst )
{
if ( decLen > dstLen )
return wxCONV_FAILED;
// undo the bit shifting done during encoding
*dst++ = in[0] << 2 | in[1] >> 4;
*dst++ = in[1] << 4 | in[2] >> 2;
*dst++ = in[2] << 6 | in[3];
}
n = 0;
}
}
if ( n )
{
if ( posErr )
*posErr = p - src;
return wxCONV_FAILED;
}
return decLen;
}
wxMemoryBuffer wxBase64Decode(const char *src,
size_t srcLen,
wxBase64DecodeMode mode,
size_t *posErr)
{
wxMemoryBuffer buf;
wxCHECK_MSG( src, buf, _T("NULL input buffer") );
if ( srcLen == wxNO_LEN )
srcLen = strlen(src);
size_t len = wxBase64DecodedSize(srcLen);
len = wxBase64Decode(buf.GetWriteBuf(len), len, src, srcLen, mode, posErr);
if ( len == wxCONV_FAILED )
len = 0;
buf.SetDataLen(len);
return buf;
}
#endif // wxUSE_BASE64