// Base64.h #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #ifndef Base64h #define Base64h #include "Bytes.h" /* Encodes binary data to Base64 formatted Text and Decodes Base64 formatted Text to the original binary data. This encoding is used to convert binary files into text files so that they can be sent through e-mail systems. The encoded data is a third larger than the original data. Base64 format is part of the MIME protocol. */ class __declspec(novtable) Base64 { Base64() {} // Private Constructor: you shouldn't create one of these. public: static void Encode(CBytes& iBytes, bool TrailingEqs=true) { // TrailingEqs is false CBytes::NullTerminated is stored as a trailing Byte if(iBytes.IsEmpty()) return; CBytes oBytes((TrailingEqs ? (4*((iBytes.GetLength()+2)/3)+1) : (iBytes.GetLength()+((iBytes.GetLength()+2)/3)+2)), true); oBytes.Length=Encode(oBytes, iBytes(), iBytes.GetLength(), TrailingEqs); // TRACE("o=%u\ti=%u\tsum=%u\t%s\t%s\r\n",oBytes.Length, iBytes.Length, iBytes.GetLength()+((iBytes.GetLength()+2)/3)+(TrailingEqs ? 0 : 1), iBytes.Bytes, oBytes.Bytes); if(!TrailingEqs) { oBytes[ oBytes.Length-1]=(iBytes.NullTerminated ? 'N' : 'n'); // append the Null Terminated Flag oBytes[++oBytes.Length-1]=0; // Have to Pre-increment or Bytes::operator[] will assert } oBytes.MoveTo(iBytes); } static void Decode(CBytes& iBytes, bool TrailingEqs=true) { if(iBytes.IsEmpty()) return; bool NullTerminated=true; if(!TrailingEqs) { NullTerminated=iBytes[ iBytes.Length-2]=='N'; // NullTerminated Flag iBytes[--iBytes.Length-1]=0; // Overwrite NullTerminated Flag } CBytes oBytes(iBytes.GetLength()-((iBytes.GetLength()+3)/4), NullTerminated); oBytes.Length=Decode(oBytes.Bytes, iBytes, iBytes.GetLength(), TrailingEqs); if(oBytes.IsEmpty()) return; // Don't decode if an error occurred if(TrailingEqs) oBytes.DetectTermination(); oBytes.MoveTo(iBytes); } /* src may contain null bytes - hence the Len parameter. Result is always null terminated and needs to hold 4*((Len+2)/3)+1 Bytes. */ static DWORD Encode(char* Result, const BYTE* src, int Len, bool TrailingEqs=true) { static const char Bit6ToBase64[65]={"\ ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz\ 0123456789+/"}; char* dst=Result+3; DWORD EncodeShifter=0; for(int i=Len/3; i; --i) { EncodeShifter=*src++; EncodeShifter=(EncodeShifter<<8)|*src++; EncodeShifter=(EncodeShifter<<8)|*src++; *dst--=Bit6ToBase64[EncodeShifter & 0x3F]; EncodeShifter>>=6; *dst--=Bit6ToBase64[EncodeShifter & 0x3F]; EncodeShifter>>=6; *dst--=Bit6ToBase64[EncodeShifter & 0x3F]; EncodeShifter>>=6; *dst =Bit6ToBase64[EncodeShifter & 0x3F]; dst+=7; } *(dst-3)=0; switch(Len%3) { default: return dst-Result+(TrailingEqs ? 5 : -2); case 1: { EncodeShifter=*src; // 111111 112222 222233 333333 EncodeShifter<<=4; // ==== if(TrailingEqs) { dst[1]=0; *dst='='; } --dst; *dst--=(TrailingEqs ? '=' : 0); *dst--=Bit6ToBase64[EncodeShifter & 0x3F]; EncodeShifter>>=6; *dst =Bit6ToBase64[EncodeShifter & 0x3F]; return dst-Result+(TrailingEqs ? 5 : 3); }case 2: { EncodeShifter=*src++; EncodeShifter=(EncodeShifter<<8)|*src; //111111 112222 222233 333333 EncodeShifter<<=2; // == if(TrailingEqs) dst[1]=0; *dst--=(TrailingEqs ? '=' : 0); *dst--=Bit6ToBase64[EncodeShifter & 0x3F]; EncodeShifter>>=6; *dst--=Bit6ToBase64[EncodeShifter & 0x3F]; EncodeShifter>>=6; *dst =Bit6ToBase64[EncodeShifter & 0x3F]; return dst-Result+(TrailingEqs ? 5 : 4); } } } /* Returns the length of Result (including Null Terminator) Result is always null terminated and needs to hold Len*3/4+1 Bytes */ static DWORD Decode(BYTE* Result, const char* src, int Len, bool TrailingEqs=true) { static const BYTE Base64ToBit6[128]={ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, 52,53,54,55,56,57,58,59,60,61,-1,-1,-1, 0,-1,-1, -1, 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,-1,-1,-1,-1,-1, -1,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,-1,-1,-1,-1,-1, }; BYTE* dst=Result+2; DWORD DecodeShifter=0; BYTE B; for(int i=(Len+3)>>2; i; --i) { // dev no mod: the Law says it's divisible by 4. Rounded up for when TrailingEqs is false if((B=*src++) & 0x80 || B<0) return *Result=0; // Bad characters in stream DecodeShifter=Base64ToBit6[B]; if((B=*src++) & 0x80 || B<0) return *Result=0; // Bad characters in stream DecodeShifter=(DecodeShifter<<6) | Base64ToBit6[B]; if((B=*src++) & 0x80 || B<0) return *Result=0; // Bad characters in stream if((B=='=') || (!TrailingEqs && (B==0))) { // 1111 11222222 --dst; // ==== DecodeShifter>>=4; *--dst=(char)DecodeShifter; return dst-Result+1; } DecodeShifter=(DecodeShifter<<6) | Base64ToBit6[B]; if((B=*src++) & 0x80 || B<0) return *Result=0; // Bad characters in stream if((B=='=') || (!TrailingEqs && (B==0))) { // 11 11112222 22333333 --dst; // == DecodeShifter>>=2; *dst--=(char)DecodeShifter; DecodeShifter>>=8; *dst =(char)DecodeShifter; return dst-Result+2; } DecodeShifter=(DecodeShifter<<6) | Base64ToBit6[B]; *dst--=(char)DecodeShifter; DecodeShifter>>=8; *dst--=(char)DecodeShifter; DecodeShifter>>=8; *dst =(char)DecodeShifter; dst+=5; } return dst-Result-2; } }; #ifdef CFile /* MIME Performs Base64 encoding and decoding of a whole File. Any one encoded line of text will not exceed 77 characters. Base64 produces smaller encoded files than Unix to Unix (UU). Usage: MIME::Encode("C:\\T.b64" ,"C:\\T.bmp"); MIME::Decode("C:\\TB64.bmp","C:\\T.b64"); */ struct MIME { // Encode 57 Bytes at a time and create 76 Character Lines until last Line which may be <=57 Bytes in. MIME(); // No Constructor defined: you shouldn't create one of these. static bool Encode(const char* outFilePath, const char* inFilePath) { CFile inFile,outFile; if(!inFile.Open(inFilePath, CFile::modeRead|CFile::shareDenyNone)) return false; if(!outFile.Open(outFilePath, CFile::modeCreate|CFile::modeWrite)) return false; int Len=(int)inFile.GetLength(); BYTE src[57]; char dst[76+1]; int Count=Len/57; for(int i=0; i