539 lines
11 KiB
C++
539 lines
11 KiB
C++
/* mime64 */
|
|
/* MIME base64 encoder/decoder by Karl Hahn hahn@lds.loral.com 3-Aug-94 */
|
|
/* Modified into an API by georgep@microsoft.com 8-Jan-96 */
|
|
|
|
#include "priv.h"
|
|
|
|
#include "mime64.h"
|
|
|
|
#define INVALID_CHAR (ULONG)-2
|
|
#define IGNORE_CHAR (ULONG)-1
|
|
|
|
extern "C" void DllAddRef();
|
|
extern "C" void DllRelease();
|
|
|
|
class CRefDll
|
|
{
|
|
public:
|
|
CRefDll() {DllAddRef();}
|
|
~CRefDll() {DllRelease();}
|
|
} ;
|
|
|
|
|
|
HRESULT CopyTo(IStream* pstmIn,
|
|
/* [unique][in] */ IStream *pstm,
|
|
/* [in] */ ULARGE_INTEGER cb,
|
|
/* [out] */ ULARGE_INTEGER *pcbRead,
|
|
/* [out] */ ULARGE_INTEGER *pcbWritten)
|
|
{
|
|
if (cb.HighPart != 0)
|
|
return E_INVALIDARG;
|
|
|
|
DWORD dwBytes = cb.LowPart;
|
|
DWORD dwStep = dwBytes;
|
|
if (dwStep >= 0x8000)
|
|
dwStep = 0x8000;
|
|
|
|
LPVOID pv = GlobalAlloc(GPTR, dwStep);
|
|
if (!pv)
|
|
return E_OUTOFMEMORY;
|
|
|
|
DWORD dwTotRead = 0;
|
|
DWORD dwTotWrite = 0;
|
|
HRESULT hres = NOERROR;
|
|
|
|
for (dwBytes; dwBytes!=0; )
|
|
{
|
|
DWORD dwThisRead = dwStep;
|
|
if (dwThisRead > dwBytes)
|
|
{
|
|
dwThisRead = dwBytes;
|
|
}
|
|
|
|
if (NOERROR!=pstmIn->Read(pv, dwThisRead, &dwThisRead) || !dwThisRead)
|
|
{
|
|
// Must be the end of the file
|
|
break;
|
|
}
|
|
dwTotRead += dwThisRead;
|
|
|
|
DWORD dwWrite;
|
|
hres = pstm->Write(pv, dwThisRead, &dwWrite);
|
|
if (FAILED(hres))
|
|
{
|
|
break;
|
|
}
|
|
dwTotWrite += dwWrite;
|
|
|
|
if (dwWrite != dwThisRead)
|
|
{
|
|
hres = E_UNEXPECTED;
|
|
break;
|
|
}
|
|
|
|
dwBytes -= dwThisRead;
|
|
}
|
|
|
|
GlobalFree(pv);
|
|
pv = NULL;
|
|
|
|
if (pcbRead)
|
|
{
|
|
pcbRead->HighPart = 0;
|
|
pcbRead->LowPart = dwTotRead;
|
|
}
|
|
|
|
if (pcbWritten)
|
|
{
|
|
pcbWritten->HighPart = 0;
|
|
pcbWritten->LowPart = dwTotWrite;
|
|
}
|
|
|
|
return(hres);
|
|
}
|
|
|
|
#undef new // Hack! need to resolve this (edwardp)
|
|
|
|
class CStreamMem : public IStream
|
|
{
|
|
private:
|
|
CStreamMem(UINT cbSize) : m_cbSize(cbSize), m_cRef(1), m_cbPos(0) {}
|
|
void* operator new(size_t cbClass, UINT cbSize)
|
|
{
|
|
return(::operator new(cbClass + cbSize - 1));
|
|
}
|
|
|
|
|
|
public:
|
|
static CStreamMem *Construct(UINT cbSize)
|
|
{
|
|
return(new(cbSize) CStreamMem(cbSize));
|
|
}
|
|
|
|
LPVOID GetPtr() { return(m_vData); }
|
|
void SetSize(UINT cbSize) { m_cbSize = cbSize; }
|
|
|
|
// IUnknown
|
|
virtual STDMETHODIMP QueryInterface(
|
|
/* [in] */ REFIID riid,
|
|
/* [out] */ void **ppvObject);
|
|
|
|
virtual STDMETHODIMP_(ULONG) AddRef(void)
|
|
{ return(++m_cRef); }
|
|
|
|
virtual STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// IStream
|
|
virtual STDMETHODIMP Read(
|
|
/* [out] */ void *pv,
|
|
/* [in] */ ULONG cb,
|
|
/* [out] */ ULONG *pcbRead);
|
|
|
|
virtual STDMETHODIMP Write(
|
|
/* [size_is][in] */ const void *pv,
|
|
/* [in] */ ULONG cb,
|
|
/* [out] */ ULONG *pcbWritten)
|
|
{ return(E_NOTIMPL); }
|
|
|
|
virtual STDMETHODIMP Seek(
|
|
/* [in] */ LARGE_INTEGER dlibMove,
|
|
/* [in] */ DWORD dwOrigin,
|
|
/* [out] */ ULARGE_INTEGER *plibNewPosition);
|
|
|
|
virtual STDMETHODIMP SetSize(
|
|
/* [in] */ ULARGE_INTEGER libNewSize)
|
|
{ return(E_NOTIMPL); }
|
|
|
|
virtual STDMETHODIMP CopyTo(
|
|
/* [unique][in] */ IStream *pstm,
|
|
/* [in] */ ULARGE_INTEGER cb,
|
|
/* [out] */ ULARGE_INTEGER *pcbRead,
|
|
/* [out] */ ULARGE_INTEGER *pcbWritten)
|
|
{ return (::CopyTo(this, pstm, cb, pcbRead, pcbWritten)); }
|
|
|
|
virtual STDMETHODIMP Commit(
|
|
/* [in] */ DWORD grfCommitFlags)
|
|
{ return(E_NOTIMPL); }
|
|
|
|
virtual STDMETHODIMP Revert( void)
|
|
{ return(E_NOTIMPL); }
|
|
|
|
virtual STDMETHODIMP LockRegion(
|
|
/* [in] */ ULARGE_INTEGER libOffset,
|
|
/* [in] */ ULARGE_INTEGER cb,
|
|
/* [in] */ DWORD dwLockType)
|
|
{ return(E_NOTIMPL); }
|
|
|
|
virtual STDMETHODIMP UnlockRegion(
|
|
/* [in] */ ULARGE_INTEGER libOffset,
|
|
/* [in] */ ULARGE_INTEGER cb,
|
|
/* [in] */ DWORD dwLockType)
|
|
{ return(E_NOTIMPL); }
|
|
|
|
virtual STDMETHODIMP Stat(
|
|
/* [out] */ STATSTG *pstatstg,
|
|
/* [in] */ DWORD grfStatFlag)
|
|
{ return(E_NOTIMPL); }
|
|
|
|
virtual STDMETHODIMP Clone(
|
|
/* [out] */ IStream **ppstm)
|
|
{ return(E_NOTIMPL); }
|
|
|
|
private:
|
|
CRefDll m_cRefDll;
|
|
|
|
ULONG m_cRef;
|
|
|
|
UINT m_cbSize;
|
|
UINT m_cbPos;
|
|
|
|
// Must be the last field in the class
|
|
BYTE m_vData[1];
|
|
} ;
|
|
|
|
STDMETHODIMP CStreamMem::QueryInterface(
|
|
/* [in] */ REFIID riid,
|
|
/* [out] */ void **ppvObject)
|
|
{
|
|
if (riid==IID_IUnknown || riid==IID_IStream)
|
|
{
|
|
AddRef();
|
|
*ppvObject = (LPVOID)(IStream*)this;
|
|
return(NOERROR);
|
|
}
|
|
|
|
*ppvObject = NULL;
|
|
return(E_NOINTERFACE);
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CStreamMem::Release( void)
|
|
{
|
|
--m_cRef;
|
|
if (!m_cRef)
|
|
{
|
|
delete this;
|
|
return(0);
|
|
}
|
|
|
|
return(m_cRef);
|
|
}
|
|
|
|
|
|
// IStream
|
|
STDMETHODIMP CStreamMem::Read(
|
|
/* [out] */ void *pv,
|
|
/* [in] */ ULONG cb,
|
|
/* [out] */ ULONG *pcbRead)
|
|
{
|
|
if (pcbRead)
|
|
{
|
|
*pcbRead = 0;
|
|
}
|
|
|
|
if (m_cbPos >= m_cbSize)
|
|
{
|
|
return(S_FALSE);
|
|
}
|
|
|
|
ULONG cbRest = m_cbSize - m_cbPos;
|
|
if (cb > cbRest)
|
|
{
|
|
cb = cbRest;
|
|
}
|
|
|
|
CopyMemory(pv, m_vData + m_cbPos, cb);
|
|
m_cbPos += cb;
|
|
|
|
if (pcbRead)
|
|
{
|
|
*pcbRead = cb;
|
|
}
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CStreamMem::Seek(
|
|
/* [in] */ LARGE_INTEGER dlibMove,
|
|
/* [in] */ DWORD dwOrigin,
|
|
/* [out] */ ULARGE_INTEGER *plibNewPosition)
|
|
{
|
|
LONG lOffset = (LONG)dlibMove.LowPart;
|
|
|
|
// Make sure we are only using 32 bits
|
|
if (dlibMove.HighPart==0 && lOffset>=0)
|
|
{
|
|
}
|
|
else if (dlibMove.HighPart==-1 && lOffset<0)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
return(E_INVALIDARG);
|
|
}
|
|
|
|
switch (dwOrigin)
|
|
{
|
|
case STREAM_SEEK_SET:
|
|
break;
|
|
|
|
case STREAM_SEEK_CUR:
|
|
lOffset = (LONG)m_cbPos + lOffset;
|
|
break;
|
|
|
|
case STREAM_SEEK_END:
|
|
lOffset = (LONG)m_cbSize + lOffset;
|
|
break;
|
|
|
|
default:
|
|
return(E_INVALIDARG);
|
|
}
|
|
|
|
// Check the new offset is in range (NOTE: it is valid to seek past "end of file")
|
|
if (lOffset < 0)
|
|
{
|
|
return(E_INVALIDARG);
|
|
}
|
|
|
|
// Store the new offset and return it
|
|
m_cbPos = (ULONG)lOffset;
|
|
|
|
if (plibNewPosition)
|
|
{
|
|
plibNewPosition->HighPart = 0;
|
|
plibNewPosition->LowPart = m_cbPos;
|
|
}
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
ULONG _BinaryFromASCII2( unsigned char alpha )
|
|
{
|
|
switch (alpha)
|
|
{
|
|
case ' ':
|
|
case '\t':
|
|
case '\n':
|
|
case '\r':
|
|
return(IGNORE_CHAR);
|
|
|
|
default:
|
|
if ( (alpha >= 'A') && (alpha <= 'Z') )
|
|
{
|
|
return (int)(alpha - 'A');
|
|
}
|
|
else if ( (alpha >= 'a') && (alpha <= 'z') )
|
|
{
|
|
return 26 + (int)(alpha - 'a');
|
|
}
|
|
else if ( (alpha >= '0') && (alpha <= '9' ) )
|
|
{
|
|
return 52 + (int)(alpha - '0');
|
|
}
|
|
|
|
return(INVALID_CHAR);
|
|
|
|
case '+':
|
|
return 62;
|
|
|
|
case '/':
|
|
return 63;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
struct _BinASCIIData
|
|
{
|
|
BOOL m_bInited;
|
|
ULONG m_anBinary[256];
|
|
} g_cBinASCIIData = { FALSE } ;
|
|
|
|
void _InitTables()
|
|
{
|
|
if (g_cBinASCIIData.m_bInited)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int i=0; i<256; ++i)
|
|
{
|
|
// Note this is thread-safe, since we always set to the same value
|
|
g_cBinASCIIData.m_anBinary[i] = _BinaryFromASCII2((unsigned char)i);
|
|
}
|
|
|
|
// Set after initing other values to make thread-safe
|
|
g_cBinASCIIData.m_bInited = TRUE;
|
|
}
|
|
|
|
|
|
inline ULONG _BinaryFromASCII( unsigned char alpha )
|
|
{
|
|
return(g_cBinASCIIData.m_anBinary[alpha]);
|
|
}
|
|
|
|
|
|
HRESULT Mime64Decode(LPCMSTR pStrData, IStream **ppstm)
|
|
{
|
|
*ppstm = NULL;
|
|
|
|
_InitTables();
|
|
|
|
// Waste some bytes so I don't have to worry about overflow
|
|
CStreamMem *pstm = CStreamMem::Construct((lstrlen(pStrData)*3)/4 + 2);
|
|
if (!pstm)
|
|
{
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
|
|
LPBYTE pData = (LPBYTE)pstm->GetPtr();
|
|
int cbData = 0;
|
|
|
|
int shift = 0;
|
|
unsigned long accum = 0;
|
|
|
|
BOOL quit = FALSE;
|
|
|
|
// This loop will ignore white space, but quit at any other invalid characters
|
|
for ( ; ; ++pStrData)
|
|
{
|
|
unsigned long value = _BinaryFromASCII(*pStrData);
|
|
|
|
if ( value < 64 )
|
|
{
|
|
accum <<= 6;
|
|
shift += 6;
|
|
accum |= value;
|
|
|
|
if ( shift >= 8 )
|
|
{
|
|
shift -= 8;
|
|
value = accum >> shift;
|
|
pData[cbData++] = (BYTE)value & 0xFF;
|
|
}
|
|
}
|
|
else if (IGNORE_CHAR == value)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
pstm->SetSize(cbData);
|
|
*ppstm = pstm;
|
|
|
|
return(NOERROR);
|
|
}
|
|
#endif
|
|
|
|
#define CHARS_PER_LINE 60
|
|
|
|
|
|
class COutputChars
|
|
{
|
|
public:
|
|
COutputChars(LPMSTR pStrData) : m_pStrData(pStrData), m_cbLine(0) {}
|
|
void AddChar(MCHAR cAdd)
|
|
{
|
|
*m_pStrData++ = cAdd;
|
|
if (++m_cbLine == CHARS_PER_LINE)
|
|
{
|
|
*m_pStrData++ = '\n';
|
|
m_cbLine = 0;
|
|
}
|
|
}
|
|
LPMSTR GetPtr() { return(m_pStrData); }
|
|
|
|
private:
|
|
LPMSTR m_pStrData;
|
|
UINT m_cbLine;
|
|
} ;
|
|
|
|
|
|
HRESULT Mime64Encode(LPBYTE pData, UINT cbData, IStream **ppstm)
|
|
{
|
|
static char const alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
"0123456789+/";
|
|
|
|
*ppstm = NULL;
|
|
|
|
// Waste some bytes so I don't have to worry about overflow
|
|
// The 81/80 is to add a '\n' at the end of every 80 characters
|
|
CStreamMem *pstm = CStreamMem::Construct((((cbData*4)/3 + 4)
|
|
*(CHARS_PER_LINE+1)/CHARS_PER_LINE+2)*sizeof(MCHAR));
|
|
if (!pstm)
|
|
{
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
|
|
COutputChars cStrData((LPMSTR)pstm->GetPtr());
|
|
LPMSTR pSaveData = cStrData.GetPtr();
|
|
|
|
int shift = 0;
|
|
int save_shift = 0;
|
|
unsigned long accum = 0;
|
|
int index = 0;
|
|
unsigned char blivit;
|
|
unsigned long value;
|
|
BOOL quit = FALSE;
|
|
|
|
while ( ( cbData ) || (shift != 0) )
|
|
{
|
|
if ( ( cbData ) && ( quit == FALSE ) )
|
|
{
|
|
blivit = *pData++;
|
|
--cbData;
|
|
}
|
|
else
|
|
{
|
|
quit = TRUE;
|
|
save_shift = shift;
|
|
blivit = 0;
|
|
}
|
|
|
|
if ( (quit == FALSE) || (shift != 0) )
|
|
{
|
|
value = (unsigned long)blivit;
|
|
accum <<= 8;
|
|
shift += 8;
|
|
accum |= value;
|
|
} /* ENDIF */
|
|
|
|
while ( shift >= 6 )
|
|
{
|
|
shift -= 6;
|
|
value = (accum >> shift) & 0x3Fl;
|
|
blivit = alphabet[value];
|
|
|
|
cStrData.AddChar(blivit);
|
|
|
|
if ( quit != FALSE )
|
|
{
|
|
shift = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( save_shift == 2 )
|
|
{
|
|
cStrData.AddChar('=');
|
|
cStrData.AddChar('=');
|
|
}
|
|
else if ( save_shift == 4 )
|
|
{
|
|
cStrData.AddChar('=');
|
|
}
|
|
|
|
cStrData.AddChar('\n');
|
|
cStrData.AddChar('\0');
|
|
|
|
pstm->SetSize((int)(cStrData.GetPtr()-pSaveData) * sizeof(MCHAR));
|
|
*ppstm = pstm;
|
|
|
|
return(NOERROR);
|
|
}
|