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

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;
}