windows-nt/Source/XPSP1/NT/shell/shdocvw/download.cpp
2020-09-26 16:20:57 +08:00

2771 lines
94 KiB
C++

#include "priv.h"
#include "bindcb.h"
#include "resource.h"
#include <vrsscan.h>
#include "iface.h"
#include "security.h"
#include <wintrust.h>
#include "iehelpid.h"
#include <shlwapi.h>
#include "inetreg.h"
#include <varutil.h>
#include "dochost.h"
#include <mluisupp.h>
#include <downloadmgr.h>
#include "apithk.h"
#include "richedit.h"
#include <brutil.h>
#define MIME
#include "filetype.h"
#define ALLFILE_WILDCARD TEXT("*.*")
#define MAX_BYTES_STRLEN 64
#define CALC_NOW 5 // Recalcs Estimated time left every this many'th call to OnProgress
//
// Enable WinVerifyTrust
//
#define CALL_WVT
#ifdef CALL_WVT
#include "wvtp.h"
//
// Note that this is a global variable. It means we don't call LoadLibrary
// everytime we download an EXE (good), but the user need to reboot if
// WINTRUST.DLL is added later (bad). Since WINTRUST.DLL is part of IE 3.0,
// this is sufficient at this point.
//
Cwvt g_wvt;
HWND g_hDlgActive = NULL; // get rid of this, not needed
//
// A named mutex is being used to determine if a critical operation exist, such as a file download.
// When we detect this we can prevent things like going offline while a download is in progress.
// To start the operation Create the named mutex. When the op is complete, close the handle.
// To see if any pending operations are in progress, Open the named mutex. Success/fail will indicate
// if any pending operations exist. This mechanism is being used to determine if a file download is
// in progress when the user attempts to go offline. If so, we prompt them to let them know that going
// offline will cancel the download(s).
HANDLE g_hCritOpMutex = NULL;
UINT _VerifyTrust(HWND hwnd, LPCTSTR pszFileName, LPCWSTR pszStatusText);
#endif // CALL_WVT
// Do strong typechecking on the parameters
#ifdef SAFECAST
#undef SAFECAST
#endif
#define SAFECAST(_src, _type) (((_type)(_src)==(_src)?0:0), (_type)(_src))
extern HRESULT _GetRequestFlagFromPIB(IBinding *pib, DWORD *pdwOptions);
extern HRESULT _PrepareURLForDisplayUTF8W(LPCWSTR pwz, LPWSTR pwzOut, LPDWORD pcchOut, BOOL fUTF8Enabled, UINT uiCP);
UINT IE_ErrorMsgBox(IShellBrowser* psb,
HWND hwnd, HRESULT hrError, LPCWSTR szError, LPCTSTR szURL,
UINT idResource, UINT wFlags);
BOOL IsAssociatedWithIE(LPCWSTR pszFileName);
extern "C" EXECUTION_STATE WINAPI pSetThreadExecutionState(EXECUTION_STATE esFlags); // Win2k+, Win98+ kernel32 API
#define DM_DOWNLOAD TF_SHDPROGRESS
#define DM_PROGRESS TF_SHDPROGRESS
#define DM_WVT TF_SHDPROGRESS
#define DWNLDMSG(psz, psz2) TraceMsg(DM_DOWNLOAD, "shd TR-DWNLD::%s %s", psz, psz2)
#define DWNLDMSG2(psz, x) TraceMsg(DM_DOWNLOAD, "shd TR-DWNLD::%s %x", psz, x)
#define DWNLDMSG3(psz, x, y) TraceMsg(DM_DOWNLOAD, "shd TR-DWNLD::%s %x %x", psz, x, y)
#define DWNLDMSG4(psz, x, y, z) TraceMsg(DM_DOWNLOAD, "shd TR-DWNLD::%s %x %x %x", psz, x, y, z)
#define SAFEMSG(psz, psz2) TraceMsg(0, "shd TR-SAFE::%s %s", psz, psz2)
#define SAFEMSG2(psz, x) TraceMsg(0, "shd TR-SAFE::%s %x", psz, x)
#define EXPMSG(psz, psz2) TraceMsg(0, "shd TR-EXP::%s %s", psz, psz2)
#define MDLGMSG(psz, x) TraceMsg(0, "shd TR-MODELESS::%s %x", psz, x)
#define MSGMSG(psz, x) TraceMsg(TF_SHDTHREAD, "ief MMSG::%s %x", psz, x)
#define PARKMSG(psz, x) TraceMsg(TF_SHDTHREAD, "ief MPARK::%s %x", psz, x)
// File name and 32 for the rest of the title string
#define TITLE_LEN (256 + 32)
#define MAX_DISPLAY_LEN 96
#define MAX_SCHEME_STRING 16
class CDownload : public IBindStatusCallback
, public IAuthenticate
, public IServiceProvider
, public IHttpNegotiate
, public IWindowForBindingUI
{
public:
// *** IUnknown methods ***
STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj);
STDMETHODIMP_(ULONG) AddRef(void) ;
STDMETHODIMP_(ULONG) Release(void);
// *** IAuthenticate ***
STDMETHODIMP Authenticate(
HWND *phwnd,
LPWSTR *pszUsername,
LPWSTR *pszPassword);
// *** IServiceProvider ***
STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppvObj);
// *** IBindStatusCallback ***
STDMETHODIMP OnStartBinding(
/* [in] */ DWORD grfBSCOption,
/* [in] */ IBinding *pib);
STDMETHODIMP GetPriority(
/* [out] */ LONG *pnPriority);
STDMETHODIMP OnLowResource(
/* [in] */ DWORD reserved);
STDMETHODIMP OnProgress(
/* [in] */ ULONG ulProgress,
/* [in] */ ULONG ulProgressMax,
/* [in] */ ULONG ulStatusCode,
/* [in] */ LPCWSTR szStatusText);
STDMETHODIMP OnStopBinding(
/* [in] */ HRESULT hresult,
/* [in] */ LPCWSTR szError);
STDMETHODIMP GetBindInfo(
/* [out] */ DWORD *grfBINDINFOF,
/* [unique][out][in] */ BINDINFO *pbindinfo);
STDMETHODIMP OnDataAvailable(
/* [in] */ DWORD grfBSCF,
/* [in] */ DWORD dwSize,
/* [in] */ FORMATETC *pformatetc,
/* [in] */ STGMEDIUM *pstgmed);
STDMETHODIMP OnObjectAvailable(
/* [in] */ REFIID riid,
/* [iid_is][in] */ IUnknown *punk);
/* *** IHttpNegotiate *** */
STDMETHODIMP BeginningTransaction(LPCWSTR szURL, LPCWSTR szHeaders,
DWORD dwReserved, LPWSTR *pszAdditionalHeaders);
STDMETHODIMP OnResponse(DWORD dwResponseCode,
LPCWSTR szResponseHeaders,
LPCWSTR szRequestHeaders,
LPWSTR *pszAdditionalRequestHeaders);
STDMETHODIMP GetWindow(REFGUID RefGUI, HWND* phWnd);
protected:
LONG _cRef;
LONG _cRefDLD;
IBinding* _pib;
IBindCtx* _pbc;
CDocObjectHost *_pdoh;
HWND _hDlg;
HWND _hwndToolTips;
BOOL _fSaveAs : 1;
BOOL _fGotFile : 1;
BOOL _fFirstTickValid : 1;
BOOL _fEndDialogCalled : 1;
BOOL _fDontPostQuitMsg : 1; // Posts WM_QUIT message in destructor
BOOL _fCallVerifyTrust : 1;
BOOL _fStrsLoaded : 1;
BOOL _fSafe : 1; // no need to call IsSafe dialog
BOOL _fDownloadStarted : 1; // Have we started receiving data
BOOL _fDownloadCompleted : 1; // We have received BSCF_LASTDATANOTIFICATION
BOOL _fDeleteFromCache : 1; // Delete the file from cache when done
BOOL _fWriteHistory : 1; // Should it be written to history? (SECURITY)
BOOL _fDismissDialog : 1;
BOOL _fUTF8Enabled : 1;
DWORD _dwFirstTick;
DWORD _dwFirstSize;
DWORD _dwTotalSize; // Size of file downloaded so far
DWORD _dwFileSize; // Size of file to download
HICON _hicon;
TCHAR _szPath[MAX_PATH]; // ok with MAX_PATH
TCHAR _szSaveToFile[MAX_PATH]; // File to Save to
TCHAR _szEstimateTime[MAX_PATH]; // ok with MAX_PATH
TCHAR _szBytesCopied[MAX_PATH]; // ok with MAX_PATH
TCHAR _szTitlePercent[TITLE_LEN];
TCHAR _szTitleBytes[TITLE_LEN];
TCHAR _szTransferRate[TITLE_LEN];
TCHAR _szURL[MAX_URL_STRING];
TCHAR _szDisplay[MAX_DISPLAY_LEN]; // URL to be displayed
TCHAR _szDefDlgTitle[256];
TCHAR _szExt[10];
DWORD _grfBINDF;
BINDINFO* _pbinfo;
LPWSTR _pwzHeaders;
IMoniker* _pmk; // WARNING: No ref-count (only for modal)
LPWSTR _pwszDisplayName;
DWORD _dwVerb;
UINT _uiCP; // Code page
DWORD _dwOldEst;
ULONG _ulOldProgress;
DWORD _dwOldRate;
DWORD _dwOldPcent;
DWORD _dwOldCur;
BOOL _fConfirmed;
void SetMoniker(IMoniker* pmk) { _pmk=pmk; }
BOOL _IsModal(void) { return (bool)_pmk; }
virtual ~CDownload();
friend INT_PTR CALLBACK DownloadDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
friend INT_PTR CALLBACK SafeOpenDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
UINT _MayAskUserIsFileSafeToOpen(LPCTSTR pszMime);
BOOL _GetSaveLocation(void);
BOOL _SaveFile(void);
void _DeleteFromCache(void);
ULONG AddRefDLD(void);
ULONG ReleaseDLD(void);
HRESULT PerformVirusScan(LPCTSTR szFileName);
public:
CDownload(BOOL fSaveAs = FALSE, LPWSTR pwzHeaders = NULL,
DWORD grfBINDF = BINDF_ASYNCHRONOUS, BINDINFO* pbinfo = NULL,
BOOL fSafe = FALSE, DWORD dwVerb = BINDVERB_GET, LPCTSTR pszRedir=NULL, UINT uiCP=CP_ACP, BOOL fConfirmed = FALSE);
static void OpenUI(IMoniker* pmk, IBindCtx *pbc, BOOL fSaveAs = FALSE, BOOL fSafe = FALSE, LPWSTR pwzHeaders = NULL, DWORD dwVerb = BINDVERB_GET, DWORD grfBINDF = 0, BINDINFO* pbinfo = NULL, LPCTSTR pszRedir=NULL, UINT uiCP=CP_ACP, BOOL fConfirmed = FALSE);
HRESULT StartBinding(IMoniker* pmk, IBindCtx *pbc = NULL);
void EndDialogDLD(UINT id);
void ShowStats(void);
BOOL SetDismissDialogFlag(BOOL fDismiss) { return(_fDismissDialog = fDismiss); }
BOOL GetDismissDialogFlag(void) { return(_fDismissDialog); }
#ifdef USE_LOCKREQUEST
HRESULT LockRequestHandle(void);
#endif
};
CDownload::CDownload(BOOL fSaveAs, LPWSTR pwzHeaders, DWORD grfBINDF, BINDINFO* pbinfo, BOOL fSafe, DWORD dwVerb, LPCTSTR pszRedir, UINT uiCP, BOOL fConfirmed)
: _cRef(1), _fSaveAs(fSaveAs), _fWriteHistory(1),
_grfBINDF(grfBINDF), _pbinfo(pbinfo), _fSafe(fSafe), _pwzHeaders(pwzHeaders), _dwVerb(dwVerb), _uiCP(uiCP), _fConfirmed(fConfirmed)
{
ASSERT(_fStrsLoaded == FALSE);
ASSERT(_fDownloadStarted == FALSE);
ASSERT(_fDownloadCompleted == FALSE);
ASSERT(_fGotFile == FALSE);
ASSERT(_fUTF8Enabled == FALSE);
ASSERT(_hDlg == NULL);
ASSERT(_pwszDisplayName == NULL);
ASSERT(_dwTotalSize == 0);
ASSERT(_dwFileSize == 0);
ASSERT(_dwFirstTick == 0);
ASSERT(_ulOldProgress == 0);
ASSERT(_dwOldRate == 0);
ASSERT(_dwOldPcent == 0);
ASSERT(_dwOldCur == 0);
_dwOldEst = 0xffffffff;
if (pszRedir && lstrlen(pszRedir))
StrCpyN(_szURL, pszRedir, ARRAYSIZE(_szURL) - 1); // -1 ???
TraceMsg(TF_SHDLIFE, "CDownload::CDownload being constructed");
}
void ProcessStartbindingError(HWND hWnd, LPTSTR pszTitle, LPTSTR pszText, UINT uiFlag, HRESULT hres)
{
if (E_ACCESSDENIED == hres)
{
pszText = MAKEINTRESOURCE(IDS_DOWNLOADDISALLOWED);
pszTitle = MAKEINTRESOURCE(IDS_SECURITYALERT);
uiFlag = MB_ICONWARNING;
}
MLShellMessageBox(hWnd, pszText, pszTitle, MB_OK | MB_SETFOREGROUND | uiFlag );
if (IsValidHWND(hWnd))
{
FORWARD_WM_COMMAND(hWnd, IDCANCEL, NULL, 0, PostMessage);
}
}
HRESULT SelectPidlInSFV(IShellFolderViewDual *psfv, LPCITEMIDLIST pidl, DWORD dwOpts)
{
VARIANT var;
HRESULT hr = InitVariantFromIDList(&var, pidl);
if (SUCCEEDED(hr))
{
hr = psfv->SelectItem(&var, dwOpts);
VariantClear(&var);
}
return hr;
}
void OpenFolderPidl(LPCITEMIDLIST pidl)
{
SHELLEXECUTEINFO shei = { 0 };
shei.cbSize = sizeof(shei);
shei.fMask = SEE_MASK_INVOKEIDLIST;
shei.nShow = SW_SHOWNORMAL;
shei.lpIDList = (LPITEMIDLIST)pidl;
ShellExecuteEx(&shei);
}
STDAPI OpenContainingFolderAndGetShellFolderView(HWND hwnd, LPCITEMIDLIST pidlFolder, IShellFolderViewDual **ppsfv)
{
*ppsfv = NULL;
IWebBrowserApp *pauto;
HRESULT hr = SHGetIDispatchForFolder(pidlFolder, &pauto);
if (SUCCEEDED(hr))
{
// We have IDispatch for window, now try to get one for
// the folder object...
HWND hwnd;
if (SUCCEEDED(pauto->get_HWND((LONG_PTR *)&hwnd)))
{
// Make sure we make this the active window
SetForegroundWindow(hwnd);
ShowWindow(hwnd, SW_SHOWNORMAL);
}
IDispatch * pautoDoc;
hr = pauto->get_Document(&pautoDoc);
if (SUCCEEDED(hr))
{
hr = pautoDoc->QueryInterface(IID_PPV_ARG(IShellFolderViewDual, ppsfv));
pautoDoc->Release();
}
pauto->Release();
}
return hr;
}
//
// Stolen (and modified) from shell\ext\mydocs2\prop.cpp which was from link.c in shell32.dll
//
void FindTarget(HWND hDlg, LPTSTR pPath)
{
USHORT uSave;
LPITEMIDLIST pidl = ILCreateFromPath( pPath );
if (!pidl)
return;
LPITEMIDLIST pidlLast = ILFindLastID(pidl);
// get the folder, special case for root objects (My Computer, Network)
// hack off the end if it is not the root item
if (pidl != pidlLast)
{
uSave = pidlLast->mkid.cb;
pidlLast->mkid.cb = 0;
}
else
uSave = 0;
LPITEMIDLIST pidlDesk;
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOPDIRECTORY, &pidlDesk)))
{
BOOL fIsDesktopDir = pidlDesk && ILIsEqual(pidl, pidlDesk);
if (fIsDesktopDir || !uSave) // if it's in the desktop dir or pidl == pidlLast (uSave == 0 from above)
{
//
// It's on the desktop...
//
MLShellMessageBox(hDlg, (LPTSTR)IDS_ON_DESKTOP, (LPTSTR)IDS_FIND_TITLE,
MB_OK | MB_ICONINFORMATION | MB_APPLMODAL | MB_TOPMOST);
}
else
{
if (WhichPlatform() == PLATFORM_BROWSERONLY)
{
OpenFolderPidl(pidl);
}
else
{
IShellFolderViewDual *psfv;
if (SUCCEEDED(OpenContainingFolderAndGetShellFolderView(hDlg, uSave ? pidl : pidlDesk, &psfv)))
{
if (uSave)
pidlLast->mkid.cb = uSave;
SelectPidlInSFV(psfv, pidlLast, SVSI_SELECT | SVSI_FOCUSED | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE);
psfv->Release();
}
}
}
ILFree(pidlDesk);
}
ILFree(pidl);
}
BOOL SetExemptDelta(LPCTSTR pszURL, DWORD dwExemptDelta)
{
BOOL fRC;
INTERNET_CACHE_ENTRY_INFO icei;
icei.dwStructSize = sizeof(icei);
icei.dwExemptDelta = dwExemptDelta; // Number of seconds from last access time to keep entry
// Retry setting the exempt delta if it fails since wininet may have either not have created the
// entry yet or might have it locked.
for (int i = 0; i < 5; i++)
{
if (fRC = SetUrlCacheEntryInfo(pszURL, &icei, CACHE_ENTRY_EXEMPT_DELTA_FC))
break;
Sleep(1000);
}
return fRC;
}
INT_PTR CALLBACK DownloadDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static fInBrowseDir = FALSE;
CDownload* pdld = (CDownload*) GetWindowLongPtr(hDlg, DWLP_USER);
DWORD dwExStyle = 0;
TCHAR szURL[MAX_URL_STRING]; // make copies since EndDialog will delete CDownload obj
BOOL fDownloadAborted;
DWNLDMSG4("DownloadDlgProc ", uMsg, wParam, lParam);
if ((pdld == NULL) && (uMsg != WM_INITDIALOG))
{
RIPMSG(TRUE, "CDownload freed (pdld == NULL) && (uMsg != WM_INITDIALOG)");
return FALSE;
}
switch (uMsg) {
case WM_INITDIALOG:
{
TCHAR szYesNo[20];
DWORD dwType = REG_SZ;
DWORD dwSize = ARRAYSIZE(szYesNo);
if (lParam == NULL)
return FALSE;
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
pdld = (CDownload*)lParam;
pdld->_hDlg = hDlg;
EnableMenuItem(GetSystemMenu(hDlg, FALSE), SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(GetSystemMenu(hDlg, FALSE), SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
EnableWindow(GetDlgItem(hDlg, IDD_OPENFILE), FALSE);
EnableWindow(GetDlgItem(hDlg, IDD_BROWSEDIR), FALSE);
// On BiDi Loc Win98 & NT5 mirroring will take care of that
// Need to fix only on BiBi Win95 Loc
if (g_bBiDiW95Loc)
{
SetWindowBits(GetDlgItem(hDlg, IDD_DIR), GWL_EXSTYLE, WS_EX_RTLREADING, WS_EX_RTLREADING);
}
MLLoadString(IDS_DEFDLGTITLE, pdld->_szDefDlgTitle, ARRAYSIZE(pdld->_szDefDlgTitle));
if (pdld->_hwndToolTips = CreateWindowEx(dwExStyle, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_ALWAYSTIP,
CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
hDlg, NULL, HINST_THISDLL, NULL))
{
TOOLINFO ti;
ti.cbSize = sizeof(ti);
ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
ti.hwnd = hDlg;
ti.uId = (UINT_PTR) GetDlgItem(hDlg, IDD_NAME);
ti.lpszText = LPSTR_TEXTCALLBACK;
ti.hinst = HINST_THISDLL;
GetWindowRect((HWND)ti.uId, &ti.rect);
SendMessage(pdld->_hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
}
// make sure we support cross-lang platform
SHSetDefaultDialogFont(hDlg, IDD_NAME);
pdld->SetDismissDialogFlag(FALSE);
if ( SHRegGetUSValue( TEXT("Software\\Microsoft\\Internet Explorer\\Main"),
TEXT("NotifyDownloadComplete"),
&dwType, (void *)szYesNo, &dwSize, FALSE, NULL, 0 ) == ERROR_SUCCESS )
{
pdld->SetDismissDialogFlag(!StrCmpI(szYesNo, TEXT("No")));
}
CheckDlgButton(hDlg, IDD_DISMISS, pdld->GetDismissDialogFlag());
DWNLDMSG("DownloadDlgProc", "Got WM_INITDIALOG");
Animate_OpenEx(GetDlgItem(hDlg, IDD_ANIMATE), HINST_THISDLL, MAKEINTRESOURCE(IDA_DOWNLOAD));
ShowWindow(GetDlgItem(hDlg, IDD_DOWNLOADICON), SW_HIDE);
g_hCritOpMutex = CreateMutexA(NULL, TRUE, "CritOpMutex");
// Automatically start binding if we are posting synchronously.
if (pdld->_IsModal())
{
HRESULT hres = pdld->StartBinding(pdld->_pmk);
ASSERT(pdld->_pmk);
if (FAILED(hres))
{
ProcessStartbindingError(hDlg, MAKEINTRESOURCE(IDS_DOWNLOADFAILED),
pdld->_szDisplay, MB_ICONWARNING, hres);
}
}
return TRUE;
}
case WM_SIZE:
if ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED))
SetWindowText(hDlg, pdld->_szDefDlgTitle);
break;
case WM_NOTIFY:
{
LPTOOLTIPTEXT lpTT = (LPTOOLTIPTEXT) lParam;
if (lpTT->hdr.code == TTN_NEEDTEXT)
{
lpTT->lpszText = pdld->_szURL;
lpTT->hinst = NULL;
}
}
break;
case WM_COMMAND:
DWNLDMSG2("DownloadDlgProc WM_COMMAND id =", GET_WM_COMMAND_ID(wParam, lParam));
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDD_SAVEAS:
if (pdld)
{
pdld->AddRefDLD();
BOOL fSuccess = FALSE;
// Prevent someone from canceling dialog while the shell copy etc. is going on
EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);
// If zone check fails or if we found virus, bail out and remove file from cache.
if (pdld->PerformVirusScan(pdld->_szPath) != S_OK)
{
pdld->_fDeleteFromCache = TRUE;
pdld->EndDialogDLD(IDCANCEL);
break;
}
fSuccess = pdld->_SaveFile();
AddUrlToUrlHistoryStg(pdld->_pwszDisplayName, NULL, NULL, pdld->_fWriteHistory, NULL, NULL, NULL);
// -- BharatS --- Only add to history if Visible ?
IEPlaySound(TEXT("SystemAsterisk"), TRUE);
if (fSuccess)
{
if (pdld->SetDismissDialogFlag(IsDlgButtonChecked(hDlg, IDD_DISMISS) == BST_CHECKED))
{
StrCpyN(szURL, pdld->_szURL, ARRAYSIZE(szURL));
pdld->EndDialogDLD(IDCANCEL);
SetExemptDelta(szURL, 0);
}
else
{
TCHAR szStr[MAX_PATH];
if (MLLoadString(IDS_CLOSE, szStr, ARRAYSIZE(szStr)))
{
SetWindowText(GetDlgItem(hDlg, IDCANCEL), szStr);
}
ShowWindow(GetDlgItem(hDlg, IDD_ANIMATE), SW_HIDE);
ShowWindow(GetDlgItem(hDlg, IDD_DNLDESTTIME), SW_HIDE);
ShowWindow(GetDlgItem(hDlg, IDD_DNLDCOMPLETEICON), SW_SHOW);
ShowWindow(GetDlgItem(hDlg, IDD_DNLDCOMPLETETEXT), SW_SHOW);
ShowWindow(GetDlgItem(hDlg, IDD_DNLDTIME), SW_SHOW);
MLLoadString(IDS_SAVED, szStr, ARRAYSIZE(szStr));
SetDlgItemText(hDlg, IDD_OPENIT, szStr);
MLLoadString(IDS_DOWNLOADCOMPLETE, szStr, ARRAYSIZE(szStr));
SetWindowText(hDlg, szStr);
EnableWindow(GetDlgItem(hDlg, IDD_OPENFILE), TRUE);
EnableWindow(GetDlgItem(hDlg, IDD_BROWSEDIR), TRUE);
pdld->ShowStats();
pdld->ReleaseDLD();
}
}
else
{
pdld->ReleaseDLD();
}
EnableWindow(GetDlgItem(hDlg, IDCANCEL), TRUE);
}
break;
case IDCANCEL: // Cancel on abort, Close on dismiss
if (pdld && IsWindowEnabled(GetDlgItem(hDlg, IDCANCEL)))
{
pdld->AddRefDLD();
fDownloadAborted = pdld->_fDownloadStarted && !pdld->_fDownloadCompleted;
StrCpyN(szURL, pdld->_szURL, ARRAYSIZE(szURL));
if (pdld->_pib)
{
HRESULT hresT;
hresT = pdld->_pib->Abort();
TraceMsg(DM_DOWNLOAD, "DownloadDlgProc::IDCANCEL: called _pib->Abort() hres=%x", pdld->_pib, hresT);
}
pdld->EndDialogDLD(IDCANCEL);
// Download was canceled. Increase exempt time to keep downloaded a bit in case download is resumed
SetExemptDelta(szURL, fDownloadAborted ?60*60*24 :0);
}
break;
case IDD_BROWSEDIR:
if (!fInBrowseDir)
{
pdld->AddRefDLD();
fInBrowseDir = TRUE;
FindTarget(hDlg, pdld->_szSaveToFile);
// Since EndDialogDLD will probably release our structure...
HWND hwndToolTips = pdld->_hwndToolTips;
pdld->_hwndToolTips = NULL;
pdld->EndDialogDLD(IDOK);
if (IsWindow(hwndToolTips))
DestroyWindow(hwndToolTips);
fInBrowseDir = FALSE;
}
#if DEBUG
else
{
TraceMsg(DM_DOWNLOAD, "DownloadDlgProc rcvd IDD_BROWSEDIR msg while already processing IDD_BROWSEDIR");
}
#endif
break;
case IDD_OPENFILE:
StrCpyN(pdld->_szPath, pdld->_szSaveToFile, ARRAYSIZE(pdld->_szPath));
case IDOK:
ShowWindow(GetDlgItem(hDlg, IDD_DISMISS), SW_HIDE);
if (pdld)
{
pdld->AddRefDLD();
if (pdld->_fGotFile)
{
// If zone check fails or if we found virus, bail out and remove file from cache.
if ( pdld->PerformVirusScan(pdld->_szPath) != S_OK )
{
pdld->_fDeleteFromCache = TRUE;
}
else
{
TCHAR szQuotedPath[MAX_PATH];
StrCpyN(szQuotedPath, pdld->_szPath, MAX_PATH);
if (PLATFORM_INTEGRATED == WhichPlatform())
{
PathQuoteSpaces(szQuotedPath);
}
SHELLEXECUTEINFO sei = { sizeof(SHELLEXECUTEINFO),
SEE_MASK_NOZONECHECKS, hDlg, NULL, szQuotedPath, NULL, NULL, SW_SHOWNORMAL, NULL};
if (!ShellExecuteEx(&sei))
{
DWNLDMSG2("ShellExecute failed", GetLastError());
}
}
}
if (!pdld->_fDeleteFromCache)
AddUrlToUrlHistoryStg(pdld->_pwszDisplayName, NULL, NULL, pdld->_fWriteHistory, NULL, NULL, NULL);
// Since EndDialogDLD will probably release our structure...
HWND hwndToolTips = pdld->_hwndToolTips;
pdld->_hwndToolTips = NULL;
StrCpyN(szURL, pdld->_szURL, ARRAYSIZE(szURL));
pdld->EndDialogDLD(!pdld->_fDeleteFromCache ?IDOK :IDCANCEL);
if (IsWindow(hwndToolTips))
DestroyWindow(hwndToolTips);
SetExemptDelta(szURL, 0);
}
break;
}
break;
case WM_ACTIVATE:
if (pdld && pdld->_IsModal())
return FALSE;
else
{
// There can be race conditions here. If the WA_ACTIVATE messages came in reverse
// order, we might end up setting up the wrong hDlg as the active window. As of right now,
// the only thing g_hDlgActive is being used for is for the IsDialogMessage in
// CDownload_MayProcessMessage. And since there is only one tab-able control in this
// dialog, a wrong hDlg in the g_hDlgActive should not hurt.
ENTERCRITICAL;
if (LOWORD(wParam) == WA_INACTIVE)
{
if (g_hDlgActive == hDlg)
{
MDLGMSG(TEXT("being inactivated"), hDlg);
g_hDlgActive = NULL;
}
}
else
{
MDLGMSG(TEXT("being activated"), hDlg);
g_hDlgActive = hDlg;
}
LEAVECRITICAL;
}
break;
case WM_NCDESTROY:
MDLGMSG(TEXT("being destroyed"), hDlg);
ASSERT((pdld && pdld->_IsModal()) || (g_hDlgActive != hDlg));
SetWindowLongPtr(hDlg, DWLP_USER, NULL);
return FALSE;
case WM_DESTROY:
SHRemoveDefaultDialogFont(hDlg);
return FALSE;
default:
return FALSE;
}
return TRUE;
}
void CDownload::ShowStats(void)
{
TCHAR szStr[MAX_PATH];
TCHAR szBytes[MAX_BYTES_STRLEN];
TCHAR szTime[MAX_BYTES_STRLEN];
TCHAR *pszTime = szTime;
DWORD dwSpent = (GetTickCount() - _dwFirstTick);
SetDlgItemText(_hDlg, IDD_NAME, _szDisplay);
MLLoadString(IDS_BYTESTIME, _szBytesCopied, ARRAYSIZE(_szBytesCopied));
StrFromTimeInterval(szTime, ARRAYSIZE(szTime), (dwSpent < 1000) ?1000 :dwSpent, 3);
while(pszTime && *pszTime && *pszTime == TEXT(' '))
pszTime++;
_FormatMessage(_szBytesCopied, szStr, ARRAYSIZE(szStr),
StrFormatByteSize(_dwTotalSize, szBytes, MAX_BYTES_STRLEN), pszTime);
SetDlgItemText(_hDlg, IDD_TIMEEST, szStr);
// division below requires at least 1/2 second to have elapsed.
if (dwSpent < 500)
dwSpent = 500;
_FormatMessage(_szTransferRate, szStr, ARRAYSIZE(szStr),
StrFormatByteSize(_dwTotalSize / ((dwSpent+500)/1000), szBytes, MAX_BYTES_STRLEN));
SetDlgItemText(_hDlg, IDD_TRANSFERRATE, szStr);
SetForegroundWindow(_hDlg);
}
void CDownload::EndDialogDLD(UINT id)
{
if (ReleaseDLD() != 0)
return;
ASSERT(!_fEndDialogCalled);
_fEndDialogCalled = TRUE;
DWNLDMSG2("EndDialogDLD cRef ==", _cRef);
TraceMsg(TF_SHDREF, "CDownload::EndDialogDLD called when _cRef=%d", _cRef);
_fDismissDialog = (IsDlgButtonChecked(_hDlg, IDD_DISMISS) == BST_CHECKED);
if (SHRegSetUSValue(TEXT("Software\\Microsoft\\Internet Explorer\\Main"),
TEXT("NotifyDownloadComplete"),
REG_SZ, _fDismissDialog ?TEXT("no") :TEXT("yes"), _fDismissDialog ?sizeof(TEXT("no")-sizeof(TCHAR)) :sizeof(TEXT("yes")-sizeof(TCHAR)), SHREGSET_FORCE_HKCU) != ERROR_SUCCESS)
{
DWNLDMSG2("SHRegSetUSValue NotifyDownloadComplete failed", GetLastError());
}
// HACK: USER does not send us WM_ACTIVATE when this dialog is
// being destroyed when it was activated. We need to work around
// this bug(?) by cleaning up g_hDlgActive.
if (g_hDlgActive == _hDlg)
{
MDLGMSG(TEXT("EndDialogDLD putting NULL in g_hDlgActive"), _hDlg);
g_hDlgActive = NULL;
}
DestroyWindow(_hDlg);
Release();
}
#define SZEXPLORERKEY TEXT("Software\\Microsoft\\Internet Explorer")
#define SZDOWNLOADDIRVAL TEXT("Download Directory")
// _GetSaveLocation
// - Tries to get the current download directory from the registry
// default is the Desktop
// - Shows the FileSave Dialog
// - If the user changed the download location, save that off into
// the registry for future downloads
// - _szSaveToFile is updated (this will be used by _SaveFile()
//
// Returns TRUE, if successfully done.
//
BOOL _GetSaveLocation(HWND hDlg, LPTSTR pszPath, LPTSTR pszExt, LPTSTR pszSaveToFile, UINT cchSaveToFile, BOOL fUTF8Enabled, UINT uiCP)
{
BOOL fRet = FALSE;
TCHAR * pszSaveTo = NULL;
HKEY hKey;
BOOL fRegFileType = FALSE;
TCHAR szDownloadDir[MAX_PATH];
TCHAR szBuffer[MAX_PATH];
TCHAR szTemp[40];
LPTSTR pszWalk = szBuffer;
int cchWalk = ARRAYSIZE(szBuffer);
int cch;
szDownloadDir[0] = 0;
// If we don't have a download directory in the registry, download to the desktop
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, SZEXPLORERKEY, 0, KEY_READ, &hKey))
{
DWORD dwType, cbData = sizeof(szDownloadDir);
RegQueryValueEx(hKey, SZDOWNLOADDIRVAL, NULL, &dwType, (LPBYTE)szDownloadDir, &cbData);
RegCloseKey(hKey);
}
if (szDownloadDir[0] == 0)
SHGetSpecialFolderPath(NULL, szDownloadDir, CSIDL_DESKTOPDIRECTORY, FALSE);
// Get the file name. If there is no filename. create one called using the string resource in IDS_DOCUMENT
pszSaveTo = PathFindFileName(pszPath);
if (pszSaveTo)
{
DWORD cchData = cchSaveToFile;
// Unescape the filename suggested by wininet.
if (_PrepareURLForDisplayUTF8W(pszSaveTo, pszSaveToFile, &cchData, fUTF8Enabled, uiCP) != S_OK)
StrCpyN(pszSaveToFile, pszSaveTo, cchSaveToFile);
// Strip out any path that may have been encoded
TCHAR * pszSaveToDst = pszSaveToFile;
pszSaveTo = PathFindFileName(pszSaveToFile);
if (pszSaveTo != pszSaveToFile)
{
while(*pszSaveTo)
*pszSaveToDst++ = *pszSaveTo++;
*pszSaveToDst = *pszSaveTo;
}
// Strip out the the cache's typical decoration of "(nn)"
PathUndecorate (pszSaveToFile);
}
else
MLLoadString(IDS_DOCUMENT, pszSaveToFile, cchSaveToFile);
if (!g_fRunningOnNT) // Win9x isn't able to deal with DBCS chars in edit controls when UI lang is non-native OS lang
{
CHAR szBufA[MAX_PATH*2];
int iRC = WideCharToMultiByte(CP_ACP, 0, pszSaveToFile, -1, szBufA, ARRAYSIZE(szBufA), NULL, NULL);
if (iRC == 0) // If we are unable to convert using system code page
*pszSaveToFile = TEXT('\0'); // make suggested file name blank
}
OPENFILENAME OFN = {0};
OFN.lStructSize = sizeof(OFN);
OFN.hwndOwner = hDlg;
OFN.nMaxFile = cchSaveToFile;
OFN.lpstrInitialDir = szDownloadDir;
OFN.lpstrFile = pszSaveToFile;
OFN.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER |
OFN_NOREADONLYRETURN | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR;
if (!pszExt || !*pszExt)
pszExt = PathFindExtension(pszPath);
if (pszExt && *pszExt)
OFN.lpstrDefExt = pszExt;
// Try to get the file type name from the registry. To add to the filter pair strings
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, pszExt, 0, KEY_READ, &hKey))
{
DWORD dwType, cbData = sizeof(szBuffer);
fRegFileType = (ERROR_SUCCESS == RegQueryValueEx(hKey, NULL, NULL, &dwType, (LPBYTE)szBuffer, &cbData));
RegCloseKey(hKey);
}
if (fRegFileType)
{
fRegFileType = FALSE;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, szBuffer, 0, KEY_READ, &hKey))
{
DWORD dwType, cbData = sizeof(szBuffer);
szBuffer[0] = 0;
fRegFileType = ERROR_SUCCESS == RegQueryValueEx(hKey, NULL, NULL, &dwType, (LPBYTE)szBuffer, &cbData);
if (fRegFileType)
{
// Now tack on the second part of the filter pair
int cchBuffer = lstrlen(szBuffer) + 1;
pszWalk = szBuffer + cchBuffer;
cchWalk = ARRAYSIZE(szBuffer) - cchBuffer;
StrCpyN(pszWalk, TEXT("*"), cchWalk);
StrCatBuff(pszWalk, pszExt, --cchWalk); // sub 1 for * above
}
RegCloseKey(hKey);
}
cch = lstrlen(pszWalk);
}
// There was no registry entry for the file type or the entry did not have a default value
// So create the file name type - "<file extension> DOCUMENT"
if (!fRegFileType || !(*szBuffer))
{
szBuffer[0] = 0;
pszWalk = szBuffer;
cchWalk = ARRAYSIZE(szBuffer);
MLLoadString(IDS_EXTDOCUMENT, szTemp, ARRAYSIZE(szTemp));
cch = wnsprintf(pszWalk, cchWalk, szTemp, pszExt, TEXT('\0'), pszExt);
}
// Add in the pair for "*.* All files"
pszWalk += (cch + 1);
cchWalk -= (cch + 1);
MLLoadString(IDS_ALLFILES, szTemp, ARRAYSIZE(szTemp));
StrCpyN(pszWalk, szTemp, cchWalk);
cch = lstrlen(pszWalk) + 1;
pszWalk += cch;
cchWalk -= cch;
StrCpyN(pszWalk, ALLFILE_WILDCARD, cchWalk);
cch = (lstrlen( ALLFILE_WILDCARD )+1); //Add the second NULL to the end of the string
pszWalk += cch;
cchWalk -= cch;
if (cchWalk > 0)
*pszWalk = 0; //because we had some garbage put after memset.
OFN.lpstrFilter = szBuffer;
if ((fRet = (!SHIsRestricted2W(hDlg, REST_NoSelectDownloadDir, NULL, 0)))
&& (fRet = GetSaveFileName(&OFN)))
{
// If the download location was changed, save that off to the registry
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, SZEXPLORERKEY, 0, KEY_WRITE, &hKey))
{
StrCpyN(szBuffer, pszSaveToFile, ARRAYSIZE(szBuffer));
PathRemoveFileSpec(szBuffer);
if (szBuffer[0])
RegSetValueEx(hKey, SZDOWNLOADDIRVAL, 0, REG_SZ, (LPBYTE)szBuffer, CbFromCch(lstrlen(szBuffer) + 1));
RegCloseKey(hKey);
}
}
return fRet;
}
BOOL CDownload::_GetSaveLocation()
{
return ::_GetSaveLocation(_hDlg, _szPath, _szExt, _szSaveToFile, ARRAYSIZE(_szSaveToFile), _fUTF8Enabled, _uiCP);
}
BOOL CDownload::_SaveFile()
{
SHFILEOPSTRUCT fo = { _hDlg, FO_COPY, _szPath, _szSaveToFile, FOF_NOCONFIRMATION | FOF_NOCOPYSECURITYATTRIBS};
// Be sure the strings are double terminated
DWORD dwLen = (DWORD)min(ARRAYSIZE(_szPath), lstrlen(_szPath) + 1);
if (dwLen == 0) // Not likely, but better to fail than trash someone else's data
return FALSE;
_szPath[dwLen] = TEXT('\0');
_szPath[dwLen-1] = TEXT('\0');
dwLen = (DWORD)min(ARRAYSIZE(_szSaveToFile), lstrlen(_szSaveToFile) + 1);
if (dwLen == 0)
return FALSE;
_szSaveToFile[dwLen] = TEXT('\0');
_szSaveToFile[dwLen-1] = TEXT('\0');
// If the file is in the cache, we probably want to delete it from the
// cache to free up some disk space rather than wait for it to be scavenged.
// This is best done after _pib->Release called from ~CDownload.
_fDeleteFromCache = TRUE;
// Copy the file (which is locked, so can't move it) to its target destination.
return !SHFileOperation(&fo);
}
void CDownload::_DeleteFromCache()
{
INTERNET_CACHE_CONFIG_INFO CCInfo;
DWORD dwCCIBufSize = sizeof(CCInfo);
// Obtain the cache directory path.
if (!GetUrlCacheConfigInfo (&CCInfo, &dwCCIBufSize, CACHE_CONFIG_CONTENT_PATHS_FC))
{
ASSERT(FALSE);
}
else if (0 == StrCmpNI (_szPath,
CCInfo.CachePaths[0].CachePath,
lstrlen(CCInfo.CachePaths[0].CachePath)))
{
// Attempt to delete the file from the cache only if resides under
// the cache directory, otherwise we could in theory nuke a preinstalled
// or edited cache entry. Here a prefix match is also a string prefix
// match since .CachePath will have a trailing slash ('/')
DeleteUrlCacheEntry(_szURL);
}
}
void CDownload::OpenUI(IMoniker* pmk, IBindCtx *pbc, BOOL fSaveAs, BOOL fSafe, LPWSTR pwzHeaders, DWORD dwVerb, DWORD grfBINDF, BINDINFO* pbinfo, LPCTSTR pszRedir, UINT uiCP, BOOL fConfirmed)
{
TraceMsg(DM_DOWNLOAD, "CDownLoad::OpenUI called with fSaveAs=%d, verb=%d", fSaveAs, dwVerb);
// CDownload will take ownership pbinfo.
CDownload* pdld = new CDownload(fSaveAs, pwzHeaders, grfBINDF, pbinfo, fSafe, dwVerb, pszRedir, uiCP, fConfirmed);
if (pdld)
{
HWND hwnd = CreateDialogParam(MLGetHinst(),
MAKEINTRESOURCE(DLG_DOWNLOADPROGRESS), NULL, DownloadDlgProc, (LPARAM)pdld);
pwzHeaders = NULL; // Owner is now CDownload
DWNLDMSG2("CDownLoad_OpenUI dialog created", hwnd);
if (hwnd)
{
HRESULT hres = pdld->StartBinding(pmk, pbc);
if (FAILED(hres))
{
TraceMsg(DM_DOWNLOAD, "CDownLoad::OpenUI() - StartBinding() Failed with hres=0x%x!", hres );
ProcessStartbindingError(hwnd, MAKEINTRESOURCE(IDS_DOWNLOADFAILED),
pdld->_szDisplay, MB_ICONWARNING, hres);
}
else
{
ShowWindow(hwnd, SW_SHOWNORMAL);
}
}
else
{
delete pdld;
pdld = NULL;
}
}
if (pwzHeaders)
{
CoTaskMemFree(pwzHeaders);
pwzHeaders = NULL;
}
}
BOOL CDownload_MayProcessMessage(MSG* pmsg)
{
if (g_hDlgActive)
return IsDialogMessage(g_hDlgActive, pmsg);
return FALSE; // not processed
}
class CDownloadThreadParam {
#ifdef DEBUG
const DWORD* _pdwSigniture;
static const DWORD s_dummy;
#endif
public:
DWORD _dwVerb;
DWORD _grfBINDF;
BINDINFO *_pbinfo;
LPWSTR _pszDisplayName;
LPWSTR _pwzHeaders;
BOOL _fSaveAs;
BOOL _fSafe;
BOOL _fConfirmed;
IStream *_pStream;
TCHAR _szRedirURL[MAX_URL_STRING];
UINT _uiCP;
~CDownloadThreadParam()
{
OleFree(_pszDisplayName);
if (_pwzHeaders)
CoTaskMemFree(_pwzHeaders);
if (_pStream)
_pStream->Release();
// CDownload releases our _pbinfo.
}
CDownloadThreadParam(LPWSTR pszDisplayName, LPWSTR pwzHeaders, BOOL fSaveAs, BOOL fSafe=FALSE, DWORD dwVerb=BINDVERB_GET, DWORD grfBINDF = 0, BINDINFO* pbinfo = NULL, LPCTSTR pszRedir=NULL, UINT uiCP=CP_ACP, BOOL fConfirmed=FALSE )
: _pszDisplayName(pszDisplayName), _fSaveAs(fSaveAs), _fSafe(fSafe), _pwzHeaders(pwzHeaders), _pStream(NULL), _dwVerb(dwVerb), _grfBINDF(grfBINDF), _pbinfo(pbinfo), _uiCP(uiCP), _fConfirmed(fConfirmed)
{
#ifdef DEBUG
_pdwSigniture = &s_dummy;
#endif
if (pszRedir && lstrlen(pszRedir))
StrCpyN(_szRedirURL, pszRedir, MAX_URL_STRING - 1);
// CDownload releases our _pbinfo.
}
void SetStream(IStream *pStm)
{
if (_pStream)
{
_pStream->Release();
}
_pStream = pStm;
if (_pStream)
{
_pStream->AddRef();
}
}
};
DWORD CALLBACK IEDownload_ThreadProc(void *pv)
{
CDownloadThreadParam* pdtp = (CDownloadThreadParam*)pv;
HRESULT hr;
IBindCtx *pbc = NULL;
if (pdtp->_pStream)
{
pdtp->_pStream->AddRef();
hr = pdtp->_pStream->Seek(c_li0,STREAM_SEEK_SET,0);
hr = CoGetInterfaceAndReleaseStream(pdtp->_pStream, IID_PPV_ARG(IBindCtx, &pbc));
pdtp->SetStream(NULL);
}
if (pbc == NULL)
CreateBindCtx(0, &pbc);
//winse#12726:Give other thread a chance to finish its work.
Sleep(100);
hr = CDownLoad_OpenUIURL(pdtp->_pszDisplayName, pbc, pdtp->_pwzHeaders, TRUE, pdtp->_fSaveAs, pdtp->_fSafe,
pdtp->_dwVerb, pdtp->_grfBINDF, pdtp->_pbinfo, pdtp->_szRedirURL, pdtp->_uiCP, NULL, pdtp->_fConfirmed);
if (SUCCEEDED(hr))
{
pdtp->_pwzHeaders = NULL; // CDownload owns freeing headers now
pdtp->_pbinfo = NULL; // CDownload owns freeing pbinfo now.
}
delete pdtp;
pdtp = NULL;
if (pbc)
{
pbc->Release();
pbc = NULL;
}
while (1)
{
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
// Note that for IE 3.0, the parking thread is also
// the owner of all modeless download dialog.
if (CDownload_MayProcessMessage(&msg))
continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
continue;
}
WaitMessage();
}
return 0;
}
void CDownLoad_OpenUI(IMoniker *pmk, IBindCtx *pbc, BOOL fSync, BOOL fSaveAs, BOOL fSafe, LPWSTR pwzHeaders, DWORD dwVerb, DWORD grfBINDF, BINDINFO* pbinfo, LPCTSTR pszRedir, UINT uiCP, IUnknown *punk, BOOL fConfirmed)
{
TraceMsg(DM_DOWNLOAD, "CDownLoad_OpenUI called with fSync=%d fSaveAs=%d", fSync, fSaveAs);
ASSERT(dwVerb == BINDVERB_GET || dwVerb == BINDVERB_POST);
if (fSync)
{
CDownload::OpenUI(pmk, pbc, fSaveAs, fSafe, pwzHeaders, dwVerb, grfBINDF, pbinfo, pszRedir, uiCP, fConfirmed);
pwzHeaders = NULL; // CDownload now owns headers
return;
}
IDownloadManager *pdlm;
HRESULT hr = IUnknown_QueryService(punk, SID_SDownloadManager, IID_PPV_ARG(IDownloadManager, &pdlm));
if (FAILED(hr))
{
hr = CreateFromRegKey(TSZIEPATH, TEXT("DownloadUI"), IID_PPV_ARG(IDownloadManager, &pdlm));
}
if (SUCCEEDED(hr))
{
hr = pdlm->Download(pmk, pbc, dwVerb, grfBINDF, pbinfo, pwzHeaders, pszRedir, uiCP);
pdlm->Release();
}
if (FAILED(hr))
{
if (pbc == NULL)
{
hr = CreateBindCtx(0, &pbc);
}
else
{
hr = S_OK;
pbc->AddRef();
}
if (SUCCEEDED(hr))
{
LPWSTR pszDisplayName = NULL;
hr = pmk->GetDisplayName(pbc, NULL, &pszDisplayName);
if (SUCCEEDED(hr))
{
CDownloadThreadParam* pdtp = new CDownloadThreadParam(pszDisplayName, pwzHeaders, fSaveAs, fSafe, dwVerb, grfBINDF, pbinfo, pszRedir, uiCP, fConfirmed);
if (pdtp)
{
pwzHeaders = NULL; // ownership is to CDTP
// Note: IAsyncBindCtx has identicial interface as IBindCtx
IBindCtx *pbcAsync;
hr = pbc->QueryInterface(IID_IAsyncBindCtx, (void **)&pbcAsync);
if (SUCCEEDED(hr))
{
// This introduces a double bind, but only for the mk: protocol and
// the fix is needed for displaying pdfs and other special mime types.
if (_tcsnicmp(pszDisplayName, _T("mk:"), 3) == 0)
{
pbcAsync->Release();
pbcAsync = NULL;
hr = CreateBindCtx(0, &pbcAsync);
}
if (SUCCEEDED(hr))
{
IStream *pStm;
hr = CoMarshalInterThreadInterfaceInStream(IID_IBindCtx, pbcAsync, &pStm);
if (hr == S_OK)
{
pdtp->SetStream(pStm);
pStm->Release();
}
pbcAsync->Release();
}
}
if (!SHCreateThread(IEDownload_ThreadProc, pdtp, CTF_PROCESS_REF | CTF_REF_COUNTED | CTF_COINIT, NULL))
{
delete pdtp;
pdtp = NULL;
}
}
else
{
OleFree(pszDisplayName);
}
}
pbc->Release();
}
}
CoTaskMemFree(pwzHeaders); // may be NULL, we consume this in all cases
}
HRESULT CDownLoad_OpenUIURL(LPCWSTR pwszURL, IBindCtx *pbc, LPWSTR pwzHeaders, BOOL fSync,BOOL fSaveAs, BOOL fSafe, DWORD dwVerb, DWORD grfBINDF, BINDINFO* pbinfo, LPCTSTR pszRedir, UINT uiCP, IUnknown *punk, BOOL fConfirmed)
{
HRESULT hr;
ASSERT(pwszURL);
if (pwszURL)
{
IMoniker* pmk = NULL;
hr = CreateURLMoniker(NULL, pwszURL, &pmk);
if (SUCCEEDED(hr))
{
CDownLoad_OpenUI(pmk, pbc, fSync, fSaveAs, fSafe, pwzHeaders, dwVerb, grfBINDF, pbinfo, pszRedir, uiCP, punk, fConfirmed);
pwzHeaders = NULL; // CDownload now owns headers
pmk->Release();
hr = S_OK;
}
if (pwzHeaders)
CoTaskMemFree(pwzHeaders);
}
else
hr = E_INVALIDARG;
return hr;
}
HRESULT CDownload::StartBinding(IMoniker* pmk, IBindCtx *pbc)
{
ASSERT(_pbc==NULL);
HRESULT hr = S_OK;
if (pbc == NULL)
{
hr = CreateBindCtx(0, &_pbc);
}
else
{
_pbc = pbc;
_pbc->AddRef();
}
if (SUCCEEDED(hr))
{
hr = RegisterBindStatusCallback(_pbc, this, 0, 0);
if (SUCCEEDED(hr))
{
hr = pmk->GetDisplayName(_pbc, NULL, &_pwszDisplayName);
if (SUCCEEDED(hr))
{
TCHAR szBuf[MAX_PATH];
DWORD dwSize = ARRAYSIZE(szBuf);
DWORD dwPUAF = PUAF_NOUI;
DWORD dwPolicy = 0, dwContext = 0;
int cch = lstrlen(_szURL);
if (!cch)
{
SHUnicodeToTChar(_pwszDisplayName, _szURL, ARRAYSIZE(_szURL));
}
TraceMsg(TF_SHDNAVIGATE, "CDld::StartBinding SHUnicodeToTChar returns %d (%s)", cch, _szURL);
// The URL from GetDisplayName() is always fully
// canonicalized and escaped. Prepare it for display.
if (PrepareURLForDisplay(_szURL, szBuf, &dwSize))
FormatUrlForDisplay(szBuf, _szDisplay, ARRAYSIZE(_szDisplay), NULL, 0, TRUE, _uiCP, NULL);
else
FormatUrlForDisplay(_szURL, _szDisplay, ARRAYSIZE(_szDisplay), NULL, 0, TRUE, _uiCP, NULL);
SetWindowText(GetDlgItem(_hDlg, IDD_NAME), _szDisplay);
if (_grfBINDF & BINDF_ENFORCERESTRICTED)
{
dwPUAF |= PUAF_ENFORCERESTRICTED;
}
ZoneCheckUrlEx(_szURL, &dwPolicy, sizeof(dwPolicy), &dwContext, sizeof(dwContext),
URLACTION_SHELL_FILE_DOWNLOAD, dwPUAF, NULL);
dwPolicy = GetUrlPolicyPermissions(dwPolicy);
if ((dwPolicy == URLPOLICY_ALLOW) || (dwPolicy == URLPOLICY_QUERY))
{
IUnknown* punk = NULL;
hr = pmk->BindToStorage(_pbc, NULL, IID_PPV_ARG(IUnknown, &punk));
DWNLDMSG3("StartBinding pmk->BindToStorage returned", hr, punk);
if (SUCCEEDED(hr) || hr == E_PENDING)
{
hr = S_OK;
if (punk)
{
ASSERT(0);
punk->Release();
}
}
else
{
TraceMsg(DM_ERROR, "CDld::StartBinding pmk->BindToStorage failed %x", hr);
HRESULT hrRevoke = RevokeBindStatusCallback( _pbc, this );
ASSERT( SUCCEEDED( hrRevoke ) );
}
}
else
{
TraceMsg(DM_ERROR, "CDld::StartBinding: Zone does not allow file download");
HRESULT hrRevoke = RevokeBindStatusCallback( _pbc, this );
ASSERT( SUCCEEDED( hrRevoke ) );
hr = E_ACCESSDENIED;
}
}
else
{
TraceMsg(DM_ERROR, "CDld::StartBinding pmk->GetDisplayName failed %x", hr);
HRESULT hrRevoke = RevokeBindStatusCallback( _pbc, this );
ASSERT( SUCCEEDED( hrRevoke ) );
}
}
else
{
TraceMsg(DM_ERROR, "CDld::StartBinding RegisterBSC failed %x", hr);
}
}
else
{
TraceMsg(DM_ERROR, "CDld::StartBinding CreateBindCtx failed %x", hr);
}
return hr;
}
HRESULT CDownload::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENT(CDownload, IBindStatusCallback), // IID_IBindStatusCallback
QITABENT(CDownload, IAuthenticate), // IID_IAuthenticate
QITABENT(CDownload, IServiceProvider), // IID_IServiceProvider
QITABENT(CDownload, IHttpNegotiate), // IID_IHttpNegotiate
QITABENT(CDownload, IWindowForBindingUI),
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
ULONG CDownload::AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG CDownload::Release()
{
DWNLDMSG2("CDownload::Release cRef=", _cRef);
if (InterlockedDecrement(&_cRef))
return _cRef;
CDownload* pdld = (CDownload*) GetWindowLongPtr(_hDlg, DWLP_USER);
if (pdld == this)
SetWindowLongPtr(_hDlg, DWLP_USER, NULL);
DWNLDMSG3("CDownload::Release delete this", pdld, this);
delete this;
return 0;
}
ULONG CDownload::AddRefDLD()
{
return InterlockedIncrement(&_cRefDLD);
}
ULONG CDownload::ReleaseDLD()
{
if (InterlockedDecrement(&_cRefDLD))
return _cRefDLD;
return 0;
}
CDownload::~CDownload()
{
if (_pbinfo) {
ReleaseBindInfo(_pbinfo);
LocalFree(_pbinfo);
_pbinfo = NULL;
}
if (_pib) {
_pib->Release();
}
if (_pbc) {
_pbc->Release();
}
if (_hicon) {
DestroyIcon(_hicon);
}
if (_pwszDisplayName)
OleFree(_pwszDisplayName);
if (_fDeleteFromCache)
_DeleteFromCache();
if ( _pwzHeaders )
CoTaskMemFree( _pwzHeaders );
TraceMsg(TF_SHDLIFE, "CDownload::~CDownload being destructed");
TraceMsg(TF_SHDTHREAD, "CDownload::EndDialogDLD calling PostQuitMessage");
// Post the quit message ONLY if this flag is set. The constructor for the
// derived class CDownloadURL resets the flag to FALSE because it doesn't
// need any quit messages.
if (!_fDontPostQuitMsg)
PostQuitMessage(0);
}
#ifdef USE_LOCKREQUEST
HRESULT CDownload::LockRequestHandle(void)
{
HRESULT hres = E_FAIL;
HANDLE hLock;
if (_pib)
{
IWinInetInfo* pwinet;
hres = _pib->QueryInterface(IID_PPV_ARG(IWinInetInfo, &pwinet));
if (SUCCEEDED(hres))
{
DWORD cbSize = sizeof(HANDLE);
hres = pwinet->QueryOption(WININETINFO_OPTION_LOCK_HANDLE, &hLock, &cbSize);
pwinet->Release();
}
}
return hres;
}
#endif
HRESULT CDownload::OnStartBinding(DWORD grfBSCOption, IBinding *pib)
{
DWNLDMSG3("OnStartBinding", _pib, pib);
if (_pib)
{
_pib->Release();
}
_pib = pib;
if (_pib)
{
_pib->AddRef();
}
SetQueryNetSessionCount(SESSION_INCREMENT);
_fUTF8Enabled = UTF8Enabled();
return S_OK;
}
HRESULT CDownload::GetPriority(LONG *pnPriority)
{
DWNLDMSG("GetPriority", "called");
*pnPriority = NORMAL_PRIORITY_CLASS;
return S_OK;
}
HRESULT CDownload::OnLowResource(DWORD reserved)
{
DWNLDMSG("OnLowResource", "called");
return S_OK;
}
#define WM_DIALMON_FIRST WM_USER+100
// message sent to dial monitor app window indicating that there has been
// winsock activity and dial monitor should reset its idle timer
#define WM_WINSOCK_ACTIVITY WM_DIALMON_FIRST + 0
#define MIN_ACTIVITY_MSG_INTERVAL 15000
void IndicateWinsockActivity(void)
{
// if there is an autodisconnect monitor, send it an activity message
// so that we don't get disconnected during long downloads. For perf's sake,
// don't send a message any more often than once every MIN_ACTIVITY_MSG_INTERVAL
// milliseconds (15 seconds). Use GetTickCount to determine interval;
// GetTickCount is very cheap.
DWORD dwTickCount = GetTickCount();
// Sharing this among multiple threads is OK
static DWORD dwLastActivityMsgTickCount = 0;
DWORD dwElapsed = dwTickCount - dwLastActivityMsgTickCount;
// have we sent an activity message recently?
if (dwElapsed > MIN_ACTIVITY_MSG_INTERVAL)
{
HWND hwndMonitorApp = FindWindow(TEXT("MS_AutodialMonitor"), NULL);
if (hwndMonitorApp)
{
PostMessage(hwndMonitorApp, WM_WINSOCK_ACTIVITY, 0, 0);
}
hwndMonitorApp = FindWindow(TEXT("MS_WebcheckMonitor"), NULL);
if (hwndMonitorApp)
{
PostMessage(hwndMonitorApp, WM_WINSOCK_ACTIVITY, 0, 0);
}
// record the tick count of the last time we sent an
// activity message
dwLastActivityMsgTickCount = dwTickCount;
}
}
#define MAXCALCCNT 5
HRESULT CDownload::OnProgress(
ULONG ulProgress,
ULONG ulProgressMax,
ULONG ulStatusCode,
LPCWSTR pwzStatusText)
{
DWNLDMSG4("OnProgress", ulProgress, ulProgressMax, ulStatusCode);
TCHAR szBytes[MAX_BYTES_STRLEN];
TCHAR szBytesMax[MAX_BYTES_STRLEN];
TCHAR szBuf[MAX_PATH]; // OK with MAX_PATH
LPTSTR pszFileName = NULL;
HWND hwndShow;
DWORD dwCur;
switch (ulStatusCode)
{
case BINDSTATUS_BEGINDOWNLOADDATA:
hwndShow = GetDlgItem(_hDlg, ulProgressMax ? IDD_PROBAR : IDD_NOFILESIZE);
if (!IsWindowVisible(hwndShow))
{
ShowWindow(GetDlgItem(_hDlg, ulProgressMax ? IDD_NOFILESIZE : IDD_PROBAR), SW_HIDE);
ShowWindow(hwndShow, SW_SHOW);
}
_ulOldProgress = ulProgress;
// fall thru
case BINDSTATUS_DOWNLOADINGDATA:
case BINDSTATUS_ENDDOWNLOADDATA:
// Prevent machines with APM enabled from suspending during download
_SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
_dwFileSize = max(ulProgressMax, ulProgress);
// every once in a while, send message
// to the hidden window that detects inactivity so that it doesn't
// think we are inactive during a long download
IndicateWinsockActivity();
// Sometimes OnProgress is called by folks who do not create a dialog
if (_hDlg )
{
if (!_fStrsLoaded)
{
MLLoadString(IDS_TITLEPERCENT, _szTitlePercent, ARRAYSIZE(_szTitlePercent));
MLLoadString(IDS_ESTIMATE, _szEstimateTime, ARRAYSIZE(_szEstimateTime));
MLLoadString(IDS_TITLEBYTES, _szTitleBytes, ARRAYSIZE(_szTitleBytes));
MLLoadString(IDS_BYTESCOPIED, _szBytesCopied, ARRAYSIZE(_szBytesCopied));
MLLoadString(IDS_TRANSFERRATE, _szTransferRate, ARRAYSIZE(_szTransferRate));
_fStrsLoaded = TRUE;
}
// Get the file name of the file being downloaded
pszFileName = PathFindFileName(_szURL);
dwCur = GetTickCount();
if (_dwOldCur == 0) // Allow the download to get started before displaying stats
_dwOldCur = dwCur;
if ((ulProgressMax > 0) && _fDownloadStarted)
{
if (_hDlg)
{
SendMessage(GetDlgItem(_hDlg, IDD_PROBAR), PBM_SETRANGE32, 0, _dwFileSize);
SendMessage(GetDlgItem(_hDlg, IDD_PROBAR), PBM_SETPOS, ulProgress, 0);
}
if (!_fFirstTickValid)
{
_dwFirstSize = ulProgress;
_fFirstTickValid = TRUE;
SetWindowText(GetDlgItem(_hDlg, IDD_NAME), _szDisplay);
}
else
{
if ((ulProgress - _dwFirstSize) && _hDlg)
{
// Recompute and display stats at least every second
if ((dwCur - _dwOldCur) >= 1000)
{
_dwOldCur = dwCur; // Save current tick count
TCHAR szTime[32];
DWORD dwSpent = ((dwCur - _dwFirstTick)+500) / 1000;
ULONG ulLeft = _dwFileSize - ulProgress;
DWORD dwRate = _dwOldRate;
dwRate = (ulProgress - _ulOldProgress) / (dwSpent ? dwSpent : 1);
TraceMsg(DM_PROGRESS, "OnProgress ulProgress=%d ulGot=%d dwSpent=%d ulLeft=%d", ulProgress, (ulProgress - _dwFirstSize), dwSpent, ulLeft);
// Compute & display estimated time left to download, bytes so far, total bytes
DWORD dwEst;
if (ulLeft > 0x100000L) // To avoid overflow, use KB for >1MB file.
dwEst = (ulLeft >> 10) / ((dwRate >> 10) ?(dwRate >> 10) :1);
else
dwEst = ulLeft / (dwRate ?dwRate :1);
if (dwEst == 0)
dwEst = 1;
TraceMsg(DM_PROGRESS, "OnProgress Estimated time left = %d", dwEst);
StrFromTimeInterval(szTime, ARRAYSIZE(szTime), dwEst * 1000, 3);
LPTSTR pszTime = szTime;
while(*pszTime && (*pszTime == ' '))
pszTime++;
_FormatMessage(_szEstimateTime, szBuf, ARRAYSIZE(szBuf), pszTime,
StrFormatByteSize(ulProgress, szBytes, MAX_BYTES_STRLEN),
StrFormatByteSize(_dwFileSize, szBytesMax, MAX_BYTES_STRLEN));
TraceMsg(DM_PROGRESS, "OnProgress Estimated string = %s", szBuf);
SetDlgItemText(_hDlg, IDD_TIMEEST, szBuf);
_dwOldEst = dwEst;
// Compute & display transfer rate
if (dwRate != _dwOldRate)
{
_dwOldRate = dwRate;
_FormatMessage(_szTransferRate, szBuf, ARRAYSIZE(szBuf), StrFormatByteSize(dwRate, szBytes, MAX_BYTES_STRLEN));
SetDlgItemText(_hDlg, IDD_TRANSFERRATE, szBuf);
}
}
// Compute & display percentage of download completed
DWORD dwPcent = (100 - MulDiv(_dwFileSize - ulProgress, 100, _dwFileSize));
if (dwPcent != _dwOldPcent)
{
_dwOldPcent = dwPcent;
if (dwPcent == 100) // Don't peg the meter until we've completed
dwPcent = 99;
TCHAR szBuf2[MAX_PATH];
DWORD dwSize = ARRAYSIZE(szBuf2);
if (PrepareURLForDisplay(pszFileName, szBuf2, &dwSize))
_FormatMessage(_szTitlePercent, szBuf, ARRAYSIZE(szBuf), (UINT)dwPcent, szBuf2);
else
_FormatMessage(_szTitlePercent, szBuf, ARRAYSIZE(szBuf), (UINT)dwPcent, pszFileName);
SetWindowText(_hDlg, szBuf);
}
}
}
}
else if (_hDlg && _fDownloadStarted) // Unknown file size, just show bytes and rate
{
// Recompute and display stats at most every second
if ((dwCur - _dwOldCur) >= 1000)
{
_dwOldCur = dwCur; // Save current tick count
DWORD dwSpent = ((dwCur - _dwFirstTick)+500) / 1000;
DWORD dwRate = ulProgress / (dwSpent ? dwSpent : 1);
_FormatMessage(_szBytesCopied, szBuf, ARRAYSIZE(szBuf),
StrFormatByteSize(ulProgress, szBytes, MAX_BYTES_STRLEN));
TraceMsg(DM_PROGRESS, "OnProgress string = %s", szBuf);
SetDlgItemText(_hDlg, IDD_TIMEEST, szBuf);
_FormatMessage(_szTransferRate, szBuf, ARRAYSIZE(szBuf), StrFormatByteSize(dwRate, szBytes, MAX_BYTES_STRLEN));
SetDlgItemText(_hDlg, IDD_TRANSFERRATE, szBuf);
{
TCHAR szBuf2[MAX_PATH];
DWORD dwSize = ARRAYSIZE(szBuf2);
if (PrepareURLForDisplay (pszFileName, szBuf2, &dwSize))
_FormatMessage(_szTitleBytes, szBuf, ARRAYSIZE(szBuf),
StrFormatByteSize(ulProgress, szBytes, MAX_BYTES_STRLEN),szBuf2);
else
_FormatMessage(_szTitleBytes, szBuf, ARRAYSIZE(szBuf),
StrFormatByteSize(ulProgress, szBytes, MAX_BYTES_STRLEN), pszFileName);
SetWindowText(_hDlg, szBuf);
}
}
}
}
break;
default: // ulStatusCode
break;
}
return S_OK;
}
HRESULT CDownload::OnStopBinding(HRESULT hrError, LPCWSTR szError)
{
TraceMsg(DM_DOWNLOAD, "OnStopBinding called with hrError==%x", hrError);
HRESULT hrDisplay = hrError;
AddRef(); // Guard against last Release by _RevokeObjectParam
HRESULT hres = RevokeBindStatusCallback(_pbc, this);
AssertMsg(SUCCEEDED(hres), TEXT("URLMON bug??? RevokeBindStatusCallback failed %x"), hres);
if (_pib)
{
CLSID clsid;
LPWSTR pwszError = NULL;
HRESULT hresT = _pib->GetBindResult(&clsid, (DWORD *)&hrDisplay, &pwszError, NULL);
TraceMsg(TF_SHDBINDING, "DLD::OnStopBinding called GetBindResult %x->%x (%x)", hrError, hrDisplay, hresT);
if (SUCCEEDED(hresT))
{
//
// URLMON returns a native Win32 error.
//
if (hrDisplay && SUCCEEDED(hrDisplay))
hrDisplay = HRESULT_FROM_WIN32(hrDisplay);
if (pwszError)
OleFree(pwszError);
}
// We don't call IBinding::Release until ~CDownload
// because we need to guarantee the download file
// exists until we have copied or executed it.
}
#ifdef DEBUG
if (hrError==S_OK && GetKeyState(VK_CONTROL) < 0)
{
hrError = E_FAIL;
}
#endif
if (FAILED(hrError) && hrError != E_ABORT)
{
IE_ErrorMsgBox(NULL, _hDlg, hrDisplay, szError,_szDisplay, IDS_CANTDOWNLOAD, MB_OK|MB_ICONSTOP);
}
if (g_hCritOpMutex != NULL)
{
CloseHandle(g_hCritOpMutex);
g_hCritOpMutex = NULL;
}
SetQueryNetSessionCount(SESSION_DECREMENT);
if (!_fGotFile || !_fDownloadCompleted)
{
AssertMsg(FAILED(hrError), TEXT("CDownload::OnStopBinding is called, but we've never got a file -- URLMON bug"));
if (!_fEndDialogCalled)
{
FORWARD_WM_COMMAND(_hDlg, IDCANCEL, NULL, 0, PostMessage);
}
}
Release(); // Guard against last Release by _RevokeObjectParam
return S_OK;
}
HRESULT CDownload::GetBindInfo(DWORD* grfBINDINFOF, BINDINFO *pbindinfo)
{
TraceMsg(DM_DOWNLOAD, "DWNLD::GetBindInfo called when _pbinfo==%x", _pbinfo);
if ( !grfBINDINFOF || !pbindinfo || !pbindinfo->cbSize )
return E_INVALIDARG;
if (_pbinfo) {
// Give the ownership to URLMON... shallow copy; don't use CopyBindInfo().
// Don't forget to keep pbindinfo cbSize!
DWORD cbSize = pbindinfo->cbSize;
CopyMemory( pbindinfo, _pbinfo, min(_pbinfo->cbSize, cbSize) );
pbindinfo->cbSize = cbSize;
if (pbindinfo->cbSize > _pbinfo->cbSize)
{
ZeroMemory((BYTE *)pbindinfo + _pbinfo->cbSize, pbindinfo->cbSize - _pbinfo->cbSize);
}
LocalFree(_pbinfo);
_pbinfo = NULL;
} else {
// We don't have a BINDINFO our selves so
// clear BINDINFO except cbSize
DWORD cbSize = pbindinfo->cbSize;
ZeroMemory( pbindinfo, cbSize );
pbindinfo->cbSize = cbSize;
if (UTF8Enabled())
pbindinfo->dwOptions = BINDINFO_OPTIONS_ENABLE_UTF8;
}
// #52524. With post build ~1100, If we do not return the following flags when URLMon calls
// GetBindInfo(), It will bind to the storage synchronously. (judej, danpoz)
*grfBINDINFOF = _grfBINDF | BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
return S_OK;
}
HRESULT CDownload::OnDataAvailable(DWORD grfBSC, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
{
DWORD dwOptions = 0;
DWNLDMSG3("OnDataAvailable (grf,pstg)", grfBSC, pstgmed);
_dwTotalSize = dwSize; // keep track of number of bytes downloaded
if (SUCCEEDED(_GetRequestFlagFromPIB(_pib, &dwOptions)) && (dwOptions & INTERNET_REQFLAG_CACHE_WRITE_DISABLED))
{
_fWriteHistory = FALSE;
}
if (grfBSC & BSCF_LASTDATANOTIFICATION)
{
_fDownloadCompleted = TRUE;
}
//
// This code gets the file name from pstgmed, when it became
// available. URLMon is supposed to pass it even though the file
// is not completely ready yet.
//
if (!_fGotFile && pstgmed)
{
Animate_Stop(GetDlgItem(_hDlg, IDD_ANIMATE));
if (pstgmed->tymed == TYMED_FILE)
{
TCHAR szBuf[MAX_PATH]; // ok with MAX_PATH (because we truncate)
SHUnicodeToTChar(pstgmed->lpszFileName, _szPath, ARRAYSIZE(_szPath));
// Because of redirection the _szURL could be http://.../redir.dll or query.exe.
// Whereas the actual filename would be something else. The Cache filename is generated
// by wininet after it has figured out what the real filename is. However, it might contain
// a "(1)" or a "(2)" at the end of the file name.
TCHAR szURL[MAX_URL_STRING];
StrCpyN(szURL, _szURL, ARRAYSIZE(szURL));
TCHAR * pszURLFName = PathFindFileName(szURL);
TCHAR * pszCacheFName = PathFindFileName(_szPath);
// Unescape the filename suggested by wininet.
DWORD cch = ARRAYSIZE(szBuf);
if (_PrepareURLForDisplayUTF8W(pszCacheFName, szBuf, &cch, _fUTF8Enabled, _uiCP) != S_OK)
StrCpyN(szBuf, pszCacheFName, ARRAYSIZE(szBuf));
// Strip out any path that may have been encoded
pszCacheFName = szBuf;
TCHAR *pszSrc = PathFindFileName(szBuf);
if (pszSrc != szBuf)
{
while(*pszSrc)
*pszCacheFName++ = *pszSrc++;
*pszCacheFName = *pszSrc;
}
// Use the Cache name. pszURLFName point to the file name in szURL. Just overwrite it
if (pszURLFName && szBuf)
{
StrCpyN(pszURLFName, szBuf, ARRAYSIZE(szURL) - ((int)(pszURLFName-szURL)/sizeof(TCHAR)));
FormatUrlForDisplay(szURL, _szDisplay, ARRAYSIZE(_szDisplay), NULL, 0, TRUE, _uiCP, NULL);
}
DWNLDMSG("OnDataAvailable got TYMED_FILE", _szPath);
_fGotFile = TRUE;
TCHAR szMime[MAX_PATH];
if (GetClipboardFormatName(pformatetc->cfFormat, szMime, sizeof(szMime)/sizeof(szMime[0])))
{
MIME_GetExtension(szMime, (LPTSTR) _szExt, SIZECHARS(_szExt));
}
SetWindowText(GetDlgItem(_hDlg, IDD_NAME), _szDisplay);
UINT uRet = _MayAskUserIsFileSafeToOpen(szMime);
switch (uRet) {
case IDOK:
MLLoadString(IDS_OPENING, szBuf, ARRAYSIZE(szBuf));
break;
case IDD_SAVEAS:
_fSaveAs = TRUE;
_fCallVerifyTrust = FALSE;
MLLoadString(IDS_SAVING, szBuf, ARRAYSIZE(szBuf));
break;
case IDCANCEL:
FORWARD_WM_COMMAND(_hDlg, IDCANCEL, NULL, 0, PostMessage);
//
// HACK: Under a certain condition, we receive one more
// OnDataAvailable from URLMON with BSCF_LASTDATANOTIFICATION
// before this posted message is dispatched. It causes
// WinVerifyTrust call, which is wrong. To prevent it,
// we unset this flag.
//
// We still assumes that OnStopBinding will not happen before
// this message is dispatched. In IE 4.0, we should introduce
// another flag (_fCancelled) to make it more robust.
//
_fCallVerifyTrust = FALSE;
return S_OK;
}
SetDlgItemText(_hDlg, IDD_OPENIT, szBuf);
if (_fSaveAs)
{
if (!_GetSaveLocation())
{
FORWARD_WM_COMMAND(_hDlg, IDCANCEL, NULL, 0, PostMessage);
return S_OK;
}
StrCpyN(szBuf, _szSaveToFile, ARRAYSIZE(szBuf));
RECT rect;
GetClientRect(GetDlgItem(_hDlg, IDD_DIR), &rect);
PathCompactPath(NULL, szBuf, rect.right);
}
else
MLLoadString(IDS_DOWNLOADTOCACHE, szBuf, ARRAYSIZE(szBuf));
SetDlgItemText(_hDlg, IDD_DIR, szBuf);
Animate_Play(GetDlgItem(_hDlg, IDD_ANIMATE),0, -1, -1);
if (_dwFirstTick == 0) // Start the timer
_dwFirstTick = GetTickCount();
}
else
{
TraceMsg(DM_WARNING, "CDownload::OnDataAvailable pstgmed->tymed (%d) != TYMED_FILE", pstgmed->tymed);
}
_fDownloadStarted = TRUE;
}
if (_fDownloadCompleted)
{
#ifdef CALL_WVT
if (_fCallVerifyTrust)
{
ShowWindow(_hDlg, SW_HIDE);
UINT uRet = _VerifyTrust(_hDlg, _szPath, _szDisplay);
switch (uRet) {
case IDOK:
break;
default:
// We assume _VerifyTrust always is able to open the file
// passed from URLMON. If it fails, we bail with no UI.
ASSERT(0);
// Fall through
case IDCANCEL:
_fDeleteFromCache = TRUE;
FORWARD_WM_COMMAND(_hDlg, IDCANCEL, NULL, 0, PostMessage);
return S_OK;
}
}
#endif // CALL_WVT
DWNLDMSG3("OnDataAvailable calling Animate_Stop", _hDlg, GetDlgItem(_hDlg, IDD_ANIMATE));
Animate_Stop(GetDlgItem(_hDlg, IDD_ANIMATE));
SendMessage(GetDlgItem(_hDlg, IDD_PROBAR), PBM_SETRANGE32, 0, 100);
SendMessage(GetDlgItem(_hDlg, IDD_PROBAR), PBM_SETPOS, 100, 0);
if (_fSaveAs) {
FORWARD_WM_COMMAND(_hDlg, IDD_SAVEAS, NULL, 0, PostMessage);
} else {
#ifdef USE_LOCKREQUEST
LockRequestHandle(); // Tell wininet that we want the file locked to allow the app to open it.
// This prevents wininet from deleting the file from the cache before the
// app gets a chance to use it. When wininet sees this file is locked, it
// will add the file to the scavenger leak list and attempt to delete the
// file in the future.
#endif
FORWARD_WM_COMMAND(_hDlg, IDOK, NULL, 0, PostMessage);
}
}
return S_OK;
}
HRESULT CDownload::OnObjectAvailable(REFIID riid, IUnknown *punk)
{
DWORD dwOptions = 0;
DWNLDMSG3("OnObjectAvailable (riid,punk)", riid, punk);
if (SUCCEEDED(_GetRequestFlagFromPIB(_pib, &dwOptions)) && (dwOptions & INTERNET_REQFLAG_CACHE_WRITE_DISABLED))
{
_fWriteHistory = FALSE;
}
return S_OK;
}
/* *** IHttpNegotiate *** */
HRESULT CDownload::BeginningTransaction(LPCWSTR szURL, LPCWSTR szHeaders,
DWORD dwReserved, LPWSTR *pszAdditionalHeaders)
{
if ((!_pwzHeaders) || (!pszAdditionalHeaders))
return S_OK;
DWORD cbHeaders = (lstrlenW(_pwzHeaders) + 1) * sizeof(WCHAR);
LPWSTR pwzHeaders = (LPWSTR)CoTaskMemAlloc(cbHeaders + sizeof(WCHAR));
if (pwzHeaders)
{
memcpy (pwzHeaders, _pwzHeaders, cbHeaders);
*pszAdditionalHeaders = pwzHeaders;
}
// Caller owns freeing *pszAdditionalHeaders
return S_OK;
}
HRESULT CDownload::OnResponse(DWORD dwResponseCode, LPCWSTR szResponseHeaders,
LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders)
{
return S_OK;
}
BOOL _RememberFileIsSafeToOpen(LPCTSTR szFileClass)
{
DWORD dwEditFlags;
ULONG cb = sizeof(dwEditFlags);
HRESULT hr;
IQueryAssociations *passoc = NULL;
BOOL bRet = FALSE;
hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &passoc));
if (SUCCEEDED(hr) && passoc)
{
hr = passoc->Init(NULL, szFileClass, NULL, NULL);
if (SUCCEEDED(hr))
{
hr = passoc->GetData(NULL, ASSOCDATA_EDITFLAGS, NULL, &dwEditFlags, &cb);
if (SUCCEEDED(hr))
{
dwEditFlags &= ~FTA_NoEdit;
dwEditFlags |= FTA_OpenIsSafe;
}
}
passoc->Release();
}
if (FAILED(hr))
dwEditFlags = FTA_OpenIsSafe;
return (SHSetValue(HKEY_CLASSES_ROOT, szFileClass, TEXT("EditFlags"),
REG_BINARY, (BYTE*)&dwEditFlags,
sizeof(dwEditFlags)) == ERROR_SUCCESS);
}
struct SAFEOPENDLGPARAM {
LPCTSTR pszFileClass;
LPCTSTR pszFriendlyURL;
LPCTSTR pszURL;
HWND hwndTT;
TCHAR* pszTTText;
LPCTSTR pszCacheFile;
DWORD uiCP;
BOOL fTypeMismatch;
BOOL fShellExecPrompt;
BOOL fPackagerCommandPrompt;
};
INT_PTR CALLBACK SafeOpenDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
UINT id;
static BOOL bCancelled;
SAFEOPENDLGPARAM* param = (SAFEOPENDLGPARAM*) GetWindowLongPtr(hDlg, DWLP_USER);
if ((param == NULL) && (uMsg != WM_INITDIALOG))
return FALSE;
switch (uMsg) {
case WM_INITDIALOG:
{
BOOL fUnsafeFile;
TCHAR szFriendlyName[MAX_DISPLAY_LEN] = {TEXT('\0')};
TCHAR szFriendlyFrom[MAX_DISPLAY_LEN] = {TEXT('\0')};
TCHAR szProcessedURL[MAX_URL_STRING] = {TEXT('\0')};
DWORD dwSize = ARRAYSIZE(szProcessedURL);
if (lParam == NULL)
return FALSE;
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
param = (SAFEOPENDLGPARAM*)lParam;
// init unsafe file to mismatch between progid and file
fUnsafeFile = param->fTypeMismatch;
// Determine whether or not to gray out the Always ask checkbox. We wil gray out in the following cases
// 1. If we were not told what the file class is
// 2. If the file class is in the unsafe extensions list
// 3. if the file extension in the URL is in the unsafe extensions list
// 4. if the cache file extension is in the unsafe extensions list (if we are redirected)
TCHAR * pszExt = NULL;
TCHAR * pszCacheExt = NULL;
if (param->pszURL)
pszExt = PathFindExtension(param->pszURL);
if (param->pszCacheFile)
pszCacheExt = PathFindExtension(param->pszCacheFile);
if(param->fPackagerCommandPrompt)
{
fUnsafeFile = TRUE;
}
else if (pszExt || pszCacheExt)
{
if (pszExt && AssocIsDangerous(pszExt))
fUnsafeFile = TRUE;
else if (pszCacheExt && AssocIsDangerous(pszCacheExt))
fUnsafeFile = TRUE;
}
else
{
fUnsafeFile = TRUE;
}
if (fUnsafeFile || SHRestricted2(REST_AlwaysPromptWhenDownload, NULL, 0))
EnableWindow(GetDlgItem(hDlg, IDC_SAFEOPEN_ALWAYS), FALSE);
// The check box is always checked by default
CheckDlgButton(hDlg, IDC_SAFEOPEN_ALWAYS, TRUE);
// adjust the warning
if (fUnsafeFile)
{
HICON hIcon = (HICON)LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDI_PRIVACY_WARN),
IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
if (hIcon != NULL)
SendDlgItemMessage(hDlg, IDC_SAFEOPEN_WARNICON, STM_SETICON, (WPARAM)hIcon, 0);
}
else
{
ShowWindow(GetDlgItem(hDlg, IDC_SAFEOPEN_WARNTEXT), SW_HIDE);
}
// cross-lang platform support
SHSetDefaultDialogFont(hDlg, IDC_SAFEOPEN_FILENAME);
SHSetDefaultDialogFont(hDlg, IDC_SAFEOPEN_FILETYPE);
SHSetDefaultDialogFont(hDlg, IDC_SAFEOPEN_FILEFROM);
// Get the URL for the tooltip. Also get URL for the display string if we weren't passed one
if (param->pszURL)
{
if (!PrepareURLForDisplay(param->pszURL, szProcessedURL, &dwSize))
{
dwSize = ARRAYSIZE(szProcessedURL);
StrCpyN(szProcessedURL, param->pszURL, dwSize);
}
}
// Now figure out what we want to display
if(param->fPackagerCommandPrompt)
{
// If this was a packager command line, then just display the full command as passed in param->pszURL
StrCpyN(szFriendlyName, param->pszURL, ARRAYSIZE(szFriendlyName));
}
else
{
FormatUrlForDisplay((LPTSTR)param->pszURL, szFriendlyName, ARRAYSIZE(szFriendlyName), szFriendlyFrom, ARRAYSIZE(szFriendlyFrom),
TRUE, param->uiCP, (PWSTR)param->pszCacheFile);
}
SetDlgItemText(hDlg, IDC_SAFEOPEN_FILENAME, szFriendlyName);
if(param->fPackagerCommandPrompt)
{
// If it was a packager command line, then display "Unknown" for the from
MLLoadString(IDS_VALUE_UNKNOWN, szFriendlyFrom, ARRAYSIZE(szFriendlyFrom));
SetDlgItemText(hDlg, IDC_SAFEOPEN_FILEFROM, szFriendlyFrom);
}
else
{
if (szFriendlyFrom[0] != '\0')
SetDlgItemText(hDlg, IDC_SAFEOPEN_FILEFROM, szFriendlyFrom);
}
if (param->pszFileClass || pszCacheExt)
{
DWORD cchName = ARRAYSIZE(szFriendlyName);
if (SUCCEEDED(AssocQueryString(0, ASSOCSTR_FRIENDLYDOCNAME,
(param->pszFileClass ? param->pszFileClass : pszCacheExt), NULL, szFriendlyName, &cchName)))
{
SetDlgItemText(hDlg, IDC_SAFEOPEN_FILETYPE, szFriendlyName);
}
}
int cch = lstrlen(szProcessedURL) + 1;
param->pszTTText = (TCHAR*)LocalAlloc(LPTR, cch * sizeof(TCHAR));
if (param->pszTTText)
{
StrCpyN(param->pszTTText, szProcessedURL, cch);
if (param->hwndTT = CreateWindow(TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_ALWAYSTIP,
CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
hDlg, NULL, HINST_THISDLL, NULL))
{
TOOLINFO ti;
ti.cbSize = sizeof(ti);
ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
ti.hwnd = hDlg;
ti.uId = (UINT_PTR) GetDlgItem(hDlg, IDC_SAFEOPEN_FILENAME);
ti.lpszText = LPSTR_TEXTCALLBACK;
ti.hinst = HINST_THISDLL;
GetWindowRect((HWND)ti.uId, &ti.rect);
SendMessage(param->hwndTT, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
SendMessage(param->hwndTT, TTM_SETMAXTIPWIDTH, 0, 300);
}
}
if (param->fShellExecPrompt)
{
EnableWindow(GetDlgItem(hDlg, IDC_SAFEOPEN_AUTOSAVE), FALSE);
// make Cancel the default action
SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDCANCEL), TRUE);
}
else
{
// make Save the default action
SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDC_SAFEOPEN_AUTOSAVE), TRUE);
}
return FALSE;
}
case WM_NOTIFY:
{
LPTOOLTIPTEXT lpTT = (LPTOOLTIPTEXT) lParam;
if (lpTT->hdr.code == TTN_NEEDTEXT)
{
lpTT->lpszText = param->pszTTText;
lpTT->hinst = NULL;
}
break;
}
case WM_DESTROY:
//deal with checkbox
if ((!bCancelled) && (!IsDlgButtonChecked(hDlg, IDC_SAFEOPEN_ALWAYS)) && param->pszURL)
{
// Now save EditFlags at the key value value that the filetypes dialog will get/set.
TCHAR * pszExt = PathFindExtension(param->pszURL);
if (*pszExt)
{
TCHAR szFileClass[MAX_PATH];
ULONG cb = sizeof(szFileClass);
*szFileClass = TEXT('\0');
SHGetValue(HKEY_CLASSES_ROOT, pszExt, NULL, NULL, szFileClass, &cb);
if (*szFileClass)
_RememberFileIsSafeToOpen(szFileClass);
}
}
SHRemoveDefaultDialogFont(hDlg);
if (IsWindow(param->hwndTT))
DestroyWindow(param->hwndTT);
if (param->pszTTText)
{
LocalFree(param->pszTTText);
param->pszTTText = NULL;
}
return FALSE;
case WM_COMMAND:
id = GET_WM_COMMAND_ID(wParam, lParam);
switch (id)
{
case IDC_SAFEOPEN_AUTOOPEN:
EndDialog(hDlg, IDOK);
break;
case IDC_SAFEOPEN_AUTOSAVE:
EndDialog(hDlg, IDD_SAVEAS);
break;
case IDM_MOREINFO:
SHHtmlHelpOnDemandWrap(hDlg, TEXT("iexplore.chm > iedefault"), HH_DISPLAY_TOPIC, (DWORD_PTR) TEXT("filedown.htm"), ML_CROSSCODEPAGE);
break;
case IDCANCEL:
bCancelled = TRUE;
EndDialog(hDlg, id);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
UINT _ShowSafeOpenDialog(HWND hwnd, UINT idRes, SAFEOPENDLGPARAM *pparam)
{
UINT uRet = -1;
// is whistler ?
if (IsOS(OS_WHISTLERORGREATER))
{
HMODULE hmod = LoadLibrary(TEXT("xpsp1res.dll"));
if (hmod)
{
uRet = (UINT) DialogBoxParam(hmod, MAKEINTRESOURCE(idRes), hwnd, SafeOpenDlgProc, (LPARAM)pparam);
FreeLibrary(hmod);
}
}
if (uRet == -1)
uRet = (UINT) DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(idRes), hwnd, SafeOpenDlgProc, (LPARAM)pparam);
return uRet;
}
UINT OpenSafeOpenDialog(HWND hwnd, UINT idRes, LPCTSTR pszFileClass, LPCTSTR pszURL, LPCTSTR pszRedirURL, LPCTSTR pszCacheName, LPCTSTR pszDisplay, UINT uiCP, IUnknown *punk, BOOL fTypeMismatch)
{
IDownloadManager *pdlm;
HRESULT hr = IUnknown_QueryService(punk, SID_SDownloadManager, IID_PPV_ARG(IDownloadManager, &pdlm));
if (SUCCEEDED(hr))
{
pdlm->Release();
return IDD_SAVEAS;
}
LPCTSTR pszTemp = pszURL;
if (pszRedirURL && lstrlen(pszRedirURL))
pszTemp = pszRedirURL;
SAFEOPENDLGPARAM param = { pszFileClass, pszDisplay, pszTemp, 0, 0, pszCacheName, uiCP, fTypeMismatch, FALSE, FALSE};
return _ShowSafeOpenDialog(hwnd, idRes, &param);
}
STDAPI_(BOOL) SafeOpenPromptForShellExec(HWND hwnd, PCWSTR pszFile)
{
SAFEOPENDLGPARAM param = { PathFindExtension(pszFile), NULL, pszFile, 0, 0, NULL, CP_ACP, TRUE, TRUE, FALSE};
return IDOK == _ShowSafeOpenDialog(hwnd, DLG_SAFEOPEN, &param);
}
STDAPI_(BOOL) SafeOpenPromptForPackager(HWND hwnd, PCWSTR pszFile, BOOL bFromCommandLine)
{
SAFEOPENDLGPARAM param = { PathFindExtension(pszFile), NULL, pszFile, 0, 0, NULL, CP_ACP, TRUE, TRUE, bFromCommandLine};
return IDOK == _ShowSafeOpenDialog(hwnd, DLG_SAFEOPEN, &param);
}
BOOL _OpenIsSafe(LPCTSTR pszClass)
{
BOOL bRet = FALSE;
IQueryAssociations *passoc;
HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &passoc));
if (SUCCEEDED(hr))
{
hr = passoc->Init(NULL, pszClass, NULL, NULL);
if (SUCCEEDED(hr))
{
DWORD dwEditFlags;
ULONG cb = sizeof(dwEditFlags);
hr = passoc->GetData(NULL, ASSOCDATA_EDITFLAGS, NULL, &dwEditFlags, &cb);
if (SUCCEEDED(hr))
{
if (dwEditFlags & FTA_OpenIsSafe)
bRet = TRUE;
}
}
passoc->Release();
}
return bRet;
}
UINT MayOpenSafeOpenDialog(HWND hwnd,
LPCTSTR pszFileClass,
LPCTSTR pszURL,
LPCTSTR pszCacheName,
LPCTSTR pszDisplay,
UINT uiCP,
IUnknown * punk,
IOleCommandTarget * pCmdTarget = NULL,
BOOL fDisableOpen = FALSE)
{
// Has some association
UINT uiRet = IDIGNORE; // default for no dlg displayed
const LPCTSTR c_szExcluded[] = {TEXT(".ins"),TEXT(".isp")};
const LPCTSTR c_szNoZoneCheckExtns[] = {TEXT(".cdf")};
BOOL fSafe = _OpenIsSafe(pszFileClass);
// We will not do Zone check on CDF files..#56297.
if (!IsTypeInList(pszFileClass, c_szNoZoneCheckExtns, ARRAYSIZE(c_szNoZoneCheckExtns)))
{
DWORD dwPolicy = 0, dwContext = 0;
ZoneCheckUrlEx(pszURL, &dwPolicy, sizeof(dwPolicy), &dwContext, sizeof(dwContext),
URLACTION_SHELL_FILE_DOWNLOAD, PUAF_NOUI, NULL);
dwPolicy = GetUrlPolicyPermissions(dwPolicy);
if ((dwPolicy != URLPOLICY_ALLOW) && (dwPolicy != URLPOLICY_QUERY))
{
ProcessStartbindingError(NULL, NULL, NULL, MB_ICONWARNING, E_ACCESSDENIED);
return IDCANCEL;
}
}
// Always ask for certain the types that we know to be unsafe. We will allow .ins and .isp
// files through for the ICW folks.
if (AssocIsDangerous(pszFileClass) &&
!IsTypeInList(pszFileClass, c_szExcluded, ARRAYSIZE(c_szExcluded)))
fSafe = FALSE;
if (!fSafe || SHRestricted2(REST_AlwaysPromptWhenDownload, NULL,0))
{
VARIANT varOut = {0};
if (pCmdTarget)
{
pCmdTarget->Exec(&CGID_ShellDocView, SHDVID_FIREFILEDOWNLOAD, 0, NULL, &varOut);
}
if ((V_VT(&varOut) != VT_BOOL) || (VARIANT_FALSE == V_BOOL(&varOut)))
{
uiRet = OpenSafeOpenDialog(hwnd,
DLG_SAFEOPEN,
pszFileClass,
pszURL,
NULL,
pszCacheName,
pszDisplay,
uiCP,
punk,
fDisableOpen);
}
}
if (uiRet != IDOK && uiRet != IDD_SAVEAS && uiRet != IDIGNORE)
DeleteUrlCacheEntry(pszURL);
return(uiRet);
}
#ifdef CALL_WVT
// Returns:
//
// IDOK -- If it's trusted
// IDNO -- If it's not known (warning dialog requried)
// IDCANCEL -- We need to stop download it
//
UINT _VerifyTrust(HWND hwnd, LPCTSTR pszFileName, LPCWSTR pszStatusText)
{
UINT uRet = IDNO; // assume unknown
HANDLE hFile = CreateFile(pszFileName, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
HRESULT hres = g_wvt.VerifyTrust(hFile, hwnd, pszStatusText);
if (SUCCEEDED(hres))
{
uRet = IDOK;
}
else
{
ASSERT((hres != HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND)) &&
(hres != HRESULT_FROM_WIN32(ERROR_DLL_NOT_FOUND)));
uRet = IDCANCEL;
}
CloseHandle(hFile);
}
else
{
TraceMsg(DM_WARNING, "_VerifyTrust CreateFile failed %x", GetLastError());
}
TraceMsg(DM_WVT, "_VerifyTrust returning %d", uRet);
return uRet;
}
#endif // CALL_WVT
//
// Returns:
// IDOK: Continue download and open it
// IDD_SAVEAS: Save it as a file
// IDCANCEL: Stop downloading
//
UINT CDownload::_MayAskUserIsFileSafeToOpen(LPCTSTR pszMime)
{
if (_fSaveAs || _fSafe)
{
return (_fSaveAs ? IDD_SAVEAS : IDOK); // no need to ask
}
// Force save as dialog if we are using SSL and
// HKCU\software\microsoft\windows\currentversion\internet settings\DisableCachingOfSSLPages is set
DWORD dwValue;
DWORD dwDefault = 0;
DWORD dwSize;
dwSize = sizeof(dwValue);
SHRegGetUSValue(TSZWININETPATH, TEXT("DisableCachingOfSSLPages"), NULL, (LPBYTE)&dwValue, &dwSize, FALSE, (void *) &dwDefault, sizeof(dwDefault));
if (dwValue != 0 && URL_SCHEME_HTTPS == GetUrlScheme(_szURL))
{
return(IDD_SAVEAS);
}
if(_fConfirmed)
{
return IDOK;
}
BOOL fUnknownType = TRUE;
UINT uRet = IDNO; // assume no extension or no association
LPTSTR pszExt = PathFindExtension(_szPath);
if (*pszExt)
{
TCHAR szFileClass[MAX_PATH];
memset(szFileClass, 0, ARRAYSIZE(szFileClass));
#ifdef CALL_WVT
//
// If this is an EXE and we have WINTRUST ready to call,
// don't popup any UI here at this point.
if ((StrCmpI(pszExt, TEXT(".exe"))==0) && SUCCEEDED(g_wvt.Init()))
{
TraceMsg(DM_WVT, "_MayAskUIFSTO this is EXE, we call _VerifyTrust later");
_fCallVerifyTrust = TRUE;
}
#endif // CALL_WVT
ULONG cb = SIZEOF(szFileClass);
if ((RegQueryValue(HKEY_CLASSES_ROOT, pszExt, szFileClass, (LONG*)&cb)
== ERROR_SUCCESS) && * szFileClass)
{
fUnknownType = FALSE;
uRet = MayOpenSafeOpenDialog(_hDlg, szFileClass, _szURL, _szPath, _szDisplay, _uiCP, NULL, NULL, FALSE);
if (uRet == IDIGNORE) // caller doesn't recognize IDIGNORE
uRet = IDOK;
}
}
if (fUnknownType)
{
uRet = OpenSafeOpenDialog(_hDlg, DLG_SAFEOPEN, NULL, _szURL, NULL, _szPath, _szDisplay, _uiCP, NULL, FALSE);
}
return uRet;
}
// *** IAuthenticate ***
HRESULT CDownload::Authenticate(HWND *phwnd, LPWSTR *pszUsername, LPWSTR *pszPassword)
{
if (!phwnd || !pszUsername || !pszPassword)
return E_POINTER;
*phwnd = _hDlg;
*pszUsername = NULL;
*pszPassword = NULL;
return S_OK;
}
HRESULT CDownload::GetWindow(REFGUID RefGUID, HWND *phWnd)
{
if (IsEqualGUID(RefGUID, IID_IHttpSecurity))
{
*phWnd = _hDlg;
return S_OK;
}
else
return E_FAIL;
}
// *** IServiceProvider ***
HRESULT CDownload::QueryService(REFGUID guidService, REFIID riid, void **ppvObj)
{
*ppvObj = NULL;
if (IsEqualGUID(guidService, IID_IAuthenticate))
{
return QueryInterface(riid, ppvObj);
}
return E_FAIL;
}
// S_OK : continue with operation
// S_FALSE : cancel operation.
HRESULT CDownload::PerformVirusScan(LPCTSTR szFileName)
{
HRESULT hr = S_OK; // default to accepting the file
IVirusScanner *pvs;
if (SUCCEEDED(CreateFromRegKey(TSZIEPATH, TEXT("VirusScanner"), IID_PPV_ARG(IVirusScanner, &pvs))))
{
STGMEDIUM stg;
WCHAR wszFileName[MAX_PATH];
VIRUSINFO vi;
vi.cbSize = sizeof(VIRUSINFO);
//
// VIRUSINFO lpszFileName is not defined as 'const' so we need to copy
// szFileName into a buffer. If it really should be const get rid of
// this copy and use a cast.
//
StrCpyN(wszFileName, szFileName, ARRAYSIZE(wszFileName));
stg.tymed = TYMED_FILE;
stg.lpszFileName = wszFileName;
stg.pUnkForRelease = NULL;
hr = pvs->ScanForVirus(_hDlg, &stg, _pwszDisplayName, SFV_DELETE, &vi);
switch (hr) {
case S_OK:
break;
case VSCAN_E_NOPROVIDERS: //No virus scanning providers
case VSCAN_E_CHECKPARTIAL: //Atleast one of providers didn't work.
case VSCAN_E_CHECKFAIL: //No providers worked.
hr = S_OK;
break;
case VSCAN_E_DELETEFAIL: //Tried to delete virus file but failed.
case S_FALSE: // Virus found.
hr = E_FAIL;
break;
// If some bizarre result, continue on.
default:
hr = S_OK;
break;
}
pvs->Release();
}
return hr;
}
// Starts a download of a file in its own window.
// This function is exported and called by HTML doc object.
// Someday we probably want to put this in a COM interface.
// Currently it just calls the internal function CDownLoad_OpenUIURL.
STDAPI DoFileDownload(LPCWSTR pwszURL)
{
return CDownLoad_OpenUIURL(pwszURL, NULL, NULL, FALSE,TRUE);
}
STDAPI DoFileDownloadEx(LPCWSTR pwszURL, BOOL fSaveAs)
{
return CDownLoad_OpenUIURL(pwszURL, NULL, NULL, FALSE, fSaveAs);
}
#ifdef DEBUG
const DWORD CDownloadThreadParam::s_dummy = 0;
#endif