Source code of Windows XP (NT5)
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.

1538 lines
46 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1994
  5. //
  6. // File: persist.cxx
  7. //
  8. // Contents: Implmentation of Office9 Thicket Save API
  9. //
  10. //----------------------------------------------------------------------------
  11. #include "priv.h"
  12. //#include "headers.hxx"
  13. //#include "formkrnl.hxx"
  14. #include <platform.h>
  15. #include <mlang.h>
  16. #include "resource.h"
  17. #include "impexp.h"
  18. #include "reload.h"
  19. //#include <siterc.h>
  20. #include "packager.h"
  21. #include "iehelpid.h"
  22. #include "thicket.h"
  23. #include "apithk.h"
  24. #include <mluisupp.h>
  25. #include <mshtmcid.h>
  26. #define NUM_OLE_CMDS 1
  27. #define SAVEAS_OK 0x00000001
  28. #define SAVEAS_NEVER_ASK_AGAIN 0x00000002
  29. #define CODEPAGE_UNICODE 0x000004B0
  30. #define CODEPAGE_UTF8 0x0000FDE9
  31. #define UNICODE_TEXT TEXT("Unicode")
  32. #define REGSTR_VAL_SAVEDIRECTORY TEXT("Save Directory")
  33. #define REGKEY_SAVEAS_WARNING_RESTRICTION TEXT("SOFTWARE\\Microsoft\\Internet Explorer\\Main")
  34. #define REGVALUE_SAVEAS_WARNING TEXT("NoSaveAsPOSTWarning")
  35. #define WM_WORKER_THREAD_COMPLETED WM_USER + 1000
  36. #define MAX_ENCODING_DESC_LEN 1024
  37. const static DWORD aSaveAsHelpIDs[] =
  38. {
  39. IDC_SAVE_CHARSET, IDH_CHAR_SET_SAVE_AS,
  40. 0, 0
  41. };
  42. INT_PTR CALLBACK SaveAsWarningDlgProc(HWND hDlg, UINT msg, WPARAM wParam,
  43. LPARAM lParam);
  44. HRESULT SaveToThicket( HWND hwnd, LPCTSTR pszFileName, IHTMLDocument2 *pDoc,
  45. UINT codepageSrc, UINT codepageDst,
  46. UINT iPackageStyle );
  47. HRESULT
  48. FormsGetFileName(
  49. HWND hwndOwner,
  50. LPTSTR pstrFile,
  51. int cchFile,
  52. LPARAM lCustData,
  53. DWORD *pnFilterIndex,
  54. BOOL bForceHTMLOnly);
  55. HRESULT
  56. GetFileNameFromURL( LPWSTR pwszURL, LPTSTR pszFile, DWORD cchFile);
  57. void ReportThicketError( HWND hwnd, HRESULT hr );
  58. #define DOWNLOAD_PROGRESS 0x9001
  59. #define DOWNLOAD_COMPLETE 0x9002
  60. #define THICKET_TIMER 0x9003
  61. #define THICKET_INTERVAL 1000
  62. #define MDLGMSG(psz, x) TraceMsg(0, "shd TR-MODELESS::%s %x", psz, x)
  63. static DWORD s_dwInetComVerMS = 0;
  64. static DWORD s_dwInetComVerLS = 0;
  65. struct ThicketCPInfo
  66. {
  67. UINT cpSrc;
  68. UINT cpDst;
  69. LPWSTR lpwstrDocCharSet;
  70. };
  71. class CThicketUI
  72. {
  73. public:
  74. CThicketUI(void) :
  75. _hDlg(NULL),
  76. _hWndProg(NULL),
  77. _iErrorDL(0),
  78. _hrDL(E_FAIL),
  79. #ifndef NO_MARSHALLING
  80. _pstmDoc(NULL),
  81. #else
  82. _pDoc(NULL),
  83. #endif
  84. _pszFileName(NULL),
  85. _dwDLMax(0),
  86. _codepageSrc(0),
  87. _codepageDst(0),
  88. _iPackageStyle(PACKAGE_THICKET),
  89. _fCancel(FALSE) {};
  90. ~CThicketUI(void)
  91. {
  92. #ifndef NO_MARSHALLING
  93. SAFERELEASE(_pstmDoc);
  94. #endif
  95. SAFELOCALFREE(_pszFileName);
  96. };
  97. // CThicketUI methods
  98. HRESULT SaveDocument( HWND hWnd, LPCTSTR pszFileName, IHTMLDocument2 *pDoc,
  99. UINT codepageSrc, UINT codepageDst,
  100. UINT iPackageStyle );
  101. protected:
  102. static BOOL_PTR ThicketUIDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
  103. static DWORD WINAPI ThicketUIThreadProc( LPVOID );
  104. BOOL DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
  105. HWND _hDlg;
  106. HWND _hWndProg;
  107. int _iErrorDL;
  108. HRESULT _hrDL;
  109. #ifndef UNIX
  110. IStream *_pstmDoc; // marshalled IHTMLDocument2
  111. #else
  112. IHTMLDocument2 *_pDoc;
  113. #endif
  114. LPTSTR _pszFileName;
  115. DWORD _dwDLMax;
  116. UINT _codepageSrc;
  117. UINT _codepageDst;
  118. BOOL _fThreadStarted;
  119. UINT _iPackageStyle;
  120. BOOL _fCancel;
  121. };
  122. HRESULT
  123. CThicketUI::SaveDocument( HWND hWnd, LPCTSTR pszFileName, IHTMLDocument2 *pDoc,
  124. UINT codepageSrc, UINT codepageDst,
  125. UINT iPackageStyle)
  126. {
  127. _pszFileName = StrDup(pszFileName);
  128. _codepageSrc = codepageSrc;
  129. _codepageDst = codepageDst;
  130. _iPackageStyle = iPackageStyle;
  131. #ifndef NO_MARSHALLING
  132. // We don't do anything with pDoc until we're on the worker thread,
  133. // so marshall it.
  134. _hrDL = CoMarshalInterThreadInterfaceInStream(IID_IHTMLDocument2, pDoc, &_pstmDoc);
  135. if (SUCCEEDED(_hrDL))
  136. #else
  137. _pDoc = pDoc;
  138. #endif
  139. {
  140. // Needs to be modal cuz we're going to work with pDoc on the worker thread
  141. // so we don't want the user to navigate away from it, close the window, etc.
  142. DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(IDD_SAVETHICKET),
  143. hWnd, CThicketUI::ThicketUIDlgProc, (LPARAM)this);
  144. // HWND hwnd = MLCreateDialogParamWrap(MLGetHinst(), MAKEINTRESOURCE(IDD_SAVETHICKET),
  145. // NULL, CThicketUI::ThicketUIDlgProc, (LPARAM)this);
  146. // if (!hwnd)
  147. // _hrDL = E_FAIL;
  148. }
  149. return _hrDL;
  150. }
  151. BOOL_PTR
  152. CThicketUI::ThicketUIDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  153. {
  154. BOOL fRet = FALSE;
  155. CThicketUI* ptui = NULL;
  156. if (msg == WM_INITDIALOG)
  157. {
  158. ptui = (CThicketUI*)lParam;
  159. }
  160. else
  161. ptui = (CThicketUI*)GetWindowLongPtr(hDlg, DWLP_USER);
  162. if (ptui)
  163. {
  164. fRet = ptui->DlgProc(hDlg, msg, wParam, lParam);
  165. if (msg == WM_DESTROY || msg == WM_WORKER_THREAD_COMPLETED)
  166. {
  167. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  168. fRet = TRUE;
  169. }
  170. }
  171. return fRet;
  172. }
  173. BOOL
  174. CThicketUI::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  175. {
  176. BOOL fRet = TRUE;
  177. switch (msg)
  178. {
  179. case WM_INITDIALOG:
  180. _hDlg = hDlg;
  181. _hWndProg = GetDlgItem(hDlg, IDC_THICKETPROGRESS);
  182. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  183. _hrDL = S_FALSE;
  184. #ifndef NO_MARSHALLING
  185. //_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ThicketUIThreadProc, this, 0, &_idThread);
  186. if (!(_fThreadStarted = SHQueueUserWorkItem(ThicketUIThreadProc,
  187. this,
  188. 0,
  189. (DWORD_PTR)NULL,
  190. (DWORD_PTR *)NULL,
  191. "shdocvw.dll",
  192. TPS_LONGEXECTIME)))
  193. _hrDL = E_FAIL;
  194. #else
  195. ThicketUIThreadProc((LPVOID)this);
  196. #endif
  197. if (FAILED(_hrDL))
  198. EndDialog(hDlg, 0);
  199. else
  200. {
  201. ShowWindow(hDlg, SW_SHOWNORMAL);
  202. Animate_OpenEx(GetDlgItem(hDlg, IDD_ANIMATE), HINST_THISDLL, IDA_DOWNLOAD);
  203. ShowWindow(GetDlgItem(hDlg, IDD_DOWNLOADICON), SW_HIDE);
  204. }
  205. fRet = FALSE;
  206. break;
  207. case WM_COMMAND:
  208. switch (LOWORD(wParam))
  209. {
  210. case IDCANCEL:
  211. _fCancel = TRUE;
  212. // and wait for the worker thread to quit, polling at WM_TIMER
  213. break;
  214. default:
  215. break;
  216. }
  217. break;
  218. case WM_WORKER_THREAD_COMPLETED:
  219. _hrDL = (DWORD) wParam;
  220. EndDialog(hDlg,0);
  221. break;
  222. //case WM_CLOSE:
  223. // KillTimer( hDlg, THICKET_TIMER );
  224. // _fCancel = TRUE;
  225. // while( _hrDL == S_FALSE );
  226. // break;
  227. case WM_DESTROY:
  228. _fCancel = TRUE;
  229. while( _hrDL == S_FALSE )
  230. {
  231. Sleep(0);
  232. }
  233. break;
  234. default:
  235. fRet = FALSE;
  236. }
  237. return fRet;
  238. }
  239. DWORD WINAPI CThicketUI::ThicketUIThreadProc( LPVOID ppv )
  240. {
  241. HRESULT hr = S_OK;
  242. CThicketUI* ptui = (CThicketUI *)ppv;
  243. ASSERT(ptui);
  244. hr = CoInitialize(NULL);
  245. if (SUCCEEDED(hr))
  246. {
  247. IHTMLDocument2 *pDoc = NULL;
  248. #ifndef NO_MARSHALLING
  249. hr = CoGetInterfaceAndReleaseStream( ptui->_pstmDoc, IID_IHTMLDocument2,(LPVOID*)&pDoc);
  250. // CoGetInterfaceAndReleaseStream always releases the stream
  251. ptui->_pstmDoc = NULL;
  252. #else
  253. pDoc = ptui->_pDoc;
  254. pDoc->AddRef();
  255. #endif
  256. if (SUCCEEDED(hr))
  257. {
  258. CThicketProgress tprog( ptui->_hDlg );
  259. CDocumentPackager docPkgr(ptui->_iPackageStyle);
  260. hr = S_FALSE;
  261. hr = docPkgr.PackageDocument( pDoc, ptui->_pszFileName,
  262. &ptui->_fCancel, &tprog,
  263. 0, 100,
  264. ptui->_codepageDst );
  265. pDoc->Release(); // release marshalled interface
  266. }
  267. CoUninitialize();
  268. }
  269. PostMessage(ptui->_hDlg, WM_WORKER_THREAD_COMPLETED, hr, 0);
  270. return 0;
  271. }
  272. //+------------------------------------------------------------------------
  273. //
  274. //
  275. //
  276. //-------------------------------------------------------------------------
  277. HRESULT
  278. SaveToThicket( HWND hWnd, LPCTSTR pszFileName, IHTMLDocument2 *pDoc,
  279. UINT codepageSrc, UINT codepageDst, UINT iPackageStyle )
  280. {
  281. HRESULT hr;
  282. CThicketUI* ptui;
  283. #ifdef OLD_THICKET
  284. LPTSTR lpszURL;
  285. lpszURL = bstrDocURL;
  286. const DWORD dwMaxPathLen = 24;
  287. URL_COMPONENTS urlComp;
  288. TCHAR rgchUrlPath[MAX_PATH];
  289. TCHAR rgchCanonicalUrl[MAX_URL_STRING];
  290. DWORD dwLen;
  291. dwLen = ARRAYSIZE(rgchCanonicalUrl);
  292. hr = UrlCanonicalize( lpszURL, rgchCanonicalUrl, &dwLen, 0);
  293. if (FAILED(hr))
  294. return E_FAIL;
  295. ZeroMemory(&urlComp, sizeof(urlComp));
  296. urlComp.dwStructSize = sizeof(urlComp);
  297. urlComp.lpszUrlPath = rgchUrlPath;
  298. urlComp.dwUrlPathLength = ARRAYSIZE(rgchUrlPath);
  299. hr = InternetCrackUrl(rgchCanonicalUrl, lstrlen(rgchCanonicalUrl), ICU_DECODE, &urlComp);
  300. if (FAILED(hr))
  301. return E_FAIL;
  302. // Since this is not a snap-shot, saving the doc over itself is a no-op.
  303. // This means we can avoid some nasty issues with the save-over, safe-save,
  304. // et al, by short circuiting the save here.
  305. if ( StrCmpI(pszFileName, rgchUrlPath) == 0 )
  306. {
  307. if (PathFileExists(pszFileName))
  308. return S_OK;
  309. else
  310. return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  311. }
  312. #endif //OLD_THICKET
  313. ptui = new CThicketUI;
  314. if (ptui)
  315. {
  316. hr = ptui->SaveDocument( hWnd, pszFileName, pDoc, codepageSrc, codepageDst, iPackageStyle );
  317. delete ptui;
  318. }
  319. else
  320. hr = E_OUTOFMEMORY;
  321. return hr;
  322. }
  323. //+------------------------------------------------------------------------
  324. //
  325. //
  326. //
  327. //-------------------------------------------------------------------------
  328. void SaveBrowserFile( HWND hwnd, LPUNKNOWN punk )
  329. {
  330. HRESULT hr;
  331. TCHAR szFileDst[MAX_PATH];
  332. DWORD iFilter = 1;
  333. IHTMLDocument2 *pDoc;
  334. BSTR bstrURL = NULL;
  335. ThicketCPInfo tcpi;
  336. BSTR bstrCharSet = NULL;
  337. BSTR bstrTitle = NULL;
  338. BSTR bstrMime = NULL;
  339. IOleCommandTarget *pOleCommandTarget = NULL;
  340. WCHAR *pwzExt = NULL;
  341. OLECMD pCmd[NUM_OLE_CMDS];
  342. ULONG nCmds = NUM_OLE_CMDS;
  343. BOOL bForceHTMLOnly = FALSE;
  344. static const WCHAR *wzImage = L" Image";
  345. hr = punk->QueryInterface(IID_IHTMLDocument2, (void**)&pDoc);
  346. if (FAILED(hr))
  347. goto Cleanup;
  348. if (FAILED(pDoc->get_URL( &bstrURL )))
  349. goto Cleanup;
  350. hr = pDoc->get_charset( &bstrCharSet );
  351. if (FAILED(hr))
  352. goto Cleanup;
  353. tcpi.cpSrc = CP_ACP;
  354. tcpi.lpwstrDocCharSet = bstrCharSet;
  355. // If it is an image file, then bring up trident to do the save.
  356. // APPCOMPAT: This is a crappy way to do this. We are hard-coding the
  357. // image types, so we know to put up the "Save as image" dialog.
  358. // We originally tried looking at the MIME type, but Trident returns
  359. // inconsistent MIME types to us (ex. under some platforms we get
  360. // "JPG Image" and under others we get "JPG File"!).
  361. ASSERT(bstrURL);
  362. pwzExt = bstrURL + lstrlenW(bstrURL);
  363. while (pwzExt > bstrURL && *pwzExt != L'.')
  364. {
  365. pwzExt--;
  366. }
  367. hr = pDoc->QueryInterface(IID_IOleCommandTarget,
  368. (void **)&pOleCommandTarget);
  369. if (FAILED(hr))
  370. {
  371. goto Cleanup;
  372. }
  373. if (pwzExt > bstrURL) {
  374. // Found a "dot". Now pwzExt points to what we think is the extension
  375. if (!StrCmpIW(pwzExt, L".JPG") ||
  376. !StrCmpIW(pwzExt, L".GIF") ||
  377. !StrCmpIW(pwzExt, L".BMP") ||
  378. !StrCmpIW(pwzExt, L".XBM") ||
  379. !StrCmpIW(pwzExt, L".ART") ||
  380. !StrCmpIW(pwzExt, L".PNG") ||
  381. !StrCmpIW(pwzExt, L".WMF") ||
  382. !StrCmpIW(pwzExt, L".TIFF") ||
  383. !StrCmpIW(pwzExt, L".JPEG"))
  384. {
  385. hr = pOleCommandTarget->Exec(&CGID_MSHTML, IDM_SAVEPICTURE, 0,
  386. NULL, NULL);
  387. // FEATURE: Handle a failed HR here. It is very unlikely that
  388. // this will fail, yet regular save-as code (that follows)
  389. // will succeed. We always exit out of here, so we will
  390. // never get two UI dialogs thrown at the user. We should
  391. // come up with a good scheme to propagate an error dialog
  392. // to the user. Possible scenario: low disk space causing
  393. // a fail-out.
  394. goto Cleanup;
  395. }
  396. }
  397. // IE5 RAID #54672: Save-as has problems saving pages generated by POSTs
  398. // This code is to detect if the page was generated by POST data and
  399. // warn the user that saving may not work.
  400. pCmd[0].cmdID = SHDVID_PAGEFROMPOSTDATA;
  401. hr = pOleCommandTarget->QueryStatus(&CGID_ShellDocView, nCmds, pCmd, NULL);
  402. if (FAILED(hr))
  403. {
  404. goto Cleanup;
  405. }
  406. if (pCmd[0].cmdf & OLECMDF_LATCHED)
  407. {
  408. HKEY hkeySaveAs = 0;
  409. DWORD dwValue = 0;
  410. DWORD dwSize = 0;
  411. INT_PTR iFlags = 0;
  412. bForceHTMLOnly = TRUE;
  413. if (RegOpenKeyEx(HKEY_CURRENT_USER,
  414. REGKEY_SAVEAS_WARNING_RESTRICTION, 0,
  415. KEY_READ, &hkeySaveAs) == ERROR_SUCCESS)
  416. {
  417. dwSize = sizeof(DWORD);
  418. if (RegQueryValueEx(hkeySaveAs, REGVALUE_SAVEAS_WARNING, NULL,
  419. NULL, (LPBYTE)&dwValue, &dwSize) == ERROR_SUCCESS)
  420. {
  421. if (dwValue)
  422. {
  423. // restriction set, don't show dialog
  424. RegCloseKey(hkeySaveAs);
  425. goto Continue;
  426. }
  427. }
  428. RegCloseKey(hkeySaveAs);
  429. }
  430. iFlags = DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(DLG_SAVEAS_WARNING),
  431. hwnd, SaveAsWarningDlgProc, (LPARAM)0);
  432. if (!(iFlags & SAVEAS_OK))
  433. {
  434. goto Cleanup;
  435. }
  436. if (iFlags & SAVEAS_NEVER_ASK_AGAIN)
  437. {
  438. HKEY hkey = 0;
  439. DWORD dwNeverAsk = 1;
  440. if (RegOpenKeyEx(HKEY_CURRENT_USER,
  441. REGKEY_SAVEAS_WARNING_RESTRICTION, 0,
  442. KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS)
  443. {
  444. RegSetValueEx(hkey, REGVALUE_SAVEAS_WARNING, 0, REG_DWORD,
  445. (CONST BYTE *)&dwNeverAsk,
  446. sizeof(dwNeverAsk));
  447. RegCloseKey(hkey);
  448. }
  449. }
  450. }
  451. Continue:
  452. // Suggest a file name
  453. szFileDst[0] = 0;
  454. // Our favorite candidate is the title, fall back on the file name.
  455. hr = pDoc->get_title(&bstrTitle);
  456. if (SUCCEEDED(hr) && lstrlenW(bstrTitle))
  457. {
  458. StrCpyN(szFileDst, bstrTitle, ARRAYSIZE(szFileDst));
  459. }
  460. else
  461. hr = GetFileNameFromURL(bstrURL, szFileDst, ARRAYSIZE(szFileDst));
  462. if (FAILED(hr))
  463. goto Cleanup;
  464. PathCleanupSpec(NULL, szFileDst);
  465. hr = FormsGetFileName(hwnd, szFileDst, ARRAYSIZE(szFileDst),
  466. (LONG_PTR)&tcpi, &iFilter, bForceHTMLOnly);
  467. if (hr==S_OK)
  468. hr = SaveToThicket( hwnd, szFileDst, pDoc, tcpi.cpSrc, tcpi.cpDst, iFilter);
  469. Cleanup:
  470. if (FAILED(hr))
  471. ReportThicketError(hwnd, hr);
  472. if (pOleCommandTarget)
  473. pOleCommandTarget->Release();
  474. if (pDoc)
  475. pDoc->Release();
  476. if (bstrURL)
  477. SysFreeString(bstrURL);
  478. if (bstrCharSet)
  479. SysFreeString(bstrCharSet);
  480. if (bstrTitle)
  481. SysFreeString(bstrTitle);
  482. return;
  483. }
  484. void ReportThicketError( HWND hwnd, HRESULT hr )
  485. {
  486. LPTSTR lpstrMsg = NULL;
  487. switch (hr)
  488. {
  489. case HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY):
  490. case E_OUTOFMEMORY:
  491. lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRMEM);
  492. break;
  493. case E_ACCESSDENIED:
  494. case STG_E_ACCESSDENIED:
  495. lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRACC);
  496. break;
  497. case HRESULT_FROM_WIN32(ERROR_DISK_FULL):
  498. case STG_E_MEDIUMFULL:
  499. lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRFULL);
  500. break;
  501. case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
  502. lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRFNF);
  503. break;
  504. case E_ABORT:
  505. // Ray says we don't want a canceled message box.
  506. //lpstrMsg = MAKEINTRESOURCE(IDS_THICKETABORT);
  507. break;
  508. case E_FAIL:
  509. default:
  510. lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRMISC);
  511. break;
  512. }
  513. if ( lpstrMsg )
  514. {
  515. MLShellMessageBox(
  516. hwnd,
  517. lpstrMsg,
  518. MAKEINTRESOURCE(IDS_THICKETERRTITLE),
  519. MB_OK | MB_ICONERROR);
  520. }
  521. }
  522. //+--------------------------------------------------------------------------
  523. //
  524. // File: file.cxx
  525. //
  526. // Contents: Import/export dialog helpers
  527. //
  528. // History: 16-May-95 RobBear Taken from formtool
  529. //
  530. //---------------------------------------------------------------------------
  531. const CHAR c_szNT4ResourceLocale[] = ".DEFAULT\\Control Panel\\International";
  532. const CHAR c_szWin9xResourceLocale[] = ".Default\\Control Panel\\desktop\\ResourceLocale";
  533. const CHAR c_szLocale[] = "Locale";
  534. LANGID
  535. MLGetShellLanguage()
  536. {
  537. LANGID lidShell = 0;
  538. // FEATURE: this fn is copied from shlwapi. there really should be a
  539. // shlwapi export. if MLGetUILanguage has any merit, then
  540. // MLGetShellLanguage has merit as well.
  541. if (IsOS(OS_WIN2000ORGREATER))
  542. {
  543. static LANGID (CALLBACK* pfnGetUserDefaultUILanguage)(void) = NULL;
  544. if (pfnGetUserDefaultUILanguage == NULL)
  545. {
  546. HMODULE hmod = GetModuleHandle(TEXT("KERNEL32"));
  547. if (hmod)
  548. pfnGetUserDefaultUILanguage = (LANGID (CALLBACK*)(void))GetProcAddress(hmod, "GetUserDefaultUILanguage");
  549. }
  550. if (pfnGetUserDefaultUILanguage)
  551. lidShell = pfnGetUserDefaultUILanguage();
  552. }
  553. else
  554. {
  555. CHAR szLangID[12];
  556. DWORD cb, dwRet;
  557. cb = sizeof(szLangID) - 2*sizeof(szLangID[0]); // avoid 2 byte buffer overrun
  558. if (IsOS(OS_NT))
  559. dwRet = SHGetValueA(HKEY_USERS, c_szNT4ResourceLocale, c_szLocale, NULL, szLangID + 2, &cb);
  560. else
  561. dwRet = SHGetValueA(HKEY_USERS, c_szWin9xResourceLocale, NULL, NULL, szLangID + 2, &cb);
  562. if (ERROR_SUCCESS == dwRet)
  563. {
  564. // IE uses a string rep of the hex value
  565. szLangID[0] = '0';
  566. szLangID[1] = 'x';
  567. StrToIntExA(szLangID, STIF_SUPPORT_HEX, (LPINT)&lidShell);
  568. }
  569. }
  570. return lidShell;
  571. }
  572. /*
  573. * Stolen from Trident's src\core\cdutil\file.cxx
  574. */
  575. // Hook procedure for open file dialog.
  576. UINT_PTR APIENTRY SaveOFNHookProc(HWND hdlg,
  577. UINT uiMsg,
  578. WPARAM wParam,
  579. LPARAM lParam)
  580. {
  581. ULONG i, iCurSel;
  582. BOOL bFoundEncoding = FALSE;
  583. WCHAR wzEncoding[MAX_ENCODING_DESC_LEN];
  584. switch (uiMsg)
  585. {
  586. // Populate the dropdown.
  587. case WM_INITDIALOG:
  588. {
  589. HRESULT hr;
  590. LPOPENFILENAME pofn = (LPOPENFILENAME)lParam;
  591. ThicketCPInfo *ptcpi = (ThicketCPInfo *)pofn->lCustData;
  592. IMultiLanguage2 *pMultiLanguage = NULL;
  593. IEnumCodePage *pEnumCodePage = NULL;
  594. //UINT codepageDefault = ptcpi->cp;
  595. MIMECSETINFO csetInfo;
  596. LANGID langid;
  597. #ifdef UNIX
  598. SetWindowLongPtr(hdlg, DWLP_USER, (LONG_PTR)ptcpi);
  599. #endif /* UNIX */
  600. hr = CoCreateInstance(
  601. CLSID_CMultiLanguage,
  602. NULL,
  603. CLSCTX_INPROC_SERVER,
  604. IID_IMultiLanguage2,
  605. (void**)&pMultiLanguage);
  606. if (hr)
  607. break;
  608. hr = pMultiLanguage->GetCharsetInfo(ptcpi->lpwstrDocCharSet,&csetInfo);
  609. if (hr)
  610. break;
  611. #ifndef UNIX
  612. // the shell combobox where this stuff shows up
  613. // doesn't know how to fontlink... so we have
  614. // to stay in the shell's codepage
  615. langid = MLGetShellLanguage();
  616. #else
  617. langid = GetSystemDefaultLangID();
  618. #endif /* UNIX */
  619. if (pMultiLanguage->EnumCodePages( MIMECONTF_SAVABLE_BROWSER | MIMECONTF_VALID,
  620. langid,
  621. &pEnumCodePage) == S_OK)
  622. {
  623. MIMECPINFO cpInfo;
  624. ULONG ccpInfo;
  625. UINT cpDefault;
  626. if (pMultiLanguage->GetCodePageInfo(csetInfo.uiInternetEncoding, langid, &cpInfo) == S_OK &&
  627. !(cpInfo.dwFlags & MIMECONTF_SAVABLE_BROWSER))
  628. {
  629. // If the codepage selected is not savable (eg JP_AUTO),
  630. // use the family codepage.
  631. cpDefault = cpInfo.uiFamilyCodePage;
  632. }
  633. else
  634. cpDefault = csetInfo.uiInternetEncoding;
  635. ptcpi->cpSrc = csetInfo.uiInternetEncoding;
  636. if (cpDefault == CODEPAGE_UNICODE &&
  637. pofn->nFilterIndex == PACKAGE_MHTML) {
  638. cpDefault = CODEPAGE_UTF8;
  639. }
  640. for (i = 0; pEnumCodePage->Next(1, &cpInfo, &ccpInfo) == S_OK; ++i)
  641. {
  642. TCHAR *lpszDesc;
  643. INT_PTR iIdx;
  644. if (cpInfo.uiCodePage == CODEPAGE_UNICODE &&
  645. pofn->nFilterIndex == PACKAGE_MHTML) {
  646. i--;
  647. continue;
  648. }
  649. if (cpDefault == cpInfo.uiCodePage)
  650. {
  651. StrCpyNW(wzEncoding, cpInfo.wszDescription,
  652. lstrlen(cpInfo.wszDescription) + 1);
  653. bFoundEncoding = TRUE;
  654. }
  655. lpszDesc = cpInfo.wszDescription;
  656. iIdx = SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  657. CB_ADDSTRING, 0,
  658. (LPARAM)lpszDesc);
  659. SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  660. CB_SETITEMDATA, iIdx,
  661. (LPARAM)cpInfo.uiCodePage);
  662. }
  663. if (bFoundEncoding)
  664. {
  665. INT_PTR iIndex = 0;
  666. iIndex = SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  667. CB_FINDSTRINGEXACT, -1,
  668. (LPARAM)wzEncoding);
  669. SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_SETCURSEL,
  670. (WPARAM)iIndex, 0);
  671. }
  672. else
  673. {
  674. // No encoding found! Bad error. Recover by selecting
  675. // the first one.
  676. SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_SETCURSEL,
  677. 0, 0);
  678. }
  679. }
  680. SAFERELEASE(pEnumCodePage);
  681. SAFERELEASE(pMultiLanguage);
  682. break;
  683. }
  684. #ifdef UNIX
  685. case WM_COMMAND:
  686. {
  687. switch (GET_WM_COMMAND_ID(wParam,lParam))
  688. {
  689. case IDOK:
  690. {
  691. ThicketCPInfo *ptcpi = (ThicketCPInfo *)GetWindowLongPtr(hdlg,DWLP_USER);
  692. ptcpi->cpDst = CP_ACP;
  693. iCurSel = (int) SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETCURSEL, 0, 0);
  694. ptcpi->cpDst =
  695. (UINT)SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
  696. (WPARAM)iCurSel, (LPARAM)0);
  697. // To spare us from re-instantiating MLANG, we'll set the src and dest
  698. // to CP_ACP if no change is indicated.
  699. if (ptcpi->cpDst == ptcpi->cpSrc)
  700. ptcpi->cpDst = ptcpi->cpSrc = CP_ACP;
  701. }
  702. break;
  703. }
  704. }
  705. break;
  706. #endif /* UNIX */
  707. case WM_NOTIFY:
  708. {
  709. LPOFNOTIFY phdr = (LPOFNOTIFY)lParam;
  710. switch (phdr->hdr.code)
  711. {
  712. case CDN_FILEOK:
  713. {
  714. LPOPENFILENAME pofn = (LPOPENFILENAME)phdr->lpOFN;
  715. ThicketCPInfo *ptcpi = (ThicketCPInfo *)pofn->lCustData;
  716. iCurSel = (int) SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETCURSEL, 0, 0);
  717. ptcpi->cpDst = //*(UINT *)phdr->lpOFN->lCustData =
  718. (UINT)SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
  719. (WPARAM)iCurSel, (LPARAM)0);
  720. }
  721. // HACK! This case is implemented to implement a hack for
  722. // IE5 RAID #60672. MIMEOLE cannot save UNICODE encoding,
  723. // so when the user selects MHTML saves, we should remove
  724. // this option. This code should be removed when MIMEOLE
  725. // fixes their bug (targeted for NT5 RTM). Contact SBailey
  726. // for the status of this.
  727. case CDN_TYPECHANGE:
  728. {
  729. LPOPENFILENAME pofn = (LPOPENFILENAME)phdr->lpOFN;
  730. ThicketCPInfo *ptcpi = (ThicketCPInfo *)pofn->lCustData;
  731. UINT uiCPSel, uiCP;
  732. int iType = pofn->nFilterIndex;
  733. UINT iCount;
  734. int iCurSel;
  735. int iSet = -1;
  736. if (iType == PACKAGE_MHTML)
  737. {
  738. iCurSel = (int)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_GETCURSEL, 0, 0);
  739. uiCPSel = (UINT)SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
  740. (WPARAM)iCurSel, (LPARAM)0);
  741. // If you selected unicode, make it look like you
  742. // really selected UTF-8
  743. if (uiCPSel == CODEPAGE_UNICODE)
  744. {
  745. uiCPSel = CODEPAGE_UTF8;
  746. }
  747. i = (int) SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  748. CB_FINDSTRINGEXACT,
  749. (WPARAM)0,
  750. (LPARAM)UNICODE_TEXT);
  751. if (i != CB_ERR)
  752. {
  753. SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  754. CB_DELETESTRING, i, (LPARAM)0);
  755. }
  756. iCount = (int)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  757. CB_GETCOUNT, 0, 0);
  758. // Set selected item back
  759. for (i = 0; i < iCount; i++)
  760. {
  761. uiCP = (UINT)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
  762. (WPARAM)i, (LPARAM)0);
  763. if (uiCP == uiCPSel)
  764. {
  765. iSet = i;
  766. }
  767. }
  768. if (iSet != 0xffffffff)
  769. {
  770. SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_SETCURSEL,
  771. (WPARAM)iSet, (LPARAM)0);
  772. }
  773. }
  774. else
  775. {
  776. // Store current selection
  777. iCurSel = (int)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_GETCURSEL, 0, 0);
  778. uiCPSel = (UINT)SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
  779. (WPARAM)iCurSel, (LPARAM)0);
  780. // Add unicode back in, if it was removed
  781. i = (int) SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  782. CB_FINDSTRINGEXACT,
  783. (WPARAM)0,
  784. (LPARAM)UNICODE_TEXT);
  785. if (i == CB_ERR) {
  786. // Unicode does not exist, add it back in
  787. i = (int) SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  788. CB_ADDSTRING, 0,
  789. (LPARAM)UNICODE_TEXT);
  790. SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  791. CB_SETITEMDATA, i,
  792. (LPARAM)CODEPAGE_UNICODE);
  793. // Make sure the same encoding selected before is
  794. // still selected.
  795. iCount = (int)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  796. CB_GETCOUNT, 0, 0);
  797. for (i = 0; i < iCount; i++)
  798. {
  799. uiCP = (UINT)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
  800. (WPARAM)i, (LPARAM)0);
  801. if (uiCP == uiCPSel)
  802. {
  803. iSet = i;
  804. }
  805. }
  806. if (iCurSel != 0xffffffff)
  807. {
  808. SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_SETCURSEL,
  809. (WPARAM)iSet, (LPARAM)0);
  810. }
  811. }
  812. }
  813. }
  814. break;
  815. }
  816. }
  817. break;
  818. case WM_HELP:
  819. {
  820. SHWinHelpOnDemandWrap((HWND)((LPHELPINFO) lParam)->hItemHandle,
  821. c_szHelpFile,
  822. HELP_WM_HELP,
  823. (DWORD_PTR)aSaveAsHelpIDs);
  824. }
  825. break;
  826. case WM_CONTEXTMENU:
  827. {
  828. SHWinHelpOnDemandWrap((HWND) wParam,
  829. c_szHelpFile,
  830. HELP_CONTEXTMENU,
  831. (DWORD_PTR)aSaveAsHelpIDs);
  832. }
  833. break;
  834. }
  835. return (FALSE);
  836. }
  837. //
  838. // Protect the naive users from themselves, if somebody enters a filename
  839. // of microsoft.com when saving http://www.microsoft.com we don't want
  840. // to save a .COM file since this will be interpreted as an executable.
  841. // bad things will happen
  842. //
  843. void CleanUpFilename(LPTSTR pszFile, int iPackageStyle)
  844. {
  845. //
  846. // If we find .COM as the file extension replace it with the file extension
  847. // of the filetype they are saving the file as
  848. //
  849. LPTSTR pszExt = PathFindExtension(pszFile);
  850. ASSERT(pszExt);
  851. if (StrCmpI(pszExt, TEXT(".COM")) == 0) // REVIEW any other file types???
  852. {
  853. //
  854. // Map the package style to a default extension. NOTE this relies on
  855. // the fact that the filter index maps to the PACKAGE style enum
  856. // (as does the rest of the thicket code).
  857. //
  858. switch (iPackageStyle)
  859. {
  860. case PACKAGE_THICKET:
  861. case PACKAGE_HTML:
  862. StrCatBuff(pszFile, TEXT(".htm"), MAX_PATH);
  863. break;
  864. case PACKAGE_MHTML:
  865. StrCatBuff(pszFile, TEXT(".mht"), MAX_PATH);
  866. break;
  867. case PACKAGE_TEXT:
  868. StrCatBuff(pszFile, TEXT(".txt"), MAX_PATH);
  869. break;
  870. default:
  871. ASSERT(FALSE); // Unknown package type
  872. break;
  873. }
  874. }
  875. }
  876. //+---------------------------------------------------------------------------
  877. //
  878. // Function: FormsGetFileName
  879. //
  880. // Synopsis: Gets a file name using either the GetOpenFileName or
  881. // GetSaveFileName functions.
  882. //
  883. // Arguments: [fSaveFile] -- TRUE means use GetSaveFileName
  884. // FALSE means use GetOpenFileName
  885. //
  886. // [idFilterRes] -- The string resource specifying text in the
  887. // dialog box. It must have the
  888. // following format:
  889. // Note: the string has to be _one_ contiguous string.
  890. // The example is broken up to make it fit
  891. // on-screen. The verical bar ("pipe") characters
  892. // are changed to '\0'-s on the fly.
  893. // This allows the strings to be localized
  894. // using Espresso.
  895. //
  896. // IDS_FILENAMERESOURCE, "Save Dialog As| // the title
  897. // odg| // default extension
  898. // Forms3 Dialog (*.odg)| // pairs of filter strings
  899. // *.odg|
  900. // Any File (*.*)|
  901. // *.*|"
  902. //
  903. // [pstrFile] -- Buffer for file name.
  904. // [cchFile] -- Size of buffer in characters.
  905. //
  906. // Modifies: [pstrFile]
  907. //
  908. //----------------------------------------------------------------------------
  909. #ifdef _MAC
  910. extern "C" {
  911. char * __cdecl _p2cstr(unsigned char *);
  912. }
  913. #endif
  914. #define CHAR_DOT TEXT('.')
  915. #define CHAR_DOT_REPLACEMENT TEXT('_')
  916. void ReplaceDotsInFileName(LPTSTR pszFileName)
  917. {
  918. ASSERT(pszFileName);
  919. while (*pszFileName)
  920. {
  921. if (*pszFileName == CHAR_DOT)
  922. {
  923. *pszFileName = CHAR_DOT_REPLACEMENT;
  924. }
  925. pszFileName++;
  926. }
  927. }
  928. HRESULT
  929. FormsGetFileName(
  930. HWND hwndOwner,
  931. LPTSTR pstrFile,
  932. int cchFile,
  933. LPARAM lCustData,
  934. DWORD *pnFilterIndex,
  935. BOOL bForceHTMLOnly)
  936. {
  937. HRESULT hr = S_OK;
  938. BOOL fOK;
  939. DWORD dwCommDlgErr;
  940. LPTSTR pstr;
  941. OPENFILENAME ofn;
  942. TCHAR achBuffer[4096]; // Max. size of a string resource
  943. TCHAR * cp;
  944. TCHAR * pstrExt;
  945. int cbBuffer;
  946. TCHAR achPath[MAX_PATH];
  947. DWORD dwType = REG_SZ;
  948. DWORD cbData = MAX_PATH * sizeof(TCHAR);
  949. int idFilterRes;
  950. // Initialize ofn struct
  951. memset(&ofn, 0, sizeof(ofn));
  952. ofn.lStructSize = sizeof(ofn);
  953. ofn.hwndOwner = hwndOwner;
  954. ofn.Flags = OFN_FILEMUSTEXIST |
  955. OFN_PATHMUSTEXIST |
  956. OFN_OVERWRITEPROMPT |
  957. OFN_HIDEREADONLY |
  958. #ifndef UNIX
  959. OFN_NOCHANGEDIR |
  960. OFN_EXPLORER;
  961. #else
  962. OFN_NOCHANGEDIR;
  963. #endif /* UNIX */
  964. ofn.lpfnHook = NULL;
  965. ofn.nMaxFile = cchFile;
  966. ofn.lCustData = lCustData;
  967. ofn.lpstrFile = pstrFile;
  968. #ifndef NO_IME
  969. // We add an extra control to the save file dialog.
  970. if (lCustData)
  971. {
  972. ofn.Flags |= OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
  973. ofn.lpfnHook = SaveOFNHookProc;
  974. ofn.lpTemplateName = MAKEINTRESOURCE(IDD_ADDTOSAVE_DIALOG);
  975. ofn.hInstance = g_hinst;
  976. }
  977. #endif
  978. //
  979. // Find the extension and set the filter index based on what the
  980. // extension is. After these loops pstrExt will either be NULL if
  981. // we didn't find an extension, or will point to the extension starting
  982. // at the '.'
  983. pstrExt = pstrFile;
  984. while (*pstrExt)
  985. pstrExt++;
  986. while ( pstrExt >= pstrFile )
  987. {
  988. if( *pstrExt == TEXT('.') )
  989. break;
  990. pstrExt--;
  991. }
  992. if( pstrExt < pstrFile )
  993. pstrExt = NULL;
  994. // Load the filter spec.
  995. // FEATURE: Convert table to stringtable for localization
  996. if ( SHRestricted2W( REST_NoBrowserSaveWebComplete, NULL, 0 ) )
  997. idFilterRes = IDS_NOTHICKET_SAVE;
  998. else if ( s_dwInetComVerMS != 0xFFFFFFFF )
  999. {
  1000. #ifndef UNIX
  1001. if (s_dwInetComVerMS == 0)
  1002. {
  1003. TCHAR szPath[MAX_PATH];
  1004. GetSystemDirectory( szPath, MAX_PATH );
  1005. StrCatBuff( szPath, TEXT("\\INETCOMM.DLL"), MAX_PATH );
  1006. if (FAILED(GetVersionFromFile(szPath, &s_dwInetComVerMS, &s_dwInetComVerLS)))
  1007. s_dwInetComVerMS = 0xFFFFFFFF;
  1008. }
  1009. if (s_dwInetComVerMS >= 0x50000 && s_dwInetComVerMS != 0xFFFFFFFF)
  1010. idFilterRes = IDS_THICKET_SAVE;
  1011. else
  1012. idFilterRes = IDS_NOMHTML_SAVE;
  1013. #else
  1014. // on UNIX we don't have inetcomm.dll if oe is not installed
  1015. {
  1016. HINSTANCE hInetComm = NULL;
  1017. if ((hInetComm = LoadLibrary(TEXT("INETCOMM.DLL"))))
  1018. {
  1019. idFilterRes = IDS_THICKET_SAVE;
  1020. FreeLibrary(hInetComm);
  1021. }
  1022. else
  1023. idFilterRes = IDS_NOMHTML_SAVE;
  1024. }
  1025. #endif
  1026. }
  1027. else
  1028. idFilterRes = IDS_THICKET_SAVE;
  1029. cbBuffer = MLLoadShellLangString(idFilterRes, achBuffer, ARRAYSIZE(achBuffer));
  1030. ASSERT(cbBuffer > 0);
  1031. if ( ! cbBuffer )
  1032. return E_FAIL;
  1033. ofn.lpstrTitle = achBuffer;
  1034. for ( cp = achBuffer; *cp; cp++ )
  1035. {
  1036. if ( *cp == TEXT('|') )
  1037. {
  1038. *cp = TEXT('\0');
  1039. }
  1040. }
  1041. ASSERT(ofn.lpstrTitle);
  1042. // Default extension is second string.
  1043. pstr = (LPTSTR) ofn.lpstrTitle;
  1044. while (*pstr++)
  1045. {
  1046. }
  1047. // N.B. (johnv) Here we assume that filter index one corresponds with the default
  1048. // extension, otherwise we would have to introduce a default filter index into
  1049. // the resource string.
  1050. ofn.nFilterIndex = ((pnFilterIndex)? *pnFilterIndex : 1);
  1051. ofn.lpstrDefExt = pstr;
  1052. // Filter is third string.
  1053. while(*pstr++)
  1054. {
  1055. }
  1056. ofn.lpstrFilter = pstr;
  1057. // Try to match the extension with an entry in the filter list
  1058. // If we match, remove the extension from the incoming path string,
  1059. // set the default extension to the one we found, and appropriately
  1060. // set the filter index.
  1061. if (pstrExt && !bForceHTMLOnly)
  1062. {
  1063. // N.B. (johnv) We are searching more than we need to.
  1064. int iIndex = 0;
  1065. const TCHAR* pSearch = ofn.lpstrFilter;
  1066. while( pSearch )
  1067. {
  1068. if( StrStr( pSearch, pstrExt ) )
  1069. {
  1070. ofn.nFilterIndex = (iIndex / 2) + 1;
  1071. ofn.lpstrDefExt = pstrExt + 1;
  1072. // Remove the extension from the file name we pass in
  1073. *pstrExt = TEXT('\0');
  1074. break;
  1075. }
  1076. pSearch += lstrlen(pSearch);
  1077. if( pSearch[1] == 0 )
  1078. break;
  1079. pSearch++;
  1080. iIndex++;
  1081. }
  1082. }
  1083. // Suggest HTML Only as default save-type
  1084. if (bForceHTMLOnly)
  1085. {
  1086. // NOTE: These are hard-coded indices based on shdoclc.rc's
  1087. // IDS_THICKET_SAVE, IDS_NOMHTML_SAVE, IDS_NOTHICKET_SAVE ordering.
  1088. // This saves us the perf hit of doing string comparisons to find
  1089. // HTML only
  1090. switch (idFilterRes)
  1091. {
  1092. case IDS_NOTHICKET_SAVE:
  1093. ofn.nFilterIndex = 1;
  1094. break;
  1095. case IDS_NOMHTML_SAVE:
  1096. ofn.nFilterIndex = 2;
  1097. break;
  1098. default:
  1099. ASSERT(idFilterRes == IDS_THICKET_SAVE);
  1100. ofn.nFilterIndex = 3;
  1101. break;
  1102. }
  1103. }
  1104. if ( SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_MAIN, REGSTR_VAL_SAVEDIRECTORY,
  1105. &dwType, achPath, &cbData) != ERROR_SUCCESS ||
  1106. !PathFileExists(achPath))
  1107. {
  1108. SHGetSpecialFolderPath(hwndOwner, achPath, CSIDL_PERSONAL, FALSE);
  1109. }
  1110. ofn.lpstrInitialDir = achPath;
  1111. // We don't want to suggest dots in the filename
  1112. ReplaceDotsInFileName(pstrFile);
  1113. // Now, at last, we're ready to call the save file dialog
  1114. fOK = GetSaveFileName(&ofn);
  1115. // if working with the abbreviated format list, adjust the index
  1116. if (idFilterRes == IDS_NOTHICKET_SAVE)
  1117. ofn.nFilterIndex += 2;
  1118. else if ( idFilterRes == IDS_NOMHTML_SAVE && ofn.nFilterIndex > 1 )
  1119. ofn.nFilterIndex += 1;
  1120. if (fOK)
  1121. {
  1122. //
  1123. // Protect the naive users from themselves, if somebody enters a filename
  1124. // of microsoft.com when saving http://www.microsoft.com we don't want
  1125. // to save a .COM file since this will be interpreted as an executable.
  1126. // bad things will happen
  1127. //
  1128. CleanUpFilename(pstrFile, ofn.nFilterIndex);
  1129. TCHAR *lpszFileName;
  1130. StrCpyN( achPath, pstrFile, ARRAYSIZE(achPath) );
  1131. lpszFileName = PathFindFileName( achPath );
  1132. *lpszFileName = 0;
  1133. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_MAIN, REGSTR_VAL_SAVEDIRECTORY,
  1134. REG_SZ, achPath, (lstrlen(achPath) * sizeof(TCHAR)));
  1135. if (pnFilterIndex)
  1136. *pnFilterIndex = ofn.nFilterIndex;
  1137. if (ofn.nFilterIndex != PACKAGE_MHTML)
  1138. {
  1139. // we can only do this if we're not packaging MHTML
  1140. // because MHTML requires that we tag with the explicit
  1141. // charset, even if it was the default. unlike thicket
  1142. // which inherits the charset from the original document,
  1143. // MHTML must be explicitly tagged or else some system
  1144. // charset tags will sneak in.
  1145. ThicketCPInfo * ptcpi = (ThicketCPInfo *)lCustData;
  1146. // To spare us from re-instantiating MLANG, we'll set the src and dest
  1147. // to CP_ACP if no change is indicated.
  1148. if (ptcpi->cpDst == ptcpi->cpSrc)
  1149. ptcpi->cpDst = ptcpi->cpSrc = CP_ACP;
  1150. }
  1151. }
  1152. else
  1153. {
  1154. #ifndef WINCE
  1155. dwCommDlgErr = CommDlgExtendedError();
  1156. if (dwCommDlgErr)
  1157. {
  1158. hr = HRESULT_FROM_WIN32(dwCommDlgErr);
  1159. }
  1160. else
  1161. {
  1162. hr = S_FALSE;
  1163. }
  1164. #else // WINCE
  1165. hr = E_FAIL;
  1166. #endif // WINCE
  1167. }
  1168. return hr;
  1169. }
  1170. HRESULT
  1171. GetFileNameFromURL( LPWSTR pwszURL, LPTSTR pszFile, DWORD cchFile)
  1172. {
  1173. HRESULT hr = S_OK;
  1174. PARSEDURLW puw = {0};
  1175. int cchUrl;
  1176. cchUrl = SysStringLen(pwszURL);
  1177. if (cchUrl)
  1178. {
  1179. puw.cbSize = sizeof(PARSEDURLW);
  1180. if (SUCCEEDED(ParseURLW(pwszURL, &puw)))
  1181. {
  1182. OLECHAR *pwchBookMark;
  1183. DWORD dwSize;
  1184. INTERNET_CACHE_ENTRY_INFOW ceiT;
  1185. LPINTERNET_CACHE_ENTRY_INFOW pcei = NULL;
  1186. // Temporarily, null out the '#' in the url
  1187. pwchBookMark = StrRChrW(puw.pszSuffix, NULL,'#');
  1188. if (pwchBookMark)
  1189. {
  1190. *pwchBookMark = 0;
  1191. }
  1192. dwSize = sizeof(INTERNET_CACHE_ENTRY_INFO);
  1193. if ( !GetUrlCacheEntryInfoW( pwszURL, &ceiT, &dwSize ) &&
  1194. GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
  1195. (pcei = (LPINTERNET_CACHE_ENTRY_INFOW)new BYTE[dwSize]) != NULL &&
  1196. GetUrlCacheEntryInfoW( pwszURL, pcei, &dwSize ) )
  1197. {
  1198. StrCpyN(pszFile, PathFindFileName(pcei->lpszLocalFileName), cchFile);
  1199. PathUndecorate(pszFile);
  1200. }
  1201. if(pcei)
  1202. delete[] pcei;
  1203. if (pwchBookMark)
  1204. *pwchBookMark = '#';
  1205. if ( !pszFile[0] )
  1206. {
  1207. OLECHAR *pwchQuery;
  1208. TCHAR szFileT[MAX_PATH];
  1209. // Temporarily, null out the '?' in the url
  1210. pwchQuery = StrRChrW(puw.pszSuffix, NULL,'?');
  1211. if (pwchQuery)
  1212. {
  1213. *pwchQuery = 0;
  1214. }
  1215. // IE5 bug 15055 - http://my.excite.com/?uid=B56E4E2D34DF3FED.save_uid
  1216. // fails to save because we were passing "my.excite.com/" as the file
  1217. // name to the file dialog. It doesn't like this.
  1218. if (!pwchQuery || (pwchQuery[-1] != '/' && pwchQuery[-1] != '\\'))
  1219. {
  1220. dwSize = ARRAYSIZE(szFileT);
  1221. StrCpyN(szFileT, PathFindFileName(puw.pszSuffix), dwSize);
  1222. if ( !InternetCanonicalizeUrl( szFileT, pszFile, &dwSize, ICU_DECODE | ICU_NO_ENCODE) )
  1223. StrCpyN(pszFile, szFileT, cchFile);
  1224. pszFile[cchFile - 1] = 0;
  1225. }
  1226. if (pwchQuery)
  1227. *pwchQuery = '?';
  1228. }
  1229. }
  1230. }
  1231. if (!pszFile[0])
  1232. {
  1233. MLLoadString(IDS_UNTITLED, pszFile, cchFile);
  1234. }
  1235. return hr;
  1236. }
  1237. INT_PTR CALLBACK SaveAsWarningDlgProc(HWND hDlg, UINT msg, WPARAM wParam,
  1238. LPARAM lParam)
  1239. {
  1240. BOOL fRet = FALSE;
  1241. HRESULT hr = S_OK;
  1242. int iFlags = 0;
  1243. INT_PTR bChecked = 0;
  1244. switch (msg)
  1245. {
  1246. case WM_INITDIALOG:
  1247. MessageBeep(MB_ICONEXCLAMATION);
  1248. fRet = TRUE;
  1249. case WM_COMMAND:
  1250. bChecked = SendDlgItemMessage(hDlg, IDC_SAVEAS_WARNING_CB,
  1251. BM_GETCHECK, 0, 0 );
  1252. iFlags = (bChecked) ? (SAVEAS_NEVER_ASK_AGAIN) : (0);
  1253. switch (LOWORD(wParam))
  1254. {
  1255. case IDYES:
  1256. iFlags |= SAVEAS_OK;
  1257. // fall through
  1258. case IDNO:
  1259. EndDialog(hDlg, iFlags);
  1260. fRet = TRUE;
  1261. break;
  1262. }
  1263. default:
  1264. fRet = FALSE;
  1265. }
  1266. return fRet;
  1267. }