Leaked source code of windows server 2003
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

  1. /*
  2. * p l a i n s t m . c p p
  3. *
  4. * Purpose:
  5. * IStream implementation that wraps a plain stream as html and does URL detection
  6. *
  7. * History
  8. * September '96: brettm - created
  9. *
  10. * Copyright (C) Microsoft Corp. 1995, 1996.
  11. */
  12. #include <pch.hxx>
  13. #include "dllmain.h"
  14. #include "strconst.h"
  15. #include "plainstm.h"
  16. #include "triutil.h"
  17. #include "oleutil.h"
  18. #include "demand.h"
  19. ASSERTDATA
  20. /*
  21. * m a c r o s
  22. */
  23. /*
  24. * t y p e d e f s
  25. */
  26. /*
  27. * c o n s t a n t s
  28. */
  29. #define MAX_URL_SIZE 8+1
  30. #define WCHAR_BYTES(_lpsz) (sizeof(_lpsz) - sizeof(WCHAR))
  31. #define WCHAR_CCH(_lpsz) (WCHAR_BYTES(_lpsz)/sizeof(WCHAR))
  32. static const WCHAR c_szHtmlNonBreakingSpaceW[] =L"&nbsp;",
  33. c_szHtmlBreakW[] =L"<BR>\r\n",
  34. c_szSpaceW[] =L" ",
  35. c_szEscGreaterThanW[] =L"&gt;",
  36. c_szEscLessThanW[] =L"&lt;",
  37. c_szEscQuoteW[] =L"&quot;",
  38. c_szEscAmpersandW[] =L"&amp;";
  39. #define chSpace ' '
  40. #define chCR '\r'
  41. #define chLF '\n'
  42. #define chQuoteChar '\"'
  43. #define chLessThan '<'
  44. #define chGreaterThan '>'
  45. #define chAmpersand '&'
  46. #define IsSpecialChar(_ch) ( _ch == chLessThan || _ch == chSpace || _ch == chCR || _ch == chLF || _ch == chQuoteChar || _ch == chLessThan || _ch == chGreaterThan || _ch == chAmpersand )
  47. /*
  48. * g l o b a l s
  49. */
  50. enum
  51. {
  52. escInvalid=-1,
  53. escGreaterThan=0,
  54. escLessThan,
  55. escAmpersand,
  56. escQuote
  57. };
  58. /*
  59. * f u n c t i o n p r o t y p e s
  60. */
  61. /*
  62. * f u n c t i o n s
  63. */
  64. HRESULT HrConvertPlainStreamW(LPSTREAM pstm, WCHAR chQuoteW, LPSTREAM *ppstmHtml)
  65. {
  66. LPPLAINCONVERTER pPlainConv=0;
  67. HRESULT hr;
  68. if (!(pPlainConv=new CPlainConverter()))
  69. return E_OUTOFMEMORY;
  70. hr=pPlainConv->HrConvert(pstm, chQuoteW, ppstmHtml);
  71. if (FAILED(hr))
  72. goto error;
  73. HrRewindStream(*ppstmHtml);
  74. error:
  75. ReleaseObj(pPlainConv);
  76. return hr;
  77. }
  78. CPlainConverter::CPlainConverter()
  79. {
  80. m_cRef=1;
  81. m_pstmPlain=NULL;
  82. m_pstmOut=NULL;
  83. m_cchPos = 0;
  84. m_cchBuffer = 0;
  85. m_cchOut = 0;
  86. m_fCRLF = 0;
  87. }
  88. CPlainConverter::~CPlainConverter()
  89. {
  90. SafeRelease(m_pstmPlain);
  91. SafeRelease(m_pstmOut);
  92. }
  93. HRESULT CPlainConverter::QueryInterface(REFIID riid, LPVOID *lplpObj)
  94. {
  95. if (!lplpObj)
  96. return E_INVALIDARG;
  97. *lplpObj = NULL; // set to NULL, in case we fail.
  98. if (IsEqualIID(riid, IID_IUnknown))
  99. *lplpObj = (LPVOID)this;
  100. if (!*lplpObj)
  101. return E_NOINTERFACE;
  102. AddRef();
  103. return NOERROR;
  104. }
  105. ULONG CPlainConverter::AddRef()
  106. {
  107. return ++m_cRef;
  108. }
  109. ULONG CPlainConverter::Release()
  110. {
  111. if (--m_cRef==0)
  112. {
  113. delete this;
  114. return 0;
  115. }
  116. return m_cRef;
  117. }
  118. HRESULT CPlainConverter::HrWrite(LPCWSTR pszW, ULONG cch)
  119. {
  120. ULONG cb;
  121. HRESULT hr=S_OK;
  122. AssertSz(cch <= sizeof(m_rgchOutBufferW)/sizeof(WCHAR), "Hey! why are you writing too much out at once");
  123. if (m_cchOut + cch > sizeof(m_rgchOutBufferW)/sizeof(WCHAR))
  124. {
  125. // fill buffer then flush it
  126. cb = sizeof(m_rgchOutBufferW) - (m_cchOut*sizeof(WCHAR));
  127. CopyMemory((LPVOID)&m_rgchOutBufferW[m_cchOut], (LPVOID)pszW, cb);
  128. hr = m_pstmOut->Write(m_rgchOutBufferW, sizeof(m_rgchOutBufferW), NULL);
  129. // we just filled the buffer with an extra cb off the string, so copy the rest into
  130. pszW = (LPCWSTR)((LPBYTE)pszW + cb);
  131. cch -= cb / sizeof(WCHAR);
  132. CopyMemory((LPVOID)m_rgchOutBufferW, (LPVOID)pszW, cch*sizeof(WCHAR));
  133. m_cchOut=cch;
  134. }
  135. else
  136. {
  137. CopyMemory((LPVOID)&m_rgchOutBufferW[m_cchOut], (LPVOID)pszW, cch*sizeof(WCHAR));
  138. m_cchOut+=cch;
  139. }
  140. return hr;
  141. }
  142. HRESULT CPlainConverter::HrConvert(LPSTREAM pstm, WCHAR chQuoteW, LPSTREAM *ppstmHtml)
  143. {
  144. HRESULT hr;
  145. if (ppstmHtml==NULL)
  146. return E_INVALIDARG;
  147. // caller wants a stream created? or use his own??
  148. if (*ppstmHtml==NULL)
  149. {
  150. if (FAILED(MimeOleCreateVirtualStream(&m_pstmOut)))
  151. return E_OUTOFMEMORY;
  152. }
  153. else
  154. {
  155. m_pstmOut=*ppstmHtml;
  156. }
  157. m_pstmPlain=pstm;
  158. if (pstm)
  159. pstm->AddRef();
  160. if (m_pstmPlain)
  161. {
  162. hr=HrRewindStream(m_pstmPlain);
  163. if (FAILED(hr))
  164. goto error;
  165. }
  166. m_nSpcs=0;
  167. m_chQuoteW=chQuoteW;
  168. if (m_pstmPlain)
  169. {
  170. // if quoting, quote the first line.
  171. HrOutputQuoteChar();
  172. hr = HrParseStream();
  173. }
  174. if (m_cchOut)
  175. hr = m_pstmOut->Write(m_rgchOutBufferW, m_cchOut*sizeof(WCHAR), NULL);
  176. *ppstmHtml=m_pstmOut;
  177. error:
  178. m_pstmOut = NULL;
  179. return hr;
  180. }
  181. HRESULT CPlainConverter::HrParseStream()
  182. {
  183. LPSTREAM pstmOut=m_pstmOut;
  184. ULONG cchLast;
  185. Assert(pstmOut);
  186. ULONG cb;
  187. Assert(m_pstmPlain);
  188. m_pstmPlain->Read(m_rgchBufferW, sizeof(m_rgchBufferW), &cb);
  189. m_cchBuffer = cb / sizeof(WCHAR);
  190. m_cchPos=0;
  191. // Raid 63406 - OE doesn't skip byte order marks when inlining unicode text
  192. if (cb >= 4 && *m_rgchBufferW == 0xfeff)
  193. m_cchPos++;
  194. while (cb)
  195. {
  196. if (m_nSpcs && m_rgchBufferW[m_cchPos] != chSpace)
  197. {
  198. // this character is not a space, and we have spaces queued up, output
  199. // spaces before the character
  200. HrOutputSpaces(m_nSpcs);
  201. m_nSpcs = 0;
  202. }
  203. switch (m_rgchBufferW[m_cchPos])
  204. {
  205. case chSpace: // queue spaces
  206. m_nSpcs++;
  207. break;
  208. case chCR: // swallow carriage returns as they are always in CRLF pairs.
  209. break;
  210. case chLF:
  211. // if we're quoting, insert a quote after the CRLF.
  212. HrWrite(c_szHtmlBreakW, WCHAR_BYTES(c_szHtmlBreakW)/sizeof(WCHAR));
  213. HrOutputQuoteChar();
  214. m_fCRLF = 1;
  215. break;
  216. case chQuoteChar:
  217. HrWrite(c_szEscQuoteW, WCHAR_BYTES(c_szEscQuoteW)/sizeof(WCHAR));
  218. m_fCRLF = 0;
  219. break;
  220. case chLessThan:
  221. HrWrite(c_szEscLessThanW, WCHAR_BYTES(c_szEscLessThanW)/sizeof(WCHAR));
  222. m_fCRLF = 0;
  223. break;
  224. case chGreaterThan:
  225. HrWrite(c_szEscGreaterThanW, WCHAR_BYTES(c_szEscGreaterThanW)/sizeof(WCHAR));
  226. m_fCRLF = 0;
  227. break;
  228. case chAmpersand:
  229. HrWrite(c_szEscAmpersandW, WCHAR_BYTES(c_szEscAmpersandW)/sizeof(WCHAR));
  230. m_fCRLF = 0;
  231. break;
  232. default:
  233. // set the last pointer and pull these up...
  234. cchLast = m_cchPos;
  235. m_cchPos++;
  236. while (m_cchPos < m_cchBuffer &&
  237. !IsSpecialChar(m_rgchBufferW[m_cchPos]))
  238. {
  239. m_cchPos++;
  240. }
  241. HrWrite(&m_rgchBufferW[cchLast], m_cchPos - cchLast);
  242. m_cchPos--; //rewind as we INC below
  243. m_fCRLF = 0;
  244. break;
  245. }
  246. m_cchPos++;
  247. Assert(m_cchPos <= m_cchBuffer);
  248. if (m_cchPos == m_cchBuffer)
  249. {
  250. // hit end of buffer, re-read next block
  251. m_pstmPlain->Read(m_rgchBufferW, sizeof(m_rgchBufferW), &cb);
  252. m_cchPos=0;
  253. m_cchBuffer = cb / sizeof(WCHAR);
  254. }
  255. }
  256. return S_OK;
  257. }
  258. HRESULT CPlainConverter::HrOutputQuoteChar()
  259. {
  260. if (m_chQuoteW)
  261. {
  262. // don't bother escaping all quote chars as we only use ">|:"
  263. AssertSz(m_chQuoteW != '<' && m_chQuoteW != '&' && m_chQuoteW != '"', "need to add support to escape these, if we use them as quote chars!!");
  264. if (m_chQuoteW== chGreaterThan)
  265. HrWrite(c_szEscGreaterThanW, WCHAR_BYTES(c_szEscGreaterThanW)/sizeof(WCHAR));
  266. else
  267. HrWrite(&m_chQuoteW, 1);
  268. HrWrite(L" ", 1);
  269. }
  270. return S_OK;
  271. }
  272. HRESULT CPlainConverter::HrOutputSpaces(ULONG cSpaces)
  273. {
  274. if (cSpaces == 1 && m_fCRLF) // if we get "\n foo" make sure it's an nbsp;
  275. return HrWrite(c_szHtmlNonBreakingSpaceW, WCHAR_CCH(c_szHtmlNonBreakingSpaceW));
  276. while (--cSpaces)
  277. HrWrite(c_szHtmlNonBreakingSpaceW, WCHAR_CCH(c_szHtmlNonBreakingSpaceW));
  278. return HrWrite(c_szSpaceW, 1);
  279. }
  280. /*
  281. * Warning This Function Trashes the Input Buffer aka: strtok
  282. *
  283. */
  284. HRESULT EscapeStringToHTML(LPWSTR pwszIn, LPWSTR *ppwszOut)
  285. {
  286. int cchPos,
  287. esc=escInvalid,
  288. cb = 0;
  289. LPWSTR pwszText = pwszIn,
  290. pwszWrite = NULL,
  291. pwszEnd = NULL;
  292. HRESULT hr = S_OK;
  293. if (!pwszIn)
  294. return S_OK;
  295. // count space required
  296. while (*pwszText)
  297. {
  298. switch (*pwszText)
  299. {
  300. case chGreaterThan:
  301. cb += WCHAR_BYTES(c_szEscGreaterThanW);
  302. break;
  303. case chLessThan:
  304. cb += WCHAR_BYTES(c_szEscLessThanW);
  305. break;
  306. case chAmpersand:
  307. cb += WCHAR_BYTES(c_szEscAmpersandW);
  308. break;
  309. case chQuoteChar:
  310. cb += WCHAR_BYTES(c_szEscQuoteW);
  311. break;
  312. default:
  313. cb += sizeof(*pwszText);
  314. }
  315. pwszText++;
  316. }
  317. IF_NULLEXIT(MemAlloc((LPVOID *)&pwszWrite, cb+sizeof(WCHAR)));
  318. pwszText = pwszIn;
  319. *ppwszOut = pwszWrite;
  320. pwszEnd = pwszWrite + (cb/sizeof(WCHAR));
  321. // count space required
  322. while (*pwszText)
  323. {
  324. switch (*pwszText)
  325. {
  326. case chGreaterThan:
  327. StrCpyNW(pwszWrite, c_szEscGreaterThanW, (DWORD)(pwszEnd-pwszWrite));
  328. pwszWrite += WCHAR_CCH(c_szEscGreaterThanW);
  329. break;
  330. case chLessThan:
  331. StrCpyNW(pwszWrite, c_szEscLessThanW, (DWORD)(pwszEnd-pwszWrite));
  332. pwszWrite += WCHAR_CCH(c_szEscLessThanW);
  333. break;
  334. case chQuoteChar:
  335. StrCpyNW(pwszWrite, c_szEscQuoteW, (DWORD)(pwszEnd-pwszWrite));
  336. pwszWrite += WCHAR_CCH(c_szEscQuoteW);
  337. break;
  338. case chAmpersand:
  339. StrCpyNW(pwszWrite, c_szEscAmpersandW, (DWORD)(pwszEnd-pwszWrite));
  340. pwszWrite += WCHAR_CCH(c_szEscAmpersandW);
  341. break;
  342. default:
  343. *pwszWrite++ = *pwszText;
  344. }
  345. pwszText++;
  346. }
  347. *pwszWrite = 0;
  348. pwszWrite = NULL;
  349. exit:
  350. MemFree(pwszWrite);
  351. return S_OK;
  352. }
  353. HRESULT HrConvertHTMLToFormat(LPSTREAM pstmHtml, LPSTREAM *ppstm, CLIPFORMAT cf)
  354. {
  355. HRESULT hr;
  356. LPUNKNOWN pUnkTrident=0;
  357. LPSTREAM pstmPlain=0;
  358. if (!ppstm)
  359. return E_INVALIDARG;
  360. hr = HrCreateSyncTridentFromStream(pstmHtml, IID_IUnknown, (LPVOID *)&pUnkTrident);
  361. if (FAILED(hr))
  362. goto error;
  363. hr = HrGetDataStream(pUnkTrident, cf, &pstmPlain);
  364. if (FAILED(hr))
  365. goto error;
  366. *ppstm = pstmPlain;
  367. pstmPlain->AddRef();
  368. error:
  369. ReleaseObj(pUnkTrident);
  370. ReleaseObj(pstmPlain);
  371. return hr;
  372. }