/*****************************************************************************\ FILE: ftpapi.cpp DESCRIPTION: This file contains functions to perform the following 2 things: 1. WININET WRAPPERS: Wininet APIs have either wierd bugs or bugs that come thru the APIs from the server. It's also important to keep track of the perf impact of each call. These wrappers solve these problems. 2. FTP STRs to PIDLs: These wrappers will take ftp filenames and file paths that come in from the server and turn them into pidls. These pidls contain both a unicode display string and the filename/path in wire bytes for future server requests. \*****************************************************************************/ #include "priv.h" #include "util.h" #include "encoding.h" #include "ftpapi.h" /////////////////////////////////////////////////////////////////////////////////////////// // 1. WININET WRAPPERS: Wininet APIs have either wierd bugs or bugs that come thru the APIs // from the server. It's also important to keep track of the perf impact // of each call. These wrappers solve these problems. /////////////////////////////////////////////////////////////////////////////////////////// /*****************************************************************************\ FUNCTION: FtpSetCurrentDirectoryWrap DESCRIPTION: PERF Notes: [Direct Net Connection] To: shapitst : 1ms To: rigel.cyberpass.net : 140ms - 200ms To: ftp.rz.uni-frankfurt.de : 570ms - 2496ms \*****************************************************************************/ HRESULT FtpSetCurrentDirectoryWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpPath) { HRESULT hr = S_OK; DWORD dwError = 0; // WARNING: FtpSetCurrentDirectory() may fail if it's not really a directory. // PERF: Status FtpGetCurrentDirectory/FtpSetCurrentDirectory() takes // 180-280ms on ftp.microsoft.com on average. // 500-2000ms on ftp://ftp.tu-clausthal.de/ on average // 0-10ms on ftp://shapitst/ on average DEBUG_CODE(DebugStartWatch()); if (!FtpSetCurrentDirectoryA(hConnect, pwFtpPath)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpSetCurrentDirectory(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pwFtpPath, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } /*****************************************************************************\ FUNCTION: FtpGetCurrentDirectoryWrap DESCRIPTION: PERF Notes: These are for short directory listings. Long listings can take 3-10x. [Direct Net Connection] To: shapitst : 1ms - 4ms To: rigel.cyberpass.net : 132ms - 213ms To: ftp.rz.uni-frankfurt.de : 507ms - 2012ms \*****************************************************************************/ HRESULT FtpGetCurrentDirectoryWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPWIRESTR pwFtpPath, DWORD cchCurrentDirectory) { HRESULT hr = S_OK; DWORD dwError = 0; // PERF: Status FtpGetCurrentDirectory/FtpSetCurrentDirectory() takes // 180-280ms on ftp.microsoft.com on average. // 500-2000ms on ftp://ftp.tu-clausthal.de/ on average // 0-10ms on ftp://shapitst/ on average DEBUG_CODE(DebugStartWatch()); if (!FtpGetCurrentDirectoryA(hConnect, pwFtpPath, &cchCurrentDirectory)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpGetCurrentDirectoryA(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pwFtpPath, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } /*****************************************************************************\ FUNCTION: FtpGetFileExWrap DESCRIPTION: PERF Notes: (*** Depends on file size ***) [Direct Net Connection] To: shapitst : 100ms - 1,000ms+ To: rigel.cyberpass.net : 1210ms - 1610ms To: ftp.rz.uni-frankfurt.de : ??????? \*****************************************************************************/ HRESULT FtpGetFileExWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpPath/*Src*/, LPCWSTR pwzFilePath/*Dest*/, BOOL fFailIfExists, DWORD dwFlagsAndAttributes, DWORD dwFlags, DWORD_PTR dwContext) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(DebugStartWatch()); if (!FtpGetFileEx(hConnect, pwFtpPath, pwzFilePath, fFailIfExists, dwFlagsAndAttributes, dwFlags, dwContext)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); if (HRESULT_FROM_WIN32(ERROR_INTERNET_OPERATION_CANCELLED) == hr) hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpGetFileEx(%#08lx, \"%hs\", \"%ls\") returned %u. Time=%lums", hConnect, pwFtpPath, pwzFilePath, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } /*****************************************************************************\ FUNCTION: FtpPutFileExWrap DESCRIPTION: PERF Notes: (*** Depends on file size ***) [Direct Net Connection] To: shapitst : 194ms - 400ms+ To: rigel.cyberpass.net : 1662ms - 8454ms To: ftp.rz.uni-frankfurt.de : ??????? \*****************************************************************************/ HRESULT FtpPutFileExWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWSTR pwzFilePath/*Src*/, LPCWIRESTR pwFtpPath/*Dest*/, DWORD dwFlags, DWORD_PTR dwContext) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(DebugStartWatch()); if (!FtpPutFileEx(hConnect, pwzFilePath, pwFtpPath, dwFlags, dwContext)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpPutFileEx(%#08lx, \"%ls\", \"%hs\") returned %u. Time=%lums", hConnect, pwzFilePath, pwFtpPath, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } /*****************************************************************************\ FUNCTION: FtpDeleteFileWrap DESCRIPTION: PERF Notes: [Direct Net Connection] To: shapitst : 4ms (22ms once in a while) To: rigel.cyberpass.net : 175ms - 291ms To: ftp.rz.uni-frankfurt.de : ??????? \*****************************************************************************/ HRESULT FtpDeleteFileWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpFileName) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(DebugStartWatch()); if (!FtpDeleteFileA(hConnect, pwFtpFileName)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpDeleteFile(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pwFtpFileName, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } /*****************************************************************************\ FUNCTION: FtpRenameFileWrap DESCRIPTION: PERF Notes: [Direct Net Connection] To: shapitst : 4ms To: rigel.cyberpass.net : 329ms - 446ms To: ftp.rz.uni-frankfurt.de : ??????? \*****************************************************************************/ HRESULT FtpRenameFileWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpFileNameExisting, LPCWIRESTR pwFtpFileNameNew) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(DebugStartWatch()); if (!FtpRenameFileA(hConnect, pwFtpFileNameExisting, pwFtpFileNameNew)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpRenameFile(%#08lx, \"%hs\", \"%hs\") returned %u. Time=%lums", hConnect, pwFtpFileNameExisting, pwFtpFileNameNew, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } /*****************************************************************************\ FUNCTION: FtpOpenFileWrap DESCRIPTION: PERF Notes: [Direct Net Connection] To: shapitst : 2ms To: rigel.cyberpass.net : 757ms - 817ms To: ftp.rz.uni-frankfurt.de : 2112ms - 10026ms \*****************************************************************************/ HRESULT FtpOpenFileWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpFileName, DWORD dwAccess, DWORD dwFlags, DWORD_PTR dwContext, HINTERNET * phFileHandle) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(DebugStartWatch()); *phFileHandle = FtpOpenFileA(hConnect, pwFtpFileName, dwAccess, dwFlags, dwContext); if (!*phFileHandle) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpOpenFile(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pwFtpFileName, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } /*****************************************************************************\ FUNCTION: FtpCreateDirectoryWrap DESCRIPTION: PERF Notes: [Direct Net Connection] To: shapitst : 3ms To: rigel.cyberpass.net : 210ms - 350ms To: ftp.rz.uni-frankfurt.de : ??????? \*****************************************************************************/ HRESULT FtpCreateDirectoryWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpPath) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(DebugStartWatch()); if (!FtpCreateDirectoryA(hConnect, pwFtpPath)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpCreateDirectoryA(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pwFtpPath, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } /*****************************************************************************\ FUNCTION: FtpRemoveDirectoryWrap DESCRIPTION: PERF Notes: [Direct Net Connection] To: shapitst : 2ms To: rigel.cyberpass.net : 157ms - 227ms To: ftp.rz.uni-frankfurt.de : ??????? \*****************************************************************************/ HRESULT FtpRemoveDirectoryWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpPath) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(DebugStartWatch()); if (!FtpRemoveDirectoryA(hConnect, pwFtpPath)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpRemoveDirectory(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pwFtpPath, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } /*****************************************************************************\ FUNCTION: FtpFindFirstFileWrap DESCRIPTION: PERF Notes: [Direct Net Connection] To: shapitst : 166ms - 189ms To: rigel.cyberpass.net : 550ms - 815ms To: ftp.rz.uni-frankfurt.de : 1925ms - 11,390ms \*****************************************************************************/ HRESULT FtpFindFirstFileWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFilterStr, LPFTP_FIND_DATA pwfd, DWORD dwINetFlags, DWORD_PTR dwContext, HINTERNET * phFindHandle) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(StrCpyNA(pwfd->cFileName, "", ARRAYSIZE(pwfd->cFileName))); ASSERT(phFindHandle); DEBUG_CODE(DebugStartWatch()); // _UNDOCUMENTED_: If you pass NULL as the second argument, it's the // same as passing TEXT("*.*"), but much faster. // PERF: Status // FtpFindFirstFile() takes 500-700ms on ftp.microsoft.com on average. // takes 2-10 secs on ftp://ftp.tu-clausthal.de/ on average // takes 150-250 secs on ftp://shapitst/ on average *phFindHandle = FtpFindFirstFileA(hConnect, pwFilterStr, pwfd, dwINetFlags, dwContext); if (!*phFindHandle) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpFindFirstFile(\"%hs\")==\"%hs\" atrb=%#08lx, hr=%#08lx, Time=%lums", EMPTYSTR_FOR_NULLA(pwFilterStr), pwfd->cFileName, pwfd->dwFileAttributes, hr, DebugStopWatch())); if (fAssertOnFailure) { // This fails in normal cases when we are checking if files exist. // WININET_ASSERT(SUCCEEDED(hr)); } ASSERT_POINTER_MATCHES_HRESULT(*phFindHandle, hr); return hr; } // Do not localize this because it's always returned from the server in english.g #define SZ_FINDFIRSTFILE_FAILURESTR ": No such file or directory" /**************************************************************\ FUNCTION: FtpDoesFileExist DESCRIPTION: IE #34868 is the fact that some FTP servers erronous results on find requests: [The file does not exist in the following cases] Request: "foo.txt" result: SUCCEEDED & "foo.txt: No such file or directory" Request: "foo bat.txt" result: SUCCEEDED & "foo: No such file or directory" \**************************************************************/ HRESULT FtpDoesFileExist(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFilterStr, LPFTP_FIND_DATA pwfd, DWORD dwINetFlags) { FTP_FIND_DATA wfd; HINTERNET hIntFind; HRESULT hr; if (!pwfd) // pwfd is optional pwfd = &wfd; // Some servers like "ftp://wired/" will fail to find "BVTBaby.gif" even // though it exists. It will find it if "BVTBaby.gif*" is used. // Wininet should fix this or implement this hack but they probably won't. WIRECHAR wFilterStr[MAX_PATH]; StrCpyNA(wFilterStr, pwFilterStr, ARRAYSIZE(wFilterStr)); // WININET WORK AROUND: Wininet won't find "BVTBaby.gif" on an IIS server (ftp://wired/) // unless it has an "*" behind it. So add one if it doesn't exist. if ('*' != wFilterStr[lstrlenA(wFilterStr) - 1]) { // We need to add it. StrCatBuffA(wFilterStr, "*", ARRAYSIZE(wFilterStr)); } hr = FtpFindFirstFileWrap(hConnect, fAssertOnFailure, wFilterStr, pwfd, dwINetFlags, 0, &hIntFind); if (S_OK == hr) { do { // is it an exact match? // #248535: Make sure we get what we asked for. Either WININET or // some weird FTP servers are screwing up. If we ask for // foobar.gif as the filter string, sometimes we get back // ".". if (!StrCmpIA(pwfd->cFileName, pwFilterStr)) { // Yes it "Should" hr = S_OK; break; } else { // However, wininet will return TRUE but the display name will be "One: No such file or directory" // if the file name is "One Two.htm" // This is a work around for bug #34868 because UNIX servers sometimes return success // and a file name of "thefile.txt: No such file or directory" if ((lstrlenA(pwfd->cFileName) > (ARRAYSIZE(SZ_FINDFIRSTFILE_FAILURESTR) - 1)) && !StrCmpA(&(pwfd->cFileName[lstrlenA(pwfd->cFileName) - (ARRAYSIZE(SZ_FINDFIRSTFILE_FAILURESTR) - 1)]), SZ_FINDFIRSTFILE_FAILURESTR)) { hr = S_OK; break; } else hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } // Next... hr = InternetFindNextFileWrap(hIntFind, TRUE, pwfd); } while (S_OK == hr); InternetCloseHandle(hIntFind); } return hr; } /*****************************************************************************\ FUNCTION: FtpCommandWrap DESCRIPTION: PERF Notes: [Direct Net Connection] To: shapitst : 1ms - 12ms To: rigel.cyberpass.net : 133ms - 184ms To: ftp.rz.uni-frankfurt.de : 1711ms - 2000ms \*****************************************************************************/ HRESULT FtpCommandWrap(HINTERNET hConnect, BOOL fAssertOnFailure, BOOL fExpectResponse, DWORD dwFlags, LPCWIRESTR pszCommand, DWORD_PTR dwContext, HINTERNET *phFtpCommand) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(DebugStartWatch()); if (!FtpCommandA(hConnect, fExpectResponse, dwFlags, pszCommand, dwContext, phFtpCommand)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpCommand(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pszCommand, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } /*** TODO INTERNETAPI DWORD WINAPI FtpGetFileSize( IN HINTERNET hFile, OUT LPDWORD lpdwFileSizeHigh OPTIONAL ); ******/ /*****************************************************************************\ FUNCTION: InternetOpenWrap DESCRIPTION: PERF Notes: [Direct Net Connection] Destination not applicable. 677-907ms \*****************************************************************************/ HRESULT InternetOpenWrap(BOOL fAssertOnFailure, LPCTSTR pszAgent, DWORD dwAccessType, LPCTSTR pszProxy, LPCTSTR pszProxyBypass, DWORD dwFlags, HINTERNET * phFileHandle) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(DebugStartWatch()); *phFileHandle = InternetOpen(pszAgent, dwAccessType, pszProxy, pszProxyBypass, dwFlags); if (!*phFileHandle) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetOpen(\"%ls\") returned %u. Time=%lums", pszAgent, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } HRESULT InternetCloseHandleWrap(HINTERNET hInternet, BOOL fAssertOnFailure) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(DebugStartWatch()); if (!InternetCloseHandle(hInternet)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetCloseHandle(%#08lx) returned %u. Time=%lums", hInternet, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } /*****************************************************************************\ FUNCTION: _GetPASVMode() DESCRIPTION: Check for Reg Key {SZ_REGKEY_FTPFOLDER}\{SZ_REGVALUE_FTP_PASV}. If the key exists, then use INTERNET_FLAG_PASSIVE in calls to wininet APIs. \*****************************************************************************/ DWORD _GetPASVMode() { DWORD dwPASVflag = -1; // We don't cache this value because we don't want to require // people to restart the process to pick up any changes. This // isn't a big deal since we are going to hit the net which is // a million times slower than the registery. // // This is why PASV is needed: // Mill #120818: FTP either uses PORT or PASV but only one. We are guaranteed that some // users will have firewalls, switches, or routers that will be incompatible in one of the // methods and support the other. Since there isn't any way for us to take care of this // automatically, we need to give the user the option to choose. We default to PORT // since that is the most compatible (MS Proxy, and others). The user can use the // Advanced Tab of the Internet Control Panel to switch. PASV will work on US West // DSL modems for example. // Give users the options of using PASV if (SHRegGetBoolUSValue(SZ_REGKEY_FTPFOLDER, SZ_REGVALUE_FTP_PASV, FALSE, FALSE)) { dwPASVflag = INTERNET_FLAG_PASSIVE; } else { dwPASVflag = NULL; } return dwPASVflag; } /*****************************************************************************\ FUNCTION: InternetConnectWrap DESCRIPTION: PERF Notes: [Direct Net Connection] To: shapitst : 144ms - 250ms (Min: 2; Max: 1,667ms) To: rigel.cyberpass.net : 717ms - 1006ms To: ftp.rz.uni-frankfurt.de : 2609ms - 14,012ms COMMON ERROR VALUES: These are the return values in these different cases: ERROR_INTERNET_NAME_NOT_RESOLVED: No Proxy & DNS Lookup failed. ERROR_INTERNET_CANNOT_CONNECT: Some Auth Proxies and Netscape's Web/Auth Proxy ERROR_INTERNET_NAME_NOT_RESOLVED: Web Proxy ERROR_INTERNET_TIMEOUT: Invalid or Web Proxy blocked IP Address ERROR_INTERNET_INCORRECT_PASSWORD: IIS & UNIX, UserName may not exist or password for the user may be incorrect on. ERROR_INTERNET_LOGIN_FAILURE: Too many Users on IIS. ERROR_INTERNET_INCORRECT_USER_NAME: I haven't seen it. ERROR_INTERNET_EXTENDED_ERROR: yahoo.com exists, but ftp.yahoo.com doesn't. \*****************************************************************************/ HRESULT InternetConnectWrap(HINTERNET hInternet, BOOL fAssertOnFailure, LPCTSTR pszServerName, INTERNET_PORT nServerPort, LPCTSTR pszUserName, LPCTSTR pszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext, HINTERNET * phFileHandle) { HRESULT hr = S_OK; DWORD dwError = 0; // Call BryanSt if this assert fires. // Did the user turn off FTP Folders? // If so, don't connect. This will fix NT #406423 where the user turned // of FTP Folders because they have a firewall (CISCO filtering Router) // that will kill packets in such a way the caller (WinSock/Wininet) needs // to wait for a timeout. During this timeout, the browser will hang causing // the user to think it crashed. AssertMsg(!SHRegGetBoolUSValue(SZ_REGKEY_FTPFOLDER, SZ_REGKEY_USE_OLD_UI, FALSE, FALSE), TEXT("BUG: We can't hit this code or we will hang the browser for 45 seconds if the user is using a certain kind of proxy. Call BryanSt.")); DEBUG_CODE(DebugStartWatch()); *phFileHandle = InternetConnect(hInternet, pszServerName, nServerPort, pszUserName, pszPassword, dwService, dwFlags | _GetPASVMode(), dwContext); if (!*phFileHandle) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetConnect(%#08lx, \"%ls\", \"%ls\", \"%ls\") returned %u. Time=%lums", hInternet, pszServerName, EMPTYSTR_FOR_NULL(pszUserName), EMPTYSTR_FOR_NULL(pszPassword), dwError, DebugStopWatch())); if (fAssertOnFailure) { // ERROR_INTERNET_NAME_NOT_RESOLVED happens when we are blocked by the // proxy. WININET_ASSERT(SUCCEEDED(hr) || (HRESULT_FROM_WIN32(ERROR_INTERNET_NAME_NOT_RESOLVED) == hr) || (HRESULT_FROM_WIN32(ERROR_INTERNET_LOGIN_FAILURE) == hr) || (HRESULT_FROM_WIN32(ERROR_INTERNET_INCORRECT_PASSWORD) == hr) || (HRESULT_FROM_WIN32(ERROR_INTERNET_INCORRECT_USER_NAME) == hr)); } return hr; } /*****************************************************************************\ FUNCTION: InternetOpenUrlWrap DESCRIPTION: PERF Notes: [Direct Net Connection] To: shapitst : 29ms To: rigel.cyberpass.net : ??????? To: ftp.rz.uni-frankfurt.de : ??????? \*****************************************************************************/ HRESULT InternetOpenUrlWrap(HINTERNET hInternet, BOOL fAssertOnFailure, LPCTSTR pszUrl, LPCTSTR pszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext, HINTERNET * phFileHandle) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(DebugStartWatch()); *phFileHandle = InternetOpenUrl(hInternet, pszUrl, pszHeaders, dwHeadersLength, dwFlags | _GetPASVMode(), dwContext); if (!*phFileHandle) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetOpenUrl(%#08lx, \"%ls\") returned %u. Time=%lums", hInternet, pszUrl, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } HRESULT InternetReadFileWrap(HINTERNET hFile, BOOL fAssertOnFailure, LPVOID pvBuffer, DWORD dwNumberOfBytesToRead, LPDWORD pdwNumberOfBytesRead) { HRESULT hr = S_OK; DWORD dwError = 0; // DEBUG_CODE(DebugStartWatch()); if (!InternetReadFile(hFile, pvBuffer, dwNumberOfBytesToRead, pdwNumberOfBytesRead)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } // DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetReadFile(%#08lx, ToRead=%d, Read=%d) returned %u. Time=%lums", hFile, dwNumberOfBytesToRead, (pdwNumberOfBytesRead ? *pdwNumberOfBytesRead : -1), dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } HRESULT InternetWriteFileWrap(HINTERNET hFile, BOOL fAssertOnFailure, LPCVOID pvBuffer, DWORD dwNumberOfBytesToWrite, LPDWORD pdwNumberOfBytesWritten) { HRESULT hr = S_OK; DWORD dwError = 0; // DEBUG_CODE(DebugStartWatch()); if (!InternetWriteFile(hFile, pvBuffer, dwNumberOfBytesToWrite, pdwNumberOfBytesWritten)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } // DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetWriteFile(%#08lx, ToWrite=%d, Writen=%d) returned %u. Time=%lums", hFile, dwNumberOfBytesToWrite, (pdwNumberOfBytesWritten ? *pdwNumberOfBytesWritten : -1), dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } /*****************************************************************************\ FUNCTION: InternetGetLastResponseInfoWrap DESCRIPTION: PERF Notes: Always takes 0 (zero) ms because it doesn't have to hit the net. \*****************************************************************************/ HRESULT InternetGetLastResponseInfoWrap(BOOL fAssertOnFailure, LPDWORD pdwError, LPWIRESTR pwBuffer, LPDWORD pdwBufferLength) { HRESULT hr = S_OK; DWORD dwDummyError; if (!pdwError) pdwError = &dwDummyError; if (pwBuffer) pwBuffer[0] = 0; DEBUG_CODE(DebugStartWatch()); InternetGetLastResponseInfoA(pdwError, pwBuffer, pdwBufferLength); if (pwBuffer) { DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetGetLastResponseInfo(\"%hs\") took %lu milliseconds", pwBuffer, DebugStopWatch())); } else { DEBUG_CODE(DebugStopWatch()); } if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } HRESULT InternetGetLastResponseInfoDisplayWrap(BOOL fAssertOnFailure, LPDWORD pdwError, LPWSTR pwzBuffer, DWORD cchBufferSize) { LPWIRESTR pwWireResponse; DWORD dwError = 0; DWORD cchResponse = 0; HRESULT hr = InternetGetLastResponseInfoWrap(TRUE, &dwError, NULL, &cchResponse); cchResponse++; /* +1 for the terminating 0 */ pwWireResponse = (LPWIRESTR)LocalAlloc(LPTR, cchResponse * sizeof(WIRECHAR)); if (pwWireResponse) { hr = InternetGetLastResponseInfoWrap(TRUE, &dwError, pwWireResponse, &cchResponse); if (SUCCEEDED(hr)) { CWireEncoding cWireEncoding; hr = cWireEncoding.WireBytesToUnicode(NULL, pwWireResponse, WIREENC_IMPROVE_ACCURACY, pwzBuffer, cchBufferSize); } LocalFree(pwWireResponse); } else hr = E_OUTOFMEMORY; return hr; } INTERNET_STATUS_CALLBACK InternetSetStatusCallbackWrap(HINTERNET hInternet, BOOL fAssertOnFailure, INTERNET_STATUS_CALLBACK pfnInternetCallback) { HRESULT hr = S_OK; DWORD dwError = 0; INTERNET_STATUS_CALLBACK pfnCallBack; DEBUG_CODE(DebugStartWatch()); pfnCallBack = InternetSetStatusCallback(hInternet, pfnInternetCallback); if (!pfnCallBack) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetSetStatusCallback(%#08lx) returned %u. Time=%lums", hInternet, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return pfnCallBack; } HRESULT InternetCheckConnectionWrap(BOOL fAssertOnFailure, LPCTSTR pszUrl, DWORD dwFlags, DWORD dwReserved) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(DebugStartWatch()); if (!InternetCheckConnection(pszUrl, dwFlags, dwReserved)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetCheckConnection(\"%ls\") returned %u. Time=%lums", pszUrl, dwError, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } //#define FEATURE_OFFLINE HRESULT InternetAttemptConnectWrap(BOOL fAssertOnFailure, DWORD dwReserved) { HRESULT hr = S_OK; #ifdef FEATURE_OFFLINE DEBUG_CODE(DebugStartWatch()); hr = HRESULT_FROM_WIN32(InternetAttemptConnect(dwReserved)); DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetAttemptConnect() returned hr=%#08lx. Time=%lums", hr, DebugStopWatch())); if (fAssertOnFailure) { WININET_ASSERT(SUCCEEDED(hr)); } #endif // FEATURE_OFFLINE return hr; } /*****************************************************************************\ FUNCTION: InternetFindNextFileWrap DESCRIPTION: PERF Notes: Always takes 0 (zero) ms because all the work is done in FtpFindFirstFile() \*****************************************************************************/ HRESULT InternetFindNextFileWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPFTP_FIND_DATA pwfd) { HRESULT hr = S_OK; DWORD dwError = 0; DEBUG_CODE(StrCpyNA(pwfd->cFileName, "", ARRAYSIZE(pwfd->cFileName))); DEBUG_CODE(DebugStartWatch()); // Bug #206068 Fix Info: // We need to treat dwFileAttributes = 0x00000000 as a directory // link. We can do this by FtpChangeDirectory() into it, call FtpGetDirectory(), // and then creating a pidl with UrlPath and navigating to it w/o creating history entry if needed. // This will solve the problem that going to ftp://ftp.cdrom.com/pub/ and clicking // on any of the soft links will change into that directory and update the address // bar to show the real destination directory. // // We correctly support SoftLinks within the virtual directory but not out of it. // PERF: The perf of this function normally is nothing because the enum of the entire directory // is done in the FtpFindFirstFile(). It will also cache the results. if (!InternetFindNextFileA(hConnect, pwfd)) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32(dwError); } DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetFindNextFile(%#08lx)==\"%hs\", atrbs=%#08lx, hr=%#08lx, Time=%lums", hConnect, pwfd->cFileName, pwfd->dwFileAttributes, hr, DebugStopWatch())); if (fAssertOnFailure && (HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES) != hr)) { WININET_ASSERT(SUCCEEDED(hr)); } return hr; } /////////////////////////////////////////////////////////////////////////////////////////// // 2. FTP STRs to PIDLs: These wrappers will take ftp filenames and file paths // that come in from the server and turn them into pidls. These pidls contain // both a unicode display string and the filename/path in wire bytes for future // server requests. /////////////////////////////////////////////////////////////////////////////////////////// /*****************************************************************************\ FUNCTION: FtpSetCurrentDirectoryPidlWrap DESCRIPTION: Change the current directory to the one specified. PARAMETERS: pidlFtpPath: If this is NULL, then go to "\". \*****************************************************************************/ HRESULT FtpSetCurrentDirectoryPidlWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCITEMIDLIST pidlFtpPath, BOOL fAbsolute, BOOL fOnlyDirs) { WIRECHAR wFtpPath[MAX_PATH]; LPWIRESTR pwFtpPath = wFtpPath; HRESULT hr = S_OK; // If pidlFtpPath is NULL, then go to "\". if (pidlFtpPath) { hr = GetWirePathFromPidl(pidlFtpPath, wFtpPath, ARRAYSIZE(wFtpPath), fOnlyDirs); if (!fAbsolute) pwFtpPath++; // Skip past the starting '\' } else StrCpyNA(wFtpPath, SZ_URL_SLASHA, ARRAYSIZE(wFtpPath)); if (SUCCEEDED(hr)) hr = FtpSetCurrentDirectoryWrap(hConnect, fAssertOnFailure, pwFtpPath); return hr; } HRESULT FtpGetCurrentDirectoryPidlWrap(HINTERNET hConnect, BOOL fAssertOnFailure, CWireEncoding * pwe, LPITEMIDLIST * ppidlFtpPath) { WIRECHAR wFtpPath[MAX_PATH]; HRESULT hr = FtpGetCurrentDirectoryWrap(hConnect, fAssertOnFailure, wFtpPath, ARRAYSIZE(wFtpPath)); *ppidlFtpPath = NULL; if (SUCCEEDED(hr)) hr = CreateFtpPidlFromFtpWirePath(wFtpPath, pwe, NULL, ppidlFtpPath, TRUE, TRUE); return hr; } HRESULT FtpFindFirstFilePidlWrap(HINTERNET hConnect, BOOL fAssertOnFailure, CMultiLanguageCache * pmlc, CWireEncoding * pwe, LPCWIRESTR pwFilterStr, LPITEMIDLIST * ppidlFtpItem, DWORD dwINetFlags, DWORD_PTR dwContext, HINTERNET * phFindHandle) { FTP_FIND_DATA wfd; *phFindHandle = NULL; HRESULT hr = FtpFindFirstFileWrap(hConnect, fAssertOnFailure, pwFilterStr, &wfd, dwINetFlags, dwContext, phFindHandle); *ppidlFtpItem = NULL; if (SUCCEEDED(hr)) { // Skip "." and ".." entries. if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || IS_VALID_FILE(wfd.cFileName)) hr = pwe->CreateFtpItemID(pmlc, &wfd, ppidlFtpItem); else hr = InternetFindNextFilePidlWrap(*phFindHandle, fAssertOnFailure, pmlc, pwe, ppidlFtpItem); if (FAILED(hr) && *phFindHandle) { InternetCloseHandle(*phFindHandle); *phFindHandle = NULL; } } return hr; } HRESULT InternetFindNextFilePidlWrap(HINTERNET hConnect, BOOL fAssertOnFailure, CMultiLanguageCache * pmlc, CWireEncoding * pwe, LPITEMIDLIST * ppidlFtpItem) { FTP_FIND_DATA wfd; HRESULT hr = InternetFindNextFileWrap(hConnect, fAssertOnFailure, &wfd); *ppidlFtpItem = NULL; if (SUCCEEDED(hr)) { ASSERT(pmlc); // We use this often enought that this might as well exist. // Skip "." and ".." entries. if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || IS_VALID_FILE(wfd.cFileName)) hr = pwe->CreateFtpItemID(pmlc, &wfd, ppidlFtpItem); else hr = InternetFindNextFilePidlWrap(hConnect, fAssertOnFailure, pmlc, pwe, ppidlFtpItem); } return hr; } HRESULT FtpRenameFilePidlWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCITEMIDLIST pidlExisting, LPCITEMIDLIST pidlNew) { return FtpRenameFileWrap(hConnect, fAssertOnFailure, FtpPidl_GetLastItemWireName(pidlExisting), FtpPidl_GetLastItemWireName(pidlNew)); } HRESULT FtpGetFileExPidlWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCITEMIDLIST pidlFtpPath/*Src*/, LPCWSTR pwzFilePath/*Dest*/, BOOL fFailIfExists, DWORD dwFlagsAndAttributes, DWORD dwFlags, DWORD_PTR dwContext) { return FtpGetFileExWrap(hConnect, fAssertOnFailure, FtpPidl_GetLastItemWireName(pidlFtpPath), pwzFilePath, fFailIfExists, dwFlagsAndAttributes, dwFlags, dwContext); }