Windows2003-3790/inetcore/outlookexpress/inetcomm/mimeole/inetconv.cpp
2020-09-30 16:53:55 +02:00

2602 lines
80 KiB
C++

// --------------------------------------------------------------------------------
// Inetconv.cpp
// Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
// Steven J. Bailey
// --------------------------------------------------------------------------------
#include "pch.hxx"
#include "dllmain.h"
#include "inetconv.h"
#include "internat.h"
#ifndef MAC
#include <shlwapi.h>
#include <mlang.h>
#endif // !MAC
#include "mimeapi.h"
#include "icoint.h"
#include "demand.h"
// --------------------------------------------------------------------------------
// FGROWBUFFER
// --------------------------------------------------------------------------------
#define FGROWBUFFER(_pBuffer, _cb) ((_pBuffer)->cb + _cb >= (_pBuffer)->cbAlloc)
// --------------------------------------------------------------------------------
// QP Encoder
// --------------------------------------------------------------------------------
const CHAR g_rgchHex[] = "0123456789ABCDEF";
// --------------------------------------------------------------------------------
// Base64 Decoding Table
// ---------------------
// Decodes one Base64 character into a numeric value
//
// 0 1 2 3 4 5 6
// 0123456789012345678901234567890123456789012345678901234567890123
// ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
// --------------------------------------------------------------------------------
const char g_rgchDecodeBase64[256] = {
64, 64, 64, 64, 64, 64, 64, 64, // 0x00
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, // 0x10
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, // 0x20
64, 64, 64, 62, 64, 64, 64, 63,
52, 53, 54, 55, 56, 57, 58, 59, // 0x30
60, 61, 64, 64, 64, 0, 64, 64,
64, 0, 1, 2, 3, 4, 5, 6, // 0x40
7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, // 0x50
23, 24, 25, 64, 64, 64, 64, 64,
64, 26, 27, 28, 29, 30, 31, 32, // 0x60
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, // 0x70
49, 50, 51, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, // 0x80
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, // 0x90
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, // 0xA0
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, // 0xB0
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, // 0xC0
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, // 0xD0
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, // 0xE0
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, // 0xF0
64, 64, 64, 64, 64, 64, 64, 64,
};
// --------------------------------------------------------------------------------
// Base64 Encoder
// --------------------------------------------------------------------------------
extern const CHAR g_rgchEncodeBase64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ ";
// --------------------------------------------------------------------------------
// BinHex Decoding Table
// ---------------------
// Decodes one BinHex character into a numeric value
//
// 0 1 2 3 4 5 6
// 0123456789012345678901234567890123456789012345678901234567890123
// !"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr
// --------------------------------------------------------------------------------
#undef BINHEX_INVALID
#undef BINHEX_REPEAT
#undef XXXX
const UCHAR BINHEX_INVALID = 0x40;
const UCHAR BINHEX_REPEAT = 0x90;
const UCHAR BINHEX_TERM = ':';
const UCHAR XXXX = BINHEX_INVALID;
const ULONG cbMinBinHexHeader = 22;
const WORD wBinHexZero = 0;
const UCHAR g_rgchDecodeBinHex[256] = {
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, // 0x00
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX,
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, // 0x10
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX,
XXXX, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // 0x20
0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, XXXX, XXXX,
0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, XXXX, // 0x30
0x14, 0x15, 0x16, XXXX, XXXX, XXXX, XXXX, XXXX,
0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, // 0x40
0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, XXXX,
0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, XXXX, // 0x50
0x2C, 0x2D, 0x2E, 0x2F, XXXX, XXXX, XXXX, XXXX,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, XXXX, // 0x60
0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, XXXX, XXXX,
0x3D, 0x3E, 0x3F, XXXX, XXXX, XXXX, XXXX, XXXX, // 0x70
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX,
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, // 0x80
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX,
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, // 0x90
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX,
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, // 0xA0
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX,
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, // 0xB0
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX,
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, // 0xC0
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX,
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, // 0xD0
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX,
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, // 0xE0
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX,
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, // 0xF0
XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX, XXXX,
};
// --------------------------------------------------------------------------------
// HrCreateLineBreaker
// --------------------------------------------------------------------------------
HRESULT HrCreateLineBreaker(IMLangLineBreakConsole **ppLineBreak)
{
// Locals
HRESULT hr=S_OK;
PFNGETCLASSOBJECT pfnDllGetClassObject=NULL;
IClassFactory *pFactory=NULL;
// Invalid Args
Assert(ppLineBreak);
// Init
*ppLineBreak = NULL;
// Thread Safety
EnterCriticalSection(&g_csMLANG);
// If not loaded yet
if (NULL == g_hinstMLANG)
{
// Load MLANG - This should be fast most of the time because MLANG is usually loaded
g_hinstMLANG = LoadLibrary("MLANG.DLL");
if (NULL == g_hinstMLANG)
{
hr = TrapError(E_FAIL);
goto exit;
}
}
// Get DllClassObject
pfnDllGetClassObject = (PFNGETCLASSOBJECT)GetProcAddress(g_hinstMLANG, "DllGetClassObject");
if (NULL == pfnDllGetClassObject)
{
hr = TrapError(E_FAIL);
goto exit;
}
// Get the MLANG Class Factory
CHECKHR(hr = (*pfnDllGetClassObject)(CLSID_CMultiLanguage, IID_IClassFactory, (LPVOID *)&pFactory));
// Finally, create the object that I actually wanted
CHECKHR(hr = pFactory->CreateInstance(NULL, IID_IMLangLineBreakConsole, (LPVOID *)ppLineBreak));
exit:
// Thread Safety
LeaveCriticalSection(&g_csMLANG);
// Cleanup
SafeRelease(pFactory);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// HrCreateInternetConverter
// --------------------------------------------------------------------------------
HRESULT HrCreateInternetConverter(LPCONVINITINFO pInitInfo, CInternetConverter **ppConverter)
{
// Allocate It
*ppConverter = new CInternetConverter();
if (NULL == *ppConverter)
return TrapError(E_OUTOFMEMORY);
// Initialize
return TrapError((*ppConverter)->HrInit(pInitInfo));
}
// --------------------------------------------------------------------------------
// BinHexCalcCRC16
// --------------------------------------------------------------------------------
void BinHexCalcCRC16( LPBYTE lpbBuff, ULONG cBuff, WORD * wCRC )
{
LPBYTE lpb;
BYTE b;
WORD uCRC;
WORD fWrap;
ULONG i;
uCRC = *wCRC;
for ( lpb = lpbBuff; lpb < lpbBuff + cBuff; lpb++ )
{
b = *lpb;
for ( i = 0; i < 8; i++ )
{
fWrap = uCRC & 0x8000;
uCRC = (uCRC << 1) | (b >> 7);
if ( fWrap )
{
uCRC = uCRC ^ 0x1021;
}
b = b << 1;
}
}
*wCRC = uCRC;
}
// --------------------------------------------------------------------------------
// HrCreateMacBinaryHeader
// --------------------------------------------------------------------------------
HRESULT HrCreateMacBinaryHeader(LPCONVERTBUFFER prBinHexHeader, LPCONVERTBUFFER prMacBinaryHeader)
{
HRESULT hr = S_OK;
LPMACBINARY pmacbin;
LPBYTE pbBinHex;
#ifndef _MAC
WORD wCRC = 0;
#endif // _MAC
if ((NULL == prBinHexHeader) || (NULL == prMacBinaryHeader))
{
hr = ERROR_INVALID_PARAMETER;
goto exit;
}
pmacbin = (LPMACBINARY)(prMacBinaryHeader->pb);
pbBinHex = (LPBYTE)(prBinHexHeader->pb);
// Zero it out first
ZeroMemory(pmacbin, sizeof(MACBINARY));
// Write in the filename length
pmacbin->cchFileName = (BYTE)min(pbBinHex[0], sizeof(pmacbin->rgchFileName)-1);
pbBinHex += 1;
// Copy over the filename
CopyMemory(pmacbin->rgchFileName, pbBinHex, pmacbin->cchFileName);
pmacbin->rgchFileName[pmacbin->cchFileName] = '\0';
pbBinHex += pmacbin->cchFileName + 1;
// Copy over the type and creator
CopyMemory(&(pmacbin->dwType), pbBinHex, sizeof(pmacbin->dwType));
pbBinHex += 4;
CopyMemory(&(pmacbin->dwCreator), pbBinHex, sizeof(pmacbin->dwCreator));
pbBinHex += 4;
// Copy over the finder flags
pmacbin->bFinderFlags = *pbBinHex;
pbBinHex++;
pmacbin->bFinderFlags2 = *pbBinHex;
pbBinHex++;
// Copy over the data fork length
CopyMemory(&(pmacbin->lcbDataFork), pbBinHex, sizeof(pmacbin->lcbDataFork));
pbBinHex += 4;
// Copy over the resource fork length
CopyMemory(&(pmacbin->lcbResourceFork), pbBinHex, sizeof(pmacbin->lcbResourceFork));
pbBinHex += 4;
// Drop on the version stamps
pmacbin->bVerMacBin2 = 129;
pmacbin->bMinVerMacBin2 = 129;
// Calculate the CRC
#ifdef _MAC
BinHexCalcCRC16((LPBYTE) pmacbin, 124, &(pmacbin->wCRC));
BinHexCalcCRC16((LPBYTE) &wBinHexZero, sizeof(wBinHexZero), &(pmacbin->wCRC));
#else // !_MAC
BinHexCalcCRC16((LPBYTE) pmacbin, 124, &(wCRC));
BinHexCalcCRC16((LPBYTE) &wBinHexZero, sizeof(wBinHexZero), &(wCRC));
// Need to keep it in Mac order
pmacbin->wCRC = HIBYTE(wCRC);
pmacbin->wCRC |= (LOBYTE(wCRC) << 8);
#endif // _MAC
prMacBinaryHeader->cb += sizeof(MACBINARY);
exit:
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter
// --------------------------------------------------------------------------------
CInternetConverter::CInternetConverter(void)
{
m_cRef = 1;
m_dwFlags = 0;
m_cbConvert = 0;
m_ietEncoding = IET_BINARY;
m_cpiSource = CP_ACP;
m_cpiDest = CP_ACP;
m_fLastBuffer = FALSE;
m_fEncoder = FALSE;
m_uchPrev = '\0';
m_pAppend = NULL;
m_pWrite = NULL;
m_convtype = ICT_UNKNOWN;
m_cchMaxLine = 0;
m_pBinhexEncode = NULL;
m_eBinHexStateDec = sSTARTING;
m_fRepeating = FALSE;
m_cAccum = 0;
m_prBinhexOutput = &m_rOut;
m_cbToProcess = 0;
m_cbDataFork = 0;
m_cbResourceFork = 0;
m_wCRC = 0;
m_wCRCForFork = 0;
m_fDataForkOnly = FALSE;
m_pLineBreak = NULL;
ZeroMemory(&m_rIn, sizeof(CONVERTBUFFER));
ZeroMemory(&m_rOut, sizeof(CONVERTBUFFER));
ZeroMemory(&m_rCset, sizeof(CONVERTBUFFER));
ZeroMemory(&m_rBinhexHeader, sizeof(CONVERTBUFFER));
}
// --------------------------------------------------------------------------------
// CInternetConverter::~CInternetConverter
// --------------------------------------------------------------------------------
CInternetConverter::~CInternetConverter(void)
{
if (m_pBinhexEncode)
delete m_pBinhexEncode;
SafeMemFree(m_rIn.pb);
SafeMemFree(m_rOut.pb);
SafeMemFree(m_rCset.pb);
SafeMemFree(m_rBinhexHeader.pb);
SafeRelease(m_pLineBreak);
}
// --------------------------------------------------------------------------------
// CInternetConverter::QueryInterface
// --------------------------------------------------------------------------------
STDMETHODIMP CInternetConverter::QueryInterface(REFIID riid, LPVOID *ppv)
{
return E_NOTIMPL;
}
// --------------------------------------------------------------------------------
// CInternetConverter::AddRef
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CInternetConverter::AddRef(void)
{
return ++m_cRef;
}
// --------------------------------------------------------------------------------
// CInternetConverter::Release
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CInternetConverter::Release(void)
{
if (0 != --m_cRef)
return m_cRef;
delete this;
return 0;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrInit
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrInit(LPCONVINITINFO pInitInfo)
{
// Locals
HRESULT hr=S_OK;
// Save Flags
m_dwFlags = pInitInfo->dwFlags;
// Save Format
m_ietEncoding = pInitInfo->ietEncoding;
// Save Source Code Page
m_cpiSource = pInitInfo->cpiSource;
// Save Dest Code Page
m_cpiDest = pInitInfo->cpiDest;
// Are we an encoder..
m_fEncoder = pInitInfo->fEncoder;
// Save Wrap Info
m_cchMaxLine = pInitInfo->cchMaxLine;
// Save MacBinary state
m_fDataForkOnly = !pInitInfo->fShowMacBinary;
// InitConvertType
CHECKHR(hr = HrInitConvertType(pInitInfo));
// DoubleCheck
Assert(m_pWrite && m_pAppend && ICT_UNKNOWN != m_convtype);
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrInitConvertType
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrInitConvertType(LPCONVINITINFO pInitInfo)
{
// Locals
HRESULT hr=S_OK;
CODEPAGEINFO CodePage;
CODEPAGEID cpiLCID;
// Time to compute m_pAppend and m_pDump...
if (ICF_WRAPTEXT & m_dwFlags)
{
// Check Assumptions
Assert((IET_7BIT == m_ietEncoding || IET_8BIT == m_ietEncoding) && TRUE == m_fEncoder);
// Code Page Conversion...
if (ICF_CODEPAGE & m_dwFlags)
m_convtype = ICT_WRAPTEXT_CODEPAGE;
else
m_convtype = ICT_WRAPTEXT;
// Load MLANG
CHECKHR(hr = HrCreateLineBreaker(&m_pLineBreak));
// Set cpiLCID
cpiLCID = m_cpiSource;
// Unicode ?
if (CP_UNICODE == m_cpiSource)
{
// Get Destination Code Page Info
if (SUCCEEDED(g_pInternat->GetCodePageInfo(m_cpiDest, &CodePage)))
{
// Set cpiLCID
cpiLCID = CodePage.cpiFamily;
}
}
// Map m_cpiSource to lcid
switch(cpiLCID)
{
case 874: m_lcid = 0x041E; break;
case 932: m_lcid = 0x0411; break;
case 936: m_lcid = 0x0804; break;
case 949: m_lcid = 0x0412; break;
case 950: m_lcid = 0x0404; break;
case 1250: m_lcid = 0x040e; break;
case 1251: m_lcid = 0x0419; break;
case 1252: m_lcid = 0x0409; break;
case 1253: m_lcid = 0x0408; break;
case 1254: m_lcid = 0x041f; break;
case 1255: m_lcid = 0x040d; break;
case 1256: m_lcid = 0x0401; break;
case 1257: m_lcid = 0x0426; break;
default: m_lcid = GetSystemDefaultLCID(); break;
}
}
// Otherwise, if encoding
else if (TRUE == m_fEncoder)
{
// If CodePage Conversion
if (ICF_CODEPAGE & m_dwFlags)
m_convtype = ICT_CODEPAGE_ENCODE;
else
m_convtype = ICT_ENCODE;
// Need binhex encoder
if (IET_BINHEX40 == m_ietEncoding)
{
// Create me an encoder
CHECKALLOC(m_pBinhexEncode = new CBinhexEncoder);
// Initialize
CHECKHR(hr = m_pBinhexEncode->HrConfig(0, 0, &pInitInfo->rMacBinary));
}
}
// Otherwise, if not encoding
else
{
// If CodePage Conversion
if (ICF_CODEPAGE & m_dwFlags)
m_convtype = ICT_DECODE_CODEPAGE;
else
m_convtype = ICT_DECODE;
}
// Map Write and Append Buffers from Conversion Type
switch(m_convtype)
{
// m_rIn --> m_rCset
case ICT_WRAPTEXT_CODEPAGE:
case ICT_DECODE_CODEPAGE:
m_pAppend = &m_rIn;
m_pWrite = &m_rCset;
break;
// m_rIn --> m_rOut
case ICT_WRAPTEXT:
case ICT_ENCODE:
case ICT_DECODE:
m_pAppend = &m_rIn;
m_pWrite = &m_rOut;
break;
// m_rCset --> m_rOut
case ICT_CODEPAGE_ENCODE:
m_pAppend = &m_rCset;
m_pWrite = &m_rOut;
break;
// Error
default:
AssertSz(FALSE, "INVALID INETCONVTYPE");
break;
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrConvBuffAppendBlock
// --------------------------------------------------------------------------------
inline HRESULT CInternetConverter::HrConvBuffAppendBlock(LPBYTE pb, ULONG cb)
{
// Locals
HRESULT hr=S_OK;
// Do I need to grow
if (FGROWBUFFER(&m_rOut, cb))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(&m_rOut, cb));
}
// Copy the buffer
CopyMemory(m_rOut.pb + m_rOut.cb, pb, cb);
// Increment Size
m_rOut.cb += cb;
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::PszConvBuffGetNextLine
// --------------------------------------------------------------------------------
inline LPSTR CInternetConverter::PszConvBuffGetNextLine(ULONG *pcbLine, ULONG *pcbRead, BOOL *pfFound)
{
// Locals
UCHAR uchThis, uchPrev;
ULONG cbLine=0;
// Invalid Arg
Assert(pcbLine && pcbRead && pfFound);
// Init
*pfFound = FALSE;
// Read to next \n
while(m_rIn.i + cbLine < m_rIn.cb)
{
// Get a character...
uchThis = m_rIn.pb[m_rIn.i + cbLine];
// Better not be null
Assert(uchThis);
// Increment Line Length
cbLine++;
// Done
if (chLF == uchThis)
{
*pfFound = TRUE;
break;
}
// Remember Previous Char
uchPrev = uchThis;
}
// Set Next Line
*pcbRead = cbLine;
// Fixup cbLine
if (chLF == uchThis)
cbLine--;
if (chCR == uchPrev)
cbLine--;
// Set Length
*pcbLine = cbLine;
// Done
return (LPSTR)(m_rIn.pb + m_rIn.i);
}
// --------------------------------------------------------------------------------
// CInternetConverter::CopyMemoryRemoveNBSP
// --------------------------------------------------------------------------------
void CInternetConverter::CopyMemoryRemoveNBSP(LPBYTE pbDest, LPBYTE pbSource, ULONG cbSource)
{
// Locals
ULONG iDest=0;
ULONG iSource=0;
// Invalid ARg
Assert(pbDest && pbSource && CP_UNICODE == m_cpiSource);
// Do It
while(1)
{
// If not a null lead, copy next two bytes...
if (iSource + 1 < cbSource)
{
// Better not be 0x00A0 - insert space
Assert(iSource % 2 == 0);
if (0xA0 == pbSource[iSource] && 0x00 == pbSource[iSource + 1])
{
// 0x0020 = Space
pbDest[iDest++] = 0x20;
pbDest[iDest++] = 0x00;
// Step Over this character...
iSource+=2;
}
// Otherwise, copy the character
else
{
// Copy This Char
pbDest[iDest++] = pbSource[iSource++];
// Copy Next Char
if (iSource < cbSource)
pbDest[iDest++] = pbSource[iSource++];
}
}
// Otherwise, just copy this once character and stop
else
{
// Copy It
if (iSource < cbSource)
pbDest[iDest++] = pbSource[iSource++];
// Done
break;
}
}
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrFillAppend
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrFillAppend(LPBLOB pData)
{
// Locals
HRESULT hr=S_OK;
// Invlaid ARg
Assert(pData && m_pAppend);
// Call Internal Function
CHECKHR(hr = HrAppendBuffer(m_pAppend, pData, (m_dwFlags & ICF_KILLNBSP)));
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrAppendBuffer
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrAppendBuffer(LPCONVERTBUFFER pBuffer, LPBLOB pData, BOOL fKillNBSP)
{
// Locals
HRESULT hr=S_OK;
// Collapse Current Buffer
if (pBuffer->i != 0)
{
// Move Memory
MoveMemory(pBuffer->pb, pBuffer->pb + pBuffer->i, pBuffer->cb - pBuffer->i);
// Decrease Size
pBuffer->cb -= pBuffer->i;
// Reset Start
pBuffer->i = 0;
}
// Enought Space ?
// Do I need to grow
if (FGROWBUFFER(pBuffer, pData->cbSize))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(pBuffer, pData->cbSize));
}
// Append the buffer...
if (fKillNBSP)
CopyMemoryRemoveNBSP(pBuffer->pb + pBuffer->cb, pData->pBlobData, pData->cbSize);
// Otherwise, this is a simple copy
else
CopyMemory(pBuffer->pb + pBuffer->cb, pData->pBlobData, pData->cbSize);
// Increment Amount of Data
pBuffer->cb += pData->cbSize;
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrGrowBuffer
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrGrowBuffer(LPCONVERTBUFFER pBuffer, ULONG cbAppend)
{
// Locals
HRESULT hr=S_OK;
ULONG cbGrow;
// Better need a grow
Assert(FGROWBUFFER(pBuffer, cbAppend));
// Compute Grow By
cbGrow = (cbAppend - (pBuffer->cbAlloc - pBuffer->cb)) + 256;
// Realloc the buffer
CHECKHR(hr = HrRealloc((LPVOID *)&pBuffer->pb, pBuffer->cbAlloc + cbGrow));
// Adjust cbAlloc
pBuffer->cbAlloc += cbGrow;
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrWriteConverted
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrWriteConverted(IStream *pStream)
{
// Locals
HRESULT hr=S_OK;
// Anything to write
if (m_pWrite->cb)
{
// Write the current block
CHECKHR(hr = pStream->Write(m_pWrite->pb, m_pWrite->cb, NULL));
// Nothing in m_rOut
m_pWrite->cb = 0;
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrWriteConverted
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrWriteConverted(CInternetConverter *pConverter)
{
// Locals
HRESULT hr=S_OK;
BLOB rData;
// Anything to write
if (m_pWrite->cb)
{
// Setup Blob
rData.pBlobData = m_pWrite->pb;
rData.cbSize = m_pWrite->cb;
// Write the current block
CHECKHR(hr = pConverter->HrFillAppend(&rData));
// Nothing in m_rOut
m_pWrite->cb = 0;
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrInternetEncode
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrInternetEncode(BOOL fLastBuffer)
{
// Locals
HRESULT hr=S_OK;
HRESULT hrWarnings=S_OK;
BLOB rData;
// We Better be an encoder
Assert(m_fEncoder);
// Set Last Buffer
m_fLastBuffer = fLastBuffer;
// Text Wrapping ?
if (ICF_WRAPTEXT & m_dwFlags)
{
// Wrap It: m_rIn -> m_rOut
if (CP_UNICODE == m_cpiSource)
CHECKHR(hr = HrWrapInternetTextW());
else
CHECKHR(hr = HrWrapInternetTextA());
// Character Set Encoding: m_rOut -> m_rCset
if (ICF_CODEPAGE & m_dwFlags)
{
// Charset Encode
CHECKHR(hr = HrCodePageFromOutToCset());
if ( S_OK != hr )
hrWarnings = TrapError(hr);
}
}
// Otherwise
else
{
// Character Set Encoding: m_rCset -> m_rIn
if (ICF_CODEPAGE & m_dwFlags)
{
// Charset Encode
CHECKHR(hr = HrCodePageFromCsetToIn());
if ( S_OK != hr )
hrWarnings = TrapError(hr);
}
// Handle Conversion type
switch(m_ietEncoding)
{
// Binary
case IET_BINARY:
case IET_7BIT:
case IET_8BIT:
// Better be at zero
Assert(m_rIn.i == 0);
// Initialize Blob to copy
rData.pBlobData = m_rIn.pb;
rData.cbSize = m_rIn.cb;
// Append to outbound buffer
CHECKHR(hr = HrAppendBuffer(&m_rOut, &rData, FALSE));
// Increment offset
m_rIn.i = m_rIn.cb = 0;
break;
// Quoted-Printable
case IET_QP:
CHECKHR(hr = HrEncodeQP());
break;
// Bas 64
case IET_BASE64:
CHECKHR(hr = HrEncode64());
break;
// UUENCODE
case IET_UUENCODE:
CHECKHR(hr = HrEncodeUU());
break;
// BINHEX
case IET_BINHEX40:
#ifdef NEVER
CHECKHR(hr = HrEncodeBinhex());
#endif // NEVER
// IE v. 5.0:33596 HrEncodeBinhex returns E_FAIL if body size is too small
// Binhex encoding doesn't currently work. I believe that it should work (or almost work)
// if the header CBinhexEncoder::m_lpmacbinHdr is initialized properly. However, this
// requires understanding the Mac file format and parsing the body stream contents into
// data and resource forks.
// - sethco 8/19/1998
CHECKHR(hr = MIME_E_INVALID_ENCODINGTYPE);
break;
// Bummer
default:
AssertSz(FALSE, "MIME_E_INVALID_ENCODINGTYPE");
break;
}
}
exit:
// If Last Buffer, we better be done
Assert(m_fLastBuffer ? m_rIn.i == m_rIn.cb : TRUE);
// Done
return (hr == S_OK) ? hrWarnings : hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrInternetDecode
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrInternetDecode(BOOL fLastBuffer)
{
// Locals
HRESULT hr=S_OK;
HRESULT hrWarnings=S_OK;
BLOB rData;
// We Better not be an encoder
Assert(!m_fEncoder);
// Set Last Buffer
m_fLastBuffer = fLastBuffer;
// Handle Format
switch(m_ietEncoding)
{
// Binary
case IET_BINARY:
case IET_7BIT:
case IET_8BIT:
// Better be at zero
Assert(m_rIn.i == 0);
// Initialize Blob to copy
rData.pBlobData = m_rIn.pb;
rData.cbSize = m_rIn.cb;
// Append to outbound buffer
CHECKHR(hr = HrAppendBuffer(&m_rOut, &rData, FALSE));
// Increment offset
m_rIn.i = m_rIn.cb = 0;
break;
// Quoted-Printable
case IET_QP:
CHECKHR(hr = HrDecodeQP());
break;
// Bas64
case IET_BASE64:
CHECKHR(hr = HrDecode64());
break;
// UUENCODE
case IET_UUENCODE:
CHECKHR(hr = HrDecodeUU());
break;
// BINHEX
case IET_BINHEX40:
CHECKHR(hr = HrDecodeBinHex());
break;
// Bummer
default:
AssertSz(FALSE, "MIME_E_INVALID_ENCODINGTYPE");
break;
}
// Character Set Decoding ?
if (ICF_CODEPAGE & m_dwFlags)
{
// Charset Decoder
CHECKHR(hr = HrCodePageFromOutToCset());
if ( S_OK != hr )
hrWarnings = TrapError(hr);
}
exit:
// If Last Buffer, we better be done
Assert(m_fLastBuffer ? m_rIn.i == m_rIn.cb : TRUE);
// Done
return (hr == S_OK) ? hrWarnings : hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrCodePageFromOutToCset
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrCodePageFromOutToCset(void)
{
// Locals
HRESULT hr=S_OK;
HRESULT hrWarnings=S_OK;
BLOB rData;
BLOB rDecoded={0};
ULONG cbRead;
// Nothing to convert...
if (0 == m_rOut.cb)
return S_OK;
// Setup Convert Blob
rData.pBlobData = m_rOut.pb;
rData.cbSize = m_rOut.cb;
// Decode text from m_intformat
hr = g_pInternat->ConvertBuffer(m_cpiSource, m_cpiDest, &rData, &rDecoded, &cbRead);
if (SUCCEEDED(hr) )
{
// save HRESULT from charset conversion
if ( S_OK != hr )
hrWarnings = TrapError(hr);
// Fill m_rIn...
CHECKHR(hr = HrAppendBuffer(&m_rCset, &rDecoded, FALSE));
}
// Otherwise, just put m_rCset as the inbound buffer
else
{
// SBAILEY: Raid-74506: MIMEOLE: error decoding text body in q-p encoded iso-2022-jp message
// CHECKHR(hr = HrAppendBuffer(&m_rCset, &rData, FALSE));
hr = S_OK;
// We read all of it
cbRead = rData.cbSize;
}
// Adjust m_rOut if cbRead != m_rOut.cb
if (cbRead != m_rOut.cb)
{
// Move Memory
MoveMemory(m_rOut.pb, m_rOut.pb + cbRead, m_rOut.cb - cbRead);
}
// Decrease Size
Assert(cbRead <= m_rOut.cb);
m_rOut.cb -= cbRead;
exit:
// Cleanup
SafeMemFree(rDecoded.pBlobData);
// Done
return (hr == S_OK) ? hrWarnings : hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrCodePageFromCsetToIn
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrCodePageFromCsetToIn(void)
{
// Locals
HRESULT hr=S_OK;
HRESULT hrWarnings=S_OK;
BLOB rData;
BLOB rEncoded={0};
ULONG cbRead;
// Check State
Assert(m_rCset.i == 0);
// Nothing to convert
if (0 == m_rCset.cb)
return S_OK;
// Setup Convert Blob
rData.pBlobData = m_rCset.pb;
rData.cbSize = m_rCset.cb;
// Decode text from m_intformat
hr = g_pInternat->ConvertBuffer(m_cpiSource, m_cpiDest, &rData, &rEncoded, &cbRead);
if (SUCCEEDED(hr) )
{
// save HRESULT from charset conversion
if ( S_OK != hr )
hrWarnings = TrapError(hr);
// Fill m_rIn...
CHECKHR(hr = HrAppendBuffer(&m_rIn, &rEncoded, FALSE));
}
// Otherwise, just put m_rCset as the inbound buffer
else
{
// SBAILEY: Raid-74506: MIMEOLE: error decoding text body in q-p encoded iso-2022-jp message
// CHECKHR(hr = HrAppendBuffer(&m_rIn, &rData, FALSE));
hr = S_OK;
// Set Read
cbRead = m_rCset.cb;
}
// Adjust m_rOut if cbRead != m_rOut.cb
if (cbRead != m_rCset.cb)
{
// Move Memory
MoveMemory(m_rCset.pb, m_rCset.pb + cbRead, m_rCset.cb - cbRead);
}
// Decrease Size
Assert(cbRead <= m_rCset.cb);
m_rCset.cb -= cbRead;
m_rCset.i = 0;
exit:
// Cleanup
SafeMemFree(rEncoded.pBlobData);
// Done
return (hr == S_OK) ? hrWarnings : hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrEncode64
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrEncode64(void)
{
// Locals
HRESULT hr=S_OK;
ULONG cbRead;
ULONG i;
UCHAR uch[3];
UCHAR *pbuf;
// Read lines and stuff dots
while(1)
{
// Compute encode buffer length
cbRead = min(CCHMAX_ENCODE64_IN, m_rIn.cb - m_rIn.i);
// Should we encode this buffer ?
if (0 == cbRead || (cbRead < CCHMAX_ENCODE64_IN && FALSE == m_fLastBuffer))
goto exit;
// Do I need to grow
if (FGROWBUFFER(&m_rOut, CCHMAX_ENCODE64_OUT))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(&m_rOut, CCHMAX_ENCODE64_OUT));
}
// Set Buffer Pointer
pbuf = (m_rIn.pb + m_rIn.i);
// Encodes 3 characters at a time
for (i=0; i<cbRead; i+=3)
{
// Setup Buffer
uch[0] = pbuf[i];
uch[1] = (i+1 < cbRead) ? pbuf[i+1] : '\0';
uch[2] = (i+2 < cbRead) ? pbuf[i+2] : '\0';
// Encode first tow
ConvBuffAppend(g_rgchEncodeBase64[(uch[0] >> 2) & 0x3F]);
ConvBuffAppend(g_rgchEncodeBase64[(uch[0] << 4 | uch[1] >> 4) & 0x3F]);
// Encode Next
if (i+1 < cbRead)
ConvBuffAppend(g_rgchEncodeBase64[(uch[1] << 2 | uch[2] >> 6) & 0x3F]);
else
ConvBuffAppend('=');
// Encode Net
if (i+2 < cbRead)
ConvBuffAppend(g_rgchEncodeBase64[(uch[2] ) & 0x3F]);
else
ConvBuffAppend('=');
}
// Increment iIn
m_rIn.i += cbRead;
// Ends encoded line and writes to storage
ConvBuffAppend(chCR);
ConvBuffAppend(chLF);
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrDecode64
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrDecode64(void)
{
// Locals
HRESULT hr=S_OK;
UCHAR uchThis;
ULONG i;
ULONG cPad=0;
ULONG cbRead=0;
ULONG cbLine;
BOOL fFound;
LPSTR pszLine;
// Read lines and stuff dots
while(1)
{
// Increment Index
m_rIn.i += cbRead;
// Get Next Line
pszLine = PszConvBuffGetNextLine(&cbLine, &cbRead, &fFound);
if (0 == cbRead || (FALSE == fFound && FALSE == m_fLastBuffer))
goto exit;
// Do I need to grow - decoded line will always be smaller than cbLine
if (FGROWBUFFER(&m_rOut, cbLine))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(&m_rOut, cbLine));
}
// Decodes characters in line buffer
for (i=0; i<cbLine; i++)
{
// Gets 4 legal Base64 characters, ignores if illegal
uchThis = pszLine[i];
// Decode It
m_uchConvert[m_cbConvert] = DECODE64(uchThis);
// Test for valid non-pad
if ((m_uchConvert[m_cbConvert] < 64) || ((uchThis == '=') && (m_cbConvert > 1)))
m_cbConvert++;
// Test for pad
if ((uchThis == '=') && (m_cbConvert > 1))
cPad++;
// Outputs when 4 legal Base64 characters are in the buffer
if (4 == m_cbConvert)
{
// Validate Buffer
Assert(m_rOut.cb + 4 <= m_rOut.cbAlloc);
// Convert
if (cPad < 3)
ConvBuffAppend((m_uchConvert[0] << 2 | m_uchConvert[1] >> 4));
if (cPad < 2)
ConvBuffAppend((m_uchConvert[1] << 4 | m_uchConvert[2] >> 2));
if (cPad < 1)
ConvBuffAppend((m_uchConvert[2] << 6 | m_uchConvert[3]));
// Reset
m_cbConvert = 0;
}
}
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrEncodeUU
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrEncodeUU(void)
{
// Locals
HRESULT hr=S_OK;
ULONG cbRead, i;
UCHAR buf[CCHMAX_ENCODEUU_IN];
// Read lines and stuff dots
while(1)
{
// Compute encode buffer length
cbRead = min(CCHMAX_ENCODEUU_IN, m_rIn.cb - m_rIn.i);
if (0 == cbRead || (cbRead < CCHMAX_ENCODEUU_IN && FALSE == m_fLastBuffer))
goto exit;
// Copy the bytes
CopyMemory(buf, m_rIn.pb + m_rIn.i, cbRead);
// Zero the Rest
ZeroMemory(buf + cbRead, sizeof(buf) - cbRead);
// Do I need to grow
if (FGROWBUFFER(&m_rOut, CCHMAX_ENCODEUU_OUT))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(&m_rOut, CCHMAX_ENCODEUU_OUT));
}
// Encode Line length
ConvBuffAppend(UUENCODE((UCHAR)cbRead));
// Encodes 3 characters at a time
for (i=0; i<cbRead; i+=3)
{
ConvBuffAppend(UUENCODE((buf[i] >> 2)));
ConvBuffAppend(UUENCODE((buf[i] << 4) | (buf[i+1] >> 4)));
ConvBuffAppend(UUENCODE((buf[i+1] << 2) | (buf[i+2] >> 6)));
ConvBuffAppend(UUENCODE((buf[i+2])));
}
// Increment i
m_rIn.i += cbRead;
// Ends encoded line and writes to storage
ConvBuffAppend(chCR);
ConvBuffAppend(chLF);
}
exit:
// If last buffer and we can't read anymore
if (TRUE == m_fLastBuffer && FALSE == FConvBuffCanRead(m_rIn))
{
// RAID-21179: ZeroLength uuencoded attachments m_rOut may not have been allocated
// Do I need to grow
if (FGROWBUFFER(&m_rOut, CCHMAX_ENCODEUU_OUT))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(&m_rOut, CCHMAX_ENCODEUU_OUT));
}
// Better have space
Assert(m_rOut.cb + 3 < m_rOut.cbAlloc);
// End
ConvBuffAppend(UUENCODE(0));
ConvBuffAppend(chCR);
ConvBuffAppend(chLF);
}
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::FUUEncodeThrowAway
// --------------------------------------------------------------------------------
BOOL CInternetConverter::FUUEncodeThrowAway(LPSTR pszLine, ULONG cbLine, ULONG *pcbActual, ULONG *pcbLine)
{
// Locals
CHAR ch;
ULONG cchOffset, cbEncoded, cbTolerance=0, cbExpected;
// RAID-25953: "BEGIN --- CUT HERE --- Cut Here --- cut here ---" - WinVN post
// partial messages that have the following line at the beginning of
// each partial. B = 66 and the length of this line is 48, so the following
// code thinks that this line is a valid UUENCODED line, so, to fix this,
// we will throw out all lines that start with BEGIN since this is not valid
// to be in uuencode.
if (StrCmpNI("BEGIN", pszLine, 5) == 0)
return TRUE;
// END Line
else if (StrCmpNI("END", pszLine, 3) == 0)
return TRUE;
// Checks line length
ch = *pszLine;
*pcbLine = cbEncoded = UUDECODE(ch);
// Comput tolerance and offset for non-conforming even line lengths
cchOffset = (cbEncoded % 3);
if (cchOffset != 0)
{
cchOffset++;
cbTolerance = 4 - cchOffset;
}
// Compute expected line length
cbExpected = 4 * (cbEncoded / 3) + cchOffset;
// Always check for '-'
if (cbLine < cbExpected)
return TRUE;
// Wack off trailing spaces
while(pszLine[cbLine-1] == ' ' && cbLine > 0 && cbLine != cbExpected)
--cbLine;
// Checksum character and encoders which include the count char in the line count
if (cbExpected != cbLine && cbExpected + cbTolerance != cbLine &&
cbExpected + 1 != cbLine && cbExpected + cbTolerance + 1 != cbLine &&
cbExpected - 1 != cbLine && cbExpected + cbTolerance - 1 != cbLine)
return TRUE;
// Set actual line length
*pcbActual = cbLine;
// Done
return FALSE;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrDecodeUU
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrDecodeUU(void)
{
// Locals
HRESULT hr=S_OK;
ULONG cbLine;
LPSTR pszLine;
ULONG cbRead=0;
ULONG cbLineLength;
BOOL fFound;
ULONG cbConvert;
ULONG cbScan;
ULONG i;
UCHAR uchConvert[4];
UCHAR uchThis;
// Read lines and stuff dots
while(1)
{
// Increment Index
m_rIn.i += cbRead;
// Get Next Line
pszLine = PszConvBuffGetNextLine(&cbLine, &cbRead, &fFound);
if (0 == cbRead || (FALSE == fFound && FALSE == m_fLastBuffer))
goto exit;
// UUENCODE ThrowAway
if (FUUEncodeThrowAway(pszLine, cbLine, &cbLine, &cbLineLength))
continue;
// Do I need to grow
if (FGROWBUFFER(&m_rOut, cbLineLength + 20))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(&m_rOut, cbLineLength + 20));
}
// Decodes 4 characters at a time
for (cbConvert=0, cbScan=0, i=1; cbScan < cbLineLength; i++)
{
// Gets 4 characters, pads with blank if necessary
uchThis = (i < cbLine) ? pszLine[i] : ' ';
// Decode
uchConvert[cbConvert++] = UUDECODE(uchThis);
// Outputs decoded characters
if (cbConvert == 4)
{
// Covnert
if (cbScan++ < cbLineLength)
ConvBuffAppend((uchConvert[0] << 2) | (uchConvert[1] >> 4));
if (cbScan++ < cbLineLength)
ConvBuffAppend((uchConvert[1] << 4) | (uchConvert[2] >> 2));
if (cbScan++ < cbLineLength)
ConvBuffAppend((uchConvert[2] << 6) | (uchConvert[3]));
// Reset
cbConvert = 0;
}
}
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrEncodeQP
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrEncodeQP(void)
{
// Locals
HRESULT hr=S_OK;
UCHAR uchThis;
ULONG cbLine=0;
ULONG iCurrent;
LONG iLastWhite=-1;
LONG iLineWhite=-1;
UCHAR szLine[CCHMAX_QPLINE+30];
// Set iCurrent
iCurrent = m_rIn.i;
// Read lines and stuff dots
while (iCurrent < m_rIn.cb)
{
// Gets the next character
uchThis = m_rIn.pb[iCurrent];
// End of line...
if (chLF == uchThis || cbLine > CCHMAX_QPLINE)
{
// Soft Line break
if (chLF != uchThis)
{
// Lets back track to last white
if (iLastWhite != -1)
{
cbLine = iLineWhite + 1;
iCurrent = iLastWhite + 1;
}
// Hex encode the 8bit octet
Assert(cbLine + 3 <= sizeof(szLine));
szLine[cbLine++] = '=';
szLine[cbLine++] = chCR;
szLine[cbLine++] = chLF;
}
// Otherwise, we may need to encode the last space
else
{
// Encode Straggling '\n'
if (chCR != m_uchPrev)
{
Assert(cbLine + 4 <= sizeof(szLine));
szLine[cbLine++] = '=';
szLine[cbLine++] = g_rgchHex[uchThis >> 4];
szLine[cbLine++] = g_rgchHex[uchThis & 0x0F];
szLine[cbLine++] = '=';
}
// Detect preceding whitespace ...
if (cbLine && (' ' == szLine[cbLine - 1] || '\t' == szLine[cbLine - 1]))
{
// Hex encode the 8bit octet
UCHAR chWhite = szLine[cbLine - 1];
cbLine--;
Assert(cbLine + 3 <= sizeof(szLine));
szLine[cbLine++] = '=';
szLine[cbLine++] = g_rgchHex[chWhite >> 4];
szLine[cbLine++] = g_rgchHex[chWhite & 0x0F];
}
// Otherwise, hard line break
Assert(cbLine + 2 <= sizeof(szLine));
szLine[cbLine++] = chCR;
szLine[cbLine++] = chLF;
iCurrent++;
}
// Copy the line
CHECKHR(hr = HrConvBuffAppendBlock(szLine, cbLine));
// Reset
iLastWhite = -1;
iLineWhite = -1;
cbLine = 0;
*szLine = '\0';
// We processed this buffer
m_rIn.i = iCurrent;
}
// Encode empty '\r'
else if (chCR == uchThis)
{
// Overflow detection
if (iCurrent + 1 < m_rIn.cb && m_rIn.pb[iCurrent + 1] != chLF || iCurrent + 1 >= m_rIn.cb)
{
Assert(cbLine + 3 <= sizeof(szLine));
szLine[cbLine++] = '=';
szLine[cbLine++] = g_rgchHex[uchThis >> 4];
szLine[cbLine++] = g_rgchHex[uchThis & 0x0F];
}
// Next Character
iCurrent++;
}
// Rule #1: Replace 8-bit and equal signs
else if (('\t' != uchThis) && (uchThis < 32 || uchThis == 61 || uchThis > 126 || '=' == uchThis))
{
// Hex encode the 8bit octet
Assert(chLF != uchThis);
Assert(cbLine + 3 <= sizeof(szLine));
szLine[cbLine++] = '=';
szLine[cbLine++] = g_rgchHex[uchThis >> 4];
szLine[cbLine++] = g_rgchHex[uchThis & 0x0F];
iCurrent++;
}
// Otherwise, write the character
else
{
// Save position of last white space
if (' ' == uchThis || '\t' == uchThis)
{
iLastWhite = iCurrent;
iLineWhite = cbLine;
}
// Rule #2: Printable literals
Assert(cbLine + 1 <= sizeof(szLine));
szLine[cbLine++] = uchThis;
iCurrent++;
}
// Save Previous Char
m_uchPrev = uchThis;
}
// Last line
if (cbLine && m_fLastBuffer)
{
// Append the Line
CHECKHR(hr = HrConvBuffAppendBlock(szLine, cbLine));
// Set i
m_rIn.i = m_rIn.cb;
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrDecodeQP
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrDecodeQP(void)
{
// Locals
HRESULT hr=S_OK;
UCHAR uchThis;
UCHAR uchNext1;
UCHAR uchNext2;
UCHAR uch1;
UCHAR uch2;
// Read lines and stuff dots
while (FConvBuffCanRead(m_rIn))
{
// bug #35230 - display trash in trident
// Can I read 2 more characters
if (FALSE == m_fLastBuffer && m_rIn.i + 2 >= m_rIn.cb)
break;
// Do I need to grow
if (FGROWBUFFER(&m_rOut, 3))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(&m_rOut, 3));
}
// Gets the next character
uchThis = m_rIn.pb[m_rIn.i];
// Determine next couple of characers for end of line detection...
uchNext1 = (m_rIn.i + 1 < m_rIn.cb) ? m_rIn.pb[m_rIn.i + 1] : '\0';
uchNext2 = (m_rIn.i + 2 < m_rIn.cb) ? m_rIn.pb[m_rIn.i + 2] : '\0';
// Dont break on \r\n
if (chCR == uchNext1 && chLF == uchNext2 && m_rIn.i + 3 >= m_rIn.cb)
{
// If last buffer, then save characters
if (m_fLastBuffer)
{
// If not a soft line break
if ('=' != uchThis)
{
ConvBuffAppend(uchThis);
ConvBuffAppend(chCR);
ConvBuffAppend(chLF);
}
// Done
m_rIn.i += 3;
}
// Done
goto exit;
}
// If not end of line...
if ('=' == uchThis)
{
// Soft NL
if (chCR == uchNext1 && chLF == uchNext2)
{
// Step over =\r\n
m_rIn.i += 3;
}
// If not end of line...
else if (m_rIn.i + 2 < m_rIn.cb)
{
// Step Over Equal Sign
m_rIn.i++;
// Convert Hex Characters
uch1 = ChConvertFromHex(m_rIn.pb[m_rIn.i++]);
uch2 = ChConvertFromHex(m_rIn.pb[m_rIn.i++]);
// Store Hex characters
if (uch1 == 255 || uch2 == 255)
ConvBuffAppend('=');
else
ConvBuffAppend((uch1 << 4) | uch2);
}
else
{
// Last Buffer ?
ConvBuffAppend(uchThis);
m_rIn.i++;
}
}
// Otherwise store the character
else if (chCR == uchThis && chLF == uchNext1)
{
// Stuff CRLF
ConvBuffAppend(chCR);
ConvBuffAppend(chLF);
// Increment i
m_rIn.i += 2;
}
// Otherwise, store the character
else
{
ConvBuffAppend(uchThis);
m_rIn.i++;
}
// Set Previous
m_uchPrev = uchThis;
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrWrapInternetTextA
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrWrapInternetTextA(void)
{
// Locals
HRESULT hr=S_OK;
LONG cchLine;
LONG cchSkip;
// Read lines and stuff dots
while(FConvBuffCanRead(m_rIn))
{
// Not enough to encode a full line and not the last buffer
if ((FALSE == m_fLastBuffer) && ((LONG)(m_rIn.cb - m_rIn.i) < m_cchMaxLine))
goto exit;
// Call LineBreaker
if (*((CHAR*)(m_rIn.pb + m_rIn.i)) == '\0')
{
// This is to prevent the endless loop in case of malformed data stream
hr = TrapError(MIME_E_BAD_TEXT_DATA);
goto exit;
}
CHECKHR(hr = m_pLineBreak->BreakLineA(m_lcid, m_cpiSource, (LPCSTR)(m_rIn.pb + m_rIn.i), (m_rIn.cb - m_rIn.i), m_cchMaxLine, &cchLine, &cchSkip));
// Do I need to grow
if (FGROWBUFFER(&m_rOut, cchLine + 5))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(&m_rOut, cchLine + 5));
}
// Have some data ?
if (cchLine)
{
// Write the line
CHECKHR(hr = HrConvBuffAppendBlock(m_rIn.pb + m_rIn.i, cchLine));
}
// Write CRLF
Assert(m_rOut.cb + 2 < m_rOut.cbAlloc);
ConvBuffAppend(chCR);
ConvBuffAppend(chLF);
// Increment iText
m_rIn.i += (cchLine + cchSkip);
}
exit:
// Done
return hr;
}
HRESULT CInternetConverter::_GetEndOfURL(IN LPCWSTR pszLine, DWORD cchSize, DWORD * pdwMax)
{
HRESULT hr = S_OK;
DWORD cchCurrent = 0;
for (cchCurrent = 0; cchCurrent < cchSize; cchCurrent++)
{
if ((L' ' == pszLine[cchCurrent]) ||
(L'\r' == pszLine[cchCurrent]))
{
(*pdwMax) = (cchCurrent + 2);
break;
}
}
return hr;
}
HRESULT CInternetConverter::_FixLineBreakingProblems(
IN LCID locale, IN const WCHAR* pszSrc,
IN long cchSrc, IN long cMaxColumns,
OUT long* pcchLine, OUT long* pcchSkip,
BOOL * pfDoURLFix)
{
HRESULT hr = S_OK;
// There was a bug where a signature marker, which is '-- '
// (dash, dash, space, end-of-line) would get stripped out.
// This happens in ILineBreak::BreakLineW(). It will strip off
// the extra space thinking it is just extra white space.
if ((3 <= cchSrc) &&
(m_rIn.cb >= 10) &&
(2 <= *pcchLine) &&
(1 <= *pcchSkip) &&
(L'-' == pszSrc[*pcchLine - 2]) &&
(L'-' == pszSrc[*pcchLine - 1]) &&
(L' ' == pszSrc[*pcchLine - 0]))
{
(*pcchLine)++;
(*pcchSkip)--;
// DebugTrace("MimeOLE - Sig Delimiter: Preserved.\n");
}
// We do not want to wrap if that causes a break in URLs.
// This is bad because when the receiver's newsgroup reader
// turns the URL into a hyperlink, it will no longer point
// to the right location because part of the URL is missing.
// This happens often with urls that contain '=' or '/', like:
// http://www.amazon.com/exec/obidos/ASIN/B0000633EM/qid=1027792220/sr=8-3/ref=sr_8_3/104-5930498-2421552
// http://www.amazon.com/exec/obidos/tg/stores/detail/-/electronics/B0000633EM/reviews/ref=e_wlt1_de_a_er/104-5930498-2421552#
if ((5 <= *pcchLine) &&
(cchSrc > *pcchLine) && // Make sure this isn't the end of the line.
(L' ' != pszSrc[*pcchLine]) &&
(L'\r' != pszSrc[*pcchLine])) // We worry if the char after this break isn't a space.
{
WCHAR szUrl[50]; // We only need the first part.
WCHAR szScheme[30];
DWORD cchScheme = ARRAYSIZE(szScheme);
StrCpyNW(szUrl, pszSrc, (int) min(ARRAYSIZE(szUrl), *pcchLine));
HRESULT hrUrlPart = UrlGetPartW(szUrl, szScheme, &cchScheme, URL_PART_SCHEME, 0);
if ((S_OK == hrUrlPart) &&
szScheme[0] &&
(!StrCmpIW(szScheme, L"http") ||
!StrCmpIW(szScheme, L"https") ||
!StrCmpIW(szScheme, L"mailto") ||
!StrCmpIW(szScheme, L"file") ||
!StrCmpIW(szScheme, L"news") ||
!StrCmpIW(szScheme, L"nntp") ||
!StrCmpIW(szScheme, L"telnet") ||
!StrCmpIW(szScheme, L"ftp")))
{
*pfDoURLFix = TRUE;
}
}
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrWrapInternetTextW
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrWrapInternetTextW(void)
{
// Locals
HRESULT hr=S_OK;
LONG cchLine;
LONG cchSkip;
LPCWSTR pszNext = NULL;
BOOL fFollowingURLFix = FALSE;
// Invalid State
Assert(m_pLineBreak);
// Read lines and stuff dots
while(FConvBuffCanRead(m_rIn))
{
DWORD cchCurrentLineLen = ((m_rIn.cb - m_rIn.i) / sizeof(WCHAR));
pszNext = (LPCWSTR)(m_rIn.pb + m_rIn.i);
// Not enough to encode a full line and not the last buffer
if ((FALSE == m_fLastBuffer) && ((LONG)cchCurrentLineLen < m_cchMaxLine))
goto exit;
// Call LineBreaker
if (pszNext[0] == L'\0')
{
// This is to prevent the endless loop in case of malformed data stream
hr = TrapError(MIME_E_BAD_TEXT_DATA);
goto exit;
}
DWORD cchMax = m_cchMaxLine;
if (fFollowingURLFix)
{
_GetEndOfURL(pszNext, cchCurrentLineLen, &cchMax);
fFollowingURLFix = FALSE;
}
CHECKHR(hr = m_pLineBreak->BreakLineW(m_lcid, pszNext, cchCurrentLineLen, cchMax, &cchLine, &cchSkip));
BOOL fDoURLFix = FALSE;
_FixLineBreakingProblems(m_lcid, pszNext, cchCurrentLineLen, m_cchMaxLine, &cchLine, &cchSkip, &fDoURLFix);
// Do I need to grow
if (FGROWBUFFER(&m_rOut, ((cchLine + 5) * sizeof(WCHAR))))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(&m_rOut, ((cchLine + 5) * sizeof(WCHAR))));
}
// Have some data
if (cchLine)
{
// Write the line
CHECKHR(hr = HrConvBuffAppendBlock((BYTE *) pszNext, (cchLine * sizeof(WCHAR))));
}
// Write CRLF
Assert(m_rOut.cb + (2 * sizeof(WCHAR)) < m_rOut.cbAlloc);
if (!fDoURLFix)
{
ConvBuffAppendW(wchCR);
ConvBuffAppendW(wchLF);
}
else
{
fFollowingURLFix = TRUE;
}
// Increment iText
m_rIn.i += ((cchLine + cchSkip) * sizeof(WCHAR));
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrEncodeDecodeBinhex
// --------------------------------------------------------------------------------
const CHAR szBINHEXSTART[] = "(This file must be converted with BinHex";
const ULONG cbBINHEXSTART = ARRAYSIZE(szBINHEXSTART)-1;
HRESULT CInternetConverter::HrEncodeBinhex(void)
{
// Locals
HRESULT hr=S_OK;
HRESULT hrError;
ULONG cbLeft;
ULONG cbRead;
ULONG cbMaxEncode;
ULONG cbWrite;
// cbLeft
cbLeft = m_rIn.cb - m_rIn.i;
// cbMaxEncode - this should always insure enough room
cbMaxEncode = cbLeft * 2;
// Do I need to grow
if (FGROWBUFFER(&m_rOut, cbMaxEncode))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(&m_rOut, cbMaxEncode));
}
// Set max amount to read
cbRead = cbLeft;
// Set max amount to write
cbWrite = cbLeft;
// We better want to read some
Assert(cbRead && cbWrite);
// Encode/Decode some data
if (m_fEncoder)
{
// Encode
if (ERROR_SUCCESS != m_pBinhexEncode->HrEmit(m_rIn.pb + m_rIn.i, &cbRead, m_rOut.pb + m_rOut.cb, &cbWrite))
{
hr = TrapError(E_FAIL);
goto exit;
}
}
// Increment Amount Read
m_rIn.i += cbRead;
// Increment Amount Wrote
m_rOut.cb += cbWrite;
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrBinhexThrowAway
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrBinhexDecodeBuffAppend(UCHAR uchIn, ULONG cchIn, ULONG cchLeft, ULONG * pcbProduced)
{
HRESULT hr = S_FALSE;
ULONG cbPad = 0;
LPBYTE pbBinHex = NULL;
if (m_eBinHexStateDec == sHDRFILESIZE)
{
// First incoming character is always the size of the stream.
Assert(cchIn == 1);
if ((uchIn < 1) || (uchIn > 63))
{
hr = E_FAIL; // ERROR_INVALID_DATA
m_eBinHexStateDec = sENDED;
goto exit;
}
// Allocate the binhex header
if (FGROWBUFFER(&m_rBinhexHeader, cbMinBinHexHeader + uchIn))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(&m_rBinhexHeader, cbMinBinHexHeader + uchIn));
}
// Mark how many characters are left to process
m_cbToProcess = cbMinBinHexHeader + uchIn;
// Switch to filling the header sHEADER
m_prBinhexOutput = &m_rBinhexHeader;
m_eBinHexStateDec = sHEADER;
}
if (1 == cchIn)
{
m_prBinhexOutput->pb[m_prBinhexOutput->cb++] = uchIn;
}
else
{
// Check output buffer for space
if (FGROWBUFFER(m_prBinhexOutput, cchLeft + cchIn))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(m_prBinhexOutput, cchLeft + cchIn));
}
// Fill output buffer
FillMemory((m_prBinhexOutput->pb + m_prBinhexOutput->cb), cchIn, uchIn);
m_prBinhexOutput->cb += cchIn;
}
// Are we done processing this fork?
if (m_cbToProcess <= (LONG) cchIn)
{
switch (m_eBinHexStateDec)
{
case sHEADER:
// Verify that we have the correct CRC
m_wCRC = 0;
BinHexCalcCRC16((LPBYTE) m_rBinhexHeader.pb, cbMinBinHexHeader + *(m_rBinhexHeader.pb) - 2, &(m_wCRC));
BinHexCalcCRC16((LPBYTE) &wBinHexZero, sizeof(wBinHexZero), &(m_wCRC));
if ( HIBYTE( m_wCRC ) != m_rBinhexHeader.pb[cbMinBinHexHeader + *(m_rBinhexHeader.pb) - 2]
|| LOBYTE( m_wCRC ) != m_rBinhexHeader.pb[cbMinBinHexHeader + *(m_rBinhexHeader.pb) - 1] )
{
hr = E_FAIL; // ERROR_INVALID_DATA
goto exit;
}
m_wCRC = 0;
*pcbProduced = 0;
// Switch to using the correct buffer
m_prBinhexOutput = &m_rOut;
cchIn -= m_cbToProcess;
// Save off the size of the two forks
pbBinHex = m_rBinhexHeader.pb + m_rBinhexHeader.pb[0] + cbMinBinHexHeader - 10;
m_cbDataFork = NATIVE_LONG_FROM_BIG(pbBinHex);
m_cbResourceFork =NATIVE_LONG_FROM_BIG(pbBinHex + 4);
if (FALSE == m_fDataForkOnly)
{
// Copy extra data into new buffer
if (FGROWBUFFER(m_prBinhexOutput, cchLeft + cchIn + sizeof(MACBINARY)))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(m_prBinhexOutput, cchLeft + cchIn + sizeof(MACBINARY)));
}
// Write out the MacBinary header
CHECKHR(hr = HrCreateMacBinaryHeader(&m_rBinhexHeader, m_prBinhexOutput));
}
if (m_cbDataFork > 0)
{
// Fill output buffer
FillMemory((m_prBinhexOutput->pb + m_prBinhexOutput->cb), cchIn, uchIn);
m_prBinhexOutput->cb += cchIn;
// delete binhex header buffer
SafeMemFree(m_rBinhexHeader.pb);
ZeroMemory(&m_rBinhexHeader, sizeof(CONVERTBUFFER));
m_cbToProcess = m_cbDataFork;
// Switch to doing the data fork.
m_eBinHexStateDec = sDATA;
}
else
{
BinHexCalcCRC16((LPBYTE) &wBinHexZero, sizeof(wBinHexZero), &(m_wCRC));
// Save off the CRC until we can get the CRC from the fork.
m_wCRCForFork = m_wCRC;
m_prBinhexOutput = &m_rBinhexHeader;
// Remove the HEADER from the buffer
FillMemory(m_prBinhexOutput->pb, cchIn, uchIn);
m_prBinhexOutput->cb = cchIn;
// Switch to filling the data CRC
m_cbToProcess = 2;
m_eBinHexStateDec = sDATACRC;
}
break;
case sDATA:
// Verify that we have the correct CRC
BinHexCalcCRC16((LPBYTE) m_prBinhexOutput->pb + m_prBinhexOutput->cb - cchIn - *pcbProduced,
m_cbToProcess + *pcbProduced, &(m_wCRC));
BinHexCalcCRC16((LPBYTE) &wBinHexZero, sizeof(wBinHexZero), &(m_wCRC));
// Save off the CRC until we can get the CRC from the fork.
m_wCRCForFork = m_wCRC;
m_wCRC = 0;
*pcbProduced = 0;
cchIn -= m_cbToProcess;
// Switch to the proper buffer for CRC calculations
if (FGROWBUFFER(&m_rBinhexHeader, cchLeft + cchIn))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(&m_rBinhexHeader, cchLeft + cchIn));
}
// Move any current bytes so we don't overwrite anything
CopyMemory((m_rBinhexHeader.pb + m_rBinhexHeader.cb),
(m_prBinhexOutput->pb + m_prBinhexOutput->cb), cchIn);
m_rBinhexHeader.cb += cchIn;
// We only need to pad for a real Mac file...
if (FALSE == m_fDataForkOnly)
{
// Check to see if the size of the fork is a multiple of 128?
cbPad = 128 - (m_cbDataFork % 128);
if (cbPad != 0)
{
uchIn = '\0';
// Check output buffer for space
if (FGROWBUFFER(m_prBinhexOutput, cchLeft + cbPad - cchIn))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(m_prBinhexOutput, cchLeft + cbPad - cchIn));
}
// Fill output buffer
FillMemory((m_prBinhexOutput->pb + m_prBinhexOutput->cb - cchIn), cbPad, uchIn);
m_prBinhexOutput->cb += cbPad - cchIn;
}
}
// Switch to filling the data fork CRC
m_prBinhexOutput = &m_rBinhexHeader;
m_cbToProcess = 2;
m_eBinHexStateDec = sDATACRC;
break;
case sDATACRC:
if ( HIBYTE( m_wCRCForFork ) != m_prBinhexOutput->pb[0]
|| LOBYTE( m_wCRCForFork ) != m_prBinhexOutput->pb[1] )
{
hr = E_FAIL; // ERROR_INVALID_DATA
goto exit;
}
m_wCRC = 0;
cchIn -= m_cbToProcess;
*pcbProduced = 0;
if (m_cbResourceFork > 0)
{
m_prBinhexOutput = &m_rOut;
// Switch to the proper buffer for CRC calculations
if (FGROWBUFFER(m_prBinhexOutput, cchLeft + cchIn))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(m_prBinhexOutput, cchLeft + cchIn));
}
// Move any current bytes so we don't overwrite anything
CopyMemory((m_prBinhexOutput->pb + m_prBinhexOutput->cb),
(m_rBinhexHeader.pb + m_rBinhexHeader.cb), cchIn);
m_prBinhexOutput->cb += cchIn;
// delete binhex header buffer
SafeMemFree(m_rBinhexHeader.pb);
ZeroMemory(&m_rBinhexHeader, sizeof(CONVERTBUFFER));
// Switch to filling the resource fork
if (FALSE == m_fDataForkOnly)
{
m_cbToProcess = m_cbResourceFork;
m_eBinHexStateDec = sRESOURCE;
}
else
{
m_cbToProcess = 0x0;
m_eBinHexStateDec = sENDING;
}
}
else
{
// Set the CRC for the data fork.
BinHexCalcCRC16((LPBYTE) &wBinHexZero, sizeof(wBinHexZero), &(m_wCRC));
// Save off the CRC until we can get the CRC from the fork.
m_wCRCForFork = m_wCRC;
// Remove the DATA CRC from the buffer
MoveMemory(m_prBinhexOutput->pb, m_prBinhexOutput->pb + 2, m_prBinhexOutput->cb - 2);
m_prBinhexOutput->cb -= 2;
// Switch to filling the resource CRC
m_cbToProcess = 2;
m_eBinHexStateDec = sRESOURCECRC;
}
break;
case sRESOURCE:
// Verify that we have the correct CRC
BinHexCalcCRC16((LPBYTE) m_prBinhexOutput->pb + m_prBinhexOutput->cb - cchIn - *pcbProduced,
m_cbToProcess + *pcbProduced, &(m_wCRC));
BinHexCalcCRC16((LPBYTE) &wBinHexZero, sizeof(wBinHexZero), &(m_wCRC));
// Save off the CRC until we can get the CRC from the fork.
m_wCRCForFork = m_wCRC;
m_wCRC = 0;
*pcbProduced = 0;
cchIn -= m_cbToProcess;
// Switch to the proper buffer for CRC calculations
if (FGROWBUFFER(&m_rBinhexHeader, cchLeft + cchIn))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(&m_rBinhexHeader, cchLeft + cchIn));
}
// Move any current bytes so we don't overwrite anything
CopyMemory((m_rBinhexHeader.pb + m_rBinhexHeader.cb),
(m_prBinhexOutput->pb + m_prBinhexOutput->cb), cchIn);
m_rBinhexHeader.cb += cchIn;
// Check to see if the size of the fork is a multiple of 128?
cbPad = 128 - (m_cbResourceFork % 128);
if (cbPad != 0)
{
uchIn = '\0';
// Check output buffer for space
if (FGROWBUFFER(m_prBinhexOutput, cchLeft + cbPad - cchIn))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(m_prBinhexOutput, cchLeft + cbPad - cchIn));
}
// Fill output buffer
FillMemory((m_prBinhexOutput->pb + m_prBinhexOutput->cb - cchIn), cbPad, uchIn);
m_prBinhexOutput->cb += cbPad - cchIn;
}
// Switch to filling the resource fork CRC
m_prBinhexOutput = &m_rBinhexHeader;
m_cbToProcess = 2;
m_eBinHexStateDec = sRESOURCECRC;
break;
case sRESOURCECRC:
if ( HIBYTE( m_wCRCForFork ) != m_prBinhexOutput->pb[0]
|| LOBYTE( m_wCRCForFork ) != m_prBinhexOutput->pb[1] )
{
hr = E_FAIL; // ERROR_INVALID_DATA
goto exit;
}
m_wCRC = 0;
cchIn -= m_cbToProcess;
m_prBinhexOutput = &m_rOut;
*pcbProduced = 0;
// Switch to the proper buffer for CRC calculations
if (FGROWBUFFER(m_prBinhexOutput, cchLeft + cchIn))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(m_prBinhexOutput, cchLeft + cchIn));
}
// Move any current bytes so we don't overwrite anything
CopyMemory((m_prBinhexOutput->pb + m_prBinhexOutput->cb),
(m_rBinhexHeader.pb + m_rBinhexHeader.cb), cchIn);
m_prBinhexOutput->cb += cchIn;
// delete binhex header buffer
SafeMemFree(m_rBinhexHeader.pb);
ZeroMemory(&m_rBinhexHeader, sizeof(CONVERTBUFFER));
// Switch to filling the resource fork
m_cbToProcess = 0x0;
m_eBinHexStateDec = sENDING;
break;
default:
Assert(FALSE);
break;
}
}
m_cbToProcess -= cchIn;
*pcbProduced += cchIn;
hr = S_OK;
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrBinhexThrowAway
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrBinhexThrowAway(LPSTR pszLine, ULONG cbLine)
{
HRESULT hr = S_FALSE;
if (m_eBinHexStateDec == sSTARTING)
{
// Ingore all lines before we start that only have whitespace characters
// or the start tag.
hr = S_OK;
for (LPSTR pszEnd = pszLine + cbLine; pszLine < pszEnd; pszLine++)
{
if (!FBINHEXRETURN(*pszLine))
{
// Need to ignore lines that start with the tag
if (((ULONG)(pszEnd - pszLine) >= cbBINHEXSTART) && (StrCmpNI(szBINHEXSTART, pszLine, cbBINHEXSTART) == 0))
{
m_eBinHexStateDec = sSTARTED;
break;
}
// We must have gotten bad data
hr = E_FAIL; // ERROR_INVALID_DATA
m_eBinHexStateDec = sENDED;
goto exit;
}
}
}
else if (m_eBinHexStateDec == sENDED)
{
// We can ignore any lines after we are done.
hr = S_OK;
}
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CInternetConverter::HrDecodeBinHex
// --------------------------------------------------------------------------------
HRESULT CInternetConverter::HrDecodeBinHex(void)
{
// Locals
HRESULT hr=S_OK;
ULONG cbLine;
LPSTR pszLine;
ULONG cbRead=0;
ULONG cbLineLength;
BOOL fFound;
ULONG cbConvert;
ULONG cbScan;
ULONG i;
UCHAR uchConvert[4];
UCHAR uchThis;
UCHAR uchDecoded;
UCHAR cuchWrite;
UCHAR rgbShift[] = {0, 4, 2, 0};
ULONG cbProduced = 0;
// Read lines and stuff dots
while(1)
{
// Increment Index
m_rIn.i += cbRead;
// Get Next Line
pszLine = PszConvBuffGetNextLine(&cbLine, &cbRead, &fFound);
if (0 == cbRead || (FALSE == fFound && FALSE == m_fLastBuffer))
{
goto exit;
}
// UUENCODE ThrowAway
hr = HrBinhexThrowAway(pszLine, cbLine);
if (FAILED(hr))
{
goto exit;
}
else if (S_OK == hr)
{
continue;
}
hr = S_OK;
// Do I need to grow
if (FGROWBUFFER(m_prBinhexOutput, cbLine + 20))
{
// Grow the buffer
CHECKHR(hr = HrGrowBuffer(m_prBinhexOutput, cbLine + 20));
}
AssertSz((m_eBinHexStateDec != sSTARTING) && (m_eBinHexStateDec != sENDED),
"Why haven't we found the start of the stream yet??\n");
// Decodes characters in line buffer
for (i=0; i<cbLine; i++)
{
uchThis = pszLine[i];
// Check for valid white space
if (FBINHEXRETURN(uchThis))
continue;
// Check for start or end of stream
if (BINHEX_TERM == uchThis)
{
if (m_eBinHexStateDec == sSTARTED)
{
m_eBinHexStateDec = sHDRFILESIZE;
continue;
}
else if (m_eBinHexStateDec == sENDING)
{
m_eBinHexStateDec = sENDED;
break;
}
}
if (m_eBinHexStateDec == sENDING)
{
if (('!' == uchThis) || (TRUE == m_fDataForkOnly))
{
continue;
}
else
{
// ensure that we're not in an invalid state. If we made it to sENDING and we got
// valid CRCs and everything is hunky dory, just ignore the terminating stuff.
continue;
}
}
// Decode It
uchDecoded = DECODEBINHEX(uchThis);
// Test for valid char
if (uchDecoded == BINHEX_INVALID)
{
hr = E_FAIL; // ERROR_INVALID_DATA
goto exit;
}
if ( m_cAccum == 0 )
{
m_ulAccum = uchDecoded;
++m_cAccum;
continue;
}
else
{
m_ulAccum = ( m_ulAccum << 6 ) | uchDecoded;
uchDecoded = (BYTE)(m_ulAccum >> rgbShift[m_cAccum]) & 0xff;
m_cAccum++;
m_cAccum %= sizeof(m_ulAccum);
}
// If we are repeating then fill the buffer with char
if (m_fRepeating)
{
m_fRepeating = FALSE;
// Check to see if it's just a literal 0x90
if (0x00 == uchDecoded)
{
// Just write out one BINHEX_REPEAT char
m_uchPrev = BINHEX_REPEAT;
cuchWrite = 1;
}
else
{
cuchWrite = uchDecoded - 1;
}
}
// Check for repeat character
else if (BINHEX_REPEAT == uchDecoded)
{
m_fRepeating = TRUE;
continue;
}
// Else it's just a normal character.
else
{
m_uchPrev = uchDecoded;
cuchWrite = 1;
}
CHECKHR(HrBinhexDecodeBuffAppend(m_uchPrev, cuchWrite, cbLine - i, &cbProduced));
}
BinHexCalcCRC16((LPBYTE) m_prBinhexOutput->pb + m_prBinhexOutput->cb - cbProduced, cbProduced, &(m_wCRC));
cbProduced = 0;
}
hr = S_OK;
exit:
// Done
return hr;
}