764 lines
24 KiB
C++
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;
|
|
}
|
|
|
|
|