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.

752 lines
25 KiB

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Copyright (C) 1993-1996 Microsoft Corporation. All Rights Reserved.
  3. //
  4. // MODULE: URL.cpp
  5. //
  6. // PURPOSE: All the URL parsing routines THOR would ever need.
  7. //
  8. #include "pch.hxx"
  9. #include "strconst.h"
  10. #include "urltest.h"
  11. #include "url.h"
  12. #include "xpcomm.h"
  13. #include <shlwapi.h>
  14. #include <shlwapip.h>
  15. #include "mimeole.h"
  16. #include <urlmon.h>
  17. #include <wininet.h>
  18. #include "imnact.h"
  19. #include "demand.h"
  20. #include <mlang.h>
  21. //
  22. // FUNCTION: URL_ParseNewsUrls
  23. //
  24. // PURPOSE: Takes a URL passed to the news view and validates it. If the
  25. // URL is valid, then the server, group, and article-id are
  26. // returned as appropriate.
  27. //
  28. // PARAMETERS:
  29. // pszURL - Pointer to the URL to parse.
  30. // ppszServer - Name of the server, this function allocates the memory.
  31. // puPort - Port number on the server to use.
  32. // ppszGroup - Name of the group, this function allocates the memory.
  33. // ppszArticle - Article id, this function allocates the memory.
  34. // pfSecure - whether to use SSL to connect
  35. //
  36. // RETURN VALUE:
  37. // Returns S_OK if the URL is valid, or an appropriate error code
  38. // otherwise.
  39. //
  40. // COMMENTS:
  41. // The URLs that are valid for news are:
  42. //
  43. // news:<newsgroup-name>
  44. // news:<article-id>
  45. // news://<server> (for Netscape compatibility)
  46. // news://<server>/ (for URL.DLL compatibility)
  47. // news://<server>/<newsgroup-name>
  48. // news://<server>/<article-id>
  49. // nntp://<host>:<port>/<newsgroup-name>/<article-id>
  50. //
  51. // $LOCALIZE - Need a separate code path for DBCS
  52. HRESULT URL_ParseNewsUrls(LPTSTR pszURL, LPTSTR* ppszServer, LPUINT puPort,
  53. LPTSTR* ppszGroup, LPTSTR* ppszArticle, LPBOOL pfSecure)
  54. {
  55. HRESULT hr;
  56. UINT cchBuffer ;
  57. LPTSTR pszBuffer,
  58. pszTemp;
  59. Assert(pszURL != NULL);
  60. // Allocate a temp buffer to work with.
  61. cchBuffer = lstrlen(pszURL) + sizeof(TCHAR);
  62. if (!MemAlloc((LPVOID*)&pszBuffer, cchBuffer))
  63. return E_OUTOFMEMORY;
  64. ZeroMemory(pszBuffer, cchBuffer);
  65. // Loop through the URL looking for the first ":". We're trying to discern
  66. // what the prefix is - either "nntp" or "news".
  67. pszTemp = pszURL;
  68. while (*pszTemp && *pszTemp != TEXT(':'))
  69. pszTemp++;
  70. CopyMemory(pszBuffer, pszURL, ((LPBYTE) pszTemp - (LPBYTE) pszURL));
  71. *ppszServer = NULL;
  72. *ppszGroup = NULL;
  73. *ppszArticle = NULL;
  74. *puPort = (UINT) -1;
  75. *pfSecure = FALSE;
  76. if (0 == lstrcmpi(pszBuffer, c_szURLNews))
  77. {
  78. // The URL starts with "news:", so advance the pointer past the ":"
  79. // and pass what's left to the appropriate parser.
  80. pszTemp++;
  81. hr = URL_ParseNEWS(pszTemp, ppszServer, ppszGroup, ppszArticle);
  82. }
  83. else if (0 == lstrcmpi(pszBuffer, c_szURLNNTP))
  84. {
  85. // The URL starts with "nntp:", so advance the pointer past the ":"
  86. // and pass what's left to the appropriate parser.
  87. pszTemp++;
  88. hr = URL_ParseNNTP(pszTemp, ppszServer, puPort, ppszGroup, ppszArticle);
  89. }
  90. else if (0 == lstrcmpi(pszBuffer, c_szURLSnews))
  91. {
  92. // The URL starts with "snews:", so advance the pointer past the ":"
  93. // and pass what's left to the appropriate parser.
  94. pszTemp++;
  95. *pfSecure = TRUE;
  96. hr = URL_ParseNEWS(pszTemp, ppszServer, ppszGroup, ppszArticle);
  97. }
  98. else
  99. {
  100. // this protocol is not a supported NEWS protocol
  101. hr = INET_E_UNKNOWN_PROTOCOL;
  102. }
  103. MemFree(pszBuffer);
  104. return hr;
  105. }
  106. // $LOCALIZE - Need a separate code path for DBCS
  107. HRESULT URL_ParseNEWS(LPTSTR pszURL, LPTSTR* ppszServer, LPTSTR* ppszGroup,
  108. LPTSTR* ppszArticle)
  109. {
  110. LPTSTR pszBuffer;
  111. LPTSTR pszBegin;
  112. UINT cch = 0;
  113. if (pszURL == NULL || *pszURL == '\0')
  114. return INET_E_INVALID_URL;
  115. // First check to see if a server has been specified. If so, then the
  116. // first two characters will be "//".
  117. if (*pszURL == TEXT('/'))
  118. {
  119. // Make sure there are two '/'
  120. pszURL++;
  121. if (*pszURL != TEXT('/'))
  122. return INET_E_INVALID_URL;
  123. pszURL++;
  124. pszBegin = pszURL;
  125. // Ok, got a server name. Find the end and copy it to ppszServer.
  126. while (*pszURL && (*pszURL != TEXT('/')))
  127. pszURL++;
  128. cch = (UINT) ((LPBYTE) pszURL - (LPBYTE) pszBegin) + sizeof(TCHAR);
  129. if (cch <= 1)
  130. return S_OK; // bug 12467
  131. if (!MemAlloc((LPVOID*) ppszServer, cch))
  132. return E_OUTOFMEMORY;
  133. ZeroMemory(*ppszServer, cch);
  134. CopyMemory(*ppszServer, pszBegin, cch - sizeof(TCHAR));
  135. // if we found the last '/' skip over it
  136. if (*pszURL)
  137. pszURL++;
  138. //
  139. // NOTE: This code makes the following URLs valid, taking us to the
  140. // root node for the server.
  141. //
  142. // news://<server>
  143. // news://<server>/
  144. //
  145. // The first form is necessary for compatibility with Netscape, and
  146. // the second form is necessary because URL.DLL adds the trailing
  147. // slash before passing the first form to us.
  148. //
  149. // If we're at the end, fake a news://server/* URL.
  150. if (!*pszURL)
  151. pszURL = (LPTSTR)g_szAsterisk;
  152. }
  153. // The difference between a group and article string is that the article
  154. // must have "@" in it somewhere.
  155. if (!lstrlen(pszURL))
  156. {
  157. if (*ppszServer)
  158. {
  159. MemFree(*ppszServer);
  160. *ppszServer = 0;
  161. }
  162. return INET_E_INVALID_URL;
  163. }
  164. ULONG cchURL = lstrlen(pszURL)+1;
  165. if (!MemAlloc((LPVOID*) &pszBuffer, cchURL * sizeof(TCHAR)))
  166. {
  167. if (*ppszServer)
  168. {
  169. MemFree(*ppszServer);
  170. *ppszServer = 0;
  171. }
  172. return INET_E_INVALID_URL;
  173. }
  174. StrCpyN(pszBuffer, pszURL, cchURL);
  175. while (*pszURL && *pszURL != TEXT('@'))
  176. pszURL++;
  177. if (*pszURL == TEXT('@'))
  178. {
  179. // This is an article
  180. *ppszGroup = NULL;
  181. *ppszArticle = pszBuffer;
  182. }
  183. else
  184. {
  185. *ppszGroup = pszBuffer;
  186. *ppszArticle = NULL;
  187. }
  188. return S_OK;
  189. }
  190. // $LOCALIZE - Need a separate code path for DBCS
  191. // Validates a URL of the form NNTP://<host>:<port>/<newsgroup-name>/<message-id>
  192. HRESULT URL_ParseNNTP(LPTSTR pszURL, LPTSTR* ppszServer, LPUINT puPort,
  193. LPTSTR* ppszGroup, LPTSTR* ppszArticle)
  194. {
  195. LPTSTR pszTemp;
  196. UINT cch;
  197. HRESULT hrReturn = S_OK;
  198. Assert(pszURL != NULL);
  199. if (pszURL == NULL || *pszURL == '\0')
  200. return INET_E_INVALID_URL;
  201. // Make sure there are leading "//"
  202. if (*pszURL != TEXT('/'))
  203. return INET_E_INVALID_URL;
  204. pszURL++;
  205. if (*pszURL != TEXT('/'))
  206. return INET_E_INVALID_URL;
  207. pszURL++;
  208. pszTemp = pszURL;
  209. // Search for the host name.
  210. while (*pszTemp && (*pszTemp != TEXT('/')) && (*pszTemp != TEXT(':')))
  211. pszTemp++;
  212. if (*pszTemp != TEXT('/') && *pszTemp != TEXT(':'))
  213. return INET_E_INVALID_URL;
  214. // Copy the host name to the server return value
  215. cch = (UINT) ((LPBYTE) pszTemp - (LPBYTE) pszURL) + sizeof(TCHAR);
  216. if (cch <= 1)
  217. return INET_E_INVALID_URL;
  218. if (!MemAlloc((LPVOID*) ppszServer, cch))
  219. return E_OUTOFMEMORY;
  220. ZeroMemory(*ppszServer, cch);
  221. CopyMemory(*ppszServer, pszURL, (LPBYTE) pszTemp - (LPBYTE) pszURL);
  222. if (*pszTemp == TEXT(':'))
  223. {
  224. // The URL specified a port, so parse that puppy out.
  225. pszTemp++;
  226. pszURL = pszTemp;
  227. while (*pszTemp && (*pszTemp != TEXT('/')))
  228. pszTemp++;
  229. cch = (UINT) ((LPBYTE) pszTemp - (LPBYTE) pszURL);
  230. if (cch <= 1)
  231. {
  232. hrReturn = INET_E_INVALID_URL;
  233. goto error;
  234. }
  235. *puPort = StrToInt(pszURL);
  236. }
  237. if (*pszTemp != TEXT('/'))
  238. {
  239. hrReturn = INET_E_INVALID_URL;
  240. goto error;
  241. }
  242. // Get the newsgroup name
  243. pszTemp++; // Pass the '/'
  244. pszURL = pszTemp;
  245. while (*pszTemp && (*pszTemp != TEXT('/')))
  246. pszTemp++;
  247. if (*pszTemp != TEXT('/'))
  248. {
  249. hrReturn = INET_E_INVALID_URL;
  250. goto error;
  251. }
  252. // Copy the group name to the group return value
  253. cch = (UINT) ((LPBYTE) pszTemp - (LPBYTE) pszURL) + sizeof(TCHAR);
  254. if (cch <= 0)
  255. {
  256. hrReturn = INET_E_INVALID_URL;
  257. goto error;
  258. }
  259. if (!MemAlloc((LPVOID*) ppszGroup, cch))
  260. return (E_OUTOFMEMORY);
  261. ZeroMemory(*ppszGroup, cch);
  262. CopyMemory(*ppszGroup, pszURL, (LPBYTE) pszTemp - (LPBYTE) pszURL);
  263. // Now copy from here to the end of the string as the article id
  264. pszTemp++;
  265. cch = lstrlen(pszTemp) + 1;
  266. if (cch <= 0)
  267. {
  268. hrReturn = INET_E_INVALID_URL;
  269. goto error;
  270. }
  271. if (!MemAlloc((LPVOID*) ppszArticle, cch*sizeof(TCHAR)))
  272. return (E_OUTOFMEMORY);
  273. StrCpyN(*ppszArticle, pszTemp, cch);
  274. return (S_OK);
  275. error:
  276. if (*ppszServer)
  277. MemFree(*ppszServer);
  278. if (*ppszGroup)
  279. MemFree(*ppszGroup);
  280. if (*ppszArticle)
  281. MemFree(*ppszArticle);
  282. *ppszServer = NULL;
  283. *ppszGroup = NULL;
  284. *ppszArticle = NULL;
  285. *puPort = (UINT) -1;
  286. return (hrReturn);
  287. }
  288. static const TCHAR c_szColon[] = ":";
  289. static const TCHAR c_szQuestion[] = "?";
  290. static const TCHAR c_szEquals[] = "=";
  291. static const TCHAR c_szAmpersand[] = "&";
  292. static const TCHAR c_szBody[] = "body";
  293. static const TCHAR c_szBcc[] = "bcc";
  294. //
  295. // FUNCTION: URL_ParseMailTo()
  296. //
  297. // PURPOSE: This function takes a mailto: URL and determines if it is a valid
  298. // URL for mail. The function then fill in a pMsg from the URL
  299. //
  300. // PARAMETERS:
  301. // pszURL - The URL to parse.
  302. // pMsg - The LPMIMEMESSAGE to fill in from the URL.
  303. //
  304. // RETURN VALUE:
  305. // Returns S_OK if the URL is a valid mail URL and the message is filled,
  306. // or an appropriate HRESULT describing why the function failed.
  307. //
  308. // COMMENTS:
  309. // Right now the only valid URL is
  310. // mailto:<SMTP address>
  311. //
  312. HRESULT URL_ParseMailTo(LPTSTR pszURL, LPMIMEMESSAGE pMsg)
  313. {
  314. CStringParser sp;
  315. HRESULT hr;
  316. HADDRESS hAddress;
  317. LPMIMEADDRESSTABLE pAddrTable = 0;
  318. sp.Init(pszURL, lstrlen(pszURL), 0);
  319. if (sp.ChParse(c_szColon))
  320. {
  321. // verify that this is a "mailto:" URL
  322. if (lstrcmpi(sp.PszValue(), c_szURLMailTo))
  323. return INET_E_UNKNOWN_PROTOCOL;
  324. hr = pMsg->GetAddressTable(&pAddrTable);
  325. if (FAILED(hr))
  326. return(hr);
  327. Assert(pAddrTable != NULL);
  328. sp.ChParse(c_szQuestion);
  329. if (sp.CchValue())
  330. {
  331. // opie says it's cool that I'm about to clobber his buffer
  332. UrlUnescapeInPlace((LPTSTR)sp.PszValue(), 0);
  333. pAddrTable->Append(IAT_TO, IET_DECODED, sp.PszValue(), NULL, &hAddress);
  334. }
  335. while (sp.ChParse(c_szEquals))
  336. {
  337. LPTSTR pszAttr = StringDup(sp.PszValue());
  338. if (pszAttr)
  339. {
  340. sp.ChParse(c_szAmpersand);
  341. if (sp.CchValue())
  342. {
  343. UrlUnescapeInPlace((LPTSTR)sp.PszValue(), 0);
  344. // are we trying to set the body?
  345. if (!lstrcmpi(c_szBody, pszAttr))
  346. {
  347. LPSTREAM pStream;
  348. if (SUCCEEDED(MimeOleCreateVirtualStream(&pStream)))
  349. {
  350. if (SUCCEEDED(pStream->Write(sp.PszValue(), lstrlen(sp.PszValue()) * sizeof(TCHAR), NULL)))
  351. {
  352. pMsg->SetTextBody(TXT_PLAIN, IET_DECODED, NULL, pStream, NULL);
  353. }
  354. pStream->Release();
  355. }
  356. }
  357. else if (0 == lstrcmpi(c_szCC, pszAttr))
  358. {
  359. pAddrTable->Append(IAT_CC, IET_DECODED, sp.PszValue(), NULL, &hAddress);
  360. }
  361. else if (0 == lstrcmpi(c_szBcc, pszAttr))
  362. {
  363. pAddrTable->Append(IAT_BCC, IET_DECODED, sp.PszValue(), NULL, &hAddress);
  364. }
  365. else if (0 == lstrcmpi(c_szTo, pszAttr))
  366. {
  367. pAddrTable->Append(IAT_TO, IET_DECODED, sp.PszValue(), NULL, &hAddress);
  368. }
  369. else
  370. {
  371. // just stuff the prop into the message
  372. MimeOleSetBodyPropA(pMsg, HBODY_ROOT, pszAttr, NOFLAGS, sp.PszValue());
  373. }
  374. }
  375. MemFree(pszAttr);
  376. }
  377. }
  378. pAddrTable->Release();
  379. }
  380. return S_OK;
  381. }
  382. #define MAX_SUBSTR_SIZE CCHMAX_DISPLAY_NAME
  383. typedef struct tagURLSub
  384. {
  385. LPCTSTR szTag;
  386. DWORD dwType;
  387. } URLSUB;
  388. const static URLSUB c_UrlSub[] = {
  389. {TEXT("{SUB_CLCID}"), URLSUB_CLCID},
  390. {TEXT("{SUB_PRD}"), URLSUB_PRD},
  391. {TEXT("{SUB_PVER}"), URLSUB_PVER},
  392. {TEXT("{SUB_NAME}"), URLSUB_NAME},
  393. {TEXT("{SUB_EMAIL}"), URLSUB_EMAIL},
  394. };
  395. HRESULT URLSubstitutionA(LPCSTR pszUrlIn, LPSTR pszUrlOut, DWORD cchSize, DWORD dwSubstitutions, IImnAccount *pCertAccount)
  396. {
  397. HRESULT hr = S_OK;
  398. DWORD dwIndex;
  399. CHAR szTempUrl[INTERNET_MAX_URL_LENGTH];
  400. Assert(cchSize <= ARRAYSIZE(szTempUrl)); // We will truncate anything longer than INTERNET_MAX_URL_LENGTH
  401. StrCpyN(szTempUrl, pszUrlIn, ARRAYSIZE(szTempUrl));
  402. for (dwIndex = 0; dwIndex < ARRAYSIZE(c_UrlSub); dwIndex++)
  403. {
  404. while (dwSubstitutions & c_UrlSub[dwIndex].dwType)
  405. {
  406. LPSTR pszTag = StrStrA(szTempUrl, c_UrlSub[dwIndex].szTag);
  407. if (pszTag)
  408. {
  409. TCHAR szCopyUrl[INTERNET_MAX_URL_LENGTH];
  410. TCHAR szSubStr[MAX_SUBSTR_SIZE]; // The Substitution
  411. // Copy URL Before Substitution.
  412. CopyMemory(szCopyUrl, szTempUrl, (int)min((pszTag - szTempUrl), sizeof(szCopyUrl)));
  413. szCopyUrl[(pszTag - szTempUrl)/sizeof(CHAR)] = TEXT('\0');
  414. pszTag += lstrlen(c_UrlSub[dwIndex].szTag);
  415. switch (c_UrlSub[dwIndex].dwType)
  416. {
  417. case URLSUB_CLCID:
  418. {
  419. LCID lcid = GetUserDefaultLCID();
  420. wnsprintf(szSubStr, ARRAYSIZE(szSubStr), "%#04lx", lcid);
  421. }
  422. break;
  423. case URLSUB_PRD:
  424. StrCpyN(szSubStr, c_szUrlSubPRD, ARRAYSIZE(szSubStr));
  425. break;
  426. case URLSUB_PVER:
  427. StrCpyN(szSubStr, c_szUrlSubPVER, ARRAYSIZE(szSubStr));
  428. break;
  429. case URLSUB_NAME:
  430. case URLSUB_EMAIL:
  431. {
  432. IImnAccount *pAccount = NULL;
  433. hr = E_FAIL;
  434. if(pCertAccount)
  435. {
  436. hr = pCertAccount->GetPropSz((c_UrlSub[dwIndex].dwType == URLSUB_NAME) ? AP_SMTP_DISPLAY_NAME : AP_SMTP_EMAIL_ADDRESS,
  437. szSubStr,
  438. ARRAYSIZE(szSubStr));
  439. }
  440. else if (g_pAcctMan && SUCCEEDED(g_pAcctMan->GetDefaultAccount(ACCT_MAIL, &pAccount)))
  441. {
  442. hr = pAccount->GetPropSz((c_UrlSub[dwIndex].dwType == URLSUB_NAME) ? AP_SMTP_DISPLAY_NAME : AP_SMTP_EMAIL_ADDRESS,
  443. szSubStr,
  444. ARRAYSIZE(szSubStr));
  445. pAccount->Release();
  446. }
  447. if (FAILED(hr))
  448. return hr;
  449. }
  450. break;
  451. default:
  452. szSubStr[0] = TEXT('\0');
  453. Assert(FALSE); // Not Impl.
  454. hr = E_NOTIMPL;
  455. break;
  456. }
  457. // Add the Substitution String to the end (will become the middle)
  458. StrCatBuff(szCopyUrl, szSubStr, ARRAYSIZE(szCopyUrl));
  459. // Add the rest of the URL after the substitution substring.
  460. StrCatBuff(szCopyUrl, pszTag, ARRAYSIZE(szCopyUrl));
  461. StrCpyN(szTempUrl, szCopyUrl, ARRAYSIZE(szTempUrl));
  462. }
  463. else
  464. break; // This will allow us to replace all the occurances of this string.
  465. }
  466. }
  467. StrCpyN(pszUrlOut, szTempUrl, cchSize);
  468. return hr;
  469. }
  470. HRESULT URLSubLoadStringA(UINT idRes, LPSTR pszUrlOut, DWORD cchSizeOut, DWORD dwSubstitutions, IImnAccount *pCertAccount)
  471. {
  472. HRESULT hr = E_FAIL;
  473. CHAR szTempUrl[INTERNET_MAX_URL_LENGTH];
  474. if (LoadStringA(g_hLocRes, idRes, szTempUrl, ARRAYSIZE(szTempUrl)))
  475. hr = URLSubstitutionA(szTempUrl, pszUrlOut, cchSizeOut, dwSubstitutions, pCertAccount);
  476. return hr;
  477. }
  478. HRESULT HrConvertStringToUnicode(UINT uiSrcCodePage, CHAR *pSrcStr, UINT cSrcSize, WCHAR *pDstStr, UINT cDstSize)
  479. {
  480. IMultiLanguage *pMLang = NULL;
  481. IMLangConvertCharset *pMLangConv = NULL;
  482. HRESULT hr = E_FAIL;
  483. IF_FAILEXIT(hr = CoCreateInstance(CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, IID_IMultiLanguage, (void**)&pMLang));
  484. IF_FAILEXIT(hr = pMLang->CreateConvertCharset(uiSrcCodePage, 1200, NULL, &pMLangConv));
  485. hr = pMLangConv->DoConversionToUnicode(pSrcStr, &cSrcSize, pDstStr, &cDstSize);
  486. exit:
  487. ReleaseObj(pMLangConv);
  488. ReleaseObj(pMLang);
  489. return hr;
  490. }
  491. static const char c_szBaseFmt[]="<BASE HREF=\"%s\">\n\r";
  492. static const char c_szBaseFileFmt[]="<BASE HREF=\"file://%s\\\">\n\r";
  493. static const WCHAR c_wszBaseFmt[]=L"<BASE HREF=\"%s\">\n\r";
  494. static const WCHAR c_wszBaseFileFmt[]=L"<BASE HREF=\"file://%s\\\">\n\r";
  495. HRESULT HrCreateBasedWebPage(LPWSTR pwszUrl, LPSTREAM *ppstmHtml)
  496. {
  497. HRESULT hr;
  498. LPSTREAM pstm = NULL,
  499. pstmCopy = NULL,
  500. pstmTemp = NULL;
  501. CHAR szBase[MAX_PATH+50],
  502. szCopy[MAX_PATH];
  503. WCHAR wszBase[MAX_PATH+50],
  504. wszCopy[MAX_PATH];
  505. ULONG cb,
  506. cbTemp;
  507. BOOL fLittleEndian;
  508. LPSTR pszUrl = NULL,
  509. pszStream = NULL,
  510. pszCharset = NULL;
  511. LPWSTR pwszStream = NULL,
  512. pwszTempUrl = NULL;
  513. BOOL fIsURL = PathIsURLW(pwszUrl),
  514. fForceUnicode,
  515. fIsUnicode;
  516. IF_FAILEXIT(hr = MimeOleCreateVirtualStream(&pstmCopy));
  517. // Are we a file or a URL?
  518. if(fIsURL)
  519. {
  520. // Since we have a url, then must be ansi
  521. IF_NULLEXIT(pszUrl = PszToANSI(CP_ACP, pwszUrl));
  522. // we can not write to this pstm, so we have pstmCopy.
  523. IF_FAILEXIT(hr = URLOpenBlockingStream(NULL, pszUrl, &pstm, 0, NULL));
  524. if (S_OK == HrIsStreamUnicode(pstm, &fLittleEndian))
  525. {
  526. BYTE rgb[2];
  527. IF_FAILEXIT(hr = pstm->Read(rgb, 2, &cb));
  528. Assert(2 == cb);
  529. IF_FAILEXIT(hr = pstmCopy->Write(rgb, 2, NULL));
  530. wnsprintfW((LPWSTR)wszBase, ARRAYSIZE(wszBase), c_wszBaseFmt, pwszUrl);
  531. IF_FAILEXIT(hr = pstmCopy->Write(wszBase, lstrlenW(wszBase) * sizeof(WCHAR), NULL));
  532. }
  533. else
  534. {
  535. wnsprintf(szBase, ARRAYSIZE(szBase), c_szBaseFmt, pszUrl);
  536. IF_FAILEXIT(hr = pstmCopy->Write(szBase, lstrlen(szBase), NULL));
  537. }
  538. }
  539. else
  540. {
  541. // If filename can't be converted to ansi, then we must do this in UNICODE
  542. // even if the stationery itself is normally ansi.
  543. IF_NULLEXIT(pszUrl = PszToANSI(CP_ACP, pwszUrl));
  544. IF_NULLEXIT(pwszTempUrl = PszToUnicode(CP_ACP, pszUrl));
  545. fForceUnicode = (0 != StrCmpW(pwszUrl, pwszTempUrl));
  546. IF_FAILEXIT(hr = CreateStreamOnHFileW(pwszUrl, GENERIC_READ, FILE_SHARE_READ, NULL,
  547. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL, &pstm));
  548. fIsUnicode = (S_OK == HrIsStreamUnicode(pstm, &fLittleEndian));
  549. if (fForceUnicode || fIsUnicode)
  550. {
  551. BYTE bUniMark = 0xFF;
  552. IF_FAILEXIT(hr = pstmCopy->Write(&bUniMark, sizeof(bUniMark), NULL));
  553. bUniMark = 0xFE;
  554. IF_FAILEXIT(hr = pstmCopy->Write(&bUniMark, sizeof(bUniMark), NULL));
  555. StrCpyNW(wszCopy, pwszUrl, ARRAYSIZE(wszCopy));
  556. PathRemoveFileSpecW(wszCopy);
  557. wnsprintfW((LPWSTR)wszBase, ARRAYSIZE(wszBase), c_wszBaseFileFmt, wszCopy);
  558. IF_FAILEXIT(hr = pstmCopy->Write(wszBase, lstrlenW(wszBase) * sizeof(WCHAR), NULL));
  559. }
  560. else
  561. {
  562. StrCpyN(szCopy, pszUrl, ARRAYSIZE(szCopy));
  563. PathRemoveFileSpec(szCopy);
  564. wnsprintf((LPSTR)szBase, ARRAYSIZE(szBase), c_szBaseFileFmt, szCopy);
  565. IF_FAILEXIT(hr = pstmCopy->Write(szBase, lstrlen(szBase), NULL));
  566. }
  567. if (fIsUnicode)
  568. {
  569. WCHAR bom;
  570. IF_FAILEXIT(hr = pstm->Read(&bom, 2, &cb));
  571. Assert(2 == cb);
  572. }
  573. // This is an ANSI stream that we are forcing into UNICODE
  574. // This area will only occur if we are streaming a file
  575. else if (fForceUnicode)
  576. {
  577. LARGE_INTEGER pos = {0};
  578. UINT uiHtmlCodepage = 0;
  579. Assert(!fIsURL);
  580. // In order for the file name to write to the stream properly, we
  581. // must convert the stream to unicode before we copy.
  582. // Get the charset
  583. GetHtmlCharset(pstm, &pszCharset);
  584. if(pszCharset)
  585. {
  586. INETCSETINFO CSetInfo = {0};
  587. HCHARSET hCharset = NULL;
  588. if (SUCCEEDED(MimeOleFindCharset(pszCharset, &hCharset)))
  589. {
  590. if(SUCCEEDED(MimeOleGetCharsetInfo(hCharset,&CSetInfo)))
  591. uiHtmlCodepage = CSetInfo.cpiInternet;
  592. }
  593. }
  594. IF_FAILEXIT(hr = HrRewindStream(pstm));
  595. // Allocate enough to read ANSI
  596. IF_FAILEXIT(hr = HrSafeGetStreamSize(pstm, &cb));
  597. IF_NULLEXIT(MemAlloc((LPVOID*)&pszStream, cb+1));
  598. // Read in ANSI
  599. IF_FAILEXIT(hr = pstm->Read(pszStream, cb, &cbTemp));
  600. Assert(cbTemp == cb);
  601. pszStream[cb] = 0;
  602. // Alloc enough for the unicode conversion. Assume that each
  603. // ANSI char will be one unicode char
  604. IF_NULLEXIT(MemAlloc((LPVOID*)&pwszStream, (cb+1)*sizeof(WCHAR)));
  605. //Convert including null, if the fancy call fails, we should at least continue
  606. //with the old dumb way.
  607. if(!uiHtmlCodepage || FAILED(HrConvertStringToUnicode(uiHtmlCodepage, pszStream, cb+1, pwszStream, cb+1)))
  608. MultiByteToWideChar(CP_ACP, 0, pszStream, cb+1, pwszStream, cb+1);
  609. // Create a new stream
  610. IF_FAILEXIT(hr = MimeOleCreateVirtualStream(&pstmTemp));
  611. IF_FAILEXIT(hr = pstmTemp->Write(pwszStream, lstrlenW(pwszStream)*sizeof(WCHAR), &cb));
  612. IF_FAILEXIT(hr = HrRewindStream(pstmTemp));
  613. ReplaceInterface(pstm, pstmTemp);
  614. }
  615. }
  616. IF_FAILEXIT(hr = HrCopyStream(pstm, pstmCopy, &cb));
  617. IF_FAILEXIT(hr = HrRewindStream(pstmCopy));
  618. *ppstmHtml=pstmCopy;
  619. pstmCopy->AddRef();
  620. exit:
  621. ReleaseObj(pstm);
  622. ReleaseObj(pstmTemp);
  623. ReleaseObj(pstmCopy);
  624. MemFree(pszUrl);
  625. MemFree(pszStream);
  626. MemFree(pwszStream);
  627. MemFree(pwszTempUrl);
  628. MemFree(pszCharset);
  629. return hr;
  630. }