Added Base64 class: encode/decode data using BASE64 alphabet.
git-svn-id: http://voip.null.ro/svn/yate@1870 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
969badc650
commit
324316ea9c
|
@ -0,0 +1,281 @@
|
|||
/**
|
||||
* Base64.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Base64 data encoding and decoding
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2006 Null Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "yateclass.h"
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
// Padding char
|
||||
#define PADDING_CHAR '='
|
||||
|
||||
static String s_eoln = "\r\n";
|
||||
static String s_ignore = "=\r\n\t ";
|
||||
|
||||
// Base64 alphabet
|
||||
// See RFC 4648 Table 1
|
||||
static char s_alphabet[65] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
#define IC 255
|
||||
// ASCII to Base64 translation table
|
||||
// Each element except for IC represents an index in s_alphabet
|
||||
static unsigned char s_ato64[256] = {
|
||||
IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,
|
||||
IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,
|
||||
IC,IC,IC,62,IC,IC,IC,63,52,53,54,55,56,57,58,59,60,61,IC,IC,
|
||||
IC,IC,IC,IC,IC, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
|
||||
15,16,17,18,19,20,21,22,23,24,25,IC,IC,IC,IC,IC,IC,26,27,28,
|
||||
29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,
|
||||
49,50,51,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,
|
||||
IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,
|
||||
IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,
|
||||
IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,
|
||||
IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,
|
||||
IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,
|
||||
IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC,IC
|
||||
};
|
||||
#undef IC
|
||||
|
||||
// Check in the translation table if 'ch' is a valid Base64 char
|
||||
static inline bool valid(char ch)
|
||||
{
|
||||
return s_ato64[(int)ch] < 64;
|
||||
}
|
||||
|
||||
// Check if 'ch' should be ignored
|
||||
// Check in the translation table if 'ch' is a valid Base64 char
|
||||
// Return -1 to ignore it, 1 to accept it, 0 on error
|
||||
static inline int validLiberal(char ch)
|
||||
{
|
||||
for (unsigned int i = 0; i < s_ignore.length(); i++)
|
||||
if (s_ignore[i] == ch)
|
||||
return -1;
|
||||
return valid(ch) ? 1 : 0;
|
||||
}
|
||||
|
||||
// Add end of line to dest and increase index if lines is non 0 and end
|
||||
// if line was reached
|
||||
static inline void addEoln(String& dest, unsigned int& idx,
|
||||
unsigned int& lines, unsigned int& crtLine, unsigned int lineLen)
|
||||
{
|
||||
if (!(lines && crtLine == lineLen))
|
||||
return;
|
||||
char* d = (char*)dest.c_str();
|
||||
d[idx++] = s_eoln[0];
|
||||
d[idx++] = s_eoln[1];
|
||||
crtLine = 0;
|
||||
lines--;
|
||||
}
|
||||
|
||||
// Add an encoded Base64 char to a destination string after clearing
|
||||
// the bits 6 and 7. Increase the string's index
|
||||
// Add end of line to dest and increase index if lines is non 0 and end
|
||||
// if line was reached
|
||||
static inline void addEnc(String& dest, unsigned int& idx, unsigned char ch,
|
||||
unsigned int& lines, unsigned int& crtLine, unsigned int lineLen)
|
||||
{
|
||||
((char*)dest.c_str())[idx++] = s_alphabet[ch & 0x3f];
|
||||
addEoln(dest,idx,lines,crtLine,lineLen);
|
||||
}
|
||||
|
||||
// Add a decoded char buffer to a destination buffer and increase the index
|
||||
// Len must be is 2,3,4
|
||||
static inline void addDec(DataBlock& dest, unsigned int& idx,
|
||||
unsigned char* dec, unsigned int len)
|
||||
{
|
||||
unsigned char* d = ((unsigned char*)dest.data());
|
||||
if (len == 4) {
|
||||
d[idx++] = dec[0] << 2 | dec[1] >> 4;
|
||||
d[idx++] = dec[1] << 4 | dec[2] >> 2;
|
||||
d[idx++] = dec[2] << 6 | dec[3];
|
||||
}
|
||||
else if (len == 3) {
|
||||
d[idx++] = dec[0] << 2 | dec[1] >> 4;
|
||||
d[idx++] = dec[1] << 4 | dec[2] >> 2;
|
||||
}
|
||||
else
|
||||
d[idx++] = dec[0] << 2 | dec[1] >> 4;
|
||||
}
|
||||
|
||||
// Add a padding char to dest and increase index
|
||||
static inline void addPadding(String& dest, unsigned int& idx,
|
||||
unsigned int& lines, unsigned int& crtLine, unsigned int lineLen)
|
||||
{
|
||||
((char*)dest.c_str())[idx++] = PADDING_CHAR;
|
||||
addEoln(dest,idx,lines,crtLine,lineLen);
|
||||
}
|
||||
|
||||
// Encode this buffer to a destination string
|
||||
void Base64::encode(String& dest, unsigned int lineLen, bool lineAtEnd)
|
||||
{
|
||||
dest = "";
|
||||
if (!length())
|
||||
return;
|
||||
|
||||
unsigned char* s = (unsigned char*)data(); // Source buffer
|
||||
unsigned int rest = length() % 3; // The number of bytes that will need padding
|
||||
unsigned int full = length() - rest; // The number of bytes in source that will
|
||||
// be processed in 3-byte chunks
|
||||
unsigned int i = 0; // Source index
|
||||
unsigned int lines = 0; // Number of lines
|
||||
unsigned int crtLine = 0; // Index in current line
|
||||
unsigned int iDest = 0; // Destination index
|
||||
unsigned int len = full / 3 * 4 + (rest ? 4 : 0); // Destination length, without EOLNs
|
||||
|
||||
// Calculate how many lines we need (except for the last one)
|
||||
if (lineLen) {
|
||||
lines = len / lineLen;
|
||||
if (0 == (len % lineLen) && lines)
|
||||
lines--;
|
||||
}
|
||||
dest.assign(PADDING_CHAR,len + lines * s_eoln.length());
|
||||
|
||||
DDebug("Base64",DebugAll,
|
||||
"Encoding %u bytes (full=%u rest=%u) to %u bytes lines=%u",
|
||||
length(),full,rest,dest.length(),lines);
|
||||
|
||||
// Encode each 3 bytes chunk from source to 4 bytes Base64 destination
|
||||
// 1: s_alphabet[bits 2-7 from s[i]]
|
||||
// 2: s_alphabet[bits 0,1 from s[i] + bits 4-7 from s[i+1]]
|
||||
// 3: s_alphabet[bits 0-3 from s[i+1] + bits 6,7 from s[i+2]]
|
||||
// 4: s_alphabet[bits 0-5 from s[i+2]]
|
||||
for (; i < full; i += 3) {
|
||||
addEnc(dest,iDest,s[i] >> 2,lines,crtLine,lineLen);
|
||||
addEnc(dest,iDest,s[i] << 4 | s[i+1] >> 4,lines,crtLine,lineLen);
|
||||
addEnc(dest,iDest,s[i+1] << 2 | s[i+2] >> 6,lines,crtLine,lineLen);
|
||||
addEnc(dest,iDest,s[i+2],lines,crtLine,lineLen);
|
||||
}
|
||||
// Encode rest (can be 1 or 2) to 4 bytes destination
|
||||
// 1: 2 chars + 2 padding, 2: 3 chars + 1 padding
|
||||
// Don't add the final padding char: destination was filled with it
|
||||
if (rest) {
|
||||
addEnc(dest,iDest,s[i] >> 2,lines,crtLine,lineLen);
|
||||
if (rest == 1)
|
||||
addEnc(dest,iDest,s[i] << 4,lines,crtLine,lineLen);
|
||||
else {
|
||||
addEnc(dest,iDest,s[i] << 4 | s[i+1] >> 4,lines,crtLine,lineLen);
|
||||
addEnc(dest,iDest,s[i+1] << 2,lines,crtLine,lineLen);
|
||||
}
|
||||
}
|
||||
// Add final end of line ?
|
||||
if (lineAtEnd)
|
||||
dest << s_eoln;
|
||||
}
|
||||
|
||||
// Decode this buffer to a destination one
|
||||
bool Base64::decode(DataBlock& dest, bool liberal)
|
||||
{
|
||||
dest.clear();
|
||||
|
||||
// Calculate the number of alphabet characters
|
||||
unsigned int full = 0;
|
||||
unsigned int rest = 0;
|
||||
unsigned char* src = (unsigned char*)data();
|
||||
if (liberal)
|
||||
for (unsigned int i = 0; i < length(); i++) {
|
||||
int res = validLiberal(src[i]);
|
||||
if (!res) {
|
||||
Debug("Base64",DebugNote,"Got invalid char 0x%x at pos %u [%p]",src[i],i,this);
|
||||
return false;
|
||||
}
|
||||
if (res > 0)
|
||||
full++;
|
||||
}
|
||||
else {
|
||||
full = length();
|
||||
// Skip padding chars from end
|
||||
for (; full; full--)
|
||||
if (src[full-1] != PADDING_CHAR)
|
||||
break;
|
||||
}
|
||||
// rest MUST be 0, 2 or 3
|
||||
// rest is 1: can't build an 8-bit ascii char from a 6-bit Base64 char
|
||||
rest = full % 4;
|
||||
full -= rest;
|
||||
if (!(full || rest) || rest == 1) {
|
||||
Debug("Base64",DebugNote,"Got invalid length %u [%p]",length(),this);
|
||||
return true;
|
||||
}
|
||||
dest.assign(0,full / 4 * 3 + (rest ? rest - 1 : 0));
|
||||
|
||||
DDebug("Base64",DebugAll,"Decoding %u bytes (full=%u rest=%u) to %u bytes",
|
||||
length(),full,rest,dest.length());
|
||||
|
||||
unsigned int iDest = 0;
|
||||
unsigned char dec[4];
|
||||
if (!liberal) {
|
||||
#define GET_DEC(a) \
|
||||
if (valid(src[i+a])) \
|
||||
dec[a] = s_ato64[src[i+a]]; \
|
||||
else { \
|
||||
Debug("Base64",DebugNote,"Got invalid char 0x%x at pos %u [%p]", \
|
||||
src[i+a],i+a,this); \
|
||||
return false; \
|
||||
}
|
||||
unsigned int i = 0;
|
||||
// Decode each 4 bytes chunk from source
|
||||
// Translate each byte and build 3 destination bytes from 4 6-bit Base64 chars
|
||||
// 1: bits 0-5 from dec[0] + bits 4,5 from dec[1]
|
||||
// 2: bits 0-3 from dec[1] + bits 2-5 from dec[2]
|
||||
// 3: bits 0,1 from dec[2] + bits 0-5 from dec[3]
|
||||
for (; i < full; i += 4) {
|
||||
GET_DEC(0)
|
||||
GET_DEC(1)
|
||||
GET_DEC(2)
|
||||
GET_DEC(3)
|
||||
addDec(dest,iDest,dec,4);
|
||||
}
|
||||
// Process the rest
|
||||
// 2: 1 destination byte. 3: 2 bytes in destination
|
||||
if (rest) {
|
||||
GET_DEC(0)
|
||||
GET_DEC(1)
|
||||
if (rest == 3)
|
||||
GET_DEC(2)
|
||||
}
|
||||
#undef GET_DEC
|
||||
}
|
||||
else {
|
||||
unsigned int iDec = 0;
|
||||
for (unsigned int i = 0; i < length(); i++, src++) {
|
||||
int res = validLiberal(*src);
|
||||
if (!res) {
|
||||
Debug("Base64",DebugNote,"Got invalid char 0x%x at pos %u [%p]",*src,i,this);
|
||||
return false;
|
||||
}
|
||||
if (res < 0)
|
||||
continue;
|
||||
dec[iDec++] = s_ato64[*src];
|
||||
if (iDec == 4) {
|
||||
addDec(dest,iDest,dec,4);
|
||||
iDec = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rest)
|
||||
addDec(dest,iDest,dec,rest);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -24,7 +24,7 @@ PINC := $(EINC) @top_srcdir@/yatephone.h
|
|||
CLINC:= $(PINC) @top_srcdir@/yatecbase.h
|
||||
LIBS :=
|
||||
CLSOBJS := TelEngine.o ObjList.o HashList.o String.o DataBlock.o NamedList.o \
|
||||
URI.o Mime.o Array.o Iterator.o YMD5.o YSHA1.o Mutex.o Thread.o Socket.o
|
||||
URI.o Mime.o Array.o Iterator.o YMD5.o YSHA1.o Base64.o Mutex.o Thread.o Socket.o
|
||||
ENGOBJS := Configuration.o Message.o Plugin.o Engine.o
|
||||
TELOBJS := DataFormat.o Channel.o
|
||||
CLIOBJS := Client.o
|
||||
|
|
66
yateclass.h
66
yateclass.h
|
@ -2883,6 +2883,72 @@ private:
|
|||
unsigned char m_bin[20];
|
||||
};
|
||||
|
||||
/**
|
||||
* Base64 encoder/decoder class
|
||||
* @short Base64 encoder/decoder class
|
||||
*/
|
||||
class Base64 : public DataBlock
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
inline Base64()
|
||||
{}
|
||||
|
||||
/**
|
||||
* Constructor. Set the buffer
|
||||
* @param src Initial data buffer
|
||||
* @param len Initial data buffer length
|
||||
* @param copyData True to make a copy of the received data
|
||||
*/
|
||||
inline Base64(void* src, unsigned int len, bool copyData = true)
|
||||
: DataBlock(src,len,copyData)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Encode this buffer to a destination string
|
||||
* @param dest Destination string
|
||||
* @param lineLen The length of a line. If non 0, a line break (CR/LF) will
|
||||
* be inserted in the encoded data after each lineLine characters.
|
||||
* No line break will be added after the last line. Use the lineAtEnd
|
||||
* parameter to do that
|
||||
* @param lineAtEnd True to add a line break at the end of encoded data
|
||||
*/
|
||||
void encode(String& dest, unsigned int lineLen = 0, bool lineAtEnd = false);
|
||||
|
||||
/**
|
||||
* Decode this buffer to a destination one
|
||||
* @param dest Destination data buffer
|
||||
* @param liberal True to use 'liberal' rules when decoding. Some non alphabet
|
||||
* characters (such as CR, LF, TAB, SPACE or the Base64 padding char '=')
|
||||
* will be accepted and ignored. The resulting number of Base64 chars to
|
||||
* decode must be a valid one
|
||||
* @return True on succes, false if an invalid (non Base64) character was
|
||||
* found or the number of Base64 characters is invalid (must be a multiple
|
||||
* of 4 plus 0, 2 or 3 characters) or the padding is incorrect
|
||||
*/
|
||||
bool decode(DataBlock& dest, bool liberal = true);
|
||||
|
||||
/**
|
||||
* Base64 append operator for Strings
|
||||
*/
|
||||
inline Base64& operator<<(const String& value)
|
||||
{ append(value); return *this; }
|
||||
|
||||
/**
|
||||
* Base64 append operator for DataBlocks
|
||||
*/
|
||||
inline Base64& operator<<(const DataBlock& data)
|
||||
{ append(data); return *this; }
|
||||
|
||||
/**
|
||||
* Base64 append operator for C strings
|
||||
*/
|
||||
inline Base64& operator<<(const char* value)
|
||||
{ return operator<<(String(value)); }
|
||||
};
|
||||
|
||||
/**
|
||||
* This class holds a named list of named strings
|
||||
* @short A named string container class
|
||||
|
|
Loading…
Reference in New Issue