Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

763 lines
25 KiB

  1. /*****************************************************************************
  2. *
  3. * ftpurl.cpp - Creating, encoding, and decoding URLs
  4. *
  5. *****************************************************************************/
  6. #include "priv.h"
  7. #include "ftpurl.h"
  8. ///////////////////////////////////////////////////////////////////////
  9. // URL Path Functions (Obsolete?)
  10. ///////////////////////////////////////////////////////////////////////
  11. /*****************************************************************************\
  12. FUNCTION: UrlGetPath
  13. DESCRIPTION:
  14. pszUrlPath will NOT include the fragment if there is any.
  15. \*****************************************************************************/
  16. HRESULT UrlGetDifference(LPCTSTR pszBaseUrl, LPCTSTR pszSuperUrl, LPTSTR pszPathDiff, DWORD cchSize)
  17. {
  18. HRESULT hr = E_INVALIDARG;
  19. pszPathDiff[0] = TEXT('\0');
  20. if ((lstrlen(pszBaseUrl) <= lstrlen(pszSuperUrl)) &&
  21. !StrCmpN(pszBaseUrl, pszSuperUrl, lstrlen(pszBaseUrl) - 1))
  22. {
  23. LPTSTR pszDelta = (LPTSTR) &pszSuperUrl[lstrlen(pszBaseUrl)];
  24. if (TEXT('/') == pszDelta[0])
  25. pszDelta = CharNext(pszDelta); // Skip past this.
  26. StrCpyN(pszPathDiff, pszDelta, cchSize);
  27. hr = S_OK;
  28. }
  29. return hr;
  30. }
  31. /*****************************************************************************\
  32. FUNCTION: UrlGetPath
  33. DESCRIPTION:
  34. pszUrlPath will NOT include the fragment if there is any.
  35. \*****************************************************************************/
  36. HRESULT UrlPathToFilePath(LPCTSTR pszSourceUrlPath, LPTSTR pszDestFilePath, DWORD cchSize)
  37. {
  38. HRESULT hr = E_INVALIDARG;
  39. LPTSTR pszSeparator;
  40. // Is the source and destination the differnt?
  41. if (pszSourceUrlPath != pszDestFilePath)
  42. {
  43. // Yes, so we need to fill the dest before we start modifying it.
  44. StrCpyN(pszDestFilePath, pszSourceUrlPath, cchSize);
  45. }
  46. while (pszSeparator = StrChr(pszDestFilePath, TEXT('/')))
  47. pszSeparator[0] = TEXT('\\');
  48. // Some people use "Test%20File.txt" when "%20" is really in the file name.
  49. // ASSERT(!StrChr(pszDestFilePath, TEXT('%'))); // Assert it doesn't contain '%' or it probably has escaped url stuff.
  50. return hr;
  51. }
  52. /*****************************************************************************\
  53. FUNCTION: UrlPathRemoveSlashW
  54. DESCRIPTION:
  55. \*****************************************************************************/
  56. HRESULT UrlPathRemoveSlashW(LPWSTR pszUrlPath)
  57. {
  58. LPWSTR pszEndOfPath = &pszUrlPath[lstrlenW(pszUrlPath) - 1];
  59. // Is it missing a backslash?
  60. if ((pszEndOfPath >= pszUrlPath) && (CH_URL_URL_SLASHW == pszEndOfPath[0]))
  61. pszEndOfPath[0] = 0; // Yes, so remove it.
  62. return S_OK;
  63. }
  64. /*****************************************************************************\
  65. FUNCTION: UrlPathRemoveSlashA
  66. DESCRIPTION:
  67. \*****************************************************************************/
  68. HRESULT UrlPathRemoveSlashA(LPSTR pszUrlPath)
  69. {
  70. LPSTR pszEndOfPath = &pszUrlPath[lstrlenA(pszUrlPath) - 1];
  71. // Is it missing a backslash?
  72. if ((pszEndOfPath >= pszUrlPath) && (CH_URL_URL_SLASHA == pszEndOfPath[0]))
  73. pszEndOfPath[0] = 0; // Yes, so remove it.
  74. return S_OK;
  75. }
  76. /*****************************************************************************\
  77. FUNCTION: UrlPathRemoveFrontSlashW
  78. DESCRIPTION:
  79. \*****************************************************************************/
  80. HRESULT UrlPathRemoveFrontSlashW(LPWSTR pszUrlPath)
  81. {
  82. if (pszUrlPath && (CH_URL_URL_SLASHW == pszUrlPath[0]))
  83. return CharReplaceWithStrW(pszUrlPath, lstrlen(pszUrlPath), 1, SZ_EMPTYW);
  84. else
  85. return S_OK;
  86. }
  87. /*****************************************************************************\
  88. FUNCTION: UrlPathRemoveFrontSlashA
  89. DESCRIPTION:
  90. \*****************************************************************************/
  91. HRESULT UrlPathRemoveFrontSlashA(LPSTR pszUrlPath)
  92. {
  93. if (pszUrlPath && (CH_URL_URL_SLASHA == pszUrlPath[0]))
  94. return CharReplaceWithStrA(pszUrlPath, lstrlenA(pszUrlPath), 1, SZ_EMPTYA);
  95. else
  96. return S_OK;
  97. }
  98. /*****************************************************************************\
  99. FUNCTION: UrlPathToFilePathW
  100. DESCRIPTION:
  101. \*****************************************************************************/
  102. HRESULT UrlPathToFilePathW(LPWSTR pszPath)
  103. {
  104. while (pszPath = StrChrW(pszPath, CH_URL_URL_SLASHW))
  105. pszPath[0] = CH_URL_SLASHW;
  106. return S_OK;
  107. }
  108. /*****************************************************************************\
  109. FUNCTION: UrlPathToFilePathA
  110. DESCRIPTION:
  111. \*****************************************************************************/
  112. HRESULT UrlPathToFilePathA(LPSTR pszPath)
  113. {
  114. while (pszPath = StrChrA(pszPath, CH_URL_URL_SLASHA))
  115. pszPath[0] = CH_URL_SLASHA;
  116. return S_OK;
  117. }
  118. /*****************************************************************************\
  119. FUNCTION: FilePathToUrlPathW
  120. DESCRIPTION:
  121. \*****************************************************************************/
  122. HRESULT FilePathToUrlPathW(LPWSTR pszPath)
  123. {
  124. while (pszPath = StrChrW(pszPath, CH_URL_SLASHW))
  125. pszPath[0] = CH_URL_URL_SLASHW;
  126. return S_OK;
  127. }
  128. /*****************************************************************************\
  129. FUNCTION: FilePathToUrlPathA
  130. DESCRIPTION:
  131. \*****************************************************************************/
  132. HRESULT FilePathToUrlPathA(LPSTR pszPath)
  133. {
  134. while (pszPath = StrChrA(pszPath, CH_URL_SLASHA))
  135. pszPath[0] = CH_URL_URL_SLASHA;
  136. return S_OK;
  137. }
  138. /*****************************************************************************\
  139. FUNCTION: UrlPathAdd
  140. DESCRIPTION:
  141. ...
  142. \*****************************************************************************/
  143. HRESULT UrlPathAdd(LPTSTR pszUrl, DWORD cchUrlSize, LPCTSTR pszSegment)
  144. {
  145. // If the segment starts with a slash, skip it.
  146. if (TEXT('/') == pszSegment[0])
  147. pszSegment = CharNext(pszSegment);
  148. StrCatBuff(pszUrl, pszSegment, cchUrlSize);
  149. return S_OK;
  150. }
  151. /*****************************************************************************\
  152. StrRetFromFtpPidl
  153. \*****************************************************************************/
  154. HRESULT StrRetFromFtpPidl(LPSTRRET pStrRet, DWORD shgno, LPCITEMIDLIST pidl)
  155. {
  156. HRESULT hr = S_OK;
  157. TCHAR szUrl[MAX_URL_STRING];
  158. szUrl[0] = 0;
  159. hr = UrlCreateFromPidl(pidl, shgno, szUrl, ARRAYSIZE(szUrl), ICU_ESCAPE | ICU_USERNAME, TRUE);
  160. if (SUCCEEDED(hr))
  161. {
  162. // Will it fit into STRRET.cStr?
  163. if (lstrlen(szUrl) < ARRAYSIZE(pStrRet->cStr))
  164. {
  165. // Yes, so there it goes...
  166. pStrRet->uType = STRRET_CSTR;
  167. SHTCharToAnsi(szUrl, pStrRet->cStr, ARRAYSIZE(pStrRet->cStr));
  168. }
  169. else
  170. {
  171. // No, so we will need to allocate it
  172. LPWSTR pwzAllocedStr = NULL;
  173. UINT cch = lstrlen(szUrl) + 1;
  174. pwzAllocedStr = (LPWSTR) SHAlloc(CbFromCchW(cch));
  175. pStrRet->uType = STRRET_WSTR;
  176. pStrRet->pOleStr = pwzAllocedStr;
  177. if (pwzAllocedStr)
  178. SHTCharToUnicode(szUrl, pwzAllocedStr, cch);
  179. else
  180. hr = E_OUTOFMEMORY;
  181. }
  182. }
  183. return hr;
  184. }
  185. /*****************************************************************************\
  186. FUNCTION: GetLastSegment
  187. DESCRIPTION:
  188. \*****************************************************************************/
  189. LPTSTR GetLastSegment(LPCTSTR pszUrl)
  190. {
  191. LPTSTR pszLastSeg = (LPTSTR) pszUrl;
  192. LPTSTR pszNextPossibleSeg;
  193. while (pszNextPossibleSeg = StrChr(pszLastSeg, TEXT('/')))
  194. {
  195. if (TEXT('\0') != CharNext(pszNextPossibleSeg))
  196. pszLastSeg = CharNext(pszNextPossibleSeg);
  197. else
  198. break; // We are done.
  199. }
  200. if (TEXT('/') == pszLastSeg[0])
  201. pszLastSeg = CharNext(pszLastSeg);
  202. return pszLastSeg;
  203. }
  204. /*****************************************************************************\
  205. FUNCTION: UrlRemoveDownloadType
  206. DESCRIPTION:
  207. \*****************************************************************************/
  208. HRESULT UrlRemoveDownloadType(LPTSTR pszUrlPath, BOOL * pfTypeSpecified, BOOL * pfType)
  209. {
  210. HRESULT hr = S_FALSE; // Specified? (Not yet)
  211. LPTSTR pszDownloadType;
  212. if (pfTypeSpecified)
  213. *pfTypeSpecified = TRUE;
  214. // Did the user specify a download type. szPath="Dir1/Dir2/file.txt;type=a".
  215. // TODO: Search Recursively because each segment in the path can have a
  216. // type.
  217. // Example Url="ftp://server/Dir1;type=a/Dir2;type=a/File.txt;type=b
  218. if (pszDownloadType = StrStrI(pszUrlPath, SZ_FTP_URL_TYPE))
  219. {
  220. TCHAR chType;
  221. if (pfTypeSpecified)
  222. *pfTypeSpecified = TRUE;
  223. pszDownloadType[0] = TEXT('\0'); // Terminate pszUrlPath and remove this junk.
  224. chType = pszDownloadType[ARRAYSIZE(SZ_FTP_URL_TYPE) - 1];
  225. if (pfType)
  226. {
  227. if ((TEXT('a') == chType) || (TEXT('A') == chType))
  228. *pfType = TRUE;
  229. else
  230. *pfType = TRUE;
  231. }
  232. hr = S_OK;
  233. }
  234. return hr;
  235. }
  236. /*****************************************************************************\
  237. FUNCTION: IsIPAddressStr
  238. DESCRIPTION:
  239. This function exists to detect an IP Address server name ("124.42.3.53") vs.
  240. a DNS domain name ("foobar", or "ftp.foobar.com"). I current accept more than
  241. 4 segments because of 6-bit IP address.
  242. TODO: To be thurough, I should probably made sure each segment is
  243. smaller than 256.
  244. \*****************************************************************************/
  245. BOOL IsIPAddressStr(LPTSTR pszServer)
  246. {
  247. BOOL fIsIPAddressStr = TRUE;
  248. LPTSTR pszCurrentChar = pszServer;
  249. int nDigits = 0;
  250. int nSegments = 1;
  251. while (fIsIPAddressStr && pszCurrentChar[0])
  252. {
  253. if (TEXT('.') == pszCurrentChar[0])
  254. {
  255. nSegments++;
  256. if ((0 == nDigits) || (4 < nDigits))
  257. fIsIPAddressStr = FALSE; // it started with a '.', ie, ".xxxxx"
  258. nDigits = 0;
  259. }
  260. nDigits++;
  261. if (nDigits > 4)
  262. fIsIPAddressStr = FALSE; // To many digits, ie "12345.xxxx"
  263. if (((TEXT('0') > pszCurrentChar[0]) || (TEXT('9') < pszCurrentChar[0])) &&
  264. (TEXT('.') != pszCurrentChar[0]))
  265. {
  266. fIsIPAddressStr = FALSE; // it's outside of the 0-9 range.
  267. }
  268. pszCurrentChar++; // Next character.
  269. }
  270. if (nSegments != 4)
  271. fIsIPAddressStr = FALSE; // Needs to have at least 4 segments ("1.2.3.4", "1.2.3.4.5")
  272. return fIsIPAddressStr;
  273. }
  274. /*****************************************************************************\
  275. FUNCTION: PidlGenerateSiteLookupStr
  276. DESCRIPTION:
  277. Sample Input: "ftp://user:[email protected]:69/Dir1/Dir2/File.txt"
  278. Sample Output: "ftp://user:[email protected]:69/"
  279. This is used to keep track of unique servers for CFtpSite. A CFtpSite needs
  280. to be created for each unique site, which includes different users that are
  281. logged onto the same site because of rooted directories.
  282. \*****************************************************************************/
  283. HRESULT PidlGenerateSiteLookupStr(LPCITEMIDLIST pidl, LPTSTR pszLookupStr, DWORD cchSize)
  284. {
  285. HRESULT hr = E_FAIL;
  286. // Some strange clients pass in non-Server IDs, like comdlg.
  287. if (FtpID_IsServerItemID(pidl))
  288. {
  289. LPITEMIDLIST pidlServer = FtpCloneServerID(pidl);
  290. if (pidlServer)
  291. {
  292. hr = UrlCreateFromPidlW(pidlServer, SHGDN_FORPARSING, pszLookupStr, cchSize, (ICU_ESCAPE | ICU_USERNAME), FALSE);
  293. ILFree(pidlServer);
  294. }
  295. else
  296. hr = E_OUTOFMEMORY;
  297. }
  298. return hr;
  299. }
  300. HRESULT PidlReplaceUserPassword(LPCITEMIDLIST pidlIn, LPITEMIDLIST * ppidlOut, IMalloc * pm, LPCTSTR pszUserName, LPCTSTR pszPassword)
  301. {
  302. TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
  303. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH];
  304. HRESULT hr = FtpPidl_GetServer(pidlIn, szServer, ARRAYSIZE(szServer));
  305. if (!pszUserName) // May be NULL.
  306. {
  307. pszUserName = szUserName;
  308. EVAL(SUCCEEDED(FtpPidl_GetUserName(pidlIn, szUserName, ARRAYSIZE(szUserName))));
  309. }
  310. *ppidlOut = NULL;
  311. if (SUCCEEDED(hr))
  312. {
  313. LPITEMIDLIST pidlServer;
  314. hr = FtpServerID_Create(szServer, pszUserName, pszPassword, FtpServerID_GetTypeID(pidlIn), FtpServerID_GetPortNum(pidlIn), &pidlServer, pm, TRUE);
  315. if (SUCCEEDED(hr))
  316. {
  317. LPITEMIDLIST pidlFtpPath = _ILNext(pidlIn);
  318. *ppidlOut = ILCombine(pidlServer, pidlFtpPath);
  319. ILFree(pidlServer);
  320. }
  321. }
  322. return hr;
  323. }
  324. HRESULT UrlReplaceUserPassword(LPTSTR pszUrlPath, DWORD cchSize, LPCTSTR pszUserName, LPCTSTR pszPassword)
  325. {
  326. HRESULT hr = E_FAIL;
  327. URL_COMPONENTS urlComps = {0};
  328. TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
  329. TCHAR szUrlPath[MAX_URL_STRING];
  330. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH];
  331. TCHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH];
  332. TCHAR szExtraInfo[MAX_PATH]; // Includes Port Number and download type (ASCII, Binary, Detect)
  333. BOOL fResult;
  334. urlComps.dwStructSize = sizeof(urlComps);
  335. urlComps.lpszHostName = szServer;
  336. urlComps.dwHostNameLength = ARRAYSIZE(szServer);
  337. urlComps.lpszUrlPath = szUrlPath;
  338. urlComps.dwUrlPathLength = ARRAYSIZE(szUrlPath);
  339. urlComps.lpszExtraInfo = szExtraInfo;
  340. urlComps.dwExtraInfoLength = ARRAYSIZE(szExtraInfo);
  341. urlComps.lpszUserName = szUserName;
  342. urlComps.dwUserNameLength = ARRAYSIZE(szUserName);
  343. urlComps.lpszPassword = szPassword;
  344. urlComps.dwPasswordLength = ARRAYSIZE(szPassword);
  345. fResult = InternetCrackUrl(pszUrlPath, 0, ICU_DECODE, &urlComps);
  346. if (fResult)
  347. {
  348. urlComps.dwStructSize = sizeof(urlComps);
  349. urlComps.lpszHostName = szServer;
  350. urlComps.lpszUserName = (LPTSTR)(pszUserName ? pszUserName : szUserName);
  351. urlComps.dwUserNameLength = (pszUserName ? lstrlen(pszUserName) : lstrlen(szUserName));
  352. urlComps.lpszPassword = (LPTSTR)pszPassword; // It may be valid for caller to pass NULL
  353. urlComps.dwPasswordLength = (pszPassword ? lstrlen(pszPassword) : 0);
  354. urlComps.lpszExtraInfo = szExtraInfo;
  355. urlComps.dwExtraInfoLength = ARRAYSIZE(szExtraInfo);
  356. fResult = InternetCreateUrl(&urlComps, (ICU_ESCAPE_AUTHORITY | ICU_ESCAPE | ICU_USERNAME), pszUrlPath, &cchSize);
  357. if (fResult)
  358. {
  359. hr = S_OK;
  360. }
  361. }
  362. return hr;
  363. }
  364. // InternetCreateUrlW() will write into pszUrl but won't terminate the string.
  365. // This is hard to detect because half the time the place where the terminator should
  366. // go may coincidentally contain a terminator. This code forces the bug to happen.
  367. #define TEST_FOR_INTERNETCREATEURL_BUG 1
  368. #define INTERNETCREATEURL_BUG_WORKAROUND 1
  369. HRESULT UrlCreateEx(LPCTSTR pszServer, LPCTSTR pszUser, LPCTSTR pszPassword, LPCTSTR pszUrlPath, LPCTSTR pszFragment, INTERNET_PORT ipPortNum, LPCTSTR pszDownloadType, LPTSTR pszUrl, DWORD cchSize, DWORD dwFlags)
  370. {
  371. HRESULT hr = E_FAIL;
  372. DWORD cchSizeCopy = cchSize;
  373. #if DEBUG && TEST_FOR_INTERNETCREATEURL_BUG
  374. LPTSTR pszDebugStr = pszUrl;
  375. for (DWORD dwIndex = (cchSize - 2); dwIndex; dwIndex--)
  376. {
  377. #ifndef INTERNETCREATEURL_BUG_WORKAROUND
  378. pszDebugStr[0] = -1; // This will force a buffer w/o terminators.
  379. #else // INTERNETCREATEURL_BUG_WORKAROUND
  380. pszDebugStr[0] = 0; // This will work around the bug.
  381. #endif // INTERNETCREATEURL_BUG_WORKAROUND
  382. pszDebugStr++;
  383. }
  384. #endif // DEBUG && TEST_FOR_INTERNETCREATEURL_BUG
  385. URL_COMPONENTS urlComp = {sizeof(URL_COMPONENTS), NULL, 0, INTERNET_SCHEME_FTP, (LPTSTR) pszServer, 0,
  386. ipPortNum, (LPTSTR) NULL_FOR_EMPTYSTR(pszUser), 0, (LPTSTR) NULL_FOR_EMPTYSTR(pszPassword), 0,
  387. (LPTSTR) pszUrlPath, 0, (LPTSTR) NULL, 0};
  388. if (EVAL(InternetCreateUrl(&urlComp, dwFlags | ICU_ESCAPE_AUTHORITY | ICU_USERNAME, pszUrl, &cchSizeCopy)))
  389. {
  390. hr = S_OK;
  391. if (pszFragment)
  392. StrCatBuff(pszUrl, pszFragment, cchSize);
  393. }
  394. #if DEBUG && TEST_FOR_INTERNETCREATEURL_BUG
  395. #ifdef INTERNETCREATEURL_BUG_WORKAROUND
  396. // Make sure we hit a terminator and not a -1, which should never happen in URL strings.
  397. for (pszDebugStr = pszUrl; pszDebugStr[0]; pszDebugStr++)
  398. ASSERT(-1 != pszDebugStr[0]);
  399. #endif // INTERNETCREATEURL_BUG_WORKAROUND
  400. #endif // DEBUG && TEST_FOR_INTERNETCREATEURL_BUG
  401. return hr;
  402. }
  403. HRESULT UrlCreate(LPCTSTR pszServer, LPCTSTR pszUser, LPCTSTR pszPassword, LPCTSTR pszUrlPath, LPCTSTR pszFragment, INTERNET_PORT ipPortNum, LPCTSTR pszDownloadType, LPTSTR pszUrl, DWORD cchSize)
  404. {
  405. return UrlCreateEx(pszServer, pszUser, pszPassword, pszUrlPath, pszFragment, ipPortNum, pszDownloadType, pszUrl, cchSize, ICU_ESCAPE);
  406. }
  407. BOOL IsEmptyUrlPath(LPCTSTR pszUrlPath)
  408. {
  409. BOOL fResult = FALSE;
  410. if (!pszUrlPath ||
  411. !pszUrlPath[0] ||
  412. (((TEXT('/') == pszUrlPath[0]) && (!pszUrlPath[1]))))
  413. {
  414. fResult = TRUE;
  415. }
  416. return fResult;
  417. }
  418. ///////////////////////////////////////////////////////////////////////
  419. // Wire Path Functions (UTF-8 or DBCS/MBCS)
  420. ///////////////////////////////////////////////////////////////////////
  421. /*****************************************************************************\
  422. FUNCTION: WirePathAdd
  423. DESCRIPTION:
  424. ...
  425. \*****************************************************************************/
  426. HRESULT WirePathAdd(LPWIRESTR pwWirePath, DWORD cchUrlSize, LPCWIRESTR pwWireSegment)
  427. {
  428. // If the segment starts with a slash, skip it.
  429. if ('/' == pwWireSegment[0])
  430. pwWireSegment = CharNextA(pwWireSegment);
  431. StrCatBuffA(pwWirePath, pwWireSegment, cchUrlSize);
  432. return S_OK;
  433. }
  434. /*****************************************************************************\
  435. FUNCTION: WirePathAppendSlash
  436. DESCRIPTION:
  437. \*****************************************************************************/
  438. HRESULT WirePathAppendSlash(LPWIRESTR pwWirePath, DWORD cchWirePathSize)
  439. {
  440. HRESULT hr = E_FAIL;
  441. DWORD cchSize = lstrlenA(pwWirePath);
  442. // Is there enough room?
  443. if (cchSize < (cchWirePathSize - 1))
  444. {
  445. LPWIRESTR pwEndOfPath = &pwWirePath[cchSize - 1];
  446. // Is it missing a backslash?
  447. if ((pwEndOfPath >= pwWirePath) && '/' != pwEndOfPath[0])
  448. StrCatBuffA(pwEndOfPath, SZ_URL_SLASHA, (int)(cchWirePathSize - (pwEndOfPath - pwWirePath))); // Yes, so add it.
  449. hr = S_OK;
  450. }
  451. return hr;
  452. }
  453. /*****************************************************************************\
  454. FUNCTION: WirePathAppend
  455. DESCRIPTION:
  456. ...
  457. \*****************************************************************************/
  458. HRESULT WirePathAppend(LPWIRESTR pwWirePath, DWORD cchUrlSize, LPCWIRESTR pwWireSegment)
  459. {
  460. if (!EVAL(pwWireSegment))
  461. return E_INVALIDARG;
  462. WirePathAppendSlash(pwWirePath, cchUrlSize); // Make sure the base url ends in a '/'. Note it may be "ftp://".
  463. return WirePathAdd(pwWirePath, cchUrlSize, pwWireSegment);
  464. }
  465. /*****************************************************************************\
  466. FUNCTION: UrlGetFirstPathSegment
  467. PARAMETERS:
  468. [IN] pszFullPath - "Dir1\Dir2\Dir3"
  469. [OUT] szFirstItem - "Dir1" [OPTIONAL]
  470. [OUT] szRemaining - "Dir2\Dir3" [OPTIONAL]
  471. \*****************************************************************************/
  472. HRESULT WirePathGetFirstSegment(LPCWIRESTR pwFtpWirePath, LPWIRESTR wFirstItem, DWORD cchFirstItemSize, BOOL * pfWasFragSeparator, LPWIRESTR wRemaining, DWORD cchRemainingSize, BOOL * pfIsDir)
  473. {
  474. HRESULT hr = S_OK;
  475. LPCWIRESTR pwSegEnding = StrChrA(pwFtpWirePath, CH_URL_URL_SLASH);
  476. if (pfIsDir)
  477. *pfIsDir = FALSE;
  478. ASSERT((CH_URL_URL_SLASHA != pwFtpWirePath[0])); // You will probably not get what you want.
  479. if (pwSegEnding)
  480. {
  481. if (wFirstItem)
  482. {
  483. DWORD cchSize = (DWORD) (pwSegEnding - pwFtpWirePath + 1);
  484. StrCpyNA(wFirstItem, pwFtpWirePath, (cchSize <= cchFirstItemSize) ? cchSize : cchFirstItemSize);
  485. }
  486. if (pfIsDir && (CH_URL_URL_SLASHA == pwSegEnding[0]))
  487. *pfIsDir = TRUE; // Tell them that it is a directory.
  488. if (wRemaining)
  489. StrCpyNA(wRemaining, CharNextA(pwSegEnding), cchRemainingSize);
  490. if (0 == pwSegEnding[1])
  491. hr = S_FALSE; // End of the line.
  492. }
  493. else
  494. {
  495. if (wFirstItem)
  496. StrCpyNA(wFirstItem, pwFtpWirePath, cchFirstItemSize); // pszFullPath contains only one segment
  497. if (wRemaining)
  498. wRemaining[0] = 0;
  499. hr = S_FALSE; // Indicate that there aren't any more directories left.
  500. }
  501. return hr;
  502. }
  503. ///////////////////////////////////////////////////////////////////////
  504. // Display Path Functions (Unicode)
  505. ///////////////////////////////////////////////////////////////////////
  506. /*****************************************************************************\
  507. FUNCTION: DisplayPathAdd
  508. DESCRIPTION:
  509. ...
  510. \*****************************************************************************/
  511. HRESULT DisplayPathAdd(LPWSTR pwzUrl, DWORD cchUrlSize, LPCWSTR pwzSegment)
  512. {
  513. // If the segment starts with a slash, skip it.
  514. if (L'/' == pwzSegment[0])
  515. pwzSegment = CharNext(pwzSegment);
  516. StrCatBuffW(pwzUrl, pwzSegment, cchUrlSize);
  517. return S_OK;
  518. }
  519. /*****************************************************************************\
  520. FUNCTION: DisplayPathAppendSlash
  521. DESCRIPTION:
  522. \*****************************************************************************/
  523. HRESULT DisplayPathAppendSlash(LPWSTR pwzDisplayPath, DWORD cchSize)
  524. {
  525. DWORD cchCurrentSize = lstrlenW(pwzDisplayPath);
  526. HRESULT hr = CO_E_PATHTOOLONG;
  527. if (cchCurrentSize < (cchSize - 2))
  528. {
  529. LPWSTR pwzEndOfPath = &pwzDisplayPath[cchCurrentSize - 1];
  530. // Is it missing a backslash?
  531. if ((pwzEndOfPath >= pwzDisplayPath) && TEXT('/') != pwzEndOfPath[0])
  532. StrCatBuff(pwzEndOfPath, SZ_URL_SLASH, (cchSize - cchCurrentSize + 1)); // Yes, so add it.
  533. hr = S_OK;
  534. }
  535. return hr;
  536. }
  537. /*****************************************************************************\
  538. FUNCTION: DisplayPathAppend
  539. DESCRIPTION:
  540. ...
  541. \*****************************************************************************/
  542. HRESULT DisplayPathAppend(LPWSTR pwzDisplayPath, DWORD cchUrlSize, LPCWSTR pwzDisplaySegment)
  543. {
  544. if (!EVAL(pwzDisplaySegment))
  545. return E_INVALIDARG;
  546. DisplayPathAppendSlash(pwzDisplayPath, cchUrlSize); // Make sure the base url ends in a '/'. Note it may be "ftp://".
  547. return DisplayPathAdd(pwzDisplayPath, cchUrlSize, pwzDisplaySegment);
  548. }
  549. /*****************************************************************************\
  550. FUNCTION: DisplayPathGetFirstSegment
  551. PARAMETERS:
  552. [IN] pszFullPath - "Dir1\Dir2\Dir3"
  553. [OUT] szFirstItem - "Dir1" [OPTIONAL]
  554. [OUT] szRemaining - "Dir2\Dir3" [OPTIONAL]
  555. \*****************************************************************************/
  556. HRESULT DisplayPathGetFirstSegment(LPCWSTR pwzFullPath, LPWSTR pwzFirstItem, DWORD cchFirstItemSize, BOOL * pfWasFragSeparator, LPWSTR pwzRemaining, DWORD cchRemainingSize, BOOL * pfIsDir)
  557. {
  558. HRESULT hr = S_OK;
  559. LPWSTR pwzSegEnding = StrChrW(pwzFullPath, CH_URL_URL_SLASH);
  560. if (pfIsDir)
  561. *pfIsDir = FALSE;
  562. // This will happen if the user enters an incorrect URL, like "ftp://wired//"
  563. // ASSERT((CH_URL_URL_SLASHW != pwzFullPath[0])); // You will probably not get what you want.
  564. if (pwzSegEnding)
  565. {
  566. if (pwzFirstItem)
  567. {
  568. DWORD cchSize = (DWORD) (pwzSegEnding - pwzFullPath + 1);
  569. StrCpyNW(pwzFirstItem, pwzFullPath, (cchSize <= cchFirstItemSize) ? cchSize : cchFirstItemSize);
  570. }
  571. if (pfIsDir && (CH_URL_URL_SLASHW == pwzSegEnding[0]))
  572. *pfIsDir = TRUE; // Tell them that it is a directory.
  573. if (pwzRemaining)
  574. StrCpyNW(pwzRemaining, CharNextW(pwzSegEnding), cchRemainingSize);
  575. if (0 == pwzSegEnding[1])
  576. hr = S_FALSE; // End of the line.
  577. }
  578. else
  579. {
  580. if (pwzFirstItem)
  581. StrCpyNW(pwzFirstItem, pwzFullPath, cchFirstItemSize); // pszFullPath contains only one segment
  582. if (pwzRemaining)
  583. pwzRemaining[0] = 0;
  584. hr = S_FALSE; // Indicate that there aren't any more directories left.
  585. }
  586. return hr;
  587. }