2922 lines
91 KiB
C++
2922 lines
91 KiB
C++
|
/*****************************************************************************\
|
||
|
ftppidl.cpp - Pointers to Item ID Lists
|
||
|
|
||
|
This is the only file that knows the internal format of our IDLs.
|
||
|
\*****************************************************************************/
|
||
|
|
||
|
#include "priv.h"
|
||
|
#include "ftppidl.h"
|
||
|
#include "ftpurl.h"
|
||
|
#include "cookie.h"
|
||
|
|
||
|
|
||
|
#define NOT_INITIALIZED 10
|
||
|
DWORD g_fNoPasswordsInAddressBar = NOT_INITIALIZED;
|
||
|
|
||
|
#define SESSIONKEY FILETIME
|
||
|
|
||
|
// Private FtpServerID Helpers
|
||
|
HRESULT FtpServerID_GetServer(LPCITEMIDLIST pidl, LPTSTR szServer, DWORD cchSize);
|
||
|
DWORD FtpItemID_GetTypeID(LPCITEMIDLIST pidl);
|
||
|
|
||
|
// v0 never went to customers but was used in NT5 before 1799 - Shipped in: Never.
|
||
|
// v1 This switch was to use password cookies for a security fix. - Shipped in: Never.
|
||
|
// v2 this was done to not use the IDelegate's IMalloc for non-first ItemIDs - Shipped in: Never (5/15/98)
|
||
|
// v3 add extra padding to ItemIDs so their dwType matches that of ServerIDs - Shipped in: IE5b1, IE5b2, NT5b2 (5/25/98)
|
||
|
// v4 add wzDisplayName to FtpItemID - Shipped in: IE5 RTM & NT5 b3 (11/16/98)
|
||
|
|
||
|
#define PIDL_VERSION_NUMBER_UPGRADE 3
|
||
|
#define PIDL_VERSION_NUMBER 4
|
||
|
|
||
|
|
||
|
|
||
|
#define SIZE_ITEMID_SIZEFIELD (sizeof(DWORD) + sizeof(WORD))
|
||
|
#define SIZE_ITEMID_TERMINATOR (sizeof(DWORD))
|
||
|
|
||
|
|
||
|
/****************************************************\
|
||
|
IDType
|
||
|
|
||
|
DESCRIPTION:
|
||
|
These bits go into FTPIDLIST.dwIDType and describe
|
||
|
what type of pidl it is AND which areas of the
|
||
|
data structure have been verified by getting the
|
||
|
data directly from the server.
|
||
|
\****************************************************/
|
||
|
|
||
|
#define IDTYPE_ISVALID 0x00000001 // Set if TYPE is valid
|
||
|
#define IDTYPE_SERVER (0x00000002 | IDTYPE_ISVALID) // Server
|
||
|
#define IDTYPE_DIR (0x00000004 | IDTYPE_ISVALID) // Folder/Dir
|
||
|
#define IDTYPE_FILE (0x00000008 | IDTYPE_ISVALID) // File
|
||
|
#define IDTYPE_FILEORDIR (0x00000010 | IDTYPE_ISVALID) // File or Dir. Wasn't specified.
|
||
|
#define IDTYPE_FRAGMENT (0x00000020 | IDTYPE_ISVALID) // File Fragment (i.e. foobar.htm#SECTION_3)
|
||
|
|
||
|
// These are bits that indicate
|
||
|
// For Server ItemIDs
|
||
|
#define IDVALID_PORT_NUM 0x00000100 // Was the port number specified
|
||
|
#define IDVALID_USERNAME 0x00000200 // Was the login name specified
|
||
|
#define IDVALID_PASSWORD 0x00000400 // Was the password specified
|
||
|
#define IDVALID_DLTYPE 0x00000800 // Download Type is specified.
|
||
|
#define IDVALID_DL_ASCII 0x00001000 // Download as ASCII if set, otherwise, download as BINARY.
|
||
|
#define IDVALID_HIDE_PASSWORD 0x00002000 // The Password entry is invalid so use the sessionkey to look it up.
|
||
|
|
||
|
#define VALID_SERVER_BITS (IDTYPE_ISVALID|IDTYPE_SERVER|IDVALID_PORT_NUM|IDVALID_USERNAME|IDVALID_PASSWORD|IDVALID_DLTYPE|IDVALID_DL_ASCII|IDVALID_HIDE_PASSWORD)
|
||
|
#define IS_VALID_SERVER_ITEMID(pItemId) (!(pItemId & ~VALID_SERVER_BITS))
|
||
|
|
||
|
// For Dir/File ItemIDs
|
||
|
#define IDVALID_FILESIZE 0x00010000 // Did we get the file size from the server?
|
||
|
#define IDVALID_MOD_DATE 0x00020000 // Did we get the modification date from the server?
|
||
|
|
||
|
#define VALID_DIRORFILE_BITS (IDTYPE_ISVALID|IDTYPE_DIR|IDTYPE_FILE|IDTYPE_FILEORDIR|IDTYPE_FRAGMENT|IDVALID_FILESIZE|IDVALID_MOD_DATE)
|
||
|
#define IS_VALID_DIRORFILE_ITEMID(pItemId) (!(pItemId & (~VALID_DIRORFILE_BITS & ~IDTYPE_ISVALID)))
|
||
|
|
||
|
|
||
|
#define IS_FRAGMENT(pFtpIDList) (IDTYPE_ISVALID != (IDTYPE_FRAGMENT & pFtpIDList->dwIDType))
|
||
|
|
||
|
///////////////////////////////////////////////////////////
|
||
|
// FTP Pidl Helper Functions
|
||
|
///////////////////////////////////////////////////////////
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: UrlGetAbstractPathFromPidl
|
||
|
|
||
|
DESCRIPTION:
|
||
|
pszUrlPath will be UNEscaped and in Wire Bytes.
|
||
|
\*****************************************************************************/
|
||
|
HRESULT UrlGetAbstractPathFromPidl(LPCITEMIDLIST pidl, BOOL fDirsOnly, BOOL fInWireBytes, void * pvPath, DWORD cchUrlPathSize)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
LPWIRESTR pwWirePath = (LPWIRESTR) pvPath;
|
||
|
LPWSTR pwzDisplayPath = (LPWSTR) pvPath;
|
||
|
|
||
|
if (!EVAL(FtpPidl_IsValid(pidl)))
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
ASSERT(pvPath && (0 < cchUrlPathSize));
|
||
|
ASSERT(IsValidPIDL(pidl));
|
||
|
|
||
|
if (fInWireBytes)
|
||
|
{
|
||
|
pwWirePath[0] = '/';
|
||
|
pwWirePath[1] = '\0'; // Make this path absolute.
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pwzDisplayPath[0] = L'/';
|
||
|
pwzDisplayPath[1] = L'\0'; // Make this path absolute.
|
||
|
}
|
||
|
|
||
|
if (!ILIsEmpty(pidl) && FtpID_IsServerItemID(pidl)) // If it's not a server, we are in trouble.
|
||
|
pidl = _ILNext(pidl); // Skip past the Server Pidl.
|
||
|
|
||
|
for (; !ILIsEmpty(pidl); pidl = _ILNext(pidl))
|
||
|
{
|
||
|
if (!fDirsOnly || FtpItemID_IsDirectory(pidl, TRUE) || !ILIsEmpty(_ILNext(pidl)))
|
||
|
{
|
||
|
if (!FtpItemID_IsFragment(pidl))
|
||
|
{
|
||
|
if (fInWireBytes)
|
||
|
{
|
||
|
LPCWIRESTR pwWireName = FtpItemID_GetWireNameReference(pidl);
|
||
|
|
||
|
if (pwWireName)
|
||
|
{
|
||
|
// The caller should never need the URL Path escaped because
|
||
|
// that will happen when it's converted into an URL.
|
||
|
WirePathAppend(pwWirePath, cchUrlPathSize, pwWireName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WCHAR szDisplayName[MAX_PATH];
|
||
|
|
||
|
if (SUCCEEDED(FtpItemID_GetDisplayName(pidl, szDisplayName, ARRAYSIZE(szDisplayName))))
|
||
|
{
|
||
|
// The caller should never need the URL Path escaped because
|
||
|
// that will happen when it's converted into an URL.
|
||
|
DisplayPathAppend(pwzDisplayPath, cchUrlPathSize, szDisplayName);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr) &&
|
||
|
(FtpItemID_IsDirectory(pidl, FALSE) || (FtpItemID_GetCompatFlags(pidl) & COMPAT_APPENDSLASHTOURL)))
|
||
|
{
|
||
|
if (fInWireBytes)
|
||
|
WirePathAppendSlash(pwWirePath, cchUrlPathSize); // Always make sure dirs end in '/'.
|
||
|
else
|
||
|
DisplayPathAppendSlash(pwzDisplayPath, cchUrlPathSize); // Always make sure dirs end in '/'.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: GetDisplayPathFromPidl
|
||
|
|
||
|
DESCRIPTION:
|
||
|
pwzDisplayPath will be UNEscaped and in display unicode.
|
||
|
\*****************************************************************************/
|
||
|
HRESULT GetDisplayPathFromPidl(LPCITEMIDLIST pidl, LPWSTR pwzDisplayPath, DWORD cchUrlPathSize, BOOL fDirsOnly)
|
||
|
{
|
||
|
return UrlGetAbstractPathFromPidl(pidl, fDirsOnly, FALSE, (void *) pwzDisplayPath, cchUrlPathSize);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: GetWirePathFromPidl
|
||
|
|
||
|
DESCRIPTION:
|
||
|
pszUrlPath will be UNEscaped and in Wire Bytes.
|
||
|
\*****************************************************************************/
|
||
|
HRESULT GetWirePathFromPidl(LPCITEMIDLIST pidl, LPWIRESTR pwWirePath, DWORD cchUrlPathSize, BOOL fDirsOnly)
|
||
|
{
|
||
|
return UrlGetAbstractPathFromPidl(pidl, fDirsOnly, TRUE, (void *) pwWirePath, cchUrlPathSize);
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifndef UNICODE
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: UrlCreateFromPidlW
|
||
|
|
||
|
DESCRIPTION:
|
||
|
\*****************************************************************************/
|
||
|
HRESULT UrlCreateFromPidlW(LPCITEMIDLIST pidl, DWORD shgno, LPWSTR pwzUrl, DWORD cchSize, DWORD dwFlags, BOOL fHidePassword)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
TCHAR szUrl[MAX_URL_STRING];
|
||
|
|
||
|
hr = UrlCreateFromPidl(pidl, shgno, szUrl, ARRAYSIZE(szUrl), dwFlags, fHidePassword);
|
||
|
if (SUCCEEDED(hr))
|
||
|
SHTCharToUnicode(szUrl, pwzUrl, cchSize);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
#else // UNICODE
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: UrlCreateFromPidlA
|
||
|
|
||
|
DESCRIPTION:
|
||
|
\*****************************************************************************/
|
||
|
HRESULT UrlCreateFromPidlA(LPCITEMIDLIST pidl, DWORD shgno, LPSTR pszUrl, DWORD cchSize, DWORD dwFlags, BOOL fHidePassword)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
TCHAR szUrl[MAX_URL_STRING];
|
||
|
|
||
|
hr = UrlCreateFromPidl(pidl, shgno, szUrl, ARRAYSIZE(szUrl), dwFlags, fHidePassword);
|
||
|
if (SUCCEEDED(hr))
|
||
|
SHTCharToAnsi(szUrl, pszUrl, cchSize);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
#endif // UNICODE
|
||
|
|
||
|
|
||
|
BOOL IncludePassword(void)
|
||
|
{
|
||
|
if (NOT_INITIALIZED == g_fNoPasswordsInAddressBar)
|
||
|
g_fNoPasswordsInAddressBar = !SHRegGetBoolUSValue(SZ_REGKEY_FTPFOLDER, SZ_REGVALUE_PASSWDSIN_ADDRBAR, FALSE, TRUE);
|
||
|
|
||
|
return g_fNoPasswordsInAddressBar;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT ParseUrlCreateFromPidl(LPCITEMIDLIST pidl, LPTSTR pszUrl, DWORD cchSize, DWORD dwFlags, BOOL fHidePassword)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
|
||
|
TCHAR szUrlPath[MAX_URL_STRING];
|
||
|
TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH];
|
||
|
TCHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH];
|
||
|
TCHAR szFragment[MAX_PATH];
|
||
|
TCHAR szDownloadType[MAX_PATH] = TEXT("");
|
||
|
INTERNET_PORT ipPortNum = INTERNET_DEFAULT_FTP_PORT;
|
||
|
|
||
|
if (ILIsEmpty(pidl))
|
||
|
{
|
||
|
ASSERT(0); // We should never have an empty pidl. Get BryanSt if we hit this. Why does CFtpFolder have ILIsEmpty(m_pidlHere).
|
||
|
szServer[0] = szUrlPath[0] = szUserName[0] = szPassword[0] = TEXT('\0');
|
||
|
hr = E_FAIL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
FtpPidl_GetServer(pidl, szServer, ARRAYSIZE(szServer));
|
||
|
GetDisplayPathFromPidl(pidl, szUrlPath, ARRAYSIZE(szUrlPath), FALSE);
|
||
|
FtpPidl_GetUserName(pidl, szUserName, ARRAYSIZE(szUserName));
|
||
|
if (FAILED(FtpPidl_GetPassword(pidl, szPassword, ARRAYSIZE(szPassword), !fHidePassword)))
|
||
|
szPassword[0] = 0;
|
||
|
|
||
|
FtpPidl_GetFragment(pidl, szFragment, ARRAYSIZE(szPassword));
|
||
|
FtpPidl_GetDownloadTypeStr(pidl, szDownloadType, ARRAYSIZE(szDownloadType));
|
||
|
UrlPathAdd(szUrlPath, ARRAYSIZE(szUrlPath), szDownloadType);
|
||
|
ipPortNum = FtpPidl_GetPortNum(pidl);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
TCHAR szUserNameEscaped[INTERNET_MAX_USER_NAME_LENGTH];
|
||
|
|
||
|
szUserNameEscaped[0] = 0;
|
||
|
if (szUserName[0])
|
||
|
EscapeString(szUserName, szUserNameEscaped, ARRAYSIZE(szUserNameEscaped));
|
||
|
|
||
|
hr = UrlCreateEx(szServer, NULL_FOR_EMPTYSTR(szUserNameEscaped), szPassword, szUrlPath, szFragment, ipPortNum, szDownloadType, pszUrl, cchSize, dwFlags);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: GetFullPrettyName
|
||
|
|
||
|
DESCRIPTION:
|
||
|
The user wants a pretty name so these are the cases we need to worry
|
||
|
about:
|
||
|
URL: Pretty Name:
|
||
|
---------------------------------- ---------------------
|
||
|
ftp://joe:psswd@serv/ serv
|
||
|
ftp://joe:psswd@serv/dir1/ dir1 on serv
|
||
|
ftp://joe:psswd@serv/dir1/dir2/ dir2 on serv
|
||
|
ftp://joe:psswd@serv/dir1/dir2/file.txt file.txt on serv
|
||
|
\*****************************************************************************/
|
||
|
HRESULT GetFullPrettyName(LPCITEMIDLIST pidl, LPTSTR pszUrl, DWORD cchSize)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
|
||
|
|
||
|
FtpPidl_GetServer(pidl, szServer, ARRAYSIZE(szServer));
|
||
|
// Is there anything after the ServerItemID?
|
||
|
if (!ILIsEmpty(_ILNext(pidl)))
|
||
|
{
|
||
|
// Yes, so let's get the name of the last item and
|
||
|
// make the string "<LastItemName> on <Server>".
|
||
|
WCHAR szLastItem[MAX_PATH];
|
||
|
|
||
|
FtpItemID_GetDisplayName(ILFindLastID(pidl), szLastItem, ARRAYSIZE(szLastItem));
|
||
|
LPTSTR pszStrArray[] = {szServer, (LPTSTR)szLastItem};
|
||
|
|
||
|
// IE #56648: Akabir found that FormatMessageW & FormatMessageWrapW() do not
|
||
|
// correctly handle UNICODE strings on Win9x. Therefore, we need to use
|
||
|
// FormatMessageA() in that case.
|
||
|
if (IsOSNT())
|
||
|
{
|
||
|
TCHAR szTemplate[MAX_PATH];
|
||
|
|
||
|
LoadString(HINST_THISDLL, IDS_PRETTYNAMEFORMAT, szTemplate, ARRAYSIZE(szTemplate));
|
||
|
EVAL(FormatMessage((FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY), (LPVOID)szTemplate,
|
||
|
0, 0, pszUrl, cchSize, (va_list*)pszStrArray));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CHAR szTemplateAnsi[MAX_PATH];
|
||
|
CHAR szURLAnsi[MAX_URL_STRING];
|
||
|
CHAR szServerAnsi[INTERNET_MAX_HOST_NAME_LENGTH];
|
||
|
CHAR szFileNameAnsi[MAX_PATH];
|
||
|
LPCSTR pszStrArrayAnsi[] = {szServerAnsi, szFileNameAnsi};
|
||
|
|
||
|
SHTCharToAnsi(szServer, szServerAnsi, ARRAYSIZE(szServerAnsi));
|
||
|
SHUnicodeToAnsi(szLastItem, szFileNameAnsi, ARRAYSIZE(szFileNameAnsi));
|
||
|
|
||
|
LoadStringA(HINST_THISDLL, IDS_PRETTYNAMEFORMATA, szTemplateAnsi, ARRAYSIZE(szTemplateAnsi));
|
||
|
EVAL(FormatMessageA((FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY), (LPVOID)szTemplateAnsi,
|
||
|
0, 0, szURLAnsi, ARRAYSIZE(szURLAnsi), (va_list*)pszStrArrayAnsi));
|
||
|
|
||
|
SHAnsiToTChar(szURLAnsi, pszUrl, cchSize);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No, so we are done.
|
||
|
StrCpyN(pszUrl, szServer, cchSize);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: UrlCreateFromPidl
|
||
|
|
||
|
DESCRIPTION:
|
||
|
Common worker that handles SHGDN_FORPARSING style GetDisplayNameOf's.
|
||
|
|
||
|
Note! that since we do not support junctions (duh), we can
|
||
|
safely walk down the pidl generating goop as we go, secure
|
||
|
in the knowledge that we are in charge of every subpidl.
|
||
|
|
||
|
_CHARSET_: Since FTP filenames are always in the ANSI character
|
||
|
set, by RFC 1738, we can return ANSI display names without loss
|
||
|
of fidelity. In a general folder implementation, we should be
|
||
|
using cStr to return display names, so that the UNICODE
|
||
|
version of the shell extension can handle UNICODE names.
|
||
|
\*****************************************************************************/
|
||
|
HRESULT UrlCreateFromPidl(LPCITEMIDLIST pidl, DWORD shgno, LPTSTR pszUrl, DWORD cchSize, DWORD dwFlags, BOOL fHidePassword)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
pszUrl[0] = 0;
|
||
|
if (!EVAL(pidl) ||
|
||
|
!EVAL(IsValidPIDL(pidl)) ||
|
||
|
!FtpPidl_IsValid(pidl) ||
|
||
|
!FtpID_IsServerItemID(pidl) ||
|
||
|
!EVAL(pszUrl && (0 < cchSize)))
|
||
|
{
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
if (shgno & SHGDN_INFOLDER)
|
||
|
{
|
||
|
// shgno & SHGDN_INFOLDER ?
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
|
||
|
if (EVAL(pidlLast && !ILIsEmpty(pidlLast)))
|
||
|
{
|
||
|
hr = FtpPidl_GetDisplayName(pidlLast, pszUrl, cchSize);
|
||
|
|
||
|
// Do they want to reparse it later? If they do and it's
|
||
|
// a server, we need to give out the scheme also.
|
||
|
// (SHGDN_INFOLDER) = "ServerName"
|
||
|
// (SHGDN_INFOLDER|SHGDN_FORPARSING) = "ftp://ServerName/"
|
||
|
if ((shgno & SHGDN_FORPARSING) &&
|
||
|
(FtpID_IsServerItemID(pidlLast)))
|
||
|
{
|
||
|
// Yes, so we need to add the server name.
|
||
|
TCHAR szServerName[MAX_PATH];
|
||
|
|
||
|
StrCpyN(szServerName, pszUrl, ARRAYSIZE(szServerName));
|
||
|
wnsprintf(pszUrl, cchSize, TEXT("ftp://%s/"), szServerName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
hr = E_FAIL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Assume they want the full URL.
|
||
|
if (!EVAL((shgno & SHGDN_FORPARSING) ||
|
||
|
(shgno & SHGDN_FORADDRESSBAR) ||
|
||
|
(shgno == SHGDN_NORMAL)))
|
||
|
{
|
||
|
TraceMsg(TF_ALWAYS, "UrlCreateFromPidl() shgno=%#08lx and I dont know what to do with that.", shgno);
|
||
|
}
|
||
|
|
||
|
if ((shgno & SHGDN_FORPARSING) || (shgno & SHGDN_FORADDRESSBAR))
|
||
|
{
|
||
|
hr = ParseUrlCreateFromPidl(pidl, pszUrl, cchSize, dwFlags, fHidePassword);
|
||
|
}
|
||
|
else
|
||
|
hr = GetFullPrettyName(pidl, pszUrl, cchSize);
|
||
|
}
|
||
|
|
||
|
// TraceMsg(TF_FTPURL_UTILS, "UrlCreateFromPidl() pszUrl=%ls, shgno=%#08lX", pszUrl, shgno);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: CreateFtpPidlFromDisplayPathHelper
|
||
|
|
||
|
DESCRIPTION:
|
||
|
The work done in CreateFtpPidlFromUrlPath requires a fair amount of
|
||
|
stack space so we do most of the work in CreateFtpPidlFromDisplayPathHelper
|
||
|
to prevent overflowing the stack.
|
||
|
\*****************************************************************************/
|
||
|
HRESULT CreateFtpPidlFromDisplayPathHelper(LPCWSTR pwzFullPath, CWireEncoding * pwe, ULONG *pcchEaten, LPITEMIDLIST * ppidl, BOOL fIsTypeKnown, BOOL fIsDir, LPITEMIDLIST * ppidlCurrentID, LPWSTR * ppwzRemaining)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
LPITEMIDLIST pidl;
|
||
|
WCHAR wzFirstItem[MAX_PATH];
|
||
|
WIRECHAR wFirstWireItem[MAX_PATH];
|
||
|
WCHAR wzRemaining[MAX_PATH];
|
||
|
BOOL fIsCurrSegmentADir = FALSE;
|
||
|
BOOL fIsCurrSegmentTypeKnown = fIsTypeKnown;
|
||
|
BOOL fIsFragSeparator = FALSE;
|
||
|
|
||
|
*ppwzRemaining = NULL;
|
||
|
*ppidl = 0;
|
||
|
|
||
|
if (pcchEaten)
|
||
|
*pcchEaten = 0; // The caller will parse the entire URL so we don't need to fill this in.
|
||
|
|
||
|
if (L'/' == pwzFullPath[0])
|
||
|
pwzFullPath = (LPWSTR) CharNextW(pwzFullPath);
|
||
|
|
||
|
DisplayPathGetFirstSegment(pwzFullPath, wzFirstItem, ARRAYSIZE(wzFirstItem), NULL, wzRemaining, ARRAYSIZE(wzRemaining), &fIsCurrSegmentADir);
|
||
|
// Is this the last segment?
|
||
|
if (!wzRemaining[0])
|
||
|
{
|
||
|
// Yes, so if the caller knows the type of the last segment, use it now.
|
||
|
if (fIsTypeKnown)
|
||
|
fIsCurrSegmentADir = fIsDir;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No, so we are assured that fIsDirCurrent is correct because it must have been followed
|
||
|
// by a '/', or how could it be followed by another path segment?
|
||
|
fIsCurrSegmentTypeKnown = TRUE;
|
||
|
ASSERT(fIsCurrSegmentADir);
|
||
|
}
|
||
|
|
||
|
// NOTE: If the user entered "ftp://serv/Dir1/Dir2" fIsDir will be false for Dir2.
|
||
|
// It will be marked as ambigious. (TODO: Check for extension?)
|
||
|
|
||
|
EVAL(SUCCEEDED(pwe->UnicodeToWireBytes(NULL, wzFirstItem, ((pwe && pwe->IsUTF8Supported()) ? WIREENC_USE_UTF8 : WIREENC_NONE), wFirstWireItem, ARRAYSIZE(wFirstWireItem))));
|
||
|
hr = FtpItemID_CreateFake(wzFirstItem, wFirstWireItem, fIsCurrSegmentTypeKnown, !fIsCurrSegmentADir, FALSE, &pidl);
|
||
|
ASSERT(IsValidPIDL(pidl));
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
if (wzRemaining[0])
|
||
|
{
|
||
|
Str_SetPtrW(ppwzRemaining, wzRemaining);
|
||
|
*ppidlCurrentID = pidl;
|
||
|
}
|
||
|
else
|
||
|
*ppidl = pidl;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: CreateFtpPidlFromUrlPath
|
||
|
|
||
|
DESCRIPTION:
|
||
|
This function will be passed the 'Path' of the URL and will create
|
||
|
each of the IDs for each path segment. This will happen by creating an ID
|
||
|
for the first path segment and then Combining that with the remaining
|
||
|
IDs which are obtained by a recursive call.
|
||
|
|
||
|
URL = "ftp://<UserName>:<Password>@<HostName>:<PortNum>/Dir1/Dir2/Dir3/file.txt[;Type=[a|b|d]]"
|
||
|
Url Path = "Dir1/Dir2/Dir3/file.txt"
|
||
|
|
||
|
pszFullPath - This URL will contain an URL Path (/Dir1/Dir2/MayBeFileOrDir).
|
||
|
fIsTypeKnown - We can detect all directories w/o ambiguity because they end
|
||
|
end '/' except for the last directory. fIsTypeKnown is used
|
||
|
if this information is known. If TRUE, fIsDir will be used to
|
||
|
disambiguate the last item. If FALSE, the last item will be marked
|
||
|
a directory if it doesn't have an extension.
|
||
|
|
||
|
The incoming name is %-encoded, but if we see an illegal %-sequence,
|
||
|
just leave the % alone.
|
||
|
|
||
|
Note that we return E_FAIL when given an unparseable path,
|
||
|
not E_INVALIDARG.
|
||
|
\*****************************************************************************/
|
||
|
HRESULT CreateFtpPidlFromDisplayPath(LPCWSTR pwzFullPath, CWireEncoding * pwe, ULONG *pcchEaten, LPITEMIDLIST * ppidl, BOOL fIsTypeKnown, BOOL fIsDir)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
LPWSTR pwzRemaining = NULL;
|
||
|
LPITEMIDLIST pidlCurrentID = NULL;
|
||
|
|
||
|
hr = CreateFtpPidlFromDisplayPathHelper(pwzFullPath, pwe, pcchEaten, ppidl, fIsTypeKnown, fIsDir, &pidlCurrentID, &pwzRemaining);
|
||
|
if (SUCCEEDED(hr) && pwzRemaining)
|
||
|
{
|
||
|
LPITEMIDLIST pidlSub;
|
||
|
|
||
|
hr = CreateFtpPidlFromDisplayPath(pwzRemaining, pwe, pcchEaten, &pidlSub, fIsTypeKnown, fIsDir);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
*ppidl = ILCombine(pidlCurrentID, pidlSub);
|
||
|
hr = *ppidl ? S_OK : E_OUTOFMEMORY;
|
||
|
ILFree(pidlSub);
|
||
|
}
|
||
|
|
||
|
ILFree(pidlCurrentID);
|
||
|
Str_SetPtrW(&pwzRemaining, NULL);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: CreateFtpPidlFromDisplayPathHelper
|
||
|
|
||
|
DESCRIPTION:
|
||
|
The work done in CreateFtpPidlFromUrlPath requires a fair amount of
|
||
|
stack space so we do most of the work in CreateFtpPidlFromDisplayPathHelper
|
||
|
to prevent overflowing the stack.
|
||
|
\*****************************************************************************/
|
||
|
HRESULT CreateFtpPidlFromFtpWirePathHelper(LPCWIRESTR pwFtpWirePath, CWireEncoding * pwe, ULONG *pcchEaten, LPITEMIDLIST * ppidl, BOOL fIsTypeKnown, BOOL fIsDir, LPITEMIDLIST * ppidlCurrentID, LPWIRESTR * ppwRemaining)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
LPITEMIDLIST pidl;
|
||
|
WIRECHAR wFirstItem[MAX_PATH];
|
||
|
WCHAR wzFirstItemDisplayName[MAX_PATH];
|
||
|
WIRECHAR wRemaining[MAX_PATH];
|
||
|
BOOL fIsCurrSegmentADir = FALSE;
|
||
|
BOOL fIsCurrSegmentTypeKnown = fIsTypeKnown;
|
||
|
BOOL fIsFragSeparator = FALSE;
|
||
|
|
||
|
*ppwRemaining = NULL;
|
||
|
*ppidl = 0;
|
||
|
|
||
|
if (pcchEaten)
|
||
|
*pcchEaten = 0; // The caller will parse the entire URL so we don't need to fill this in.
|
||
|
|
||
|
if ('/' == pwFtpWirePath[0])
|
||
|
pwFtpWirePath = (LPWIRESTR) CharNextA(pwFtpWirePath);
|
||
|
|
||
|
WirePathGetFirstSegment(pwFtpWirePath, wFirstItem, ARRAYSIZE(wFirstItem), NULL, wRemaining, ARRAYSIZE(wRemaining), &fIsCurrSegmentADir);
|
||
|
// Is this the last segment?
|
||
|
if (!wRemaining[0])
|
||
|
{
|
||
|
// Yes, so if the caller knows the type of the last segment, use it now.
|
||
|
if (fIsTypeKnown)
|
||
|
fIsCurrSegmentADir = fIsDir;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No, so we are assured that fIsDirCurrent is correct because it must have been followed
|
||
|
// by a '/', or how could it be followed by another path segment?
|
||
|
fIsCurrSegmentTypeKnown = TRUE;
|
||
|
ASSERT(fIsCurrSegmentADir);
|
||
|
}
|
||
|
|
||
|
// NOTE: If the user entered "ftp://serv/Dir1/Dir2" fIsDir will be false for Dir2.
|
||
|
// It will be marked as ambigious. (TODO: Check for extension?)
|
||
|
EVAL(SUCCEEDED(pwe->WireBytesToUnicode(NULL, wFirstItem, WIREENC_IMPROVE_ACCURACY, wzFirstItemDisplayName, ARRAYSIZE(wzFirstItemDisplayName))));
|
||
|
hr = FtpItemID_CreateFake(wzFirstItemDisplayName, wFirstItem, fIsCurrSegmentTypeKnown, !fIsCurrSegmentADir, FALSE, &pidl);
|
||
|
ASSERT(IsValidPIDL(pidl));
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
if (wRemaining[0])
|
||
|
{
|
||
|
Str_SetPtrA(ppwRemaining, wRemaining);
|
||
|
*ppidlCurrentID = pidl;
|
||
|
}
|
||
|
else
|
||
|
*ppidl = pidl;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CreateFtpPidlFromFtpWirePath(LPCWIRESTR pwFtpWirePath, CWireEncoding * pwe, ULONG *pcchEaten, LPITEMIDLIST * ppidl, BOOL fIsTypeKnown, BOOL fIsDir)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
LPWIRESTR pwRemaining = NULL;
|
||
|
LPITEMIDLIST pidlCurrentID = NULL;
|
||
|
|
||
|
*ppidl = NULL;
|
||
|
if (!pwFtpWirePath[0] || (0 == StrCmpA(pwFtpWirePath, SZ_URL_SLASHA)))
|
||
|
return S_OK;
|
||
|
|
||
|
hr = CreateFtpPidlFromFtpWirePathHelper(pwFtpWirePath, pwe, pcchEaten, ppidl, fIsTypeKnown, fIsDir, &pidlCurrentID, &pwRemaining);
|
||
|
if (SUCCEEDED(hr) && pwRemaining)
|
||
|
{
|
||
|
LPITEMIDLIST pidlSub;
|
||
|
|
||
|
hr = CreateFtpPidlFromFtpWirePath(pwRemaining, pwe, pcchEaten, &pidlSub, fIsTypeKnown, fIsDir);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
*ppidl = ILCombine(pidlCurrentID, pidlSub);
|
||
|
hr = *ppidl ? S_OK : E_OUTOFMEMORY;
|
||
|
ILFree(pidlSub);
|
||
|
}
|
||
|
|
||
|
ILFree(pidlCurrentID);
|
||
|
Str_SetPtrA(&pwRemaining, NULL);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CreateFtpPidlFromUrlPathAndPidl(LPCITEMIDLIST pidl, CWireEncoding * pwe, LPCWIRESTR pwFtpWirePath, LPITEMIDLIST * ppidl)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
LPITEMIDLIST pidlNew = ILClone(pidl);
|
||
|
|
||
|
if (pidlNew)
|
||
|
{
|
||
|
LPITEMIDLIST pidlLast = (LPITEMIDLIST) ILGetLastID(pidlNew);
|
||
|
|
||
|
while (!FtpID_IsServerItemID(pidlLast))
|
||
|
{
|
||
|
pidlLast->mkid.cb = 0; // Remove this ID.
|
||
|
pidlLast = (LPITEMIDLIST) ILGetLastID(pidlNew);
|
||
|
}
|
||
|
|
||
|
LPITEMIDLIST pidlUrlPath = NULL;
|
||
|
hr = CreateFtpPidlFromFtpWirePath(pwFtpWirePath, pwe, NULL, &pidlUrlPath, TRUE, TRUE);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
*ppidl = ILCombine(pidlNew, pidlUrlPath);
|
||
|
}
|
||
|
|
||
|
if (pidlLast)
|
||
|
ILFree(pidlLast);
|
||
|
|
||
|
if (pidlUrlPath)
|
||
|
ILFree(pidlUrlPath);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
CreateFtpPidlFromUrl
|
||
|
|
||
|
The incoming name is %-encoded, but if we see an illegal %-sequence,
|
||
|
just leave the % alone.
|
||
|
|
||
|
Note that we return E_FAIL when given an unparseable path,
|
||
|
not E_INVALIDARG.
|
||
|
\*****************************************************************************/
|
||
|
HRESULT CreateFtpPidlFromUrl(LPCTSTR pszUrl, CWireEncoding * pwe, ULONG *pcchEaten, LPITEMIDLIST * ppidl, IMalloc * pm, BOOL fHidePassword)
|
||
|
{
|
||
|
return CreateFtpPidlFromUrlEx(pszUrl, pwe, pcchEaten, ppidl, pm, fHidePassword, FALSE, FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: CreateFtpPidlFromUrlEx
|
||
|
|
||
|
DESCRIPTION:
|
||
|
pszUrl - This URL will contain an URL Path (/Dir1/Dir2/MayBeFileOrDir).
|
||
|
fIsTypeKnown - We can detect all directories w/o ambiguity because they end
|
||
|
end '/' except for the last directory. fIsTypeKnown is used
|
||
|
if this information is known. If TRUE, fIsDir will be used to
|
||
|
disambiguate the last item. If FALSE, the last item will be marked
|
||
|
a directory if it doesn't have an extension.
|
||
|
|
||
|
The incoming name is %-encoded, but if we see an illegal %-sequence,
|
||
|
just leave the % alone.
|
||
|
|
||
|
Note that we return E_FAIL when given an unparseable path,
|
||
|
not E_INVALIDARG.
|
||
|
\*****************************************************************************/
|
||
|
HRESULT CreateFtpPidlFromUrlEx(LPCTSTR pszUrl, CWireEncoding * pwe, ULONG *pcchEaten, LPITEMIDLIST * ppidl, IMalloc * pm, BOOL fHidePassword, BOOL fIsTypeKnown, BOOL fIsDir)
|
||
|
{
|
||
|
URL_COMPONENTS urlComps = {0};
|
||
|
HRESULT hr = E_FAIL;
|
||
|
|
||
|
// URL = "ftp://<UserName>:<Password>@<HostName>:<PortNum>/Dir1/Dir2/Dir3/file.txt[;Type=[a|b|d]]"
|
||
|
TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
|
||
|
TCHAR szUrlPath[MAX_URL_STRING];
|
||
|
TCHAR szExtraInfo[MAX_PATH]; // Includes Port Number and download type (ASCII, Binary, Detect)
|
||
|
TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH];
|
||
|
TCHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH];
|
||
|
|
||
|
*ppidl = 0;
|
||
|
|
||
|
urlComps.dwStructSize = sizeof(urlComps);
|
||
|
urlComps.lpszHostName = szServer;
|
||
|
urlComps.dwHostNameLength = ARRAYSIZE(szServer);
|
||
|
urlComps.lpszUrlPath = szUrlPath;
|
||
|
urlComps.dwUrlPathLength = ARRAYSIZE(szUrlPath);
|
||
|
|
||
|
urlComps.lpszUserName = szUserName;
|
||
|
urlComps.dwUserNameLength = ARRAYSIZE(szUserName);
|
||
|
urlComps.lpszPassword = szPassword;
|
||
|
urlComps.dwPasswordLength = ARRAYSIZE(szPassword);
|
||
|
urlComps.lpszExtraInfo = szExtraInfo;
|
||
|
urlComps.dwExtraInfoLength = ARRAYSIZE(szExtraInfo);
|
||
|
|
||
|
BOOL fResult = InternetCrackUrl(pszUrl, 0, ICU_DECODE, &urlComps);
|
||
|
if (fResult && (INTERNET_SCHEME_FTP == urlComps.nScheme))
|
||
|
{
|
||
|
LPITEMIDLIST pidl;
|
||
|
DWORD dwDownloadType = 0; // Indicate that it hasn't yet been specified.
|
||
|
BOOL fASCII;
|
||
|
|
||
|
ASSERT(INTERNET_SCHEME_FTP == urlComps.nScheme);
|
||
|
// NOTE:
|
||
|
// If the user is trying to give an NT UserName/DomainName pair, a bug will be encountered.
|
||
|
// Url in AddressBand="ftp://DomainName\UserName:Password@ServerName/"
|
||
|
// Url passed to us="ftp://DomainName/UserName:Password@ServerName/"
|
||
|
// We need to detect this case and fix it because this will cause "DomainName" to become
|
||
|
// the server name and the rest will become the UrlPath.
|
||
|
// ASSERT(!StrChr(szUrlPath, TEXT(':')) && !StrChr(szUrlPath, TEXT('@')));
|
||
|
|
||
|
if (S_OK == UrlRemoveDownloadType(szUrlPath, NULL, &fASCII))
|
||
|
{
|
||
|
if (fASCII)
|
||
|
dwDownloadType = (IDVALID_DLTYPE | IDVALID_DL_ASCII);
|
||
|
else
|
||
|
dwDownloadType = IDVALID_DLTYPE;
|
||
|
}
|
||
|
|
||
|
if (!szServer[0])
|
||
|
{
|
||
|
TraceMsg(TF_FTPURL_UTILS, "CreateFtpPidlFromUrl() failed because szServer=%s", szServer);
|
||
|
hr = E_FAIL; // Bad URL so fail.
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//TraceMsg(TF_FTPURL_UTILS, "CreateFtpPidlFromUrl() szServer=%s, szUrlPath=%s, szUserName=%s, szPassword=%s", szServer, szUrlPath, szUserName, szPassword);
|
||
|
hr = FtpServerID_Create(szServer, szUserName, szPassword, dwDownloadType, urlComps.nPort, &pidl, pm, fHidePassword);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERT(IsValidPIDL(pidl));
|
||
|
if (szUrlPath[0] && StrCmp(szUrlPath, SZ_URL_SLASH))
|
||
|
{
|
||
|
LPITEMIDLIST pidlSub;
|
||
|
|
||
|
hr = CreateFtpPidlFromDisplayPath(szUrlPath, pwe, pcchEaten, &pidlSub, fIsTypeKnown, fIsDir);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// Wininet chokes during requests through Netscape proxies when the GET is
|
||
|
// redirected by the proxy to include the slash. Both FTP folder and
|
||
|
// web-based FTP navigations are affected by this.
|
||
|
if (szUrlPath[lstrlen(szUrlPath)-1] == TEXT(CH_URL_URL_SLASHA))
|
||
|
{
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidlSub);
|
||
|
|
||
|
if (pidlLast)
|
||
|
FtpItemID_SetCompatFlags(pidlLast, FtpItemID_GetCompatFlags(pidlLast) | COMPAT_APPENDSLASHTOURL);
|
||
|
}
|
||
|
*ppidl = ILCombine(pidl, pidlSub);
|
||
|
if (szExtraInfo[0])
|
||
|
{
|
||
|
LPITEMIDLIST pidlFragment;
|
||
|
WIRECHAR wFragment[MAX_PATH];
|
||
|
|
||
|
// The code page is just whatever the user is using but oh well, I don't
|
||
|
// care about fragments.
|
||
|
SHUnicodeToAnsi(szExtraInfo, wFragment, ARRAYSIZE(wFragment));
|
||
|
// There is a fragment, so we need to add it.
|
||
|
hr = FtpItemID_CreateFake(szExtraInfo, wFragment, TRUE, FALSE, TRUE, &pidlFragment);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPITEMIDLIST pidlPrevious = *ppidl;
|
||
|
|
||
|
*ppidl = ILCombine(pidlPrevious, pidlFragment);
|
||
|
ILFree(pidlPrevious);
|
||
|
ILFree(pidlFragment);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
hr = *ppidl ? S_OK : E_OUTOFMEMORY;
|
||
|
ILFree(pidlSub);
|
||
|
}
|
||
|
ILFree(pidl);
|
||
|
}
|
||
|
else
|
||
|
*ppidl = pidl;
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERT(IsValidPIDL(*ppidl));
|
||
|
if (pcchEaten)
|
||
|
*pcchEaten = lstrlen(pszUrl); // TODO: Someday we can do this recursively.
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
TraceMsg(TF_FTPURL_UTILS, "CreateFtpPidlFromUrl() failed InternetCrackUrl() because pszUrl=%s, fResult=%d, urlComps.nScheme=%d", pszUrl, fResult, urlComps.nScheme);
|
||
|
|
||
|
//TraceMsg(TF_FTPURL_UTILS, "CreateFtpPidlFromUrl() is returning, hr=%#08lx", hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: Win32FindDataFromPidl
|
||
|
|
||
|
DESCRIPTION:
|
||
|
Fill in the WIN32_FIND_DATA data structure from the info in the pidl.
|
||
|
\*****************************************************************************/
|
||
|
HRESULT Win32FindDataFromPidl(LPCITEMIDLIST pidl, LPWIN32_FIND_DATAW pwfd, BOOL fFullPath, BOOL fInDisplayFormat)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
|
||
|
ASSERT(pwfd);
|
||
|
if (!EVAL(FtpPidl_IsValid(pidl)))
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
// I don't want to lie when I pass out File Size and Date info.
|
||
|
if ((IDVALID_FILESIZE | IDVALID_MOD_DATE) & FtpItemID_GetTypeID(pidlLast))
|
||
|
{
|
||
|
pwfd->dwReserved0 = 0;
|
||
|
pwfd->dwReserved1 = 0;
|
||
|
pwfd->cAlternateFileName[0] = 0;
|
||
|
|
||
|
pwfd->nFileSizeLow = FtpItemID_GetFileSizeLo(pidlLast);
|
||
|
pwfd->nFileSizeHigh = FtpItemID_GetFileSizeHi(pidlLast);
|
||
|
pwfd->dwFileAttributes = FtpItemID_GetAttributes(pidlLast);
|
||
|
|
||
|
// See the notes in priv.h on how time works.
|
||
|
pwfd->ftCreationTime = FtpPidl_GetFTPFileTime(pidlLast);
|
||
|
pwfd->ftLastWriteTime = pwfd->ftCreationTime;
|
||
|
pwfd->ftLastAccessTime = pwfd->ftCreationTime;
|
||
|
|
||
|
if (fFullPath)
|
||
|
{
|
||
|
if (fInDisplayFormat)
|
||
|
hr = GetDisplayPathFromPidl(pidl, pwfd->cFileName, ARRAYSIZE(pwfd->cFileName), FALSE);
|
||
|
else
|
||
|
hr = GetWirePathFromPidl(pidl, (LPWIRESTR)pwfd->cFileName, ARRAYSIZE(pwfd->cFileName), FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = S_OK;
|
||
|
if (fInDisplayFormat)
|
||
|
{
|
||
|
FtpPidl_GetLastFileDisplayName(pidl, pwfd->cFileName, ARRAYSIZE(pwfd->cFileName));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LPCWIRESTR pszName = FtpPidl_GetLastItemWireName(pidl);
|
||
|
|
||
|
StrCpyNA((LPWIRESTR)pwfd->cFileName, (pszName ? pszName : ""), ARRAYSIZE(pwfd->cFileName));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
STDAPI_(UINT) ILGetSizeOfFirst(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
return pidl->mkid.cb;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/****************************************************\
|
||
|
FTP Server ItemIDs
|
||
|
\****************************************************/
|
||
|
|
||
|
/****************************************************\
|
||
|
FTP PIDL Cooking functions
|
||
|
\****************************************************/
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
DATA STRUCTURE: FTPIDLIST
|
||
|
|
||
|
DESCRIPTION:
|
||
|
What our private IDLIST looks like for a file, a dir, or a fragment.
|
||
|
|
||
|
The bytes sent to an ftp server or received from an FTP server are
|
||
|
wire bytes (could be UTF-8 or DBCS/MBCS) encoded. We also store
|
||
|
a unicode version that has already been converted after trying to guess
|
||
|
the code page.
|
||
|
|
||
|
Note that the use of any TCHAR inside an IDLIST is COMPLETELY WRONG!
|
||
|
IDLISTs can be saved in a file and reloaded later. If it were saved
|
||
|
by an ANSI version of the shell extension but loaded by a UNICODE
|
||
|
version, things would turn ugly real fast.
|
||
|
\*****************************************************************************/
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FTPSERVERIDLIST structure
|
||
|
|
||
|
A typical full pidl looks like this:
|
||
|
<Not Our ItemID> [Our ItemID]
|
||
|
|
||
|
<The Internet>\[server,username,password,port#,downloadtype]\[subdir]\...\[file]
|
||
|
|
||
|
The <The Internet> part is whatever the shell gives us in our
|
||
|
CFtpFolder::_Initialize, telling us where in the namespace
|
||
|
we are rooted.
|
||
|
|
||
|
We are concerned only with the parts after the <The Internet> root,
|
||
|
the offset to which is remembered in the CFtpFolder class
|
||
|
in m_ibPidlRoot. Ways of accessing various bits of
|
||
|
information related to our full pidl are provided by our
|
||
|
CFtpFolder implementation, qv.
|
||
|
|
||
|
The first FTP IDList entry describes the server. The remaining
|
||
|
entries describe objects (files or folders) on the server.
|
||
|
\*****************************************************************************/
|
||
|
|
||
|
typedef struct tagFTPSERVERIDLIST
|
||
|
{
|
||
|
DWORD dwIDType; // Server ItemID or Dir ItemID? Which Bits are valid?
|
||
|
DWORD dwVersion; // version
|
||
|
SESSIONKEY sessionKey; // Session Key
|
||
|
DWORD dwPasswordCookie; // Password Cookie
|
||
|
DWORD dwReserved1; // for future use
|
||
|
DWORD dwReserved2; // for future use
|
||
|
DWORD dwReserved3; // for future use
|
||
|
DWORD dwPortNumber; // Port Number on server
|
||
|
DWORD cchServerSize; // StrLen of szServer
|
||
|
CHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH]; // Server
|
||
|
DWORD cchUserNameSize; // StrLen of szUserName
|
||
|
CHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH]; // User Name for Login
|
||
|
DWORD cchPasswordSize; // StrLen of szPassword
|
||
|
CHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH]; // Password for Login
|
||
|
} FTPSERVERIDLIST;
|
||
|
|
||
|
typedef UNALIGNED FTPSERVERIDLIST * LPFTPSERVERIDLIST;
|
||
|
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
DESCRIPTION:
|
||
|
On ia64, we need to worry about alignment issues. The easiest way is
|
||
|
to allocate the struct we have in our PIDL so it's quad word aligned.
|
||
|
We can then use existing code to read out of it. The problem is that
|
||
|
we need to be compatible with old pidls from pre-whistler that are only
|
||
|
DWORD aligned (for alpha machines).
|
||
|
\*****************************************************************************/
|
||
|
LPFTPSERVERIDLIST FtpServerID_GetDataThunk(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
#ifndef ALIGNMENT_MACHINE
|
||
|
LPFTPSERVERIDLIST pFtpServerItemId = (LPFTPSERVERIDLIST) ProtocolIdlInnerData(pidl);
|
||
|
if (!FtpPidl_IsValid(pidl) ||
|
||
|
!IS_VALID_SERVER_ITEMID(pFtpServerItemId->dwIDType)) // If any other bits are sit, it's invalid.
|
||
|
{
|
||
|
pFtpServerItemId = NULL;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
LPFTPSERVERIDLIST pFtpServerItemId = NULL;
|
||
|
LPFTPSERVERIDLIST pLocation = (LPFTPSERVERIDLIST) ProtocolIdlInnerData(pidl);
|
||
|
|
||
|
if (FtpPidl_IsValid(pidl) &&
|
||
|
IS_VALID_SERVER_ITEMID(pLocation->dwIDType)) // If any other bits are sit, it's invalid.
|
||
|
{
|
||
|
DWORD cbOffset = (DWORD) (((BYTE *)pLocation - (BYTE *)pidl) % ALIGN_QUAD);
|
||
|
DWORD cbSize = ILGetSizeOfFirst(pidl);
|
||
|
pFtpServerItemId = (LPFTPSERVERIDLIST) LocalAlloc(LPTR, cbSize + cbOffset);
|
||
|
if (pFtpServerItemId)
|
||
|
{
|
||
|
CopyMemory(pFtpServerItemId, pLocation, cbSize-cbOffset);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif // ALIGNMENT_MACHINE
|
||
|
|
||
|
return pFtpServerItemId;
|
||
|
}
|
||
|
|
||
|
|
||
|
void FtpServerID_FreeThunk(LPFTPSERVERIDLIST pFtpServerItemId)
|
||
|
{
|
||
|
#ifndef ALIGNMENT_MACHINE
|
||
|
// We don't need to do anything.
|
||
|
#else
|
||
|
if (pFtpServerItemId)
|
||
|
{
|
||
|
LocalFree(pFtpServerItemId);
|
||
|
}
|
||
|
#endif // ALIGNMENT_MACHINE
|
||
|
}
|
||
|
|
||
|
|
||
|
LPFTPSERVERIDLIST FtpServerID_GetDataSafe(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
LPFTPSERVERIDLIST pFtpServerItemId = NULL;
|
||
|
|
||
|
if (EVAL(pidl) && !ILIsEmpty(pidl))
|
||
|
{
|
||
|
pFtpServerItemId = (LPFTPSERVERIDLIST) ProtocolIdlInnerData(pidl);
|
||
|
}
|
||
|
|
||
|
return pFtpServerItemId;
|
||
|
}
|
||
|
|
||
|
|
||
|
LPFTPSERVERIDLIST FtpServerID_GetDataSafeThunk(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
LPFTPSERVERIDLIST pFtpServerItemId = NULL;
|
||
|
|
||
|
if (pidl && !ILIsEmpty(pidl))
|
||
|
{
|
||
|
pFtpServerItemId = FtpServerID_GetDataThunk(pidl);
|
||
|
}
|
||
|
|
||
|
return pFtpServerItemId;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL FtpID_IsServerItemID(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
LPFTPSERVERIDLIST pFtpServerItemID = FtpServerID_GetDataSafeThunk(pidl);
|
||
|
BOOL fIsServerItemID = FALSE;
|
||
|
|
||
|
if (pFtpServerItemID && IS_VALID_SERVER_ITEMID(pFtpServerItemID->dwIDType))
|
||
|
fIsServerItemID = TRUE;
|
||
|
|
||
|
FtpServerID_FreeThunk(pFtpServerItemID);
|
||
|
return fIsServerItemID;
|
||
|
}
|
||
|
|
||
|
|
||
|
LPCITEMIDLIST FtpID_GetLastIDReferense(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
LPCITEMIDLIST pidlCurrent = pidl;
|
||
|
LPCITEMIDLIST pidlNext = pidl;
|
||
|
|
||
|
if (!pidl || ILIsEmpty(pidl))
|
||
|
return pidl;
|
||
|
|
||
|
for (; !ILIsEmpty(pidlNext); pidl = _ILNext(pidl))
|
||
|
{
|
||
|
pidlCurrent = pidlNext;
|
||
|
pidlNext = _ILNext(pidlNext);
|
||
|
}
|
||
|
|
||
|
return pidlCurrent;
|
||
|
}
|
||
|
|
||
|
|
||
|
CCookieList * g_pCookieList = NULL;
|
||
|
|
||
|
CCookieList * GetCookieList(void)
|
||
|
{
|
||
|
ENTERCRITICAL;
|
||
|
if (!g_pCookieList)
|
||
|
g_pCookieList = new CCookieList();
|
||
|
ASSERT(g_pCookieList);
|
||
|
LEAVECRITICAL;
|
||
|
|
||
|
return g_pCookieList;
|
||
|
}
|
||
|
|
||
|
SESSIONKEY g_SessionKey = {-1, -1};
|
||
|
|
||
|
HRESULT PurgeSessionKey(void)
|
||
|
{
|
||
|
GetSystemTimeAsFileTime(&g_SessionKey);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
SESSIONKEY GetSessionKey(void)
|
||
|
{
|
||
|
if (-1 == g_SessionKey.dwHighDateTime)
|
||
|
PurgeSessionKey();
|
||
|
|
||
|
return g_SessionKey;
|
||
|
}
|
||
|
|
||
|
BOOL AreSessionKeysEqual(SESSIONKEY sk1, SESSIONKEY sk2)
|
||
|
{
|
||
|
if ((sk1.dwHighDateTime == sk2.dwHighDateTime) &&
|
||
|
(sk1.dwLowDateTime == sk2.dwLowDateTime))
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// This is used in order to make sure Alpha machines don't get DWORD mis-aligned.
|
||
|
#define LENGTH_AFTER_ALIGN(nLen, nAlignSize) (((nLen) % (nAlignSize)) ? ((nLen) + ((nAlignSize) - ((nLen) % (nAlignSize)))) : (nLen))
|
||
|
|
||
|
/****************************************************\
|
||
|
FUNCTION: FtpServerID_Create
|
||
|
|
||
|
DESCRIPTION:
|
||
|
Create a Ftp Server ItemID and fill it in.
|
||
|
\****************************************************/
|
||
|
HRESULT FtpServerID_Create(LPCTSTR pszServer, LPCTSTR pszUserName, LPCTSTR pszPassword,
|
||
|
DWORD dwFlags, INTERNET_PORT ipPortNum, LPITEMIDLIST * ppidl, IMalloc *pm, BOOL fHidePassword)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
DWORD cb;
|
||
|
LPITEMIDLIST pidl = NULL;
|
||
|
DWORD cchServerLen = lstrlen(pszServer);
|
||
|
DWORD cchUserNameLen = lstrlen(pszUserName);
|
||
|
DWORD cchPasswordLen = lstrlen(pszPassword);
|
||
|
LPFTPSERVERIDLIST pFtpServerID = NULL;
|
||
|
|
||
|
cchServerLen = LENGTH_AFTER_ALIGN(cchServerLen + 1, sizeof(DWORD));
|
||
|
cchUserNameLen = LENGTH_AFTER_ALIGN(cchUserNameLen + 1, sizeof(DWORD));
|
||
|
cchPasswordLen = LENGTH_AFTER_ALIGN(cchPasswordLen + 1, sizeof(DWORD));
|
||
|
|
||
|
if (!(EVAL(ppidl) && pszServer[0]))
|
||
|
return E_FAIL;
|
||
|
|
||
|
// Set bits in dwFlags that are appropriate
|
||
|
if (pszUserName[0])
|
||
|
dwFlags |= IDVALID_USERNAME;
|
||
|
|
||
|
if (pszPassword[0])
|
||
|
dwFlags |= IDVALID_PASSWORD;
|
||
|
|
||
|
// Find lenght of FTPSERVERIDLIST struct without the MAX_PATH strings
|
||
|
cb = (sizeof(*pFtpServerID) - sizeof(pFtpServerID->szServer) - sizeof(pFtpServerID->szUserName) - sizeof(pFtpServerID->szPassword));
|
||
|
|
||
|
// Add the size of the strings.
|
||
|
cb += (cchServerLen + cchUserNameLen + cchPasswordLen);
|
||
|
|
||
|
ASSERT(0 == (cb % sizeof(DWORD))); // Make sure it's DWORD aligned for Alpha machines.
|
||
|
|
||
|
pFtpServerID = (LPFTPSERVERIDLIST) LocalAlloc(LPTR, cb);
|
||
|
if (pFtpServerID)
|
||
|
{
|
||
|
LPSTR pszNext;
|
||
|
|
||
|
pszNext = pFtpServerID->szServer;
|
||
|
|
||
|
ZeroMemory(pFtpServerID, cb);
|
||
|
pFtpServerID->dwIDType = (dwFlags | IDTYPE_ISVALID | IDTYPE_SERVER | IDVALID_PORT_NUM);
|
||
|
ASSERT(IS_VALID_SERVER_ITEMID(pFtpServerID->dwIDType));
|
||
|
|
||
|
pFtpServerID->dwVersion = PIDL_VERSION_NUMBER;
|
||
|
pFtpServerID->sessionKey = GetSessionKey();
|
||
|
pFtpServerID->dwPasswordCookie = -1;
|
||
|
pFtpServerID->dwPortNumber = ipPortNum;
|
||
|
|
||
|
pFtpServerID->cchServerSize = cchServerLen;
|
||
|
SHTCharToAnsi(pszServer, pszNext, pFtpServerID->cchServerSize);
|
||
|
|
||
|
pszNext += cchServerLen; // Advance to cchUserNameSize
|
||
|
*((LPDWORD) pszNext) = cchUserNameLen; // Fill in cchUserNameSize
|
||
|
pszNext = (LPSTR)(((UNALIGNED BYTE *) pszNext) + sizeof(DWORD)); // Advance to szUserName
|
||
|
SHTCharToAnsi(pszUserName, pszNext, cchUserNameLen);
|
||
|
|
||
|
if (fHidePassword)
|
||
|
{
|
||
|
pFtpServerID->dwIDType |= IDVALID_HIDE_PASSWORD;
|
||
|
if (EVAL(GetCookieList()))
|
||
|
pFtpServerID->dwPasswordCookie = GetCookieList()->GetCookie(pszPassword);
|
||
|
|
||
|
ASSERT(-1 != pFtpServerID->dwPasswordCookie);
|
||
|
pszPassword = TEXT("");
|
||
|
}
|
||
|
|
||
|
// TraceMsg(TF_FTPURL_UTILS, "FtpServerID_Create(\"ftp://%s:%s@%s/\") dwIDType=%#80lx", pszUserName, pszPassword, pszServer, pFtpServerID->dwIDType);
|
||
|
pszNext += cchUserNameLen; // Advance to cchPasswordLen
|
||
|
*((LPDWORD) pszNext) = cchPasswordLen; // Fill in cchPasswordLen
|
||
|
pszNext = (LPSTR)(((UNALIGNED BYTE *) pszNext) + sizeof(DWORD)); // Advance to szPassword
|
||
|
SHTCharToAnsi(pszPassword, pszNext, cchPasswordLen); // Fill in pszPassword
|
||
|
|
||
|
pidl = (LPITEMIDLIST) pm->Alloc(cb);
|
||
|
if (pidl)
|
||
|
{
|
||
|
LPFTPSERVERIDLIST pFtpServerIDDest = FtpServerID_GetDataSafe(pidl);
|
||
|
|
||
|
if (pFtpServerIDDest)
|
||
|
{
|
||
|
CopyMemory(pFtpServerIDDest, pFtpServerID, cb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LocalFree(pFtpServerID);
|
||
|
}
|
||
|
|
||
|
*ppidl = pidl;
|
||
|
hr = pidl ? S_OK : E_OUTOFMEMORY;
|
||
|
ASSERT(IsValidPIDL(*ppidl));
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD FtpServerID_GetTypeID(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
DWORD dwResult = 0;
|
||
|
LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
|
||
|
|
||
|
ASSERT(FtpID_IsServerItemID(pidl));
|
||
|
if (pFtpServerID &&
|
||
|
EVAL(FtpPidl_IsValid(pidl)))
|
||
|
{
|
||
|
dwResult = pFtpServerID->dwIDType;
|
||
|
}
|
||
|
|
||
|
FtpServerID_FreeThunk(pFtpServerID);
|
||
|
return dwResult;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpServerID_GetServer(LPCITEMIDLIST pidl, LPTSTR pszServer, DWORD cchSize)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
|
||
|
|
||
|
if (pFtpServerID)
|
||
|
{
|
||
|
SHAnsiToTChar(pFtpServerID->szServer, pszServer, cchSize);
|
||
|
FtpServerID_FreeThunk(pFtpServerID);
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL FtpServerID_ServerStrCmp(LPCITEMIDLIST pidl, LPCTSTR pszServer)
|
||
|
{
|
||
|
BOOL fMatch = FALSE;
|
||
|
LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
|
||
|
|
||
|
#ifdef UNICODE
|
||
|
CHAR szServerAnsi[MAX_PATH];
|
||
|
|
||
|
SHUnicodeToAnsi(pszServer, szServerAnsi, ARRAYSIZE(szServerAnsi));
|
||
|
#endif // UNICODE
|
||
|
|
||
|
if (pFtpServerID)
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
fMatch = (0 == StrCmpA(pFtpServerID->szServer, szServerAnsi));
|
||
|
#else // UNICODE
|
||
|
fMatch = (0 == StrCmpA(pFtpServerID->szServer, pszServer));
|
||
|
#endif // UNICODE
|
||
|
}
|
||
|
|
||
|
FtpServerID_FreeThunk(pFtpServerID);
|
||
|
return fMatch;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpServerID_GetUserName(LPCITEMIDLIST pidl, LPTSTR pszUserName, DWORD cchSize)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
|
||
|
|
||
|
if (pFtpServerID)
|
||
|
{
|
||
|
LPCSTR pszSourceUserName = pFtpServerID->szServer + pFtpServerID->cchServerSize + sizeof(DWORD);
|
||
|
|
||
|
SHAnsiToTChar(pszSourceUserName, pszUserName, cchSize);
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
|
||
|
FtpServerID_FreeThunk(pFtpServerID);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT FtpServerID_GetPassword(LPCITEMIDLIST pidl, LPTSTR pszPassword, DWORD cchSize, BOOL fIncludingHiddenPassword)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
|
||
|
|
||
|
pszPassword[0] = 0;
|
||
|
if (pFtpServerID)
|
||
|
{
|
||
|
// Was the password hidden?
|
||
|
if (fIncludingHiddenPassword &&
|
||
|
(IDVALID_HIDE_PASSWORD & pFtpServerID->dwIDType))
|
||
|
{
|
||
|
// Yes, so get it out of the cookie jar (list)
|
||
|
if (EVAL(GetCookieList()) &&
|
||
|
AreSessionKeysEqual(pFtpServerID->sessionKey, GetSessionKey()))
|
||
|
{
|
||
|
hr = GetCookieList()->GetString(pFtpServerID->dwPasswordCookie, pszPassword, cchSize);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No, so what's in the pidl is the real password.
|
||
|
BYTE * pvSizeOfUserName = (BYTE *) (pFtpServerID->szServer + pFtpServerID->cchServerSize);
|
||
|
DWORD dwSizeOfUserName = *(DWORD *) pvSizeOfUserName;
|
||
|
LPCSTR pszSourcePassword = (LPCSTR) (pvSizeOfUserName + dwSizeOfUserName + 2*sizeof(DWORD));
|
||
|
|
||
|
SHAnsiToTChar(pszSourcePassword, pszPassword, cchSize);
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FtpServerID_FreeThunk(pFtpServerID);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
INTERNET_PORT FtpServerID_GetPortNum(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
INTERNET_PORT portReturn = INTERNET_DEFAULT_FTP_PORT;
|
||
|
LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
|
||
|
|
||
|
ASSERT(FtpID_IsServerItemID(pidl));
|
||
|
if (pFtpServerID)
|
||
|
{
|
||
|
portReturn = (INTERNET_PORT)pFtpServerID->dwPortNumber;
|
||
|
FtpServerID_FreeThunk(pFtpServerID);
|
||
|
}
|
||
|
|
||
|
return portReturn;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpServerID_SetHiddenPassword(LPITEMIDLIST pidl, LPCTSTR pszPassword)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
|
||
|
|
||
|
ASSERT(FtpID_IsServerItemID(pidl));
|
||
|
if (pFtpServerID)
|
||
|
{
|
||
|
pFtpServerID->sessionKey = GetSessionKey();
|
||
|
pFtpServerID->dwIDType |= IDVALID_HIDE_PASSWORD;
|
||
|
if (EVAL(GetCookieList()))
|
||
|
pFtpServerID->dwPasswordCookie = GetCookieList()->GetCookie(pszPassword);
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
|
||
|
FtpServerID_FreeThunk(pFtpServerID);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpServerID_GetStrRet(LPCITEMIDLIST pidl, LPSTRRET lpName)
|
||
|
{
|
||
|
LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
|
||
|
|
||
|
ASSERT(FtpID_IsServerItemID(pidl));
|
||
|
if (pFtpServerID)
|
||
|
{
|
||
|
lpName->uType = STRRET_OFFSET;
|
||
|
lpName->uOffset = (DWORD) (sizeof(FTPSERVERIDLIST) - sizeof(pFtpServerID->szServer) + (LPBYTE)pFtpServerID - (LPBYTE)pidl);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lpName->uType = STRRET_CSTR;
|
||
|
lpName->cStr[0] = '\0';
|
||
|
}
|
||
|
|
||
|
FtpServerID_FreeThunk(pFtpServerID);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/****************************************************\
|
||
|
FTP File/Dir ItemIDs
|
||
|
\****************************************************/
|
||
|
|
||
|
typedef struct tagFTPIDLIST
|
||
|
{
|
||
|
DWORD dwIDType; // Server ItemID or Dir ItemID? Which Bits are valid?
|
||
|
DWORD dwAttributes; // What are the file/dir attributes
|
||
|
ULARGE_INTEGER uliFileSize;
|
||
|
FILETIME ftModified; // Stored in Local Time Zone. (FTP Time)
|
||
|
DWORD dwUNIXPermission; // UNIX CHMOD Permissions (0x00000777, 4=Read, 2=Write, 1=Exec, <Owner><Group><All>)
|
||
|
DWORD dwCompatFlags; // Special case handling
|
||
|
WIRECHAR szWireName[MAX_PATH]; // Needs to go last.
|
||
|
WCHAR wzDisplayName[MAX_PATH]; // Converted to unicode to be displayed in the UI.
|
||
|
} FTPIDLIST;
|
||
|
|
||
|
typedef UNALIGNED FTPIDLIST * LPFTPIDLIST;
|
||
|
|
||
|
HRESULT FtpItemID_Alloc(LPFTPIDLIST pfi, LPITEMIDLIST * ppidl);
|
||
|
|
||
|
|
||
|
typedef struct _FTPIDLIST_WITHHEADER
|
||
|
{
|
||
|
USHORT cb; // size
|
||
|
FTPIDLIST fidListData;
|
||
|
USHORT cbTerminator; // size of next ID (Empty)
|
||
|
} FTPIDLIST_WITHHEADER;
|
||
|
|
||
|
|
||
|
|
||
|
LPFTPIDLIST FtpItemID_GetDataInternal(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
BYTE * pbData = (BYTE *) pidl;
|
||
|
|
||
|
pbData += SIZE_ITEMID_SIZEFIELD; // Skip over the size.
|
||
|
LPFTPIDLIST pFtpItemId = (LPFTPIDLIST) pbData;
|
||
|
|
||
|
if (!EVAL(IS_VALID_DIRORFILE_ITEMID(pFtpItemId->dwIDType))) // If any other bits are sit, it's invalid.
|
||
|
pFtpItemId = NULL;
|
||
|
|
||
|
return pFtpItemId;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
DESCRIPTION:
|
||
|
On ia64, we need to worry about alignment issues. The easiest way is
|
||
|
to allocate the struct we have in our PIDL so it's quad word aligned.
|
||
|
We can then use existing code to read out of it. The problem is that
|
||
|
we need to be compatible with old pidls from pre-whistler that are only
|
||
|
DWORD aligned (for alpha machines).
|
||
|
\*****************************************************************************/
|
||
|
LPFTPIDLIST FtpItemID_GetDataThunk(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
#ifndef ALIGNMENT_MACHINE
|
||
|
LPFTPIDLIST pFtpItemId = FtpItemID_GetDataInternal(pidl);
|
||
|
#else
|
||
|
LPFTPIDLIST pFtpItemId = NULL;
|
||
|
LPFTPIDLIST pLocation = FtpItemID_GetDataInternal(pidl);
|
||
|
|
||
|
if (pLocation)
|
||
|
{
|
||
|
DWORD cbSize = ILGetSizeOfFirst(pidl);
|
||
|
pFtpItemId = (LPFTPIDLIST) LocalAlloc(LPTR, cbSize);
|
||
|
if (pFtpItemId)
|
||
|
{
|
||
|
CopyMemory(pFtpItemId, pLocation, cbSize - SIZE_ITEMID_SIZEFIELD);
|
||
|
}
|
||
|
}
|
||
|
#endif // ALIGNMENT_MACHINE
|
||
|
|
||
|
return pFtpItemId;
|
||
|
}
|
||
|
|
||
|
|
||
|
void FtpItemID_FreeThunk(LPFTPIDLIST pFtpItemId)
|
||
|
{
|
||
|
#ifndef ALIGNMENT_MACHINE
|
||
|
// We don't need to do anything.
|
||
|
#else
|
||
|
if (pFtpItemId)
|
||
|
{
|
||
|
LocalFree(pFtpItemId);
|
||
|
}
|
||
|
#endif // ALIGNMENT_MACHINE
|
||
|
}
|
||
|
|
||
|
|
||
|
LPCUWSTR FtpItemID_GetDisplayNameReference(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
BYTE * pbData = (BYTE *) pidl;
|
||
|
LPCUWSTR pwzDisplayName = NULL;
|
||
|
DWORD cbWireName;
|
||
|
|
||
|
// Is the version OK?
|
||
|
// if (PIDL_VERSION_NUMBER > FtpPidl_GetVersion(pidl))
|
||
|
// return NULL;
|
||
|
|
||
|
pbData += SIZE_ITEMID_SIZEFIELD; // Skip over the size.
|
||
|
LPFTPIDLIST pFtpItemId = (LPFTPIDLIST) pbData;
|
||
|
|
||
|
cbWireName = LENGTH_AFTER_ALIGN((lstrlenA(pFtpItemId->szWireName) + 1), sizeof(DWORD));
|
||
|
pwzDisplayName = (LPCUWSTR) ((BYTE *)(&pFtpItemId->szWireName[0]) + cbWireName);
|
||
|
|
||
|
if (!EVAL(IS_VALID_DIRORFILE_ITEMID(pFtpItemId->dwIDType))) // If any other bits are sit, it's invalid.
|
||
|
pwzDisplayName = NULL;
|
||
|
|
||
|
return pwzDisplayName;
|
||
|
}
|
||
|
|
||
|
DWORD FtpItemID_GetTypeID(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
LPFTPIDLIST pFtpItemId = FtpItemID_GetDataThunk(pidl);
|
||
|
DWORD dwType = (pFtpItemId ? pFtpItemId->dwIDType : 0);
|
||
|
|
||
|
FtpItemID_FreeThunk(pFtpItemId);
|
||
|
return dwType;
|
||
|
}
|
||
|
|
||
|
|
||
|
void FtpItemID_SetTypeID(LPITEMIDLIST pidl, DWORD dwNewTypeID)
|
||
|
{
|
||
|
LPFTPIDLIST pFtpItemId = FtpItemID_GetDataInternal(pidl);
|
||
|
if (EVAL(pFtpItemId))
|
||
|
pFtpItemId->dwIDType = dwNewTypeID;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************\
|
||
|
FUNCTION: FtpItemID_Alloc
|
||
|
|
||
|
DESCRIPTION:
|
||
|
We are passed a pointer to a FTPIDLIST data
|
||
|
structure and our goal is to create a ItemID from
|
||
|
it. This mainly includes making it only big enough
|
||
|
for the current string(s).
|
||
|
\****************************************************/
|
||
|
HRESULT FtpItemID_Alloc(LPFTPIDLIST pfi, LPITEMIDLIST * ppidl)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
WORD cbTotal;
|
||
|
WORD cbDataFirst;
|
||
|
WORD cbData;
|
||
|
BYTE * pbMemory;
|
||
|
DWORD cchSizeOfName = lstrlenA(pfi->szWireName);
|
||
|
DWORD cchSizeOfDispName = ualstrlenW(pfi->wzDisplayName);
|
||
|
|
||
|
ASSERT(pfi && ppidl);
|
||
|
|
||
|
// Find lenght of FTPIDLIST struct if the szName member only needed enought room
|
||
|
// for the string, not the full MAX_PATH.
|
||
|
// Size EQUALS: (Everything in the struct) - (the 2 full statusly sized strings) + (the 2 packed strings + alignment)
|
||
|
cbDataFirst = (WORD)((sizeof(*pfi) - sizeof(pfi->szWireName) - sizeof(pfi->wzDisplayName)) + LENGTH_AFTER_ALIGN(cchSizeOfName + 1, sizeof(DWORD)) - sizeof(DWORD));
|
||
|
cbData = cbDataFirst + (WORD) LENGTH_AFTER_ALIGN((cchSizeOfDispName + 1) * sizeof(WCHAR), sizeof(DWORD));
|
||
|
|
||
|
ASSERT((cbData % sizeof(DWORD)) == 0); // Verify it's DWORD aligned.
|
||
|
cbTotal = (SIZE_ITEMID_SIZEFIELD + cbData + SIZE_ITEMID_TERMINATOR);
|
||
|
|
||
|
pbMemory = (BYTE *) CoTaskMemAlloc(cbTotal);
|
||
|
if (pbMemory)
|
||
|
{
|
||
|
USHORT * pIDSize = (USHORT *)pbMemory;
|
||
|
BYTE * pbData = (pbMemory + SIZE_ITEMID_SIZEFIELD); // the Data starts at the second DWORD.
|
||
|
USHORT * pIDTerminator = (USHORT *)(pbMemory + SIZE_ITEMID_SIZEFIELD + cbData);
|
||
|
|
||
|
pIDSize[0] = (cbTotal - SIZE_ITEMID_TERMINATOR); // Set the size of the ItemID (including the next ItemID as terminator)
|
||
|
ASSERT(cbData <= sizeof(*pfi)); // Don't let me copy too much.
|
||
|
CopyMemory(pbData, pfi, cbDataFirst);
|
||
|
CopyMemory((pbData + cbDataFirst), &(pfi->wzDisplayName), ((cchSizeOfDispName + 1) * sizeof(WCHAR)));
|
||
|
pIDTerminator[0] = 0; // Terminate the next ID.
|
||
|
|
||
|
// TraceMsg(TF_FTPURL_UTILS, "FtpItemID_Alloc(\"%ls\") dwIDType=%#08lx, dwAttributes=%#08lx", pfi->wzDisplayName, pfi->dwIDType, pfi->dwAttributes);
|
||
|
}
|
||
|
|
||
|
*ppidl = (LPITEMIDLIST) pbMemory;
|
||
|
hr = pbMemory ? S_OK : E_OUTOFMEMORY;
|
||
|
|
||
|
ASSERT(IsValidPIDL(*ppidl));
|
||
|
ASSERT_POINTER_MATCHES_HRESULT(*ppidl, hr);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: FtpItemID_CreateReal
|
||
|
|
||
|
DESCRIPTION:
|
||
|
Cook up a pidl based on a WIN32_FIND_DATA.
|
||
|
|
||
|
The cFileName field is itself MAX_PATH characters long,
|
||
|
so its length cannot possibly exceed MAX_PATH...
|
||
|
\*****************************************************************************/
|
||
|
HRESULT FtpItemID_CreateReal(const LPFTP_FIND_DATA pwfd, LPCWSTR pwzDisplayName, LPITEMIDLIST * ppidl)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
FTPIDLIST fi = {0};
|
||
|
|
||
|
// Fill in fi.
|
||
|
fi.dwIDType = (IDTYPE_ISVALID | IDVALID_FILESIZE | IDVALID_MOD_DATE);
|
||
|
fi.uliFileSize.LowPart = pwfd->nFileSizeLow;
|
||
|
fi.uliFileSize.HighPart = pwfd->nFileSizeHigh;
|
||
|
fi.ftModified = pwfd->ftLastWriteTime;
|
||
|
fi.dwAttributes = pwfd->dwFileAttributes;
|
||
|
fi.dwUNIXPermission = pwfd->dwReserved0; // Set by WININET
|
||
|
StrCpyNA(fi.szWireName, pwfd->cFileName, ARRAYSIZE(fi.szWireName));
|
||
|
StrCpyN(fi.wzDisplayName, pwzDisplayName, ARRAYSIZE(fi.wzDisplayName));
|
||
|
|
||
|
if (pwfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||
|
fi.dwIDType |= IDTYPE_DIR;
|
||
|
else
|
||
|
fi.dwIDType |= IDTYPE_FILE;
|
||
|
|
||
|
hr = FtpItemID_Alloc(&fi, ppidl);
|
||
|
ASSERT(IsValidPIDL(*ppidl));
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************\
|
||
|
FUNCTION: FtpItemID_CreateFake
|
||
|
|
||
|
DESCRIPTION:
|
||
|
Create a ItemID but we are only setting the
|
||
|
name. We don't know the true file attributes,
|
||
|
file size, or modification date yet because
|
||
|
we haven't touched the server yet. If we did,
|
||
|
we would use the returned WIN32_FIND_DATA struct
|
||
|
to create the ItemID by using FtpItemID_CreateReal().
|
||
|
\****************************************************/
|
||
|
HRESULT FtpItemID_CreateFake(LPCWSTR pwzDisplayName, LPCWIRESTR pwWireName, BOOL fTypeKnown, BOOL fIsFile, BOOL fIsFragment, LPITEMIDLIST * ppidl)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
DWORD dwType = IDTYPE_ISVALID;
|
||
|
FTPIDLIST fi = {0};
|
||
|
|
||
|
// Is it unknown?
|
||
|
if (!fTypeKnown)
|
||
|
{
|
||
|
// HACK: We will assume everything w/o a file extension is a Dir
|
||
|
// and everything w/an extension is a file.
|
||
|
fTypeKnown = TRUE;
|
||
|
fIsFile = (!pwzDisplayName || (0 == *PathFindExtension(pwzDisplayName))) ? FALSE : TRUE;
|
||
|
}
|
||
|
if (fTypeKnown)
|
||
|
{
|
||
|
if (fIsFile)
|
||
|
dwType |= IDTYPE_FILE;
|
||
|
else if (fIsFragment)
|
||
|
dwType |= IDTYPE_FRAGMENT;
|
||
|
else
|
||
|
dwType |= IDTYPE_DIR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// You need to know if it's a fragment because there is no
|
||
|
// heuristic to find out.
|
||
|
ASSERT(!fIsFragment);
|
||
|
|
||
|
dwType |= IDTYPE_FILEORDIR;
|
||
|
}
|
||
|
|
||
|
fi.dwIDType = dwType;
|
||
|
fi.dwAttributes = (fIsFile ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_DIRECTORY);
|
||
|
fi.uliFileSize.QuadPart = 0;
|
||
|
StrCpyNW(fi.wzDisplayName, pwzDisplayName, ARRAYSIZE(fi.wzDisplayName));
|
||
|
StrCpyNA(fi.szWireName, pwWireName, ARRAYSIZE(fi.szWireName));
|
||
|
|
||
|
hr = FtpItemID_Alloc(&fi, ppidl);
|
||
|
ASSERT(IsValidPIDL(*ppidl));
|
||
|
ASSERT_POINTER_MATCHES_HRESULT(*ppidl, hr);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: FtpItemID_SetName
|
||
|
|
||
|
DESCRIPTION:
|
||
|
The user chose a new name for the ftp file or dir (in unicode). We
|
||
|
now need to create the name in wire bytes and we will use the original
|
||
|
wire byte name to decide how to do that (from pidl).
|
||
|
\*****************************************************************************/
|
||
|
HRESULT FtpItemID_CreateWithNewName(LPCITEMIDLIST pidl, LPCWSTR pwzDisplayName, LPCWIRESTR pwWireName, LPITEMIDLIST * ppidlOut)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
FTPIDLIST fi;
|
||
|
const FTPIDLIST UNALIGNED * pfi = FtpItemID_GetDataThunk(pidl);
|
||
|
|
||
|
if (pfi)
|
||
|
{
|
||
|
CWireEncoding cWireEncoding;
|
||
|
|
||
|
CopyMemory(&fi, pfi, sizeof(FTPIDLIST) - sizeof(fi.szWireName) - sizeof(fi.wzDisplayName));
|
||
|
StrCpyNW(fi.wzDisplayName, pwzDisplayName, ARRAYSIZE(fi.wzDisplayName));
|
||
|
StrCpyNA(fi.szWireName, pwWireName, ARRAYSIZE(fi.szWireName));
|
||
|
|
||
|
hr = FtpItemID_Alloc(&fi, ppidlOut);
|
||
|
ASSERT(IsValidPIDL(*ppidlOut));
|
||
|
FtpItemID_FreeThunk((FTPIDLIST UNALIGNED *) pfi);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT Private_GetFileInfo(SHFILEINFO *psfi, DWORD rgf, LPCTSTR pszName, DWORD dwFileAttributes)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
|
||
|
if (SHGetFileInfo(pszName, dwFileAttributes, psfi, sizeof(*psfi), rgf | SHGFI_USEFILEATTRIBUTES))
|
||
|
hr = S_OK;
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: FtpPidl_GetFileInfo
|
||
|
|
||
|
DESCRIPTION:
|
||
|
_UNDOCUMENTED_: We strip the Hidden and System bits so
|
||
|
that SHGetFileInfo won't think that we're passing something
|
||
|
that might be a junction.
|
||
|
|
||
|
We also force the SHGFI_USEFILEATTRIBUTES bit to remind the shell
|
||
|
that this isn't a file.
|
||
|
\*****************************************************************************/
|
||
|
HRESULT FtpPidl_GetFileInfo(LPCITEMIDLIST pidl, SHFILEINFO *psfi, DWORD rgf)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
TCHAR szDisplayName[MAX_PATH];
|
||
|
|
||
|
psfi->iIcon = 0;
|
||
|
psfi->hIcon = NULL;
|
||
|
psfi->dwAttributes = 0;
|
||
|
psfi->szDisplayName[0] = 0;
|
||
|
psfi->szTypeName[0] = 0;
|
||
|
|
||
|
ASSERT(IsValidPIDL(pidl));
|
||
|
if (FtpID_IsServerItemID(pidl))
|
||
|
{
|
||
|
FtpServerID_GetServer(pidl, szDisplayName, ARRAYSIZE(szDisplayName));
|
||
|
hr = Private_GetFileInfo(psfi, rgf, szDisplayName, FILE_ATTRIBUTE_DIRECTORY);
|
||
|
if (psfi->hIcon)
|
||
|
DestroyIcon(psfi->hIcon);
|
||
|
|
||
|
psfi->hIcon = LoadIcon(HINST_THISDLL, MAKEINTRESOURCE(IDI_FTPFOLDER));
|
||
|
ASSERT(psfi->hIcon);
|
||
|
|
||
|
// Now replace the type (szTypeName) with "FTP Server" because
|
||
|
// it could go in the Properties dialog
|
||
|
EVAL(LoadString(HINST_THISDLL, IDS_ITEMTYPE_SERVER, psfi->szTypeName, ARRAYSIZE(psfi->szTypeName)));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LPFTPIDLIST pfi = FtpItemID_GetDataThunk(pidl);
|
||
|
|
||
|
if (pfi)
|
||
|
{
|
||
|
FtpItemID_GetDisplayName(pidl, szDisplayName, ARRAYSIZE(szDisplayName));
|
||
|
hr = Private_GetFileInfo(psfi, rgf, szDisplayName, (pfi->dwAttributes & ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)));
|
||
|
|
||
|
FtpItemID_FreeThunk(pfi);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT FtpPidl_GetFileType(LPCITEMIDLIST pidl, LPTSTR pszType, DWORD cchSize)
|
||
|
{
|
||
|
SHFILEINFO sfi;
|
||
|
HRESULT hr;
|
||
|
|
||
|
ASSERT(IsValidPIDL(pidl));
|
||
|
hr = FtpPidl_GetFileInfo(pidl, &sfi, SHGFI_TYPENAME);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
StrCpyN(pszType, sfi.szTypeName, cchSize);
|
||
|
if (sfi.hIcon)
|
||
|
DestroyIcon(sfi.hIcon);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpPidl_GetFileTypeStrRet(LPCITEMIDLIST pidl, LPSTRRET pstr)
|
||
|
{
|
||
|
WCHAR szType[MAX_URL_STRING];
|
||
|
HRESULT hr;
|
||
|
|
||
|
ASSERT(IsValidPIDL(pidl));
|
||
|
hr = FtpPidl_GetFileType(pidl, szType, ARRAYSIZE(szType));
|
||
|
if (EVAL(SUCCEEDED(hr)))
|
||
|
StringToStrRetW(szType, pstr);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: _FtpItemID_CompareOneID
|
||
|
|
||
|
DESCRIPTION:
|
||
|
ici - attribute (column) to compare
|
||
|
|
||
|
Note! that UNIX filenames are case-*sensitive*.
|
||
|
|
||
|
We make two passes on the name. If the names are different in other
|
||
|
than case, we return the result of that comparison. Otherwise,
|
||
|
we return the result of a case-sensitive comparison.
|
||
|
|
||
|
This algorithm ensures that the items sort themselves in a
|
||
|
case-insensitive way, with ties broken by a case-sensitive
|
||
|
comparison. This makes ftp folders act "mostly" like normal
|
||
|
folders.
|
||
|
|
||
|
_UNDOCUMENTED_: The documentation says that the ici parameter
|
||
|
is undefined and must be zero. In reality, it is the column
|
||
|
number (defined by IShellView) for which the comparison is to
|
||
|
be made.
|
||
|
\*****************************************************************************/
|
||
|
HRESULT _FtpItemID_CompareOneID(LPARAM ici, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwCompFlags)
|
||
|
{
|
||
|
int iRc = 0; // 0 means we don't know.
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
ASSERT(IsValidPIDL(pidl1));
|
||
|
ASSERT(IsValidPIDL(pidl2));
|
||
|
|
||
|
// Are they both the same type? (Both Dirs or both files?)
|
||
|
if (!(dwCompFlags & FCMP_GROUPDIRS) || (!FtpPidl_IsDirectory(pidl1, FALSE) == !FtpPidl_IsDirectory(pidl2, FALSE)))
|
||
|
{
|
||
|
switch (ici & SHCIDS_COLUMNMASK)
|
||
|
{
|
||
|
case COL_NAME:
|
||
|
{
|
||
|
// Yes they are the same, so we will key off the name...
|
||
|
WIRECHAR szName1[MAX_PATH];
|
||
|
WIRECHAR szName2[MAX_PATH];
|
||
|
|
||
|
szName1[0] = TEXT('\0');
|
||
|
szName2[0] = TEXT('\0');
|
||
|
|
||
|
FtpPidl_GetWireName(pidl1, szName1, ARRAYSIZE(szName1));
|
||
|
FtpPidl_GetWireName(pidl2, szName2, ARRAYSIZE(szName2));
|
||
|
|
||
|
iRc = StrCmpIA(szName1, szName2);
|
||
|
if (0 == iRc)
|
||
|
{
|
||
|
if (!(dwCompFlags & FCMP_CASEINSENSE))
|
||
|
iRc = StrCmpA(szName1, szName2);
|
||
|
|
||
|
/*
|
||
|
// They are the same name, so now lets check on the username
|
||
|
// if they are Server IDs.
|
||
|
if ((0 == iRc) && (FtpID_IsServerItemID(pidl1)))
|
||
|
{
|
||
|
FtpPidl_GetUserName(pidl1, szName1, ARRAYSIZE(szName1));
|
||
|
FtpPidl_GetUserName(pidl2, szName2, ARRAYSIZE(szName2));
|
||
|
iRc = StrCmp(szName1, szName2);
|
||
|
}
|
||
|
*/
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case COL_SIZE:
|
||
|
if (FtpPidl_GetFileSize(pidl1) < FtpPidl_GetFileSize(pidl2))
|
||
|
iRc = -1;
|
||
|
else if (FtpPidl_GetFileSize(pidl1) > FtpPidl_GetFileSize(pidl2))
|
||
|
iRc = +1;
|
||
|
else
|
||
|
iRc = 0; // I don't know
|
||
|
break;
|
||
|
|
||
|
case COL_TYPE:
|
||
|
if (!FtpID_IsServerItemID(pidl1) && !FtpID_IsServerItemID(pidl2))
|
||
|
{
|
||
|
TCHAR szType1[MAX_PATH];
|
||
|
|
||
|
hr = FtpPidl_GetFileType(pidl1, szType1, ARRAYSIZE(szType1));
|
||
|
if (EVAL(SUCCEEDED(hr)))
|
||
|
{
|
||
|
TCHAR szType2[MAX_PATH];
|
||
|
hr = FtpPidl_GetFileType(pidl2, szType2, ARRAYSIZE(szType2));
|
||
|
if (EVAL(SUCCEEDED(hr)))
|
||
|
iRc = StrCmpI(szType1, szType2);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case COL_MODIFIED:
|
||
|
{
|
||
|
FILETIME ft1 = FtpPidl_GetFileTime(pidl1);
|
||
|
FILETIME ft2 = FtpPidl_GetFileTime(pidl2);
|
||
|
iRc = CompareFileTime(&ft1, &ft2);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
hr = E_NOTIMPL;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No they are different. We want the Folder to always come first.
|
||
|
// This doesn't seam right, but it forces folders to bubble to the top
|
||
|
// in the most frequent case and it matches DefView's Behavior.
|
||
|
if (FtpPidl_IsDirectory(pidl1, FALSE))
|
||
|
iRc = -1;
|
||
|
else
|
||
|
iRc = 1;
|
||
|
}
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
hr = HRESULT_FROM_SUCCESS_VALUE(iRc); // encode the sort value in the return code.
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: FtpItemID_CompareIDs
|
||
|
|
||
|
DESCRIPTION:
|
||
|
ici - attribute (column) to compare
|
||
|
|
||
|
Note! that we rely on the fact that IShellFolders are
|
||
|
uniform; we do not need to bind to the shell folder in
|
||
|
order to compare its sub-itemids.
|
||
|
|
||
|
_UNDOCUMENTED_: The documentation does not say whether or not
|
||
|
complex pidls can be received. In fact, they can.
|
||
|
|
||
|
The reason why the shell asks you to handle complex pidls
|
||
|
is that you can often short-circuit the comparison by walking
|
||
|
the ID list directly. (Formally speaking, you need to bind
|
||
|
to each ID and then call yourself recursively. But if your
|
||
|
pidls are uniform, you can just use a loop like the one below.)
|
||
|
\*****************************************************************************/
|
||
|
HRESULT FtpItemID_CompareIDs(LPARAM ici, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwCompFlags)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (!pidl1 || ILIsEmpty(pidl1))
|
||
|
{
|
||
|
if (!pidl2 || ILIsEmpty(pidl2))
|
||
|
hr = HRESULT_FROM_SUCCESS_VALUE(0); // Both ID lists are empty
|
||
|
else
|
||
|
hr = HRESULT_FROM_SUCCESS_VALUE(-1); // pidl1 is empty, pidl2 is nonempty
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!pidl2 || ILIsEmpty(pidl2))
|
||
|
hr = HRESULT_FROM_SUCCESS_VALUE(1); // pidl1 is nonempty, pidl2 is empty
|
||
|
else
|
||
|
{
|
||
|
ASSERT(IsValidPIDL(pidl1));
|
||
|
ASSERT(IsValidPIDL(pidl2));
|
||
|
hr = _FtpItemID_CompareOneID(ici, pidl1, pidl2, dwCompFlags); // both are nonempty
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If this level of ItemsIDs are equal, then we will compare the next
|
||
|
// level of ItemIDs
|
||
|
if ((hr == HRESULT_FROM_SUCCESS_VALUE(0)) && pidl1 && !ILIsEmpty(pidl1))
|
||
|
hr = FtpItemID_CompareIDs(ici, _ILNext(pidl1), _ILNext(pidl2), dwCompFlags);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
int FtpItemID_CompareIDsInt(LPARAM ici, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwCompFlags)
|
||
|
{
|
||
|
HRESULT hr = FtpItemID_CompareIDs(ici, pidl1, pidl2, dwCompFlags);
|
||
|
int nResult = (DWORD)(short)hr;
|
||
|
|
||
|
return nResult;
|
||
|
}
|
||
|
|
||
|
DWORD FtpItemID_GetAttributes(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
DWORD dwAttributes = 0;
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
|
||
|
|
||
|
if (pFtpIDList)
|
||
|
{
|
||
|
dwAttributes = pFtpIDList->dwAttributes;
|
||
|
FtpItemID_FreeThunk(pFtpIDList);
|
||
|
}
|
||
|
|
||
|
return dwAttributes;
|
||
|
}
|
||
|
|
||
|
HRESULT FtpItemID_SetAttributes(LPCITEMIDLIST pidl, DWORD dwAttribs)
|
||
|
{
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
|
||
|
|
||
|
if (!pFtpIDList)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
pFtpIDList->dwAttributes = dwAttribs;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD FtpItemID_GetUNIXPermissions(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
DWORD dwPermissions = 0;
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
|
||
|
|
||
|
if (pFtpIDList)
|
||
|
{
|
||
|
dwPermissions = pFtpIDList->dwUNIXPermission;
|
||
|
FtpItemID_FreeThunk(pFtpIDList);
|
||
|
}
|
||
|
|
||
|
return dwPermissions;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpItemID_SetUNIXPermissions(LPCITEMIDLIST pidl, DWORD dwUNIXPermission)
|
||
|
{
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
|
||
|
|
||
|
if (!pFtpIDList)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
pFtpIDList->dwUNIXPermission = dwUNIXPermission;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD FtpItemID_GetCompatFlags(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
DWORD dwCompatFlags = 0;
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
|
||
|
|
||
|
if (pFtpIDList)
|
||
|
{
|
||
|
dwCompatFlags = pFtpIDList->dwCompatFlags;
|
||
|
FtpItemID_FreeThunk(pFtpIDList);
|
||
|
}
|
||
|
|
||
|
return dwCompatFlags;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpItemID_SetCompatFlags(LPCITEMIDLIST pidl, DWORD dwCompatFlags)
|
||
|
{
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
|
||
|
|
||
|
if (!pFtpIDList)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
pFtpIDList->dwCompatFlags = dwCompatFlags;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONGLONG FtpItemID_GetFileSize(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
ULONGLONG uliFileSize = 0;
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
|
||
|
|
||
|
if (pFtpIDList)
|
||
|
{
|
||
|
ASSERT(IsFlagSet(pFtpIDList->dwIDType, IDVALID_FILESIZE));
|
||
|
uliFileSize = pFtpIDList->uliFileSize.QuadPart;
|
||
|
FtpItemID_FreeThunk(pFtpIDList);
|
||
|
}
|
||
|
|
||
|
return uliFileSize;
|
||
|
}
|
||
|
|
||
|
void FtpItemID_SetFileSize(LPCITEMIDLIST pidl, ULARGE_INTEGER uliFileSize)
|
||
|
{
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
|
||
|
|
||
|
if (!pFtpIDList)
|
||
|
return;
|
||
|
|
||
|
pFtpIDList->uliFileSize = uliFileSize;
|
||
|
pFtpIDList->dwIDType |= IDVALID_FILESIZE;
|
||
|
}
|
||
|
|
||
|
DWORD FtpItemID_GetFileSizeLo(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
|
||
|
|
||
|
if (!pFtpIDList)
|
||
|
return NULL;
|
||
|
|
||
|
ASSERT(IsFlagSet(pFtpIDList->dwIDType, IDVALID_FILESIZE));
|
||
|
DWORD dwSize = pFtpIDList->uliFileSize.LowPart;
|
||
|
FtpItemID_FreeThunk(pFtpIDList);
|
||
|
|
||
|
return dwSize;
|
||
|
}
|
||
|
|
||
|
DWORD FtpItemID_GetFileSizeHi(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
|
||
|
|
||
|
if (!pFtpIDList)
|
||
|
return NULL;
|
||
|
|
||
|
ASSERT(IsFlagSet(pFtpIDList->dwIDType, IDVALID_FILESIZE));
|
||
|
DWORD dwSize = pFtpIDList->uliFileSize.HighPart;
|
||
|
FtpItemID_FreeThunk(pFtpIDList);
|
||
|
|
||
|
return dwSize;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return value is in Local Time Zone.
|
||
|
FILETIME FtpItemID_GetFileTime(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
|
||
|
FILETIME ftEmpty = {0};
|
||
|
|
||
|
if (pFtpIDList)
|
||
|
{
|
||
|
ASSERT(IsFlagSet(pFtpIDList->dwIDType, IDVALID_MOD_DATE));
|
||
|
ftEmpty = pFtpIDList->ftModified;
|
||
|
|
||
|
FtpItemID_FreeThunk(pFtpIDList);
|
||
|
}
|
||
|
|
||
|
return ftEmpty;
|
||
|
}
|
||
|
|
||
|
|
||
|
LPCWIRESTR FtpItemID_GetWireNameReference(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
|
||
|
|
||
|
if (!pFtpIDList || IS_FRAGMENT(pFtpIDList))
|
||
|
return NULL;
|
||
|
|
||
|
return pFtpIDList->szWireName;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpItemID_GetDisplayName(LPCITEMIDLIST pidl, LPWSTR pwzName, DWORD cchSize)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
|
||
|
|
||
|
if (pFtpIDList)
|
||
|
{
|
||
|
if (!IS_FRAGMENT(pFtpIDList))
|
||
|
{
|
||
|
LPCUWSTR pszUnalignedName = FtpItemID_GetDisplayNameReference(pidl);
|
||
|
|
||
|
if (pszUnalignedName)
|
||
|
{
|
||
|
// The display name wasn't stored in v3
|
||
|
ualstrcpynW(pwzName, pszUnalignedName, cchSize);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pwzName[0] = TEXT('\0');
|
||
|
hr = E_FAIL;
|
||
|
}
|
||
|
|
||
|
FtpItemID_FreeThunk(pFtpIDList);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpItemID_GetWireName(LPCITEMIDLIST pidl, LPWIRESTR pszName, DWORD cchSize)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
|
||
|
|
||
|
if (pFtpIDList && !IS_FRAGMENT(pFtpIDList))
|
||
|
StrCpyNA(pszName, pFtpIDList->szWireName, cchSize);
|
||
|
else
|
||
|
{
|
||
|
pszName[0] = TEXT('\0');
|
||
|
hr = E_FAIL;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpItemID_GetFragment(LPCITEMIDLIST pidl, LPWSTR pwzFragmentStr, DWORD cchSize)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
|
||
|
|
||
|
if (pFtpIDList && IS_FRAGMENT(pFtpIDList))
|
||
|
{
|
||
|
LPCUWSTR pszUnalignedName = FtpItemID_GetDisplayNameReference(pidl);
|
||
|
|
||
|
if (pszUnalignedName)
|
||
|
{
|
||
|
// The display name wasn't stored in v3
|
||
|
ualstrcpynW(pwzFragmentStr, pszUnalignedName, cchSize);
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pwzFragmentStr[0] = TEXT('\0');
|
||
|
hr = E_FAIL;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL FtpItemID_IsFragment(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
BOOL fIsFrag = FALSE;
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
|
||
|
|
||
|
if (pFtpIDList && IS_FRAGMENT(pFtpIDList))
|
||
|
fIsFrag = TRUE;
|
||
|
|
||
|
return fIsFrag;
|
||
|
}
|
||
|
|
||
|
|
||
|
// fileTime In UTC
|
||
|
void FtpItemID_SetFileTime(LPCITEMIDLIST pidl, FILETIME fileTime)
|
||
|
{
|
||
|
FILETIME fileTimeLocal;
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
|
||
|
|
||
|
if (!pFtpIDList)
|
||
|
return;
|
||
|
|
||
|
FileTimeToLocalFileTime(&fileTime, &fileTimeLocal);
|
||
|
pFtpIDList->ftModified = fileTimeLocal;
|
||
|
pFtpIDList->dwIDType |= IDVALID_MOD_DATE;
|
||
|
}
|
||
|
|
||
|
BOOL FtpItemID_IsDirectory(LPCITEMIDLIST pidl, BOOL fAssumeDirForUnknown)
|
||
|
{
|
||
|
LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
|
||
|
|
||
|
if (!pFtpIDList)
|
||
|
return NULL;
|
||
|
|
||
|
BOOL fIsDir = (IsFlagSet(pFtpIDList->dwIDType, IDTYPE_DIR));
|
||
|
|
||
|
if (fAssumeDirForUnknown && (IDTYPE_FILEORDIR == pFtpIDList->dwIDType))
|
||
|
{
|
||
|
// TraceMsg(TF_FTPURL_UTILS, "FtpItemID_IsDirectory() IDTYPE_FILEORDIR is set, so we assume %s", (fAssumeDirForUnknown ? TEXT("DIR") : TEXT("FILE")));
|
||
|
fIsDir = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// TraceMsg(TF_FTPURL_UTILS, "FtpItemID_IsDirectory() It is known to be a %s", (fIsDir ? TEXT("DIR") : TEXT("FILE")));
|
||
|
}
|
||
|
|
||
|
FtpItemID_FreeThunk(pFtpIDList);
|
||
|
return fIsDir;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/****************************************************\
|
||
|
Functions to work on an entire FTP PIDLs
|
||
|
\****************************************************/
|
||
|
#define SZ_ASCII_DOWNLOAD_TYPE TEXT("a")
|
||
|
#define SZ_BINARY_DOWNLOAD_TYPE TEXT("b")
|
||
|
|
||
|
HRESULT FtpPidl_GetDownloadTypeStr(LPCITEMIDLIST pidl, LPTSTR szDownloadType, DWORD cchTypeStrSize)
|
||
|
{
|
||
|
HRESULT hr = S_FALSE; // We may not have a type.
|
||
|
DWORD dwTypeID = FtpServerID_GetTypeID(pidl);
|
||
|
|
||
|
szDownloadType[0] = TEXT('\0');
|
||
|
if (IDVALID_DLTYPE & dwTypeID)
|
||
|
{
|
||
|
hr = S_OK;
|
||
|
StrCpyN(szDownloadType, SZ_FTP_URL_TYPE, cchTypeStrSize);
|
||
|
|
||
|
if (IDVALID_DL_ASCII & dwTypeID)
|
||
|
StrCatBuff(szDownloadType, SZ_ASCII_DOWNLOAD_TYPE, cchTypeStrSize);
|
||
|
else
|
||
|
StrCatBuff(szDownloadType, SZ_BINARY_DOWNLOAD_TYPE, cchTypeStrSize);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
DWORD FtpPidl_GetDownloadType(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
DWORD dwAttribs = FTP_TRANSFER_TYPE_UNKNOWN;
|
||
|
DWORD dwTypeID = FtpServerID_GetTypeID(pidl);
|
||
|
|
||
|
ASSERT(FtpID_IsServerItemID(pidl));
|
||
|
if (IDVALID_DLTYPE & dwTypeID)
|
||
|
{
|
||
|
if (IDVALID_DL_ASCII & dwTypeID)
|
||
|
dwAttribs = FTP_TRANSFER_TYPE_ASCII;
|
||
|
else
|
||
|
dwAttribs = FTP_TRANSFER_TYPE_BINARY;
|
||
|
}
|
||
|
|
||
|
return dwAttribs;
|
||
|
}
|
||
|
|
||
|
|
||
|
INTERNET_PORT FtpPidl_GetPortNum(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
ASSERT(FtpID_IsServerItemID(pidl));
|
||
|
|
||
|
return FtpServerID_GetPortNum(pidl);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL FtpPidl_IsDirectory(LPCITEMIDLIST pidl, BOOL fAssumeDirForUnknown)
|
||
|
{
|
||
|
BOOL fIsDir = FALSE;
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
|
||
|
if (!FtpID_IsServerItemID(pidlLast))
|
||
|
fIsDir = FtpItemID_IsDirectory(pidlLast, fAssumeDirForUnknown);
|
||
|
|
||
|
return fIsDir;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL FtpPidl_IsAnonymous(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
BOOL fIsAnonymous = TRUE;
|
||
|
|
||
|
if (IDVALID_USERNAME & FtpServerID_GetTypeID(pidl))
|
||
|
fIsAnonymous = FALSE;
|
||
|
|
||
|
return fIsAnonymous;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpPidl_GetServer(LPCITEMIDLIST pidl, LPTSTR pszServer, DWORD cchSize)
|
||
|
{
|
||
|
if (!FtpID_IsServerItemID(pidl)) // Will fail if we are handed a non-server ID.
|
||
|
return E_FAIL;
|
||
|
|
||
|
return FtpServerID_GetServer(pidl, pszServer, cchSize);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL FtpPidl_IsDNSServerName(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
BOOL fIsDNSServer = FALSE;
|
||
|
TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
|
||
|
|
||
|
if (EVAL(SUCCEEDED(FtpPidl_GetServer(pidl, szServer, ARRAYSIZE(szServer)))))
|
||
|
fIsDNSServer = !IsIPAddressStr(szServer);
|
||
|
|
||
|
return fIsDNSServer;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpPidl_GetUserName(LPCITEMIDLIST pidl, LPTSTR pszUserName, DWORD cchSize)
|
||
|
{
|
||
|
ASSERT(FtpID_IsServerItemID(pidl));
|
||
|
return FtpServerID_GetUserName(pidl, pszUserName, cchSize);
|
||
|
}
|
||
|
|
||
|
HRESULT FtpPidl_GetPassword(LPCITEMIDLIST pidl, LPTSTR pszPassword, DWORD cchSize, BOOL fIncludingHiddenPassword)
|
||
|
{
|
||
|
ASSERT(FtpID_IsServerItemID(pidl));
|
||
|
return FtpServerID_GetPassword(pidl, pszPassword, cchSize, fIncludingHiddenPassword);
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONGLONG FtpPidl_GetFileSize(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
ULONGLONG ullFileSize;
|
||
|
ullFileSize = 0;
|
||
|
|
||
|
if (!FtpID_IsServerItemID(pidlLast))
|
||
|
ullFileSize = FtpItemID_GetFileSize(pidlLast);
|
||
|
|
||
|
return ullFileSize;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpPidl_SetFileSize(LPCITEMIDLIST pidl, DWORD dwSizeHigh, DWORD dwSizeLow)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
|
||
|
if (!FtpID_IsServerItemID(pidlLast))
|
||
|
{
|
||
|
ULARGE_INTEGER uliFileSize;
|
||
|
|
||
|
uliFileSize.HighPart = dwSizeHigh;
|
||
|
uliFileSize.LowPart = dwSizeLow;
|
||
|
FtpItemID_SetFileSize(pidlLast, uliFileSize);
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// Return value in UTC time.
|
||
|
FILETIME FtpPidl_GetFileTime(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
FILETIME fileTimeFTP = FtpPidl_GetFTPFileTime(pidl); // This is what servers will be.
|
||
|
FILETIME fileTime;
|
||
|
|
||
|
EVAL(LocalFileTimeToFileTime(&fileTimeFTP, &fileTime));
|
||
|
|
||
|
return fileTime;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return value is in Local Time Zone.
|
||
|
FILETIME FtpPidl_GetFTPFileTime(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
FILETIME fileTime = {0}; // This is what servers will be.
|
||
|
|
||
|
if (!FtpID_IsServerItemID(pidl))
|
||
|
fileTime = FtpItemID_GetFileTime(pidl);
|
||
|
|
||
|
return fileTime;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpPidl_GetDisplayName(LPCITEMIDLIST pidl, LPWSTR pwzName, DWORD cchSize)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
|
||
|
if (pidl)
|
||
|
{
|
||
|
if (FtpID_IsServerItemID(pidl))
|
||
|
hr = FtpServerID_GetServer(pidl, pwzName, cchSize);
|
||
|
else
|
||
|
hr = FtpItemID_GetDisplayName(pidl, pwzName, cchSize);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpPidl_GetWireName(LPCITEMIDLIST pidl, LPWIRESTR pwName, DWORD cchSize)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
|
||
|
if (pidl)
|
||
|
{
|
||
|
if (FtpID_IsServerItemID(pidl))
|
||
|
{
|
||
|
WCHAR wzServerName[INTERNET_MAX_HOST_NAME_LENGTH];
|
||
|
|
||
|
// It's a good thing Server Names need to be in US ANSI
|
||
|
hr = FtpServerID_GetServer(pidl, wzServerName, ARRAYSIZE(wzServerName));
|
||
|
SHUnicodeToAnsi(wzServerName, pwName, cchSize);
|
||
|
}
|
||
|
else
|
||
|
hr = FtpItemID_GetWireName(pidl, pwName, cchSize);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpPidl_GetFragment(LPCITEMIDLIST pidl, LPTSTR pszFragment, DWORD cchSize)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
|
||
|
if (!FtpID_IsServerItemID(pidlLast))
|
||
|
hr = FtpItemID_GetFragment(pidlLast, pszFragment, cchSize);
|
||
|
else
|
||
|
{
|
||
|
pszFragment[0] = 0;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD FtpPidl_GetAttributes(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
DWORD dwAttribs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_TEMPORARY;
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
|
||
|
if (!FtpID_IsServerItemID(pidlLast))
|
||
|
dwAttribs = FtpItemID_GetAttributes(pidlLast);
|
||
|
else
|
||
|
dwAttribs = FILE_ATTRIBUTE_DIRECTORY;
|
||
|
|
||
|
return dwAttribs;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpPidl_SetAttributes(LPCITEMIDLIST pidl, DWORD dwAttribs)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
|
||
|
if (!FtpID_IsServerItemID(pidlLast))
|
||
|
hr = FtpItemID_SetAttributes(pidlLast, dwAttribs);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
// ftTimeDate In UTC
|
||
|
HRESULT FtpPidl_SetFileTime(LPCITEMIDLIST pidl, FILETIME ftTimeDate)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
|
||
|
if (!FtpID_IsServerItemID(pidlLast))
|
||
|
{
|
||
|
FtpItemID_SetFileTime(pidlLast, ftTimeDate);
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************\
|
||
|
FUNCTION: FtpPidl_GetFileWireName
|
||
|
|
||
|
DESCRIPTION:
|
||
|
Get the file name.
|
||
|
\****************************************************/
|
||
|
LPCWIRESTR FtpPidl_GetFileWireName(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
if (EVAL(!FtpID_IsServerItemID(pidl)) &&
|
||
|
!FtpItemID_IsFragment(pidl))
|
||
|
{
|
||
|
return FtpItemID_GetWireNameReference(pidl);
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************\
|
||
|
FUNCTION: FtpPidl_GetLastItemDisplayName
|
||
|
|
||
|
DESCRIPTION:
|
||
|
This will get the last item name, even if that
|
||
|
last item is a fragment.
|
||
|
\****************************************************/
|
||
|
HRESULT FtpPidl_GetLastItemDisplayName(LPCITEMIDLIST pidl, LPWSTR pwzName, DWORD cchSize)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
|
||
|
if (EVAL(!FtpID_IsServerItemID(pidlLast)) &&
|
||
|
FtpItemID_GetDisplayNameReference(pidlLast))
|
||
|
{
|
||
|
hr = FtpItemID_GetDisplayName(pidlLast, pwzName, cchSize);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************\
|
||
|
FUNCTION: FtpPidl_GetLastItemWireName
|
||
|
|
||
|
DESCRIPTION:
|
||
|
This will get the last item name, even if that
|
||
|
last item is a fragment.
|
||
|
\****************************************************/
|
||
|
LPCWIRESTR FtpPidl_GetLastItemWireName(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
LPCWIRESTR pszName = NULL;
|
||
|
|
||
|
if (pidl)
|
||
|
{
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
|
||
|
if (FtpItemID_IsFragment(pidlLast) && (pidlLast != pidl))
|
||
|
{
|
||
|
// Oops, we went to far. Step back one.
|
||
|
LPCITEMIDLIST pidlFrag = pidlLast;
|
||
|
|
||
|
pidlLast = pidl; // Start back at the beginning.
|
||
|
while (!FtpItemID_IsEqual(_ILNext(pidlLast), pidlFrag))
|
||
|
{
|
||
|
if (ILIsEmpty(pidlLast))
|
||
|
return NULL; // Break infinite loop.
|
||
|
|
||
|
pidlLast = _ILNext(pidlLast);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pszName = FtpPidl_GetFileWireName(pidlLast);
|
||
|
}
|
||
|
|
||
|
return pszName;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************\
|
||
|
FUNCTION: FtpPidl_GetLastFileDisplayName
|
||
|
|
||
|
DESCRIPTION:
|
||
|
This will get the last item name, even if that
|
||
|
last item is a fragment.
|
||
|
\****************************************************/
|
||
|
HRESULT FtpPidl_GetLastFileDisplayName(LPCITEMIDLIST pidl, LPWSTR pwzName, DWORD cchSize)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
|
||
|
if (FtpItemID_IsFragment(pidlLast) && (pidlLast != pidl))
|
||
|
{
|
||
|
// Oops, we went to far. Step back one.
|
||
|
LPCITEMIDLIST pidlFrag = pidlLast;
|
||
|
|
||
|
pidlLast = pidl; // Start back at the beginning.
|
||
|
while (!FtpItemID_IsEqual(_ILNext(pidlLast), pidlFrag))
|
||
|
{
|
||
|
if (ILIsEmpty(pidlLast))
|
||
|
return NULL; // Break infinite loop.
|
||
|
|
||
|
pidlLast = _ILNext(pidlLast);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (EVAL(!FtpID_IsServerItemID(pidlLast)))
|
||
|
{
|
||
|
LPCUWSTR pszUnalignedName = FtpItemID_GetDisplayNameReference(pidlLast);
|
||
|
|
||
|
if (pszUnalignedName)
|
||
|
{
|
||
|
ualstrcpynW(pwzName, pszUnalignedName, cchSize);
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************\
|
||
|
FUNCTION: FtpPidl_InsertVirtualRoot
|
||
|
|
||
|
DESCRIPTION:
|
||
|
This function will insert the virtual root path
|
||
|
(pidlVirtualRoot) into pidlFtpPath.
|
||
|
|
||
|
PARAMETERS:
|
||
|
pidlVirtualRoot: Does not have a ServerID
|
||
|
pidlFtpPath: Can have a ServerID
|
||
|
*ppidl: Will be pidlVirtualRoot with item ItemIDs from
|
||
|
pidlFtpPath behind it. (No ServerID)
|
||
|
\****************************************************/
|
||
|
HRESULT FtpPidl_InsertVirtualRoot(LPCITEMIDLIST pidlVirtualRoot, LPCITEMIDLIST pidlFtpPath, LPITEMIDLIST * ppidl)
|
||
|
{
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
|
||
|
if (FtpID_IsServerItemID(pidlFtpPath))
|
||
|
pidlFtpPath = _ILNext(pidlFtpPath);
|
||
|
|
||
|
*ppidl = ILCombine(pidlVirtualRoot, pidlFtpPath);
|
||
|
if (*ppidl)
|
||
|
hr = S_OK;
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD FtpPidl_GetVersion(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
DWORD dwVersion = 0;
|
||
|
|
||
|
if (EVAL(FtpID_IsServerItemID(pidl)))
|
||
|
{
|
||
|
LPFTPSERVERIDLIST pFtpServerItemID = FtpServerID_GetDataSafeThunk(pidl);
|
||
|
|
||
|
if (pFtpServerItemID)
|
||
|
{
|
||
|
dwVersion = pFtpServerItemID->dwVersion;
|
||
|
FtpServerID_FreeThunk(pFtpServerItemID);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return dwVersion;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL FtpPidl_IsValid(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
if (!EVAL(IsValidPIDL(pidl)))
|
||
|
return FALSE;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL FtpPidl_IsValidFull(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
if (!EVAL(FtpID_IsServerItemID(pidl)))
|
||
|
return FALSE;
|
||
|
|
||
|
if (!EVAL(FtpPidl_IsValid(pidl)))
|
||
|
return FALSE;
|
||
|
|
||
|
// We consider anything older than PIDL_VERSION_NUMBER_UPGRADE
|
||
|
// to be invalid.
|
||
|
return ((PIDL_VERSION_NUMBER_UPGRADE - 1) < FtpPidl_GetVersion(pidl));
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL FtpPidl_IsValidRelative(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
if (!EVAL(!FtpID_IsServerItemID(pidl)))
|
||
|
return FALSE; // This is a server item id which is not relative.
|
||
|
|
||
|
return FtpPidl_IsValid(pidl);
|
||
|
}
|
||
|
|
||
|
|
||
|
LPITEMIDLIST ILCloneFirstItemID(LPITEMIDLIST pidl)
|
||
|
{
|
||
|
LPITEMIDLIST pidlCopy = ILClone(pidl);
|
||
|
|
||
|
if (pidlCopy && pidlCopy->mkid.cb)
|
||
|
{
|
||
|
LPITEMIDLIST pSecondID = (LPITEMIDLIST)_ILNext(pidlCopy);
|
||
|
|
||
|
ASSERT(pSecondID);
|
||
|
// Remove the last one
|
||
|
pSecondID->mkid.cb = 0; // null-terminator
|
||
|
}
|
||
|
|
||
|
return pidlCopy;
|
||
|
}
|
||
|
|
||
|
BOOL FtpPidl_HasPath(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
BOOL fResult = TRUE;
|
||
|
|
||
|
if (!FtpPidl_IsValid(pidl) || !EVAL(FtpID_IsServerItemID(pidl)))
|
||
|
return FALSE;
|
||
|
|
||
|
if (!ILIsEmpty(pidl) && ILIsEmpty(_ILNext(pidl)))
|
||
|
fResult = FALSE;
|
||
|
|
||
|
return fResult;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT FtpPidl_SetFileItemType(LPITEMIDLIST pidl, BOOL fIsDir)
|
||
|
{
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
|
||
|
if (EVAL(FtpPidl_IsValid(pidl)) && EVAL(!FtpID_IsServerItemID(pidlLast)))
|
||
|
{
|
||
|
DWORD dwIDType = FtpItemID_GetTypeID(pidlLast);
|
||
|
|
||
|
ClearFlag(dwIDType, (IDTYPE_FILEORDIR | IDTYPE_DIR | IDTYPE_FILE));
|
||
|
SetFlag(dwIDType, (fIsDir ? IDTYPE_DIR : IDTYPE_FILE));
|
||
|
FtpItemID_SetTypeID((LPITEMIDLIST) pidlLast, dwIDType);
|
||
|
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL IsFtpPidlQuestionable(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
BOOL fIsQuestionable = FALSE;
|
||
|
LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
|
||
|
|
||
|
// Is it a Server Pidl? (All Server pidls aren't questionable)
|
||
|
if (FtpPidl_IsValid(pidl) && !FtpID_IsServerItemID(pidlLast))
|
||
|
{
|
||
|
// No, so it might be questionable.
|
||
|
|
||
|
// Does it have "File or Dir" bit set?
|
||
|
if (IsFlagSet(FtpItemID_GetTypeID(pidlLast), IDTYPE_FILEORDIR))
|
||
|
fIsQuestionable = TRUE;
|
||
|
}
|
||
|
|
||
|
return fIsQuestionable;
|
||
|
}
|
||
|
|
||
|
|
||
|
LPITEMIDLIST FtpCloneServerID(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
LPITEMIDLIST pidlResult = NULL;
|
||
|
|
||
|
if (EVAL(FtpID_IsServerItemID(pidl)))
|
||
|
{
|
||
|
pidlResult = ILClone(pidl);
|
||
|
|
||
|
while (!ILIsEmpty(_ILNext(pidlResult)))
|
||
|
ILRemoveLastID(pidlResult);
|
||
|
}
|
||
|
|
||
|
return pidlResult;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************\
|
||
|
FUNCTION: FtpPidl_ReplacePath
|
||
|
|
||
|
DESCRIPTION:
|
||
|
This function will fill in *ppidlOut with a pidl that contains the
|
||
|
FtpServerID from pidlServer and the FtpItemIDs from pidlFtpPath.
|
||
|
\*****************************************************************************/
|
||
|
HRESULT FtpPidl_ReplacePath(LPCITEMIDLIST pidlServer, LPCITEMIDLIST pidlFtpPath, LPITEMIDLIST * ppidlOut)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
|
||
|
*ppidlOut = NULL;
|
||
|
if (EVAL(FtpID_IsServerItemID(pidlServer)))
|
||
|
{
|
||
|
LPITEMIDLIST pidlServerOnly = FtpCloneServerID(pidlServer);
|
||
|
|
||
|
if (pidlServerOnly)
|
||
|
{
|
||
|
if (FtpID_IsServerItemID(pidlFtpPath))
|
||
|
pidlFtpPath = _ILNext(pidlFtpPath);
|
||
|
|
||
|
*ppidlOut = ILCombine(pidlServerOnly, pidlFtpPath);
|
||
|
if (*ppidlOut)
|
||
|
hr = S_OK;
|
||
|
else
|
||
|
hr = E_FAIL;
|
||
|
|
||
|
ILFree(pidlServerOnly);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ASSERT_POINTER_MATCHES_HRESULT(*ppidlOut, hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL FtpItemID_IsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
||
|
{
|
||
|
// Don't repeat recursively.
|
||
|
return (S_OK == _FtpItemID_CompareOneID(COL_NAME, pidl1, pidl2, FALSE));
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL FtpPidl_IsPathEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
||
|
{
|
||
|
// This works recursively.
|
||
|
return ((0 == FtpItemID_CompareIDsInt(COL_NAME, pidl1, pidl2, FCMP_NORMAL)) ? TRUE : FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
// is pidlChild a child of pidlParent
|
||
|
BOOL FtpItemID_IsParent(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlChild)
|
||
|
{
|
||
|
BOOL fIsChild = TRUE;
|
||
|
|
||
|
if (pidlChild)
|
||
|
{
|
||
|
LPITEMIDLIST pidl1Iterate = (LPITEMIDLIST) pidlParent;
|
||
|
LPITEMIDLIST pidl2Iterate = (LPITEMIDLIST) pidlChild;
|
||
|
|
||
|
ASSERT(!FtpID_IsServerItemID(pidl1Iterate) && pidlParent && !FtpID_IsServerItemID(pidl2Iterate));
|
||
|
|
||
|
// Let's see if pidl starts off with
|
||
|
while (fIsChild && pidl1Iterate && !ILIsEmpty(pidl1Iterate) &&
|
||
|
pidl2Iterate && !ILIsEmpty(pidl2Iterate) &&
|
||
|
FtpItemID_IsEqual(pidl1Iterate, pidl2Iterate))
|
||
|
{
|
||
|
fIsChild = FtpItemID_IsEqual(pidl1Iterate, pidl2Iterate);
|
||
|
|
||
|
pidl1Iterate = _ILNext(pidl1Iterate);
|
||
|
pidl2Iterate = _ILNext(pidl2Iterate);
|
||
|
}
|
||
|
|
||
|
if (!(ILIsEmpty(pidl1Iterate) && !ILIsEmpty(pidl2Iterate)))
|
||
|
fIsChild = FALSE;
|
||
|
}
|
||
|
else
|
||
|
fIsChild = FALSE;
|
||
|
|
||
|
return fIsChild;
|
||
|
}
|
||
|
|
||
|
|
||
|
// is pidlChild a child of pidlParent, so all the itemIDs in
|
||
|
// pidlParent are in pidlChild, but pidlChild has more.
|
||
|
// This will return a pointer to those itemIDs.
|
||
|
LPCITEMIDLIST FtpItemID_FindDifference(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlChild)
|
||
|
{
|
||
|
LPCITEMIDLIST pidlDiff = (LPITEMIDLIST) pidlChild;
|
||
|
|
||
|
if (pidlChild)
|
||
|
{
|
||
|
LPITEMIDLIST pidl1Iterate = (LPITEMIDLIST) pidlParent;
|
||
|
|
||
|
if (FtpID_IsServerItemID(pidl1Iterate))
|
||
|
pidl1Iterate = _ILNext(pidl1Iterate);
|
||
|
|
||
|
if (FtpID_IsServerItemID(pidlDiff))
|
||
|
pidlDiff = _ILNext(pidlDiff);
|
||
|
|
||
|
// Let's see if pidl starts off with
|
||
|
while (pidl1Iterate && !ILIsEmpty(pidl1Iterate) &&
|
||
|
pidlDiff && !ILIsEmpty(pidlDiff) &&
|
||
|
FtpItemID_IsEqual(pidl1Iterate, pidlDiff))
|
||
|
{
|
||
|
pidlDiff = _ILNext(pidlDiff);
|
||
|
pidl1Iterate = _ILNext(pidl1Iterate);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pidlDiff;
|
||
|
}
|
||
|
|
||
|
|