|
|
// --------------------------------------------------------------------------------
// InetStm.cpp
// Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
// Steven J. Bailey
// --------------------------------------------------------------------------------
#include "pch.hxx"
#include "inetstm.h"
#include "stmlock.h"
#include "shlwapi.h"
#include "demand.h"
// --------------------------------------------------------------------------------
// INETSTMTRACING
// --------------------------------------------------------------------------------
//#define INETSTMTRACING 1
#ifdef INETSTMTRACING
#define INETSTMTRACE DebugTrace
#else
#define INETSTMTRACE
#endif
// --------------------------------------------------------------------------------
// CInternetStream::CInternetStream
// --------------------------------------------------------------------------------
CInternetStream::CInternetStream(void) { m_cRef = 1; m_pStmLock = NULL; m_fFullyAvailable = TRUE; ULISet32(m_uliOffset, 0); ZeroMemory(&m_rLine, sizeof(INETSTREAMLINE)); m_rLine.pb = m_rLine.rgbScratch; m_rLine.cbAlloc = sizeof(m_rLine.rgbScratch); }
// --------------------------------------------------------------------------------
// CInternetStream::~CInternetStream
// --------------------------------------------------------------------------------
CInternetStream::~CInternetStream(void) { // Do i need to free the line
if (m_rLine.pb && m_rLine.pb != m_rLine.rgbScratch) g_pMalloc->Free(m_rLine.pb);
// Reset the position of the stream to the real current offset
if (m_pStmLock) { // Preserve the position of the stream
SideAssert(SUCCEEDED(m_pStmLock->HrSetPosition(m_uliOffset)));
// Release the LockBytes
m_pStmLock->Release(); } }
// --------------------------------------------------------------------------------
// CInternetStream::AddRef
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CInternetStream::AddRef(void) { return ++m_cRef; }
// --------------------------------------------------------------------------------
// CInternetStream::Release
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CInternetStream::Release(void) { if (0 != --m_cRef) return m_cRef; delete this; return 0; }
// --------------------------------------------------------------------------------
// CInternetStream::HrInitNew
// --------------------------------------------------------------------------------
HRESULT CInternetStream::HrInitNew(IStream *pStream) { // Locals
HRESULT hr=S_OK; CStreamLockBytes *pStmLock=NULL; DWORD cbOffset;
// Invalid Arg
Assert(pStream);
// Wrap pStream in a pStmLock
CHECKALLOC(pStmLock = new CStreamLockBytes(pStream));
// Get Current Stream Position
CHECKHR(hr = HrGetStreamPos(pStream, &cbOffset));
// Create text stream object
InitNew(cbOffset, pStmLock);
exit: // Cleanup
SafeRelease(pStmLock);
// Done
return hr; }
// --------------------------------------------------------------------------------
// CInternetStream::InitNew
// --------------------------------------------------------------------------------
void CInternetStream::InitNew(DWORD dwOffset, CStreamLockBytes *pStmLock) { // Invalid Arg
Assert(pStmLock);
// Release Current
SafeRelease(m_pStmLock);
// Zero Current Buffer
ZeroMemory(&m_rBuffer, sizeof(INETSTREAMBUFFER));
// Reset m_rLine
m_rLine.cb = 0;
// Assume new StreamLockBytes
m_pStmLock = pStmLock; m_pStmLock->AddRef();
// Safe the Offset
m_uliOffset.QuadPart = dwOffset; }
// --------------------------------------------------------------------------------
// CInternetStream::GetLockBytes
// --------------------------------------------------------------------------------
void CInternetStream::GetLockBytes(CStreamLockBytes **ppStmLock) { // Invalid Arg
Assert(ppStmLock && m_pStmLock);
// Return It
(*ppStmLock) = m_pStmLock; (*ppStmLock)->AddRef(); }
// --------------------------------------------------------------------------------
// CInternetStream::HrGetSize
// --------------------------------------------------------------------------------
HRESULT CInternetStream::HrGetSize(DWORD *pcbSize) { // Locals
HRESULT hr=S_OK; STATSTG rStat;
// Invalid Arg
Assert(pcbSize && m_pStmLock);
// Get the Stat
CHECKHR(hr = m_pStmLock->Stat(&rStat, STATFLAG_NONAME));
// Return Size
*pcbSize = (DWORD)rStat.cbSize.QuadPart;
exit: // Done
return hr; }
// --------------------------------------------------------------------------------
// CInternetStream::Seek
// --------------------------------------------------------------------------------
void CInternetStream::Seek(DWORD dwOffset) { // Locals
HRESULT hr=S_OK; BOOL fResetCache=FALSE; DWORD dw;
// State Check
Assert((m_rBuffer.cb == 0) || (m_uliOffset.QuadPart == m_rBuffer.uliOffset.QuadPart + m_rBuffer.i));
// Already at the requested position
if (dwOffset == m_uliOffset.QuadPart) goto exit;
// Less than current position
if (dwOffset < m_uliOffset.QuadPart) { // Compute Offset from current location
dw = (DWORD)m_uliOffset.QuadPart - dwOffset;
// Less than beginning
if (dw > m_rBuffer.i) fResetCache = TRUE; else { Assert(dw <= m_rBuffer.i); m_rBuffer.i -= dw; } }
// Else dwOffset > m_uliOffset.QuadPart
else { // Compute Offset from current location
dw = dwOffset - (DWORD)m_uliOffset.QuadPart;
// Less than beginning
if (m_rBuffer.i + dw > m_rBuffer.cb) fResetCache = TRUE; else { m_rBuffer.i += dw; Assert(m_rBuffer.i <= m_rBuffer.cb); } }
// Reset the cache
if (fResetCache) { // Empty current line and buffer
*m_rLine.pb = *m_rBuffer.rgb = '\0';
// No buffer
m_rBuffer.uliOffset.QuadPart = m_rLine.cb = m_rBuffer.i = m_rBuffer.cb = 0; }
// Save this position
m_uliOffset.QuadPart = dwOffset;
exit: // Done
return; }
// --------------------------------------------------------------------------------
// CInternetStream::HrReadToEnd
// --------------------------------------------------------------------------------
HRESULT CInternetStream::HrReadToEnd(void) { // Locals
HRESULT hr=S_OK;
// While
while(1) { // Validate
Assert(m_rBuffer.i <= m_rBuffer.cb);
// Increment Offset to end of current buffer
m_uliOffset.QuadPart += (m_rBuffer.cb - m_rBuffer.i);
// Set m_rBuffer.i to end of current buffer
m_rBuffer.i = m_rBuffer.cb; // Get next buffer
CHECKHR(hr = _HrGetNextBuffer());
// No more data
if (0 == m_rBuffer.cb) break; }
exit: // Done
return hr; }
// --------------------------------------------------------------------------------
// CInternetStream::_HrGetNextBuffer
// --------------------------------------------------------------------------------
HRESULT CInternetStream::_HrGetNextBuffer(void) { // Locals
HRESULT hr=S_OK; ULONG cbRead;
// Validate
Assert(m_rBuffer.i <= m_rBuffer.cb);
// Do we need to read a new buffer
if (m_rBuffer.i == m_rBuffer.cb) { // Read a block from the stream, this could return E_PENDING
CHECKHR(hr = m_pStmLock->ReadAt(m_uliOffset, m_rBuffer.rgb, sizeof(m_rBuffer.rgb), &cbRead));
// Raid 43408: Work around Urlmon IStream::Read returning S_FALSE when it should return E_PENDING
#ifdef DEBUG
if (FALSE == m_fFullyAvailable && 0 == cbRead && S_FALSE == hr) { //AssertSz(FALSE, "Raid-43408 - Danpo Zhang is working on this bug, I hope.");
//hr = E_PENDING;
//goto exit;
} #endif
// Save cbRead
m_rBuffer.cb = cbRead;
// Save the offset of the start of this buffer
m_rBuffer.uliOffset.QuadPart = m_uliOffset.QuadPart;
// Reset buffer index
m_rBuffer.i = 0; } else Assert(m_uliOffset.QuadPart == m_rBuffer.uliOffset.QuadPart + m_rBuffer.i);
exit: // Done
return hr; }
// --------------------------------------------------------------------------------
// CInternetStream::HrReadLine
// --------------------------------------------------------------------------------
HRESULT CInternetStream::HrReadLine(LPPROPSTRINGA pLine) { // Locals
HRESULT hr=S_OK; UCHAR ch, chEndOfLine; ULONG cbRead, iStart; BOOL fEndOfLine=FALSE;
// Init
pLine->pszVal = NULL; pLine->cchVal = 0;
// Reset the line
if (m_rLine.fReset) { m_rLine.cb = 0; m_rLine.fReset = 0; }
// Do the loop
while(1) { // Get next buffer
CHECKHR(hr = _HrGetNextBuffer());
// Nothing Read ?
if (m_rBuffer.cb == 0) break;
// Seek to first '\n'
iStart = m_rBuffer.i;
Assert(chLF<32); Assert(chCR<32); Assert(0<32);
// For large messages, the while-loop below ends up being a big
// percentage of the execution time for IMimeMessage::Load. So,
// we have carefully crafted our C++ code so that we get the
// optimum code generation for this loop (at least, on Intel, with
// VC 11.00.7071...)
{ register UCHAR *pCurr = m_rBuffer.rgb + m_rBuffer.i; register UCHAR *pEnd = m_rBuffer.rgb + m_rBuffer.cb;
// We need to initialize this variable for two reasons: First,
// if we don't initialize it, then for some reason VC decides that
// it doesn't want to enregister it. And more importantly, if
// we don't enter the while-loop at all, we need this variable
// to be set this way...
register UCHAR chPrev = m_rBuffer.chPrev;
ch = m_rBuffer.chPrev;
// While we have data
while (pCurr < pEnd) {
// Remember the previous character.
chPrev = ch;
// Get Character, and Increment
ch = *pCurr; pCurr++;
// The most common case - it's just a regular
// character, and we haven't seen a carriage-return.
// So jump back to the top of the loop and keep lookin'...
if ((chCR != chPrev) && (ch >= 32)) { continue; }
// The next most common case - we are at end-of-line because
// of a line-feed.
if (chLF == ch) { chPrev = ch; chEndOfLine = ch; fEndOfLine = TRUE; break; }
// This case really only happens when we are getting malformed
// e-mail - there are embedded carriage-returns, which are *not*
// followed by line-feeds. When we see those lonely CR's,
// we make it look like we got a normal CR LF sequence.
if (chCR == chPrev) { chPrev = chLF; pCurr--; chEndOfLine = chCR; fEndOfLine = TRUE; break; }
// Fairly rare case - these are malformed messages because they
// have NULL's embedded in them. We silently convert the
// NULL's to dots.
if ('\0' == ch) { ch = '.'; *(pCurr-1) = ch; } }
m_rBuffer.i = (ULONG) (pCurr - m_rBuffer.rgb); m_rBuffer.chPrev = chPrev; }
// Number of bytes Read
cbRead = (m_rBuffer.i - iStart);
// Increment Position
m_uliOffset.QuadPart += cbRead;
// Do we need to realloc the line buffer ?
if (m_rLine.cb + cbRead + 2 > m_rLine.cbAlloc) { // Fixup pszLine
if (m_rLine.pb == m_rLine.rgbScratch) { // Null It
m_rLine.pb = NULL;
// Allocate it to m_rLine.cb
CHECKHR(hr = HrAlloc((LPVOID *)&m_rLine.pb, m_rLine.cb + 1));
// Copy static buffer
CopyMemory(m_rLine.pb, m_rLine.rgbScratch, m_rLine.cb); }
// Always Add a little extra to reduce the number of allocs
m_rLine.cbAlloc = m_rLine.cb + cbRead + 256;
// Realloc or alloc new
CHECKHR(hr = HrRealloc((LPVOID *)&m_rLine.pb, m_rLine.cbAlloc)); }
// Copy the data
CopyMemory(m_rLine.pb + m_rLine.cb, m_rBuffer.rgb + iStart, cbRead);
// Update Counters and indexes
m_rLine.cb += cbRead;
// If End of line and last character was a '\r', append a '\n'
if (TRUE == fEndOfLine) { // Better have something in the line
Assert(m_rLine.cb);
// If line ended with a '\r'
if (chCR == chEndOfLine) { // Better have room for one more char
Assert(m_rLine.cb + 1 < m_rLine.cbAlloc);
// Append a '\n'
m_rLine.pb[m_rLine.cb] = chLF;
// Increment Length
m_rLine.cb++; }
// Otherwise...
else { // Line better have ended with a \n
Assert(chLF == chEndOfLine && chLF == m_rLine.pb[m_rLine.cb - 1]);
// If Previous Character was not a \r
if (m_rLine.cb < 2 || chCR != m_rLine.pb[m_rLine.cb - 2]) { // Convert last char from \n to a \r
m_rLine.pb[m_rLine.cb - 1] = chCR;
// Better have room for one more char
Assert(m_rLine.cb + 1 < m_rLine.cbAlloc);
// Append a '\n'
m_rLine.pb[m_rLine.cb] = chLF;
// Increment Length
m_rLine.cb++; } }
// Done
break; } }
// A little check
Assert(fEndOfLine ? m_rLine.cb >= 2 && chLF == m_rLine.pb[m_rLine.cb-1] && chCR == m_rLine.pb[m_rLine.cb-2] : TRUE);
// Null terminator
m_rLine.pb[m_rLine.cb] = '\0';
// Set return values
pLine->pszVal = (LPSTR)m_rLine.pb; pLine->cchVal = m_rLine.cb;
// Tracking
INETSTMTRACE("CInternetStream: %s", (LPSTR)m_rLine.pb);
// No Line
m_rLine.fReset = TRUE;
exit: // Done
return hr; }
// --------------------------------------------------------------------------------
// CInternetStream::HrReadHeaderLine
// --------------------------------------------------------------------------------
HRESULT CInternetStream::HrReadHeaderLine(LPPROPSTRINGA pLine, LONG *piColonPos) { // Locals
HRESULT hr=S_OK; CHAR ch; ULONG cbRead=0, iStart, i; BOOL fEndOfLine=FALSE; DWORD cTrailingSpace=0;
// Init
*piColonPos = -1; pLine->pszVal = NULL; pLine->cchVal = 0;
// Reset the line
if (m_rLine.fReset) { m_rLine.cb = 0; m_rLine.fReset = 0; }
// Do the loop
while(1) { // Get next buffer
CHECKHR(hr = _HrGetNextBuffer());
// Nothing Read ?
if (m_rBuffer.cb == 0) break;
// Reset fSeenN
fEndOfLine = FALSE;
// Initialize
iStart = m_rBuffer.i;
// Seek to first '\n'
while (m_rBuffer.i < m_rBuffer.cb) { // Get Character
ch = *(m_rBuffer.rgb + m_rBuffer.i);
// Convert Nulls to '.'
if ('\0' == ch) { ch = '.'; *(m_rBuffer.rgb + m_rBuffer.i) = ch; }
// Goto next character
m_rBuffer.i++;
// New Line
if (chLF == ch) { m_rBuffer.chPrev = ch; fEndOfLine = TRUE; break; }
// Otherwise, if previous character was a '\r', then end of line
else if (chCR == m_rBuffer.chPrev) { AssertSz(m_rBuffer.i > 0, "This is an un-handled boundary condition"); if (m_rBuffer.i > 0) m_rBuffer.i--; m_rBuffer.chPrev = '\0'; fEndOfLine = TRUE; break; }
// Is Space
if (' ' == ch || '\t' == ch) cTrailingSpace++; else cTrailingSpace = 0;
// Save Previous Character
m_rBuffer.chPrev = ch; }
// Number of bytes Read
cbRead = (m_rBuffer.i - iStart);
// Increment Position
m_uliOffset.QuadPart += cbRead;
// Adjust cbRead to remove CRLF
if (cbRead && chLF == m_rBuffer.rgb[iStart + cbRead - 1]) cbRead--; if (cbRead && chCR == m_rBuffer.rgb[iStart + cbRead - 1]) cbRead--;
// Do we need to realloc the line buffer ?
if (m_rLine.cb + cbRead + 3 > m_rLine.cbAlloc) { // Fixup pszLine
if (m_rLine.pb == m_rLine.rgbScratch) { // Null It
m_rLine.pb = NULL;
// Allocate it to m_rLine.cb
CHECKHR(hr = HrAlloc((LPVOID *)&m_rLine.pb, m_rLine.cb + 3));
// Copy static buffer
CopyMemory(m_rLine.pb, m_rLine.rgbScratch, m_rLine.cb); }
// Always Add a little extra to reduce the number of allocs
m_rLine.cbAlloc = m_rLine.cb + cbRead + 256;
// Realloc or alloc new
CHECKHR(hr = HrRealloc((LPVOID *)&m_rLine.pb, m_rLine.cbAlloc)); }
// Copy the data
CopyMemory(m_rLine.pb + m_rLine.cb, m_rBuffer.rgb + iStart, cbRead);
// Increment line byte count
m_rLine.cb += cbRead;
// If fSeenN, then check for continuation line (i.e. next character is ' ' or '\t'
if (fEndOfLine) { // Get next buffer
CHECKHR(hr = _HrGetNextBuffer());
// Compare for continuation
ch = m_rBuffer.rgb[m_rBuffer.i];
// If line starts with a TAB or a space, this is a continuation line, keep reading
if ((ch != ' ' && ch != '\t') || (0 == cbRead && 0 == m_rLine.cb)) { // Done
break; }
// Otherwise, strip continuation...
else { // Per RFC822, we should not step over a space
if (ch == '\t') { m_rBuffer.i++; m_uliOffset.QuadPart++; }
// No characters since last whitespace
if (0 == cTrailingSpace) { // Locals
DWORD cFrontSpace=0;
// Look ahead in the buffer a little
for (DWORD iLookAhead = m_rBuffer.i; iLookAhead < m_rBuffer.cb; iLookAhead++) { // Get Char
ch = m_rBuffer.rgb[iLookAhead];
// Break on non space
if (' ' != ch && '\t' != ch) break;
// Count Front Space
cFrontSpace++; }
// No Front Space ?
if (0 == cFrontSpace) { // Lets do this only fro Received: and for Date: since dates that get split don't get parsed correctly?
if ((m_rLine.cb >= 4 && 0 == StrCmpNI("Date", (LPCSTR)m_rLine.pb, 4)) || (m_rLine.cb >= 8 && 0 == StrCmpNI("Received", (LPCSTR)m_rLine.pb, 8))) { // Put a space in
*(m_rLine.pb + m_rLine.cb) = ' ';
// Increment line byte count
m_rLine.cb += 1; } } }
// Get next buffer
CHECKHR(hr = _HrGetNextBuffer());
// If Next character is a \r or \n, then stop, NETSCAPE bug
ch = m_rBuffer.rgb[m_rBuffer.i]; if (chCR == ch || chLF == ch) break; }
// Reset
cTrailingSpace = 0; } }
// A little check
#ifndef _WIN64
Assert(chLF != m_rLine.pb[m_rLine.cb-1] && chCR != m_rLine.pb[m_rLine.cb-1]); #endif
// Null terminator
*(m_rLine.pb + m_rLine.cb) = '\0';
// Lets locate the colon
for (i=0; i<m_rLine.cb; i++) { // Colon ?
if (':' == m_rLine.pb[i]) { *piColonPos = i; break; } }
// Set return values
pLine->pszVal = (LPSTR)m_rLine.pb; pLine->cchVal = m_rLine.cb;
// Tracking
INETSTMTRACE("CInternetStream: %s\n", (LPSTR)m_rLine.pb);
// Reset the Line
m_rLine.fReset = TRUE;
exit: // Done
return hr; }
|