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.

1199 lines
38 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. dav.cxx
  5. Abstract:
  6. This file contains the implementations of
  7. HttpCheckDavCompliance
  8. HttpCheckDavCollection
  9. HttpCheckCachedDavStatus
  10. The following functions are exported by this module:
  11. HttpCheckDavComplianceA
  12. HttpCheckDavCollectionA
  13. HttpCheckCachedDavStatusA
  14. HttpCheckDavComplianceW
  15. HttpCheckDavCollectionW
  16. HttpCheckCachedDavStatusW
  17. Author:
  18. Mead Himelstein (Meadh) 01-Jun-1998
  19. Revision History:
  20. --*/
  21. #include <wininetp.h>
  22. #include "httpp.h"
  23. #undef DAVCACHING //IE5 bug 15696, removing unused code but may add back in later
  24. INTERNETAPI_(BOOL) HttpCheckDavComplianceA(
  25. IN LPCSTR lpszUrl,
  26. IN LPCSTR lpszComplianceToken,
  27. IN OUT LPBOOL lpfFound,
  28. IN HWND hWnd,
  29. IN LPVOID lpvReserved
  30. )
  31. /*++
  32. Routine Description:
  33. Determines if the resource identified by lpszUrl is DAV compliant (level determined by lpszComplianceToken).
  34. Returns TRUE in lpfFound if detected. Furthermore, if the token is "1" we also cache todays date
  35. and whether or not we found the server to be DAV level 1 compliant in the visited links cache.
  36. Arguments:
  37. lpszUrl - URL to the resource to check for DAV compliance, i.e. "http://webdav/davfood/"
  38. lpszComplianceToken - DAV compliance class identifier (i.e. "1") BUGBUG MUST NOT CONTAIN INTERNAL WHITE SPACE "foo bar" = bad, " foo " = ok
  39. lpfFound - address of a BOOL to receive TRUE if DAV compliance is found or FALSE otherwise.
  40. hWnd - Handle to window for displaying authentication dialog if needed. May be NULL indicating
  41. no UI is to be displayed and authentication failures should be quietly handled.
  42. lpvReserved - Reserved, must be NULL
  43. Return Value:
  44. TRUE - Success, check lpfFound for results.
  45. FALSE - failure, GetLastError returns the error code
  46. --*/
  47. {
  48. BOOL fRet = TRUE;
  49. DEBUG_ENTER((DBG_API,
  50. Bool,
  51. "HttpCheckDavComplianceA",
  52. "%sq, %sq, %#x, %#x, %#x",
  53. lpszUrl,
  54. lpszComplianceToken,
  55. lpfFound,
  56. hWnd,
  57. lpvReserved
  58. ));
  59. DWORD dwStatusCode, dwHeaderLength, dwIndex = 0;
  60. DWORD dwError = ERROR_SUCCESS, dwStatusLength = sizeof(DWORD);
  61. HINTERNET hSession = NULL, hConnection = NULL, hHTTPReq = NULL;
  62. LPSTR lpszDavHeader = NULL, lpszRead = NULL, lpszWrite = NULL, lpszVisitedUrl=NULL;
  63. LPCACHE_ENTRY_INFO lpCEI = NULL;
  64. FILETIME ftNow;
  65. WORD wDate, wTime;
  66. URL_COMPONENTS ucUrl;
  67. #define CEI_BUFFER_SIZE 512
  68. #define IsWhite(c) ((DWORD) (c) > 32 ? FALSE : TRUE)
  69. // Debug param checking
  70. INET_ASSERT(lpszUrl && lpszComplianceToken && lpfFound && !lpvReserved);
  71. DEBUG_ENTER_API((DBG_API,
  72. Bool,
  73. "HttpCheckDavComplianceA",
  74. "%s %s %x %x %x",
  75. (lpszUrl)?lpszUrl :"NULL Url",
  76. (lpszComplianceToken)?lpszComplianceToken :"NULL ComplianceToken",
  77. hWnd,
  78. lpfFound, lpvReserved
  79. ));
  80. // non-debug param checking
  81. if (!lpszUrl || !lpszComplianceToken || !lpfFound || lpvReserved)
  82. {
  83. dwError = ERROR_INVALID_PARAMETER;
  84. fRet = FALSE;
  85. goto cleanup;
  86. }
  87. *lpfFound = FALSE;
  88. // Build the URL_COMPONENTS struct
  89. memset(&ucUrl, 0, sizeof(ucUrl));
  90. ucUrl.dwStructSize = sizeof(ucUrl);
  91. // non-zero length enables retrieval during CrackUrl
  92. ucUrl.dwSchemeLength = 1;
  93. ucUrl.dwHostNameLength = 1;
  94. ucUrl.dwUrlPathLength = 1;
  95. if (!InternetCrackUrl(lpszUrl, 0, 0, &ucUrl))
  96. {
  97. dwError = GetLastError();
  98. fRet = FALSE;
  99. goto cleanup;
  100. }
  101. DEBUG_PRINT_API(API,
  102. INFO,
  103. ("URL Cracked. Host = %q Path = %q\n ",
  104. ucUrl.lpszHostName, ucUrl.lpszUrlPath
  105. ));
  106. // Ensures the global vszCurrentUser is set
  107. GetWininetUserName();
  108. INET_ASSERT(vszCurrentUser);
  109. // Perform an OPTIONS call on the server and check for a DAV header
  110. hSession = InternetOpen(NULL,
  111. INTERNET_OPEN_TYPE_PRECONFIG,
  112. NULL,
  113. NULL,
  114. NULL);
  115. if(!hSession)
  116. {
  117. dwError = GetLastError();
  118. fRet = FALSE;
  119. goto cleanup;
  120. }
  121. hConnection = InternetConnect(hSession,
  122. ucUrl.lpszHostName,
  123. INTERNET_DEFAULT_HTTP_PORT,
  124. (LPCSTR) vszCurrentUser,
  125. NULL,
  126. INTERNET_SERVICE_HTTP,
  127. NULL,
  128. NULL);
  129. if(!hConnection)
  130. {
  131. dwError = GetLastError();
  132. fRet = FALSE;
  133. goto cleanup;
  134. }
  135. hHTTPReq = HttpOpenRequest(hConnection,
  136. "OPTIONS",
  137. ucUrl.lpszUrlPath,
  138. "HTTP/1.0",
  139. NULL,
  140. NULL,
  141. INTERNET_FLAG_PRAGMA_NOCACHE,NULL);
  142. if(!hHTTPReq)
  143. {
  144. dwError = GetLastError();
  145. fRet = FALSE;
  146. goto cleanup;
  147. }
  148. if (!HttpSendRequest(hHTTPReq,NULL, 0L, NULL, NULL))
  149. {
  150. dwError = GetLastError();
  151. fRet = FALSE;
  152. goto cleanup;
  153. }
  154. // Authentication handling
  155. if (HttpQueryInfo(hHTTPReq,
  156. HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER,
  157. &dwStatusCode,
  158. &dwStatusLength,
  159. &dwIndex))
  160. {
  161. // fRet = TRUE at this point
  162. // If request was denied or proxy auth required and callee has said
  163. // we can display UI, we put up the InternetErrorDlg and ask for
  164. // credentials. Otherwise, if dwStatus != success we cache that
  165. // this resource is not DAV compliant
  166. if ((hWnd) && ((dwStatusCode == HTTP_STATUS_DENIED) || (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)))
  167. {
  168. DWORD dwRetval;
  169. DWORD dwAuthTries = 0;
  170. BOOL fDone;
  171. fDone = FALSE;
  172. while ((!fDone) && (dwAuthTries < 3))
  173. {
  174. dwRetval = InternetErrorDlg(hWnd,
  175. hHTTPReq,
  176. ERROR_INTERNET_INCORRECT_PASSWORD,
  177. 0L,
  178. NULL);
  179. if (dwRetval == ERROR_INTERNET_FORCE_RETRY) // User pressed ok on credentials dialog
  180. { // Resend request, new credentials are cached and will be replayed by HSR()
  181. if (!HttpSendRequest(hHTTPReq,NULL, 0L, NULL, NULL))
  182. {
  183. dwError = GetLastError();
  184. fRet = FALSE;
  185. goto cleanup;
  186. }
  187. dwStatusCode = 0;
  188. if (!HttpQueryInfo(hHTTPReq,
  189. HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER,
  190. &dwStatusCode,
  191. &dwStatusLength,
  192. &dwIndex))
  193. {
  194. dwError = GetLastError();
  195. fRet = FALSE;
  196. goto cleanup;
  197. }
  198. if ((dwStatusCode != HTTP_STATUS_DENIED) && (dwStatusCode != HTTP_STATUS_PROXY_AUTH_REQ))
  199. {
  200. fDone = TRUE;
  201. }
  202. }
  203. else // User pressed cancel from dialog (note ERROR_SUCCESS == ERROR_CANCELLED from IED())
  204. {
  205. fDone = TRUE;
  206. }
  207. dwAuthTries++;
  208. }
  209. }
  210. if ((dwStatusCode == HTTP_STATUS_DENIED) || (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ))
  211. { // Don't want to cache this (user might have forgotten password or we may not
  212. // be able to show credential UI)
  213. goto cleanup;
  214. }
  215. else if (dwStatusCode != HTTP_STATUS_OK)
  216. { // Either initial request failed for non-auth issue, cache as not compliant
  217. goto update_cache; //note update_cache NOT cleanup, want to cache this as non compliant
  218. }
  219. }
  220. else
  221. {
  222. dwError = GetLastError();
  223. fRet = FALSE;
  224. goto cleanup;
  225. }
  226. // If we received a DAV header, snag it and check for compliance level
  227. dwHeaderLength = INTERNET_MAX_URL_LENGTH;
  228. lpszDavHeader = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, dwHeaderLength);
  229. if (!lpszDavHeader)
  230. {
  231. dwError = ERROR_NOT_ENOUGH_MEMORY;
  232. goto cleanup;
  233. }
  234. lstrcpy(lpszDavHeader,"DAV");
  235. if (!HttpQueryInfo(hHTTPReq,
  236. HTTP_QUERY_CUSTOM,
  237. lpszDavHeader,
  238. &dwHeaderLength,
  239. &dwIndex))
  240. {
  241. dwError = GetLastError();
  242. fRet = FALSE;
  243. goto update_cache; //note update_cache NOT cleanup, want to cache this as non compliant
  244. }
  245. INET_ASSERT(fRet); // sb TRUE if it made it this far
  246. // Walk the DAV header looking for the token by itself, indicating DAV compliance
  247. // Note: DAV header is comma delimited, but otherwise we can make no assumptions on formatting.
  248. // BUGBUG Currently will not work for tokens with internal white space i.e. "foo bar" = bad, " foobar " = good
  249. lpszRead = lpszDavHeader;
  250. lpszWrite = lpszDavHeader;
  251. INET_ASSERT(!*lpfFound);
  252. INET_ASSERT(fRet);
  253. while(*lpszRead && !*lpfFound)
  254. {
  255. if (IsWhite(*lpszRead))
  256. {
  257. while ((*(++lpszRead))&&(IsWhite(*lpszRead)));
  258. }
  259. if (*lpszRead)
  260. {
  261. if(*lpszRead == ',')
  262. {
  263. *(lpszRead++) = '\0';
  264. if (*lpfFound = (lstrcmp(lpszDavHeader, lpszComplianceToken) == 0) ? TRUE:FALSE)
  265. {
  266. goto update_cache;
  267. }
  268. lpszWrite = lpszRead;
  269. lpszDavHeader = lpszRead;
  270. }
  271. else *(lpszWrite++) = *(lpszRead++);
  272. }
  273. }
  274. *lpszWrite = *lpszRead; // copy the terminator
  275. INET_ASSERT(!*lpfFound);
  276. *lpfFound = (lstrcmp(lpszDavHeader, lpszComplianceToken) == 0) ? TRUE:FALSE; //Final compare on last fragment
  277. update_cache :
  278. #ifdef DAVCACHING // Commenting out as per bug 15696
  279. if (lstrcmp("1", lpszComplianceToken) == 0)
  280. // Cache if this is a known DAV level 1 server or atleast the last time we tried
  281. {
  282. // Update visited links cache
  283. // We store the cached info as:
  284. // CacheEntryInfo.dwExemptDelta(HiWord) = Date DAV discovery was last run
  285. // CacheEntryInfo.dwExemptDelta(LoWord) & DAV_LEVEL1_STATUS = Is DAV server (*lpfFound)
  286. // CacheEntryInfo.dwExemptDelta(LoWord) & DAV_COLLECTION_STATUS = Is DAV collection (set in HttpCheckDavCollection)
  287. // Assemble the URL to the visited links container "VISITED: username@URL"
  288. DWORD cbNeededBuf = lstrlen("Visited: ") + lstrlen(vszCurrentUser) + lstrlen(lpszUrl) + 32;
  289. lpszVisitedUrl = (LPSTR) ALLOCATE_MEMORY(LMEM_FIXED, cbNeededBuf);
  290. if (!lpszVisitedUrl)
  291. {
  292. dwError = ERROR_NOT_ENOUGH_MEMORY;
  293. goto cleanup;
  294. }
  295. lstrcpy(lpszVisitedUrl, "Visited: "); // For compatibility with urlhist.c visited URLs
  296. lstrcat(lpszVisitedUrl, vszCurrentUser);
  297. lstrcat(lpszVisitedUrl, "@");
  298. lstrcat(lpszVisitedUrl, lpszUrl);
  299. lpCEI = (LPCACHE_ENTRY_INFO) ALLOCATE_MEMORY(LMEM_FIXED, CEI_BUFFER_SIZE);
  300. if (!lpCEI)
  301. {
  302. dwError = ERROR_NOT_ENOUGH_MEMORY;
  303. goto cleanup;
  304. }
  305. GetSystemTimeAsFileTime(&ftNow);
  306. if (!FileTimeToDosDateTime(&ftNow,
  307. &wDate,
  308. &wTime
  309. ))
  310. {
  311. INET_ASSERT(FALSE); // Should "never" hit this
  312. goto cleanup;
  313. }
  314. // Stuff our 16-bit Date into the upper 16-bits of dwExemptDelta
  315. lpCEI->dwExemptDelta = (DWORD)wDate <<16;
  316. // Set the DAV_LEVEL1_STATUS_BIT if the resource was found compliant above
  317. if (*lpfFound)
  318. {
  319. lpCEI->dwExemptDelta |= (DWORD) DAV_LEVEL1_STATUS;
  320. }
  321. if (!SetUrlCacheEntryInfo (lpszVisitedUrl,
  322. lpCEI,
  323. CACHE_ENTRY_EXEMPT_DELTA_FC
  324. ))
  325. {
  326. if (GetLastError() == ERROR_FILE_NOT_FOUND)
  327. // No existing cache entry, so create one and try again
  328. {
  329. FILETIME ftNone;
  330. if (CommitUrlCacheEntry (lpszVisitedUrl,
  331. NULL,
  332. ftNone,
  333. ftNone,
  334. 0,
  335. NULL,
  336. 0,
  337. NULL,
  338. 0
  339. ))
  340. {
  341. // Entry is now created, so try one more time
  342. SetUrlCacheEntryInfo (lpszVisitedUrl,
  343. lpCEI,
  344. CACHE_ENTRY_EXEMPT_DELTA_FC
  345. );
  346. }
  347. }
  348. }
  349. }
  350. #endif //DAVCACHING
  351. cleanup:
  352. // Free memory
  353. if (lpCEI) FREE_MEMORY (lpCEI);
  354. if (lpszVisitedUrl) FREE_MEMORY (lpszVisitedUrl);
  355. if (lpszDavHeader != NULL) FREE_MEMORY(lpszDavHeader);
  356. // Close the handles
  357. if (hHTTPReq) InternetCloseHandle(hHTTPReq);
  358. if (hConnection) InternetCloseHandle(hConnection);
  359. SetLastError(dwError);
  360. DEBUG_LEAVE_API(fRet);
  361. return fRet;
  362. }
  363. INTERNETAPI_(BOOL) HttpCheckDavCollectionA(
  364. IN LPCSTR lpszUrl,
  365. IN OUT LPBOOL lpfFound,
  366. IN HWND hWnd,
  367. IN LPVOID lpvReserved
  368. )
  369. /*++
  370. Routine Description:
  371. Checks the resource at lpszUrl to see if it is a DAV collection. Typically, callee has
  372. already called HttpCheckDavCompliance or HttpCheckCachedDavStatus to determine
  373. the resource supports DAV level 1, although it is not necessary to do so. Note this method
  374. will blindly attempt either way.
  375. Arguments:
  376. lpszUrl - URL of the resource to check
  377. lpfFound - address of a BOOL to receive TRUE if DAV compliance is found or FALSE otherwise.
  378. hWnd - Handle to window for displaying authentication dialog if needed. May be NULL indicating
  379. no UI is to be displayed and authentication failures should be quietly handled.
  380. lpvReserved - Reserved, must be NULL
  381. Return Value:
  382. TRUE - Success, check lpfFound for results.
  383. FALSE - failure, GetLastError returns the error code
  384. --*/
  385. {
  386. #define CEI_BUFFER_SIZE 512
  387. #define READ_BUFFER_SIZE 4096
  388. #define READ_BUFFER_AVAILABLE (READ_BUFFER_SIZE - sizeof(LPSTR))
  389. BOOL fRet = TRUE, fDone=FALSE;
  390. DEBUG_ENTER((DBG_API,
  391. Bool,
  392. "HttpCheckDavCollectionA",
  393. "%sq, %#x, %#x, %#x",
  394. lpszUrl,
  395. lpfFound,
  396. hWnd,
  397. lpvReserved
  398. ));
  399. DWORD dwRead=0, dwBuffers =1;
  400. DWORD dwStatusCode, dwIndex = 0;
  401. DWORD dwError = ERROR_SUCCESS, dwStatusLength = sizeof(DWORD);
  402. HINTERNET hSession = NULL, hConnection = NULL, hHTTPReq = NULL;
  403. LPSTR lpszVisitedUrl=NULL, lpszXmlResponse=NULL, lpszBufferHead=NULL;
  404. LPCACHE_ENTRY_INFO lpCEI = NULL;
  405. FILETIME ftNow;
  406. WORD wDate, wTime;
  407. URL_COMPONENTS ucUrl;
  408. char lpszHeaders[] = "Depth:0\r\n";
  409. // Debug param checking
  410. INET_ASSERT(lpszUrl && lpfFound && !lpvReserved);
  411. DEBUG_ENTER_API((DBG_API,
  412. Bool,
  413. "HttpCheckDavCollectionA",
  414. "%s %x %x",
  415. (lpszUrl)?lpszUrl :"NULL Url",
  416. lpfFound, lpvReserved
  417. ));
  418. // non-debug param checking
  419. if (!lpszUrl || !lpfFound || lpvReserved)
  420. {
  421. dwError = ERROR_INVALID_PARAMETER;
  422. fRet = FALSE;
  423. goto cleanup;
  424. }
  425. *lpfFound = FALSE;
  426. // Build the URL_COMPONENTS struct
  427. memset(&ucUrl, 0, sizeof(ucUrl));
  428. ucUrl.dwStructSize = sizeof(ucUrl);
  429. // non-zero length enables retrieval during CrackUrl
  430. ucUrl.dwSchemeLength = 1;
  431. ucUrl.dwHostNameLength = 1;
  432. ucUrl.dwUrlPathLength = 1;
  433. if (!InternetCrackUrl(lpszUrl, 0, 0, &ucUrl))
  434. {
  435. dwError = GetLastError();
  436. fRet = FALSE;
  437. goto cleanup;
  438. }
  439. INET_ASSERT(ucUrl.lpszHostName && ucUrl.lpszUrlPath);
  440. DEBUG_PRINT_API(API,
  441. INFO,
  442. ("URL Cracked. Host = %q Path = %q\n ",
  443. ucUrl.lpszHostName, ucUrl.lpszUrlPath
  444. ));
  445. // Ensures the global vszCurrentUser is set
  446. GetWininetUserName();
  447. INET_ASSERT(vszCurrentUser);
  448. // Build the XML request body for a PROPFIND
  449. //<?xml version='1.0' ?>
  450. //<?xml:namespace ns='DAV:' prefix='D' ?>
  451. //<D:propfind>
  452. // <D:prop>
  453. // <D:resourcetype/>
  454. // </D:prop>
  455. //</D:propfind>
  456. #define REQUEST_RESOURCE_TYPE "\
  457. <?xml version='1.0' ?>\r\n \
  458. <?xml:namespace ns='DAV:' prefix='D' ?>\r\n \
  459. <D:propfind>\r\n \
  460. <D:prop>\r\n \
  461. <D:resourcetype/>\r\n \
  462. </D:prop>\r\n \
  463. </D:propfind>\r\n"
  464. DWORD cbReqSize;
  465. cbReqSize = lstrlen(REQUEST_RESOURCE_TYPE);
  466. LPCSTR lpszXmlRequest;
  467. lpszXmlRequest = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, cbReqSize);
  468. // Perform a PROPFIND call, looking for resourcetype = DAV:collection
  469. hSession = InternetOpen(NULL,
  470. INTERNET_OPEN_TYPE_PRECONFIG,
  471. NULL,
  472. NULL,
  473. NULL);
  474. if(!hSession)
  475. {
  476. dwError = GetLastError();
  477. fRet = FALSE;
  478. goto cleanup;
  479. }
  480. hConnection = InternetConnect(hSession,
  481. ucUrl.lpszHostName,
  482. INTERNET_DEFAULT_HTTP_PORT,
  483. (LPCSTR) vszCurrentUser,
  484. NULL,
  485. INTERNET_SERVICE_HTTP,
  486. NULL,
  487. NULL);
  488. if(!hConnection)
  489. {
  490. dwError = GetLastError();
  491. fRet = FALSE;
  492. goto cleanup;
  493. }
  494. hHTTPReq = HttpOpenRequest(hConnection,
  495. "PROPFIND",
  496. ucUrl.lpszUrlPath,
  497. "HTTP/1.0",
  498. NULL,
  499. NULL,
  500. INTERNET_FLAG_PRAGMA_NOCACHE,NULL);
  501. if(!hHTTPReq)
  502. {
  503. dwError = GetLastError();
  504. fRet = FALSE;
  505. goto cleanup;
  506. }
  507. if (!HttpAddRequestHeaders(hHTTPReq, lpszHeaders, -1L, HTTP_ADDREQ_FLAG_ADD))
  508. {
  509. dwError = GetLastError();
  510. fRet = FALSE;
  511. goto cleanup;
  512. }
  513. if (!HttpSendRequest(hHTTPReq,NULL, 0L, (LPVOID)lpszXmlRequest, cbReqSize))
  514. {
  515. dwError = GetLastError();
  516. fRet = FALSE;
  517. goto cleanup;
  518. }
  519. if (HttpQueryInfo(hHTTPReq,
  520. HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER,
  521. &dwStatusCode,
  522. &dwStatusLength,
  523. &dwIndex))
  524. {
  525. // fRet = TRUE at this point
  526. // If request was denied or proxy auth required and callee has said
  527. // we can display UI, we put up the InternetErrorDlg and ask for
  528. // credentials. Otherwise, if dwStatus != success we cache that
  529. // this resource is not DAV compliant
  530. if ((hWnd) && ((dwStatusCode == HTTP_STATUS_DENIED) || (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)))
  531. {
  532. DWORD dwRetval;
  533. BOOL fDone;
  534. fDone = FALSE;
  535. while (!fDone)
  536. {
  537. dwRetval = InternetErrorDlg(hWnd,
  538. hHTTPReq,
  539. ERROR_INTERNET_INCORRECT_PASSWORD,
  540. 0L,
  541. NULL);
  542. if (dwRetval == ERROR_INTERNET_FORCE_RETRY) // User pressed ok on credentials dialog
  543. { // Resend request, new credentials are cached and will be replayed by HSR()
  544. if (!HttpSendRequest(hHTTPReq,NULL, 0L, NULL, NULL))
  545. {
  546. dwError = GetLastError();
  547. fRet = FALSE;
  548. goto cleanup;
  549. }
  550. dwStatusCode = 0;
  551. if (!HttpQueryInfo(hHTTPReq,
  552. HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER,
  553. &dwStatusCode,
  554. &dwStatusLength,
  555. &dwIndex))
  556. {
  557. dwError = GetLastError();
  558. fRet = FALSE;
  559. goto cleanup;
  560. }
  561. if ((dwStatusCode != HTTP_STATUS_DENIED) && (dwStatusCode != HTTP_STATUS_PROXY_AUTH_REQ))
  562. {
  563. fDone = TRUE;
  564. }
  565. }
  566. else // User pressed cancel from dialog (note ERROR_SUCCESS == ERROR_CANCELLED from IED())
  567. {
  568. fDone = TRUE;
  569. }
  570. }
  571. }
  572. if ((dwStatusCode == HTTP_STATUS_DENIED) || (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ))
  573. { // Don't want to cache this (user might have forgotten password or we may not
  574. // be able to show credential UI)
  575. goto cleanup;
  576. }
  577. else if (dwStatusCode != HTTP_STATUS_OK)
  578. { // Either initial request failed for non-auth issue, cache as not compliant
  579. goto update_cache; //note update_cache NOT cleanup, want to cache this as non compliant
  580. }
  581. }
  582. else
  583. {
  584. dwError = GetLastError();
  585. fRet = FALSE;
  586. goto cleanup;
  587. }
  588. // Note, what we are doing here is allocating a single page of memory and reserving
  589. // the first sizeof(LPSTR) bytes to point to the next buffer fragment if required,
  590. // and so on until the last buffer fragment has NULL at the dwPtrOffset
  591. lpszBufferHead = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, READ_BUFFER_SIZE);
  592. if (!lpszBufferHead)
  593. {
  594. dwError = ERROR_NOT_ENOUGH_MEMORY;
  595. goto cleanup;
  596. }
  597. memset(lpszBufferHead, 0, READ_BUFFER_SIZE);
  598. LPSTR lpNext;
  599. lpNext=lpszBufferHead+sizeof(LPSTR); // Jump over the next buffer ptr space when writing data
  600. while(!fDone)
  601. {
  602. if (!InternetReadFile(hHTTPReq,
  603. lpNext,
  604. READ_BUFFER_AVAILABLE,
  605. &dwRead))
  606. {
  607. dwError = GetLastError();
  608. fRet = FALSE;
  609. goto cleanup;
  610. }
  611. else
  612. {
  613. if (dwRead < READ_BUFFER_AVAILABLE)
  614. {
  615. fDone = TRUE;
  616. }
  617. else
  618. {
  619. dwBuffers++;
  620. *((LPSTR*)lpNext+READ_BUFFER_AVAILABLE) = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, READ_BUFFER_SIZE);
  621. lpNext = *((LPSTR*)lpNext+READ_BUFFER_AVAILABLE);
  622. if (!lpNext)
  623. {
  624. dwError = ERROR_NOT_ENOUGH_MEMORY;
  625. goto cleanup;
  626. }
  627. memset(lpNext, 0, READ_BUFFER_SIZE);
  628. }
  629. }
  630. }
  631. // Done reading response, time to parse it
  632. update_cache :
  633. #ifdef DAVCACHING // commenting out as per bug 15696
  634. if (TRUE)
  635. // Cache if this is a known DAV level 1 server or atleast the last time we tried
  636. {
  637. // Update visited links cache
  638. // We store the cached info as:
  639. // CacheEntryInfo.dwExemptDelta(HiWord) = Date DAV discovery was last run
  640. // CacheEntryInfo.dwExemptDelta(LoWord) & DAV_LEVEL1_STATUS = Is DAV server (*lpfFound)
  641. // CacheEntryInfo.dwExemptDelta(LoWord) & DAV_COLLECTION_STATUS = Is DAV collection (set in HttpCheckDavCollection)
  642. // Assemble the URL to the visited links container "VISITED: username@URL"
  643. DWORD cbNeededBuf = lstrlen("Visited: ") + lstrlen(vszCurrentUser) + lstrlen(lpszUrl) + 32;
  644. lpszVisitedUrl = (LPSTR) ALLOCATE_MEMORY(LMEM_FIXED, cbNeededBuf);
  645. if (!lpszVisitedUrl)
  646. {
  647. dwError = ERROR_NOT_ENOUGH_MEMORY;
  648. goto cleanup;
  649. }
  650. lstrcpy(lpszVisitedUrl, "Visited: "); // For compatibility with urlhist.c visited URLs
  651. lstrcat(lpszVisitedUrl, vszCurrentUser);
  652. lstrcat(lpszVisitedUrl, "@");
  653. lstrcat(lpszVisitedUrl, lpszUrl);
  654. lpCEI = (LPCACHE_ENTRY_INFO) ALLOCATE_MEMORY(LMEM_FIXED, CEI_BUFFER_SIZE);
  655. if (!lpCEI)
  656. {
  657. dwError = ERROR_NOT_ENOUGH_MEMORY;
  658. goto cleanup;
  659. }
  660. GetSystemTimeAsFileTime(&ftNow);
  661. if (!FileTimeToDosDateTime(&ftNow,
  662. &wDate,
  663. &wTime
  664. ))
  665. {
  666. INET_ASSERT(FALSE); // Should "never" hit this
  667. goto cleanup;
  668. }
  669. // Stuff our 16-bit Date into the upper 16-bits of dwExemptDelta
  670. lpCEI->dwExemptDelta = (DWORD)wDate <<16;
  671. // Set the DAV_LEVEL1_STATUS_BIT if the resource was found compliant above
  672. if (*lpfFound)
  673. {
  674. lpCEI->dwExemptDelta |= (DWORD) DAV_LEVEL1_STATUS;
  675. }
  676. if (!SetUrlCacheEntryInfo (lpszVisitedUrl,
  677. lpCEI,
  678. CACHE_ENTRY_EXEMPT_DELTA_FC
  679. ))
  680. {
  681. if (GetLastError() == ERROR_FILE_NOT_FOUND)
  682. // No existing cache entry, so create one and try again
  683. {
  684. FILETIME ftNone;
  685. if (CommitUrlCacheEntry (lpszVisitedUrl,
  686. NULL,
  687. ftNone,
  688. ftNone,
  689. 0,
  690. NULL,
  691. 0,
  692. NULL,
  693. 0
  694. ))
  695. {
  696. // Entry is now created, so try one more time
  697. SetUrlCacheEntryInfo (lpszVisitedUrl,
  698. lpCEI,
  699. CACHE_ENTRY_EXEMPT_DELTA_FC
  700. );
  701. }
  702. }
  703. }
  704. }
  705. #endif //DAVCACHING
  706. cleanup:
  707. // Free memory
  708. // Walk buffer chain
  709. if (lpszBufferHead)
  710. {
  711. lpNext = (LPSTR)*(lpszBufferHead+READ_BUFFER_AVAILABLE);
  712. FREE_MEMORY(lpszBufferHead);
  713. while (lpNext)
  714. {
  715. lpszBufferHead = lpNext;
  716. lpNext = (LPSTR)*(lpszBufferHead+READ_BUFFER_AVAILABLE);
  717. FREE_MEMORY(lpszBufferHead);
  718. }
  719. }
  720. if (lpszXmlRequest) FREE_MEMORY(lpszXmlRequest);
  721. if (lpCEI) FREE_MEMORY (lpCEI);
  722. if (lpszVisitedUrl) FREE_MEMORY (lpszVisitedUrl);
  723. // Close the handles
  724. if (hHTTPReq) InternetCloseHandle(hHTTPReq);
  725. if (hConnection) InternetCloseHandle(hConnection);
  726. SetLastError(dwError);
  727. DEBUG_LEAVE_API(fRet);
  728. return fRet;
  729. }
  730. #ifdef DAVCACHING // commenting out as per bug 15696
  731. INTERNETAPI_(BOOL) HttpCheckCachedDavStatusA(
  732. IN LPCSTR lpszUrl,
  733. IN OUT LPDWORD lpdwStatus
  734. )
  735. /*++
  736. Routine Description:
  737. Checks if there is cached DAV information for this site or if detection is required, After a successfull call,
  738. *lpdwStatus will contain a bitfield as described in arguments below.
  739. Checks HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\DaysBetweenDavDetection for time in days
  740. to pass between performing detection. If this key is missing, a default of 10 is used.
  741. Arguments:
  742. lpszUrl - Url of the resource to check
  743. lpdwStatus - address of a DWORD (which must contain zero initially!!) that on return may contain a
  744. combination of the following staus bits:
  745. DAV_LEVEL1_STATUS - If this bit is set, the site is known to support DAV level 1
  746. DAV_COLLECTION_STATUS - If this bit is set, the resource is a collection.
  747. DAV_DETECTION_REQUIRED - Either no information is available or information is stale and detection should be performed.
  748. Return Value:
  749. TRUE - Success, check lpdwStatus for results.
  750. FALSE - failure, GetLastError returns the error code
  751. --*/
  752. {
  753. BOOL fRet = TRUE;
  754. DEBUG_ENTER((DBG_API,
  755. Bool,
  756. "HttpCheckCachedDavStatusA",
  757. "%sq, %#x",
  758. lpszUrl,
  759. lpdwStatus
  760. ));
  761. DWORD dwError;
  762. LPCACHE_ENTRY_INFO lpCEI = NULL;
  763. FILETIME ftNow, ftNext, ftWork;
  764. WORD wDate;
  765. LPSTR lpszVisitedUrl=NULL;
  766. DWORD cbCEI = 512, dwDays = 0;
  767. #define DEFAULT_DAV_DAYS 10
  768. #define FILE_SEC_TICKS (10000000)
  769. // SECS PER DAY
  770. #define DAY_SECS (24*60*60)
  771. // Debug param checking
  772. INET_ASSERT(lpszUrl && lpdwStatus && (*lpdwStatus==0));
  773. DEBUG_ENTER_API((DBG_API,
  774. Bool,
  775. "HttpCheckCachedDavStatusA",
  776. "%s %x %x",
  777. (lpszUrl)?lpszUrl :"NULL Url",
  778. lpdwStatus,
  779. *lpdwStatus
  780. ));
  781. // non-debug param checking
  782. if (!lpszUrl || !lpdwStatus || (*lpdwStatus != 0))
  783. {
  784. dwError = ERROR_INVALID_PARAMETER;
  785. fRet = FALSE;
  786. goto cleanup;
  787. }
  788. // Ensures the global vszCurrentUser is set
  789. GetWininetUserName();
  790. INET_ASSERT(vszCurrentUser);
  791. // Check visited links cache
  792. // We store the cached info as:
  793. // CacheEntryInfo.dwExemptDelta(HiWord) = Date DAV discovery was last run
  794. // CacheEntryInfo.dwExemptDelta(LoWord) & DAV_LEVEL1_STATUS = Is DAV server (*lpfFound)
  795. // CacheEntryInfo.dwExemptDelta(LoWord) & DAV_COLLECTION_STATUS = Is DAV collection (set in HttpCheckDavCollection)
  796. // Assemble the URL to the visited links container "VISITED: username@URL"
  797. DWORD cbNeededBuf = lstrlen("Visited: ") + lstrlen(vszCurrentUser) + lstrlen(lpszUrl) + 32;
  798. lpszVisitedUrl = (LPSTR) ALLOCATE_MEMORY(LMEM_FIXED, cbNeededBuf);
  799. if (!lpszVisitedUrl)
  800. {
  801. dwError = ERROR_NOT_ENOUGH_MEMORY;
  802. goto cleanup;
  803. }
  804. lstrcpy(lpszVisitedUrl, "Visited: "); // For compatibility with urlhist.c visited URLs
  805. lstrcat(lpszVisitedUrl, vszCurrentUser);
  806. lstrcat(lpszVisitedUrl, "@");
  807. lstrcat(lpszVisitedUrl, lpszUrl);
  808. lpCEI = (LPCACHE_ENTRY_INFO) ALLOCATE_MEMORY(LMEM_FIXED, cbCEI);
  809. if (!lpCEI)
  810. {
  811. dwError = ERROR_NOT_ENOUGH_MEMORY;
  812. goto cleanup;
  813. }
  814. if (!GetUrlCacheEntryInfo (lpszVisitedUrl,
  815. lpCEI,
  816. &cbCEI
  817. ))
  818. {
  819. if (GetLastError() == ERROR_FILE_NOT_FOUND)
  820. // No existing cache entry, so return DAV_DETECTION_REQUIRED
  821. {
  822. *lpdwStatus = DAV_DETECTION_REQUIRED;
  823. goto cleanup;
  824. }
  825. else
  826. {
  827. dwError = GetLastError();
  828. fRet = FALSE;
  829. goto cleanup;
  830. }
  831. }
  832. // Get registry setting for # of days between detection or use default if not found
  833. if ((dwError = InternetReadRegistryDword("DaysBetweenDavDetection", &dwDays)) != ERROR_SUCCESS)
  834. {
  835. dwDays = DEFAULT_DAV_DAYS;
  836. }
  837. // Get the last date we checked this URL
  838. wDate = HIWORD(lpCEI->dwExemptDelta);
  839. DosDateTimeToFileTime(wDate, WORD(0), &ftWork);
  840. _int64 i64Base;
  841. i64Base = (((_int64)ftWork.dwHighDateTime) << 32) | ftWork.dwLowDateTime;
  842. i64Base /= FILE_SEC_TICKS;
  843. i64Base /= DAY_SECS;
  844. i64Base += dwDays;
  845. i64Base *= FILE_SEC_TICKS;
  846. i64Base *= DAY_SECS;
  847. ftNext.dwHighDateTime = (DWORD) ((i64Base >> 32) & 0xFFFFFFFF);
  848. ftNext.dwLowDateTime = (DWORD) (i64Base & 0xFFFFFFFF);
  849. GetSystemTimeAsFileTime(&ftNow);
  850. if (CompareFileTime(ftNow, ftNext) >= 0) // If Now >= Next detect date
  851. {
  852. *lpdwStatus = DAV_DETECTION_REQUIRED;
  853. }
  854. *lpdwStatus |= LOWORD(lpCEI->dwExemptDelta);
  855. dwError = ERROR_SUCCESS;
  856. cleanup:
  857. // Free memory
  858. if (lpCEI) FREE_MEMORY (lpCEI);
  859. if (lpszVisitedUrl) FREE_MEMORY (lpszVisitedUrl);
  860. SetLastError(dwError);
  861. DEBUG_LEAVE_API(fRet);
  862. return fRet;
  863. }
  864. #endif //DAVCACHING
  865. INTERNETAPI_(BOOL) HttpCheckDavComplianceW(
  866. IN LPCWSTR lpszUrlW,
  867. IN LPCWSTR lpszComplianceTokenW,
  868. IN OUT LPBOOL lpfFound,
  869. IN HWND hWnd,
  870. IN LPVOID lpvReserved
  871. )
  872. /*++
  873. Routine Description:
  874. Determines the level of DAV compliance of the server. Currently just checks for level 1
  875. compliance and returns TRUE in lpfFound if detected.
  876. Arguments:
  877. lpszServer - name of server to check
  878. lpszPath - path to resource on the server to check
  879. lpszComplianceToken - DAV compliance class identifier (i.e. "1") BUGBUG MUST NOT CONTAIN INTERNAL WHITE SPACE "foo bar" = bad, " foo " = ok
  880. lpfFound - address of a BOOL to receive TRUE if DAV compliance is found or FALSE otherwise.
  881. hWnd - Handle to window for displaying authentication dialog if needed. May be NULL indicating
  882. no UI is to be displayed and authentication failures should be quietly handled.
  883. lpvReserved - Reserved, must be NULL
  884. Return Value:
  885. TRUE - Success, check lpfFound for results.
  886. FALSE - failure, GetLastError returns the error code
  887. --*/
  888. {
  889. DEBUG_ENTER((DBG_API,
  890. Bool,
  891. "HttpCheckDavComplianceW",
  892. "%wq, %wq, %#x, %#x, %#x",
  893. lpszUrlW,
  894. lpszComplianceTokenW,
  895. lpfFound,
  896. hWnd,
  897. lpvReserved
  898. ));
  899. MEMORYPACKET mpUrlA, mpComplianceTokenA;
  900. BOOL fRet = TRUE;
  901. DWORD dwError = ERROR_SUCCESS;
  902. // Debug param checking
  903. INET_ASSERT(lpszUrlW && lpszComplianceTokenW && lpfFound && !lpvReserved);
  904. // non-debug param checking
  905. if (!lpszUrlW || !lpszComplianceTokenW || !lpfFound || lpvReserved)
  906. {
  907. dwError = ERROR_INVALID_PARAMETER;
  908. fRet = FALSE;
  909. goto cleanup;
  910. }
  911. ALLOC_MB(lpszUrlW, 0, mpUrlA);
  912. if (!mpUrlA.psStr)
  913. {
  914. dwError = ERROR_NOT_ENOUGH_MEMORY;
  915. fRet = FALSE;
  916. goto cleanup;
  917. }
  918. UNICODE_TO_ANSI(lpszUrlW, mpUrlA);
  919. // MAKE_ANSI(lpszComplianceTokenW, 0, mpComplianceTokenA);
  920. ALLOC_MB(lpszComplianceTokenW, 0, mpComplianceTokenA);
  921. if (!mpComplianceTokenA.psStr)
  922. {
  923. dwError = ERROR_NOT_ENOUGH_MEMORY;
  924. fRet = FALSE;
  925. goto cleanup;
  926. }
  927. UNICODE_TO_ANSI(lpszComplianceTokenW, mpComplianceTokenA);
  928. fRet = HttpCheckDavComplianceA(mpUrlA.psStr, mpComplianceTokenA.psStr, lpfFound, hWnd, lpvReserved);
  929. cleanup:
  930. DEBUG_LEAVE_API(fRet);
  931. return(fRet);
  932. }
  933. #ifdef DAVCACHING //commenting out as per bug 15696
  934. INTERNETAPI_(BOOL) HttpCheckCachedDavStatusW(
  935. IN LPCWSTR lpszUrlW,
  936. IN OUT LPDWORD lpdwStatus
  937. )
  938. /*++
  939. Routine Description:
  940. Checks if there is cached DAV information for this site or if detection is required,
  941. Arguments:
  942. lpszUrl - Url of the resource to check
  943. lpdwStatus - address of a DWORD (which must contain zero initially!!) that on return may contain a
  944. combination of the following staus bits:
  945. DAV_LEVEL1_STATUS - If this bit is set, the site is known to support DAV level 1
  946. DAV_COLLECTION_STATUS - If this bit is set, the resource is a collection.
  947. DAV_DETECTION_REQUIRED - Either no information is available or information is stale and detection should be performed.
  948. Return Value:
  949. TRUE - Success, check lpdwStatus for results.
  950. FALSE - failure, GetLastError returns the error code
  951. --*/
  952. {
  953. DEBUG_ENTER((DBG_API,
  954. Bool,
  955. "HttpCheckCachedDavStatusW",
  956. "%wq, %#x",
  957. lpszUrlW,
  958. lpdwStatus
  959. ));
  960. MEMORYPACKET mpUrlA;
  961. BOOL fRet = TRUE;
  962. DWORD dwError = ERROR_SUCCESS;
  963. // Debug param checking
  964. INET_ASSERT(lpszUrlW && lpdwStatus && (*lpdwStatus==0));
  965. // non-debug param checking
  966. if (!lpszUrlW || !lpdwStatus || (*lpdwStatus!=0))
  967. {
  968. dwError = ERROR_INVALID_PARAMETER;
  969. fRet = FALSE;
  970. goto cleanup;
  971. }
  972. ALLOC_MB(lpszUrlW, 0, mpUrlA);
  973. if (!mpUrlA.psStr)
  974. {
  975. dwError = ERROR_NOT_ENOUGH_MEMORY;
  976. fRet = FALSE;
  977. goto cleanup;
  978. }
  979. UNICODE_TO_ANSI(lpszUrlW, mpUrlA);
  980. fRet = HttpCheckCachedDavStatusA(mpUrlA.psStr, lpdwStatus);
  981. cleanup:
  982. DEBUG_LEAVE_API(fRet);
  983. return(fRet);
  984. }
  985. #endif //DAVCACHING