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

764 lines
24 KiB
C++

/*****************************************************************************
*
* ftpurl.cpp - Creating, encoding, and decoding URLs
*
*****************************************************************************/
#include "priv.h"
#include "ftpurl.h"
///////////////////////////////////////////////////////////////////////
// URL Path Functions (Obsolete?)
///////////////////////////////////////////////////////////////////////
/*****************************************************************************\
FUNCTION: UrlGetPath
DESCRIPTION:
pszUrlPath will NOT include the fragment if there is any.
\*****************************************************************************/
HRESULT UrlGetDifference(LPCTSTR pszBaseUrl, LPCTSTR pszSuperUrl, LPTSTR pszPathDiff, DWORD cchSize)
{
HRESULT hr = E_INVALIDARG;
pszPathDiff[0] = TEXT('\0');
if ((lstrlen(pszBaseUrl) <= lstrlen(pszSuperUrl)) &&
!StrCmpN(pszBaseUrl, pszSuperUrl, lstrlen(pszBaseUrl) - 1))
{
LPTSTR pszDelta = (LPTSTR) &pszSuperUrl[lstrlen(pszBaseUrl)];
if (TEXT('/') == pszDelta[0])
pszDelta = CharNext(pszDelta); // Skip past this.
StrCpyN(pszPathDiff, pszDelta, cchSize);
hr = S_OK;
}
return hr;
}
/*****************************************************************************\
FUNCTION: UrlGetPath
DESCRIPTION:
pszUrlPath will NOT include the fragment if there is any.
\*****************************************************************************/
HRESULT UrlPathToFilePath(LPCTSTR pszSourceUrlPath, LPTSTR pszDestFilePath, DWORD cchSize)
{
HRESULT hr = E_INVALIDARG;
LPTSTR pszSeparator;
// Is the source and destination the differnt?
if (pszSourceUrlPath != pszDestFilePath)
{
// Yes, so we need to fill the dest before we start modifying it.
StrCpyN(pszDestFilePath, pszSourceUrlPath, cchSize);
}
while (pszSeparator = StrChr(pszDestFilePath, TEXT('/')))
pszSeparator[0] = TEXT('\\');
// Some people use "Test%20File.txt" when "%20" is really in the file name.
// ASSERT(!StrChr(pszDestFilePath, TEXT('%'))); // Assert it doesn't contain '%' or it probably has escaped url stuff.
return hr;
}
/*****************************************************************************\
FUNCTION: UrlPathRemoveSlashW
DESCRIPTION:
\*****************************************************************************/
HRESULT UrlPathRemoveSlashW(LPWSTR pszUrlPath)
{
LPWSTR pszEndOfPath = &pszUrlPath[lstrlenW(pszUrlPath) - 1];
// Is it missing a backslash?
if ((pszEndOfPath >= pszUrlPath) && (CH_URL_URL_SLASHW == pszEndOfPath[0]))
pszEndOfPath[0] = 0; // Yes, so remove it.
return S_OK;
}
/*****************************************************************************\
FUNCTION: UrlPathRemoveSlashA
DESCRIPTION:
\*****************************************************************************/
HRESULT UrlPathRemoveSlashA(LPSTR pszUrlPath)
{
LPSTR pszEndOfPath = &pszUrlPath[lstrlenA(pszUrlPath) - 1];
// Is it missing a backslash?
if ((pszEndOfPath >= pszUrlPath) && (CH_URL_URL_SLASHA == pszEndOfPath[0]))
pszEndOfPath[0] = 0; // Yes, so remove it.
return S_OK;
}
/*****************************************************************************\
FUNCTION: UrlPathRemoveFrontSlashW
DESCRIPTION:
\*****************************************************************************/
HRESULT UrlPathRemoveFrontSlashW(LPWSTR pszUrlPath)
{
if (pszUrlPath && (CH_URL_URL_SLASHW == pszUrlPath[0]))
return CharReplaceWithStrW(pszUrlPath, lstrlen(pszUrlPath), 1, SZ_EMPTYW);
else
return S_OK;
}
/*****************************************************************************\
FUNCTION: UrlPathRemoveFrontSlashA
DESCRIPTION:
\*****************************************************************************/
HRESULT UrlPathRemoveFrontSlashA(LPSTR pszUrlPath)
{
if (pszUrlPath && (CH_URL_URL_SLASHA == pszUrlPath[0]))
return CharReplaceWithStrA(pszUrlPath, lstrlenA(pszUrlPath), 1, SZ_EMPTYA);
else
return S_OK;
}
/*****************************************************************************\
FUNCTION: UrlPathToFilePathW
DESCRIPTION:
\*****************************************************************************/
HRESULT UrlPathToFilePathW(LPWSTR pszPath)
{
while (pszPath = StrChrW(pszPath, CH_URL_URL_SLASHW))
pszPath[0] = CH_URL_SLASHW;
return S_OK;
}
/*****************************************************************************\
FUNCTION: UrlPathToFilePathA
DESCRIPTION:
\*****************************************************************************/
HRESULT UrlPathToFilePathA(LPSTR pszPath)
{
while (pszPath = StrChrA(pszPath, CH_URL_URL_SLASHA))
pszPath[0] = CH_URL_SLASHA;
return S_OK;
}
/*****************************************************************************\
FUNCTION: FilePathToUrlPathW
DESCRIPTION:
\*****************************************************************************/
HRESULT FilePathToUrlPathW(LPWSTR pszPath)
{
while (pszPath = StrChrW(pszPath, CH_URL_SLASHW))
pszPath[0] = CH_URL_URL_SLASHW;
return S_OK;
}
/*****************************************************************************\
FUNCTION: FilePathToUrlPathA
DESCRIPTION:
\*****************************************************************************/
HRESULT FilePathToUrlPathA(LPSTR pszPath)
{
while (pszPath = StrChrA(pszPath, CH_URL_SLASHA))
pszPath[0] = CH_URL_URL_SLASHA;
return S_OK;
}
/*****************************************************************************\
FUNCTION: UrlPathAdd
DESCRIPTION:
...
\*****************************************************************************/
HRESULT UrlPathAdd(LPTSTR pszUrl, DWORD cchUrlSize, LPCTSTR pszSegment)
{
// If the segment starts with a slash, skip it.
if (TEXT('/') == pszSegment[0])
pszSegment = CharNext(pszSegment);
StrCatBuff(pszUrl, pszSegment, cchUrlSize);
return S_OK;
}
/*****************************************************************************\
StrRetFromFtpPidl
\*****************************************************************************/
HRESULT StrRetFromFtpPidl(LPSTRRET pStrRet, DWORD shgno, LPCITEMIDLIST pidl)
{
HRESULT hr = S_OK;
TCHAR szUrl[MAX_URL_STRING];
szUrl[0] = 0;
hr = UrlCreateFromPidl(pidl, shgno, szUrl, ARRAYSIZE(szUrl), ICU_ESCAPE | ICU_USERNAME, TRUE);
if (SUCCEEDED(hr))
{
// Will it fit into STRRET.cStr?
if (lstrlen(szUrl) < ARRAYSIZE(pStrRet->cStr))
{
// Yes, so there it goes...
pStrRet->uType = STRRET_CSTR;
SHTCharToAnsi(szUrl, pStrRet->cStr, ARRAYSIZE(pStrRet->cStr));
}
else
{
// No, so we will need to allocate it
LPWSTR pwzAllocedStr = NULL;
UINT cch = lstrlen(szUrl) + 1;
pwzAllocedStr = (LPWSTR) SHAlloc(CbFromCchW(cch));
pStrRet->uType = STRRET_WSTR;
pStrRet->pOleStr = pwzAllocedStr;
if (pwzAllocedStr)
SHTCharToUnicode(szUrl, pwzAllocedStr, cch);
else
hr = E_OUTOFMEMORY;
}
}
return hr;
}
/*****************************************************************************\
FUNCTION: GetLastSegment
DESCRIPTION:
\*****************************************************************************/
LPTSTR GetLastSegment(LPCTSTR pszUrl)
{
LPTSTR pszLastSeg = (LPTSTR) pszUrl;
LPTSTR pszNextPossibleSeg;
while (pszNextPossibleSeg = StrChr(pszLastSeg, TEXT('/')))
{
if (TEXT('\0') != CharNext(pszNextPossibleSeg))
pszLastSeg = CharNext(pszNextPossibleSeg);
else
break; // We are done.
}
if (TEXT('/') == pszLastSeg[0])
pszLastSeg = CharNext(pszLastSeg);
return pszLastSeg;
}
/*****************************************************************************\
FUNCTION: UrlRemoveDownloadType
DESCRIPTION:
\*****************************************************************************/
HRESULT UrlRemoveDownloadType(LPTSTR pszUrlPath, BOOL * pfTypeSpecified, BOOL * pfType)
{
HRESULT hr = S_FALSE; // Specified? (Not yet)
LPTSTR pszDownloadType;
if (pfTypeSpecified)
*pfTypeSpecified = TRUE;
// Did the user specify a download type. szPath="Dir1/Dir2/file.txt;type=a".
// TODO: Search Recursively because each segment in the path can have a
// type.
// Example Url="ftp://server/Dir1;type=a/Dir2;type=a/File.txt;type=b
if (pszDownloadType = StrStrI(pszUrlPath, SZ_FTP_URL_TYPE))
{
TCHAR chType;
if (pfTypeSpecified)
*pfTypeSpecified = TRUE;
pszDownloadType[0] = TEXT('\0'); // Terminate pszUrlPath and remove this junk.
chType = pszDownloadType[ARRAYSIZE(SZ_FTP_URL_TYPE) - 1];
if (pfType)
{
if ((TEXT('a') == chType) || (TEXT('A') == chType))
*pfType = TRUE;
else
*pfType = TRUE;
}
hr = S_OK;
}
return hr;
}
/*****************************************************************************\
FUNCTION: IsIPAddressStr
DESCRIPTION:
This function exists to detect an IP Address server name ("124.42.3.53") vs.
a DNS domain name ("foobar", or "ftp.foobar.com"). I current accept more than
4 segments because of 6-bit IP address.
TODO: To be thurough, I should probably made sure each segment is
smaller than 256.
\*****************************************************************************/
BOOL IsIPAddressStr(LPTSTR pszServer)
{
BOOL fIsIPAddressStr = TRUE;
LPTSTR pszCurrentChar = pszServer;
int nDigits = 0;
int nSegments = 1;
while (fIsIPAddressStr && pszCurrentChar[0])
{
if (TEXT('.') == pszCurrentChar[0])
{
nSegments++;
if ((0 == nDigits) || (4 < nDigits))
fIsIPAddressStr = FALSE; // it started with a '.', ie, ".xxxxx"
nDigits = 0;
}
nDigits++;
if (nDigits > 4)
fIsIPAddressStr = FALSE; // To many digits, ie "12345.xxxx"
if (((TEXT('0') > pszCurrentChar[0]) || (TEXT('9') < pszCurrentChar[0])) &&
(TEXT('.') != pszCurrentChar[0]))
{
fIsIPAddressStr = FALSE; // it's outside of the 0-9 range.
}
pszCurrentChar++; // Next character.
}
if (nSegments != 4)
fIsIPAddressStr = FALSE; // Needs to have at least 4 segments ("1.2.3.4", "1.2.3.4.5")
return fIsIPAddressStr;
}
/*****************************************************************************\
FUNCTION: PidlGenerateSiteLookupStr
DESCRIPTION:
Sample Input: "ftp://user:password@ftp.server.com:69/Dir1/Dir2/File.txt"
Sample Output: "ftp://user:password@ftp.server.com:69/"
This is used to keep track of unique servers for CFtpSite. A CFtpSite needs
to be created for each unique site, which includes different users that are
logged onto the same site because of rooted directories.
\*****************************************************************************/
HRESULT PidlGenerateSiteLookupStr(LPCITEMIDLIST pidl, LPTSTR pszLookupStr, DWORD cchSize)
{
HRESULT hr = E_FAIL;
// Some strange clients pass in non-Server IDs, like comdlg.
if (FtpID_IsServerItemID(pidl))
{
LPITEMIDLIST pidlServer = FtpCloneServerID(pidl);
if (pidlServer)
{
hr = UrlCreateFromPidlW(pidlServer, SHGDN_FORPARSING, pszLookupStr, cchSize, (ICU_ESCAPE | ICU_USERNAME), FALSE);
ILFree(pidlServer);
}
else
hr = E_OUTOFMEMORY;
}
return hr;
}
HRESULT PidlReplaceUserPassword(LPCITEMIDLIST pidlIn, LPITEMIDLIST * ppidlOut, IMalloc * pm, LPCTSTR pszUserName, LPCTSTR pszPassword)
{
TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH];
HRESULT hr = FtpPidl_GetServer(pidlIn, szServer, ARRAYSIZE(szServer));
if (!pszUserName) // May be NULL.
{
pszUserName = szUserName;
EVAL(SUCCEEDED(FtpPidl_GetUserName(pidlIn, szUserName, ARRAYSIZE(szUserName))));
}
*ppidlOut = NULL;
if (SUCCEEDED(hr))
{
LPITEMIDLIST pidlServer;
hr = FtpServerID_Create(szServer, pszUserName, pszPassword, FtpServerID_GetTypeID(pidlIn), FtpServerID_GetPortNum(pidlIn), &pidlServer, pm, TRUE);
if (SUCCEEDED(hr))
{
LPITEMIDLIST pidlFtpPath = _ILNext(pidlIn);
*ppidlOut = ILCombine(pidlServer, pidlFtpPath);
ILFree(pidlServer);
}
}
return hr;
}
HRESULT UrlReplaceUserPassword(LPTSTR pszUrlPath, DWORD cchSize, LPCTSTR pszUserName, LPCTSTR pszPassword)
{
HRESULT hr = E_FAIL;
URL_COMPONENTS urlComps = {0};
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 szExtraInfo[MAX_PATH]; // Includes Port Number and download type (ASCII, Binary, Detect)
BOOL fResult;
urlComps.dwStructSize = sizeof(urlComps);
urlComps.lpszHostName = szServer;
urlComps.dwHostNameLength = ARRAYSIZE(szServer);
urlComps.lpszUrlPath = szUrlPath;
urlComps.dwUrlPathLength = ARRAYSIZE(szUrlPath);
urlComps.lpszExtraInfo = szExtraInfo;
urlComps.dwExtraInfoLength = ARRAYSIZE(szExtraInfo);
urlComps.lpszUserName = szUserName;
urlComps.dwUserNameLength = ARRAYSIZE(szUserName);
urlComps.lpszPassword = szPassword;
urlComps.dwPasswordLength = ARRAYSIZE(szPassword);
fResult = InternetCrackUrl(pszUrlPath, 0, ICU_DECODE, &urlComps);
if (fResult)
{
urlComps.dwStructSize = sizeof(urlComps);
urlComps.lpszHostName = szServer;
urlComps.lpszUserName = (LPTSTR)(pszUserName ? pszUserName : szUserName);
urlComps.dwUserNameLength = (pszUserName ? lstrlen(pszUserName) : lstrlen(szUserName));
urlComps.lpszPassword = (LPTSTR)pszPassword; // It may be valid for caller to pass NULL
urlComps.dwPasswordLength = (pszPassword ? lstrlen(pszPassword) : 0);
urlComps.lpszExtraInfo = szExtraInfo;
urlComps.dwExtraInfoLength = ARRAYSIZE(szExtraInfo);
fResult = InternetCreateUrl(&urlComps, (ICU_ESCAPE | ICU_USERNAME), pszUrlPath, &cchSize);
if (fResult)
{
hr = S_OK;
}
}
return hr;
}
// InternetCreateUrlW() will write into pszUrl but won't terminate the string.
// This is hard to detect because half the time the place where the terminator should
// go may coincidentally contain a terminator. This code forces the bug to happen.
#define TEST_FOR_INTERNETCREATEURL_BUG 1
#define INTERNETCREATEURL_BUG_WORKAROUND 1
HRESULT UrlCreateEx(LPCTSTR pszServer, LPCTSTR pszUser, LPCTSTR pszPassword, LPCTSTR pszUrlPath, LPCTSTR pszFragment, INTERNET_PORT ipPortNum, LPCTSTR pszDownloadType, LPTSTR pszUrl, DWORD cchSize, DWORD dwFlags)
{
HRESULT hr = E_FAIL;
DWORD cchSizeCopy = cchSize;
#if DEBUG && TEST_FOR_INTERNETCREATEURL_BUG
LPTSTR pszDebugStr = pszUrl;
for (DWORD dwIndex = (cchSize - 2); dwIndex; dwIndex--)
{
#ifndef INTERNETCREATEURL_BUG_WORKAROUND
pszDebugStr[0] = -1; // This will force a buffer w/o terminators.
#else // INTERNETCREATEURL_BUG_WORKAROUND
pszDebugStr[0] = 0; // This will work around the bug.
#endif // INTERNETCREATEURL_BUG_WORKAROUND
pszDebugStr++;
}
#endif // DEBUG && TEST_FOR_INTERNETCREATEURL_BUG
URL_COMPONENTS urlComp = {sizeof(URL_COMPONENTS), NULL, 0, INTERNET_SCHEME_FTP, (LPTSTR) pszServer, 0,
ipPortNum, (LPTSTR) NULL_FOR_EMPTYSTR(pszUser), 0, (LPTSTR) NULL_FOR_EMPTYSTR(pszPassword), 0,
(LPTSTR) pszUrlPath, 0, (LPTSTR) NULL, 0};
if (EVAL(InternetCreateUrl(&urlComp, dwFlags | ICU_USERNAME, pszUrl, &cchSizeCopy)))
{
hr = S_OK;
if (pszFragment)
StrCatBuff(pszUrl, pszFragment, cchSize);
}
#if DEBUG && TEST_FOR_INTERNETCREATEURL_BUG
#ifdef INTERNETCREATEURL_BUG_WORKAROUND
// Make sure we hit a terminator and not a -1, which should never happen in URL strings.
for (pszDebugStr = pszUrl; pszDebugStr[0]; pszDebugStr++)
ASSERT(-1 != pszDebugStr[0]);
#endif // INTERNETCREATEURL_BUG_WORKAROUND
#endif // DEBUG && TEST_FOR_INTERNETCREATEURL_BUG
return hr;
}
HRESULT UrlCreate(LPCTSTR pszServer, LPCTSTR pszUser, LPCTSTR pszPassword, LPCTSTR pszUrlPath, LPCTSTR pszFragment, INTERNET_PORT ipPortNum, LPCTSTR pszDownloadType, LPTSTR pszUrl, DWORD cchSize)
{
return UrlCreateEx(pszServer, pszUser, pszPassword, pszUrlPath, pszFragment, ipPortNum, pszDownloadType, pszUrl, cchSize, ICU_ESCAPE);
}
BOOL IsEmptyUrlPath(LPCTSTR pszUrlPath)
{
BOOL fResult = FALSE;
if (!pszUrlPath ||
!pszUrlPath[0] ||
(((TEXT('/') == pszUrlPath[0]) && (!pszUrlPath[1]))))
{
fResult = TRUE;
}
return fResult;
}
///////////////////////////////////////////////////////////////////////
// Wire Path Functions (UTF-8 or DBCS/MBCS)
///////////////////////////////////////////////////////////////////////
/*****************************************************************************\
FUNCTION: WirePathAdd
DESCRIPTION:
...
\*****************************************************************************/
HRESULT WirePathAdd(LPWIRESTR pwWirePath, DWORD cchUrlSize, LPCWIRESTR pwWireSegment)
{
// If the segment starts with a slash, skip it.
if ('/' == pwWireSegment[0])
pwWireSegment = CharNextA(pwWireSegment);
StrCatBuffA(pwWirePath, pwWireSegment, cchUrlSize);
return S_OK;
}
/*****************************************************************************\
FUNCTION: WirePathAppendSlash
DESCRIPTION:
\*****************************************************************************/
HRESULT WirePathAppendSlash(LPWIRESTR pwWirePath, DWORD cchWirePathSize)
{
HRESULT hr = E_FAIL;
DWORD cchSize = lstrlenA(pwWirePath);
// Is there enough room?
if (cchSize < (cchWirePathSize - 1))
{
LPWIRESTR pwEndOfPath = &pwWirePath[cchSize - 1];
// Is it missing a backslash?
if ((pwEndOfPath >= pwWirePath) && '/' != pwEndOfPath[0])
StrCatA(pwEndOfPath, SZ_URL_SLASHA); // Yes, so add it.
hr = S_OK;
}
return hr;
}
/*****************************************************************************\
FUNCTION: WirePathAppend
DESCRIPTION:
...
\*****************************************************************************/
HRESULT WirePathAppend(LPWIRESTR pwWirePath, DWORD cchUrlSize, LPCWIRESTR pwWireSegment)
{
if (!EVAL(pwWireSegment))
return E_INVALIDARG;
WirePathAppendSlash(pwWirePath, cchUrlSize); // Make sure the base url ends in a '/'. Note it may be "ftp://".
return WirePathAdd(pwWirePath, cchUrlSize, pwWireSegment);
}
/*****************************************************************************\
FUNCTION: UrlGetFirstPathSegment
PARAMETERS:
[IN] pszFullPath - "Dir1\Dir2\Dir3"
[OUT] szFirstItem - "Dir1" [OPTIONAL]
[OUT] szRemaining - "Dir2\Dir3" [OPTIONAL]
\*****************************************************************************/
HRESULT WirePathGetFirstSegment(LPCWIRESTR pwFtpWirePath, LPWIRESTR wFirstItem, DWORD cchFirstItemSize, BOOL * pfWasFragSeparator, LPWIRESTR wRemaining, DWORD cchRemainingSize, BOOL * pfIsDir)
{
HRESULT hr = S_OK;
LPCWIRESTR pwSegEnding = StrChrA(pwFtpWirePath, CH_URL_URL_SLASH);
if (pfIsDir)
*pfIsDir = FALSE;
ASSERT((CH_URL_URL_SLASHA != pwFtpWirePath[0])); // You will probably not get what you want.
if (pwSegEnding)
{
if (wFirstItem)
{
DWORD cchSize = (DWORD) (pwSegEnding - pwFtpWirePath + 1);
StrCpyNA(wFirstItem, pwFtpWirePath, (cchSize <= cchFirstItemSize) ? cchSize : cchFirstItemSize);
}
if (pfIsDir && (CH_URL_URL_SLASHA == pwSegEnding[0]))
*pfIsDir = TRUE; // Tell them that it is a directory.
if (wRemaining)
StrCpyNA(wRemaining, CharNextA(pwSegEnding), cchRemainingSize);
if (0 == pwSegEnding[1])
hr = S_FALSE; // End of the line.
}
else
{
if (wFirstItem)
StrCpyNA(wFirstItem, pwFtpWirePath, cchFirstItemSize); // pszFullPath contains only one segment
if (wRemaining)
wRemaining[0] = 0;
hr = S_FALSE; // Indicate that there aren't any more directories left.
}
return hr;
}
///////////////////////////////////////////////////////////////////////
// Display Path Functions (Unicode)
///////////////////////////////////////////////////////////////////////
/*****************************************************************************\
FUNCTION: DisplayPathAdd
DESCRIPTION:
...
\*****************************************************************************/
HRESULT DisplayPathAdd(LPWSTR pwzUrl, DWORD cchUrlSize, LPCWSTR pwzSegment)
{
// If the segment starts with a slash, skip it.
if (L'/' == pwzSegment[0])
pwzSegment = CharNext(pwzSegment);
StrCatBuffW(pwzUrl, pwzSegment, cchUrlSize);
return S_OK;
}
/*****************************************************************************\
FUNCTION: DisplayPathAppendSlash
DESCRIPTION:
\*****************************************************************************/
HRESULT DisplayPathAppendSlash(LPWSTR pwzDisplayPath, DWORD cchSize)
{
DWORD cchCurrentSize = lstrlenW(pwzDisplayPath);
HRESULT hr = CO_E_PATHTOOLONG;
if (cchCurrentSize < (cchSize - 2))
{
LPWSTR pwzEndOfPath = &pwzDisplayPath[cchCurrentSize - 1];
// Is it missing a backslash?
if ((pwzEndOfPath >= pwzDisplayPath) && TEXT('/') != pwzEndOfPath[0])
StrCatBuff(pwzEndOfPath, SZ_URL_SLASH, (cchCurrentSize + 2)); // Yes, so add it.
hr = S_OK;
}
return hr;
}
/*****************************************************************************\
FUNCTION: DisplayPathAppend
DESCRIPTION:
...
\*****************************************************************************/
HRESULT DisplayPathAppend(LPWSTR pwzDisplayPath, DWORD cchUrlSize, LPCWSTR pwzDisplaySegment)
{
if (!EVAL(pwzDisplaySegment))
return E_INVALIDARG;
DisplayPathAppendSlash(pwzDisplayPath, cchUrlSize); // Make sure the base url ends in a '/'. Note it may be "ftp://".
return DisplayPathAdd(pwzDisplayPath, cchUrlSize, pwzDisplaySegment);
}
/*****************************************************************************\
FUNCTION: DisplayPathGetFirstSegment
PARAMETERS:
[IN] pszFullPath - "Dir1\Dir2\Dir3"
[OUT] szFirstItem - "Dir1" [OPTIONAL]
[OUT] szRemaining - "Dir2\Dir3" [OPTIONAL]
\*****************************************************************************/
HRESULT DisplayPathGetFirstSegment(LPCWSTR pwzFullPath, LPWSTR pwzFirstItem, DWORD cchFirstItemSize, BOOL * pfWasFragSeparator, LPWSTR pwzRemaining, DWORD cchRemainingSize, BOOL * pfIsDir)
{
HRESULT hr = S_OK;
LPWSTR pwzSegEnding = StrChrW(pwzFullPath, CH_URL_URL_SLASH);
if (pfIsDir)
*pfIsDir = FALSE;
// This will happen if the user enters an incorrect URL, like "ftp://wired//"
// ASSERT((CH_URL_URL_SLASHW != pwzFullPath[0])); // You will probably not get what you want.
if (pwzSegEnding)
{
if (pwzFirstItem)
{
DWORD cchSize = (DWORD) (pwzSegEnding - pwzFullPath + 1);
StrCpyNW(pwzFirstItem, pwzFullPath, (cchSize <= cchFirstItemSize) ? cchSize : cchFirstItemSize);
}
if (pfIsDir && (CH_URL_URL_SLASHW == pwzSegEnding[0]))
*pfIsDir = TRUE; // Tell them that it is a directory.
if (pwzRemaining)
StrCpyNW(pwzRemaining, CharNextW(pwzSegEnding), cchRemainingSize);
if (0 == pwzSegEnding[1])
hr = S_FALSE; // End of the line.
}
else
{
if (pwzFirstItem)
StrCpyNW(pwzFirstItem, pwzFullPath, cchFirstItemSize); // pszFullPath contains only one segment
if (pwzRemaining)
pwzRemaining[0] = 0;
hr = S_FALSE; // Indicate that there aren't any more directories left.
}
return hr;
}