⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ftpeidl.cpp

📁 很好用的ftp源码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*****************************************************************************
 *
 *    ftpeidl.cpp - IEnumIDList interface
 *
 *    FtpNameCache
 *
 *    Enumerating an FTP site is an expensive operation, because
 *    it can entail dialing the phone, connecting to an ISP, then
 *    connecting to the site, logging in, cd'ing to the appropriate
 *    location, pumping over an "ls" command, parsing the result,
 *    then closing the connection.
 *
 *    So we cache the results of an enumeration inside a pidl list.
 *    If the user does a REFRESH, then we toss the list and create
 *    a new one.
 *
 *    NOTE! that the WinINet API does not allow a FindFirst to be
 *    interrupted.  In other words, once you do an FtpFindFirst,
 *    you must read the directory to completion and close the
 *    handle before you can do anything else to the site.
 *
 *    As a result, we cannot use lazy evaluation on the enumerated
 *    contents.  (Not that it helps any, because WinINet will just
 *    do an "ls", parse the output, and then hand the items back
 *    one element at a time via FtpFindNext.  You may as well suck them
 *    all down the moment they're ready.)
 *
\*****************************************************************************/

#include "priv.h"
#include "ftpeidl.h"
#include "view.h"
#include "util.h"


/*****************************************************************************
 *
 *    We actually cache the result of the enumeration in the parent
 *    FtpDir, because FTP enumeration is very expensive.
 *
 *    Since DVM_REFRESH forces us to re-enumerate, but we might have
 *    outstanding IEnumIDList's, we need to treat the object cache
 *    as yet another object that needs to be refcounted.
 *
 *****************************************************************************/


/*****************************************************************************
 *    _fFilter
 *
 *    Decides whether the file attributes agree with the filter criteria.
 *
 *    If hiddens are excluded, then exclude hiddens.  (Duh.)
 *
 *    Else, include or exclude based on folder/nonfolder-ness.
 *
 *    Let's look at that expression in slow motion.
 *
 *    "The attributes pass the filter if both...
 *        (1) it passes the INCLUDEHIDDEN criterion, and
 *        (2) it passes the FOLDERS/NONFOLDERS criterion.
 *
 *    The INCLUDEHIDDEN criterion is passed if FILE_ATTRIBUTE_HIDDEN
 *    implies SHCONTF_INCLUDEHIDDEN.
 *
 *    The FOLDERS/NONFOLDERS criterion is passed if the appropriate bit
 *    is set in the shcontf, based on the actual type of the file."
 *****************************************************************************/
BOOL CFtpEidl::_fFilter(DWORD shcontf, DWORD dwFAFLFlags)
{
    BOOL fResult = FALSE;

    if (shcontf & SHCONTF_FOLDERS)
        fResult |= dwFAFLFlags & FILE_ATTRIBUTE_DIRECTORY;

    if (shcontf & SHCONTF_NONFOLDERS)
        fResult |= !(dwFAFLFlags & FILE_ATTRIBUTE_DIRECTORY);

    if ((dwFAFLFlags & FILE_ATTRIBUTE_HIDDEN) && !(shcontf & SHCONTF_INCLUDEHIDDEN))
        fResult = FALSE;

    return fResult;
}


/*****************************************************************************\
 *    _AddFindDataToPidlList
 *
 *    Add information in a WIN32_FIND_DATA to the cache.
 *    Except that dot and dotdot don't go in.
\*****************************************************************************/
HRESULT CFtpEidl::_AddFindDataToPidlList(LPCITEMIDLIST pidl)
{
    HRESULT hr = E_FAIL;

    if (EVAL(m_pflHfpl))
    {
        ASSERT(IsValidPIDL(pidl));
        hr = m_pflHfpl->InsertSorted(pidl);
    }
    
    return hr;
}


/*****************************************************************************\
    FUNCTION: _HandleSoftLinks

    DESCRIPTION:
        A softlink is a file on an UNIX server that reference another file or
    directory.  We can detect these by the fact that (pwfd->dwFileAttribes == 0).
    If that is true, we have some work to do.  First we find out if it's a file
    or a directory by trying to ChangeCurrentWorking directories into it.  If we
    can we turn the dwFileAttributes from 0 to (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT).
    If it's just a softlink to a file, then we change it to
    (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_REPARSE_POINT).  We later use the
    FILE_ATTRIBUTE_REPARSE_POINT attribute to put the shortcut overlay on it to
    que the user.

    RETURN VALUE:
        HRESULT - If FAILED() is returned, the item will not be added to the
                  list view.
\*****************************************************************************/
HRESULT CFtpEidl::_HandleSoftLinks(HINTERNET hint, LPITEMIDLIST pidl, LPWIRESTR pwCurrentDir, DWORD cchSize)
{
    HRESULT hr = S_OK;

    // Is it a softlink? It just came in off the wire and wininet returns 0 (zero)
    // for softlinks.  This function will determine if it's a SoftLink to a file
    // or a directory and then set FILE_ATTRIBUTE_REPARSE_POINT or
    // (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT) respectively.
    if (0 == FtpPidl_GetAttributes(pidl))
    {
        LPCWIRESTR pwWireFileName = FtpPidl_GetFileWireName(pidl);

        // Yes, so I will need to attempt to CD into that directory to test if it's a directory.
        // I need to get back because ".." won't work.  I will cache the return so I don't keep
        // getting it if there is a directory full of them.

        // Did we get the current directory yet?  This is the bread crums so I can
        // find my way back.
        if (!pwCurrentDir[0])
            EVAL(SUCCEEDED(FtpGetCurrentDirectoryWrap(hint, TRUE, pwCurrentDir, cchSize)));

        // Yes, so is it a directory?
        if (SUCCEEDED(FtpSetCurrentDirectoryPidlWrap(hint, TRUE, pidl, FALSE, FALSE)))  // Relative CD
        {
            // Does it have a virtual root?
            if (m_pfd->GetFtpSite()->HasVirtualRoot())
            {
                LPCITEMIDLIST pidlVirtualRoot = m_pfd->GetFtpSite()->GetVirtualRootReference();
                LPITEMIDLIST pidlSoftLinkDest = NULL;
                CWireEncoding * pwe = m_pfd->GetFtpSite()->GetCWireEncoding();

                // Yes, so we need to make sure this dir softlink doesn't point
                // outside of the virtual root, or it would cause invalid FTP URLs.
                // File SoftLinks are fine because the old FTP Code abuses FTP URLs.
                // I'm just not ready to drop my morals just yet.
                if (EVAL(SUCCEEDED(FtpGetCurrentDirectoryPidlWrap(hint, TRUE, pwe, &pidlSoftLinkDest))))
                {
                    if (!FtpItemID_IsParent(pidlVirtualRoot, pidlSoftLinkDest))
                    {
                        // This is a Softlink or HardLink to a directory outside of the virtual root.
                        hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);  // Skip this one.
                    }

                    ILFree(pidlSoftLinkDest);
                }
            }

            // Return to where we came from.
            //TraceMsg(TF_WININET_DEBUG, "_HandleSoftLinks FtpSetCurrentDirectory(%hs) worked", pwWireFileName);
            EVAL(SUCCEEDED(FtpSetCurrentDirectoryWrap(hint, TRUE, pwCurrentDir)));  // Absolute CD
            FtpPidl_SetAttributes(pidl, (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT));
            FtpPidl_SetFileItemType(pidl, TRUE);
        }
        else    // No, it's one of those files w/o extensions.
        {
            TraceMsg(TF_WININET_DEBUG, "_HandleSoftLinks FtpSetCurrentDirectory(%s) failed", pwWireFileName);
            FtpPidl_SetAttributes(pidl, (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_REPARSE_POINT));
            FtpPidl_SetFileItemType(pidl, FALSE);
        }
    }

    return hr;
}


/*****************************************************************************\
 *    CFtpEidl::_PopulateItem
 *
 *    Fill a cache with stuff.
 *
 *    BUGBUG -- EEK!  Some ftp servers (e.g., ftp.funet.fi) run with ls -F!
 *    This means that things get "*" appended to them if they are executable.
\*****************************************************************************/
HRESULT CFtpEidl::_PopulateItem(HINTERNET hint0, HINTPROCINFO * phpi)
{
    HRESULT hr = S_OK;
    HINTERNET hint;
    LPITEMIDLIST pidl;
    CMultiLanguageCache cmlc;
    CWireEncoding * pwe = m_pfd->GetFtpSite()->GetCWireEncoding();

    if (phpi->psb)
    {
        phpi->psb->SetStatusMessage(IDS_LS, NULL);
        EVAL(SUCCEEDED(_SetStatusBarZone(phpi->psb, phpi->pfd->GetFtpSite())));
    }

    hr = FtpFindFirstFilePidlWrap(hint0, TRUE, &cmlc, pwe, NULL, &pidl, 
                (INTERNET_NO_CALLBACK | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RESYNCHRONIZE | INTERNET_FLAG_RELOAD), NULL, &hint);
    if (hint)
    {
        WIRECHAR wCurrentDir[MAX_PATH];   // Used for _HandleSoftLinks().

        wCurrentDir[0] = 0;
        if (EVAL(m_pff))
        {
            // It would be better to CoCreateInstance the History object by using
            // shell32!_SHCoCreateInstance() because it doesn't require COM.
            // If any more bugs are found, see if it's exported in Win95 and use it.           
            if (FAILED(m_hrOleInited))
            {
                // Win95's background enum thread doesn't call CoInitialize() so this AddToUrlHistory will fail.
                // We init it ourselves.
                m_hrOleInited = SHCoInitialize();
            }
            m_pff->AddToUrlHistory(m_pfd->GetPidlReference());
        }

        //TraceMsg(TF_FTP_OTHER, "CFtpEidl::_PopulateItem() adding Name=%s", wCurrentDir);
        if (pidl && SUCCEEDED(_HandleSoftLinks(hint0, pidl, wCurrentDir, ARRAYSIZE(wCurrentDir))))
            hr = _AddFindDataToPidlList(pidl);

        ILFree(pidl);
        while (SUCCEEDED(hr))
        {
            hr = InternetFindNextFilePidlWrap(hint, TRUE, &cmlc, pwe, &pidl);
            if (SUCCEEDED(hr))
            {
                //TraceMsg(TF_FTP_OTHER, "CFtpEidl::_PopulateItem() adding Name=%hs", FtpPidl_GetLastItemWireName(pidl));
                // We may decide to not add it for some reasons.
                if (SUCCEEDED(_HandleSoftLinks(hint0, pidl, wCurrentDir, ARRAYSIZE(wCurrentDir))))
                    hr = _AddFindDataToPidlList(pidl);

                ILFree(pidl);
            }
            else
            {
                // We failed to get the next file.
                if (HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES) != hr)
                {
                    DisplayWininetError(phpi->hwnd, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_FOLDERENUM, IDS_FTPERR_WININET, MB_OK, NULL);
                    hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);       // Clean error to indicate we already displayed the error and don't need to do it later.
                }
                else
                    hr = S_OK;        // That's fine if there aren't any more files to get

                break;    // We are done here.
            }
        }

        EVAL(SUCCEEDED(pwe->ReSetCodePages(&cmlc, m_pflHfpl)));
        InternetCloseHandle(hint);
    }
    else
    {
        // This will happen in two cases.
        // 1. The folder is empty. (GetLastError() == ERROR_NO_MORE_FILES)
        // 2. The user doesn't have enough access to view the folder. (GetLastError() == ERROR_INTERNET_EXTENDED_ERROR)
        if (HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES) != hr)
        {
            DisplayWininetError(phpi->hwnd, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_OPENFOLDER, IDS_FTPERR_WININET, MB_OK, NULL);
            hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);       // Clean error to indicate we already displayed the error and don't need to do it later.
            WININET_ASSERT(SUCCEEDED(hr));
        }
        else
            hr = S_OK;

        TraceMsg(TF_FTP_IDENUM, "CFtpEnum_New() - Can't opendir. hres=%#08lx.", hr);
    }

    if (phpi->psb)
        phpi->psb->SetStatusMessage(IDS_EMPTY, NULL);

    return hr;
}


/*****************************************************************************\
 *    CFtpEidl::_Init
\*****************************************************************************/
HRESULT CFtpEidl::_Init(void)
{
    HRESULT hr = S_FALSE;
    
    ASSERT(m_pfd);
    IUnknown_Set(&m_pflHfpl, NULL);
    m_pflHfpl = m_pfd->GetHfpl();       // Use cached copy if it exists.

    if (m_pflHfpl)
    {
        // We will just use the previous copy because we already have the contents.
        // TODO: Maybe we want to purge the results if a certain amount of time as ellapsed.
        m_fInited = TRUE;
        hr = S_OK;
    }
    else if (!m_pfd->GetFtpSite()->IsSiteBlockedByRatings(m_hwndOwner))
    {
        CFtpPidlList_Create(0, NULL, &m_pflHfpl);
        if (EVAL(m_pflHfpl))
        {
            CStatusBar * psb = GetCStatusBarFromDefViewSite(_punkSite);

            ASSERT(!m_pfd->IsRoot());
            //TraceMsg(TF_ALWAYS, "CFtpEidl::_Init() and enumerating");
            hr = m_pfd->WithHint(psb, m_hwndOwner, CFtpEidl::_PopulateItemCB, this, _punkSite, m_pff);
            if (SUCCEEDED(hr))
            {
                m_pfd->SetCache(m_pflHfpl);
                m_fInited = TRUE;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -