#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
