You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
468 lines
12 KiB
468 lines
12 KiB
/*
|
|
* p l a i n s t m . c p p
|
|
*
|
|
* Purpose:
|
|
* IStream implementation that wraps a plain stream as html and does URL detection
|
|
*
|
|
* History
|
|
* September '96: brettm - created
|
|
*
|
|
* Copyright (C) Microsoft Corp. 1995, 1996.
|
|
*/
|
|
|
|
#include <pch.hxx>
|
|
#include "dllmain.h"
|
|
#include "strconst.h"
|
|
#include "plainstm.h"
|
|
#include "triutil.h"
|
|
#include "oleutil.h"
|
|
#include "demand.h"
|
|
|
|
ASSERTDATA
|
|
|
|
/*
|
|
* m a c r o s
|
|
*/
|
|
|
|
/*
|
|
* t y p e d e f s
|
|
*/
|
|
|
|
/*
|
|
* c o n s t a n t s
|
|
*/
|
|
#define MAX_URL_SIZE 8+1
|
|
|
|
#define WCHAR_BYTES(_lpsz) (sizeof(_lpsz) - sizeof(WCHAR))
|
|
#define WCHAR_CCH(_lpsz) (WCHAR_BYTES(_lpsz)/sizeof(WCHAR))
|
|
|
|
static const WCHAR c_szHtmlNonBreakingSpaceW[] =L" ",
|
|
c_szHtmlBreakW[] =L"<BR>\r\n",
|
|
c_szSpaceW[] =L" ",
|
|
c_szEscGreaterThanW[] =L">",
|
|
c_szEscLessThanW[] =L"<",
|
|
c_szEscQuoteW[] =L""",
|
|
c_szEscAmpersandW[] =L"&";
|
|
|
|
#define chSpace ' '
|
|
#define chCR '\r'
|
|
#define chLF '\n'
|
|
#define chQuoteChar '\"'
|
|
#define chLessThan '<'
|
|
#define chGreaterThan '>'
|
|
#define chAmpersand '&'
|
|
|
|
#define IsSpecialChar(_ch) ( _ch == chLessThan || _ch == chSpace || _ch == chCR || _ch == chLF || _ch == chQuoteChar || _ch == chLessThan || _ch == chGreaterThan || _ch == chAmpersand )
|
|
|
|
/*
|
|
* g l o b a l s
|
|
*/
|
|
enum
|
|
{
|
|
escInvalid=-1,
|
|
escGreaterThan=0,
|
|
escLessThan,
|
|
escAmpersand,
|
|
escQuote
|
|
};
|
|
|
|
/*
|
|
* f u n c t i o n p r o t y p e s
|
|
*/
|
|
|
|
|
|
/*
|
|
* f u n c t i o n s
|
|
*/
|
|
|
|
HRESULT HrConvertPlainStreamW(LPSTREAM pstm, WCHAR chQuoteW, LPSTREAM *ppstmHtml)
|
|
{
|
|
LPPLAINCONVERTER pPlainConv=0;
|
|
HRESULT hr;
|
|
|
|
if (!(pPlainConv=new CPlainConverter()))
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr=pPlainConv->HrConvert(pstm, chQuoteW, ppstmHtml);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
HrRewindStream(*ppstmHtml);
|
|
|
|
error:
|
|
ReleaseObj(pPlainConv);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
CPlainConverter::CPlainConverter()
|
|
{
|
|
m_cRef=1;
|
|
m_pstmPlain=NULL;
|
|
m_pstmOut=NULL;
|
|
m_cchPos = 0;
|
|
m_cchBuffer = 0;
|
|
m_cchOut = 0;
|
|
m_fCRLF = 0;
|
|
}
|
|
|
|
CPlainConverter::~CPlainConverter()
|
|
{
|
|
SafeRelease(m_pstmPlain);
|
|
SafeRelease(m_pstmOut);
|
|
}
|
|
|
|
HRESULT CPlainConverter::QueryInterface(REFIID riid, LPVOID *lplpObj)
|
|
{
|
|
if (!lplpObj)
|
|
return E_INVALIDARG;
|
|
|
|
*lplpObj = NULL; // set to NULL, in case we fail.
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown))
|
|
*lplpObj = (LPVOID)this;
|
|
|
|
if (!*lplpObj)
|
|
return E_NOINTERFACE;
|
|
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
ULONG CPlainConverter::AddRef()
|
|
{
|
|
return ++m_cRef;
|
|
}
|
|
|
|
ULONG CPlainConverter::Release()
|
|
{
|
|
if (--m_cRef==0)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_cRef;
|
|
}
|
|
|
|
HRESULT CPlainConverter::HrWrite(LPCWSTR pszW, ULONG cch)
|
|
{
|
|
ULONG cb;
|
|
HRESULT hr=S_OK;
|
|
|
|
AssertSz(cch <= sizeof(m_rgchOutBufferW)/sizeof(WCHAR), "Hey! why are you writing too much out at once");
|
|
|
|
if (m_cchOut + cch > sizeof(m_rgchOutBufferW)/sizeof(WCHAR))
|
|
{
|
|
// fill buffer then flush it
|
|
cb = sizeof(m_rgchOutBufferW) - (m_cchOut*sizeof(WCHAR));
|
|
CopyMemory((LPVOID)&m_rgchOutBufferW[m_cchOut], (LPVOID)pszW, cb);
|
|
hr = m_pstmOut->Write(m_rgchOutBufferW, sizeof(m_rgchOutBufferW), NULL);
|
|
|
|
// we just filled the buffer with an extra cb off the string, so copy the rest into
|
|
pszW = (LPCWSTR)((LPBYTE)pszW + cb);
|
|
cch -= cb / sizeof(WCHAR);
|
|
CopyMemory((LPVOID)m_rgchOutBufferW, (LPVOID)pszW, cch*sizeof(WCHAR));
|
|
m_cchOut=cch;
|
|
}
|
|
else
|
|
{
|
|
CopyMemory((LPVOID)&m_rgchOutBufferW[m_cchOut], (LPVOID)pszW, cch*sizeof(WCHAR));
|
|
m_cchOut+=cch;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CPlainConverter::HrConvert(LPSTREAM pstm, WCHAR chQuoteW, LPSTREAM *ppstmHtml)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (ppstmHtml==NULL)
|
|
return E_INVALIDARG;
|
|
|
|
// caller wants a stream created? or use his own??
|
|
if (*ppstmHtml==NULL)
|
|
{
|
|
if (FAILED(MimeOleCreateVirtualStream(&m_pstmOut)))
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
m_pstmOut=*ppstmHtml;
|
|
}
|
|
|
|
m_pstmPlain=pstm;
|
|
if (pstm)
|
|
pstm->AddRef();
|
|
|
|
if (m_pstmPlain)
|
|
{
|
|
hr=HrRewindStream(m_pstmPlain);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
|
|
m_nSpcs=0;
|
|
m_chQuoteW=chQuoteW;
|
|
|
|
if (m_pstmPlain)
|
|
{
|
|
// if quoting, quote the first line.
|
|
HrOutputQuoteChar();
|
|
hr = HrParseStream();
|
|
}
|
|
|
|
if (m_cchOut)
|
|
hr = m_pstmOut->Write(m_rgchOutBufferW, m_cchOut*sizeof(WCHAR), NULL);
|
|
|
|
*ppstmHtml=m_pstmOut;
|
|
|
|
error:
|
|
m_pstmOut = NULL;
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CPlainConverter::HrParseStream()
|
|
{
|
|
LPSTREAM pstmOut=m_pstmOut;
|
|
ULONG cchLast;
|
|
|
|
Assert(pstmOut);
|
|
|
|
|
|
ULONG cb;
|
|
|
|
Assert(m_pstmPlain);
|
|
|
|
m_pstmPlain->Read(m_rgchBufferW, sizeof(m_rgchBufferW), &cb);
|
|
m_cchBuffer = cb / sizeof(WCHAR);
|
|
m_cchPos=0;
|
|
|
|
// Raid 63406 - OE doesn't skip byte order marks when inlining unicode text
|
|
if (cb >= 4 && *m_rgchBufferW == 0xfeff)
|
|
m_cchPos++;
|
|
|
|
while (cb)
|
|
{
|
|
if (m_nSpcs && m_rgchBufferW[m_cchPos] != chSpace)
|
|
{
|
|
// this character is not a space, and we have spaces queued up, output
|
|
// spaces before the character
|
|
HrOutputSpaces(m_nSpcs);
|
|
m_nSpcs = 0;
|
|
}
|
|
|
|
switch (m_rgchBufferW[m_cchPos])
|
|
{
|
|
case chSpace: // queue spaces
|
|
m_nSpcs++;
|
|
break;
|
|
|
|
case chCR: // swallow carriage returns as they are always in CRLF pairs.
|
|
break;
|
|
|
|
case chLF:
|
|
// if we're quoting, insert a quote after the CRLF.
|
|
HrWrite(c_szHtmlBreakW, WCHAR_BYTES(c_szHtmlBreakW)/sizeof(WCHAR));
|
|
HrOutputQuoteChar();
|
|
m_fCRLF = 1;
|
|
break;
|
|
|
|
case chQuoteChar:
|
|
HrWrite(c_szEscQuoteW, WCHAR_BYTES(c_szEscQuoteW)/sizeof(WCHAR));
|
|
m_fCRLF = 0;
|
|
break;
|
|
|
|
case chLessThan:
|
|
HrWrite(c_szEscLessThanW, WCHAR_BYTES(c_szEscLessThanW)/sizeof(WCHAR));
|
|
m_fCRLF = 0;
|
|
break;
|
|
|
|
case chGreaterThan:
|
|
HrWrite(c_szEscGreaterThanW, WCHAR_BYTES(c_szEscGreaterThanW)/sizeof(WCHAR));
|
|
m_fCRLF = 0;
|
|
break;
|
|
|
|
case chAmpersand:
|
|
HrWrite(c_szEscAmpersandW, WCHAR_BYTES(c_szEscAmpersandW)/sizeof(WCHAR));
|
|
m_fCRLF = 0;
|
|
break;
|
|
|
|
default:
|
|
// set the last pointer and pull these up...
|
|
cchLast = m_cchPos;
|
|
m_cchPos++;
|
|
while (m_cchPos < m_cchBuffer &&
|
|
!IsSpecialChar(m_rgchBufferW[m_cchPos]))
|
|
{
|
|
m_cchPos++;
|
|
}
|
|
HrWrite(&m_rgchBufferW[cchLast], m_cchPos - cchLast);
|
|
m_cchPos--; //rewind as we INC below
|
|
m_fCRLF = 0;
|
|
break;
|
|
}
|
|
|
|
m_cchPos++;
|
|
Assert(m_cchPos <= m_cchBuffer);
|
|
if (m_cchPos == m_cchBuffer)
|
|
{
|
|
// hit end of buffer, re-read next block
|
|
m_pstmPlain->Read(m_rgchBufferW, sizeof(m_rgchBufferW), &cb);
|
|
m_cchPos=0;
|
|
m_cchBuffer = cb / sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CPlainConverter::HrOutputQuoteChar()
|
|
{
|
|
if (m_chQuoteW)
|
|
{
|
|
// don't bother escaping all quote chars as we only use ">|:"
|
|
AssertSz(m_chQuoteW != '<' && m_chQuoteW != '&' && m_chQuoteW != '"', "need to add support to escape these, if we use them as quote chars!!");
|
|
if (m_chQuoteW== chGreaterThan)
|
|
HrWrite(c_szEscGreaterThanW, WCHAR_BYTES(c_szEscGreaterThanW)/sizeof(WCHAR));
|
|
else
|
|
HrWrite(&m_chQuoteW, 1);
|
|
HrWrite(L" ", 1);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CPlainConverter::HrOutputSpaces(ULONG cSpaces)
|
|
{
|
|
if (cSpaces == 1 && m_fCRLF) // if we get "\n foo" make sure it's an nbsp;
|
|
return HrWrite(c_szHtmlNonBreakingSpaceW, WCHAR_CCH(c_szHtmlNonBreakingSpaceW));
|
|
|
|
|
|
while (--cSpaces)
|
|
HrWrite(c_szHtmlNonBreakingSpaceW, WCHAR_CCH(c_szHtmlNonBreakingSpaceW));
|
|
|
|
return HrWrite(c_szSpaceW, 1);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Warning This Function Trashes the Input Buffer aka: strtok
|
|
*
|
|
*/
|
|
HRESULT EscapeStringToHTML(LPWSTR pwszIn, LPWSTR *ppwszOut)
|
|
{
|
|
int cchPos,
|
|
esc=escInvalid,
|
|
cb = 0;
|
|
LPWSTR pwszText = pwszIn,
|
|
pwszWrite = NULL,
|
|
pwszEnd = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!pwszIn)
|
|
return S_OK;
|
|
|
|
// count space required
|
|
while (*pwszText)
|
|
{
|
|
switch (*pwszText)
|
|
{
|
|
case chGreaterThan:
|
|
cb += WCHAR_BYTES(c_szEscGreaterThanW);
|
|
break;
|
|
|
|
case chLessThan:
|
|
cb += WCHAR_BYTES(c_szEscLessThanW);
|
|
break;
|
|
|
|
case chAmpersand:
|
|
cb += WCHAR_BYTES(c_szEscAmpersandW);
|
|
break;
|
|
|
|
case chQuoteChar:
|
|
cb += WCHAR_BYTES(c_szEscQuoteW);
|
|
break;
|
|
|
|
default:
|
|
cb += sizeof(*pwszText);
|
|
}
|
|
|
|
pwszText++;
|
|
}
|
|
|
|
IF_NULLEXIT(MemAlloc((LPVOID *)&pwszWrite, cb+sizeof(WCHAR)));
|
|
|
|
pwszText = pwszIn;
|
|
*ppwszOut = pwszWrite;
|
|
pwszEnd = pwszWrite + (cb/sizeof(WCHAR));
|
|
|
|
// count space required
|
|
while (*pwszText)
|
|
{
|
|
switch (*pwszText)
|
|
{
|
|
case chGreaterThan:
|
|
StrCpyNW(pwszWrite, c_szEscGreaterThanW, (DWORD)(pwszEnd-pwszWrite));
|
|
pwszWrite += WCHAR_CCH(c_szEscGreaterThanW);
|
|
break;
|
|
|
|
case chLessThan:
|
|
StrCpyNW(pwszWrite, c_szEscLessThanW, (DWORD)(pwszEnd-pwszWrite));
|
|
pwszWrite += WCHAR_CCH(c_szEscLessThanW);
|
|
break;
|
|
|
|
case chQuoteChar:
|
|
StrCpyNW(pwszWrite, c_szEscQuoteW, (DWORD)(pwszEnd-pwszWrite));
|
|
pwszWrite += WCHAR_CCH(c_szEscQuoteW);
|
|
break;
|
|
|
|
case chAmpersand:
|
|
StrCpyNW(pwszWrite, c_szEscAmpersandW, (DWORD)(pwszEnd-pwszWrite));
|
|
pwszWrite += WCHAR_CCH(c_szEscAmpersandW);
|
|
break;
|
|
|
|
default:
|
|
*pwszWrite++ = *pwszText;
|
|
}
|
|
|
|
pwszText++;
|
|
}
|
|
*pwszWrite = 0;
|
|
pwszWrite = NULL;
|
|
|
|
exit:
|
|
MemFree(pwszWrite);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT HrConvertHTMLToFormat(LPSTREAM pstmHtml, LPSTREAM *ppstm, CLIPFORMAT cf)
|
|
{
|
|
HRESULT hr;
|
|
LPUNKNOWN pUnkTrident=0;
|
|
LPSTREAM pstmPlain=0;
|
|
|
|
if (!ppstm)
|
|
return E_INVALIDARG;
|
|
|
|
hr = HrCreateSyncTridentFromStream(pstmHtml, IID_IUnknown, (LPVOID *)&pUnkTrident);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = HrGetDataStream(pUnkTrident, cf, &pstmPlain);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
*ppstm = pstmPlain;
|
|
pstmPlain->AddRef();
|
|
|
|
error:
|
|
ReleaseObj(pUnkTrident);
|
|
ReleaseObj(pstmPlain);
|
|
return hr;
|
|
}
|