thicket.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 48k
Category:

Windows Kernel

Development Platform:

Visual C++

  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.     
  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.     // BUGBUG: 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.             // BUGBUG: 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.         
  438.         if (iFlags & SAVEAS_NEVER_ASK_AGAIN)
  439.         {
  440.             HKEY                 hkey = 0;
  441.             DWORD                dwNeverAsk = 1;
  442.             if (RegOpenKeyEx(HKEY_CURRENT_USER,
  443.                              REGKEY_SAVEAS_WARNING_RESTRICTION, 0,
  444.                              KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS)
  445.             {
  446.                 RegSetValueEx(hkey, REGVALUE_SAVEAS_WARNING, 0, REG_DWORD,
  447.                               (CONST BYTE *)&dwNeverAsk,
  448.                               sizeof(dwNeverAsk));
  449.                 RegCloseKey(hkey);
  450.             }
  451.         }
  452.     }
  453.     
  454. Continue:
  455.     // Suggest a file name
  456.     
  457.     szFileDst[0] = 0;
  458.     // Our favorite candidate is the title,  fall back on the file name.
  459.     hr = pDoc->get_title(&bstrTitle);
  460.     if (SUCCEEDED(hr) && lstrlenW(bstrTitle))
  461.     {
  462.         StrCpyN(szFileDst, bstrTitle, ARRAYSIZE(szFileDst));
  463.     }
  464.     else
  465.         hr = GetFileNameFromURL(bstrURL, szFileDst, ARRAYSIZE(szFileDst));
  466.     if (FAILED(hr))
  467.         goto Cleanup;
  468.     PathCleanupSpec(NULL, szFileDst);
  469.     hr = FormsGetFileName(hwnd, szFileDst, ARRAYSIZE(szFileDst),
  470.                           (LONG_PTR)&tcpi, &iFilter, bForceHTMLOnly);
  471.     if (hr==S_OK)
  472.         hr = SaveToThicket( hwnd, szFileDst, pDoc, tcpi.cpSrc, tcpi.cpDst, iFilter);
  473. Cleanup:
  474.     if (FAILED(hr))
  475.         ReportThicketError(hwnd, hr);
  476.     if (pOleCommandTarget)
  477.         pOleCommandTarget->Release();
  478.     if (pDoc)
  479.         pDoc->Release();
  480.     if (bstrURL)
  481.         SysFreeString(bstrURL);
  482.     if (bstrCharSet)
  483.         SysFreeString(bstrCharSet);
  484.     if (bstrTitle)
  485.         SysFreeString(bstrTitle);
  486.     return;
  487. }
  488. void ReportThicketError( HWND hwnd, HRESULT hr )
  489. {
  490.     LPTSTR lpstrMsg = NULL;
  491.     switch (hr)
  492.     {
  493.     case HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY):
  494.     case E_OUTOFMEMORY:
  495.         lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRMEM);
  496.         break;
  497.     case E_ACCESSDENIED:
  498.     case STG_E_ACCESSDENIED:
  499.         lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRACC);
  500.         break;
  501.     case HRESULT_FROM_WIN32(ERROR_DISK_FULL):
  502.     case STG_E_MEDIUMFULL:
  503.         lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRFULL);
  504.         break;
  505.     case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
  506.         lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRFNF);
  507.         break;
  508.     case E_ABORT:
  509.         // Ray says we don't want a canceled message box.
  510.         //lpstrMsg = MAKEINTRESOURCE(IDS_THICKETABORT);
  511.         break;
  512.     case E_FAIL:
  513.     default:
  514.         lpstrMsg = MAKEINTRESOURCE(IDS_THICKETERRMISC);
  515.         break;
  516.     }
  517.     if ( lpstrMsg )
  518.     {
  519.         MLShellMessageBox(
  520.                         hwnd,
  521.                         lpstrMsg,
  522.                         MAKEINTRESOURCE(IDS_THICKETERRTITLE),
  523.                         MB_OK | MB_ICONERROR);
  524.     }
  525. }
  526. //+--------------------------------------------------------------------------
  527. //
  528. //  File:       file.cxx
  529. //
  530. //  Contents:   Import/export dialog helpers
  531. //
  532. //  History:    16-May-95   RobBear     Taken from formtool
  533. //
  534. //---------------------------------------------------------------------------
  535. const CHAR c_szNT4ResourceLocale[]      = ".DEFAULT\Control Panel\International";
  536. const CHAR c_szWin9xResourceLocale[]    = ".Default\Control Panel\desktop\ResourceLocale";
  537. const CHAR c_szLocale[]                 = "Locale";
  538. LANGID
  539. MLGetShellLanguage()
  540. {
  541.     LANGID  lidShell = 0;
  542.     // BUGBUG this fn is copied from shlwapi. there really should be a
  543.     // shlwapi export. if MLGetUILanguage has any merit, then
  544.     // MLGetShellLanguage has merit as well.
  545.     if (IsOS(OS_NT5))
  546.     {
  547.         static LANGID (CALLBACK* pfnGetUserDefaultUILanguage)(void) = NULL;
  548.         if (pfnGetUserDefaultUILanguage == NULL)
  549.         {
  550.             HMODULE hmod = GetModuleHandle(TEXT("KERNEL32"));
  551.             if (hmod)
  552.                 pfnGetUserDefaultUILanguage = (LANGID (CALLBACK*)(void))GetProcAddress(hmod, "GetUserDefaultUILanguage");
  553.         }
  554.         if (pfnGetUserDefaultUILanguage)
  555.             lidShell = pfnGetUserDefaultUILanguage();
  556.     }
  557.     else
  558.     {
  559.         CHAR szLangID[12];
  560.         DWORD cb, dwRet;
  561.         cb = sizeof(szLangID) - 2*sizeof(szLangID[0]);  // avoid 2 byte buffer overrun
  562.         if (IsOS(OS_NT))
  563.             dwRet = SHGetValueA(HKEY_USERS, c_szNT4ResourceLocale, c_szLocale, NULL, szLangID + 2, &cb);
  564.         else
  565.             dwRet = SHGetValueA(HKEY_USERS, c_szWin9xResourceLocale, NULL, NULL, szLangID + 2, &cb);
  566.         if (ERROR_SUCCESS == dwRet)
  567.         {
  568.             // IE uses a string rep of the hex value
  569.             szLangID[0] = '0';
  570.             szLangID[1] = 'x';
  571.             StrToIntExA(szLangID, STIF_SUPPORT_HEX, (LPINT)&lidShell);
  572.         }
  573.     }
  574.     return lidShell;
  575. }
  576. /*
  577.  *  Stolen from Trident's srccorecdutilfile.cxx
  578.  */
  579. // Hook procedure for open file dialog.
  580. UINT_PTR APIENTRY SaveOFNHookProc(HWND hdlg,
  581.                                   UINT uiMsg,
  582.                                   WPARAM wParam,
  583.                                   LPARAM lParam)
  584. {
  585.     ULONG i, iCurSel;
  586.     BOOL  bFoundEncoding = FALSE;
  587.     WCHAR wzEncoding[MAX_ENCODING_DESC_LEN];
  588.     switch (uiMsg)
  589.     {
  590.         // Populate the dropdown.
  591.         case WM_INITDIALOG:
  592.         {
  593.             HRESULT hr;
  594.             LPOPENFILENAME pofn = (LPOPENFILENAME)lParam;
  595.             ThicketCPInfo *ptcpi = (ThicketCPInfo *)pofn->lCustData;
  596.             IMultiLanguage2 *pMultiLanguage = NULL;
  597.             IEnumCodePage  *pEnumCodePage = NULL;
  598.             //UINT            codepageDefault = ptcpi->cp;
  599.             MIMECSETINFO    csetInfo;
  600.             LANGID          langid;
  601. #ifdef UNIX
  602.             SetWindowLongPtr(hdlg, DWLP_USER, (LONG_PTR)ptcpi);
  603. #endif /* UNIX */
  604.             hr = CoCreateInstance(
  605.                     CLSID_CMultiLanguage,
  606.                     NULL,
  607.                     CLSCTX_INPROC_SERVER,
  608.                     IID_IMultiLanguage2,
  609.                     (void**)&pMultiLanguage);
  610.             if (hr)
  611.                 break;
  612.             hr = pMultiLanguage->GetCharsetInfo(ptcpi->lpwstrDocCharSet,&csetInfo);
  613.             if (hr)
  614.                 break;
  615. #ifndef UNIX
  616.             // the shell combobox where this stuff shows up
  617.             // doesn't know how to fontlink... so we have
  618.             // to stay in the shell's codepage
  619.             langid = MLGetShellLanguage();
  620. #else
  621.             langid = GetSystemDefaultLangID();
  622. #endif /* UNIX */
  623.             if (pMultiLanguage->EnumCodePages( MIMECONTF_SAVABLE_BROWSER | MIMECONTF_VALID,
  624.                                                langid,
  625.                                                &pEnumCodePage) == S_OK)
  626.             {
  627.                 MIMECPINFO cpInfo;
  628.                 ULONG      ccpInfo;
  629.                 UINT       cpDefault;
  630.                 if (pMultiLanguage->GetCodePageInfo(csetInfo.uiInternetEncoding, langid, &cpInfo) == S_OK &&
  631.                     !(cpInfo.dwFlags & MIMECONTF_SAVABLE_BROWSER))
  632.                 {
  633.                     // If the codepage selected is not savable (eg JP_AUTO),
  634.                     // use the family codepage.
  635.                     cpDefault = cpInfo.uiFamilyCodePage;
  636.                 }
  637.                 else
  638.                     cpDefault = csetInfo.uiInternetEncoding;
  639.                 ptcpi->cpSrc = csetInfo.uiInternetEncoding;
  640.                 if (cpDefault == CODEPAGE_UNICODE &&
  641.                     pofn->nFilterIndex == PACKAGE_MHTML) {
  642.                     cpDefault = CODEPAGE_UTF8;
  643.                 }
  644.                 for (i = 0; pEnumCodePage->Next(1, &cpInfo, &ccpInfo) == S_OK; ++i)
  645.                 {
  646.                     TCHAR *lpszDesc;
  647.                     INT_PTR iIdx;
  648.                     if (cpInfo.uiCodePage == CODEPAGE_UNICODE &&
  649.                         pofn->nFilterIndex == PACKAGE_MHTML) {
  650.                         i--;
  651.                         continue;
  652.                     }
  653.                     if (cpDefault == cpInfo.uiCodePage)
  654.                     {
  655.                        StrCpyNW(wzEncoding, cpInfo.wszDescription,
  656.                                 lstrlen(cpInfo.wszDescription) + 1);
  657.                        bFoundEncoding = TRUE;
  658.                     }
  659.                     lpszDesc = cpInfo.wszDescription;
  660.                     iIdx = SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  661.                                               CB_ADDSTRING, 0,
  662.                                               (LPARAM)lpszDesc);
  663.                     SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  664.                                        CB_SETITEMDATA, iIdx,
  665.                                        (LPARAM)cpInfo.uiCodePage);
  666.                 }
  667.                 if (bFoundEncoding)
  668.                 {
  669.                     INT_PTR iIndex = 0;
  670.                     iIndex = SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  671.                                                 CB_FINDSTRINGEXACT, -1,
  672.                                                 (LPARAM)wzEncoding);
  673.                     SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_SETCURSEL,
  674.                                        (WPARAM)iIndex, 0);
  675.                 }
  676.                 else
  677.                 {
  678.                     // No encoding found! Bad error. Recover by selecting
  679.                     // the first one.
  680.                     SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_SETCURSEL,
  681.                                        0, 0);
  682.                 }
  683.             }
  684.             SAFERELEASE(pEnumCodePage);
  685.             SAFERELEASE(pMultiLanguage);
  686.             break;
  687.         }
  688. #ifdef UNIX
  689.         case WM_COMMAND:
  690.         {
  691.           switch (GET_WM_COMMAND_ID(wParam,lParam))
  692.           {
  693.             case IDOK:
  694.             {
  695.                  ThicketCPInfo *ptcpi = (ThicketCPInfo *)GetWindowLongPtr(hdlg,DWLP_USER);
  696.                  ptcpi->cpDst = CP_ACP;
  697.                  iCurSel = (int) SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETCURSEL, 0, 0);
  698.                  ptcpi->cpDst =
  699.                  (UINT)SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
  700.                  (WPARAM)iCurSel, (LPARAM)0);
  701.                  // To spare us from re-instantiating MLANG, we'll set the src and dest
  702.                  // to CP_ACP if no change is indicated.
  703.                  if (ptcpi->cpDst == ptcpi->cpSrc)
  704.                     ptcpi->cpDst = ptcpi->cpSrc = CP_ACP;
  705.            }
  706.            break;
  707.          }
  708.         }
  709.         break;
  710. #endif /* UNIX */
  711.         case WM_NOTIFY:
  712.         {
  713.             LPOFNOTIFY phdr = (LPOFNOTIFY)lParam;
  714.             switch (phdr->hdr.code)
  715.             {
  716.                 case CDN_FILEOK:
  717.                 {
  718.                     LPOPENFILENAME pofn = (LPOPENFILENAME)phdr->lpOFN;
  719.                     ThicketCPInfo *ptcpi = (ThicketCPInfo *)pofn->lCustData;
  720.                     iCurSel = (int) SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETCURSEL, 0, 0);
  721.                     ptcpi->cpDst = //*(UINT *)phdr->lpOFN->lCustData =
  722.                         (UINT)SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
  723.                                              (WPARAM)iCurSel, (LPARAM)0);
  724.                 }
  725.                 // HACK! This case is implemented to implement a hack for
  726.                 // IE5 RAID #60672. MIMEOLE cannot save UNICODE encoding,
  727.                 // so when the user selects MHTML saves, we should remove
  728.                 // this option.  This code should be removed when MIMEOLE
  729.                 // fixes their bug (targeted for NT5 RTM). Contact SBailey
  730.                 // for the status of this.
  731.                 case CDN_TYPECHANGE:
  732.                 {
  733.                     LPOPENFILENAME pofn = (LPOPENFILENAME)phdr->lpOFN;
  734.                     ThicketCPInfo *ptcpi = (ThicketCPInfo *)pofn->lCustData;
  735.                     UINT uiCPSel, uiCP;
  736.                     int iType = pofn->nFilterIndex;
  737.                     UINT iCount;
  738.                     int iCurSel;
  739.                     int iSet = -1;
  740.                     if (iType == PACKAGE_MHTML)
  741.                     {
  742.                         iCurSel = (int)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_GETCURSEL, 0, 0);
  743.                         uiCPSel = (UINT)SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
  744.                                              (WPARAM)iCurSel, (LPARAM)0);
  745.                          
  746.                         // If you selected unicode, make it look like you
  747.                         // really selected UTF-8
  748.                         if (uiCPSel == CODEPAGE_UNICODE)
  749.                         {
  750.                             uiCPSel = CODEPAGE_UTF8;
  751.                         }
  752.                         i = (int) SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  753.                                                      CB_FINDSTRINGEXACT,
  754.                                                      (WPARAM)0,
  755.                                                      (LPARAM)UNICODE_TEXT);
  756.                         if (i != CB_ERR)
  757.                         {
  758.                             SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  759.                                                CB_DELETESTRING, i, (LPARAM)0);
  760.                         }
  761.                         iCount = (int)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  762.                                                          CB_GETCOUNT, 0, 0);
  763.                         // Set selected item back
  764.                         for (i = 0; i < iCount; i++)
  765.                         {
  766.                             uiCP = (UINT)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
  767.                                                             (WPARAM)i, (LPARAM)0);
  768.                             if (uiCP == uiCPSel)
  769.                             {
  770.                                 iSet = i;
  771.                             }
  772.                         }
  773.                         if (iSet != 0xffffffff)
  774.                         {
  775.                             SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_SETCURSEL,
  776.                                                (WPARAM)iSet, (LPARAM)0);
  777.                         }
  778.                     }
  779.                     else
  780.                     {
  781.                         // Store current selection
  782.                         iCurSel = (int)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_GETCURSEL, 0, 0);
  783.                         uiCPSel = (UINT)SendDlgItemMessage (hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
  784.                                              (WPARAM)iCurSel, (LPARAM)0);
  785.                         // Add unicode back in, if it was removed
  786.                         i = (int) SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  787.                                                      CB_FINDSTRINGEXACT,
  788.                                                      (WPARAM)0,
  789.                                                      (LPARAM)UNICODE_TEXT);
  790.                         if (i == CB_ERR) {
  791.                             // Unicode does not exist, add it back in
  792.                             i = (int) SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  793.                                                          CB_ADDSTRING, 0,
  794.                                                          (LPARAM)UNICODE_TEXT);
  795.     
  796.                             SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  797.                                                CB_SETITEMDATA, i,
  798.                                                (LPARAM)CODEPAGE_UNICODE);
  799.     
  800.                             // Make sure the same encoding selected before is
  801.                             // still selected.
  802.                             iCount = (int)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET,
  803.                                                              CB_GETCOUNT, 0, 0);
  804.                             for (i = 0; i < iCount; i++)
  805.                             {
  806.                                 uiCP = (UINT)SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_GETITEMDATA,
  807.                                                                 (WPARAM)i, (LPARAM)0);
  808.                                 if (uiCP == uiCPSel)
  809.                                 {
  810.                                     iSet = i;
  811.                                 }
  812.                             }
  813.     
  814.                             if (iCurSel != 0xffffffff)
  815.                             {
  816.                                 SendDlgItemMessage(hdlg, IDC_SAVE_CHARSET, CB_SETCURSEL,
  817.                                                    (WPARAM)iSet, (LPARAM)0);
  818.                             }
  819.                         }
  820.                     }
  821.                 }
  822.                 break;
  823.             }
  824.         }
  825.         break;
  826.         case WM_HELP:
  827.         {
  828.             SHWinHelpOnDemandWrap((HWND)((LPHELPINFO) lParam)->hItemHandle,
  829.                           c_szHelpFile,
  830.                           HELP_WM_HELP,
  831.                           (DWORD_PTR)aSaveAsHelpIDs);
  832.         }
  833.         break;
  834.         case WM_CONTEXTMENU:
  835.         {
  836.             SHWinHelpOnDemandWrap((HWND) wParam,
  837.                           c_szHelpFile, 
  838.                           HELP_CONTEXTMENU,
  839.                           (DWORD_PTR)aSaveAsHelpIDs);
  840.         }
  841.         break;
  842.     }
  843.     return (FALSE);
  844. }
  845. //
  846. // Protect the naive users from themselves, if somebody enters a filename
  847. // of microsoft.com when saving http://www.microsoft.com we don't want
  848. // to save a .COM file since this will be interpreted as an executable.
  849. // bad things will happen
  850. //
  851. void CleanUpFilename(LPTSTR pszFile, int iPackageStyle)
  852. {
  853.     //
  854.     // If we find .COM as the file extension replace it with the file extension
  855.     // of the filetype they are saving the file as
  856.     //
  857.     LPTSTR pszExt = PathFindExtension(pszFile);
  858.     ASSERT(pszExt);
  859.     if (StrCmpI(pszExt, TEXT(".COM")) == 0) // REVIEW any other file types???
  860.     {
  861.         //
  862.         // Map the package style to a default extension. NOTE this relies on 
  863.         // the fact that the filter index maps to the PACKAGE style enum
  864.         // (as does the rest of the thicket code).
  865.         //
  866.         switch (iPackageStyle)
  867.         {
  868.         case PACKAGE_THICKET:
  869.         case PACKAGE_HTML:
  870.             StrCatBuff(pszFile, TEXT(".htm"), MAX_PATH); 
  871.             break;
  872.         case PACKAGE_MHTML:
  873.             StrCatBuff(pszFile, TEXT(".mht"), MAX_PATH); 
  874.             break;
  875.         case PACKAGE_TEXT:
  876.             StrCatBuff(pszFile, TEXT(".txt"), MAX_PATH); 
  877.             break;
  878.         default:
  879.             ASSERT(FALSE);  // Unknown package type
  880.             break;
  881.         }
  882.     }
  883. }
  884. //+---------------------------------------------------------------------------
  885. //
  886. //  Function:   FormsGetFileName
  887. //
  888. //  Synopsis:   Gets a file name using either the GetOpenFileName or
  889. //              GetSaveFileName functions.
  890. //
  891. //  Arguments:  [fSaveFile]   -- TRUE means use GetSaveFileName
  892. //                               FALSE means use GetOpenFileName
  893. //
  894. //              [idFilterRes] -- The string resource specifying text in the
  895. //                                  dialog box.  It must have the
  896. //                                  following format:
  897. //                            Note: the string has to be _one_ contiguous string.
  898. //                                  The example is broken up to make it fit
  899. //                                  on-screen. The verical bar ("pipe") characters
  900. //                                  are changed to ''-s on the fly.
  901. //                                  This allows the strings to be localized
  902. //                                  using Espresso.
  903. //
  904. //          IDS_FILENAMERESOURCE, "Save Dialog As|         // the title
  905. //                                 odg|                    // default extension
  906. //                                 Forms3 Dialog (*.odg)|  // pairs of filter strings
  907. //                                 *.odg|
  908. //                                 Any File (*.*)|
  909. //                                 *.*|"
  910. //
  911. //              [pstrFile]    -- Buffer for file name.
  912. //              [cchFile]     -- Size of buffer in characters.
  913. //
  914. //  Modifies:   [pstrFile]
  915. //
  916. //----------------------------------------------------------------------------
  917. #ifdef _MAC
  918. extern "C" {
  919. char * __cdecl _p2cstr(unsigned char *);
  920. }
  921. #endif
  922. #define CHAR_DOT                TEXT('.')
  923. #define CHAR_DOT_REPLACEMENT    TEXT('_')
  924. void ReplaceDotsInFileName(LPTSTR pszFileName)
  925. {
  926.     ASSERT(pszFileName);
  927.     while (*pszFileName)
  928.     {
  929.         if (*pszFileName == CHAR_DOT)
  930.         {
  931.             *pszFileName = CHAR_DOT_REPLACEMENT;
  932.         }
  933.         pszFileName++;
  934.     }
  935. }
  936. HRESULT
  937. FormsGetFileName(
  938.         HWND hwndOwner,
  939.         LPTSTR pstrFile,
  940.         int cchFile,
  941.         LPARAM lCustData,
  942.         DWORD *pnFilterIndex,
  943.         BOOL bForceHTMLOnly)
  944. {
  945.     HRESULT         hr  = S_OK;
  946.     BOOL            fOK;
  947.     DWORD           dwCommDlgErr;
  948.     LPTSTR          pstr;
  949.     OPENFILENAME    ofn;
  950.     TCHAR           achBuffer[4096];    //  Max. size of a string resource
  951.     TCHAR *         cp;
  952.     TCHAR *         pstrExt;
  953.     int             cbBuffer;
  954.     TCHAR           achPath[MAX_PATH];
  955.     DWORD           dwType = REG_SZ;
  956.     DWORD           cbData = MAX_PATH * sizeof(TCHAR);
  957.     int             idFilterRes;
  958.     // Initialize ofn struct
  959.     memset(&ofn, 0, sizeof(ofn));
  960.     ofn.lStructSize     = sizeof(ofn);
  961.     ofn.hwndOwner       = hwndOwner;
  962.     ofn.Flags           =   OFN_FILEMUSTEXIST   |
  963.                             OFN_PATHMUSTEXIST   |
  964.                             OFN_OVERWRITEPROMPT |
  965.                             OFN_HIDEREADONLY    |
  966. #ifndef UNIX
  967.                             OFN_NOCHANGEDIR     |
  968.                             OFN_EXPLORER;
  969. #else
  970.                             OFN_NOCHANGEDIR;
  971. #endif /* UNIX */
  972.     ofn.lpfnHook        = NULL;
  973.     ofn.nMaxFile        = cchFile;
  974.     ofn.lCustData       = lCustData;
  975.     ofn.lpstrFile       = pstrFile;
  976. #ifndef NO_IME
  977.     // We add an extra control to the save file dialog.
  978.     if (lCustData)
  979.     {
  980.         ofn.Flags |= OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
  981.         ofn.lpfnHook = SaveOFNHookProc;
  982.         ofn.lpTemplateName = MAKEINTRESOURCE(IDD_ADDTOSAVE_DIALOG);
  983.         ofn.hInstance = g_hinst;
  984.     }
  985. #endif
  986.     //
  987.     // Find the extension and set the filter index based on what the
  988.     // extension is.  After these loops pstrExt will either be NULL if
  989.     // we didn't find an extension, or will point to the extension starting
  990.     // at the '.'
  991.     pstrExt = pstrFile;
  992.     while (*pstrExt)
  993.         pstrExt++;
  994.     while ( pstrExt >= pstrFile )
  995.     {
  996.         if( *pstrExt == TEXT('.') )
  997.             break;
  998.         pstrExt--;
  999.     }
  1000.     if( pstrExt < pstrFile )
  1001.         pstrExt = NULL;
  1002.     // Load the filter spec.
  1003.     // BUGBUG: Convert table to stringtable for localization
  1004.     if ( SHRestricted2W( REST_NoBrowserSaveWebComplete, NULL, 0 ) )
  1005.         idFilterRes = IDS_NOTHICKET_SAVE;
  1006.     else if ( s_dwInetComVerMS != 0xFFFFFFFF )
  1007.     {
  1008. #ifndef UNIX
  1009.         if (s_dwInetComVerMS == 0)
  1010.         {
  1011.             TCHAR szPath[MAX_PATH];
  1012.             GetSystemDirectory( szPath, MAX_PATH );
  1013.             StrCatBuff( szPath, TEXT("\INETCOMM.DLL"), MAX_PATH );
  1014.             if (FAILED(GetVersionFromFile(szPath, &s_dwInetComVerMS, &s_dwInetComVerLS)))
  1015.                 s_dwInetComVerMS = 0xFFFFFFFF;
  1016.         }
  1017.         if (s_dwInetComVerMS >= 0x50000 && s_dwInetComVerMS != 0xFFFFFFFF)
  1018.             idFilterRes = IDS_THICKET_SAVE;
  1019.         else
  1020.             idFilterRes = IDS_NOMHTML_SAVE;
  1021. #else
  1022.         // on UNIX we don't have inetcomm.dll if oe is not installed
  1023.         {
  1024.            HINSTANCE hInetComm = NULL;
  1025.            if ((hInetComm = LoadLibrary(TEXT("INETCOMM.DLL"))))
  1026.            {
  1027.               idFilterRes = IDS_THICKET_SAVE;
  1028.               FreeLibrary(hInetComm);
  1029.            }
  1030.            else
  1031.               idFilterRes = IDS_NOMHTML_SAVE;
  1032.         }
  1033. #endif
  1034.     }
  1035.     else
  1036.         idFilterRes = IDS_THICKET_SAVE;
  1037.     cbBuffer = MLLoadShellLangString(idFilterRes, achBuffer, ARRAYSIZE(achBuffer));
  1038.     ASSERT(cbBuffer > 0);
  1039.     if ( ! cbBuffer )
  1040.         return E_FAIL;
  1041.     ofn.lpstrTitle = achBuffer;
  1042.     for ( cp = achBuffer; *cp; cp++ )
  1043.     {
  1044.         if ( *cp == TEXT('|') )
  1045.         {
  1046.             *cp = TEXT('');
  1047.         }
  1048.     }
  1049.     ASSERT(ofn.lpstrTitle);
  1050.     // Default extension is second string.
  1051.     pstr = (LPTSTR) ofn.lpstrTitle;
  1052.     while (*pstr++)
  1053.     {
  1054.     }
  1055.     // N.B. (johnv) Here we assume that filter index one corresponds with the default
  1056.     //  extension, otherwise we would have to introduce a default filter index into
  1057.     //  the resource string.
  1058.     ofn.nFilterIndex    = ((pnFilterIndex)? *pnFilterIndex : 1);
  1059.     ofn.lpstrDefExt     = pstr;
  1060.     // Filter is third string.
  1061.     while(*pstr++)
  1062.     {
  1063.     }
  1064.     ofn.lpstrFilter = pstr;
  1065.     // Try to match the extension with an entry in the filter list
  1066.     // If we match, remove the extension from the incoming path string,
  1067.     //   set the default extension to the one we found, and appropriately
  1068.     //   set the filter index.
  1069.     if (pstrExt && !bForceHTMLOnly)
  1070.     {
  1071.         // N.B. (johnv) We are searching more than we need to.
  1072.         int    iIndex = 0;
  1073.         const TCHAR* pSearch = ofn.lpstrFilter;
  1074.         while( pSearch )
  1075.         {
  1076.             if( StrStr( pSearch, pstrExt ) )
  1077.             {
  1078.                 ofn.nFilterIndex = (iIndex / 2) + 1;
  1079.                 ofn.lpstrDefExt = pstrExt + 1;
  1080.                 // Remove the extension from the file name we pass in
  1081.                 *pstrExt = TEXT('');
  1082.                 break;
  1083.             }
  1084.             pSearch += lstrlen(pSearch);
  1085.             if( pSearch[1] == 0 )
  1086.                 break;
  1087.             pSearch++;
  1088.             iIndex++;
  1089.         }
  1090.     }
  1091.     // Suggest HTML Only as default save-type
  1092.     if (bForceHTMLOnly)
  1093.     {
  1094.         // BUGBUG: These are hard-coded indices based on shdoclc.rc's
  1095.         // IDS_THICKET_SAVE, IDS_NOMHTML_SAVE, IDS_NOTHICKET_SAVE ordering.
  1096.         // This saves us the perf hit of doing string comparisons to find
  1097.         // HTML only
  1098.         switch (idFilterRes)
  1099.         {
  1100.             case IDS_NOTHICKET_SAVE:
  1101.                 ofn.nFilterIndex = 1;
  1102.                 break;
  1103.             case IDS_NOMHTML_SAVE:
  1104.                 ofn.nFilterIndex = 2;
  1105.                 break;
  1106.             default:
  1107.                 ASSERT(idFilterRes == IDS_THICKET_SAVE);
  1108.                 ofn.nFilterIndex = 3;
  1109.                 break;
  1110.         }
  1111.     }
  1112.     if ( SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_MAIN, REGSTR_VAL_SAVEDIRECTORY,
  1113.                     &dwType, achPath, &cbData) != ERROR_SUCCESS ||
  1114.          !PathFileExists(achPath))
  1115.     {
  1116.         SHGetSpecialFolderPath(hwndOwner, achPath, CSIDL_PERSONAL, FALSE);
  1117.     }
  1118.     ofn.lpstrInitialDir = achPath;
  1119.     // We don't want to suggest dots in the filename
  1120.     ReplaceDotsInFileName(pstrFile);
  1121.     // Now, at last, we're ready to call the save file dialog
  1122.     fOK = GetSaveFileName(&ofn);
  1123.     // if working with the abbreviated format list, adjust the index
  1124.     if (idFilterRes == IDS_NOTHICKET_SAVE)
  1125.         ofn.nFilterIndex += 2;
  1126.     else if ( idFilterRes == IDS_NOMHTML_SAVE && ofn.nFilterIndex > 1 )
  1127.         ofn.nFilterIndex += 1;
  1128.     if (fOK)
  1129.     {
  1130.         //
  1131.         // Protect the naive users from themselves, if somebody enters a filename
  1132.         // of microsoft.com when saving http://www.microsoft.com we don't want
  1133.         // to save a .COM file since this will be interpreted as an executable.
  1134.         // bad things will happen
  1135.         //
  1136.         CleanUpFilename(pstrFile, ofn.nFilterIndex);
  1137.         TCHAR *lpszFileName;
  1138.         StrCpyN( achPath, pstrFile, ARRAYSIZE(achPath) );
  1139.         lpszFileName = PathFindFileName( achPath );
  1140.         *lpszFileName = 0;
  1141.         SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_MAIN, REGSTR_VAL_SAVEDIRECTORY,
  1142.                    REG_SZ, achPath, (lstrlen(achPath) * sizeof(TCHAR)));
  1143.         if (pnFilterIndex)
  1144.             *pnFilterIndex = ofn.nFilterIndex;
  1145.         if (ofn.nFilterIndex != PACKAGE_MHTML)
  1146.         {
  1147.             // we can only do this if we're not packaging MHTML
  1148.             // because MHTML requires that we tag with the explicit
  1149.             // charset, even if it was the default. unlike thicket
  1150.             // which inherits the charset from the original document,
  1151.             // MHTML must be explicitly tagged or else some system
  1152.             // charset tags will sneak in.
  1153.             ThicketCPInfo * ptcpi = (ThicketCPInfo *)lCustData;
  1154.             // To spare us from re-instantiating MLANG, we'll set the src and dest
  1155.             // to CP_ACP if no change is indicated.
  1156.             if (ptcpi->cpDst == ptcpi->cpSrc)
  1157.                ptcpi->cpDst = ptcpi->cpSrc = CP_ACP;
  1158.         }
  1159.     }
  1160.     else
  1161.     {
  1162. #ifndef WINCE
  1163.         dwCommDlgErr = CommDlgExtendedError();
  1164.         if (dwCommDlgErr)
  1165.         {
  1166.             hr = HRESULT_FROM_WIN32(dwCommDlgErr);
  1167.         }
  1168.         else
  1169.         {
  1170.             hr = S_FALSE;
  1171.         }
  1172. #else // WINCE
  1173.         hr = E_FAIL;
  1174. #endif // WINCE
  1175.     }
  1176.     return hr;
  1177. }
  1178. HRESULT
  1179. GetFileNameFromURL( LPWSTR pwszURL, LPTSTR pszFile, DWORD cchFile)
  1180. {
  1181.     HRESULT         hr = S_OK;
  1182.     PARSEDURLW      puw = {0};
  1183.     int             cchUrl;
  1184.     cchUrl = SysStringLen(pwszURL);
  1185.     if (cchUrl)
  1186.     {
  1187.         puw.cbSize = sizeof(PARSEDURLW);
  1188.         if (SUCCEEDED(ParseURLW(pwszURL, &puw)))
  1189.         {
  1190.             OLECHAR *pwchBookMark;
  1191.             DWORD   dwSize;
  1192.             INTERNET_CACHE_ENTRY_INFOW      ceiT;
  1193.             LPINTERNET_CACHE_ENTRY_INFOW    pcei = NULL;
  1194.             // Temporarily, null out the '#' in the url
  1195.             pwchBookMark = StrRChrW(puw.pszSuffix, NULL,'#');
  1196.             if (pwchBookMark)
  1197.             {
  1198.                 *pwchBookMark = 0;
  1199.             }
  1200.             dwSize = sizeof(INTERNET_CACHE_ENTRY_INFO);
  1201.             if ( !GetUrlCacheEntryInfoW( pwszURL, &ceiT, &dwSize ) &&
  1202.                  GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
  1203.                  (pcei = (LPINTERNET_CACHE_ENTRY_INFOW)new BYTE[dwSize]) != NULL &&
  1204.                  GetUrlCacheEntryInfoW( pwszURL, pcei, &dwSize ) )
  1205.             {
  1206.                 StrCpyN(pszFile, PathFindFileName(pcei->lpszLocalFileName), cchFile);
  1207.                 PathUndecorate(pszFile);
  1208.             }
  1209.             if(pcei)
  1210.                 delete[] pcei;
  1211.             if (pwchBookMark)
  1212.                 *pwchBookMark = '#';
  1213.             if ( !pszFile[0] )
  1214.             {
  1215.                 OLECHAR *pwchQuery;
  1216.                 TCHAR   szFileT[MAX_PATH];
  1217.                 // Temporarily, null out the '?' in the url
  1218.                 pwchQuery = StrRChrW(puw.pszSuffix, NULL,'?');
  1219.                 if (pwchQuery)
  1220.                 {
  1221.                     *pwchQuery = 0;
  1222.                 }
  1223.                 // IE5 bug 15055 - http://my.excite.com/?uid=B56E4E2D34DF3FED.save_uid
  1224.                 // fails to save because we were passing "my.excite.com/" as the file
  1225.                 // name to the file dialog. It doesn't like this.
  1226.                 if (!pwchQuery || (pwchQuery[-1] != '/' && pwchQuery[-1] != '\'))
  1227.                 {
  1228.                     dwSize = ARRAYSIZE(szFileT);
  1229.                     StrCpyN(szFileT, PathFindFileName(puw.pszSuffix), dwSize);
  1230.                     if ( !InternetCanonicalizeUrl( szFileT, pszFile, &dwSize, ICU_DECODE | ICU_NO_ENCODE) )
  1231.                         StrCpyN(pszFile, szFileT, cchFile);
  1232.                     pszFile[cchFile - 1] = 0;
  1233.                 }
  1234.                 if (pwchQuery)
  1235.                     *pwchQuery = '?';
  1236.             }
  1237.         }
  1238.     }
  1239.     if (!pszFile[0])
  1240.     {
  1241.         MLLoadString(IDS_UNTITLED, pszFile, cchFile);
  1242.     }
  1243.     return hr;
  1244. }
  1245. INT_PTR CALLBACK SaveAsWarningDlgProc(HWND hDlg, UINT msg, WPARAM wParam,
  1246.                                       LPARAM lParam)
  1247. {
  1248.     BOOL        fRet = FALSE;
  1249.     HRESULT     hr = S_OK;
  1250.     int         iFlags = 0;
  1251.     INT_PTR     bChecked = 0;
  1252.     switch (msg)
  1253.     {
  1254.         case WM_INITDIALOG:
  1255.             MessageBeep(MB_ICONEXCLAMATION);
  1256.             fRet = TRUE;
  1257.         case WM_COMMAND:
  1258.             bChecked = SendDlgItemMessage(hDlg, IDC_SAVEAS_WARNING_CB,
  1259.                                                   BM_GETCHECK, 0, 0 );
  1260.             iFlags = (bChecked) ? (SAVEAS_NEVER_ASK_AGAIN) : (0);
  1261.             switch (LOWORD(wParam))
  1262.             {
  1263.                 case IDOK:
  1264.                     iFlags |= SAVEAS_OK;
  1265.                     // fall through
  1266.                 case IDCANCEL:
  1267.                     EndDialog(hDlg, iFlags);
  1268.                     fRet = TRUE;
  1269.                     break;
  1270.             }
  1271.     
  1272.         default:
  1273.             fRet = FALSE;
  1274.     }
  1275.     return fRet;
  1276. }