404 lines
12 KiB
C++
404 lines
12 KiB
C++
// Copyright (C) 2006 Davis E. King (davis@dlib.net)
|
|
// License: Boost Software License See LICENSE.txt for the full license.
|
|
#ifndef DLIB_BASE64_KERNEL_1_CPp_
|
|
#define DLIB_BASE64_KERNEL_1_CPp_
|
|
|
|
#include "base64_kernel_1.h"
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <climits>
|
|
|
|
namespace dlib
|
|
{
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
base64::line_ending_type base64::
|
|
line_ending (
|
|
) const
|
|
{
|
|
return eol_style;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
void base64::
|
|
set_line_ending (
|
|
line_ending_type eol_style_
|
|
)
|
|
{
|
|
eol_style = eol_style_;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
base64::
|
|
base64 (
|
|
) :
|
|
encode_table(0),
|
|
decode_table(0),
|
|
bad_value(100),
|
|
eol_style(LF)
|
|
{
|
|
try
|
|
{
|
|
encode_table = new char[64];
|
|
decode_table = new unsigned char[UCHAR_MAX];
|
|
}
|
|
catch (...)
|
|
{
|
|
if (encode_table) delete [] encode_table;
|
|
if (decode_table) delete [] decode_table;
|
|
throw;
|
|
}
|
|
|
|
// now set up the tables with the right stuff
|
|
encode_table[0] = 'A';
|
|
encode_table[17] = 'R';
|
|
encode_table[34] = 'i';
|
|
encode_table[51] = 'z';
|
|
|
|
encode_table[1] = 'B';
|
|
encode_table[18] = 'S';
|
|
encode_table[35] = 'j';
|
|
encode_table[52] = '0';
|
|
|
|
encode_table[2] = 'C';
|
|
encode_table[19] = 'T';
|
|
encode_table[36] = 'k';
|
|
encode_table[53] = '1';
|
|
|
|
encode_table[3] = 'D';
|
|
encode_table[20] = 'U';
|
|
encode_table[37] = 'l';
|
|
encode_table[54] = '2';
|
|
|
|
encode_table[4] = 'E';
|
|
encode_table[21] = 'V';
|
|
encode_table[38] = 'm';
|
|
encode_table[55] = '3';
|
|
|
|
encode_table[5] = 'F';
|
|
encode_table[22] = 'W';
|
|
encode_table[39] = 'n';
|
|
encode_table[56] = '4';
|
|
|
|
encode_table[6] = 'G';
|
|
encode_table[23] = 'X';
|
|
encode_table[40] = 'o';
|
|
encode_table[57] = '5';
|
|
|
|
encode_table[7] = 'H';
|
|
encode_table[24] = 'Y';
|
|
encode_table[41] = 'p';
|
|
encode_table[58] = '6';
|
|
|
|
encode_table[8] = 'I';
|
|
encode_table[25] = 'Z';
|
|
encode_table[42] = 'q';
|
|
encode_table[59] = '7';
|
|
|
|
encode_table[9] = 'J';
|
|
encode_table[26] = 'a';
|
|
encode_table[43] = 'r';
|
|
encode_table[60] = '8';
|
|
|
|
encode_table[10] = 'K';
|
|
encode_table[27] = 'b';
|
|
encode_table[44] = 's';
|
|
encode_table[61] = '9';
|
|
|
|
encode_table[11] = 'L';
|
|
encode_table[28] = 'c';
|
|
encode_table[45] = 't';
|
|
encode_table[62] = '+';
|
|
|
|
encode_table[12] = 'M';
|
|
encode_table[29] = 'd';
|
|
encode_table[46] = 'u';
|
|
encode_table[63] = '/';
|
|
|
|
encode_table[13] = 'N';
|
|
encode_table[30] = 'e';
|
|
encode_table[47] = 'v';
|
|
|
|
encode_table[14] = 'O';
|
|
encode_table[31] = 'f';
|
|
encode_table[48] = 'w';
|
|
|
|
encode_table[15] = 'P';
|
|
encode_table[32] = 'g';
|
|
encode_table[49] = 'x';
|
|
|
|
encode_table[16] = 'Q';
|
|
encode_table[33] = 'h';
|
|
encode_table[50] = 'y';
|
|
|
|
|
|
|
|
// we can now fill out the decode_table by using the encode_table
|
|
for (int i = 0; i < UCHAR_MAX; ++i)
|
|
{
|
|
decode_table[i] = bad_value;
|
|
}
|
|
for (unsigned char i = 0; i < 64; ++i)
|
|
{
|
|
decode_table[(unsigned char)encode_table[i]] = i;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
base64::
|
|
~base64 (
|
|
)
|
|
{
|
|
delete [] encode_table;
|
|
delete [] decode_table;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
void base64::
|
|
encode (
|
|
std::istream& in_,
|
|
std::ostream& out_
|
|
) const
|
|
{
|
|
using namespace std;
|
|
streambuf& in = *in_.rdbuf();
|
|
streambuf& out = *out_.rdbuf();
|
|
|
|
unsigned char inbuf[3];
|
|
unsigned char outbuf[4];
|
|
streamsize status = in.sgetn(reinterpret_cast<char*>(&inbuf),3);
|
|
|
|
unsigned char c1, c2, c3, c4, c5, c6;
|
|
|
|
int counter = 19;
|
|
|
|
// while we haven't hit the end of the input stream
|
|
while (status != 0)
|
|
{
|
|
if (counter == 0)
|
|
{
|
|
counter = 19;
|
|
// write a newline
|
|
char ch;
|
|
switch (eol_style)
|
|
{
|
|
case CR:
|
|
ch = '\r';
|
|
if (out.sputn(&ch,1)!=1)
|
|
throw std::ios_base::failure("error occurred in the base64 object");
|
|
break;
|
|
case LF:
|
|
ch = '\n';
|
|
if (out.sputn(&ch,1)!=1)
|
|
throw std::ios_base::failure("error occurred in the base64 object");
|
|
break;
|
|
case CRLF:
|
|
ch = '\r';
|
|
if (out.sputn(&ch,1)!=1)
|
|
throw std::ios_base::failure("error occurred in the base64 object");
|
|
ch = '\n';
|
|
if (out.sputn(&ch,1)!=1)
|
|
throw std::ios_base::failure("error occurred in the base64 object");
|
|
break;
|
|
default:
|
|
DLIB_CASSERT(false,"this should never happen");
|
|
}
|
|
}
|
|
--counter;
|
|
|
|
if (status == 3)
|
|
{
|
|
// encode the bytes in inbuf to base64 and write them to the output stream
|
|
c1 = inbuf[0]&0xfc;
|
|
c2 = inbuf[0]&0x03;
|
|
c3 = inbuf[1]&0xf0;
|
|
c4 = inbuf[1]&0x0f;
|
|
c5 = inbuf[2]&0xc0;
|
|
c6 = inbuf[2]&0x3f;
|
|
|
|
outbuf[0] = c1>>2;
|
|
outbuf[1] = (c2<<4)|(c3>>4);
|
|
outbuf[2] = (c4<<2)|(c5>>6);
|
|
outbuf[3] = c6;
|
|
|
|
|
|
outbuf[0] = encode_table[outbuf[0]];
|
|
outbuf[1] = encode_table[outbuf[1]];
|
|
outbuf[2] = encode_table[outbuf[2]];
|
|
outbuf[3] = encode_table[outbuf[3]];
|
|
|
|
// write the encoded bytes to the output stream
|
|
if (out.sputn(reinterpret_cast<char*>(&outbuf),4)!=4)
|
|
{
|
|
throw std::ios_base::failure("error occurred in the base64 object");
|
|
}
|
|
|
|
// get 3 more input bytes
|
|
status = in.sgetn(reinterpret_cast<char*>(&inbuf),3);
|
|
continue;
|
|
}
|
|
else if (status == 2)
|
|
{
|
|
// we are at the end of the input stream and need to add some padding
|
|
|
|
// encode the bytes in inbuf to base64 and write them to the output stream
|
|
c1 = inbuf[0]&0xfc;
|
|
c2 = inbuf[0]&0x03;
|
|
c3 = inbuf[1]&0xf0;
|
|
c4 = inbuf[1]&0x0f;
|
|
c5 = 0;
|
|
|
|
outbuf[0] = c1>>2;
|
|
outbuf[1] = (c2<<4)|(c3>>4);
|
|
outbuf[2] = (c4<<2)|(c5>>6);
|
|
outbuf[3] = '=';
|
|
|
|
outbuf[0] = encode_table[outbuf[0]];
|
|
outbuf[1] = encode_table[outbuf[1]];
|
|
outbuf[2] = encode_table[outbuf[2]];
|
|
|
|
// write the encoded bytes to the output stream
|
|
if (out.sputn(reinterpret_cast<char*>(&outbuf),4)!=4)
|
|
{
|
|
throw std::ios_base::failure("error occurred in the base64 object");
|
|
}
|
|
|
|
|
|
break;
|
|
}
|
|
else // in this case status must be 1
|
|
{
|
|
// we are at the end of the input stream and need to add some padding
|
|
|
|
// encode the bytes in inbuf to base64 and write them to the output stream
|
|
c1 = inbuf[0]&0xfc;
|
|
c2 = inbuf[0]&0x03;
|
|
c3 = 0;
|
|
|
|
outbuf[0] = c1>>2;
|
|
outbuf[1] = (c2<<4)|(c3>>4);
|
|
outbuf[2] = '=';
|
|
outbuf[3] = '=';
|
|
|
|
outbuf[0] = encode_table[outbuf[0]];
|
|
outbuf[1] = encode_table[outbuf[1]];
|
|
|
|
|
|
// write the encoded bytes to the output stream
|
|
if (out.sputn(reinterpret_cast<char*>(&outbuf),4)!=4)
|
|
{
|
|
throw std::ios_base::failure("error occurred in the base64 object");
|
|
}
|
|
|
|
break;
|
|
}
|
|
} // while (status != 0)
|
|
|
|
|
|
// make sure the stream buffer flushes to its I/O channel
|
|
out.pubsync();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
void base64::
|
|
decode (
|
|
std::istream& in_,
|
|
std::ostream& out_
|
|
) const
|
|
{
|
|
using namespace std;
|
|
streambuf& in = *in_.rdbuf();
|
|
streambuf& out = *out_.rdbuf();
|
|
|
|
unsigned char inbuf[4];
|
|
unsigned char outbuf[3];
|
|
int inbuf_pos = 0;
|
|
streamsize status = in.sgetn(reinterpret_cast<char*>(inbuf),1);
|
|
|
|
// only count this character if it isn't some kind of filler
|
|
if (status == 1 && decode_table[inbuf[0]] != bad_value )
|
|
++inbuf_pos;
|
|
|
|
unsigned char c1, c2, c3, c4, c5, c6;
|
|
streamsize outsize;
|
|
|
|
// while we haven't hit the end of the input stream
|
|
while (status != 0)
|
|
{
|
|
// if we have 4 valid characters
|
|
if (inbuf_pos == 4)
|
|
{
|
|
inbuf_pos = 0;
|
|
|
|
// this might be the end of the encoded data so we need to figure out if
|
|
// there was any padding applied.
|
|
outsize = 3;
|
|
if (inbuf[3] == '=')
|
|
{
|
|
if (inbuf[2] == '=')
|
|
outsize = 1;
|
|
else
|
|
outsize = 2;
|
|
}
|
|
|
|
// decode the incoming characters
|
|
inbuf[0] = decode_table[inbuf[0]];
|
|
inbuf[1] = decode_table[inbuf[1]];
|
|
inbuf[2] = decode_table[inbuf[2]];
|
|
inbuf[3] = decode_table[inbuf[3]];
|
|
|
|
|
|
// now pack these guys into bytes rather than 6 bit chunks
|
|
c1 = inbuf[0]<<2;
|
|
c2 = inbuf[1]>>4;
|
|
c3 = inbuf[1]<<4;
|
|
c4 = inbuf[2]>>2;
|
|
c5 = inbuf[2]<<6;
|
|
c6 = inbuf[3];
|
|
|
|
outbuf[0] = c1|c2;
|
|
outbuf[1] = c3|c4;
|
|
outbuf[2] = c5|c6;
|
|
|
|
|
|
// write the encoded bytes to the output stream
|
|
if (out.sputn(reinterpret_cast<char*>(&outbuf),outsize)!=outsize)
|
|
{
|
|
throw std::ios_base::failure("error occurred in the base64 object");
|
|
}
|
|
}
|
|
|
|
// get more input characters
|
|
status = in.sgetn(reinterpret_cast<char*>(inbuf + inbuf_pos),1);
|
|
// only count this character if it isn't some kind of filler
|
|
if ((decode_table[inbuf[inbuf_pos]] != bad_value || inbuf[inbuf_pos] == '=') &&
|
|
status != 0)
|
|
++inbuf_pos;
|
|
} // while (status != 0)
|
|
|
|
if (inbuf_pos != 0)
|
|
{
|
|
ostringstream sout;
|
|
sout << inbuf_pos << " extra characters were found at the end of the encoded data."
|
|
<< " This may indicate that the data stream has been truncated.";
|
|
// this happens if we hit EOF in the middle of decoding a 24bit block.
|
|
throw decode_error(sout.str());
|
|
}
|
|
|
|
// make sure the stream buffer flushes to its I/O channel
|
|
out.pubsync();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
}
|
|
|
|
#endif // DLIB_BASE64_KERNEL_1_CPp_
|
|
|