/*++ Copyright (c) 1994 Microsoft Corporation Module Name: inetapia.cxx Abstract: Contains the ANSI and character-mode-independent Internet APIs Contents: InternetCrackUrlA InternetCreateUrlA InternetCanonicalizeUrlA InternetCombineUrlA InternetOpenA InternetCloseHandle _InternetCloseHandle _InternetCloseHandleNoContext InternetConnectA InternetOpenUrlA InternetReadFile ReadFile_End InternetReadFileExA InternetWriteFile InternetWriteFileExA InternetSetFilePointer InternetQueryDataAvailable InternetFindNextFileA InternetQueryOptionA InternetSetOptionA InternetSetOptionExA InternetGetLastResponseInfoA InternetSetStatusCallbackA //InternetCancelAsyncRequest (wInternetCloseConnectA) (GetEmailNameAndPassword) InternetAttemptConnect (CreateDeleteSocket) InternetLockRequestFile InternetUnlockRequestFile InternetCheckConnectionA Author: Richard L Firth (rfirth) 02-Mar-1995 Environment: Win32 user-mode DLL Revision History: 02-Mar-1995 rfirth Created 07-Mar-1995 madana --*/ #include #include #include "inetapiu.h" // because wininet doesnt know IStream #define NO_SHLWAPI_STREAM #include #include #include "autodial.h" // // public ..? // extern "C" { INTERNETAPI_(BOOL) InternetGetCertByURLA( IN LPSTR lpszURL, IN OUT LPSTR lpszCertText, OUT DWORD dwcbCertText ); } // // private manifests // // // private prototypes // PRIVATE DWORD ReadFile_Fsm( IN CFsm_ReadFile * Fsm ); PRIVATE DWORD ReadFileEx_Fsm( IN CFsm_ReadFileEx * Fsm ); PRIVATE VOID ReadFile_End( IN BOOL bDeref, IN BOOL bSuccess, IN HINTERNET hFileMapped, IN DWORD dwBytesRead, IN LPVOID lpBuffer OPTIONAL, IN DWORD dwNumberOfBytesToRead, OUT LPDWORD lpdwNumberOfBytesRead OPTIONAL ); PRIVATE DWORD QueryAvailable_Fsm( IN CFsm_QueryAvailable * Fsm ); PRIVATE DWORD wInternetCloseConnectA( IN HINTERNET lpConnectHandle, IN DWORD ServiceType ); PRIVATE DWORD GetEmailNameAndPassword( IN OUT LPSTR* lplpszUserName, IN OUT LPSTR* lplpszPassword, OUT LPSTR lpszEmailName, IN DWORD dwEmailNameLength ); PRIVATE BOOL InternetParseCommon( IN LPCTSTR lpszBaseUrl, IN LPCTSTR lpszRelativeUrl, OUT LPTSTR lpszBuffer, IN OUT LPDWORD lpdwBufferLength, IN DWORD dwFlags ); //PRIVATE //DWORD //CreateDeleteSocket( // VOID // ); BOOL GetWininetUserName( VOID ); // // functions // INTERNETAPI_(BOOL) InternetCrackUrlA( IN LPCSTR lpszUrl, IN DWORD dwUrlLength, IN DWORD dwFlags, IN LPURL_COMPONENTSA lpUrlComponents ) /*++ Routine Description: Cracks an URL into its constituent parts. Optionally escapes the url-path. We assume that the user has supplied large enough buffers for the various URL parts Arguments: lpszUrl - pointer to URL to crack dwUrlLength - 0 if lpszUrl is ASCIIZ string, else length of lpszUrl dwFlags - flags controlling operation lpUrlComponents - pointer to URL_COMPONENTS Return Value: BOOL Success - TRUE Failure - FALSE. Call GetLastError() for more info --*/ { DEBUG_ENTER_API((DBG_API, Bool, "InternetCrackUrlA", "%q, %#x, %#x, %#x", lpszUrl, dwUrlLength, dwFlags, lpUrlComponents )); DWORD error; // // validate parameters // if (ARGUMENT_PRESENT(lpszUrl)) { if (dwUrlLength == 0) { error = ProbeString((LPSTR)lpszUrl, &dwUrlLength); } else { error = ProbeReadBuffer((LPVOID)lpszUrl, dwUrlLength); } } else { error = ERROR_INVALID_PARAMETER; } if (error != ERROR_SUCCESS) { goto quit; } if ((lpUrlComponents == NULL) || (lpUrlComponents->dwStructSize != sizeof(*lpUrlComponents))) { error = ERROR_INVALID_PARAMETER; } else { error = ProbeWriteBuffer((LPVOID)lpUrlComponents, sizeof(*lpUrlComponents) ); } if (error != ERROR_SUCCESS) { goto quit; } // // we only allow two flags for this API // if (dwFlags & ~(ICU_ESCAPE | ICU_DECODE)) { error = ERROR_INVALID_PARAMETER; goto quit; } if (!GlobalDataInitialized) { error = GlobalDataInitialize(); if (error != ERROR_SUCCESS) { goto quit; } } // // get the individual components to return. If they reference a buffer then // check it for writeability // LPSTR lpUrl; LPSTR urlCopy; INTERNET_SCHEME schemeType; LPSTR schemeName; DWORD schemeNameLength; LPSTR hostName; DWORD hostNameLength; INTERNET_PORT nPort; LPSTR userName; DWORD userNameLength; LPSTR password; DWORD passwordLength; LPSTR urlPath; DWORD urlPathLength; LPSTR extraInfo; DWORD extraInfoLength; BOOL copyComponent; BOOL havePort; copyComponent = FALSE; schemeName = lpUrlComponents->lpszScheme; schemeNameLength = lpUrlComponents->dwSchemeLength; if ((schemeName != NULL) && (schemeNameLength != 0)) { error = ProbeWriteBuffer((LPVOID)schemeName, schemeNameLength); if (error != ERROR_SUCCESS) { goto quit; } *schemeName = '\0'; copyComponent = TRUE; } hostName = lpUrlComponents->lpszHostName; hostNameLength = lpUrlComponents->dwHostNameLength; if ((hostName != NULL) && (hostNameLength != 0)) { error = ProbeWriteBuffer((LPVOID)hostName, hostNameLength); if (error != ERROR_SUCCESS) { goto quit; } *hostName = '\0'; copyComponent = TRUE; } userName = lpUrlComponents->lpszUserName; userNameLength = lpUrlComponents->dwUserNameLength; if ((userName != NULL) && (userNameLength != 0)) { error = ProbeWriteBuffer((LPVOID)userName, userNameLength); if (error != ERROR_SUCCESS) { goto quit; } *userName = '\0'; copyComponent = TRUE; } password = lpUrlComponents->lpszPassword; passwordLength = lpUrlComponents->dwPasswordLength; if ((password != NULL) && (passwordLength != 0)) { error = ProbeWriteBuffer((LPVOID)password, passwordLength); if (error != ERROR_SUCCESS) { goto quit; } *password = '\0'; copyComponent = TRUE; } urlPath = lpUrlComponents->lpszUrlPath; urlPathLength = lpUrlComponents->dwUrlPathLength; if ((urlPath != NULL) && (urlPathLength != 0)) { error = ProbeWriteBuffer((LPVOID)urlPath, urlPathLength); if (error != ERROR_SUCCESS) { goto quit; } *urlPath = '\0'; copyComponent = TRUE; } extraInfo = lpUrlComponents->lpszExtraInfo; extraInfoLength = lpUrlComponents->dwExtraInfoLength; if ((extraInfo != NULL) && (extraInfoLength != 0)) { error = ProbeWriteBuffer((LPVOID)extraInfo, extraInfoLength); if (error != ERROR_SUCCESS) { goto quit; } *extraInfo = '\0'; copyComponent = TRUE; } // // we can only escape or decode the URL if the caller has provided us with // buffers to write the escaped strings into // if (dwFlags & (ICU_ESCAPE | ICU_DECODE)) { if (!copyComponent) { error = ERROR_INVALID_PARAMETER; goto quit; } // // create a copy of the URL. CrackUrl() will modify this in situ. We // need to copy the results back to the user's buffer(s) // urlCopy = NewString((LPSTR)lpszUrl, dwUrlLength); if (urlCopy == NULL) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } lpUrl = urlCopy; } else { lpUrl = (LPSTR)lpszUrl; urlCopy = NULL; } // // crack the URL into its constituent parts // error = CrackUrl(lpUrl, dwUrlLength, (dwFlags & ICU_ESCAPE) ? TRUE : FALSE, &schemeType, &schemeName, &schemeNameLength, &hostName, &hostNameLength, &nPort, &userName, &userNameLength, &password, &passwordLength, &urlPath, &urlPathLength, extraInfoLength ? &extraInfo : NULL, extraInfoLength ? &extraInfoLength : 0, &havePort ); if (error != ERROR_SUCCESS) { goto crack_error; } BOOL copyFailure; copyFailure = FALSE; // // update the URL_COMPONENTS structure based on the results, and what was // asked for // if (lpUrlComponents->lpszScheme != NULL) { if (lpUrlComponents->dwSchemeLength > schemeNameLength) { memcpy((LPVOID)lpUrlComponents->lpszScheme, (LPVOID)schemeName, schemeNameLength ); lpUrlComponents->lpszScheme[schemeNameLength] = '\0'; // Windows Bug 757146 // No longer unescaping the scheme. It's just wrong to do so. // if (dwFlags & ICU_DECODE) { // UrlUnescapeInPlace(lpUrlComponents->lpszScheme, 0); // } } else { ++schemeNameLength; copyFailure = TRUE; } lpUrlComponents->dwSchemeLength = schemeNameLength; } else if (lpUrlComponents->dwSchemeLength != 0) { lpUrlComponents->lpszScheme = schemeName; lpUrlComponents->dwSchemeLength = schemeNameLength; } if (lpUrlComponents->lpszHostName != NULL) { if (lpUrlComponents->dwHostNameLength > hostNameLength) { memcpy((LPVOID)lpUrlComponents->lpszHostName, (LPVOID)hostName, hostNameLength ); lpUrlComponents->lpszHostName[hostNameLength] = '\0'; // Windows Bug 757146 // No longer unescaping the host name if ICU_DECODE set. // It already has been unescaped once in CrackUrl above. // if (dwFlags & ICU_DECODE) { // UrlUnescapeInPlace(lpUrlComponents->lpszHostName, 0); // } } else { ++hostNameLength; copyFailure = TRUE; } lpUrlComponents->dwHostNameLength = hostNameLength; } else if (lpUrlComponents->dwHostNameLength != 0) { lpUrlComponents->lpszHostName = hostName; lpUrlComponents->dwHostNameLength = hostNameLength; } if (lpUrlComponents->lpszUserName != NULL) { if (lpUrlComponents->dwUserNameLength > userNameLength) { memcpy((LPVOID)lpUrlComponents->lpszUserName, (LPVOID)userName, userNameLength ); lpUrlComponents->lpszUserName[userNameLength] = '\0'; // Windows Bug 757146 // No longer unescaping the user name if ICU_DECODE set. // It already has been unescaped once in CrackUrl above. // if (dwFlags & ICU_DECODE) { // UrlUnescapeInPlace(lpUrlComponents->lpszUserName, 0); // } } else { ++userNameLength; copyFailure = TRUE; } lpUrlComponents->dwUserNameLength = userNameLength; } else if (lpUrlComponents->dwUserNameLength != 0) { lpUrlComponents->lpszUserName = userName; lpUrlComponents->dwUserNameLength = userNameLength; } if (lpUrlComponents->lpszPassword != NULL) { if (lpUrlComponents->dwPasswordLength > passwordLength) { memcpy((LPVOID)lpUrlComponents->lpszPassword, (LPVOID)password, passwordLength ); lpUrlComponents->lpszPassword[passwordLength] = '\0'; // Windows Bug 757146 // No longer unescaping the password if ICU_DECODE set. // It already has been unescaped once in CrackUrl above. // if (dwFlags & ICU_DECODE) { // UrlUnescapeInPlace(lpUrlComponents->lpszPassword, 0); // } } else { ++passwordLength; copyFailure = TRUE; } lpUrlComponents->dwPasswordLength = passwordLength; } else if (lpUrlComponents->dwPasswordLength != 0) { lpUrlComponents->lpszPassword = password; lpUrlComponents->dwPasswordLength = passwordLength; } if (lpUrlComponents->lpszUrlPath != NULL) { if(schemeType == INTERNET_SCHEME_FILE) { // // for file: urls we return the path component // as a valid dos path. // copyFailure = FAILED(PathCreateFromUrl(lpUrl, lpUrlComponents->lpszUrlPath, &(lpUrlComponents->dwUrlPathLength), 0)); } else if (lpUrlComponents->dwUrlPathLength > urlPathLength) { memcpy((LPVOID)lpUrlComponents->lpszUrlPath, (LPVOID)urlPath, urlPathLength ); lpUrlComponents->lpszUrlPath[urlPathLength] = '\0'; // Windows Bug 757146 // Unescape the path if only ICU_DECODE is set. // ICU_ESCAPE causes the path to be unescaped in // CrackUrl above. if ((dwFlags & (ICU_DECODE | ICU_ESCAPE)) == ICU_DECODE) { UrlUnescapeInPlace(lpUrlComponents->lpszUrlPath, 0); } lpUrlComponents->dwUrlPathLength = urlPathLength; } else { ++urlPathLength; copyFailure = TRUE; lpUrlComponents->dwUrlPathLength = urlPathLength; } } else if (lpUrlComponents->dwUrlPathLength != 0) { lpUrlComponents->lpszUrlPath = urlPath; lpUrlComponents->dwUrlPathLength = urlPathLength; } if (lpUrlComponents->lpszExtraInfo != NULL) { if (lpUrlComponents->dwExtraInfoLength > extraInfoLength) { memcpy((LPVOID)lpUrlComponents->lpszExtraInfo, (LPVOID)extraInfo, extraInfoLength ); lpUrlComponents->lpszExtraInfo[extraInfoLength] = '\0'; // Windows Bug 757146 // Unescape the extrainfo if only ICU_DECODE is set. // ICU_ESCAPE causes the extrainfo to be unescaped in // CrackUrl above. if ((dwFlags & (ICU_DECODE | ICU_ESCAPE)) == ICU_DECODE) { UrlUnescapeInPlace(lpUrlComponents->lpszExtraInfo, 0); } } else { ++extraInfoLength; copyFailure = TRUE; } lpUrlComponents->dwExtraInfoLength = extraInfoLength; } else if (lpUrlComponents->dwExtraInfoLength != 0) { lpUrlComponents->lpszExtraInfo = extraInfo; lpUrlComponents->dwExtraInfoLength = extraInfoLength; } // // we may have failed to copy one or more components because we didn't have // enough buffer space. // // N.B. Don't change error below here. If need be, move this test lower // if (copyFailure) { error = ERROR_INSUFFICIENT_BUFFER; } // // copy the scheme type // lpUrlComponents->nScheme = schemeType; // // convert 0 port (not in URL) to default value for scheme // if (nPort == INTERNET_INVALID_PORT_NUMBER && !havePort) { switch (schemeType) { case INTERNET_SCHEME_FTP: nPort = INTERNET_DEFAULT_FTP_PORT; break; case INTERNET_SCHEME_GOPHER: nPort = INTERNET_DEFAULT_GOPHER_PORT; break; case INTERNET_SCHEME_HTTP: nPort = INTERNET_DEFAULT_HTTP_PORT; break; case INTERNET_SCHEME_HTTPS: nPort = INTERNET_DEFAULT_HTTPS_PORT; break; } } lpUrlComponents->nPort = nPort; crack_error: if (urlCopy != NULL) { DEL_STRING(urlCopy); } quit: BOOL success = (error==ERROR_SUCCESS); if (!success) { DEBUG_ERROR(API, error); SetLastError(error); } DEBUG_LEAVE_API(success); return success; } INTERNETAPI_(BOOL) InternetCreateUrlA( IN LPURL_COMPONENTSA lpUrlComponents, IN DWORD dwFlags, OUT LPSTR lpszUrl OPTIONAL, IN OUT LPDWORD lpdwUrlLength ) /*++ Routine Description: Creates an URL from its constituent parts Arguments: lpUrlComponents - pointer to URL_COMPONENTS structure containing pointers and lengths of components of interest dwFlags - flags controlling function: ICU_ESCAPE - the components contain characters that must be escaped in the output URL lpszUrl - pointer to buffer where output URL will be written lpdwUrlLength - IN: number of bytes in lpszUrl buffer OUT: if success, number of characters in lpszUrl, else number of bytes required for buffer Return Value: BOOL Success - URL written to lpszUrl Failure - call GetLastError() for more info --*/ { DEBUG_ENTER_API((DBG_API, Bool, "InternetCreateUrlA", "%#x, %#x, %#x, %#x", lpUrlComponents, dwFlags, lpszUrl, lpdwUrlLength )); #if INET_DEBUG LPSTR lpszUrlOriginal = lpszUrl; #endif DWORD error = ERROR_SUCCESS; LPSTR encodedUserName = NULL; LPSTR encodedPassword = NULL; LPSTR encodedHostName = NULL; LPSTR encodedUrlPath = NULL; LPSTR encodedExtraInfo = NULL; // // validate parameters // if ((lpUrlComponents == NULL) || (lpUrlComponents->dwStructSize != sizeof(*lpUrlComponents)) || (dwFlags & ~(ICU_ESCAPE | ICU_USERNAME | ICU_ESCAPE_AUTHORITY)) || (lpdwUrlLength == NULL)) { error = ERROR_INVALID_PARAMETER; goto quit; } if (!ARGUMENT_PRESENT(lpszUrl)) { *lpdwUrlLength = 0; } // // allocate large buffers from heap // encodedHostName = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, INTERNET_MAX_HOST_NAME_LENGTH + 1); encodedUserName = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, INTERNET_MAX_USER_NAME_LENGTH + 1); encodedPassword = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, INTERNET_MAX_PASSWORD_LENGTH + 1); encodedUrlPath = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, INTERNET_MAX_URL_LENGTH + 1); encodedExtraInfo = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, INTERNET_MAX_URL_LENGTH + 1); if ((encodedHostName == NULL) || (encodedUserName == NULL) || (encodedPassword == NULL) || (encodedUrlPath == NULL) || (encodedExtraInfo == NULL) ) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } // // if we get an exception, we return ERROR_INVALID_PARAMETER // __try { // // get the individual components to copy // LPSTR schemeName; DWORD schemeNameLength; DWORD schemeFlags; LPSTR hostName; DWORD hostNameLength; DWORD encodedHostNameLength; INTERNET_PORT nPort; DWORD portLength; LPSTR userName; DWORD userNameLength; DWORD encodedUserNameLength; LPSTR password; DWORD passwordLength; DWORD encodedPasswordLength; LPSTR urlPath; DWORD urlPathLength; DWORD extraLength; DWORD encodedUrlPathLength; LPSTR extraInfo; DWORD extraInfoLength; DWORD encodedExtraInfoLength; LPSTR schemeSep; DWORD schemeSepLength; INTERNET_SCHEME schemeType; INTERNET_PORT defaultPort; // // if the scheme name is absent then we use the default // schemeName = lpUrlComponents->lpszScheme; schemeType = lpUrlComponents->nScheme; if (schemeName == NULL) { if (schemeType == INTERNET_SCHEME_DEFAULT){ schemeName = DEFAULT_URL_SCHEME_NAME; schemeNameLength = sizeof(DEFAULT_URL_SCHEME_NAME) - 1; } else { schemeName = MapUrlScheme(schemeType, &schemeNameLength); } } else { schemeNameLength = lpUrlComponents->dwSchemeLength; if (schemeNameLength == 0) { schemeNameLength = lstrlen(schemeName); } } // // determine the schemeFlags for possible use in encoding below // schemeFlags = 0; if (strnicmp(schemeName, "http", schemeNameLength) == 0) { schemeFlags = SCHEME_HTTP; } else if (strnicmp(schemeName, "ftp", schemeNameLength) == 0) { schemeFlags = SCHEME_FTP; } else if (strnicmp(schemeName, "gopher", schemeNameLength) == 0) { schemeFlags = SCHEME_GOPHER; } // // doesn't have to be a host name // hostName = lpUrlComponents->lpszHostName; portLength = 0; if (hostName != NULL) { hostNameLength = lpUrlComponents->dwHostNameLength; if (hostNameLength == 0) { hostNameLength = lstrlen(hostName); } // Windows Bugs 625684 // encode hostname if ICU_ESCAPE_AUTHORITY is set if (dwFlags & ICU_ESCAPE_AUTHORITY) { encodedHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH + 1; error = EncodeAuthorityComponent( hostName, hostNameLength, encodedHostName, &encodedHostNameLength ); if (error == ERROR_SUCCESS) { hostName = encodedHostName; hostNameLength = encodedHostNameLength; } } // // if the port is default then we don't add it to the URL, else we need to // copy it as a string // // there won't be a port unless there's host. schemeType = MapUrlSchemeName(schemeName, schemeNameLength ? schemeNameLength : -1); switch (schemeType) { case INTERNET_SCHEME_FTP: defaultPort = INTERNET_DEFAULT_FTP_PORT; break; case INTERNET_SCHEME_GOPHER: defaultPort = INTERNET_DEFAULT_GOPHER_PORT; break; case INTERNET_SCHEME_HTTP: defaultPort = INTERNET_DEFAULT_HTTP_PORT; break; case INTERNET_SCHEME_HTTPS: defaultPort = INTERNET_DEFAULT_HTTPS_PORT; break; default: defaultPort = INTERNET_INVALID_PORT_NUMBER; break; } if (lpUrlComponents->nPort != defaultPort) { INTERNET_PORT divisor; nPort = lpUrlComponents->nPort; if (nPort) { divisor = 10000; portLength = 6; // max is 5 characters, plus 1 for ':' while ((nPort / divisor) == 0) { --portLength; divisor /= 10; } } else { portLength = 2; // port is ":0" } } } else { hostNameLength = 0; } // // doesn't have to be a user name // if (error == ERROR_SUCCESS) { userName = lpUrlComponents->lpszUserName; if (userName != NULL) { userNameLength = lpUrlComponents->dwUserNameLength; if (userNameLength == 0) { userNameLength = lstrlen(userName); } // Windows Bugs 625684 // encode hostname if ICU_ESCAPE_AUTHORITY is set if (dwFlags & ICU_ESCAPE_AUTHORITY) { encodedUserNameLength = INTERNET_MAX_USER_NAME_LENGTH + 1; error = EncodeAuthorityComponent( userName, userNameLength, encodedUserName, &encodedUserNameLength ); if (error == ERROR_SUCCESS) { userName = encodedUserName; userNameLength = encodedUserNameLength; } } } else { // // BUGBUG - if ICU_USERNAME then we get the value from the registry // userNameLength = 0; } } // // doesn't have to be a password // if (error == ERROR_SUCCESS) { password = lpUrlComponents->lpszPassword; if (password != NULL) { passwordLength = lpUrlComponents->dwPasswordLength; if (passwordLength == 0) { passwordLength = lstrlen(password); } // Windows Bugs 625684 // encode hostname if ICU_ESCAPE_AUTHORITY is set if (dwFlags & ICU_ESCAPE_AUTHORITY) { encodedPasswordLength = INTERNET_MAX_PASSWORD_LENGTH + 1; error = EncodeAuthorityComponent( password, passwordLength, encodedPassword, &encodedPasswordLength ); if (error == ERROR_SUCCESS) { password = encodedPassword; passwordLength = encodedPasswordLength; } } } else { // // BUGBUG - if ICU_USERNAME then we get the value from the registry // passwordLength = 0; } } // // but if there's a password without a user name, then its an error // if (error == ERROR_SUCCESS) { if (password && !userName) { error = ERROR_INVALID_PARAMETER; } else { // // doesn't have to be an URL-path. Empty string is default // urlPath = lpUrlComponents->lpszUrlPath; if (urlPath != NULL) { urlPathLength = lpUrlComponents->dwUrlPathLength; if (urlPathLength == 0) { urlPathLength = lstrlen(urlPath); } if ((*urlPath != '/') && (*urlPath != '\\')) { extraLength = 1; } else { extraLength = 0; } // // if requested, we will encode the URL-path // if (dwFlags & ICU_ESCAPE) { // // only encode the URL-path if it's a recognized scheme // if (schemeFlags != 0) { encodedUrlPathLength = INTERNET_MAX_URL_LENGTH + 1; error = EncodeUrlPath(NO_ENCODE_PATH_SEP, schemeFlags, urlPath, urlPathLength, encodedUrlPath, &encodedUrlPathLength ); if (error == ERROR_SUCCESS) { urlPath = encodedUrlPath; urlPathLength = encodedUrlPathLength; } } } } else { urlPathLength = 0; extraLength = 0; } // // handle extra info if present // if (error == ERROR_SUCCESS) { extraInfo = lpUrlComponents->lpszExtraInfo; if (extraInfo != NULL) { extraInfoLength = lpUrlComponents->dwExtraInfoLength; if (extraInfoLength == 0) { extraInfoLength = lstrlen(extraInfo); } // // if requested, we will encode the extra info // if (dwFlags & ICU_ESCAPE) { // // only encode the extra info if it's a recognized scheme // if (schemeFlags != 0) { encodedExtraInfoLength = INTERNET_MAX_URL_LENGTH + 1; error = EncodeUrlPath(0, schemeFlags, extraInfo, extraInfoLength, encodedExtraInfo, &encodedExtraInfoLength ); if (error == ERROR_SUCCESS) { extraInfo = encodedExtraInfo; extraInfoLength = encodedExtraInfoLength; } } } } else { extraInfoLength = 0; } } DWORD requiredSize; if (error == ERROR_SUCCESS) { // // Determine if we have a protocol scheme that requires slashes // if (DoesSchemeRequireSlashes(schemeName, schemeNameLength, (hostName != NULL)) || ((schemeType == INTERNET_SCHEME_NEWS) && urlPath && (strchr(urlPath, '/') || strchr(urlPath, '\\')))) { schemeSep = "://"; schemeSepLength = sizeof("://") - 1; } else { schemeSep = ":"; schemeSepLength = sizeof(":") - 1; } // // ensure we have enough buffer space // requiredSize = schemeNameLength + schemeSepLength + hostNameLength + portLength + (userName ? userNameLength + 1 : 0) // +1 for '@' + (password ? passwordLength + 1 : 0) // +1 for ':' + urlPathLength + extraLength + extraInfoLength + 1 // +1 for '\0' ; // // if there is enough buffer, copy the URL // if (*lpdwUrlLength >= requiredSize) { memcpy((LPVOID)lpszUrl, (LPVOID)schemeName, schemeNameLength); lpszUrl += schemeNameLength; memcpy((LPVOID)lpszUrl, (LPVOID)schemeSep, schemeSepLength); lpszUrl += schemeSepLength; if (userName) { memcpy((LPVOID)lpszUrl, (LPVOID)userName, userNameLength); lpszUrl += userNameLength; if (password) { *lpszUrl++ = ':'; memcpy((LPVOID)lpszUrl, (LPVOID)password, passwordLength); lpszUrl += passwordLength; } *lpszUrl++ = '@'; } if (hostName) { memcpy((LPVOID)lpszUrl, (LPVOID)hostName, hostNameLength); lpszUrl += hostNameLength; // We won't attach a port unless there's a host to go with it. if (portLength) { lpszUrl += wsprintf(lpszUrl, ":%d", nPort & 0xffff); } } if (urlPath) { // // Only do extraLength if we've actually copied something // after the scheme. Also, don't copy slash if it's // mailto: // if (extraLength != 0 && (userName || hostName || portLength) && schemeType != INTERNET_SCHEME_MAILTO) { *lpszUrl++ = '/'; } else if (extraLength != 0) { --requiredSize; } memcpy((LPVOID)lpszUrl, (LPVOID)urlPath, urlPathLength); lpszUrl += urlPathLength; } else if (extraLength != 0) { --requiredSize; } if (extraInfo) { memcpy((LPVOID)lpszUrl, (LPVOID)extraInfo, extraInfoLength); lpszUrl += extraInfoLength; } // // terminate string // *lpszUrl = '\0'; // // -1 for terminating '\0' // --requiredSize; } else { // // not enough buffer space - just return the required buffer length // error = ERROR_INSUFFICIENT_BUFFER; } } // // update returned parameters // *lpdwUrlLength = requiredSize; } } } __except(EXCEPTION_EXECUTE_HANDLER) { error = ERROR_INVALID_PARAMETER; } ENDEXCEPT quit: // // clear up the buffers we allocated // if (encodedUserName != NULL) { FREE_MEMORY(encodedUserName); } if (encodedPassword != NULL) { FREE_MEMORY(encodedPassword); } if (encodedHostName != NULL) { FREE_MEMORY(encodedHostName); } if (encodedUrlPath != NULL) { FREE_MEMORY(encodedUrlPath); } if (encodedExtraInfo != NULL) { FREE_MEMORY(encodedExtraInfo); } BOOL success = (error==ERROR_SUCCESS); if (success) { DEBUG_PRINT_API(API, INFO, ("URL = %q\n", lpszUrlOriginal )); } else { DEBUG_ERROR(API, error); SetLastError(error); } DEBUG_LEAVE_API(success); return success; } // // ICUHrToWin32Error() is specifically for converting the return codes for // Url* APIs in shlwapi into win32 errors. // WARNING: it should not be used for any other purpose. // DWORD ICUHrToWin32Error(HRESULT hr) { DWORD err = ERROR_INVALID_PARAMETER; switch(hr) { case E_OUTOFMEMORY: err = ERROR_NOT_ENOUGH_MEMORY; break; case E_POINTER: err = ERROR_INSUFFICIENT_BUFFER; break; case S_OK: err = ERROR_SUCCESS; break; default: break; } return err; } INTERNETAPI_(BOOL) InternetCanonicalizeUrlA( IN LPCSTR lpszUrl, OUT LPSTR lpszBuffer, IN OUT LPDWORD lpdwBufferLength, IN DWORD dwFlags ) /*++ Routine Description: Combines a relative URL with a base URL to form a new full URL. Arguments: lpszUrl - pointer to URL to be canonicalize lpszBuffer - pointer to buffer where new URL is written lpdwBufferLength - size of buffer on entry, length of new URL on exit dwFlags - flags controlling operation Return Value: BOOL - TRUE if successful, FALSE if not --*/ { DEBUG_ENTER_API((DBG_API, Bool, "InternetCanonicalizeUrlA", "%q, %#x, %#x [%d], %#x", lpszUrl, lpszBuffer, lpdwBufferLength, lpdwBufferLength ? *lpdwBufferLength : 0, dwFlags )); HRESULT hr ; BOOL bRet = TRUE;; INET_ASSERT(lpszUrl); INET_ASSERT(lpszBuffer); INET_ASSERT(lpdwBufferLength && (*lpdwBufferLength > 0)); // // the flags for the Url* APIs in shlwapi should be the same // except that NO_ENCODE is on by default. so we need to flip it // dwFlags ^= ICU_NO_ENCODE; // Check for invalid parameters if (!lpszUrl || !lpszBuffer || !lpdwBufferLength || *lpdwBufferLength == 0 || IsBadWritePtr(lpszBuffer, *lpdwBufferLength*sizeof(CHAR))) { hr = E_INVALIDARG; } else { hr = UrlCanonicalizeA(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags | URL_WININET_COMPATIBILITY); } if(FAILED(hr)) { DWORD dw = ICUHrToWin32Error(hr); bRet = FALSE; DEBUG_ERROR(API, dw); SetLastError(dw); } DEBUG_LEAVE_API(bRet); return bRet; } INTERNETAPI_(BOOL) InternetCombineUrlA( IN LPCSTR lpszBaseUrl, IN LPCSTR lpszRelativeUrl, OUT LPSTR lpszBuffer, IN OUT LPDWORD lpdwBufferLength, IN DWORD dwFlags ) /*++ Routine Description: Combines a relative URL with a base URL to form a new full URL. Arguments: lpszBaseUrl - pointer to base URL lpszRelativeUrl - pointer to relative URL lpszBuffer - pointer to buffer where new URL is written lpdwBufferLength - size of buffer on entry, length of new URL on exit dwFlags - flags controlling operation Return Value: BOOL - TRUE if successful, FALSE if not --*/ { DEBUG_ENTER_API((DBG_API, Bool, "InternetCombineUrlA", "%q, %q, %#x, %#x [%d], %#x", lpszBaseUrl, lpszRelativeUrl, lpszBuffer, lpdwBufferLength, lpdwBufferLength ? *lpdwBufferLength : 0, dwFlags )); HRESULT hr ; BOOL bRet; INET_ASSERT(lpszBaseUrl); INET_ASSERT(lpszRelativeUrl); INET_ASSERT(lpdwBufferLength); // // the flags for the Url* APIs in shlwapi should be the same // except that NO_ENCODE is on by default. so we need to flip it // dwFlags ^= ICU_NO_ENCODE; // Check for invalid parameters if (!lpszBaseUrl || !lpszRelativeUrl || !lpdwBufferLength || (lpszBuffer && IsBadWritePtr(lpszBuffer, *lpdwBufferLength*sizeof(CHAR)))) { hr = E_INVALIDARG; } else { hr = UrlCombineA(lpszBaseUrl, lpszRelativeUrl, lpszBuffer, lpdwBufferLength, dwFlags | URL_WININET_COMPATIBILITY); } if(FAILED(hr)) { DWORD dw = ICUHrToWin32Error(hr); bRet = FALSE; DEBUG_ERROR(API, dw); SetLastError(dw); } else bRet = TRUE; IF_DEBUG_CODE() { if (bRet) { DEBUG_PRINT_API(API, INFO, ("URL = %q\n", lpszBuffer )); } } DEBUG_LEAVE_API(bRet); return bRet; } INTERNETAPI_(HINTERNET) InternetOpenA( IN LPCSTR lpszAgent, IN DWORD dwAccessType, IN LPCSTR lpszProxy OPTIONAL, IN LPCSTR lpszProxyBypass OPTIONAL, IN DWORD dwFlags ) /*++ Routine Description: Opens a root Internet handle from which all HINTERNET objects are derived Arguments: lpszAgent - name of the application making the request (arbitrary identifying string). Used in "User-Agent" header when communicating with HTTP servers, if the application does not add a User-Agent header of its own dwAccessType - type of access required. Can be INTERNET_OPEN_TYPE_PRECONFIG - Gets the configuration from the registry INTERNET_OPEN_TYPE_DIRECT - Requests are made directly to the nominated server INTERNET_OPEN_TYPE_PROXY - Requests are made via the nominated proxy INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY - Like Pre-Config, but prevents JavaScript, INS and other auto-proxy types from being used. lpszProxy - if INTERNET_OPEN_TYPE_PROXY, a list of proxy servers to use lpszProxyBypass - if INTERNET_OPEN_TYPE_PROXY, a list of servers which we will communicate with directly dwFlags - flags to control the operation of this API or potentially all APIs called on the handle generated by this API. Currently supported are: INTERNET_FLAG_ASYNC - if specified then all subsequent API calls made against the handle returned from this API, or handles descended from the handle returned by this API, have the opportunity to complete asynchronously, depending on other factors relevant at the time the API is called Return Value: HINTERNET Success - handle of Internet object Failure - NULL. For more information, call GetLastError() --*/ { PERF_INIT(); DEBUG_ENTER_API((DBG_API, Handle, "InternetOpenA", "%q, %s (%d), %q, %q, %#x", lpszAgent, InternetMapOpenType(dwAccessType), dwAccessType, lpszProxy, lpszProxyBypass, dwFlags )); DWORD error; HINTERNET hInternet = NULL; if (!GlobalDataInitialized) { error = GlobalDataInitialize(); if (error != ERROR_SUCCESS) { goto quit; } } // // we are doing GetUserName here instead of in DLL_PROCESS_ATTACH // As every caller of wininet has to do this first, we ensure // that the username is initialized when they get to actually doing // any real operation // GetWininetUserName(); // // validate parameters // if (! ( (dwAccessType == INTERNET_OPEN_TYPE_DIRECT) || (dwAccessType == INTERNET_OPEN_TYPE_PROXY) || (dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG) || (dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY) || ( (dwAccessType == INTERNET_OPEN_TYPE_PROXY) && ( !ARGUMENT_PRESENT(lpszProxy) || (*lpszProxy == '\0') ) ) || (dwFlags & ~INTERNET_FLAGS_MASK) ) ) { error = ERROR_INVALID_PARAMETER; goto quit; } GlobalHaveInternetOpened = TRUE; // // Initalize an auto proxy dll if needed, // as long as the caller is allowing us free rein to do this // by calling us with INTERNET_OPEN_TYPE_PRECONFIG. // //if ( dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG ) //{ // if ( ! InitalizeAutoConfigDllIfNeeded() ) // { // error = GetLastError(); // // INET_ASSERT(error != ERROR_SUCCESS); // // goto quit; // } // // INTERNET_HANDLE_OBJECT * lpInternet; lpInternet = new INTERNET_HANDLE_OBJECT(lpszAgent, dwAccessType, (LPSTR)lpszProxy, (LPSTR)lpszProxyBypass, dwFlags ); if (lpInternet == NULL) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } error = lpInternet->GetStatus(); if (error == ERROR_SUCCESS) { hInternet = (HINTERNET)lpInternet; // // success - don't return the object address, return the pseudo-handle // value we generated // hInternet = ((HANDLE_OBJECT *)hInternet)->GetPseudoHandle(); // // start async support now if required. If we can't start it, we'll get // another chance the next time we create an async request // if (dwFlags & INTERNET_FLAG_ASYNC) { InitializeAsyncSupport(); } } else { // // hack fix to stop InternetIndicateStatus (called from the handle // object destructor) blowing up if there is no handle object in the // thread info block. We can't call back anyway // LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo) { // // BUGBUG - incorrect handle value // _InternetSetObjectHandle(lpThreadInfo, lpInternet, lpInternet); } // // we failed during initialization. Kill the handle using Dereference() // (in order to stop the debug version complaining about the reference // count not being 0. Invalidate for same reason) // lpInternet->Invalidate(); lpInternet->Dereference(); INET_ASSERT(hInternet == NULL); } quit: if (error != ERROR_SUCCESS) { DEBUG_ERROR(API, error); SetLastError(error); } DEBUG_LEAVE_API(hInternet); return hInternet; } INTERNETAPI_(BOOL) InternetCloseHandle( IN HINTERNET hInternet ) /*++ Routine Description: Closes any open internet handle object Arguments: hInternet - handle of internet object to close Return Value: BOOL Success - TRUE Failure - FALSE. For more information call GetLastError() --*/ { DEBUG_ENTER_API((DBG_API, Bool, "InternetCloseHandle", "%#x", hInternet )); PERF_ENTER(InternetCloseHandle); DWORD error; BOOL success = FALSE; HINTERNET hInternetMapped = NULL; static DWORD ticks = GetTickCountWrap(); if (!GlobalDataInitialized) { error = GlobalDataInitialize(); if (error != ERROR_SUCCESS) { goto quit; } } // // map the handle. Don't invalidate it (_InternetCloseHandle() does this) // error = MapHandleToAddress(hInternet, (LPVOID *)&hInternetMapped, FALSE); if (error != ERROR_SUCCESS) { if (hInternetMapped == NULL) { // // the handle never existed or has been completely destroyed // DEBUG_PRINT(API, ERROR, ("Handle %#x is invalid\n", hInternet )); // // catch invalid handles - may help caller // DEBUG_BREAK(INVALID_HANDLES); } else { // // this handle is already being closed (it's invalidated). We only // need one InternetCloseHandle() operation to invalidate the handle. // All other threads will simply dereference the handle, and // eventually it will be destroyed // DereferenceObject((LPVOID)hInternetMapped); } goto quit; } // // the handle is not invalidated // HANDLE_OBJECT * pHandle; pHandle = (HANDLE_OBJECT *)hInternetMapped; if ( ! ((INTERNET_HANDLE_OBJECT *)hInternetMapped)->IsAsyncHandle() ) { if ((GetTickCountWrap() - ticks) >= 5000) { PurgeServerInfoList(FALSE); InterlockedExchange((LPLONG) &ticks, (LONG) GetTickCountWrap()); } } DEBUG_PRINT(INET, INFO, ("handle %#x == %#x == %s\n", hInternet, hInternetMapped, InternetMapHandleType(pHandle->GetHandleType()) )); // // if this is an http request handle, notify all filters. // if (pHandle->GetHandleType() == TypeHttpRequestHandle) { HttpFiltOnTransactionComplete (hInternet); } // // if this is a delete-parent-with-child subtree then find the root node // while (pHandle->GetDeleteWithChild()) { HINTERNET handleObject; handleObject = pHandle->GetParent(); INET_ASSERT(handleObject != NULL); // // remove the delete-parent-with-child indication, or we'll get stuck // in a loop // pHandle->SetParent(handleObject, FALSE); // // if the parent handle is an EXISTING_CONNECT connect handle then we // just mark it unused & close the current handle // HINTERNET_HANDLE_TYPE handleType; handleType = ((HANDLE_OBJECT *)handleObject)->GetHandleType(); if ((handleType == TypeFtpConnectHandle) || (handleType == TypeGopherConnectHandle) || (handleType == TypeHttpConnectHandle)) { INTERNET_CONNECT_HANDLE_OBJECT * pConnect; pConnect = (INTERNET_CONNECT_HANDLE_OBJECT *)handleObject; // // SetUnused() will only operate on a connect handle object that // has been created with INTERNET_FLAG_EXISTING_CONNECT // if (pConnect->SetUnused()) { // // only handle type should be FTP connect handle for now // INET_ASSERT(handleType == TypeFtpConnectHandle); DEBUG_PRINT(INET, INFO, ("caching unused %s connect handle object %#x. RefCount = %d\n", (handleType == TypeFtpConnectHandle) ? "FTP" : (handleType == TypeGopherConnectHandle) ? "Gopher" : "HTTP", ((HANDLE_OBJECT *)handleObject)->GetPseudoHandle(), ((HANDLE_OBJECT *)handleObject)->ReferenceCount() )); break; } } pHandle = (HANDLE_OBJECT *)handleObject; hInternet = pHandle->GetPseudoHandle(); } // // close all child handles first // while (pHandle->HaveChildren()) { // // we'll fall out at the first error we get. It *should* mean that this // handle (and its descendents) is already being closed // if (!InternetCloseHandle(pHandle->NextChild())) { break; } } // // clear the handle object last error variables // InternetClearLastError(); // // remove the reference added by MapHandleToAddress(), or the handle won't // be destroyed by _InternetCloseHandle() // DereferenceObject((LPVOID)hInternetMapped); // // use _InternetCloseHandle() to do the work // success = _InternetCloseHandle(hInternet); quit: // SetLastError must be called after PERF_LEAVE !!! PERF_LEAVE(InternetCloseHandle); if (error != ERROR_SUCCESS) { DEBUG_ERROR(API, error); SetLastError(error); } DEBUG_LEAVE_API(success); return success; } BOOL _InternetCloseHandle( IN HINTERNET hInternet ) /*++ Routine Description: Same as InternetCloseHandle() except does not clear out the last error text. Mainly for FTP Arguments: hInternet - handle of internet object to close Return Value: BOOL Success - TRUE Failure - FALSE. For more information call GetLastError() --*/ { DEBUG_ENTER((DBG_INET, Bool, "_InternetCloseHandle", "%#x", hInternet )); DWORD error; BOOL success; HINTERNET hInternetMapped = NULL; LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo == NULL) { if (InDllCleanup) { error = ERROR_INTERNET_SHUTDOWN; } else { INET_ASSERT(FALSE); error = ERROR_INTERNET_INTERNAL_ERROR; } goto quit; } // // map the handle and invalidate it. This will cause any new requests with // the handle as a parameter to fail // error = MapHandleToAddress(hInternet, (LPVOID *)&hInternetMapped, TRUE); if (error != ERROR_SUCCESS) { if (hInternetMapped != NULL) { // // the handle is already being closed, or is already deleted // DereferenceObject((LPVOID)hInternetMapped); } // // since this is the only function that can invalidate a handle, if we // are here then the handle is just waiting for its refcount to go to // zero. We already removed the refcount we added above, so we're in // the clear // goto quit; } // // there may be an active socket operation. We close the socket to abort the // operation // ((INTERNET_HANDLE_OBJECT *)hInternetMapped)->AbortSocket(); // // we need the parent handle - we will set this as the handle object being // processed by this thread. This is required for async worker threads (see // below) // HINTERNET hParent; HINTERNET hParentMapped; DWORD_PTR dwParentContext; hParentMapped = ((HANDLE_OBJECT *)hInternetMapped)->GetParent(); if (hParentMapped != NULL) { hParent = ((HANDLE_OBJECT *)hParentMapped)->GetPseudoHandle(); dwParentContext = ((HANDLE_OBJECT *)hParentMapped)->GetContext(); } // // set the object handle and context in the per-thread data structure // _InternetSetObjectHandle(lpThreadInfo, hInternet, hInternetMapped); _InternetSetContext(lpThreadInfo, ((INTERNET_HANDLE_OBJECT *)hInternetMapped)->GetContext() ); // // at this point, there should *always* be at least 2 references on the // handle - one added when the object was created, and one added by // MapHandleToAddress() above. If the object is still alive after the 2 // dereferences, then it will be destroyed when the current owning thread // dereferences it // (void)DereferenceObject((LPVOID)hInternetMapped); error = DereferenceObject((LPVOID)hInternetMapped); // // now set the object to be the parent. This is necessary for e.g. // FtpGetFile() and async requests (where the async worker thread will make // an extra callback to deliver the results of the async request) // if (hParentMapped != NULL) { _InternetSetObjectHandle(lpThreadInfo, hParent, hParentMapped); if (dwParentContext != 0) { _InternetSetContext(lpThreadInfo, dwParentContext); } } if (g_bHibernating) { InterruptSelect(); } // // if the handle was still alive after dereferencing it then we will inform // the app that the close is pending // quit: // // if the handle is still alive then we return success - it is invalidated // and will be deleted as soon as possible // if (error == ERROR_INTERNET_HANDLE_EXISTS) { error = ERROR_SUCCESS; } success = (error==ERROR_SUCCESS); if (!success) { SetLastError(error); DEBUG_ERROR(INET, error); } DEBUG_LEAVE(success); return success; } DWORD _InternetCloseHandleNoContext( IN HINTERNET hInternet ) /*++ Routine Description: Same as _InternetCloseHandle() except does not change the per-thread info structure handle/context values BUGBUG - This should be handled via a parameter to _InternetCloseHandle(), but its close to shipping... Arguments: hInternet - handle of internet object to close Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_INVALID_HANDLE --*/ { DEBUG_ENTER((DBG_INET, Bool, "_InternetCloseHandleNoContext", "%#x", hInternet )); DWORD error; HINTERNET hInternetMapped = NULL; // // map the handle and invalidate it. This will cause any new requests with // the handle as a parameter to fail // error = MapHandleToAddress(hInternet, (LPVOID *)&hInternetMapped, TRUE); if (error != ERROR_SUCCESS) { if (hInternetMapped != NULL) { // // the handle is already being closed, or is already deleted // DereferenceObject((LPVOID)hInternetMapped); } // // since this is the only function that can invalidate a handle, if we // are here then the handle is just waiting for its refcount to go to // zero. We already removed the refcount we added above, so we're in // the clear // goto quit; } // // there may be an active socket operation. We close the socket to abort the // operation // ((INTERNET_HANDLE_OBJECT *)hInternetMapped)->AbortSocket(); // // at this point, there should *always* be at least 2 references on the // handle - one added when the object was created, and one added by // MapHandleToAddress() above. If the object is still alive after the 2 // dereferences, then it will be destroyed when the current owning thread // dereferences it // (void)DereferenceObject((LPVOID)hInternetMapped); error = DereferenceObject((LPVOID)hInternetMapped); quit: // // if the handle is still alive then we return success - it is invalidated // and will be deleted as soon as possible // if (error == ERROR_INTERNET_HANDLE_EXISTS) { error = ERROR_SUCCESS; } DEBUG_LEAVE(error); return error; } INTERNETAPI_(BOOL) InternetGetCertByURLA( IN LPSTR lpszURL, IN OUT LPSTR lpszCertText, OUT DWORD dwcbCertText ) /*++ Routine Description: Does a high-level lookup against the Certificate Cache. Searches by URL (broken down into hostname) for the Certificate, and returns it in a formatted (&localized) string. Arguments: lpszUrl - pointer to URL to crack lpszCertText - Output of formatted certifcate dwcbCertText - Size of lpszCertText Return Value: BOOL Success - TRUE Failure - FALSE. Call GetLastError() for more info --*/ { BOOL fSuccess = FALSE; /* LPSTR lpszHostName; DWORD dwcbHostName; INTERNET_CERTIFICATE_INFO cInfo; CHAR chBackup; DWORD error = ERROR_SUCCESS; ZeroMemory(&cInfo, sizeof(INTERNET_CERTIFICATE_INFO)); error = CrackUrl(lpszURL, lstrlen(lpszURL), FALSE, NULL, // Scheme Type NULL, // Scheme Name NULL, // Scheme Length &lpszHostName, // Host Name &dwcbHostName, // Host Length NULL, // Internet Port NULL, // UserName NULL, // UserName Length NULL, // Password NULL, // Password Lenth NULL, // Path NULL, // Path Length NULL, // Extra Info NULL, // Extra Info Length NULL ); if ( error != ERROR_SUCCESS) goto quit; chBackup = lpszHostName[dwcbHostName]; lpszHostName[dwcbHostName] = '\0'; fSuccess = GlobalCertCache.GetCert( lpszHostName, &cInfo ); lpszHostName[dwcbHostName] = chBackup; if ( ! fSuccess ) { error = ERROR_INTERNET_INVALID_OPERATION; goto quit; } LPSTR szResult; szResult = FormatCertInfo(&cInfo); if ( ! szResult ) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } DWORD dwcbResult; dwcbResult = lstrlen(szResult); if ( dwcbCertText < (dwcbResult+1) ) { error = ERROR_INSUFFICIENT_BUFFER; goto quit; } memcpy( lpszCertText, szResult, (dwcbResult + 1) * sizeof(TCHAR)); quit: if (NULL != szResult) { FREE_MEMORY(szResult); } if (NULL != cInfo.lpszSubjectInfo) { FREE_MEMORY(cInfo.lpszSubjectInfo); } if (NULL != cInfo.lpszIssuerInfo) { FREE_MEMORY(cInfo.lpszIssuerInfo); } if (NULL != cInfo.lpszSignatureAlgName) { FREE_MEMORY(cInfo.lpszSignatureAlgName); } if (NULL != cInfo.lpszEncryptionAlgName) { FREE_MEMORY(cInfo.lpszEncryptionAlgName); } if (NULL != cInfo.lpszProtocolName) { FREE_MEMORY(cInfo.lpszProtocolName); } fSuccess = TRUE; if ( error != ERROR_SUCCESS ) { fSuccess = FALSE; SetLastError(error); }*/ return fSuccess; } INTERNETAPI_(BOOL) InternetShowSecurityInfoByURLA( IN LPSTR lpszURL, IN HWND hwndRootWindow ) /*++ Routine Description: Does a high-level lookup against the Certificate Cache. Searches by URL (broken down into hostname) for the Certificate, and returns it in a formatted (&localized) string. Arguments: lpszUrl - pointer to URL to crack lpszCertText - Output of formatted certifcate dwcbCertText - Size of lpszCertText Return Value: BOOL Success - TRUE Failure - FALSE. Call GetLastError() for more info --*/ { DEBUG_ENTER_API((DBG_INET, Bool, "InternetShowSecurityInfoA", "%q %#x", lpszURL, hwndRootWindow )); LPSTR lpszHostName; DWORD dwcbHostName; INTERNET_SECURITY_INFO cInfo; CHAR chBackup; DWORD dwFlags; DWORD error = ERROR_SUCCESS; WCHAR szTitle[MAX_PATH]; WCHAR szMessage[MAX_PATH]; INTERNET_SCHEME ustSchemeType; BOOL fResult = FALSE; if (!GlobalDataInitialized) { if (GlobalDataInitialize() != ERROR_SUCCESS) { goto Cleanup; } } ZeroMemory(&cInfo, sizeof(INTERNET_SECURITY_INFO)); error = CrackUrl(lpszURL, lstrlen(lpszURL), FALSE, &ustSchemeType, NULL, // Scheme Name NULL, // Scheme Length &lpszHostName, // Host Name &dwcbHostName, // Host Length NULL, // Internet Port NULL, // UserName NULL, // UserName Length NULL, // Password NULL, // Password Lenth NULL, // Path NULL, // Path Length NULL, // Extra Info NULL, // Extra Info Length NULL ); if ( error != ERROR_SUCCESS) { goto Cleanup; } if ( ustSchemeType != INTERNET_SCHEME_HTTPS ) { goto Cleanup; } if ( lpszHostName == NULL || dwcbHostName == 0 ) { fResult = TRUE; goto done; } chBackup = lpszHostName[dwcbHostName]; lpszHostName[dwcbHostName] = '\0'; SECURITY_CACHE_LIST_ENTRY *pEntry; pEntry = GlobalCertCache.Find(lpszHostName); lpszHostName[dwcbHostName] = chBackup; if(pEntry) { pEntry->CopyOut(cInfo); pEntry->Release(); ShowSecurityInfo(hwndRootWindow, &cInfo); CertFreeCertificateContext(cInfo.pCertificate); fResult = TRUE; goto done; } Cleanup: // No certificate info, display messagebox. LoadStringWrapW( GlobalDllHandle, IDS_NOCERT_TITLE, szTitle, sizeof(szTitle) / sizeof(szTitle[0])); LoadStringWrapW( GlobalDllHandle, IDS_NOCERT, szMessage, sizeof(szMessage) / sizeof(szMessage[0])); MessageBoxWrapW(hwndRootWindow, szMessage, szTitle, MB_ICONINFORMATION | MB_OK); done: DEBUG_LEAVE_API(fResult); return fResult; } INTERNETAPI_(HINTERNET) InternetConnectA( IN HINTERNET hInternet, IN LPCSTR lpszServerName, IN INTERNET_PORT nServerPort, IN LPCSTR lpszUserName OPTIONAL, IN LPCSTR lpszPassword OPTIONAL, IN DWORD dwService, IN DWORD dwFlags, IN DWORD_PTR dwContext ) /*++ Routine Description: Opens a connection with a server, logging-on the user in the process. Arguments: hInternet - Internet handle, returned by InternetOpen() lpszServerName - name of server with which to connect nServerPort - port at which server listens lpszUserName - name of current user lpszPassword - password of current user dwService - service required. Controls type of handle generated. May be one of: - INTERNET_SERVICE_FTP - INTERNET_SERVICE_GOPHER - INTERNET_SERVICE_HTTP dwFlags - protocol-specific flags. The following are defined: - INTERNET_FLAG_PASSIVE (FTP) - INTERNET_FLAG_KEEP_CONNECTION (HTTP) - INTERNET_FLAG_SECURE (HTTP) dwContext - application-supplied value used to identify this request in callbacks Return Value: HINTERNET Success - address of a new handle object Failure - NULL. Call GetLastError() for more info --*/ { DEBUG_ENTER_API((DBG_API, Handle, "InternetConnectA", "%#x, %q, %d, %q, %q, %s (%d), %#08x, %#x", hInternet, lpszServerName, nServerPort, lpszUserName, lpszPassword, InternetMapService(dwService), dwService, dwFlags, dwContext )); char emailName[INTERNET_MAX_HOST_NAME_LENGTH + 1]; char proxyBuf[INTERNET_MAX_HOST_NAME_LENGTH + 1]; HINTERNET connectHandle = NULL; HINTERNET hInternetMapped = NULL; HINTERNET hObject; HINTERNET hObjectMapped = NULL; LPINTERNET_THREAD_INFO lpThreadInfo; BOOL fUseProxy = FALSE; LPSTR serverName = NULL; LPSTR userName = (LPSTR)lpszUserName; LPSTR password = (LPSTR)lpszPassword; LPSTR realServerName = (LPSTR)lpszServerName; LPSTR realUserName = (LPSTR)lpszUserName; BOOL existingConnection = FALSE; BOOL viaProxy = FALSE; INTERNET_CONNECT_HANDLE_OBJECT * pConnect = NULL; //CServerInfo * lpServerInfo; BOOL bProtocolLevel = !(dwFlags & INTERNET_FLAG_OFFLINE); BOOL bIsWorker = FALSE; BOOL bNonNestedAsync = FALSE; BOOL isLocal; BOOL isAsync; BOOL bFTPSetPerUserItem = FALSE; DWORD error = ERROR_SUCCESS; if (!GlobalDataInitialized) { error = ERROR_INTERNET_NOT_INITIALIZED; goto done; } // // get the per-thread info block // lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo == NULL) { INET_ASSERT(FALSE); error = ERROR_INTERNET_INTERNAL_ERROR; goto done; } _InternetIncNestingCount(); bIsWorker = lpThreadInfo->IsAsyncWorkerThread; bNonNestedAsync = bIsWorker && (lpThreadInfo->NestedRequests == 1); // // handle any global proxy settings changes first // if (InternetSettingsChanged()) { ChangeGlobalSettings(); } // // handle/refcount munging: // // sync: // map hInternet on input (+1 ref) // generate connect handle (1 ref) // if failure && !connect handle // close connect handle (0 refs: delete) // if success // deref hInternet (-1 ref) // else if going async // ref connect handle (2 refs) // // async: // hInternet is mapped connect handle (2 refs) // get real hInternet from connect handle parent (2 refs (e.g.)) // deref connect handle (1 ref) // if failure // close connect handle (0 refs: delete) // deref open handle (-1 ref) // // N.B. the final deref of the *indicated* handle on async callback will // happen in the async code // if (bNonNestedAsync) { connectHandle = hInternet; hInternetMapped = ((HANDLE_OBJECT *)connectHandle)->GetParent(); hInternet = ((HANDLE_OBJECT *)hInternetMapped)->GetPseudoHandle(); } else { error = MapHandleToAddress(hInternet, (LPVOID *)&hInternetMapped, FALSE); if ((error != ERROR_SUCCESS) && (hInternetMapped == NULL)) { goto quit; } // // set the info context and clear the last error info // _InternetSetObjectHandle(lpThreadInfo, hInternet, hInternetMapped); _InternetClearLastError(lpThreadInfo); _InternetSetContext(lpThreadInfo, dwContext); // // quit now if the handle object is invalidated // if (error != ERROR_SUCCESS) { goto quit; } // // validate the handle & discover local/remote & sync/async // error = RIsHandleLocal(hInternetMapped, &isLocal, &isAsync, TypeInternetHandle ); if (error != ERROR_SUCCESS) { goto quit; } // // we allow all valid flags to be passed in // if ((dwFlags & ~INTERNET_FLAGS_MASK) || (lpszServerName == NULL) || (*lpszServerName == '\0')) { error = ERROR_INVALID_PARAMETER; goto quit; } } INTERNET_SCHEME schemeType; switch (dwService) { case INTERNET_SERVICE_FTP: schemeType = INTERNET_SCHEME_FTP; break; case INTERNET_SERVICE_HTTP: schemeType = (dwFlags & INTERNET_FLAG_SECURE) ? INTERNET_SCHEME_HTTPS : INTERNET_SCHEME_HTTP; break; case INTERNET_SERVICE_GOPHER: // disable gopher by default if (GlobalEnableGopher) { schemeType = INTERNET_SCHEME_GOPHER; break; } default: error = ERROR_INVALID_PARAMETER; goto quit; } // // validate arguments if we're not in the async thread context, in which // case we did this when the original request was made // if (bNonNestedAsync) { pConnect = (INTERNET_CONNECT_HANDLE_OBJECT *)connectHandle; goto sync_path; } // // app thread or in async worker thread but being called from another // async API, such as InternetOpenUrl() // // // special case: if the server name is the NULL pointer or empty string, and // the port is 0 AND we have a proxy configured for this protocol then the // app is asking to connect directly to the proxy (the proxy server itself // had better be in the bypass list!) // // BUGBUG - not sure if this is really where we want to do this // INTERNET_HANDLE_OBJECT * lpInternet; lpInternet = (INTERNET_HANDLE_OBJECT * )hInternetMapped; // // if the port value is 0 convert it to the default port for the // protocol // if (nServerPort == INTERNET_INVALID_PORT_NUMBER) { switch (dwService) { case INTERNET_SERVICE_FTP: nServerPort = INTERNET_DEFAULT_FTP_PORT; break; case INTERNET_SERVICE_GOPHER: nServerPort = INTERNET_DEFAULT_GOPHER_PORT; break; case INTERNET_SERVICE_HTTP: if (dwFlags & INTERNET_FLAG_SECURE) { nServerPort = INTERNET_DEFAULT_HTTPS_PORT; } else { nServerPort = INTERNET_DEFAULT_HTTP_PORT; } break; } } // // if we have been given a net (i.e. IP) address, try to convert it to the // corresponding host name // //if (IsNetAddress((LPSTR)lpszServerName)) { //lpszServerName = (LPCSTR)MapNetAddressToName((LPSTR)lpszServerName); realServerName = (LPSTR)lpszServerName; //} // // we need to get the username and password for the current user before we // make the connection proper. The reason for this is that if we leave it // to the server, it will end up with a username of "SYSTEM" for all // anonymous FTP connects // if (dwService == INTERNET_SERVICE_FTP) { // // Make sure we have the correct Proxy-Network Settings, at this point. // InternetAutodialIfNotLocalHost(NULL, (LPSTR) lpszServerName); // // check the user name & password. If NULLs were supplied, use the values // from the registry // // // Do we need to set this item as per user? If a username was supplied ( // either cracked from the URL or set on the handle, userName will be // non-null. Currently, this is the only criteria for when we set pu. // // Need to check this BEFORE calling GetEmailNameAndPassword which will // plugin "anonymous" if no username provided. // bFTPSetPerUserItem = userName ? TRUE : FALSE; DEBUG_PRINT(FTP, INFO, ("InternetConnectA:FTP: bFTPSetPerUserItem = %d\n", bFTPSetPerUserItem )); error = GetEmailNameAndPassword(&userName, &password, emailName, sizeof(emailName) ); if (error != ERROR_SUCCESS) { goto quit; } // // this is the user name we will use for the object, i.e. either the // name supplied, or "anonymous" as mapped above // realUserName = userName; // // if this request is going via an FTP proxy, then convert the parameters // now. We convert the username to @, the password // remains the same, and the server name & port become the proxy server // name and port // // N.B. We ONLY do this once on the initial (synchronous) path // AUTO_PROXY_ASYNC_MSG proxyInfoQuery( INTERNET_SCHEME_FTP, (LPSTR)lpszServerName, lstrlen((LPSTR)lpszServerName) ); AUTO_PROXY_ASYNC_MSG *pProxyInfoQuery; pProxyInfoQuery = &proxyInfoQuery; proxyInfoQuery.SetAvoidAsyncCall(TRUE); error = lpInternet->GetProxyInfo( &pProxyInfoQuery ); if ( error != ERROR_SUCCESS ) { goto quit; } if ( proxyInfoQuery.IsUseProxy() ) { if (proxyInfoQuery.GetProxyScheme() == INTERNET_SCHEME_FTP) { int ulen = lstrlen(userName); int slen = lstrlen(lpszServerName); INET_ASSERT((ulen + slen) < (sizeof(proxyBuf) - 1)); if ((ulen + slen) < (sizeof(proxyBuf) - 1)) { memcpy(proxyBuf, userName, ulen); proxyBuf[ulen++] = '@'; memcpy(&proxyBuf[ulen], lpszServerName, slen + 1); // // keep a pointer to the real user name for when we // create the object // realUserName = userName; userName = proxyBuf; // // create a copy of the proxy name. We have to do // this in case the current proxy list is replaced // while we are using this string // // // N.B. we can't be here if we determined that the // proxy server was the destination (i.e. we mapped2 // the empty server name above) // INET_ASSERT(serverName == NULL); serverName = NewString((LPCSTR)proxyInfoQuery._lpszProxyHostName); if ( serverName == NULL ) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } // // keep a pointer to the real (origin) server name // for when we create the object // realServerName = (LPSTR)lpszServerName; lpszServerName = serverName; // // BUGBUG - what if proxyPort != nServerPort? Where // should the port go (user@server:port?) // nServerPort = proxyInfoQuery._nProxyHostPort; // // this request will go via proxy // viaProxy = TRUE; } else { // // blew internal limit // error = ERROR_INTERNET_INTERNAL_ERROR; goto quit; } } } } else { if (userName != NULL) { if (IsBadStringPtr(userName, INTERNET_MAX_USER_NAME_LENGTH)) { error = ERROR_INVALID_PARAMETER; goto quit; } else if (*userName == '\0') { userName = NULL; } } if (password != NULL) { if (IsBadStringPtr(password, INTERNET_MAX_PASSWORD_LENGTH)) { error = ERROR_INVALID_PASSWORD; goto quit; } else if (*password == '\0') { password = NULL; } } } // // find the handle object if EXISTING_CONNECT AND we are creating protocol- // level connections, else create it // INET_ASSERT(connectHandle == NULL); INET_ASSERT(error == ERROR_SUCCESS); if ((dwFlags & INTERNET_FLAG_EXISTING_CONNECT) && bProtocolLevel) { connectHandle = FindExistingConnectObject(hInternet, realServerName, nServerPort, realUserName, password, dwService, dwFlags, dwContext ); } if (connectHandle != NULL) { existingConnection = TRUE; } else { // // turn off INTERNET_FLAG_EXISTING_CONNECT if we are creating a cache // handle - we don't want the handle to hang around after we delete // the request handle (i.e. be set unused by InternetCloseHandle()). // N.B. We don't need this flag after this operation, so its safe to // remove it from dwFlags // if (!bProtocolLevel) { dwFlags &= ~INTERNET_FLAG_EXISTING_CONNECT; } error = RMakeInternetConnectObjectHandle( hInternetMapped, &connectHandle, (CONNECT_CLOSE_HANDLE_FUNC)wInternetCloseConnectA, realServerName, // origin server, not proxy nServerPort, realUserName, // just user name, not user@server password, dwService, dwFlags, dwContext ); if (error != ERROR_SUCCESS) { goto quit; } } // // this new handle will be used in callbacks // _InternetSetObjectHandle(lpThreadInfo, ((HANDLE_OBJECT *)connectHandle)->GetPseudoHandle(), connectHandle ); // // based on whether we have been asked to perform async I/O AND we are not // in an async worker thread context AND the request is to connect with an // FTP service (currently only FTP because this request performs network // I/O - gopher and HTTP just allocate & fill in memory) AND there is a // valid context value, we will queue the async request, or execute the // request synchronously // // // BUGBUG - GetFlags() // if ((lpInternet->GetInternetOpenFlags() | dwFlags) & INTERNET_FLAG_OFFLINE) { error = ERROR_SUCCESS; goto quit; } pConnect = (INTERNET_CONNECT_HANDLE_OBJECT *)connectHandle; //lpServerInfo = pConnect->GetServerInfo(); DEBUG_PRINT(FTP, INFO, ("bIsWorker = %d isAsync = %d dwContext %s INC dwService %s ISF bProtocolLevel = %d\n", bIsWorker, isAsync, (dwContext == INTERNET_NO_CALLBACK) ? "==":"!=", (dwService == INTERNET_SERVICE_FTP) ? "==":"!=", bProtocolLevel)); if (!bIsWorker && isAsync && (dwContext != INTERNET_NO_CALLBACK) && ((dwService == INTERNET_SERVICE_FTP) ? bProtocolLevel : FALSE)) { // If we determined item should be set pu, do so now pConnect->SetPerUserItem(bFTPSetPerUserItem); DEBUG_PRINT(FTP, INFO, ("InternetConnectA:Async Path: SetPerUserItem to %\r\n\ \r\n", pConnect->IsPerUserItem(), pConnect, connectHandle)); CFsm_FtpConnect * pFsm; pFsm = new CFsm_FtpConnect(lpszServerName, userName, password, nServerPort, dwService, dwFlags, dwContext ); if (pFsm != NULL && pFsm->GetError() == ERROR_SUCCESS) { BOOL bDerefConnect = TRUE; error = pConnect->Reference(); if (error == ERROR_ACCESS_DENIED) { bDerefConnect = FALSE; } else if (error == ERROR_SUCCESS) { error = pFsm->QueueWorkItem(); if (error == ERROR_IO_PENDING) { hInternetMapped = NULL; bDerefConnect = FALSE; } } if (bDerefConnect) { pConnect->Dereference(); } } else { error = ERROR_NOT_ENOUGH_MEMORY; if ( pFsm ) { error = pFsm->GetError(); delete pFsm; pFsm = NULL; } } // // if we're here then ERROR_SUCCESS cannot have been returned from // the above calls // INET_ASSERT(error != ERROR_SUCCESS); DEBUG_PRINT(FTP, INFO, ("processing request asynchronously: error = %d\n", error )); goto quit; } sync_path: if (bProtocolLevel && !existingConnection) { // // generate the protocol-level connect 'object' if required (for FTP). // This simply creates a memory object // HINTERNET protocolConnectHandle = NULL; INET_ASSERT(error == ERROR_SUCCESS); if (dwService == INTERNET_SERVICE_FTP) { error = wFtpConnect(lpszServerName, nServerPort, userName, password, dwService, dwFlags, &protocolConnectHandle ); if (error != ERROR_SUCCESS) { goto quit; } } // // associate the protocol-level handle and INTERNET_CONNECT_HANDLE_OBJECT // pConnect->SetConnectHandle(protocolConnectHandle); // If we determined item should be set pu, do so now pConnect->SetPerUserItem(bFTPSetPerUserItem); DEBUG_PRINT(FTP, INFO, ("InternetConnectA:Sync Path:SetPerUserItem to %d\r\n\ \r\n", pConnect->IsPerUserItem(), pConnect, protocolConnectHandle, connectHandle)); // for all connect types, get the server info and resolve the server // name. If we can't resolve the name then we fail this request // //lpServerInfo = pConnect->GetServerInfo(); //if ((lpServerInfo != NULL) && !lpServerInfo->IsNameResolved()) { // error = pConnect->SetServerInfo(schemeType, FALSE, FALSE); // if (error != ERROR_SUCCESS) { // goto quit; // } //} // // if we succeeded in creating the connect object and this is an FTP // request then we will now attempt to connect to the server proper. // // We don't need to do this for gopher and HTTP because (currently) they // don't make server connections until we perform some other action, // like find file, e.g. // if (dwService == INTERNET_SERVICE_FTP) { error = wFtpMakeConnection(protocolConnectHandle, userName, password ); } } quit: _InternetDecNestingCount(1); // // free the buffer we used to hold the proxy name if we had to map the // empty string to a proxy name // if (serverName != NULL) { DEL_STRING(serverName); } done: if (error == ERROR_SUCCESS) { // // set the via proxy flag // ((INTERNET_CONNECT_HANDLE_OBJECT *)connectHandle)->SetViaProxy(viaProxy); // // success - return generated pseudo-handle // connectHandle = ((HANDLE_OBJECT *)connectHandle)->GetPseudoHandle(); // // created a handle. If we are generating protocol-level connections // then flush the existing connection cache // if (bProtocolLevel) { FlushExistingConnectObjects(hInternet); } } else { if (bNonNestedAsync && (/*((HANDLE_OBJECT *)connectHandle)->Dereference() ||*/ ((HANDLE_OBJECT *)connectHandle)->IsInvalidated())) { error = ERROR_INTERNET_OPERATION_CANCELLED; } // // if we are not pending an async request but we created a handle object // then close it // if ((error != ERROR_IO_PENDING) && (connectHandle != NULL)) { // // use _InternetCloseHandle() to close the handle: it doesn't clear // out the last error text, so that an app can find out what the // server sent us in the event of an FTP login failure // if (bNonNestedAsync) { // // this handle deref'd at async completion // hInternetMapped = NULL; } else { _InternetCloseHandle(((HANDLE_OBJECT *)connectHandle)->GetPseudoHandle()); } } connectHandle = NULL; } if (hInternetMapped != NULL) { DereferenceObject((LPVOID)hInternetMapped); } if (error != ERROR_SUCCESS) { DEBUG_ERROR(API, error); SetLastError(error); } DEBUG_LEAVE_API(connectHandle); return connectHandle; } INTERNETAPI_(HINTERNET) InternetOpenUrlA( IN HINTERNET hInternet, IN LPCSTR lpszUrl, IN LPCSTR lpszHeaders OPTIONAL, IN DWORD dwHeadersLength, IN DWORD dwFlags, IN DWORD_PTR dwContext ) /*++ Routine Description: Opens an URL. This consists of creating a handle to the type of item identified by the URL - directory or file Arguments: hInternet - root Internet handle lpszUrl - pointer to the URL to use to open the item lpszHeaders - headers to send to HTTP server. May be NULL dwHeadersLength - length of lpszHeaders. May be -1 if the app wants us to perform the strlen() dwFlags - open flags (cache/nocache, etc.) dwContext - app-supplied context value for call-backs Return Value: HINTERNET Success - open handle to item described by URL Failure - NULL. Use GetLastError() to get more information about why the call failed. There may be error text returned from the server (in the case of a gopher or FTP URL) --*/ { DEBUG_ENTER_API((DBG_API, Handle, "InternetOpenUrlA", "%#x, %q, %.80q, %d, %#08x, %#x", hInternet, lpszUrl, lpszHeaders, dwHeadersLength, dwFlags, dwContext )); LPINTERNET_THREAD_INFO lpThreadInfo = NULL; DWORD error; DWORD nestingLevel = 0; HINTERNET hInternetMapped = NULL; HINTERNET hUrlMapped = NULL; HINTERNET hUrl = NULL; PROXY_STATE * pProxyState = NULL; BOOL bDeref = TRUE; if (!GlobalDataInitialized) { error = ERROR_INTERNET_NOT_INITIALIZED; goto quit; } // // get the thread info block // lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo == NULL) { INET_ASSERT(FALSE); error = ERROR_INTERNET_INTERNAL_ERROR; goto quit; } _InternetIncNestingCount(); nestingLevel = 1; // // map the handle // error = MapHandleToAddress(hInternet, (LPVOID *)&hInternetMapped, FALSE); if ((error != ERROR_SUCCESS) && (hInternetMapped == NULL)) { goto quit; } // // set the info context and clear the last error info // _InternetSetObjectHandle(lpThreadInfo, hInternet, hInternetMapped); _InternetClearLastError(lpThreadInfo); _InternetSetContext(lpThreadInfo, dwContext); // // quit now if the handle object is invalidated // if (error != ERROR_SUCCESS) { goto quit; } // // validate the handle & discover async/sync & local/remote // BOOL isLocal; BOOL isAsync; error = RIsHandleLocal(hInternetMapped, &isLocal, &isAsync, TypeInternetHandle ); if (error != ERROR_SUCCESS) { goto quit; } // // validate parameters if we're not in the async worker thread context // if (!lpThreadInfo->IsAsyncWorkerThread) { // // ensure we have good values for the headers pointer and length // INET_ASSERT(error == ERROR_SUCCESS); if (ARGUMENT_PRESENT(lpszHeaders) && (dwHeadersLength == -1)) { __try { dwHeadersLength = lstrlen(lpszHeaders); } __except(EXCEPTION_EXECUTE_HANDLER) { error = ERROR_INVALID_PARAMETER; } ENDEXCEPT if (error != ERROR_SUCCESS) { goto quit; } } else if (!ARGUMENT_PRESENT(lpszHeaders) || (dwHeadersLength == 0)) { lpszHeaders = NULL; dwHeadersLength = 0; } if (!ARGUMENT_PRESENT(lpszUrl) || (*lpszUrl == '\0') // || !IsValidUrl(lpszUrl) || (dwFlags & ~INTERNET_FLAGS_MASK)) { error = ERROR_INVALID_PARAMETER; goto quit; } } // // determine if this is a http request or FTP request - either http URL, or via http proxy. // or via another protocol. // For any Async request, we must go async in order to determine proxy information // bDeref = FALSE; hUrl = hInternet; if (isAsync && !lpThreadInfo->IsAsyncWorkerThread && (dwContext != INTERNET_NO_CALLBACK)) { CFsm_ParseUrlForHttp *pFsm; pFsm = new CFsm_ParseUrlForHttp(&hUrl, (INTERNET_HANDLE_OBJECT *)hInternetMapped, lpszUrl, lpszHeaders, dwHeadersLength, dwFlags, dwContext ); if (pFsm != NULL) { // IE6 BUG #27905 // check if ctor of CFsm_ParseUrlForHttp encountered an error error = pFsm->GetError(); if (error == ERROR_SUCCESS) { // first call will not be on this API's thread context pFsm->ClearOnApiCall(); error = pFsm->QueueWorkItem(); } else { delete pFsm; } } else { error = ERROR_NOT_ENOUGH_MEMORY; } } else { bDeref = TRUE; CFsm_ParseUrlForHttp *pFsm; pFsm = new CFsm_ParseUrlForHttp(&hUrl, (INTERNET_HANDLE_OBJECT *)hInternetMapped, lpszUrl, lpszHeaders, dwHeadersLength, dwFlags, dwContext ); if (pFsm != NULL) { // IE6 BUG #27905 // check if ctor of CFsm_ParseUrlForHttp encountered an error error = pFsm->GetError(); if (error == ERROR_SUCCESS) { error = DoFsm(pFsm); } else { delete pFsm; } } else { error = ERROR_NOT_ENOUGH_MEMORY; } } if ( error == ERROR_IO_PENDING ) { bDeref = FALSE; } quit: if (bDeref && hInternetMapped != NULL) { DereferenceObject((LPVOID)hInternetMapped); } if ( lpThreadInfo != NULL ) { _InternetDecNestingCount(nestingLevel); } if (error != ERROR_SUCCESS) { DEBUG_ERROR(API, error); SetLastError(error); hUrl = NULL; } DEBUG_LEAVE_API(hUrl); return hUrl; } INTERNETAPI_(BOOL) InternetReadFile( IN HINTERNET hFile, IN LPVOID lpBuffer, IN DWORD dwNumberOfBytesToRead, OUT LPDWORD lpdwNumberOfBytesRead ) /*++ Routine Description: This functions reads the next block of data from the file object. The following handle/data types are supported: TypeGopherFileHandle - raw gopher file data TypeGopherFileHandleHtml - HTML-encapsulated gopher file data TypeGopherFindHandleHtml - HTML-encapsulated gopher directory data TypeFtpFileHandle - raw FTP file data TypeFtpFileHandleHtml - HTML-encapsulated FTP file data TypeFtpFindHandleHtml - HTML-encapsulated FTP directory data Arguments: hFile - handle returned from Open function lpBuffer - pointer to caller's buffer dwNumberOfBytesToRead - size of lpBuffer in BYTEs lpdwNumberOfBytesRead - returned number of bytes read into lpBuffer Return Value: BOOL Success - TRUE Failure - FALSE. Call GetLastError() for more info --*/ { DEBUG_ENTER_API((DBG_API, Bool, "InternetReadFile", "%#x, %#x, %d, %#x", hFile, lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead )); LPINTERNET_THREAD_INFO lpThreadInfo; DWORD nestingLevel = 0; DWORD error; BOOL success = FALSE; HINTERNET hFileMapped = NULL; DWORD bytesRead = 0; BOOL bEndRead = TRUE; HINTERNET_HANDLE_TYPE handleType = TypeWildHandle; if (!GlobalDataInitialized) { error = ERROR_INTERNET_NOT_INITIALIZED; goto done; } // // we need the thread info block // lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo == NULL) { INET_ASSERT(FALSE); error = ERROR_INTERNET_INTERNAL_ERROR; goto done; } //INET_ASSERT(lpThreadInfo->Fsm == NULL); _InternetIncNestingCount(); nestingLevel = 1; // // map the handle // error = MapHandleToAddress(hFile, (LPVOID *)&hFileMapped, FALSE); if ((error != ERROR_SUCCESS) && (hFileMapped == NULL)) { goto quit; } // // set the context, handle, and last-error info in the per-thread data block // before we go any further. This allows us to return a status in the async // case, even if the handle has been closed // DWORD_PTR context; RGetContext(hFileMapped, &context); if (!lpThreadInfo->IsAsyncWorkerThread) { PERF_LOG(PE_CLIENT_REQUEST_START, AR_INTERNET_READ_FILE, lpThreadInfo->ThreadId, hFile ); } _InternetSetContext(lpThreadInfo, context); _InternetSetObjectHandle(lpThreadInfo, hFile, hFileMapped); _InternetClearLastError(lpThreadInfo); // // if MapHandleToAddress() returned a non-NULL object address, but also an // error status, then the handle is being closed - quit // if (error != ERROR_SUCCESS) { goto quit; } // // validate handle and retrieve its type // error = RGetHandleType(hFileMapped, &handleType); if (error != ERROR_SUCCESS) { goto quit; } BOOL isLocal; BOOL isAsync; error = RIsHandleLocal(hFileMapped, &isLocal, &isAsync, handleType); if (error != ERROR_SUCCESS) { // // we should not get an error - we already believe the handle object // is valid and of the type just retrieved! // INET_ASSERT(FALSE); goto quit; } // // ensure correct handle type // if ((handleType != TypeHttpRequestHandle) && (handleType != TypeFtpFileHandle) && (handleType != TypeGopherFileHandle) && (handleType != TypeFtpFindHandleHtml) && (handleType != TypeGopherFindHandleHtml) && (handleType != TypeFtpFileHandleHtml) && (handleType != TypeGopherFileHandleHtml) && (handleType != TypeFileRequestHandle)) { error = ERROR_INTERNET_INCORRECT_HANDLE_TYPE; goto quit; } // // validate parameters // if (!lpThreadInfo->IsAsyncWorkerThread) { error = ProbeAndSetDword(lpdwNumberOfBytesRead, 0); if (error != ERROR_SUCCESS) { goto quit; } error = ProbeWriteBuffer(lpBuffer, dwNumberOfBytesToRead); if (error != ERROR_SUCCESS) { goto quit; } *lpdwNumberOfBytesRead = 0; if (((handleType == TypeFtpFindHandleHtml) || (handleType == TypeGopherFindHandleHtml)) && ((INTERNET_CONNECT_HANDLE_OBJECT *)hFileMapped)-> IsCacheReadInProgress()) { error = ((INTERNET_CONNECT_HANDLE_OBJECT *)hFileMapped)-> ReadCache((LPBYTE)lpBuffer, dwNumberOfBytesToRead, &bytesRead); success = (error == ERROR_SUCCESS); goto quit; } if (handleType == TypeHttpRequestHandle) { HTTP_REQUEST_HANDLE_OBJECT *lpRequest = (HTTP_REQUEST_HANDLE_OBJECT *) hFileMapped; // See if request can be fulfilled from file system. if (lpRequest->AttemptReadFromFile (lpBuffer, dwNumberOfBytesToRead, &bytesRead)) { success = TRUE; goto quit; } } // end if (handleType == TypeHttpRequestHandle) else { // // trap a zero-length buffer before we go to the trouble of going async. // Maintain compatibility with base ReadFile(), although this is // POTENTIALLY A BUG. ReadFile() is *supposed* to return TRUE and number // of bytes read equal to zero to indicate end-of-file, but it will also // return TRUE and zero if a read of zero bytes is requested. According // to MarkL, that's the way it is. Good enough for me... // // For http, AttemptToReadFromFile traps zero-length reads if (dwNumberOfBytesToRead == 0) { // // *lpdwNumberOfBytesRead and error should have been correctly set // during parameter validation // INET_ASSERT(*lpdwNumberOfBytesRead == 0); INET_ASSERT(error == ERROR_SUCCESS); success = TRUE; goto quit; } } // end else (handleType != TypeHttpRequestHandle) } // end if (!lpThreadInfo->IsAsyncWorkerThread) // // the request will only be made asynchronously if more data is requested // than is immediately available AND we haven't reached end of file // DWORD available; available = ((INTERNET_HANDLE_OBJECT *)hFileMapped)->AvailableDataLength(); BOOL eof; eof = ((INTERNET_HANDLE_OBJECT *)hFileMapped)->IsEndOfFile(); if (!lpThreadInfo->IsAsyncWorkerThread && isAsync && (context != INTERNET_NO_CALLBACK) && (dwNumberOfBytesToRead > available) && !eof && (handleType != TypeHttpRequestHandle) && (handleType != TypeFileRequestHandle)) { // MakeAsyncRequest CFsm_InternetReadFile * pFsm; pFsm = new CFsm_InternetReadFile(hFile, lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead); if (pFsm != NULL) { error = pFsm->QueueWorkItem(); if ( error == ERROR_IO_PENDING ) { bEndRead = FALSE; } } else { error = ERROR_NOT_ENOUGH_MEMORY; } // // if we're here then ERROR_SUCCESS cannot have been returned from // the above calls // INET_ASSERT(error != ERROR_SUCCESS); DEBUG_PRINT(FTP, INFO, ("processing request asynchronously: error = %d\n", error )); goto quit; // // we're going synchronous - set the error so we do the right thing when // we exit // error = ERROR_SUCCESS; } else if ((available >= dwNumberOfBytesToRead) || eof) { DEBUG_PRINT(API, INFO, ("immediate read: %d requested, %d available. EOF = %B\n", dwNumberOfBytesToRead, available, eof )); } INET_ASSERT(error == ERROR_SUCCESS); // // just call the underlying API: return whatever it returns, and let it // handle setting the last error // switch (handleType) { case TypeFtpFileHandle: success = FtpReadFile(hFileMapped, lpBuffer, dwNumberOfBytesToRead, &bytesRead ); break; case TypeGopherFileHandle: success = GopherReadFile(hFileMapped, lpBuffer, dwNumberOfBytesToRead, &bytesRead ); break; case TypeFtpFindHandleHtml: case TypeGopherFindHandleHtml: // // HTML handle types - convert underlying data to HTML document // success = ReadHtmlUrlData(hFileMapped, lpBuffer, dwNumberOfBytesToRead, &bytesRead ); if (((INTERNET_CONNECT_HANDLE_OBJECT *)hFileMapped)->IsCacheWriteInProgress()) { DWORD errorCache; if (success) { if (bytesRead) { errorCache = ((INTERNET_CONNECT_HANDLE_OBJECT *)hFileMapped)-> WriteCache( (LPBYTE)lpBuffer, bytesRead ); } else { errorCache = ERROR_NO_MORE_FILES; } } else { errorCache = GetLastError(); } // if the thing failed because the caller passed in // insufficient buffer for internetreadfile // then we should do nothing if ((errorCache != ERROR_SUCCESS)&& (errorCache != ERROR_INSUFFICIENT_BUFFER)) { if (handleType == TypeFtpFindHandleHtml) { // we save extension in the index file // this is used to differentiate between html directory // entry from the non-html one for the same url InbLocalEndCacheWrite( hFileMapped, "htm", // save extension in index file (errorCache == ERROR_NO_MORE_FILES) ); } else { InbGopherLocalEndCacheWrite( hFileMapped, "htm", (errorCache == ERROR_NO_MORE_FILES) ); } } } break; case TypeFtpFileHandleHtml: case TypeGopherFileHandleHtml: // // HTML handle types - convert underlying data to HTML document // success = ReadHtmlUrlData(hFileMapped, lpBuffer, dwNumberOfBytesToRead, &bytesRead ); break; case TypeHttpRequestHandle: { //HTTP_REQUEST_HANDLE_OBJECT * lpRequest; // //lpRequest = (HTTP_REQUEST_HANDLE_OBJECT *)hFileMapped; // //error = lpRequest->QuickSyncRead( // lpBuffer, // dwNumberOfBytesToRead, // lpdwNumberOfBytesRead, // 0 // ); // //if ( error == ERROR_IO_PENDING ) //{ error = DoFsm(new CFsm_ReadFile(lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead )); //} success = (error == ERROR_SUCCESS) ? TRUE : FALSE; bEndRead = FALSE; break; } case TypeFileRequestHandle: success = ReadFile( ((INTERNET_FILE_HANDLE_OBJECT *) hFileMapped)->GetFileHandle(), lpBuffer, dwNumberOfBytesToRead, &bytesRead, NULL // overlapped I/O ); if (!success) { error = GetLastError(); } else { error = ERROR_SUCCESS; } break; case TypeFtpFindHandle: case TypeGopherFindHandle: // // you cannot receive RAW directory data using this API. You have // to call InternetFindNextFile() // default: // // the handle is a valid handle (or else RGetHandleType() would // have returned ERROR_INVALID_HANDLE), but this operation is // inconsistent with the handle type. Return a more prosaic error // code // error = ERROR_INTERNET_INVALID_OPERATION; break; } quit: _InternetDecNestingCount(nestingLevel);; if (bEndRead) { // // if handleType is not HttpRequest or File then we are making this // request in the context of an uninterruptable async worker thread. // HTTP and file requests use the normal mechanism. In the case of non- // HTTP and file requests, we need to treat the request as if it were // sync and deref the handle // ReadFile_End(!lpThreadInfo->IsAsyncWorkerThread || !((handleType == TypeHttpRequestHandle) || (handleType == TypeFileRequestHandle)), success, hFileMapped, bytesRead, lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead ); } if (lpThreadInfo && !lpThreadInfo->IsAsyncWorkerThread) { PERF_LOG(PE_CLIENT_REQUEST_END, AR_INTERNET_READ_FILE, bytesRead, lpThreadInfo->ThreadId, hFile ); } done: // // if error is not ERROR_SUCCESS then this function returning the error, // otherwise the error has already been set by the API we called, // irrespective of the value of success // if (error != ERROR_SUCCESS) { DEBUG_ERROR(API, error); SetLastError(error); success = FALSE; } DEBUG_LEAVE_API(success); return success; } PRIVATE VOID ReadFile_End( IN BOOL bDeref, IN BOOL bSuccess, IN HINTERNET hFileMapped, IN DWORD dwBytesRead, IN LPVOID lpBuffer OPTIONAL, IN DWORD dwNumberOfBytesToRead, OUT LPDWORD lpdwNumberOfBytesRead OPTIONAL ) /*++ Routine Description: Common end-of-read processing: - update bytes read parameter - dump data if logging & API data requested - dereference handle if not async request Arguments: bDeref - TRUE if handle should be dereferenced (should be FALSE for async request) bSuccess - TRUE if Read completed successfully hFileMapped - mapped file handle dwBytesRead - number of bytes read lpBuffer - into this buffer dwNumberOfBytesToRead - originally requested bytes to read lpdwNumberOfBytesRead - where bytes read is stored Return Value: None. --*/ { DEBUG_ENTER((DBG_INET, None, "ReadFile_End", "%B, %B, %#x, %d, %#x, %d, %#x", bDeref, bSuccess, hFileMapped, dwBytesRead, lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead )); if (bSuccess) { // // update the amount of immediate data available only if we succeeded // ((INTERNET_HANDLE_OBJECT *)hFileMapped)->ReduceAvailableDataLength(dwBytesRead); if (lpdwNumberOfBytesRead != NULL) { *lpdwNumberOfBytesRead = dwBytesRead; DEBUG_PRINT(API, INFO, ("*lpdwNumberOfBytesRead = %d\n", *lpdwNumberOfBytesRead )); // // dump API data only if requested // IF_DEBUG_CONTROL(DUMP_API_DATA) { DEBUG_DUMP_API(API, "Received data:\n", lpBuffer, *lpdwNumberOfBytesRead ); } } if (dwBytesRead < dwNumberOfBytesToRead) { DEBUG_PRINT(API, INFO, ("(!) bytes read (%d) < bytes requested (%d)\n", dwBytesRead, dwNumberOfBytesToRead )); } } // // if async request, handle will be deref'd after REQUEST_COMPLETE callback // is delivered // if (bDeref && (hFileMapped != NULL)) { DereferenceObject((LPVOID)hFileMapped); } PERF_LOG(PE_CLIENT_REQUEST_END, AR_INTERNET_READ_FILE, dwBytesRead, 0, (!bDeref && hFileMapped) ? ((INTERNET_HANDLE_OBJECT *)hFileMapped)->GetPseudoHandle() : NULL ); DEBUG_LEAVE(0); } DWORD CFsm_ReadFile::RunSM( IN CFsm * Fsm ) { DEBUG_ENTER((DBG_HTTP, Dword, "CFsm_ReadFile::RunSM", "%#x", Fsm )); DWORD error; CFsm_ReadFile * stateMachine = (CFsm_ReadFile *)Fsm; switch (Fsm->GetState()) { case FSM_STATE_INIT: case FSM_STATE_CONTINUE: error = ReadFile_Fsm(stateMachine); break; default: error = ERROR_INTERNET_INTERNAL_ERROR; Fsm->SetDone(ERROR_INTERNET_INTERNAL_ERROR); INET_ASSERT(FALSE); break; } DEBUG_LEAVE(error); return error; } PRIVATE DWORD ReadFile_Fsm( IN CFsm_ReadFile * Fsm ) { DEBUG_ENTER((DBG_INET, Dword, "ReadFile_Fsm", "%#x", Fsm )); CFsm_ReadFile & fsm = *Fsm; DWORD error = fsm.GetError(); if ((error == ERROR_SUCCESS) && (fsm.GetState() == FSM_STATE_INIT)) { error = HttpReadData(fsm.GetMappedHandle(), fsm.m_lpBuffer, fsm.m_dwNumberOfBytesToRead, &fsm.m_dwBytesRead, 0 ); if (error == ERROR_IO_PENDING) { goto quit; } } ReadFile_End(!fsm.GetThreadInfo()->IsAsyncWorkerThread, (error == ERROR_SUCCESS) ? TRUE : FALSE, fsm.GetMappedHandle(), fsm.m_dwBytesRead, fsm.m_lpBuffer, fsm.m_dwNumberOfBytesToRead, fsm.m_lpdwNumberOfBytesRead ); fsm.SetDone(); quit: DEBUG_LEAVE(error); return error; } INTERNETAPI_(BOOL) InternetReadFileExA( IN HINTERNET hFile, OUT LPINTERNET_BUFFERSA lpBuffersOut, IN DWORD dwFlags, IN DWORD_PTR dwContext ) { DEBUG_ENTER_API((DBG_API, Bool, "InternetReadFileExA", "%#x, %#x [%#x, %d], %#x, %#x", hFile, lpBuffersOut, (lpBuffersOut ? lpBuffersOut->lpvBuffer : NULL), (lpBuffersOut ? lpBuffersOut->dwBufferLength : 0), dwFlags, dwContext )); LPINTERNET_THREAD_INFO lpThreadInfo; DWORD nestingLevel = 0; DWORD error; HINTERNET hFileMapped = NULL; DWORD bytesRead = 0; LPVOID lpBuffer = NULL; DWORD dwNumberOfBytesToRead; BOOL bEndRead = TRUE; BOOL success = TRUE; if (!GlobalDataInitialized) { error = ERROR_INTERNET_NOT_INITIALIZED; goto done; } // // we need the thread info block // lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo == NULL) { INET_ASSERT(FALSE); error = ERROR_INTERNET_INTERNAL_ERROR; goto done; } _InternetIncNestingCount(); nestingLevel = 1; // // map the handle // error = MapHandleToAddress(hFile, (LPVOID *)&hFileMapped, FALSE); if ((error != ERROR_SUCCESS) && (hFileMapped == NULL)) { goto done; } // // set the context, handle, and last-error info in the per-thread data block // before we go any further. This allows us to return a status in the async // case, even if the handle has been closed // DWORD_PTR context; RGetContext(hFileMapped, &context); if (!lpThreadInfo->IsAsyncWorkerThread) { PERF_LOG(PE_CLIENT_REQUEST_START, AR_INTERNET_READ_FILE, lpThreadInfo->ThreadId, hFile ); } _InternetSetContext(lpThreadInfo, context); _InternetSetObjectHandle(lpThreadInfo, hFile, hFileMapped); _InternetClearLastError(lpThreadInfo); // // if MapHandleToAddress() returned a non-NULL object address, but also an // error status, then the handle is being closed - quit // if (error != ERROR_SUCCESS) { goto done; } // // validate handle and retrieve its type // HINTERNET_HANDLE_TYPE handleType; error = RGetHandleType(hFileMapped, &handleType); if (error != ERROR_SUCCESS) { goto done; } BOOL isLocal; BOOL isAsync; error = RIsHandleLocal(hFileMapped, &isLocal, &isAsync, handleType); if (error != ERROR_SUCCESS) { // // we should not get an error - we already believe the handle object // is valid and of the type just retrieved! // INET_ASSERT(FALSE); goto done; } // // only accepting HTTP handles currently // if (handleType != TypeHttpRequestHandle) { error = ERROR_INTERNET_INCORRECT_HANDLE_TYPE; goto done; } HTTP_REQUEST_HANDLE_OBJECT * lpRequest; lpRequest = (HTTP_REQUEST_HANDLE_OBJECT *)hFileMapped; // // validate params // if (lpBuffersOut->dwStructSize != sizeof(INTERNET_BUFFERS)) { error = ERROR_INVALID_PARAMETER; goto quit; } lpBuffer = lpBuffersOut->lpvBuffer; dwNumberOfBytesToRead = lpBuffersOut->dwBufferLength; INET_ASSERT(dwNumberOfBytesToRead > 0); // // See if request can be fulfilled from file system. // if (lpRequest->AttemptReadFromFile(lpBuffer, dwNumberOfBytesToRead, &bytesRead)) { error = ERROR_SUCCESS; goto quit; } // // trap a zero-length buffer before we go to the trouble of going async. // Maintain compatibility with base ReadFile(), although this is // POTENTIALLY A BUG. ReadFile() is *supposed* to return TRUE and number // of bytes read equal to zero to indicate end-of-file, but it will also // return TRUE and zero if a read of zero bytes is requested. According // to MarkL, that's the way it is. Good enough for me... // // For http, AttemptToReadFromFile traps zero-length reads if (dwNumberOfBytesToRead == 0) { // // *lpdwNumberOfBytesRead and error should have been correctly set // during parameter validation // INET_ASSERT(error == ERROR_SUCCESS); goto quit; } //error = lpRequest->QuickSyncRead( // lpBuffer, // dwNumberOfBytesToRead, // &bytesRead, // SF_NO_WAIT // ); // //if ( error == ERROR_IO_PENDING ) //{ error = DoFsm(new CFsm_ReadFileEx(lpBuffersOut, dwFlags, dwContext )); //} if (error == ERROR_SUCCESS) { bytesRead = lpBuffersOut->dwBufferLength; } bEndRead = FALSE; quit: _InternetDecNestingCount(nestingLevel);; if (bEndRead) { ReadFile_End(TRUE, (error == ERROR_SUCCESS), hFileMapped, bytesRead, lpBuffersOut->lpvBuffer, dwNumberOfBytesToRead, &lpBuffersOut->dwBufferLength ); } done: if (error != ERROR_SUCCESS) { DEBUG_ERROR(API, error); SetLastError(error); success = FALSE; } DEBUG_LEAVE_API(success); return success; } DWORD CFsm_ReadFileEx::RunSM( IN CFsm * Fsm ) { DEBUG_ENTER((DBG_HTTP, Dword, "CFsm_ReadFileEx::RunSM", "%#x", Fsm )); DWORD error; CFsm_ReadFileEx * stateMachine = (CFsm_ReadFileEx *)Fsm; switch (Fsm->GetState()) { case FSM_STATE_INIT: case FSM_STATE_CONTINUE: error = ReadFileEx_Fsm(stateMachine); break; default: error = ERROR_INTERNET_INTERNAL_ERROR; Fsm->SetDone(ERROR_INTERNET_INTERNAL_ERROR); INET_ASSERT(FALSE); break; } DEBUG_LEAVE(error); return error; } PRIVATE DWORD ReadFileEx_Fsm( IN CFsm_ReadFileEx * Fsm ) { DEBUG_ENTER((DBG_INET, Dword, "ReadFileEx_Fsm", "%#x", Fsm )); CFsm_ReadFileEx & fsm = *Fsm; DWORD error = fsm.GetError(); if ((error == ERROR_SUCCESS) && (fsm.GetState() == FSM_STATE_INIT)) { fsm.m_dwNumberOfBytesToRead = fsm.m_lpBuffersOut->dwBufferLength; error = HttpReadData(fsm.GetMappedHandle(), fsm.m_lpBuffersOut->lpvBuffer, fsm.m_dwNumberOfBytesToRead, &fsm.m_dwBytesRead, (fsm.m_dwFlags & IRF_NO_WAIT) ? SF_NO_WAIT : 0 ); if (error == ERROR_IO_PENDING) { goto quit; } } // // if we are asynchronously completing a no-wait read then we don't update // any app parameters - we simply return the indication that we completed. // The app will then make another no-wait read to get the data // BOOL bNoOutput; bNoOutput = ((fsm.m_dwFlags & IRF_NO_WAIT) && fsm.GetThreadInfo()->IsAsyncWorkerThread) ? TRUE : FALSE; ReadFile_End(!fsm.GetThreadInfo()->IsAsyncWorkerThread, (error == ERROR_SUCCESS) ? TRUE : FALSE, fsm.GetMappedHandle(), bNoOutput ? 0 : fsm.m_dwBytesRead, bNoOutput ? NULL : fsm.m_lpBuffersOut->lpvBuffer, bNoOutput ? 0 : fsm.m_dwNumberOfBytesToRead, bNoOutput ? NULL : &fsm.m_lpBuffersOut->dwBufferLength ); fsm.SetDone(); quit: DEBUG_LEAVE(error); return error; } INTERNETAPI_(BOOL) InternetWriteFile( IN HINTERNET hFile, IN LPCVOID lpBuffer, IN DWORD dwNumberOfBytesToWrite, OUT LPDWORD lpdwNumberOfBytesWritten ) /*++ Routine Description: This function write next block of data to the internet file. Currently it supports the following protocol data: FtpWriteFile HttpWriteFile FileWriteFile Arguments: hFile - handle that was obtained by OpenFile Call lpBuffer - pointer to the data buffer dwNumberOfBytesToWrite - number of bytes in the above buffer lpdwNumberOfBytesWritten - pointer to a DWORD where the number of bytes of data actually written is returned Return Value: BOOL Success - TRUE Failure - FALSE. Call GetLastError() for more info --*/ { DEBUG_ENTER_API((DBG_API, Bool, "InternetWriteFile", "%#x, %#x, %d, %#x", hFile, lpBuffer, dwNumberOfBytesToWrite, lpdwNumberOfBytesWritten )); LPINTERNET_THREAD_INFO lpThreadInfo; DWORD nestingLevel = 0; DWORD error; BOOL success = FALSE; BOOL fNeedDeref = TRUE; HINTERNET hFileMapped = NULL; if (!GlobalDataInitialized) { error = ERROR_INTERNET_NOT_INITIALIZED; goto done; } // // get the per-thread info block // lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo == NULL) { INET_ASSERT(FALSE); error = ERROR_INTERNET_INTERNAL_ERROR; goto done; } _InternetIncNestingCount(); nestingLevel = 1; // // map the handle // error = MapHandleToAddress(hFile, (LPVOID *)&hFileMapped, FALSE); if ((error != ERROR_SUCCESS) && (hFileMapped == NULL)) { goto quit; } // // set the context, handle, and last-error info in the per-thread data block // before we go any further. This allows us to return a status in the async // case, even if the handle has been closed // DWORD_PTR context; RGetContext(hFileMapped, &context); _InternetSetContext(lpThreadInfo, context); _InternetSetObjectHandle(lpThreadInfo, hFile, hFileMapped); _InternetClearLastError(lpThreadInfo); // // if MapHandleToAddress() returned a non-NULL object address, but also an // error status, then the handle is being closed - quit // if (error != ERROR_SUCCESS) { goto quit; } // // validate handle and retrieve its type // HINTERNET_HANDLE_TYPE handleType; error = RGetHandleType(hFileMapped, &handleType); if (error != ERROR_SUCCESS) { goto quit; } BOOL isLocal; BOOL isAsync; error = RIsHandleLocal(hFileMapped, &isLocal, &isAsync, handleType); if (error != ERROR_SUCCESS) { // // we should not get an error - we already believe the handle object // is valid and of the type just retrieved! // INET_ASSERT(FALSE); goto quit; } // // validate parameters - write length cannot be 0 // if (!lpThreadInfo->IsAsyncWorkerThread) { if (dwNumberOfBytesToWrite != 0) { error = ProbeReadBuffer((LPVOID)lpBuffer, dwNumberOfBytesToWrite); if (error == ERROR_SUCCESS) { error = ProbeAndSetDword(lpdwNumberOfBytesWritten, 0); } } else { error = ERROR_INVALID_PARAMETER; } if (error != ERROR_SUCCESS) { goto quit; } } // # 62953 // If the authentication state of the handle is Negotiate, // don't submit data to the server but return success. // ** Added test for NTLM or Negotiate - Adriaanc. if (handleType == TypeHttpRequestHandle) { HTTP_REQUEST_HANDLE_OBJECT *pRequest; pRequest = (HTTP_REQUEST_HANDLE_OBJECT*) hFileMapped; if (pRequest->GetAuthState() == AUTHSTATE_NEGOTIATE && !((PLUG_CTX*) (pRequest->GetAuthCtx()))->_fNTLMProxyAuth && !(pRequest->GetAuthCtx()->GetSchemeType() == AUTHCTX::SCHEME_DPA)) { *lpdwNumberOfBytesWritten = dwNumberOfBytesToWrite; error = ERROR_SUCCESS; success = TRUE; goto quit; } } // // we have to do some work. If the file object handle was created with // async I/O capability then we will queue an async request, otherwise // we will process the request synchronously // if (isAsync && !lpThreadInfo->IsAsyncWorkerThread && (handleType != TypeHttpRequestHandle) && (handleType != TypeFileRequestHandle)) { // MakeAsyncRequest CFsm_InternetWriteFile * pFsm; pFsm = new CFsm_InternetWriteFile(hFile, lpBuffer, dwNumberOfBytesToWrite, lpdwNumberOfBytesWritten); if (pFsm != NULL) { error = pFsm->QueueWorkItem(); if ( error == ERROR_IO_PENDING ) { fNeedDeref = FALSE; } } else { error = ERROR_NOT_ENOUGH_MEMORY; } // // if we're here then ERROR_SUCCESS cannot have been returned from // the above calls // INET_ASSERT(error != ERROR_SUCCESS); DEBUG_PRINT(FTP, INFO, ("processing request asynchronously: error = %d\n", error )); goto quit; // // we're going synchronous. Change error to ERROR_SUCCESS so that we do // the right thing at quit // error = ERROR_SUCCESS; } INET_ASSERT(error == ERROR_SUCCESS); switch (handleType) { case TypeFtpFileHandle: success = FtpWriteFile(hFileMapped, (LPVOID)lpBuffer, dwNumberOfBytesToWrite, lpdwNumberOfBytesWritten ); break; case TypeHttpRequestHandle: error = HttpWriteData(hFileMapped, (LPVOID)lpBuffer, dwNumberOfBytesToWrite, lpdwNumberOfBytesWritten, 0 ); // // Don't Derefrence if we're going pending cause the FSM will do // it for us. // if ( error == ERROR_IO_PENDING ) { fNeedDeref = FALSE; } success = (error == ERROR_SUCCESS) ? TRUE : FALSE; //bEndRead = FALSE; break; case TypeFileRequestHandle: success = WriteFile(((INTERNET_FILE_HANDLE_OBJECT *) hFileMapped)->GetFileHandle(), (LPVOID)lpBuffer, dwNumberOfBytesToWrite, lpdwNumberOfBytesWritten, NULL // overlapped I/O ); if ( !success ) { error = GetLastError(); } else { error = ERROR_SUCCESS; } break; default: error = ERROR_INVALID_HANDLE; break; } quit: if (hFileMapped != NULL && fNeedDeref) { DereferenceObject((LPVOID)hFileMapped); } _InternetDecNestingCount(nestingLevel);; done: if (error != ERROR_SUCCESS) { DEBUG_ERROR(API, error); SetLastError(error); } DEBUG_LEAVE_API(success); return success; } INTERNETAPI_(BOOL) InternetWriteFileExA( IN HINTERNET hFile, IN LPINTERNET_BUFFERSA lpBuffersIn, IN DWORD dwFlags, IN DWORD_PTR dwContext ) { SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } INTERNETAPI_(DWORD) InternetSetFilePointer( IN HINTERNET hFile, IN LONG lDistanceToMove, IN PVOID pReserved, IN DWORD dwMoveMethod, IN DWORD_PTR dwContext ) /*++ Routine Description: Sets a file position for InternetReadFile. It is a synchronous call, however subsequent calls to InternetReadFile may block or return pending if the data is not available from the cache and the server does not support random access. Arguments: hFile A valid handle returned from a previous call to InternetOpenUrl or a handle returned from HttpOpenRequest for a GET or HEAD method and passed to HttpSendRequest. The handle must have been created without INTERNET_FLAG_DONT_CACHE. lDistanceToMove Specifies the number of bytes to move the file pointer. A positive value moves the pointer forward in the file and a negative value moves it backward. pReserved Reserved, pass NULL. dwMoveMethod Specifies the starting point for the file pointer move. This parameter can be one of the following values: Value Meaning FILE_BEGIN The starting point is zero or the beginning of the file. If FILE_BEGIN is specified, DistanceToMove is interpreted as an unsigned location for the new file pointer. FILE_CURRENT The current value of the file pointer is the starting point. FILE_END The current end-of-file position is the starting point. This method will fail if the content length is unknown. Return Value: -1 on failure, else the current file position. --*/ { DEBUG_ENTER_API((DBG_API, Int, "InternetSetFilePointer", "%#x, %#x, %#x, %#x %#x", hFile, lDistanceToMove, pReserved, dwMoveMethod, dwContext )); DWORD dwNewPosition = (DWORD) -1L; DWORD error; HINTERNET hFileMapped = NULL; if (!GlobalDataInitialized) { error = ERROR_INTERNET_NOT_INITIALIZED; goto done; } // Validate parameters... error = MapHandleToAddress(hFile, &hFileMapped, FALSE); if ((error != ERROR_SUCCESS) && (hFileMapped == NULL)) { goto quit; } HINTERNET_HANDLE_TYPE handleType; error = RGetHandleType(hFileMapped, &handleType); if (error != ERROR_SUCCESS) { goto quit; } switch (handleType) { // case TypeFtpFileHandle: // case TypeGopherFileHandle: case TypeHttpRequestHandle: break; default: error = ERROR_INTERNET_INCORRECT_HANDLE_TYPE; goto quit; } HTTP_REQUEST_HANDLE_OBJECT *lpRequest; lpRequest = (HTTP_REQUEST_HANDLE_OBJECT *) hFileMapped; dwNewPosition = lpRequest->SetStreamPointer (lDistanceToMove, dwMoveMethod); quit: if (hFileMapped != NULL) { DereferenceObject((LPVOID)hFileMapped); } done: DEBUG_LEAVE_API(dwNewPosition); return dwNewPosition; } INTERNETAPI_(BOOL) InternetQueryDataAvailable( IN HINTERNET hFile, OUT LPDWORD lpdwNumberOfBytesAvailable, IN DWORD dwFlags, IN DWORD_PTR dwContext ) /*++ Routine Description: Determines the amount of data currently available to be read on the handle Arguments: hFile - handle of internet object lpdwNumberOfBytesAvailable - pointer to returned bytes available dwFlags - flags controlling operation - FUTURE dwContext - used to differentiate multiple requests - FUTURE Return Value: BOOL Success - TRUE Failure - FALSE. Call GetLastError() for more info --*/ { DEBUG_ENTER_API((DBG_API, Bool, "InternetQueryDataAvailable", "%#x, %#x, %#x, %#x", hFile, lpdwNumberOfBytesAvailable, dwFlags, dwContext )); BOOL success; DWORD error; LPINTERNET_THREAD_INFO lpThreadInfo = NULL; HINTERNET hFileMapped = NULL; BOOL bDeref = TRUE; if (!GlobalDataInitialized) { error = ERROR_INTERNET_NOT_INITIALIZED; bDeref = FALSE; goto quit; } INET_ASSERT(hFile); // // get the per-thread info block // lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo == NULL) { INET_ASSERT(FALSE); error = ERROR_INTERNET_INTERNAL_ERROR; goto quit; } //INET_ASSERT(lpThreadInfo->Fsm == NULL); PERF_LOG(PE_CLIENT_REQUEST_START, AR_INTERNET_QUERY_DATA_AVAILABLE, lpThreadInfo->ThreadId, hFile ); // // validate parameters // error = MapHandleToAddress(hFile, &hFileMapped, FALSE); if ((error != ERROR_SUCCESS) && (hFileMapped == NULL)) { goto quit; } INET_ASSERT(hFileMapped); // // set the context and handle values in the per-thread info block (this API // can't return extended error info, so we don't care about it) // _InternetSetContext(lpThreadInfo, ((INTERNET_HANDLE_OBJECT *)hFileMapped)->GetContext() ); _InternetSetObjectHandle(lpThreadInfo, hFile, hFileMapped); // // if the handle is invalid, quit now // if (error != ERROR_SUCCESS) { goto quit; } // // validate rest of parameters // error = ProbeAndSetDword(lpdwNumberOfBytesAvailable, 0); if (error != ERROR_SUCCESS) { goto quit; } // // get the handle type // HINTERNET_HANDLE_TYPE handleType; handleType = ((HANDLE_OBJECT *)hFileMapped)->GetHandleType(); // // find out if we're sync or async // BOOL isLocal; BOOL isAsync; error = RIsHandleLocal(hFileMapped, &isLocal, &isAsync, TypeWildHandle); if (error != ERROR_SUCCESS) { goto quit; } // WinSE 4998. If there's no context on the handle, force the request to be synchronous. // if (isAsync && lpThreadInfo->Context == INTERNET_NO_CALLBACK) { DEBUG_PRINT(API, ERROR, ("Zero context: Call is Synchronous\n" )); isAsync = FALSE; } // // since the async worker thread doesn't come back through this API, the // following test is sufficient. Note that we only go async if there is // no data currently available on the handle // BOOL dataAvailable; dataAvailable = ((INTERNET_HANDLE_OBJECT *)hFileMapped)->IsDataAvailable(); BOOL eof; eof = ((INTERNET_HANDLE_OBJECT *)hFileMapped)->IsEndOfFile(); if ((handleType != TypeHttpRequestHandle) && isAsync && !dataAvailable && !eof) { INET_ASSERT(hFileMapped); // MakeAsyncRequest CFsm_InternetQueryDataAvailable * pFsm; pFsm = new CFsm_InternetQueryDataAvailable(hFileMapped, lpdwNumberOfBytesAvailable, dwFlags, dwContext); if (pFsm != NULL) { error = pFsm->QueueWorkItem(); if (error == ERROR_IO_PENDING) { bDeref = FALSE; } } else { error = ERROR_NOT_ENOUGH_MEMORY; } // // if we're here then ERROR_SUCCESS cannot have been returned from // the above calls // INET_ASSERT(error != ERROR_SUCCESS); DEBUG_PRINT(FTP, INFO, ("processing request asynchronously: error = %d\n", error )); goto quit; // // we will continue along the synchronous path, in which case we // need to set error back to ERROR_SUCCESS so that our exit // processing (at quit) does the right thing // error = ERROR_SUCCESS; } else if (dataAvailable || eof) { DWORD available; available = ((INTERNET_HANDLE_OBJECT *)hFileMapped)->AvailableDataLength(); // // we have immediate data; if the handle type is FTP or gopher find and // the data is coming from cache, then we only want to indicate that a // single (fixed-length) find structure is available // switch (((HANDLE_OBJECT *)hFileMapped)->GetHandleType()) { case TypeFtpFindHandle: available = min(available, sizeof(WIN32_FIND_DATA)); break; case TypeGopherFindHandle: available = min(available, sizeof(GOPHER_FIND_DATA)); break; } DEBUG_PRINT(API, INFO, ("%d bytes are immediately available\n", available )); *lpdwNumberOfBytesAvailable = available; success = TRUE; goto finish; } INET_ASSERT(hFileMapped); // // sync path. wInternetQueryDataAvailable will set the last error code // if it fails // if (handleType == TypeHttpRequestHandle) { error = DoFsm(new CFsm_QueryAvailable(lpdwNumberOfBytesAvailable, dwFlags, dwContext )); if (error == ERROR_SUCCESS) { success = TRUE; } else { if (error == ERROR_IO_PENDING) { bDeref = FALSE; } goto quit; } } else { success = wInternetQueryDataAvailable(hFileMapped, lpdwNumberOfBytesAvailable, dwFlags, dwContext ); } finish: DEBUG_PRINT_API(API, INFO, ("*lpdwNumberOfBytesAvailable (%#x) = %d\n", lpdwNumberOfBytesAvailable, *lpdwNumberOfBytesAvailable )); if (bDeref && (hFileMapped != NULL)) { DereferenceObject((LPVOID)hFileMapped); } if (lpThreadInfo) { PERF_LOG(PE_CLIENT_REQUEST_END, AR_INTERNET_QUERY_DATA_AVAILABLE, *lpdwNumberOfBytesAvailable, lpThreadInfo->ThreadId, hFile ); } DEBUG_LEAVE_API(success); return success; // // we only come here if we are returning an error before calling // wInternetQueryDataAvailable // INET_ASSERT(error != ERROR_SUCCESS); quit: DEBUG_ERROR(API, error); SetLastError(error); success = FALSE; goto finish; } DWORD CFsm_QueryAvailable::RunSM( IN CFsm * Fsm ) { DEBUG_ENTER((DBG_HTTP, Dword, "CFsm_QueryAvailable::RunSM", "%#x", Fsm )); DWORD error; CFsm_QueryAvailable * stateMachine = (CFsm_QueryAvailable *)Fsm; switch (Fsm->GetState()) { case FSM_STATE_INIT: case FSM_STATE_CONTINUE: error = QueryAvailable_Fsm(stateMachine); break; default: error = ERROR_INTERNET_INTERNAL_ERROR; Fsm->SetDone(ERROR_INTERNET_INTERNAL_ERROR); INET_ASSERT(FALSE); break; } DEBUG_LEAVE(error); return error; } PRIVATE DWORD QueryAvailable_Fsm( IN CFsm_QueryAvailable * Fsm ) { DEBUG_ENTER((DBG_INET, Dword, "QueryAvailable_Fsm", "%#x", Fsm )); CFsm_QueryAvailable & fsm = *Fsm; DWORD error = fsm.GetError(); if (error != ERROR_SUCCESS) { goto quit; } HTTP_REQUEST_HANDLE_OBJECT * pRequest; pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)fsm.GetMappedHandle(); if (fsm.GetState() == FSM_STATE_INIT) { error = pRequest->QueryDataAvailable(fsm.m_lpdwNumberOfBytesAvailable); } if (error == ERROR_SUCCESS) { pRequest->SetAvailableDataLength(*fsm.m_lpdwNumberOfBytesAvailable); DEBUG_PRINT(INET, INFO, ("%d bytes available\n", *fsm.m_lpdwNumberOfBytesAvailable )); fsm.SetApiData(*fsm.m_lpdwNumberOfBytesAvailable); } quit: if (error != ERROR_IO_PENDING) { fsm.SetDone(); } DEBUG_LEAVE(error); return error; } INTERNETAPI_(BOOL) InternetFindNextFileA( IN HINTERNET hFind, OUT LPVOID lpBuffer ) /*++ Routine Description: Returns the next directory entry in the listing identified by the handle Arguments: hFind - find handle, as returned by e.g. FtpFindFirstFile() lpBuffer - pointer to buffer where next directory entry information will be written. Contents of buffer may be different depending on type of directory request, and protocol used (FTP/gopher/etc.) Return Value: BOOL Success - TRUE Failure - FALSE. Call GetLastError() for more info --*/ { DEBUG_ENTER_API((DBG_API, Bool, "InternetFindNextFileA", "%#x, %#x", hFind, lpBuffer )); LPINTERNET_THREAD_INFO lpThreadInfo; DWORD nestingLevel = 0; DWORD error; BOOL success = FALSE; BOOL fDeref = TRUE; HINTERNET hFindMapped = NULL; if (!GlobalDataInitialized) { error = ERROR_INTERNET_NOT_INITIALIZED; goto done; } // // we need the per-thread info block on all paths // lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo == NULL) { INET_ASSERT(FALSE); error = ERROR_INTERNET_INTERNAL_ERROR; goto done; } _InternetIncNestingCount(); nestingLevel = 1; // // map the handle // error = MapHandleToAddress(hFind, (LPVOID *)&hFindMapped, FALSE); if ((error != ERROR_SUCCESS) && (hFindMapped == NULL)) { goto quit; } // // set the context, handle, and last-error info in the per-thread data block // before we go any further. This allows us to return a status in the async // case, even if the handle has been closed // DWORD_PTR context; RGetContext(hFindMapped, &context); _InternetSetContext(lpThreadInfo, context); _InternetSetObjectHandle(lpThreadInfo, hFind, hFindMapped); _InternetClearLastError(lpThreadInfo); // // if MapHandleToAddress() returned a non-NULL object address, but also an // error status, then the handle is being closed - quit // if (error != ERROR_SUCCESS) { goto quit; } // // retrieve handle type, and validate in the process // HINTERNET_HANDLE_TYPE handleType; error = RGetHandleType(hFindMapped, &handleType); if (error != ERROR_SUCCESS) { goto quit; } // // get async/sync and local/remote // BOOL isLocal; BOOL isAsync; error = RIsHandleLocal(hFindMapped, &isLocal, &isAsync, handleType); if (error != ERROR_SUCCESS) { // // this should never happen - we just successfully called // RGetHandleType(), so RIsHandleLocal() should have worked too // DEBUG_PRINT(INET, ERROR, ("RIsHandleLocal() returns %d\n", error )); INET_ASSERT(FALSE); goto quit; } // // make sure the handle type is valid for this request. We only support // FTP find handle and gopher find handle (both raw data) // if (!((handleType == TypeFtpFindHandle) || (handleType == TypeGopherFindHandle))) { error = ERROR_INTERNET_INVALID_OPERATION; goto quit; } // // if we're not in an async worker thread context then probe the buffer. If // we are in the async worker thread context, then we've already validated // the buffer. If is has since become invalid, then the app will fail // if (!lpThreadInfo->IsAsyncWorkerThread) { error = ProbeWriteBuffer(lpBuffer, (handleType == TypeFtpFindHandle) ? sizeof(WIN32_FIND_DATA) : sizeof(GOPHER_FIND_DATA) ); if (error != ERROR_SUCCESS) { goto quit; } // // if this is an async request and we are not an async worker thread // then queue the request // if (isAsync) { // MakeAsyncRequest CFsm_InternetFindNextFile * pFsm; pFsm = new CFsm_InternetFindNextFile(hFind, lpBuffer); if (pFsm != NULL) { error = pFsm->QueueWorkItem(); if ( error == ERROR_IO_PENDING ) { fDeref = FALSE; } } else { error = ERROR_NOT_ENOUGH_MEMORY; } // // if we're here then ERROR_SUCCESS cannot have been returned from // the above calls // INET_ASSERT(error != ERROR_SUCCESS); DEBUG_PRINT(FTP, INFO, ("processing request asynchronously: error = %d\n", error )); goto quit; // // we will continue along the synchronous path, in which case we // need to set error back to ERROR_SUCCESS so that our exit // processing (at quit) does the right thing // error = ERROR_SUCCESS; } } // // dispatch to the underlying API. Return what the API returns, and let // the API SetLastError() // // N.B. We have already checked the handle type above, and we know at // this stage that we have a correct handle type // INET_ASSERT(error == ERROR_SUCCESS); switch (handleType) { case TypeFtpFindHandle: success = FtpFindNextFileA(hFindMapped, (LPWIN32_FIND_DATA)lpBuffer ); break; case TypeGopherFindHandle: success = GopherFindNextA(hFindMapped, (LPGOPHER_FIND_DATA)lpBuffer ); break; } quit: if (hFindMapped != NULL && fDeref) { DereferenceObject((LPVOID)hFindMapped); } _InternetDecNestingCount(nestingLevel);; done: // // if error is not ERROR_SUCCESS then this function returning the error, // otherwise the error has already been set by the API we called, // irrespective of the value of success // if (error != ERROR_SUCCESS) { DEBUG_ERROR(API, error); SetLastError(error); } DEBUG_LEAVE_API(success); return success; } INTERNETAPI_(BOOL) InternetGetLastResponseInfoA( OUT LPDWORD lpdwErrorCategory, IN LPSTR lpszBuffer OPTIONAL, IN OUT LPDWORD lpdwBufferLength ) /*++ Routine Description: This function returns the per-thread last internet error description text or server response. If this function is successful, *lpdwBufferLength contains the string length of lpszBuffer. If this function returns a failure indication, *lpdwBufferLength contains the number of BYTEs required to hold the response text Arguments: lpdwErrorCategory - pointer to DWORD location where the error catagory is returned lpszBuffer - pointer to buffer where the error text is returned lpdwBufferLength - IN: length of lpszBuffer OUT: number of characters in lpszBuffer if successful else size of buffer required to hold response text Return Value: BOOL Success - TRUE lpszBuffer contains the error text. The caller must check *lpdwBufferLength: if 0 then there was no text to return Failure - FALSE Call GetLastError() for more information --*/ { DEBUG_ENTER_API((DBG_API, Bool, "InternetGetLastResponseInfoA", "%#x, %#x, %#x [%d]", lpdwErrorCategory, lpszBuffer, lpdwBufferLength, lpdwBufferLength ? *lpdwBufferLength : 0 )); DWORD error; BOOL success; DWORD textLength; LPINTERNET_THREAD_INFO lpThreadInfo; // // validate parameters // if (IsBadWritePtr(lpdwErrorCategory, sizeof(*lpdwErrorCategory)) || IsBadWritePtr(lpdwBufferLength, sizeof(*lpdwBufferLength)) || (ARGUMENT_PRESENT(lpszBuffer) ? IsBadWritePtr(lpszBuffer, *lpdwBufferLength) : FALSE)) { error = ERROR_INVALID_PARAMETER; goto quit; } // // if the buffer pointer is NULL then its the same as a zero-length buffer // if (!ARGUMENT_PRESENT(lpszBuffer)) { *lpdwBufferLength = 0; } else if (*lpdwBufferLength != 0) { *lpszBuffer = '\0'; } lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo == NULL) { DEBUG_PRINT(INET, ERROR, ("failed to get INTERNET_THREAD_INFO\n" )); INET_ASSERT(FALSE); error = ERROR_INTERNET_INTERNAL_ERROR; goto quit; } // // there may not be any error text for this thread - either no server // error/response has been received, or the error text has been cleared by // an intervening API // if (lpThreadInfo->hErrorText != NULL) { // // copy as much as we can fit in the user supplied buffer // textLength = lpThreadInfo->ErrorTextLength; if (*lpdwBufferLength) { LPBYTE errorText; errorText = (LPBYTE)LOCK_MEMORY(lpThreadInfo->hErrorText); if (errorText != NULL) { textLength = min(textLength, *lpdwBufferLength) - 1; memcpy(lpszBuffer, errorText, textLength); // // the error text should always be zero terminated, so the // calling app can treat it as a string // lpszBuffer[textLength] = '\0'; UNLOCK_MEMORY(lpThreadInfo->hErrorText); if (textLength == lpThreadInfo->ErrorTextLength - 1) { error = ERROR_SUCCESS; } else { // // returned length is amount of buffer required // textLength = lpThreadInfo->ErrorTextLength; error = ERROR_INSUFFICIENT_BUFFER; } } else { DEBUG_PRINT(INET, ERROR, ("failed to lock hErrorText (%#x): %d\n", lpThreadInfo->hErrorText, GetLastError() )); error = ERROR_INTERNET_INTERNAL_ERROR; } } else { // // user's buffer is not large enough to hold the info. We'll // let them know the required length // error = ERROR_INSUFFICIENT_BUFFER; } } else { INET_ASSERT(lpThreadInfo->ErrorTextLength == 0); textLength = 0; error = ERROR_SUCCESS; } *lpdwErrorCategory = lpThreadInfo->ErrorNumber; *lpdwBufferLength = textLength; IF_DEBUG(ANY) { if ((error == ERROR_SUCCESS) || ((textLength != 0) && (lpszBuffer != NULL))) { DEBUG_DUMP_API(API, "Last Response Info:\n", lpszBuffer, textLength ); } } quit: success = (error == ERROR_SUCCESS); if (!success) { DEBUG_ERROR(API, error); SetLastError(error); } DEBUG_LEAVE_API(success); return success; } INTERNETAPI_(INTERNET_STATUS_CALLBACK) InternetSetStatusCallbackCore( IN HINTERNET hInternet, IN INTERNET_STATUS_CALLBACK lpfnInternetCallback, IN BOOL fType ) /*++ Routine Description: Sets the status callback function for the DLL or the handle object Arguments: hInternet - handle of the object for which we wish to set the status callback lpfnInternetCallback - pointer to caller-supplied status function Return Value: FARPROC Success - previous status callback function address Failure - INTERNET_INVALID_STATUS_CALLBACK. Call GetLastErrorInfo() for more information: ERROR_INVALID_PARAMETER The callback function is invalid ERROR_INTERNET_INCORRECT_HANDLE_TYPE Cannot set the callback on the supplied handle (probably a NULL handle - per-process callbacks no longer supported) --*/ { DWORD dwErr = ERROR_SUCCESS; INTERNET_STATUS_CALLBACK previousCallback = INTERNET_INVALID_STATUS_CALLBACK; HINTERNET hObjectMapped = NULL; if (!GlobalDataInitialized) { dwErr = GlobalDataInitialize(); if (dwErr != ERROR_SUCCESS) { goto cleanup; } } if ((lpfnInternetCallback != NULL) && IsBadCodePtr((FARPROC)lpfnInternetCallback)) { dwErr = ERROR_INVALID_PARAMETER; goto cleanup; } if (!hInternet) { dwErr = ERROR_INTERNET_INCORRECT_HANDLE_TYPE; goto cleanup; } // // map the handle // dwErr = MapHandleToAddress(hInternet, (LPVOID *)&hObjectMapped, FALSE); if (dwErr == ERROR_SUCCESS) { // // swap the new and previous handle object status callbacks, ONLY // if there are no pending requests on this handle // previousCallback = lpfnInternetCallback; dwErr = RExchangeStatusCallback(hObjectMapped, &previousCallback, fType); } if (hObjectMapped != NULL) { DereferenceObject((LPVOID)hObjectMapped); } cleanup: if (dwErr!=ERROR_SUCCESS) { SetLastError(dwErr); DEBUG_ERROR(API, dwErr); } return previousCallback; } INTERNETAPI_(INTERNET_STATUS_CALLBACK) InternetSetStatusCallbackA( IN HINTERNET hInternet, IN INTERNET_STATUS_CALLBACK lpfnInternetCallback ) /*++ Routine Description: Sets the status callback function for the DLL or the handle object Arguments: hInternet - handle of the object for which we wish to set the status callback lpfnInternetCallback - pointer to caller-supplied status function Return Value: FARPROC Success - previous status callback function address Failure - INTERNET_INVALID_STATUS_CALLBACK. Call GetLastErrorInfo() for more information: ERROR_INVALID_PARAMETER The callback function is invalid ERROR_INTERNET_INCORRECT_HANDLE_TYPE Cannot set the callback on the supplied handle (probably a NULL handle - per-process callbacks no longer supported) --*/ { DEBUG_ENTER_API((DBG_INET, Pointer, "InternetSetStatusCallbackA", "%#x, %#x", hInternet, lpfnInternetCallback )); INTERNET_STATUS_CALLBACK previousCallback = InternetSetStatusCallbackCore( hInternet, lpfnInternetCallback, FALSE ); DEBUG_LEAVE_API(previousCallback); return previousCallback; } // //INTERNETAPI_(BOOL) InternetCancelAsyncRequest( // IN DWORD dwAsyncId // ) // ///*++ // //Routine Description: // // Cancels an outstanding async request // //Arguments: // // dwAsyncId - identifier of the async I/O request // //Return Value: // // BOOL // Success - TRUE // Request was cancelled // // Failure - FALSE // Call GetLastError() for more information // //--*/ // //{ // DEBUG_ENTER((DBG_INET, // Bool, // "InternetCancelAsyncRequest", // "%d", // dwAsyncId // )); // // DWORD error; // BOOL success; // // error = CancelAsyncRequest(dwAsyncId); // if (error != ERROR_SUCCESS) { // // DEBUG_ERROR(INET, error); // // SetLastError(error); // success = FALSE; // } else { // success = TRUE; // } // // DEBUG_LEAVE(success); // // return success; //} // // private functions // PRIVATE DWORD wInternetCloseConnectA( IN HINTERNET hConnect, IN DWORD dwService ) /*++ Routine Description: The obverse of InternetConnect(). Closes the handle created in InternetConnect() Arguments: hConnect - protocol handle created in InternetConnect() dwService - service required. Controls type of handle generated. May be one of: - INTERNET_SERVICE_FTP - INTERNET_SERVICE_GOPHER - INTERNET_SERVICE_HTTP Return Value: Success - ERROR_SUCCESS Failure - ERROR_INVALID_PARAMETER Incorrect dwService parameter (*never* expect this) Windows error Wininet error WSA error Error from protocol-specific disconnect function --*/ { DEBUG_ENTER((DBG_INET, Dword, "wInternetCloseConnectA", "%#x, %d", hConnect, dwService )); DWORD error; switch (dwService) { case INTERNET_SERVICE_FTP : error = wFtpDisconnect(hConnect, CF_EXPEDITED_CLOSE); break; case INTERNET_SERVICE_GOPHER : //error = wGopherDisconnect(hConnect); error = ERROR_SUCCESS; break; case INTERNET_SERVICE_HTTP: //error = wHttpConnectClose((LPHINTERNET)hConnect); error = ERROR_SUCCESS; break; default: error = ERROR_INVALID_PARAMETER; break; } DEBUG_LEAVE(error); return error; } PRIVATE DWORD GetEmailNameAndPassword( IN OUT LPSTR* lplpszUserName, IN OUT LPSTR* lplpszPassword, OUT LPSTR EmailName, IN DWORD EmailNameLength ) /*++ Routine Description: Gets the login name and password for the FTP server (but can be used for any other protocol) Arguments: lplpszUserName - IN: pointer to pointer to user name OUT: pointer to pointer to user name; may be modified lplpszPassword - IN: pointer to pointer to password OUT: pointer to pointer to password; may be modified EmailName - pointer to buffer in which to store password if "anonymous" returned for login name EmailNameLength - length of EmailName Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_INVALID_PARAMETER --*/ { DWORD error; LPSTR lpszUserName; LPSTR lpszPassword; lpszUserName = *lplpszUserName; lpszPassword = *lplpszPassword; // // validate username and password arguments. Valid combinations are: // (N.B. NULL means NULL pointer or NUL string) // // lpszUsername lpszPassword Result // // NULL NULL "anonymous", "emailname@domain" // !NULL NULL lpszUserName, NULL // NULL !NULL ERROR // !NULL !NULL lpszUserName, lpszPassword // error = ERROR_SUCCESS; if (lpszUserName != NULL) { if (IsBadStringPtr(lpszUserName, INTERNET_MAX_USER_NAME_LENGTH)) { error = ERROR_INVALID_PARAMETER; } else if (*lpszUserName == '\0') { lpszUserName = NULL; } } if (error == ERROR_SUCCESS) { if (lpszPassword != NULL) { if (IsBadStringPtr(lpszPassword, INTERNET_MAX_PASSWORD_LENGTH)) { error = ERROR_INVALID_PASSWORD; } else if (*lpszPassword == '\0') { lpszPassword = NULL; } } } if (error == ERROR_SUCCESS) { if (lpszPassword == NULL) { if (lpszUserName == NULL) { DWORD length; // // both name and password are null pointers. We will convert to // "anonymous", "EmailName@DomainName" // // // because we don't require a client to be running TCP/IP, we // may be unable to get a domain name. Hence we now require the // EmailName entry in the registry to contain the entire // EmailName@DomainName string, including the '@'. If this is // not available, then we will just return an error // lpszUserName = "anonymous"; length = EmailNameLength; error = GetMyEmailName(EmailName, &EmailNameLength); if (error == ERROR_SUCCESS) { lpszPassword = EmailName; } } else { lpszPassword = ""; } } else if (lpszUserName == NULL) { error = ERROR_INVALID_PARAMETER; } } *lplpszUserName = lpszUserName; *lplpszPassword = lpszPassword; return error; } INTERNETAPI_(DWORD) InternetAttemptConnect( IN DWORD dwReserved ) /*++ Routine Description: This routine attempts to make a loopback socket Clients call this to either invoke the dialdialog or to see whether they are connected to the net (??). 4/29/97 (darrenmi) This function now calls InternetAutodial to see if a connection needs to be made. This function Arguments: dwReserved - ? Return Value: DWORD Windows error code, or sockets error code --*/ { DEBUG_ENTER_API((DBG_API, Dword, "InternetAttemptConnect", "%d", dwReserved )); DWORD error = ERROR_SUCCESS; if (!GlobalDataInitialized) { error = GlobalDataInitialize(); if (error != ERROR_SUCCESS) { goto quit; } } if(!InternetAutodial(0, 0)) { error = ERROR_GEN_FAILURE; } quit: DEBUG_LEAVE_API(error); return error; } INTERNETAPI_(BOOL) InternetLockRequestFile( IN HINTERNET hInternet, OUT HANDLE *lphLockReqHandle ) /*++ Routine Description: This routine allows the caller to place a lock on the file that he is using by doing a CreateFile. This ensures that if this file is associated with this url, and another download on this url tries to commit another file, then this file won't vanish because the cache does a safe delete when updating or deleting the cache entry. The caller can then call the InternetUnlockRequestFile to give wininet the permission to delete this file if not committed to the cache. Arguments: hInternet request object which is doing the download lphLocReqHandle place to return LockRequestHandle Return Value: TRUE - Success FALSE - failure, GetLastError returns the error code --*/ { DEBUG_ENTER_API((DBG_API, Bool, "InternetLockRequest", "%#x, %#x", hInternet, lphLockReqHandle )); DWORD error, dwSize, dwUrlLenPlus1, dwFileLenPlus1; HINTERNET_HANDLE_TYPE handleType; HINTERNET hObjectMapped = NULL; INTERNET_CONNECT_HANDLE_OBJECT * pConnect; LPLOCK_REQUEST_INFO lpLockReqInfo = NULL; LPSTR lpSource; BOOL locked = FALSE; if (!GlobalDataInitialized) { error = GlobalDataInitialize(); if (error != ERROR_SUCCESS) { goto Cleanup; } } error = MapHandleToAddress(hInternet, (LPVOID *)&hInternet, FALSE); if (error == ERROR_SUCCESS) { hObjectMapped = hInternet; pConnect = (INTERNET_CONNECT_HANDLE_OBJECT *)hInternet; error = RGetHandleType(hInternet, &handleType); } if (error != ERROR_SUCCESS) { goto Cleanup; } if ((handleType == TypeGenericHandle)|| (handleType == TypeInternetHandle)|| (handleType == TypeFtpConnectHandle)|| (handleType == TypeGopherConnectHandle)|| (handleType == TypeHttpConnectHandle) || (handleType == TypeFileRequestHandle)) { error = ERROR_INVALID_HANDLE; goto Cleanup; } EnterCriticalSection(&LockRequestFileCritSec); locked = TRUE; // // If a lock handle was already created, simply increment the refcount. // if(lpLockReqInfo = (LPLOCK_REQUEST_INFO)(pConnect->GetLockRequestHandle())) { lpLockReqInfo->dwCount++; *lphLockReqHandle = (HANDLE)lpLockReqInfo; error = ERROR_SUCCESS; goto Cleanup; } // // Record the URL and associated filename in the lock handle. // lpSource = pConnect->GetDataFileName(); if (!lpSource) { error = ERROR_FILE_NOT_FOUND; goto Cleanup; } dwSize = sizeof(LOCK_REQUEST_INFO) +(dwUrlLenPlus1 = lstrlen(pConnect->GetCacheKey())+1) +(dwFileLenPlus1 = lstrlen(lpSource)+1)+3; // atmost 3 bytes slop lpLockReqInfo = (LPLOCK_REQUEST_INFO)ALLOCATE_MEMORY(LPTR, dwSize); if (!lpLockReqInfo) { error = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } lpLockReqInfo->dwSignature = LOCK_REQUEST_SIGNATURE; lpLockReqInfo->dwSize = dwSize; lpLockReqInfo->fNoCacheLookup = FALSE; memcpy(lpLockReqInfo->rgBuff, pConnect->GetCacheKey(), dwUrlLenPlus1); lpLockReqInfo->UrlName = lpLockReqInfo->rgBuff; // align filename to DWORD lpLockReqInfo->FileName = &(lpLockReqInfo->rgBuff[((dwUrlLenPlus1+sizeof(DWORD)) & ~(3))]); memcpy(lpLockReqInfo->FileName, lpSource, dwFileLenPlus1); DEBUG_PRINT(INET, INFO, ("Url==%s, File== %s\n", lpLockReqInfo->UrlName, lpLockReqInfo->FileName )); // // Open the file so it will not be deleted upon cache entry delete/update. // lpLockReqInfo->hFile = CreateFile ( lpSource, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (lpLockReqInfo->hFile == INVALID_HANDLE_VALUE) { error = GetLastError(); goto Cleanup; } // // Set refcount to 2, one for connect handle and the other for lock handle. // Whichever one is closed last will perform cleanup. // lpLockReqInfo->dwCount = 2; *lphLockReqHandle = (HANDLE)lpLockReqInfo; pConnect->SetLockRequestHandle((HANDLE)lpLockReqInfo); // Check to see if this file corresponds to an installed cache // entry. If so, set fNoDelete so unlocking cannot delete the file. // Note - because installed cache entries are generally not downloaded // by wininet this is ok to do - it is only necessary to check if there // is a cache entry in the request object and if it is an installed type. if (handleType == TypeHttpRequestHandle) { LPCACHE_ENTRY_INFO pcei; pcei = ((HTTP_REQUEST_HANDLE_OBJECT*) pConnect)->GetCacheEntryInfo(); if (pcei) { if (pcei->CacheEntryType & INSTALLED_CACHE_ENTRY) lpLockReqInfo->fNoDelete = TRUE; } } error = ERROR_SUCCESS; Cleanup: BOOL fRet = (error==ERROR_SUCCESS); if (!fRet) { if (lpLockReqInfo) { FREE_MEMORY(lpLockReqInfo); } DEBUG_ERROR(API, error); SetLastError(error); } else { DEBUG_PRINT(INET, INFO, ("Url==%s, File== %s RefCount=%d, Handle = %#x\n", lpLockReqInfo->UrlName, lpLockReqInfo->FileName, lpLockReqInfo->dwCount, *lphLockReqHandle )); } if (locked) { LeaveCriticalSection(&LockRequestFileCritSec); } if (hObjectMapped) { DereferenceObject((LPVOID)hObjectMapped); } DEBUG_LEAVE_API(fRet); return fRet; } INTERNETAPI_(BOOL) InternetUnlockRequestFile( IN HANDLE hLockHandle ) /*++ Routine Description: This routine allows the caller to unlock a request file that was locked using the InternetLockRequestFile routine. This allows the file to be deleted after the request object is long gone. Arguments: hLockHandle Lock Request Handle that was returned in InternetLockRequestFile Return Value: TRUE - Success FALSE - failure, GetLastError returns the error code --*/ { DEBUG_ENTER_API((DBG_API, Bool, "InternetUnlockRequest", "%#x", hLockHandle )); DWORD error, dwUrlLen, dwFileNameLen; LPLOCK_REQUEST_INFO lpLockReqInfo; if (!GlobalDataInitialized) { error = GlobalDataInitialize(); if (error != ERROR_SUCCESS) { goto quit; } } EnterCriticalSection(&LockRequestFileCritSec); lpLockReqInfo = (LPLOCK_REQUEST_INFO)hLockHandle; __try { if (lpLockReqInfo->dwSignature == LOCK_REQUEST_SIGNATURE) { DEBUG_PRINT(INET, INFO, ("Url==%s, File== %s, refcount=%d\n", lpLockReqInfo->UrlName, lpLockReqInfo->FileName, lpLockReqInfo->dwCount )); if (--lpLockReqInfo->dwCount == 0) { if (!CloseHandle(lpLockReqInfo->hFile)) { DEBUG_PRINT(INET, ERROR, ("Error=%d while Closing OpenHandle for file=%s for url=%s\n", GetLastError(), lpLockReqInfo->FileName, lpLockReqInfo->UrlName )); } if (!lpLockReqInfo->fNoDelete) { // // Validate URL and filename. // dwUrlLen = lstrlen(lpLockReqInfo->UrlName); dwFileNameLen = lstrlen(lpLockReqInfo->FileName); // // Check if there is a cache entry for the URL. // DWORD dwSize; LPINTERNET_CACHE_ENTRY_INFO pCEI; char buf[sizeof(INTERNET_CACHE_ENTRY_INFO)+MAX_PATH+1]; pCEI = (LPINTERNET_CACHE_ENTRY_INFO)buf; dwSize = sizeof(buf); if (lpLockReqInfo->fNoCacheLookup) { error = ERROR_FILE_NOT_FOUND; } else { // Grab info and // Check if the filename actually matches. error = GetUrlCacheEntryInfoEx(lpLockReqInfo->UrlName, pCEI, &dwSize, NULL, NULL, NULL, INTERNET_CACHE_FLAG_ADD_FILENAME_ONLY) ? (lstrcmpi(lpLockReqInfo->FileName, pCEI->lpszLocalFileName) ? ERROR_FILE_NOT_FOUND : ERROR_SUCCESS) : GetLastError(); } // end else if (!lpLockReqInfo->fNoCacheLookup) if (error != ERROR_SUCCESS) { // // The file was not committed to cache, so attempt to delete it. // DEBUG_PRINT(INET, INFO,("deleting %q\n",lpLockReqInfo->FileName)); if (!DeleteFile(lpLockReqInfo->FileName)) { DEBUG_PRINT(INET, ERROR, ("Error=%d while deleting file=%s for url=%s\n", GetLastError(), lpLockReqInfo->FileName, lpLockReqInfo->UrlName )); if (lpLockReqInfo->fNoCacheLookup) { switch (GetLastError()) { case ERROR_SHARING_VIOLATION: case ERROR_ACCESS_DENIED: UrlCacheAddLeakFile (lpLockReqInfo->FileName); } } } // end if (!DeleteFile(...)) } } // end if (!lpLockReqInfo->fNoDelete) FREE_MEMORY(lpLockReqInfo); error = ERROR_SUCCESS; } else { DEBUG_PRINT(INET, INFO, ("Quitting after decrementing refcount, new refcount=%d\n", lpLockReqInfo->dwCount )); error = ERROR_SUCCESS; } } else { error = ERROR_INVALID_PARAMETER; } } __except(EXCEPTION_EXECUTE_HANDLER) { error = ERROR_INVALID_PARAMETER; } ENDEXCEPT LeaveCriticalSection(&LockRequestFileCritSec); quit: BOOL fRet = (error==ERROR_SUCCESS); if (fRet) { DEBUG_ERROR(API, error); SetLastError(error); } DEBUG_LEAVE_API(fRet); return fRet; } INTERNETAPI_(BOOL) InternetCheckConnectionA( IN LPCSTR lpszUrl, IN DWORD dwFlags, IN DWORD dwReserved ) /*++ Routine Description: This routine tells the caller whether he can establish a connection to the network. If no URL is specified and dwFlags are set to NULL then wininet a) checks whether it has an outstanding socket connection and if so then the API returns TRUE. b) If there are no outstanding socket connections then a check is made in the wininet serverdatabase for servers which were connected to in the recent past. If one is found then the API returns TRUE. If neither a) or b) succeeds the API returns FALSE. Arguments: lpszUrl this parameter is an indication to the API to attempt a specific host. The use of this parameter is based on the flags set in the dwFlags parameter dwFlags a bitwise OR of the following flags INTERNET_FLAG_ICC_FORCE_CONNECTION - force a connection A sockets connection is attempted in the following order 1) If lpszUrl is non-NULL then a host value is extracted fromt it used that to ping the specific host 2) If the lpszUrl parameter is NULL then if there is an entry in the wininet's internal server database for the nearest server, it is used to do the pinging If neither of these are available then ERROR_NOT_CONNECTED is returned in GetLastError() dwReserved reserved Return Value: TRUE - Success FALSE - failure, GetLastError returns the error code --*/ { DEBUG_ENTER_API((DBG_API, Bool, "InternetCheckConnectionA", "%s %x", lpszUrl, dwFlags )); DWORD dwError = ERROR_SUCCESS; LPSTR lpszHostName; DWORD dwHostNameLen; INTERNET_PORT ServerPort = INTERNET_DEFAULT_HTTP_PORT; INTERNET_SCHEME ustScheme = INTERNET_SCHEME_HTTP; ICSocket *pSocket = NULL; char buff[INTERNET_MAX_HOST_NAME_LENGTH+1]; LPINTERNET_THREAD_INFO lpThreadInfo; HINTERNET hInternet = NULL, hConnect = NULL, hConnectMapped = NULL; CServerInfo * lpServerInfo = NULL; if (!GlobalDataInitialized) { dwError = GlobalDataInitialize(); if (dwError != ERROR_SUCCESS) { goto Cleanup; } } // if the main sockets database thinks we are // unconditionally offline, then let us give that to the user //if(vSocketsDatabase.IsOffline()) //{ // // dwError = ERROR_NOT_CONNECTED; //} //else { // We are not explicitly in offline mode lpServerInfo = FindNearestServer(); if (dwFlags & FLAG_ICC_FORCE_CONNECTION) { buff[0] = 0; if (lpszUrl) { if (((dwError = CrackUrl( (LPSTR)lpszUrl, // url lstrlen(lpszUrl), // url length FALSE, // escape the URL ? &ustScheme, // scheme type NULL, // scheme name NULL, // scheme length &lpszHostName, // hostname pointer &dwHostNameLen, // hostname length &ServerPort, // port NULL, // UserName NULL, // UserName Length NULL, // Password NULL, // Password Length NULL, // Path NULL, // Path Length NULL, // Extra NULL, // Extra Length NULL // have port? )) == ERROR_SUCCESS)&& (dwHostNameLen<=INTERNET_MAX_HOST_NAME_LENGTH)) { memcpy(buff, lpszHostName, dwHostNameLen); buff[dwHostNameLen] = 0; // make sure we have a valid scheme if(INTERNET_SCHEME_UNKNOWN == ustScheme) { ustScheme = INTERNET_SCHEME_HTTP; } // make sure we have a valid port if(0 == ServerPort) { switch(ustScheme) { case INTERNET_SCHEME_FTP: ServerPort = INTERNET_DEFAULT_FTP_PORT; break; case INTERNET_SCHEME_HTTPS: ServerPort = INTERNET_DEFAULT_HTTPS_PORT; break; default: ServerPort = INTERNET_DEFAULT_HTTP_PORT; break; } } // for our purposes, HTTPS == HTTP if(INTERNET_SCHEME_HTTPS == ustScheme) { ustScheme = INTERNET_SCHEME_HTTP; } // we need the serverinfo struct for this server, not // the nearest one that we already found if(lpServerInfo) { lpServerInfo->Dereference(); } lpServerInfo = FindServerInfo(buff); if(!lpServerInfo) { // new CServerInfo has ref count 1 already // we need to raise it so another thread won't go ahead and destroy this. LockSerializedList(&GlobalServerInfoList); lpServerInfo = new CServerInfo(buff, &dwError, INTERNET_SERVICE_HTTP, 0); if(NULL == lpServerInfo) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } else if (dwError != ERROR_SUCCESS) { delete lpServerInfo; lpServerInfo = NULL; } else { lpServerInfo->Reference(); } UnlockSerializedList(&GlobalServerInfoList); } } } else { if (lpServerInfo) { buff[sizeof(buff)-1] = 0; strncpy(buff, lpServerInfo->GetHostName(), sizeof(buff)-1); } else { // FORCE but no server to try - set error dwError = ERROR_INTERNET_INVALID_OPERATION; goto Cleanup; } } if (buff[0] && lpServerInfo) { // we have a host name, ping it. // This threadinfo/InternetOpen stuff is being done // because the ICSocket class is intertwined with // an internet handle, so we are just getting round that // difficulty. Ideally, ICSocket should have been a // standalone sockets class lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo == NULL) { INET_ASSERT(FALSE); dwError = ERROR_INTERNET_INTERNAL_ERROR; goto Cleanup; } hInternet = InternetOpen("Internal", 0, NULL, NULL, 0 ); if (!hInternet) { dwError = GetLastError(); goto Cleanup; } hConnect = InternetConnect(hInternet, buff, ServerPort, NULL, NULL, ustScheme, 0, 0); if (!hConnect) { dwError = GetLastError(); goto Cleanup; } dwError = MapHandleToAddress(hConnect, (LPVOID *)&hConnectMapped, FALSE); if (dwError != ERROR_SUCCESS) { goto Cleanup; } _InternetSetObjectHandle(lpThreadInfo, hConnect, hConnectMapped); // Ping the server if (pSocket = new ICSocket()) { pSocket->SetPort(ServerPort); dwError = pSocket->SocketConnect( GetTimeoutValue(INTERNET_OPTION_CONNECT_TIMEOUT), GetTimeoutValue(INTERNET_OPTION_CONNECT_RETRIES), 0, lpServerInfo ); if (dwError == ERROR_SUCCESS) { pSocket->Disconnect(); } } else { dwError = ERROR_NOT_ENOUGH_MEMORY; } } } else{ // caller doesn't ask us to force a connection // do the best we can and tell him dwError = (/*vSocketsDatabase.GetSocketCount() ||*/ lpServerInfo)? ERROR_SUCCESS: ERROR_NOT_CONNECTED; } } Cleanup: if (lpServerInfo) { lpServerInfo->Dereference(); } if (pSocket) { pSocket->Dereference(); } if (hConnectMapped) { DereferenceObject((LPVOID)hConnectMapped); } if (hConnect) { InternetCloseHandle(hConnect); } if (hInternet) { InternetCloseHandle(hInternet); } BOOL fRet = (dwError==ERROR_SUCCESS); if (FALSE == fRet) { SetLastError(dwError); DEBUG_ERROR(API, dwError); } DEBUG_LEAVE_API(fRet); return (fRet); }