/*++ Copyright (c) 1998 Microsoft Corporation Module Name: dav.cxx Abstract: This file contains the implementations of HttpCheckDavCompliance HttpCheckDavCollection HttpCheckCachedDavStatus The following functions are exported by this module: HttpCheckDavComplianceA HttpCheckDavCollectionA HttpCheckCachedDavStatusA HttpCheckDavComplianceW HttpCheckDavCollectionW HttpCheckCachedDavStatusW Author: Mead Himelstein (Meadh) 01-Jun-1998 Revision History: --*/ #include #include "httpp.h" #undef DAVCACHING //IE5 bug 15696, removing unused code but may add back in later INTERNETAPI_(BOOL) HttpCheckDavComplianceA( IN LPCSTR lpszUrl, IN LPCSTR lpszComplianceToken, IN OUT LPBOOL lpfFound, IN HWND hWnd, IN LPVOID lpvReserved ) /*++ Routine Description: Determines if the resource identified by lpszUrl is DAV compliant (level determined by lpszComplianceToken). Returns TRUE in lpfFound if detected. Furthermore, if the token is "1" we also cache todays date and whether or not we found the server to be DAV level 1 compliant in the visited links cache. Arguments: lpszUrl - URL to the resource to check for DAV compliance, i.e. "http://webdav/davfood/" lpszComplianceToken - DAV compliance class identifier (i.e. "1") BUGBUG MUST NOT CONTAIN INTERNAL WHITE SPACE "foo bar" = bad, " foo " = ok lpfFound - address of a BOOL to receive TRUE if DAV compliance is found or FALSE otherwise. hWnd - Handle to window for displaying authentication dialog if needed. May be NULL indicating no UI is to be displayed and authentication failures should be quietly handled. lpvReserved - Reserved, must be NULL Return Value: TRUE - Success, check lpfFound for results. FALSE - failure, GetLastError returns the error code --*/ { BOOL fRet = TRUE; DEBUG_ENTER((DBG_API, Bool, "HttpCheckDavComplianceA", "%sq, %sq, %#x, %#x, %#x", lpszUrl, lpszComplianceToken, lpfFound, hWnd, lpvReserved )); DWORD dwStatusCode, dwHeaderLength, dwIndex = 0; DWORD dwError = ERROR_SUCCESS, dwStatusLength = sizeof(DWORD); HINTERNET hSession = NULL, hConnection = NULL, hHTTPReq = NULL; LPSTR lpszDavHeader = NULL, lpszRead = NULL, lpszWrite = NULL, lpszVisitedUrl=NULL; LPCACHE_ENTRY_INFO lpCEI = NULL; FILETIME ftNow; WORD wDate, wTime; URL_COMPONENTS ucUrl; #define CEI_BUFFER_SIZE 512 #define IsWhite(c) ((DWORD) (c) > 32 ? FALSE : TRUE) // Debug param checking INET_ASSERT(lpszUrl && lpszComplianceToken && lpfFound && !lpvReserved); DEBUG_ENTER_API((DBG_API, Bool, "HttpCheckDavComplianceA", "%s %s %x %x %x", (lpszUrl)?lpszUrl :"NULL Url", (lpszComplianceToken)?lpszComplianceToken :"NULL ComplianceToken", hWnd, lpfFound, lpvReserved )); // non-debug param checking if (!lpszUrl || !lpszComplianceToken || !lpfFound || lpvReserved) { dwError = ERROR_INVALID_PARAMETER; fRet = FALSE; goto cleanup; } *lpfFound = FALSE; // Build the URL_COMPONENTS struct memset(&ucUrl, 0, sizeof(ucUrl)); ucUrl.dwStructSize = sizeof(ucUrl); // non-zero length enables retrieval during CrackUrl ucUrl.dwSchemeLength = 1; ucUrl.dwHostNameLength = 1; ucUrl.dwUrlPathLength = 1; if (!InternetCrackUrl(lpszUrl, 0, 0, &ucUrl)) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } DEBUG_PRINT_API(API, INFO, ("URL Cracked. Host = %q Path = %q\n ", ucUrl.lpszHostName, ucUrl.lpszUrlPath )); // Ensures the global vszCurrentUser is set GetWininetUserName(); INET_ASSERT(vszCurrentUser); // Perform an OPTIONS call on the server and check for a DAV header hSession = InternetOpen(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, NULL); if(!hSession) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } hConnection = InternetConnect(hSession, ucUrl.lpszHostName, INTERNET_DEFAULT_HTTP_PORT, (LPCSTR) vszCurrentUser, NULL, INTERNET_SERVICE_HTTP, NULL, NULL); if(!hConnection) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } hHTTPReq = HttpOpenRequest(hConnection, "OPTIONS", ucUrl.lpszUrlPath, "HTTP/1.0", NULL, NULL, INTERNET_FLAG_PRAGMA_NOCACHE,NULL); if(!hHTTPReq) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } if (!HttpSendRequest(hHTTPReq,NULL, 0L, NULL, NULL)) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } // Authentication handling if (HttpQueryInfo(hHTTPReq, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, &dwStatusCode, &dwStatusLength, &dwIndex)) { // fRet = TRUE at this point // If request was denied or proxy auth required and callee has said // we can display UI, we put up the InternetErrorDlg and ask for // credentials. Otherwise, if dwStatus != success we cache that // this resource is not DAV compliant if ((hWnd) && ((dwStatusCode == HTTP_STATUS_DENIED) || (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ))) { DWORD dwRetval; DWORD dwAuthTries = 0; BOOL fDone; fDone = FALSE; while ((!fDone) && (dwAuthTries < 3)) { dwRetval = InternetErrorDlg(hWnd, hHTTPReq, ERROR_INTERNET_INCORRECT_PASSWORD, 0L, NULL); if (dwRetval == ERROR_INTERNET_FORCE_RETRY) // User pressed ok on credentials dialog { // Resend request, new credentials are cached and will be replayed by HSR() if (!HttpSendRequest(hHTTPReq,NULL, 0L, NULL, NULL)) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } dwStatusCode = 0; if (!HttpQueryInfo(hHTTPReq, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, &dwStatusCode, &dwStatusLength, &dwIndex)) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } if ((dwStatusCode != HTTP_STATUS_DENIED) && (dwStatusCode != HTTP_STATUS_PROXY_AUTH_REQ)) { fDone = TRUE; } } else // User pressed cancel from dialog (note ERROR_SUCCESS == ERROR_CANCELLED from IED()) { fDone = TRUE; } dwAuthTries++; } } if ((dwStatusCode == HTTP_STATUS_DENIED) || (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)) { // Don't want to cache this (user might have forgotten password or we may not // be able to show credential UI) goto cleanup; } else if (dwStatusCode != HTTP_STATUS_OK) { // Either initial request failed for non-auth issue, cache as not compliant goto update_cache; //note update_cache NOT cleanup, want to cache this as non compliant } } else { dwError = GetLastError(); fRet = FALSE; goto cleanup; } // If we received a DAV header, snag it and check for compliance level dwHeaderLength = INTERNET_MAX_URL_LENGTH; lpszDavHeader = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, dwHeaderLength); if (!lpszDavHeader) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } lstrcpy(lpszDavHeader,"DAV"); if (!HttpQueryInfo(hHTTPReq, HTTP_QUERY_CUSTOM, lpszDavHeader, &dwHeaderLength, &dwIndex)) { dwError = GetLastError(); fRet = FALSE; goto update_cache; //note update_cache NOT cleanup, want to cache this as non compliant } INET_ASSERT(fRet); // sb TRUE if it made it this far // Walk the DAV header looking for the token by itself, indicating DAV compliance // Note: DAV header is comma delimited, but otherwise we can make no assumptions on formatting. // BUGBUG Currently will not work for tokens with internal white space i.e. "foo bar" = bad, " foobar " = good lpszRead = lpszDavHeader; lpszWrite = lpszDavHeader; INET_ASSERT(!*lpfFound); INET_ASSERT(fRet); while(*lpszRead && !*lpfFound) { if (IsWhite(*lpszRead)) { while ((*(++lpszRead))&&(IsWhite(*lpszRead))); } if (*lpszRead) { if(*lpszRead == ',') { *(lpszRead++) = '\0'; if (*lpfFound = (lstrcmp(lpszDavHeader, lpszComplianceToken) == 0) ? TRUE:FALSE) { goto update_cache; } lpszWrite = lpszRead; lpszDavHeader = lpszRead; } else *(lpszWrite++) = *(lpszRead++); } } *lpszWrite = *lpszRead; // copy the terminator INET_ASSERT(!*lpfFound); *lpfFound = (lstrcmp(lpszDavHeader, lpszComplianceToken) == 0) ? TRUE:FALSE; //Final compare on last fragment update_cache : #ifdef DAVCACHING // Commenting out as per bug 15696 if (lstrcmp("1", lpszComplianceToken) == 0) // Cache if this is a known DAV level 1 server or atleast the last time we tried { // Update visited links cache // We store the cached info as: // CacheEntryInfo.dwExemptDelta(HiWord) = Date DAV discovery was last run // CacheEntryInfo.dwExemptDelta(LoWord) & DAV_LEVEL1_STATUS = Is DAV server (*lpfFound) // CacheEntryInfo.dwExemptDelta(LoWord) & DAV_COLLECTION_STATUS = Is DAV collection (set in HttpCheckDavCollection) // Assemble the URL to the visited links container "VISITED: username@URL" DWORD cbNeededBuf = lstrlen("Visited: ") + lstrlen(vszCurrentUser) + lstrlen(lpszUrl) + 32; lpszVisitedUrl = (LPSTR) ALLOCATE_MEMORY(LMEM_FIXED, cbNeededBuf); if (!lpszVisitedUrl) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } lstrcpy(lpszVisitedUrl, "Visited: "); // For compatibility with urlhist.c visited URLs lstrcat(lpszVisitedUrl, vszCurrentUser); lstrcat(lpszVisitedUrl, "@"); lstrcat(lpszVisitedUrl, lpszUrl); lpCEI = (LPCACHE_ENTRY_INFO) ALLOCATE_MEMORY(LMEM_FIXED, CEI_BUFFER_SIZE); if (!lpCEI) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } GetSystemTimeAsFileTime(&ftNow); if (!FileTimeToDosDateTime(&ftNow, &wDate, &wTime )) { INET_ASSERT(FALSE); // Should "never" hit this goto cleanup; } // Stuff our 16-bit Date into the upper 16-bits of dwExemptDelta lpCEI->dwExemptDelta = (DWORD)wDate <<16; // Set the DAV_LEVEL1_STATUS_BIT if the resource was found compliant above if (*lpfFound) { lpCEI->dwExemptDelta |= (DWORD) DAV_LEVEL1_STATUS; } if (!SetUrlCacheEntryInfo (lpszVisitedUrl, lpCEI, CACHE_ENTRY_EXEMPT_DELTA_FC )) { if (GetLastError() == ERROR_FILE_NOT_FOUND) // No existing cache entry, so create one and try again { FILETIME ftNone; if (CommitUrlCacheEntry (lpszVisitedUrl, NULL, ftNone, ftNone, 0, NULL, 0, NULL, 0 )) { // Entry is now created, so try one more time SetUrlCacheEntryInfo (lpszVisitedUrl, lpCEI, CACHE_ENTRY_EXEMPT_DELTA_FC ); } } } } #endif //DAVCACHING cleanup: // Free memory if (lpCEI) FREE_MEMORY (lpCEI); if (lpszVisitedUrl) FREE_MEMORY (lpszVisitedUrl); if (lpszDavHeader != NULL) FREE_MEMORY(lpszDavHeader); // Close the handles if (hHTTPReq) InternetCloseHandle(hHTTPReq); if (hConnection) InternetCloseHandle(hConnection); SetLastError(dwError); DEBUG_LEAVE_API(fRet); return fRet; } INTERNETAPI_(BOOL) HttpCheckDavCollectionA( IN LPCSTR lpszUrl, IN OUT LPBOOL lpfFound, IN HWND hWnd, IN LPVOID lpvReserved ) /*++ Routine Description: Checks the resource at lpszUrl to see if it is a DAV collection. Typically, callee has already called HttpCheckDavCompliance or HttpCheckCachedDavStatus to determine the resource supports DAV level 1, although it is not necessary to do so. Note this method will blindly attempt either way. Arguments: lpszUrl - URL of the resource to check lpfFound - address of a BOOL to receive TRUE if DAV compliance is found or FALSE otherwise. hWnd - Handle to window for displaying authentication dialog if needed. May be NULL indicating no UI is to be displayed and authentication failures should be quietly handled. lpvReserved - Reserved, must be NULL Return Value: TRUE - Success, check lpfFound for results. FALSE - failure, GetLastError returns the error code --*/ { #define CEI_BUFFER_SIZE 512 #define READ_BUFFER_SIZE 4096 #define READ_BUFFER_AVAILABLE (READ_BUFFER_SIZE - sizeof(LPSTR)) BOOL fRet = TRUE, fDone=FALSE; DEBUG_ENTER((DBG_API, Bool, "HttpCheckDavCollectionA", "%sq, %#x, %#x, %#x", lpszUrl, lpfFound, hWnd, lpvReserved )); DWORD dwRead=0, dwBuffers =1; DWORD dwStatusCode, dwIndex = 0; DWORD dwError = ERROR_SUCCESS, dwStatusLength = sizeof(DWORD); HINTERNET hSession = NULL, hConnection = NULL, hHTTPReq = NULL; LPSTR lpszVisitedUrl=NULL, lpszXmlResponse=NULL, lpszBufferHead=NULL; LPCACHE_ENTRY_INFO lpCEI = NULL; FILETIME ftNow; WORD wDate, wTime; URL_COMPONENTS ucUrl; char lpszHeaders[] = "Depth:0\r\n"; // Debug param checking INET_ASSERT(lpszUrl && lpfFound && !lpvReserved); DEBUG_ENTER_API((DBG_API, Bool, "HttpCheckDavCollectionA", "%s %x %x", (lpszUrl)?lpszUrl :"NULL Url", lpfFound, lpvReserved )); // non-debug param checking if (!lpszUrl || !lpfFound || lpvReserved) { dwError = ERROR_INVALID_PARAMETER; fRet = FALSE; goto cleanup; } *lpfFound = FALSE; // Build the URL_COMPONENTS struct memset(&ucUrl, 0, sizeof(ucUrl)); ucUrl.dwStructSize = sizeof(ucUrl); // non-zero length enables retrieval during CrackUrl ucUrl.dwSchemeLength = 1; ucUrl.dwHostNameLength = 1; ucUrl.dwUrlPathLength = 1; if (!InternetCrackUrl(lpszUrl, 0, 0, &ucUrl)) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } INET_ASSERT(ucUrl.lpszHostName && ucUrl.lpszUrlPath); DEBUG_PRINT_API(API, INFO, ("URL Cracked. Host = %q Path = %q\n ", ucUrl.lpszHostName, ucUrl.lpszUrlPath )); // Ensures the global vszCurrentUser is set GetWininetUserName(); INET_ASSERT(vszCurrentUser); // Build the XML request body for a PROPFIND // // // // // // // #define REQUEST_RESOURCE_TYPE "\ \r\n \ \r\n \ \r\n \ \r\n \ \r\n \ \r\n \ \r\n" DWORD cbReqSize; cbReqSize = lstrlen(REQUEST_RESOURCE_TYPE); LPCSTR lpszXmlRequest; lpszXmlRequest = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, cbReqSize); // Perform a PROPFIND call, looking for resourcetype = DAV:collection hSession = InternetOpen(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, NULL); if(!hSession) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } hConnection = InternetConnect(hSession, ucUrl.lpszHostName, INTERNET_DEFAULT_HTTP_PORT, (LPCSTR) vszCurrentUser, NULL, INTERNET_SERVICE_HTTP, NULL, NULL); if(!hConnection) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } hHTTPReq = HttpOpenRequest(hConnection, "PROPFIND", ucUrl.lpszUrlPath, "HTTP/1.0", NULL, NULL, INTERNET_FLAG_PRAGMA_NOCACHE,NULL); if(!hHTTPReq) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } if (!HttpAddRequestHeaders(hHTTPReq, lpszHeaders, -1L, HTTP_ADDREQ_FLAG_ADD)) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } if (!HttpSendRequest(hHTTPReq,NULL, 0L, (LPVOID)lpszXmlRequest, cbReqSize)) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } if (HttpQueryInfo(hHTTPReq, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, &dwStatusCode, &dwStatusLength, &dwIndex)) { // fRet = TRUE at this point // If request was denied or proxy auth required and callee has said // we can display UI, we put up the InternetErrorDlg and ask for // credentials. Otherwise, if dwStatus != success we cache that // this resource is not DAV compliant if ((hWnd) && ((dwStatusCode == HTTP_STATUS_DENIED) || (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ))) { DWORD dwRetval; BOOL fDone; fDone = FALSE; while (!fDone) { dwRetval = InternetErrorDlg(hWnd, hHTTPReq, ERROR_INTERNET_INCORRECT_PASSWORD, 0L, NULL); if (dwRetval == ERROR_INTERNET_FORCE_RETRY) // User pressed ok on credentials dialog { // Resend request, new credentials are cached and will be replayed by HSR() if (!HttpSendRequest(hHTTPReq,NULL, 0L, NULL, NULL)) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } dwStatusCode = 0; if (!HttpQueryInfo(hHTTPReq, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, &dwStatusCode, &dwStatusLength, &dwIndex)) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } if ((dwStatusCode != HTTP_STATUS_DENIED) && (dwStatusCode != HTTP_STATUS_PROXY_AUTH_REQ)) { fDone = TRUE; } } else // User pressed cancel from dialog (note ERROR_SUCCESS == ERROR_CANCELLED from IED()) { fDone = TRUE; } } } if ((dwStatusCode == HTTP_STATUS_DENIED) || (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)) { // Don't want to cache this (user might have forgotten password or we may not // be able to show credential UI) goto cleanup; } else if (dwStatusCode != HTTP_STATUS_OK) { // Either initial request failed for non-auth issue, cache as not compliant goto update_cache; //note update_cache NOT cleanup, want to cache this as non compliant } } else { dwError = GetLastError(); fRet = FALSE; goto cleanup; } // Note, what we are doing here is allocating a single page of memory and reserving // the first sizeof(LPSTR) bytes to point to the next buffer fragment if required, // and so on until the last buffer fragment has NULL at the dwPtrOffset lpszBufferHead = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, READ_BUFFER_SIZE); if (!lpszBufferHead) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } memset(lpszBufferHead, 0, READ_BUFFER_SIZE); LPSTR lpNext; lpNext=lpszBufferHead+sizeof(LPSTR); // Jump over the next buffer ptr space when writing data while(!fDone) { if (!InternetReadFile(hHTTPReq, lpNext, READ_BUFFER_AVAILABLE, &dwRead)) { dwError = GetLastError(); fRet = FALSE; goto cleanup; } else { if (dwRead < READ_BUFFER_AVAILABLE) { fDone = TRUE; } else { dwBuffers++; *((LPSTR*)lpNext+READ_BUFFER_AVAILABLE) = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, READ_BUFFER_SIZE); lpNext = *((LPSTR*)lpNext+READ_BUFFER_AVAILABLE); if (!lpNext) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } memset(lpNext, 0, READ_BUFFER_SIZE); } } } // Done reading response, time to parse it update_cache : #ifdef DAVCACHING // commenting out as per bug 15696 if (TRUE) // Cache if this is a known DAV level 1 server or atleast the last time we tried { // Update visited links cache // We store the cached info as: // CacheEntryInfo.dwExemptDelta(HiWord) = Date DAV discovery was last run // CacheEntryInfo.dwExemptDelta(LoWord) & DAV_LEVEL1_STATUS = Is DAV server (*lpfFound) // CacheEntryInfo.dwExemptDelta(LoWord) & DAV_COLLECTION_STATUS = Is DAV collection (set in HttpCheckDavCollection) // Assemble the URL to the visited links container "VISITED: username@URL" DWORD cbNeededBuf = lstrlen("Visited: ") + lstrlen(vszCurrentUser) + lstrlen(lpszUrl) + 32; lpszVisitedUrl = (LPSTR) ALLOCATE_MEMORY(LMEM_FIXED, cbNeededBuf); if (!lpszVisitedUrl) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } lstrcpy(lpszVisitedUrl, "Visited: "); // For compatibility with urlhist.c visited URLs lstrcat(lpszVisitedUrl, vszCurrentUser); lstrcat(lpszVisitedUrl, "@"); lstrcat(lpszVisitedUrl, lpszUrl); lpCEI = (LPCACHE_ENTRY_INFO) ALLOCATE_MEMORY(LMEM_FIXED, CEI_BUFFER_SIZE); if (!lpCEI) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } GetSystemTimeAsFileTime(&ftNow); if (!FileTimeToDosDateTime(&ftNow, &wDate, &wTime )) { INET_ASSERT(FALSE); // Should "never" hit this goto cleanup; } // Stuff our 16-bit Date into the upper 16-bits of dwExemptDelta lpCEI->dwExemptDelta = (DWORD)wDate <<16; // Set the DAV_LEVEL1_STATUS_BIT if the resource was found compliant above if (*lpfFound) { lpCEI->dwExemptDelta |= (DWORD) DAV_LEVEL1_STATUS; } if (!SetUrlCacheEntryInfo (lpszVisitedUrl, lpCEI, CACHE_ENTRY_EXEMPT_DELTA_FC )) { if (GetLastError() == ERROR_FILE_NOT_FOUND) // No existing cache entry, so create one and try again { FILETIME ftNone; if (CommitUrlCacheEntry (lpszVisitedUrl, NULL, ftNone, ftNone, 0, NULL, 0, NULL, 0 )) { // Entry is now created, so try one more time SetUrlCacheEntryInfo (lpszVisitedUrl, lpCEI, CACHE_ENTRY_EXEMPT_DELTA_FC ); } } } } #endif //DAVCACHING cleanup: // Free memory // Walk buffer chain if (lpszBufferHead) { lpNext = (LPSTR)*(lpszBufferHead+READ_BUFFER_AVAILABLE); FREE_MEMORY(lpszBufferHead); while (lpNext) { lpszBufferHead = lpNext; lpNext = (LPSTR)*(lpszBufferHead+READ_BUFFER_AVAILABLE); FREE_MEMORY(lpszBufferHead); } } if (lpszXmlRequest) FREE_MEMORY(lpszXmlRequest); if (lpCEI) FREE_MEMORY (lpCEI); if (lpszVisitedUrl) FREE_MEMORY (lpszVisitedUrl); // Close the handles if (hHTTPReq) InternetCloseHandle(hHTTPReq); if (hConnection) InternetCloseHandle(hConnection); SetLastError(dwError); DEBUG_LEAVE_API(fRet); return fRet; } #ifdef DAVCACHING // commenting out as per bug 15696 INTERNETAPI_(BOOL) HttpCheckCachedDavStatusA( IN LPCSTR lpszUrl, IN OUT LPDWORD lpdwStatus ) /*++ Routine Description: Checks if there is cached DAV information for this site or if detection is required, After a successfull call, *lpdwStatus will contain a bitfield as described in arguments below. Checks HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\DaysBetweenDavDetection for time in days to pass between performing detection. If this key is missing, a default of 10 is used. Arguments: lpszUrl - Url of the resource to check lpdwStatus - address of a DWORD (which must contain zero initially!!) that on return may contain a combination of the following staus bits: DAV_LEVEL1_STATUS - If this bit is set, the site is known to support DAV level 1 DAV_COLLECTION_STATUS - If this bit is set, the resource is a collection. DAV_DETECTION_REQUIRED - Either no information is available or information is stale and detection should be performed. Return Value: TRUE - Success, check lpdwStatus for results. FALSE - failure, GetLastError returns the error code --*/ { BOOL fRet = TRUE; DEBUG_ENTER((DBG_API, Bool, "HttpCheckCachedDavStatusA", "%sq, %#x", lpszUrl, lpdwStatus )); DWORD dwError; LPCACHE_ENTRY_INFO lpCEI = NULL; FILETIME ftNow, ftNext, ftWork; WORD wDate; LPSTR lpszVisitedUrl=NULL; DWORD cbCEI = 512, dwDays = 0; #define DEFAULT_DAV_DAYS 10 #define FILE_SEC_TICKS (10000000) // SECS PER DAY #define DAY_SECS (24*60*60) // Debug param checking INET_ASSERT(lpszUrl && lpdwStatus && (*lpdwStatus==0)); DEBUG_ENTER_API((DBG_API, Bool, "HttpCheckCachedDavStatusA", "%s %x %x", (lpszUrl)?lpszUrl :"NULL Url", lpdwStatus, *lpdwStatus )); // non-debug param checking if (!lpszUrl || !lpdwStatus || (*lpdwStatus != 0)) { dwError = ERROR_INVALID_PARAMETER; fRet = FALSE; goto cleanup; } // Ensures the global vszCurrentUser is set GetWininetUserName(); INET_ASSERT(vszCurrentUser); // Check visited links cache // We store the cached info as: // CacheEntryInfo.dwExemptDelta(HiWord) = Date DAV discovery was last run // CacheEntryInfo.dwExemptDelta(LoWord) & DAV_LEVEL1_STATUS = Is DAV server (*lpfFound) // CacheEntryInfo.dwExemptDelta(LoWord) & DAV_COLLECTION_STATUS = Is DAV collection (set in HttpCheckDavCollection) // Assemble the URL to the visited links container "VISITED: username@URL" DWORD cbNeededBuf = lstrlen("Visited: ") + lstrlen(vszCurrentUser) + lstrlen(lpszUrl) + 32; lpszVisitedUrl = (LPSTR) ALLOCATE_MEMORY(LMEM_FIXED, cbNeededBuf); if (!lpszVisitedUrl) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } lstrcpy(lpszVisitedUrl, "Visited: "); // For compatibility with urlhist.c visited URLs lstrcat(lpszVisitedUrl, vszCurrentUser); lstrcat(lpszVisitedUrl, "@"); lstrcat(lpszVisitedUrl, lpszUrl); lpCEI = (LPCACHE_ENTRY_INFO) ALLOCATE_MEMORY(LMEM_FIXED, cbCEI); if (!lpCEI) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } if (!GetUrlCacheEntryInfo (lpszVisitedUrl, lpCEI, &cbCEI )) { if (GetLastError() == ERROR_FILE_NOT_FOUND) // No existing cache entry, so return DAV_DETECTION_REQUIRED { *lpdwStatus = DAV_DETECTION_REQUIRED; goto cleanup; } else { dwError = GetLastError(); fRet = FALSE; goto cleanup; } } // Get registry setting for # of days between detection or use default if not found if ((dwError = InternetReadRegistryDword("DaysBetweenDavDetection", &dwDays)) != ERROR_SUCCESS) { dwDays = DEFAULT_DAV_DAYS; } // Get the last date we checked this URL wDate = HIWORD(lpCEI->dwExemptDelta); DosDateTimeToFileTime(wDate, WORD(0), &ftWork); _int64 i64Base; i64Base = (((_int64)ftWork.dwHighDateTime) << 32) | ftWork.dwLowDateTime; i64Base /= FILE_SEC_TICKS; i64Base /= DAY_SECS; i64Base += dwDays; i64Base *= FILE_SEC_TICKS; i64Base *= DAY_SECS; ftNext.dwHighDateTime = (DWORD) ((i64Base >> 32) & 0xFFFFFFFF); ftNext.dwLowDateTime = (DWORD) (i64Base & 0xFFFFFFFF); GetSystemTimeAsFileTime(&ftNow); if (CompareFileTime(ftNow, ftNext) >= 0) // If Now >= Next detect date { *lpdwStatus = DAV_DETECTION_REQUIRED; } *lpdwStatus |= LOWORD(lpCEI->dwExemptDelta); dwError = ERROR_SUCCESS; cleanup: // Free memory if (lpCEI) FREE_MEMORY (lpCEI); if (lpszVisitedUrl) FREE_MEMORY (lpszVisitedUrl); SetLastError(dwError); DEBUG_LEAVE_API(fRet); return fRet; } #endif //DAVCACHING INTERNETAPI_(BOOL) HttpCheckDavComplianceW( IN LPCWSTR lpszUrlW, IN LPCWSTR lpszComplianceTokenW, IN OUT LPBOOL lpfFound, IN HWND hWnd, IN LPVOID lpvReserved ) /*++ Routine Description: Determines the level of DAV compliance of the server. Currently just checks for level 1 compliance and returns TRUE in lpfFound if detected. Arguments: lpszServer - name of server to check lpszPath - path to resource on the server to check lpszComplianceToken - DAV compliance class identifier (i.e. "1") BUGBUG MUST NOT CONTAIN INTERNAL WHITE SPACE "foo bar" = bad, " foo " = ok lpfFound - address of a BOOL to receive TRUE if DAV compliance is found or FALSE otherwise. hWnd - Handle to window for displaying authentication dialog if needed. May be NULL indicating no UI is to be displayed and authentication failures should be quietly handled. lpvReserved - Reserved, must be NULL Return Value: TRUE - Success, check lpfFound for results. FALSE - failure, GetLastError returns the error code --*/ { DEBUG_ENTER((DBG_API, Bool, "HttpCheckDavComplianceW", "%wq, %wq, %#x, %#x, %#x", lpszUrlW, lpszComplianceTokenW, lpfFound, hWnd, lpvReserved )); MEMORYPACKET mpUrlA, mpComplianceTokenA; BOOL fRet = TRUE; DWORD dwError = ERROR_SUCCESS; // Debug param checking INET_ASSERT(lpszUrlW && lpszComplianceTokenW && lpfFound && !lpvReserved); // non-debug param checking if (!lpszUrlW || !lpszComplianceTokenW || !lpfFound || lpvReserved) { dwError = ERROR_INVALID_PARAMETER; fRet = FALSE; goto cleanup; } ALLOC_MB(lpszUrlW, 0, mpUrlA); if (!mpUrlA.psStr) { dwError = ERROR_NOT_ENOUGH_MEMORY; fRet = FALSE; goto cleanup; } UNICODE_TO_ANSI(lpszUrlW, mpUrlA); // MAKE_ANSI(lpszComplianceTokenW, 0, mpComplianceTokenA); ALLOC_MB(lpszComplianceTokenW, 0, mpComplianceTokenA); if (!mpComplianceTokenA.psStr) { dwError = ERROR_NOT_ENOUGH_MEMORY; fRet = FALSE; goto cleanup; } UNICODE_TO_ANSI(lpszComplianceTokenW, mpComplianceTokenA); fRet = HttpCheckDavComplianceA(mpUrlA.psStr, mpComplianceTokenA.psStr, lpfFound, hWnd, lpvReserved); cleanup: DEBUG_LEAVE_API(fRet); return(fRet); } #ifdef DAVCACHING //commenting out as per bug 15696 INTERNETAPI_(BOOL) HttpCheckCachedDavStatusW( IN LPCWSTR lpszUrlW, IN OUT LPDWORD lpdwStatus ) /*++ Routine Description: Checks if there is cached DAV information for this site or if detection is required, Arguments: lpszUrl - Url of the resource to check lpdwStatus - address of a DWORD (which must contain zero initially!!) that on return may contain a combination of the following staus bits: DAV_LEVEL1_STATUS - If this bit is set, the site is known to support DAV level 1 DAV_COLLECTION_STATUS - If this bit is set, the resource is a collection. DAV_DETECTION_REQUIRED - Either no information is available or information is stale and detection should be performed. Return Value: TRUE - Success, check lpdwStatus for results. FALSE - failure, GetLastError returns the error code --*/ { DEBUG_ENTER((DBG_API, Bool, "HttpCheckCachedDavStatusW", "%wq, %#x", lpszUrlW, lpdwStatus )); MEMORYPACKET mpUrlA; BOOL fRet = TRUE; DWORD dwError = ERROR_SUCCESS; // Debug param checking INET_ASSERT(lpszUrlW && lpdwStatus && (*lpdwStatus==0)); // non-debug param checking if (!lpszUrlW || !lpdwStatus || (*lpdwStatus!=0)) { dwError = ERROR_INVALID_PARAMETER; fRet = FALSE; goto cleanup; } ALLOC_MB(lpszUrlW, 0, mpUrlA); if (!mpUrlA.psStr) { dwError = ERROR_NOT_ENOUGH_MEMORY; fRet = FALSE; goto cleanup; } UNICODE_TO_ANSI(lpszUrlW, mpUrlA); fRet = HttpCheckCachedDavStatusA(mpUrlA.psStr, lpdwStatus); cleanup: DEBUG_LEAVE_API(fRet); return(fRet); } #endif //DAVCACHING