windows-nt/Source/XPSP1/NT/shell/ext/ftp/ftpapi.cpp

1035 lines
36 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*****************************************************************************\
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 <Down the Hall>: 1ms
To: rigel.cyberpass.net <San Diego, CA>: 140ms - 200ms
To: ftp.rz.uni-frankfurt.de <Germany>: 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 <Down the Hall>: 1ms - 4ms
To: rigel.cyberpass.net <San Diego, CA>: 132ms - 213ms
To: ftp.rz.uni-frankfurt.de <Germany>: 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 <Down the Hall>: 100ms - 1,000ms+
To: rigel.cyberpass.net <San Diego, CA>: 1210ms - 1610ms
To: ftp.rz.uni-frankfurt.de <Germany>: ???????
\*****************************************************************************/
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 <Down the Hall>: 194ms - 400ms+
To: rigel.cyberpass.net <San Diego, CA>: 1662ms - 8454ms
To: ftp.rz.uni-frankfurt.de <Germany>: ???????
\*****************************************************************************/
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 <Down the Hall>: 4ms (22ms once in a while)
To: rigel.cyberpass.net <San Diego, CA>: 175ms - 291ms
To: ftp.rz.uni-frankfurt.de <Germany>: ???????
\*****************************************************************************/
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 <Down the Hall>: 4ms
To: rigel.cyberpass.net <San Diego, CA>: 329ms - 446ms
To: ftp.rz.uni-frankfurt.de <Germany>: ???????
\*****************************************************************************/
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 <Down the Hall>: 2ms
To: rigel.cyberpass.net <San Diego, CA>: 757ms - 817ms
To: ftp.rz.uni-frankfurt.de <Germany>: 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 <Down the Hall>: 3ms
To: rigel.cyberpass.net <San Diego, CA>: 210ms - 350ms
To: ftp.rz.uni-frankfurt.de <Germany>: ???????
\*****************************************************************************/
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 <Down the Hall>: 2ms
To: rigel.cyberpass.net <San Diego, CA>: 157ms - 227ms
To: ftp.rz.uni-frankfurt.de <Germany>: ???????
\*****************************************************************************/
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 <Down the Hall>: 166ms - 189ms
To: rigel.cyberpass.net <San Diego, CA>: 550ms - 815ms
To: ftp.rz.uni-frankfurt.de <Germany>: 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, "<Not Found>", 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 <Down the Hall>: 1ms - 12ms
To: rigel.cyberpass.net <San Diego, CA>: 133ms - 184ms
To: ftp.rz.uni-frankfurt.de <Germany>: 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 <Down the Hall>: 144ms - 250ms (Min: 2; Max: 1,667ms)
To: rigel.cyberpass.net <San Diego, CA>: 717ms - 1006ms
To: ftp.rz.uni-frankfurt.de <Germany>: 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 <Down the Hall>: 29ms
To: rigel.cyberpass.net <San Diego, CA>: ???????
To: ftp.rz.uni-frankfurt.de <Germany>: ???????
\*****************************************************************************/
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, "<Not Found>", 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);
}