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.

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