Windows2003-3790/inetcore/wininet/ftp/ftpapia.cxx
2020-09-30 16:53:55 +02:00

4667 lines
118 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
ftpapia.cxx
Abstract:
ANSI versions of Windows Internet Client DLL FTP APIs
Contents:
FtpFindFirstFileA
FtpGetFileA
FtpPutFileA
FtpDeleteFileA
FtpRenameFileA
FtpOpenFileA
FtpCreateDirectoryA
FtpRemoveDirectoryA
FtpSetCurrentDirectoryA
FtpGetCurrentDirectoryA
FtpCommandA
FtpGetFileSize
FtpGetSystemNameA
FtpFindNextFileA
FtpReadFile
FtpWriteFile
pFtpGetUrlString
Author:
Heath Hunnicutt [t-heathh] 13-Jul-1994
Environment:
Win32 user-level DLL
Revision History:
09-Mar-1995 rfirth
moved from findfile.c, ftphelp.c
13-Jul-1994 t-heathh
Created
--*/
#include <wininetp.h>
#include "ftpapih.h"
//
// manifests
//
#define DEFAULT_TRANSFER_BUFFER_LENGTH (4 K)
#define ALLOWED_FTP_FLAGS (INTERNET_FLAGS_MASK \
| FTP_TRANSFER_TYPE_MASK \
)
//
// private prototypes
//
PRIVATE
BOOL
FBeginCacheReadProcessing(
IN HINTERNET hFtpSession,
IN LPCSTR lpszFileName,
IN DWORD dwAccess,
IN DWORD dwFlags,
IN DWORD_PTR dwContext,
IN BOOL fIsHtmlFind
);
PRIVATE
BOOL
FFtpCanReadFromCache(
IN HINTERNET hFtpSession
);
PRIVATE
BOOL
FBeginCacheWriteProcessing(
IN HINTERNET hFtpSession,
IN LPCSTR lpszFileName,
IN DWORD dwAccess,
IN DWORD dwFlags,
IN DWORD_PTR dwContext,
IN BOOL fIsHtmlFind
);
PRIVATE
BOOL
FFtpCanWriteToCache(
HINTERNET hFtpSession
);
DWORD
InbLocalEndCacheWrite(
IN HINTERNET hFtpFile,
IN LPSTR lpszFileExtension,
IN BOOL fNormal
);
PRIVATE
BOOL
FGetCWDFromCache(
HINTERNET hFtpSession,
LPSTR lpBuff,
LPDWORD lpdwBuffSize
);
PRIVATE
BOOL
FIsFtpExpired(
HINTERNET handle,
LPCACHE_ENTRY_INFO lpCEI
);
VOID
LocalSetObjectName(
HINTERNET hFtpMapped,
LPSTR lpszFileName
);
PRIVATE
BOOL
IsSearchFileDirectory(
LPCSTR lpszFileDirName
);
//
// functions
//
INTERNETAPI_(HINTERNET) FtpFindFirstFileA(
IN HINTERNET hFtpSession,
IN LPCSTR lpszSearchFile OPTIONAL,
OUT LPWIN32_FIND_DATA lpFindFileData OPTIONAL,
IN DWORD dwFlags,
IN DWORD_PTR dwContext
)
/*++
Routine Description:
Download the remote site's directory listing and parse it into
WIN32_FIND_DATA structures that we can pass back to the app.
If the FTP session is currently involved in a data transfer, such as
a FtpOpenFile()....FtpCloseFile() series of calls, this function will
fail.
Arguments:
hFtpSession - Handle to an FTP session, as returned from FtpOpen()
lpszSearchFile - Pointer to a string containing a file specification
that constrains the search. (e.g., "*.txt"). A NULL
pointer is treated the same as an empty string
lpFindFileData - Pointer to a buffer that will contain WIN32_FIND_DATA
information when this call succeeds. If this parameter
is NULL, then we can still return success, but all find
information will be returned via InternetFindNextFile()
dwFlags - controlling caching, etc.
dwContext - app-supplied context value for call-backs
Return Value:
HINTERNET
Success - new find handle
Failure - NULL. Call GetLastError() for more information:
ERROR_INVALID_HANDLE
The session handle is not recognized
ERROR_FTP_TRANSFER_IN_PROGRESS
The data connection is already in use
ERROR_NO_MORE_FILES
The end of the directory listing has been reached
ERROR_INTERNET_EXTENDED_ERROR
Call InternetGetLastResponseInfo() for the text
ERROR_INTERNET_INTERNAL_ERROR
Something bad happened
--*/
{
DEBUG_ENTER_API((DBG_API,
Handle,
"FtpFindFirstFileA",
"%#x, %.80q, %#x, %#x, %#x",
hFtpSession,
lpszSearchFile,
lpFindFileData,
dwFlags,
dwContext
));
HINTERNET hFind = InternalFtpFindFirstFileA(hFtpSession,
lpszSearchFile,
lpFindFileData,
dwFlags,
dwContext,
FALSE // not a CACHE_ONLY request
);
DEBUG_LEAVE_API(hFind);
return hFind;
}
HINTERNET
InternalFtpFindFirstFileA(
IN HINTERNET hFtpSession,
IN LPCSTR lpszSearchFile OPTIONAL,
OUT LPWIN32_FIND_DATA lpFindFileData OPTIONAL,
IN DWORD dwFlags,
IN DWORD_PTR dwContext,
IN BOOL fCacheOnly,
IN BOOL fAllowEmpty
)
/*++
Routine Description:
Download the remote site's directory listing and parse it into
WIN32_FIND_DATA structures that we can pass back to the app.
If the FTP session is currently involved in a data transfer, such as
a FtpOpenFile()....FtpCloseFile() series of calls, this function will
fail.
Arguments:
hFtpSession - Handle to an FTP session, as returned from FtpOpen()
lpszSearchFile - Pointer to a string containing a file specification
that constrains the search. (e.g., "*.txt"). A NULL
pointer is treated the same as an empty string
lpFindFileData - Pointer to a buffer that will contain WIN32_FIND_DATA
information when this call succeeds. If this parameter
is NULL, then we can still return success, but all find
information will be returned via InternetFindNextFile()
dwFlags - controlling caching, etc.
dwContext - app-supplied context value for call-backs,
fCacheOnly - don't go remote if didn't find in the cache
fAllowEmpty - return handle even if no files found
Return Value:
HINTERNET
Success - new find handle
Failure - NULL. Call GetLastError() for more information:
ERROR_INVALID_HANDLE
The session handle is not recognized
ERROR_FTP_TRANSFER_IN_PROGRESS
The data connection is already in use
ERROR_NO_MORE_FILES
The end of the directory listing has been reached
ERROR_INTERNET_EXTENDED_ERROR
Call InternetGetLastResponseInfo() for the text
ERROR_INTERNET_INTERNAL_ERROR
Something bad happened
--*/
{
DEBUG_ENTER((DBG_FTP,
Handle,
"InternalFtpFindFirstFileA",
"%#x, %.80q, %#x, %#x, %#x, %B, %B",
hFtpSession,
lpszSearchFile,
lpFindFileData,
dwFlags,
dwContext,
fCacheOnly,
fAllowEmpty
));
HINTERNET findHandle = NULL;
HINTERNET hConnectMapped = NULL;
BOOL isLocal;
BOOL isAsync = FALSE;
LPINTERNET_THREAD_INFO lpThreadInfo;
DWORD error;
BOOL bIsWorker = FALSE;
BOOL bNonNestedAsync = FALSE;
if (!GlobalDataInitialized) {
error = ERROR_INTERNET_NOT_INITIALIZED;
goto done;
}
lpThreadInfo = InternetGetThreadInfo();
if (lpThreadInfo == NULL) {
INET_ASSERT(FALSE);
error = ERROR_INTERNET_INTERNAL_ERROR;
goto done;
}
_InternetIncNestingCount();
bIsWorker = lpThreadInfo->IsAsyncWorkerThread;
bNonNestedAsync = bIsWorker && (lpThreadInfo->NestedRequests == 1);
//
// if this is the async part of the request and this function is not nested
// then hFtpSession is actually the mapped address of the find handle
//
if (bNonNestedAsync) {
findHandle = hFtpSession;
hConnectMapped = ((FTP_FIND_HANDLE_OBJECT *)findHandle)->GetParent();
} else {
error = MapHandleToAddress(hFtpSession, (LPVOID *)&hConnectMapped, FALSE);
if ((error != ERROR_SUCCESS) && (hConnectMapped == NULL)) {
goto quit;
}
error = RIsHandleLocal(hConnectMapped,
&isLocal,
&isAsync,
TypeFtpConnectHandle
);
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// validate parameters
//
if ((ARGUMENT_PRESENT(lpFindFileData)
&& IsBadWritePtr(lpFindFileData, sizeof(*lpFindFileData)))
|| (ARGUMENT_PRESENT(lpszSearchFile)
&& IsBadStringPtr(lpszSearchFile, INTERNET_MAX_PATH_LENGTH + 1))) {
error = ERROR_INVALID_PARAMETER;
goto quit;
}
//
// convert NULL search argument to empty string
//
if (!ARGUMENT_PRESENT(lpszSearchFile)) {
lpszSearchFile = "";
}
//
// set the context and handle info and clear last error variables
//
_InternetSetObjectHandle(lpThreadInfo, hFtpSession, hConnectMapped);
_InternetSetContext(lpThreadInfo, dwContext);
_InternetClearLastError(lpThreadInfo);
//
// create the handle object now. This can be used to cancel the async
// operation, or the sync operation if InternetCloseHandle() is called
// from a different thread
//
INET_ASSERT(findHandle == NULL);
error = RMakeFtpFindObjectHandle(hConnectMapped,
&findHandle,
(CLOSE_HANDLE_FUNC)wFtpFindClose,
dwContext
);
if (error != ERROR_SUCCESS) {
INET_ASSERT(findHandle == NULL);
goto quit;
}
//
// add another reference: we need this to protect the handle against
// closure in callbacks and across the async thread transition
//
((HANDLE_OBJECT *)findHandle)->Reference();
//
// this new handle will be used in callbacks
//
_InternetSetObjectHandle(lpThreadInfo,
((HANDLE_OBJECT *)findHandle)->GetPseudoHandle(),
findHandle
);
}
//
// check to see if the data is in the cache. Do it here so that we don't
// waste any time going async if we already have the data locally
//
if (IsSearchFileDirectory(lpszSearchFile)
&& FBeginCacheReadProcessing(findHandle,
lpszSearchFile,
GENERIC_READ,
dwFlags,
dwContext,
((INTERNET_CONNECT_HANDLE_OBJECT *)hConnectMapped)
->IsHtmlFind()))// doing html finds
{
error = ERROR_SUCCESS;
if (lpFindFileData) {
DWORD dwBytes = sizeof(WIN32_FIND_DATA);
error = ((INTERNET_CONNECT_HANDLE_OBJECT *)findHandle)->ReadCache(
(LPBYTE)lpFindFileData,
sizeof(WIN32_FIND_DATA),
&dwBytes
);
}
if (error == ERROR_SUCCESS) {
goto quit;
} else {
((INTERNET_CONNECT_HANDLE_OBJECT *)findHandle)->EndCacheRetrieval();
}
}
if (!((INTERNET_CONNECT_HANDLE_OBJECT *)findHandle)->IsCacheReadInProgress()) {
// if this is a cacheonly request or we are in OFFLINE mode
// then fail
if (fCacheOnly ||
((((INTERNET_CONNECT_HANDLE_OBJECT *)findHandle)->
GetInternetOpenFlags() | dwFlags) & INTERNET_FLAG_OFFLINE)) {
error = ERROR_PATH_NOT_FOUND;
goto quit;
}
}
//
// the data wasn't in the cache. If the app requested async operation and
// we are in the app's synchronous thread context then queue the request to
// the async scheduler
//
if (!bIsWorker && isAsync && (dwContext != INTERNET_NO_CALLBACK)) {
CFsm_FtpFindFirstFile * pFsm = new CFsm_FtpFindFirstFile(
lpszSearchFile,
lpFindFileData,
dwFlags,
dwContext
);
if (pFsm != NULL &&
pFsm->GetError() == ERROR_SUCCESS)
{
if (error == ERROR_SUCCESS) {
error = pFsm->QueueWorkItem();
if (error == ERROR_IO_PENDING) {
hConnectMapped = NULL;
findHandle = NULL;
}
}
}
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;
}
//
// if we're here then we're on the synchronous path: either async I/O was
// not requested, or we failed to make the request asynchronous
//
HINTERNET protocolFtpHandle;
error = RGetLocalHandle(hConnectMapped, &protocolFtpHandle);
if (error == ERROR_SUCCESS) {
HINTERNET protocolFindHandle;
error = wFtpFindFirstFile(protocolFtpHandle,
lpszSearchFile,
lpFindFileData,
&protocolFindHandle
);
if (error == ERROR_NO_MORE_FILES && fAllowEmpty) {
//
// The directory is empty. Allow a handle to be returned,
// but mark it empty so FtpFindNextFile doesn't complain.
//
((FTP_FIND_HANDLE_OBJECT *) findHandle)->SetIsEmpty();
error = ERROR_SUCCESS;
}
if (error == ERROR_SUCCESS) {
((FTP_FIND_HANDLE_OBJECT *)findHandle)->SetFindHandle(
protocolFindHandle
);
}
}
//
// if we succeeded in getting the data, add it to the cache
//
if (error == ERROR_SUCCESS) {
//
// don't worry about errors if cache write fails
//
if (IsSearchFileDirectory(lpszSearchFile) && FBeginCacheWriteProcessing(findHandle,
lpszSearchFile,
GENERIC_READ,
dwFlags,
dwContext,
((INTERNET_CONNECT_HANDLE_OBJECT *)hConnectMapped)
->IsHtmlFind()// doing html finds
)) {
if (!((INTERNET_CONNECT_HANDLE_OBJECT *)hConnectMapped)->IsHtmlFind()
&& lpFindFileData) {
DWORD dwBytes = sizeof(WIN32_FIND_DATA);
DWORD errorCache;
errorCache = ((INTERNET_CONNECT_HANDLE_OBJECT *)findHandle)->
WriteCache((LPBYTE)lpFindFileData,
sizeof(WIN32_FIND_DATA)
);
if (errorCache != ERROR_SUCCESS) {
InbLocalEndCacheWrite(findHandle,
NULL,
(errorCache == ERROR_NO_MORE_FILES)
);
}
}
}
}
quit:
_InternetDecNestingCount(1);
if ((!bNonNestedAsync
|| ((error != ERROR_SUCCESS) && (error != ERROR_IO_PENDING)))
&& (findHandle != NULL)) {
//
// balance the extra reference we added to protect against closure in
// callbacks and across the async thread transition. If non-nested
// async request, this will be accomplished after REQUEST_COMPLETE
// callback
//
if (((HANDLE_OBJECT *)findHandle)->Dereference()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
}
}
done:
if (error == ERROR_SUCCESS) {
//
// success - return generated pseudo-handle
//
findHandle = ((HANDLE_OBJECT *)findHandle)->GetPseudoHandle();
} else {
if (bNonNestedAsync) {
if (((HANDLE_OBJECT *)findHandle)->IsInvalidated()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
}
}
if ((error != ERROR_IO_PENDING) && (findHandle != NULL)) {
_InternetCloseHandle(((HANDLE_OBJECT *)findHandle)->GetPseudoHandle());
if (bNonNestedAsync) {
//
// this handle deref'd at async completion
//
hConnectMapped = NULL;
}
if (lpThreadInfo) {
_InternetSetContext(lpThreadInfo, dwContext);
}
}
findHandle = NULL;
}
if (hConnectMapped != NULL) {
DereferenceObject((LPVOID)hConnectMapped);
}
if (error != ERROR_SUCCESS) {
DEBUG_ERROR(API, error);
SetLastError(error);
}
DEBUG_LEAVE(findHandle);
return findHandle;
}
INTERNETAPI_(BOOL) FtpGetFileA(
IN HINTERNET hFtpSession,
IN LPCSTR lpszRemoteFile,
IN LPCSTR lpszNewFile,
IN BOOL fFailIfExists,
IN DWORD dwFlagsAndAttributes,
IN DWORD dwFlags,
IN DWORD_PTR dwContext
)
/*++
Routine Description:
This is a 'wrapper' function that opens/creates a local file and calls other
FTP APIs to copy a file from an FTP server to the local file.
This API does not get remoted, although APIs called herein do
Arguments:
hFtpSession - identifies the FTP server where the file resides
lpszRemoteFile - name of the file on the server to get
lpszNewFile - name of the local file to create
fFailIfExists - TRUE if we should not overwrite an existing file
dwFlagsAndAttributes - various flags
dwFlags - how to transfer the file: as ASCII text or binary
and open options
dwContext - app-supplied context value for call-backs
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Use GetLastError() for more info
--*/
{
DEBUG_ENTER_API((DBG_API,
Bool,
"FtpGetFileA",
"%#x, %q, %q, %B, %#x, %#x, %#x",
hFtpSession,
lpszRemoteFile,
lpszNewFile,
fFailIfExists,
dwFlagsAndAttributes,
dwFlags,
dwContext
));
BOOL fSuccess = FALSE;
DWORD error = ERROR_SUCCESS;
PWSTR pwszRemoteFile = NULL, pwszNewFile = NULL;
DWORD cc;
if (IsBadStringPtr(lpszRemoteFile, INTERNET_MAX_PATH_LENGTH + 1)
|| (*lpszRemoteFile == '\0')
|| IsBadStringPtr(lpszNewFile, INTERNET_MAX_PATH_LENGTH + 1)
|| (*lpszNewFile == '\0'))
{
error = ERROR_INVALID_PARAMETER;
goto done;
}
cc = MultiByteToWideChar(CP_ACP, 0, lpszRemoteFile, -1, NULL, 0);
pwszRemoteFile = (PWSTR)ALLOCATE_FIXED_MEMORY((cc*sizeof(WCHAR)));
if (!pwszRemoteFile)
{
error = ERROR_NOT_ENOUGH_MEMORY;
goto done;
}
MultiByteToWideChar(CP_ACP, 0, lpszRemoteFile, -1, pwszRemoteFile, cc);
cc = MultiByteToWideChar(CP_ACP, 0, lpszNewFile, -1, NULL, 0);
pwszNewFile = (PWSTR)ALLOCATE_FIXED_MEMORY((cc*sizeof(WCHAR)));
if (!pwszNewFile)
{
error = ERROR_NOT_ENOUGH_MEMORY;
goto done;
}
MultiByteToWideChar(CP_ACP, 0, lpszNewFile, -1, pwszNewFile, cc);
fSuccess = FtpGetFileW(
hFtpSession,
pwszRemoteFile,
pwszNewFile,
fFailIfExists,
dwFlagsAndAttributes,
dwFlags,
dwContext);
done:
if (pwszRemoteFile)
{
FREE_MEMORY(pwszRemoteFile);
}
if (pwszNewFile)
{
FREE_MEMORY(pwszNewFile);
}
if (error != ERROR_SUCCESS) {
SetLastError(error);
DEBUG_ERROR(API, error);
}
DEBUG_LEAVE_API(fSuccess);
return fSuccess;
}
INTERNETAPI_(BOOL) FtpPutFileA(
IN HINTERNET hFtpSession,
IN LPCSTR lpszLocalFile,
IN LPCSTR lpszNewRemoteFile,
IN DWORD dwFlags,
IN DWORD_PTR dwContext
)
/*++
Routine Description:
This is a 'wrapper' function that opens/creates a local file and calls other
FTP APIs to copy a file from an FTP server to the local file.
This API does not get remoted, although APIs called herein do
BUGBUG - this API is virtually the same as FtpGetFileA(). Check out
possibility of commonalizing
Arguments:
hFtpSession - identifies the FTP server where the file resides
lpszLocalFile - name of the local file to upload
lpszNewRemoteFile - name of the file on the server to create
dwFlags - how to transfer the file: as ASCII text or binary and
open options
dwContext - app-supplied context value for call-backs
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Use GetLastError() for more info
--*/
{
DEBUG_ENTER_API((DBG_API,
Bool,
"FtpPutFileA",
"%#x, %q, %q, %#x, %#x",
hFtpSession,
lpszLocalFile,
lpszNewRemoteFile,
dwFlags,
dwContext
));
BOOL fSuccess = FALSE;
DWORD error = ERROR_SUCCESS;
PWSTR pwszRemoteFile = NULL, pwszNewFile = NULL;
DWORD cc;
if (IsBadStringPtr(lpszNewRemoteFile, INTERNET_MAX_PATH_LENGTH + 1)
|| (*lpszNewRemoteFile == '\0')
|| IsBadStringPtr(lpszLocalFile, INTERNET_MAX_PATH_LENGTH + 1)
|| (*lpszLocalFile == '\0'))
{
error = ERROR_INVALID_PARAMETER;
goto done;
}
cc = MultiByteToWideChar(CP_ACP, 0, lpszNewRemoteFile, -1, NULL, 0);
pwszRemoteFile = (PWSTR)ALLOCATE_FIXED_MEMORY((cc*sizeof(WCHAR)));
if (!pwszRemoteFile)
{
error = ERROR_NOT_ENOUGH_MEMORY;
goto done;
}
MultiByteToWideChar(CP_ACP, 0, lpszNewRemoteFile, -1, pwszRemoteFile, cc);
cc = MultiByteToWideChar(CP_ACP, 0, lpszLocalFile, -1, NULL, 0);
pwszNewFile = (PWSTR)ALLOCATE_FIXED_MEMORY((cc*sizeof(WCHAR)));
if (!pwszNewFile)
{
error = ERROR_NOT_ENOUGH_MEMORY;
goto done;
}
MultiByteToWideChar(CP_ACP, 0, lpszLocalFile, -1, pwszNewFile, cc);
fSuccess = FtpPutFileW(
hFtpSession,
pwszNewFile,
pwszRemoteFile,
dwFlags,
dwContext);
done:
if (pwszRemoteFile)
{
FREE_MEMORY(pwszRemoteFile);
}
if (pwszNewFile)
{
FREE_MEMORY(pwszNewFile);
}
if (error != ERROR_SUCCESS) {
SetLastError(error);
DEBUG_ERROR(API, error);
}
DEBUG_LEAVE_API(fSuccess);
return fSuccess;
}
INTERNETAPI_(BOOL) FtpDeleteFileA(
IN HINTERNET hFtpSession,
IN LPCSTR lpszFileName
)
/*++
Routine Description:
Deletes the named file at the FTP server
Arguments:
hFtpSession - identifies FTP server where file is to be deleted
lpszFileName - name of file to delete
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Use GetLastError() for more info
--*/
{
DEBUG_ENTER_API((DBG_API,
Bool,
"FtpDeleteFileA",
"%#x, %q",
hFtpSession,
lpszFileName
));
DWORD error;
LPINTERNET_THREAD_INFO lpThreadInfo;
DWORD nestingLevel = 0;
HINTERNET hMapped = NULL;
BOOL fDeref = TRUE;
if (!GlobalDataInitialized) {
error = ERROR_INTERNET_NOT_INITIALIZED;
goto done;
}
lpThreadInfo = InternetGetThreadInfo();
if (lpThreadInfo == NULL) {
INET_ASSERT(FALSE);
error = ERROR_INTERNET_INTERNAL_ERROR;
goto done;
}
_InternetIncNestingCount();
nestingLevel = 1;
//
// map the handle
//
error = MapHandleToAddress(hFtpSession, (LPVOID *)&hMapped, FALSE);
if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
goto quit;
}
//
// set the context and handle info and clear last error variables
//
_InternetSetObjectHandle(lpThreadInfo, hFtpSession, hMapped);
_InternetSetContext(lpThreadInfo,
((INTERNET_HANDLE_OBJECT *)hMapped)->GetContext()
);
_InternetClearLastError(lpThreadInfo);
//
// quit now if the handle is invalid
//
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// validate handle
//
BOOL isLocal;
BOOL isAsync;
error = RIsHandleLocal(hMapped,
&isLocal,
&isAsync,
TypeFtpConnectHandle
);
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// perform sync work
//
if (!lpThreadInfo->IsAsyncWorkerThread
|| (lpThreadInfo->NestedRequests > 1)) {
//
// validate parameters
//
if (IsBadStringPtr(lpszFileName, INTERNET_MAX_PATH_LENGTH + 1)
|| (*lpszFileName == '\0')) {
error = ERROR_INVALID_PARAMETER;
goto quit;
}
// in offline mode modifications are disallowed
// someday we will do internet briefcase but not today
// BUGBUG there is hole in this API, there is no dwFlags
// so there is no way to know whether these operations are
// happening online or offline
if (((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->GetInternetOpenFlags() &
INTERNET_FLAG_OFFLINE) {
error = ERROR_WRITE_PROTECT;
goto quit;
}
if (!lpThreadInfo->IsAsyncWorkerThread && isAsync) {
// MakeAsyncRequest
CFsm_FtpDeleteFile * pFsm;
pFsm = new CFsm_FtpDeleteFile(hFtpSession, lpszFileName);
if (pFsm != NULL &&
pFsm->GetError() == ERROR_SUCCESS)
{
error = pFsm->QueueWorkItem();
if ( error == ERROR_IO_PENDING ) {
fDeref = FALSE;
}
}
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;
}
}
LocalSetObjectName(hMapped, (LPSTR)lpszFileName);
HINTERNET ftpHandle;
error = RGetLocalHandle(hMapped, &ftpHandle);
if (error == ERROR_SUCCESS) {
error = wFtpDeleteFile(ftpHandle, lpszFileName);
if (error == ERROR_SUCCESS) {
((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->ExpireDependents();
}
}
quit:
if ((hMapped != NULL) && fDeref) {
DereferenceObject((LPVOID)hMapped);
}
_InternetDecNestingCount(nestingLevel);;
done:
BOOL success;
if (error != ERROR_SUCCESS) {
SetLastError(error);
success = FALSE;
DEBUG_ERROR(API, error);
} else {
success = TRUE;
}
DEBUG_LEAVE_API(success);
return success;
}
INTERNETAPI_(BOOL) FtpRenameFileA(
IN HINTERNET hFtpSession,
IN LPCSTR lpszExisting,
IN LPCSTR lpszNew
)
/*++
Routine Description:
Renames a file on an FTP server
Arguments:
hFtpSession - identifies FTP server where file is to be renamed
lpszExisting - current file name
lpszNew - new file name
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Use GetLastError() for more info
--*/
{
DEBUG_ENTER_API((DBG_API,
Bool,
"FtpRenameFileA",
"%#x, %q, %q",
hFtpSession,
lpszExisting,
lpszNew
));
DWORD error;
LPINTERNET_THREAD_INFO lpThreadInfo;
DWORD nestingLevel = 0;
HINTERNET hMapped = NULL;
BOOL fDeref = TRUE;
if (!GlobalDataInitialized) {
error = ERROR_INTERNET_NOT_INITIALIZED;
goto done;
}
//
// get 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(hFtpSession, (LPVOID *)&hMapped, FALSE);
if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
goto quit;
}
//
// set the context and handle info and clear last error variables
//
_InternetSetObjectHandle(lpThreadInfo, hFtpSession, hMapped);
_InternetSetContext(lpThreadInfo,
((INTERNET_HANDLE_OBJECT *)hMapped)->GetContext()
);
_InternetClearLastError(lpThreadInfo);
//
// quit now if the handle is invalid
//
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// validate the handle
//
BOOL isLocal;
BOOL isAsync;
error = RIsHandleLocal(hMapped,
&isLocal,
&isAsync,
TypeFtpConnectHandle
);
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// perform sync work
//
if (!lpThreadInfo->IsAsyncWorkerThread
|| (lpThreadInfo->NestedRequests > 1))
{
//
// validate parameters
//
if (IsBadStringPtr(lpszExisting, INTERNET_MAX_PATH_LENGTH + 1)
|| (*lpszExisting == '\0')
|| IsBadStringPtr(lpszNew, INTERNET_MAX_PATH_LENGTH + 1)
|| (*lpszNew == '\0')) {
error = ERROR_INVALID_PARAMETER;
goto quit;
}
// in offline mode modifications are disallowed
// someday we will do internet briefcase but not today
// BUGBUG there is hole in this API, there is no dwFlags
// so there is no way to know whether these operations are
// happening online or offline
if (((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->GetInternetOpenFlags() &
INTERNET_FLAG_OFFLINE) {
error = ERROR_WRITE_PROTECT;
goto quit;
}
if (!lpThreadInfo->IsAsyncWorkerThread && isAsync) {
// MakeAsyncRequest
CFsm_FtpRenameFile * pFsm;
pFsm = new CFsm_FtpRenameFile(hFtpSession, lpszExisting, lpszNew);
if (pFsm != NULL &&
pFsm->GetError() == ERROR_SUCCESS)
{
error = pFsm->QueueWorkItem();
if ( error == ERROR_IO_PENDING ) {
fDeref = FALSE;
}
}
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;
}
}
HINTERNET ftpHandle;
LocalSetObjectName(hMapped, (LPSTR)lpszExisting);
error = RGetLocalHandle(hMapped, &ftpHandle);
if (error == ERROR_SUCCESS) {
error = wFtpRenameFile(ftpHandle,
lpszExisting,
lpszNew
);
if (error == ERROR_SUCCESS) {
((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->ExpireDependents();
}
}
quit:
if ((hMapped != NULL) && fDeref) {
DereferenceObject((LPVOID)hMapped);
}
_InternetDecNestingCount(nestingLevel);;
done:
BOOL success;
if (error != ERROR_SUCCESS) {
SetLastError(error);
success = FALSE;
DEBUG_ERROR(API, error);
} else {
success = TRUE;
}
DEBUG_LEAVE_API(success);
return success;
}
INTERNETAPI_(HINTERNET) FtpOpenFileA(
IN HINTERNET hFtpSession,
IN LPCSTR lpszFileName,
IN DWORD dwAccess,
IN DWORD dwFlags,
IN DWORD_PTR dwContext
)
/*++
Routine Description:
Sets up the FTP session to read or write a file at the FTP server
Arguments:
hFtpSession - InternetConnect handle identifying FTP server
lpszFileName - name of file to open
dwAccess - how to access file - for read or write. Can be one of:
- GENERIC_READ
- GENERIC_WRITE
dwFlags - how to transfer file - ASCII text, or binary and open
options. Can be any or all of the following:
- INTERNET_FLAG_RELOAD
- INTERNET_FLAG_RAW_DATA (passed through by
InternetOpenUrl(), meaningless here)
- INTERNET_FLAG_EXISTING_CONNECT (passed through by
InternetOpenUrl(), meaningless here)
- FTP_TRANSFER_TYPE_XXX
dwContext - app-supplied context value for call-backs
Return Value:
HINTERNET
Success - handle of FTP file object
Failure - NULL. Use GetLastError() for more info
--*/
{
DEBUG_ENTER_API((DBG_API,
Handle,
"FtpOpenFileA",
"%#x, %q, %#x, %#x, %#x",
hFtpSession,
lpszFileName,
dwAccess,
dwFlags,
dwContext
));
HINTERNET hFile = InternalFtpOpenFileA(hFtpSession,
lpszFileName,
dwAccess,
dwFlags,
dwContext,
FALSE // this is not a cachonly request
);
DEBUG_LEAVE_API(hFile);
return hFile;
}
HINTERNET
InternalFtpOpenFileA(
IN HINTERNET hFtpSession,
IN LPCSTR lpszFileName,
IN DWORD dwAccess,
IN DWORD dwFlags,
IN DWORD_PTR dwContext,
IN BOOL fCacheOnly
)
/*++
Routine Description:
Sets up the FTP session to read or write a file at the FTP server
Arguments:
hFtpSession - InternetConnect handle identifying FTP server
lpszFileName - name of file to open
dwAccess - how to access file - for read or write. Can be one of:
- GENERIC_READ
- GENERIC_WRITE
dwFlags - how to transfer file - ASCII text, or binary and open
options. Can be any or all of the following:
- INTERNET_FLAG_RELOAD
- INTERNET_FLAG_RAW_DATA (passed through by
InternetOpenUrl(), meaningless here)
- INTERNET_FLAG_EXISTING_CONNECT (passed through by
InternetOpenUrl(), meaningless here)
- FTP_TRANSFER_TYPE_XXX
dwContext - app-supplied context value for call-backs
fCacheOnly - TRUE if this operation must be satisfied from cache
Return Value:
HINTERNET
Success - handle of FTP file object
Failure - NULL. Use GetLastError() for more info
--*/
{
DEBUG_ENTER((DBG_FTP,
Handle,
"InternalFtpOpenFileA",
"%#x, %q, %#x, %#x, %#x, %B",
hFtpSession,
lpszFileName,
dwAccess,
dwFlags,
dwContext,
fCacheOnly
));
HINTERNET fileHandle = NULL;
HINTERNET hConnectMapped;
HINTERNET hObject;
HINTERNET hObjectMapped = NULL;
BOOL bNonNestedAsync = FALSE;
LPINTERNET_THREAD_INFO lpThreadInfo;
DWORD error;
DWORD nestingLevel = 0;
if (!GlobalDataInitialized) {
error = ERROR_INTERNET_NOT_INITIALIZED;
goto done;
}
//
// get the thread info block
//
lpThreadInfo = InternetGetThreadInfo();
if (lpThreadInfo == NULL) {
INET_ASSERT(FALSE);
error = ERROR_INTERNET_INTERNAL_ERROR;
goto done;
}
bNonNestedAsync = lpThreadInfo->IsAsyncWorkerThread
&& (lpThreadInfo->NestedRequests == 0);
_InternetIncNestingCount();
nestingLevel = 1;
//
// if this is the async worker thread AND we haven't been called from
// another API which is running asynchronously, then what we think is
// hFtpSession is really the file handle object. Get the handles in the
// right variables
//
if (bNonNestedAsync) {
hObject = hFtpSession;
error = MapHandleToAddress(hObject, (LPVOID *)&hObjectMapped, FALSE);
if ((error != ERROR_SUCCESS) && (hObjectMapped == NULL)) {
goto quit;
}
fileHandle = hObjectMapped;
hConnectMapped = ((FTP_FILE_HANDLE_OBJECT *)fileHandle)->GetParent();
} else {
error = MapHandleToAddress(hFtpSession, (LPVOID *)&hConnectMapped, FALSE);
if ((error != ERROR_SUCCESS) && (hConnectMapped == NULL)) {
goto quit;
}
hObject = hFtpSession;
hObjectMapped = hConnectMapped;
}
//
// set the context and handle info and clear last error variables
//
_InternetSetObjectHandle(lpThreadInfo, hObject, hObjectMapped);
_InternetSetContext(lpThreadInfo, dwContext);
_InternetClearLastError(lpThreadInfo);
//
// quit now if the handle is invalid
//
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// validate handle
//
BOOL isLocal;
BOOL isAsync;
error = RIsHandleLocal(hConnectMapped,
&isLocal,
&isAsync,
TypeFtpConnectHandle
);
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// perform sync work
//
if (!lpThreadInfo->IsAsyncWorkerThread
|| (lpThreadInfo->NestedRequests > 1)) {
//
// validate parameters
//
if (IsBadStringPtr(lpszFileName, INTERNET_MAX_PATH_LENGTH + 1)
|| (*lpszFileName == '\0')
//
// dwAccess must be GENERIC_READ or GENERIC_WRITE, but not both, and
// can't be zero or have undefined bits set. Comparing for equality
// works
//
|| ((dwAccess != GENERIC_READ) && (dwAccess != GENERIC_WRITE))
//
// must be a recognized transfer type
//
|| (((dwFlags & FTP_TRANSFER_TYPE_MASK) != 0)
? (((dwFlags & FTP_TRANSFER_TYPE_MASK) != FTP_TRANSFER_TYPE_ASCII)
&& ((dwFlags & FTP_TRANSFER_TYPE_MASK) != FTP_TRANSFER_TYPE_BINARY))
: FALSE)
|| ((dwFlags & ~ALLOWED_FTP_FLAGS) != 0)) {
error = ERROR_INVALID_PARAMETER;
goto quit;
}
//
// use default transfer type if so requested
//
if ((dwFlags & FTP_TRANSFER_TYPE_MASK) == 0) {
dwFlags |= FTP_TRANSFER_TYPE_BINARY;
}
//
// create the handle object now. This can be used to cancel the async
// operation, or the sync operation if InternetCloseHandle() is called
// from a different thread
//
INET_ASSERT(fileHandle == NULL);
error = RMakeFtpFileObjectHandle(hConnectMapped,
&fileHandle,
(CLOSE_HANDLE_FUNC)wFtpCloseFile,
dwContext
);
if (error != ERROR_SUCCESS) {
INET_ASSERT(fileHandle == NULL);
goto quit;
}
//
// add reference to keep handle alive during callbacks and across
// async thread transition
//
((HANDLE_OBJECT *)fileHandle)->Reference();
//
// this new handle will be used in callbacks
//
_InternetSetObjectHandle(lpThreadInfo,
((HANDLE_OBJECT *)fileHandle)->GetPseudoHandle(),
fileHandle
);
}
if (((FTP_FILE_HANDLE_OBJECT *)fileHandle)->SetFileName(lpszFileName) != ERROR_SUCCESS)
{
error = ERROR_NOT_ENOUGH_MEMORY;
goto quit;
}
//
// check to see if the data is in the cache
//
if (FBeginCacheReadProcessing(fileHandle,
lpszFileName,
dwAccess,
dwFlags,
dwContext, FALSE)) {
error = ERROR_SUCCESS;
goto quit;
} else {
if (fCacheOnly ||
((((INTERNET_CONNECT_HANDLE_OBJECT *)fileHandle)->
GetInternetOpenFlags() | dwFlags )& INTERNET_FLAG_OFFLINE)) {
// if we are offline,or doing cacheonly request
// let us give the right error and quit
error = ERROR_FILE_NOT_FOUND;
goto quit;
}
}
if (!lpThreadInfo->IsAsyncWorkerThread
&& isAsync
&& (dwContext != INTERNET_NO_CALLBACK)) {
// MakeAsyncRequest
CFsm_FtpOpenFile * pFsm;
pFsm = new CFsm_FtpOpenFile(((HANDLE_OBJECT *)fileHandle)->GetPseudoHandle(),
dwContext,
lpszFileName,
dwAccess,
dwFlags
);
if (pFsm != NULL &&
pFsm->GetError() == ERROR_SUCCESS)
{
error = pFsm->QueueWorkItem();
if (error == ERROR_IO_PENDING) {
fileHandle = NULL;
hObjectMapped = NULL;
}
}
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;
}
HINTERNET protocolFtpHandle;
error = RGetLocalHandle(hConnectMapped, &protocolFtpHandle);
if (error == ERROR_SUCCESS) {
HINTERNET protocolFileHandle;
error = wFtpOpenFile(protocolFtpHandle,
lpszFileName,
dwAccess,
dwFlags,
&protocolFileHandle
);
if (error == ERROR_SUCCESS) {
((FTP_FILE_HANDLE_OBJECT *)fileHandle)->SetFileHandle(
protocolFileHandle
);
// Now seems like a reasonable time to remove the entry from the
// cache IFF the open has GENERIC_WRITE access set, i.e. the
// caller is about to write to this url and make the cache entry
// stale. This fixes a problem in FtpParseUrl where we bypass the expiry
// info by forcing an offline state.
if (dwAccess & GENERIC_WRITE) {
DeleteUrlCacheEntryA(((FTP_FILE_HANDLE_OBJECT *)fileHandle)->GetURL());
}
}
}
if (error == ERROR_SUCCESS) {
//
// don't worry about errors if cache write does not begin
//
FBeginCacheWriteProcessing(fileHandle,
lpszFileName,
dwAccess,
dwFlags,
dwContext,
FALSE // not a find
);
}
quit:
if (fileHandle != NULL) {
//
// balance the refcount we added to keep the handle alive during
// callbacks and across the async thread transition. If this is a non-
// nested async request then the reference will be balanced after the
// REQUEST_COMPLETE callback
//
((HANDLE_OBJECT *)fileHandle)->Dereference();
}
_InternetDecNestingCount(nestingLevel);;
done:
if (bNonNestedAsync && (error == ERROR_SUCCESS)) {
hObjectMapped = hConnectMapped;
}
if (hObjectMapped != NULL) {
//
// if we are about to deref the file handle BUT it is already invalidated
// because the operation was cancelled, e.g., then do not perform the
// deref - leave it for the close. Otherwise, the close will fail and
// will not reinstate the callback parameters
//
if (!((hObjectMapped == fileHandle) && ((HANDLE_OBJECT *)fileHandle)->IsInvalidated())) {
DereferenceObject((LPVOID)hObjectMapped);
}
}
if (error != ERROR_SUCCESS) {
//
// if we are not pending an async request but we created a handle object
// then close it
//
if ((error != ERROR_IO_PENDING) && (fileHandle != NULL)) {
HANDLE_OBJECT * hConnMapped;
HINTERNET hConn;
hConnMapped = (HANDLE_OBJECT *)((HANDLE_OBJECT *)fileHandle)->GetParent();
if (hConnMapped != NULL) {
hConn = (HINTERNET)hConnMapped->GetPseudoHandle();
}
((HANDLE_OBJECT *)fileHandle)->Invalidate();
((HANDLE_OBJECT *)fileHandle)->Dereference();
if (hConnMapped != NULL) {
_InternetSetObjectHandle(lpThreadInfo, hConn, hConnMapped);
_InternetSetContext(lpThreadInfo, dwContext);
}
}
//
// error situation, or request is being processed asynchronously: return
// a NULL handle
//
fileHandle = NULL;
DEBUG_ERROR(API, error);
SetLastError(error);
} else {
//
// success - return generated pseudo-handle
//
fileHandle = ((HANDLE_OBJECT *)fileHandle)->GetPseudoHandle();
}
DEBUG_LEAVE(fileHandle);
return fileHandle;
}
INTERNETAPI_(BOOL) FtpCreateDirectoryA(
IN HINTERNET hFtpSession,
IN LPCSTR lpszDirectory
)
/*++
Routine Description:
Creates a directory on the FTP server
Arguments:
hFtpSession - identifies FTP server where directory is to be created
lpszDirectory - name of directory to create
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Use GetLastError() for more info
--*/
{
DEBUG_ENTER_API((DBG_API,
Bool,
"FtpCreateDirectoryA",
"%#x, %q",
hFtpSession,
lpszDirectory
));
DWORD error;
LPINTERNET_THREAD_INFO lpThreadInfo;
DWORD nestingLevel = 0;
HINTERNET hMapped = NULL;
BOOL fDeref = TRUE;
if (!GlobalDataInitialized) {
error = ERROR_INTERNET_NOT_INITIALIZED;
goto done;
}
//
// 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(hFtpSession, (LPVOID *)&hMapped, FALSE);
if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
goto quit;
}
//
// set the context and handle info and clear last error variables
//
_InternetSetObjectHandle(lpThreadInfo, hFtpSession, hMapped);
_InternetSetContext(lpThreadInfo,
((INTERNET_HANDLE_OBJECT *)hMapped)->GetContext()
);
_InternetClearLastError(lpThreadInfo);
//
// quit now if the handle is invalid
//
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// validate handle
//
BOOL isLocal;
BOOL isAsync;
error = RIsHandleLocal(hMapped,
&isLocal,
&isAsync,
TypeFtpConnectHandle
);
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// perform sync work
//
if (!lpThreadInfo->IsAsyncWorkerThread
|| (lpThreadInfo->NestedRequests > 1)) {
//
// validate parameters
//
if (IsBadStringPtr(lpszDirectory, INTERNET_MAX_PATH_LENGTH + 1)
|| (*lpszDirectory == '\0')) {
error = ERROR_INVALID_PARAMETER;
goto quit;
}
// in offline mode modifications are disallowed
// someday we will do internet briefcase but not today
// BUGBUG there is hole in this API, there is no dwFlags
// so there is no way to know whether these operations are
// happening online or offline
if (((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->GetInternetOpenFlags() &
INTERNET_FLAG_OFFLINE) {
error = ERROR_WRITE_PROTECT;
goto quit;
}
if (!lpThreadInfo->IsAsyncWorkerThread && isAsync) {
// MakeAsyncRequest
CFsm_FtpCreateDirectory * pFsm;
pFsm = new CFsm_FtpCreateDirectory(hFtpSession, lpszDirectory);
if (pFsm != NULL &&
pFsm->GetError() == ERROR_SUCCESS)
{
error = pFsm->QueueWorkItem();
if ( error == ERROR_IO_PENDING ) {
fDeref = FALSE;
}
}
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;
}
}
HINTERNET ftpHandle;
LocalSetObjectName(hMapped, (LPSTR)lpszDirectory);
error = RGetLocalHandle(hMapped, &ftpHandle);
if (error == ERROR_SUCCESS) {
error = wFtpCreateDirectory(ftpHandle,
lpszDirectory
);
if (error == ERROR_SUCCESS) {
((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->ExpireDependents();
}
}
quit:
_InternetDecNestingCount(nestingLevel);;
done:
BOOL success;
if (error != ERROR_SUCCESS) {
SetLastError(error);
success = FALSE;
DEBUG_ERROR(API, error);
} else {
success = TRUE;
}
if ((hMapped != NULL) && fDeref) {
DereferenceObject((LPVOID)hMapped);
}
DEBUG_LEAVE_API(success);
return success;
}
INTERNETAPI_(BOOL) FtpRemoveDirectoryA(
IN HINTERNET hFtpSession,
IN LPCSTR lpszDirectory
)
/*++
Routine Description:
Removes a directory at an FTP server
Arguments:
hFtpSession - identifies FTP server where directory is to be removed
lpszDirectory - name of directory to remove
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Use GetLastError() for more info
--*/
{
DEBUG_ENTER_API((DBG_API,
Bool,
"FtpRemoveDirectoryA",
"%#x, %q",
hFtpSession,
lpszDirectory
));
DWORD error;
LPINTERNET_THREAD_INFO lpThreadInfo;
DWORD nestingLevel = 0;
HINTERNET hMapped = NULL;
BOOL fDeref = TRUE;
if (!GlobalDataInitialized) {
error = ERROR_INTERNET_NOT_INITIALIZED;
goto done;
}
//
// get 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(hFtpSession, (LPVOID *)&hMapped, FALSE);
if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
goto quit;
}
//
// set the context and handle info and clear last error variables
//
_InternetSetObjectHandle(lpThreadInfo, hFtpSession, hMapped);
_InternetSetContext(lpThreadInfo,
((INTERNET_HANDLE_OBJECT *)hMapped)->GetContext()
);
_InternetClearLastError(lpThreadInfo);
//
// quit now if the handle is invalid
//
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// validate handle
//
BOOL isLocal;
BOOL isAsync;
error = RIsHandleLocal(hMapped,
&isLocal,
&isAsync,
TypeFtpConnectHandle
);
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// perform sync work
//
if (!lpThreadInfo->IsAsyncWorkerThread
|| (lpThreadInfo->NestedRequests > 1)) {
//
// validate parameters
//
if (IsBadStringPtr(lpszDirectory, INTERNET_MAX_PATH_LENGTH + 1)
|| (*lpszDirectory == '\0')) {
error = ERROR_INVALID_PARAMETER;
goto quit;
}
// in offline mode modifications are disallowed
// someday we will do internet briefcase but not today
// BUGBUG there is hole in this API, there is no dwFlags
// so there is no way to know whether these operations are
// happening online or offline
if (((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->GetInternetOpenFlags() &
INTERNET_FLAG_OFFLINE) {
error = ERROR_WRITE_PROTECT;
goto quit;
}
if (!lpThreadInfo->IsAsyncWorkerThread && isAsync) {
// MakeAsyncRequest
CFsm_FtpRemoveDirectory * pFsm;
pFsm = new CFsm_FtpRemoveDirectory(hFtpSession, lpszDirectory);
if (pFsm != NULL &&
pFsm->GetError() == ERROR_SUCCESS)
{
error = pFsm->QueueWorkItem();
if ( error == ERROR_IO_PENDING ) {
fDeref = FALSE;
}
}
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;
}
}
HINTERNET ftpHandle;
error = RGetLocalHandle(hMapped, &ftpHandle);
if (error == ERROR_SUCCESS) {
error = wFtpRemoveDirectory(ftpHandle,
lpszDirectory
);
}
quit:
_InternetDecNestingCount(nestingLevel);;
done:
BOOL success;
if (error != ERROR_SUCCESS) {
SetLastError(error);
success = FALSE;
DEBUG_ERROR(API, error);
} else {
success = TRUE;
}
if ((hMapped != NULL) && fDeref) {
DereferenceObject((LPVOID)hMapped);
}
DEBUG_LEAVE_API(success);
return success;
}
INTERNETAPI_(BOOL) FtpSetCurrentDirectoryA(
IN HINTERNET hFtpSession,
IN LPCSTR lpszDirectory
)
/*++
Routine Description:
Sets the current directory (for this session) at an FTP server
Arguments:
hFtpSession - identifies FTP server at which directory is to be set
lpszDirectory - name of directory to make current working directory
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Use GetLastError() for more info
--*/
{
DEBUG_ENTER_API((DBG_API,
Bool,
"FtpSetCurrentDirectoryA",
"%#x, %q",
hFtpSession,
lpszDirectory
));
DWORD error;
LPINTERNET_THREAD_INFO lpThreadInfo;
DWORD nestingLevel = 0;
HINTERNET hMapped = NULL;
BOOL fDeref = TRUE;
if (!GlobalDataInitialized) {
error = ERROR_INTERNET_NOT_INITIALIZED;
goto done;
}
//
// get 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(hFtpSession, (LPVOID *)&hMapped, FALSE);
if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
goto quit;
}
//
// set the context and handle info and clear last error variables
//
_InternetSetObjectHandle(lpThreadInfo, hFtpSession, hMapped);
_InternetSetContext(lpThreadInfo,
((INTERNET_HANDLE_OBJECT *)hMapped)->GetContext()
);
_InternetClearLastError(lpThreadInfo);
//
// quit now if the handle is invalid
//
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// validate handle
//
BOOL isLocal;
BOOL isAsync;
error = RIsHandleLocal(hMapped,
&isLocal,
&isAsync,
TypeFtpConnectHandle
);
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// perform sync work
//
INTERNET_CONNECT_HANDLE_OBJECT * pMapped;
pMapped = (INTERNET_CONNECT_HANDLE_OBJECT *)hMapped;
if (!lpThreadInfo->IsAsyncWorkerThread
|| (lpThreadInfo->NestedRequests > 1)) {
//
// validate parameters
//
if (IsBadStringPtr(lpszDirectory, INTERNET_MAX_PATH_LENGTH + 1)
|| (*lpszDirectory == '\0')) {
error = ERROR_INVALID_PARAMETER;
goto quit;
}
//
// if we are not going to net at all (offline), spoof setting directory
// at the server
//
if (pMapped->GetInternetOpenFlags() & INTERNET_FLAG_OFFLINE) {
goto set_object_cwd;
}
//
// we have to hit the net. This will be an asynchronous operation if the
// app requested async
//
if (!lpThreadInfo->IsAsyncWorkerThread && isAsync) {
// MakeAsyncRequest
CFsm_FtpSetCurrentDirectory * pFsm;
pFsm = new CFsm_FtpSetCurrentDirectory(hFtpSession, lpszDirectory);
if (pFsm != NULL &&
pFsm->GetError() == ERROR_SUCCESS)
{
error = pFsm->QueueWorkItem();
if ( error == ERROR_IO_PENDING ) {
fDeref = FALSE;
}
}
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;
}
}
HINTERNET ftpHandle;
error = RGetLocalHandle(hMapped, &ftpHandle);
if (error == ERROR_SUCCESS) {
error = wFtpSetCurrentDirectory(ftpHandle, lpszDirectory);
}
set_object_cwd:
if (error == ERROR_SUCCESS) {
error = pMapped->SetCurrentWorkingDirectory((LPSTR)lpszDirectory);
}
quit:
_InternetDecNestingCount(nestingLevel);;
done:
BOOL success;
if (error != ERROR_SUCCESS) {
SetLastError(error);
success = FALSE;
DEBUG_ERROR(API, error);
} else {
success = TRUE;
}
if ((hMapped != NULL) && fDeref) {
DereferenceObject((LPVOID)hMapped);
}
DEBUG_LEAVE_API(success);
return success;
}
INTERNETAPI_(BOOL) FtpGetCurrentDirectoryA(
IN HINTERNET hFtpSession,
OUT LPSTR lpszCurrentDirectory,
IN OUT LPDWORD lpdwCurrentDirectory
)
/*++
Routine Description:
Gets the name of the current working directory for this session at the
FTP server identified by hFtpSession
Arguments:
hFtpSession - identifies FTP server from which to get directory
lpszCurrentDirectory - buffer where name of current directory will be written
lpdwCurrentDirectory - IN: size of the buffer
OUT: number of bytes returned
Return Value:
BOOL
TRUE - *lpdwCurrentDirectory contains number of characters returned
FALSE - Use GetLastError() to get more info. One of the following will
be returned:
ERROR_INVALID_PARAMETER
ERROR_INSUFFICIENT_BUFFER
*lpdwCurrentDirectory contains required buffer length
--*/
{
DEBUG_ENTER_API((DBG_API,
Bool,
"FtpGetCurrentDirectoryA",
"%#x, %#x, %#x [%d]",
hFtpSession,
lpszCurrentDirectory,
lpdwCurrentDirectory,
lpdwCurrentDirectory ? *lpdwCurrentDirectory : 0
));
DWORD error;
LPINTERNET_THREAD_INFO lpThreadInfo;
DWORD nestingLevel = 0;
HINTERNET hMapped = NULL;
BOOL fDeref = TRUE;
if (!GlobalDataInitialized) {
error = ERROR_INTERNET_NOT_INITIALIZED;
goto done;
}
//
// get 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(hFtpSession, (LPVOID *)&hMapped, FALSE);
if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
goto quit;
}
//
// set the context and handle info and clear last error variables
//
_InternetSetObjectHandle(lpThreadInfo, hFtpSession, hMapped);
_InternetSetContext(lpThreadInfo,
((INTERNET_HANDLE_OBJECT *)hMapped)->GetContext()
);
_InternetClearLastError(lpThreadInfo);
//
// quit now if the handle is invalid
//
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// validate handle
//
BOOL isLocal;
BOOL isAsync;
error = RIsHandleLocal(hMapped,
&isLocal,
&isAsync,
TypeFtpConnectHandle
);
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// perform sync work
//
if (!lpThreadInfo->IsAsyncWorkerThread
|| (lpThreadInfo->NestedRequests > 1)) {
//
// validate parameters
//
//
// lpdwCurrentDirectory must be present (and writeable - we assume it is)
//
if (!ARGUMENT_PRESENT(lpdwCurrentDirectory)
//
// lpszCurrentDirectory may be not present, but if it is must be writeable
// by the number of bytes specified in *lpdwCurrentDirectory
//
|| (ARGUMENT_PRESENT(lpszCurrentDirectory)
? IsBadWritePtr(lpszCurrentDirectory, *lpdwCurrentDirectory) : FALSE)) {
error = ERROR_INVALID_PARAMETER;
goto quit;
}
}
//
// we get CWD from the cache only in disconnected state
//
if (FGetCWDFromCache(hMapped, lpszCurrentDirectory, lpdwCurrentDirectory)) {
error = ERROR_SUCCESS;
goto quit;
}
if (!lpThreadInfo->IsAsyncWorkerThread
&& isAsync) {
// MakeAsyncRequest
CFsm_FtpGetCurrentDirectory * pFsm;
pFsm = new CFsm_FtpGetCurrentDirectory(hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
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;
}
HINTERNET ftpHandle;
error = RGetLocalHandle(hMapped, &ftpHandle);
if (error == ERROR_SUCCESS) {
error = wFtpGetCurrentDirectory(
ftpHandle,
//
// if the caller supplied no buffer then the
// buffer length is 0
//
ARGUMENT_PRESENT(lpszCurrentDirectory)
? *lpdwCurrentDirectory
: 0,
lpszCurrentDirectory,
lpdwCurrentDirectory
);
}
quit:
_InternetDecNestingCount(nestingLevel);;
done:
BOOL success;
if (error != ERROR_SUCCESS) {
SetLastError(error);
success = FALSE;
DEBUG_ERROR(API, error);
} else {
success = TRUE;
}
if ((hMapped != NULL) && fDeref) {
DereferenceObject((LPVOID)hMapped);
}
DEBUG_LEAVE_API(success);
return success;
}
INTERNETAPI_(BOOL) FtpCommandA(
IN HINTERNET hFtpSession,
IN BOOL fExpectResponse,
IN DWORD dwFlags,
IN LPCSTR lpszCommand,
IN DWORD_PTR dwContext,
OUT HINTERNET *phFtpCommand OPTIONAL
)
/*++
Routine Description:
Runs an arbitrary command at the identified FTP server
Arguments:
hFtpSession - identifies FTP server where this command is to be run
fExpectResponse - TRUE if we expect response data
dwTransferType - how to receive the data - as ASCII text, or as BINARY,
and open options
lpszCommand - string describing the command to run
dwContext - app-supplied context value for call-backs
phFtpCommand - pointer to an optional handle that will be created if
a valid data socket is opened, fExpectResponse must
be set to TRUE phFtpCommand to be filled
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Use GetLastError() for more info
--*/
{
DEBUG_ENTER_API((DBG_API,
Bool,
"FtpCommandA",
"%#x, %B, %#x, %q, %#x, %#x",
hFtpSession,
fExpectResponse,
dwFlags,
lpszCommand,
dwContext,
phFtpCommand
));
DWORD error;
HINTERNET hMapped = NULL;
HINTERNET hCommandMapped = NULL;
LPINTERNET_THREAD_INFO lpThreadInfo;
BOOL fDeref = 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;
}
//
// map the handle
//
error = MapHandleToAddress(hFtpSession, (LPVOID *)&hMapped, FALSE);
if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
goto quit;
}
INTERNET_CONNECT_HANDLE_OBJECT * pMapped;
pMapped = (INTERNET_CONNECT_HANDLE_OBJECT *)hMapped;
//
// this is the handle we are currently working on
//
_InternetSetObjectHandle(lpThreadInfo,
hFtpSession,
hMapped
);
_InternetSetContext(lpThreadInfo, dwContext);
//
// clear the per-thread object last error variables
//
InternetClearLastError();
BOOL isLocal;
BOOL isAsync;
//
// make RPC or local-worker function call
//
error = RIsHandleLocal(hMapped,
&isLocal,
&isAsync,
TypeFtpConnectHandle
);
if ( error != ERROR_SUCCESS ) {
goto quit;
}
//
// in offline mode we can't execute commands
//
if ((pMapped->GetInternetOpenFlags()|dwFlags) & INTERNET_FLAG_OFFLINE) {
error = ERROR_INTERNET_NO_DIRECT_ACCESS;
goto quit;
}
//
// validate parameters
//
if ( !lpThreadInfo->IsAsyncWorkerThread
|| (lpThreadInfo->NestedRequests > 1) )
{
//
// BUGBUG - reasonable upper limit for command string length?
//
if (fExpectResponse && (phFtpCommand == NULL))
{
error = ERROR_INVALID_PARAMETER;
goto quit;
}
if (IsBadStringPtr(lpszCommand, 1024)
|| (*lpszCommand == '\0')
|| (fExpectResponse
&& (((dwFlags & FTP_TRANSFER_TYPE_MASK) != FTP_TRANSFER_TYPE_ASCII)
&& ((dwFlags & FTP_TRANSFER_TYPE_MASK) != FTP_TRANSFER_TYPE_BINARY)))
|| ((dwFlags & ~ALLOWED_FTP_FLAGS) != 0))
{
error = ERROR_INVALID_PARAMETER;
goto quit;
}
}
if (!lpThreadInfo->IsAsyncWorkerThread
&& isAsync
&& (dwContext != INTERNET_NO_CALLBACK)) {
CFsm_FtpCommand * pFsm;
pFsm = new CFsm_FtpCommand(hFtpSession,
fExpectResponse,
dwFlags,
lpszCommand,
dwContext,
phFtpCommand
);
if (pFsm != NULL &&
pFsm->GetError() == ERROR_SUCCESS)
{
error = pFsm->QueueWorkItem();
if ( error == ERROR_IO_PENDING ) {
fDeref = FALSE;
}
}
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;
}
INET_ASSERT (error == ERROR_SUCCESS) ;
if ( fExpectResponse )
{
//
// create the handle object now. This can be used to cancel the async
// operation, or the sync operation if InternetCloseHandle() is called
// from a different thread
//
error = RMakeFtpFileObjectHandle(hMapped,
&hCommandMapped,
(CLOSE_HANDLE_FUNC)wFtpCloseFile,
dwContext
);
if (error != ERROR_SUCCESS) {
goto quit;
}
//
// this new handle will be used in callbacks
//
}
HINTERNET ftpHandle;
error = RGetLocalHandle(hMapped, &ftpHandle);
if (error == ERROR_SUCCESS)
{
//InternetSetContext(dwContext);
error = wFtpCommand(ftpHandle,
fExpectResponse,
dwFlags,
lpszCommand
);
if ( fExpectResponse )
{
//
// FTP can only have one active operation per session, so we just return
// this session handle as the find handle
//
((FTP_FILE_HANDLE_OBJECT *)hCommandMapped)->SetFileHandle(
ftpHandle
);
*phFtpCommand = ((HANDLE_OBJECT *)hCommandMapped)->GetPseudoHandle();
}
}
quit:
BOOL success;
if ((hMapped != NULL) && fDeref) {
DereferenceObject((LPVOID)hMapped);
}
if (error != ERROR_SUCCESS) {
SetLastError(error);
success = FALSE;
DEBUG_ERROR(API, error);
} else {
success = TRUE;
}
DEBUG_LEAVE_API(success);
return success;
}
INTERNETAPI_(DWORD) FtpGetFileSize(
IN HINTERNET hFile,
OUT LPDWORD lpdwFileSizeHigh OPTIONAL
)
/*++
Routine Description:
Same as base Win32 GetFileSize() function. Returns size of file as reported
by server (if known). For the IIS FTP server, the file size is reported in
the response string when we open the file. For other (Unix) server, we need
to send a "SIZE <filename>" command
Arguments:
hFile - hInternet of FTP_FILE_HANDLE_OBJECT returned by
FtpOpenFile()
lpdwFileSizeHigh - optional pointer to returned high 32 bits of file size
Return Value:
DWORD
Success - low 32 bits of size of file associated with hFile. If
0xFFFFFFFF is returned then GetLastError() must be used to
determine if an error occurred. If GetLastError() returns
ERROR_SUCCESS then the file size is 4GB-1
Failure - 0xFFFFFFFF. GetLastError() returns possible error codes:
ERROR_INVALID_HANDLE
The handle is not a valid HINTERNET
ERROR_INTERNET_INCORRECT_HANDLE_TYPE
The handle is a valid HINTERNET, but does not describe
a FTP FILE handle object
ERROR_INVALID_PARAMETER
lpdwFileSizeHigh is an invalid pointer
ERROR_INTERNET_OVERFLOW
The server reported that the file size was >0xFFFFFFFF,
but lpdwFileSizeHigh was NULL
--*/
{
DEBUG_ENTER_API((DBG_API,
Dword,
"FtpGetFileSize",
"%#x, %#x",
hFile,
lpdwFileSizeHigh
));
DWORD error = ERROR_SUCCESS;
DWORD dwSizeLow = 0;
DWORD dwSizeHigh = 0;
LPINTERNET_THREAD_INFO lpThreadInfo;
DWORD nestingLevel = 0;
FTP_FILE_HANDLE_OBJECT * pFileMapped = NULL;
INTERNET_CONNECT_HANDLE_OBJECT * pConnectMapped = NULL;
HINTERNET hMapped;
BOOL fFile = TRUE;
BOOL isAsync;
BOOL isLocal;
BOOL fDeref = TRUE;
BOOL fDerefSession = FALSE;
if (!GlobalDataInitialized) {
error = ERROR_INTERNET_NOT_INITIALIZED;
goto quit;
}
lpThreadInfo = InternetGetThreadInfo();
if (lpThreadInfo == NULL) {
INET_ASSERT(FALSE);
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
//
// validate parameters
//
if (lpdwFileSizeHigh != NULL) {
if(IsBadWritePtr(lpdwFileSizeHigh, sizeof(*lpdwFileSizeHigh))) {
error = ERROR_INVALID_PARAMETER;
goto quit;
}
*lpdwFileSizeHigh = 0;
}
error = MapHandleToAddress(hFile, (LPVOID *)&pFileMapped, FALSE);
if ((error != ERROR_SUCCESS) && (pFileMapped == NULL)) {
goto quit;
}
hMapped = pFileMapped;
_InternetSetObjectHandle(lpThreadInfo, hFile, pFileMapped);
_InternetSetContext(lpThreadInfo,
((FTP_FILE_HANDLE_OBJECT *)pFileMapped)->GetContext()
);
_InternetClearLastError(lpThreadInfo);
error = RIsHandleLocal(hMapped, // mapped file handle
&isLocal,
&isAsync,
TypeFtpFileHandle
);
if (error != ERROR_SUCCESS) {
#if 0
error = RIsHandleLocal((HINTERNET) hMapped,
&isLocal,
&isAsync,
TypeFtpConnectHandle
);
if ( error != ERROR_SUCESS ) {
goto quit;
}
//else ...
fFile = FALSE;
pConnectMapped = (INTERNET_CONNECT_HANDLE_OBJECT *) pFileMapped;
hMapped = (HINTERNET) pConnectMapped;
pFileMapped = NULL;
#else
goto quit;
#endif
}
//
// If we're reading from the cache we need to find the file size via the cache api.
//
//
// BUGBUG [arthurbi] - Need to test the cached code path, as it may be that we're
// not properly reading from the cached handle
//
if (fFile && pFileMapped->IsCacheReadInProgress())
{
CACHE_ENTRY_INFO ceiCacheInfo;
DWORD dwBuffSize = sizeof(ceiCacheInfo);
if (!pFileMapped->CacheGetUrlInfo(
&ceiCacheInfo,
&dwBuffSize
))
{
error = GetLastError();
goto quit;
}
dwSizeLow = ceiCacheInfo.dwSizeLow;
dwSizeHigh = ceiCacheInfo.dwSizeHigh;
goto quit;
}
//
// if we already know the size of the file from a 150 response, return it
// immediately, else we need to send a "SIZE" request to the server to get
// it
//
LPFTP_SESSION_INFO lpSessionInfo;
HINTERNET hHandleMapped;
HINTERNET hFtpSession;
//
// find the FTP_SESSION_INFO and ensure it is set up to receive data
//
error = RGetLocalHandle(hMapped, &hFtpSession);
if (error == ERROR_SUCCESS) {
if (!FindFtpSession( hFtpSession, &lpSessionInfo)) {
error = ERROR_INVALID_HANDLE;
goto quit;
}
}
else
{
goto quit;
}
fDerefSession = TRUE;
#if 0
if (!fFile)
{
if (!lpThreadInfo->IsAsyncWorkerThread && isAsync)
{
CFsm_FtpGetFileSize * pFsm;
pFsm = new CFsm_FtpGetFileSize(hFile);
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;
}
error = wFtpGetFileSize(hMapped, lpSessionInfo, &dwSizeLow, &dwSizeHigh);
}
else
#endif
{
INET_ASSERT(fFile);
//
// if FFTP_KNOWN_FILE_SIZE is set then we already know the file size
//
if ( (lpSessionInfo->Flags & FFTP_KNOWN_FILE_SIZE) == 0 ) {
error = ERROR_INTERNET_ITEM_NOT_FOUND;
goto quit;
}
// set dwSizeLow here..
dwSizeLow = lpSessionInfo->dwFileSizeLow;
}
quit:
if ( fDerefSession ) {
DereferenceFtpSession(lpSessionInfo);
}
if (pFileMapped != NULL && fDeref) {
DereferenceObject((LPVOID)pFileMapped);
}
if ( error != ERROR_SUCCESS )
{
dwSizeLow = 0xffffffff;
}
SetLastError(error);
DEBUG_LEAVE_API(dwSizeLow);
return dwSizeLow;
}
INTERNETAPI_(BOOL) FtpGetSystemNameA(
IN HINTERNET hSession,
OUT LPSTR lpszBuffer,
IN OUT LPDWORD lpdwBufferLength
)
/*++
Routine Description:
Returns the results of a "SYST" command sent to the FTP server to identify
the FTP server/operating system type in use at the site. The server will
return a string of the form "215 Windows_NT Version 4.0". The FTP status
code substring "215 " will be stripped before the string is returned to the
caller
Arguments:
hSession - a HINTERNET returned by InternetConnect() describing
an FTP session
lpszBuffer - pointer to buffer where output string will be stored
lpdwBufferLength - IN: the size of lpszBuffer in BYTEs
OUT: if successful, the number of CHARACTERs comprising
the string in lpszBuffer minus 1 for the string
terminator. If ERROR_INSUFFICIENT_BUFFER is returned,
the number of BYTEs required to hold the string,
including the string termination character
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Use GetLastError() to return the error information.
Possible error codes are:
ERROR_INVALID_HANDLE
The handle is not a valid HINTERNET
ERROR_INTERNET_INCORRECT_HANDLE_TYPE
The handle is a valid HINTERNET, but does not describe
an FTP connect handle object
ERROR_INVALID_PARAMETER
lpszBuffer or lpdwBufferLength are invalid pointers
ERROR_INSUFFICIENT_BUFFER
The buffer size given in *lpdwBufferLength is too small
to hold the resultant string. The required buffer length
is returned in *lpdwBufferLength
--*/
{
DEBUG_ENTER_API((DBG_API,
Bool,
"FtpGetSystemNameA",
"%#x, %#x, %#x [%d]",
hSession,
lpszBuffer,
lpdwBufferLength,
lpdwBufferLength ? *lpdwBufferLength : 0
));
DWORD error;
INTERNET_CONNECT_HANDLE_OBJECT * phMapped = NULL;
if (!GlobalDataInitialized) {
error = ERROR_INTERNET_NOT_INITIALIZED;
goto quit;
}
//
// validate parameters
//
if ((lpdwBufferLength == NULL)
|| IsBadWritePtr(lpdwBufferLength, sizeof(*lpdwBufferLength))
|| IsBadWritePtr(lpszBuffer, *lpdwBufferLength)) {
error = ERROR_INVALID_PARAMETER;
goto quit;
}
error = MapHandleToAddress(hSession, (LPVOID *)&phMapped, FALSE);
if ((error != ERROR_SUCCESS) && (phMapped == NULL)) {
goto quit;
}
quit:
if (phMapped != NULL) {
DereferenceObject((LPVOID)phMapped);
}
BOOL success;
if (error == ERROR_SUCCESS) {
success = TRUE;
} else {
DEBUG_ERROR(API, error);
SetLastError(error);
success = FALSE;
}
DEBUG_LEAVE_API(success);
return success;
}
//
// Internet subordinate functions
//
BOOL
FtpFindNextFileA(
IN HINTERNET hFind,
OUT LPWIN32_FIND_DATA lpFindFileData
)
/*++
Routine Description:
Returns the directory entry in the list returned by FtpFindFirstFile()
Assumes: 1. We are being called from InternetFindNextFile() which has
already validated the parameters, set the thread variables,
and cleared the object last error info
Arguments:
hFind - find object handle, returned by FtpFindFirstFile()
lpFindFileData - Pointer to a buffer that will contain WIN32_FIND_DATA
information when this call succeeds.
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Call GetLastError() for more info
--*/
{
DEBUG_ENTER((DBG_FTP,
Bool,
"FtpFindNextFileA",
"%#x, %#x",
hFind,
lpFindFileData
));
INET_ASSERT(GlobalDataInitialized);
DWORD error, errorCache;
BOOL isLocal;
BOOL isAsync, fIsHtml = FALSE;
//
// make RPC or local-worker function call
//
error = RIsHandleLocal(hFind,
&isLocal,
&isAsync,
TypeFtpFindHandle
);
if (error != ERROR_SUCCESS) {
//
// if the handle is actually a HTML FTP find handle, then we allow the
// operation. Note: we can do this because FtpFindNextFile() is not
// exported, so a rogue app cannot call this function after opening the
// handle via InternetOpenUrl()
//
error = RIsHandleLocal(hFind,
&isLocal,
&isAsync,
TypeFtpFindHandleHtml
);
fIsHtml = (error == ERROR_SUCCESS);
}
FTP_FIND_HANDLE_OBJECT * pFind;
pFind = (FTP_FIND_HANDLE_OBJECT *)hFind;
if (error == ERROR_SUCCESS) {
if (pFind->IsCacheReadInProgress()) {
//we should never get here when the it is a findhtml type handle.
// internetopenurl should skim it off the top.
INET_ASSERT(!fIsHtml);
DWORD dwLen = sizeof(WIN32_FIND_DATA);
error = pFind->ReadCache((LPBYTE)lpFindFileData,
dwLen,
&dwLen);
if ((error == ERROR_SUCCESS) && !dwLen) {
error = ERROR_NO_MORE_FILES;
}
goto quit;
} else {
INET_ASSERT(!(pFind->GetInternetOpenFlags() & INTERNET_FLAG_OFFLINE));
}
HINTERNET ftpHandle;
error = RGetLocalHandle(hFind, &ftpHandle);
if (error == ERROR_SUCCESS) {
if (pFind->IsEmpty()) {
error = ERROR_NO_MORE_FILES;
} else {
error = wFtpFindNextFile(ftpHandle, lpFindFileData);
}
}
}
errorCache = error;
if (error == ERROR_SUCCESS) {
if (((INTERNET_CONNECT_HANDLE_OBJECT *)hFind)->IsCacheWriteInProgress()) {
// write here only if it is a native find handle
// otherwise, internetreadurl will take care
if (!fIsHtml) {
errorCache = ((INTERNET_CONNECT_HANDLE_OBJECT *)hFind)->WriteCache(
(LPBYTE)lpFindFileData,
sizeof(WIN32_FIND_DATA)
);
}
}
}
if (errorCache != ERROR_SUCCESS) {
if (!fIsHtml) {
InbLocalEndCacheWrite(hFind, NULL, (errorCache == ERROR_NO_MORE_FILES));
}
}
quit:
BOOL success;
if (error != ERROR_SUCCESS) {
success = FALSE;
DEBUG_ERROR(API, error);
} else {
success = TRUE;
}
DEBUG_LEAVE(success);
SetLastError(error);
return success;
}
BOOL
FtpReadFile(
IN HINTERNET hFile,
IN LPVOID lpBuffer,
IN DWORD dwNumberOfBytesToRead,
OUT LPDWORD lpdwNumberOfBytesRead
)
/*++
Routine Description:
Reads number of bytes from file at FTP server
Assumes: 1. We are being called from InternetReadFile() which has
already validated the parameters, handled the zero byte
read case, set the thread variables, and cleared the object
last error info
Arguments:
hFile - file object handle, returned by FtpOpenFile()
lpBuffer - pointer to user's buffer
dwNumberOfBytesToRead - size of user's buffer
lpdwNumberOfBytesRead - number of bytes copied to user's buffer on output
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Call GetLastError() for more info
--*/
{
DEBUG_ENTER((DBG_FTP,
Bool,
"FtpReadFile",
"%#x, %#x, %d, %#x",
hFile,
lpBuffer,
dwNumberOfBytesToRead,
lpdwNumberOfBytesRead
));
INET_ASSERT(GlobalDataInitialized);
DWORD error, errorCache;
BOOL isLocal;
BOOL isAsync;
FTP_FILE_HANDLE_OBJECT * pFile = (FTP_FILE_HANDLE_OBJECT *)hFile;
//
// make RPC or local-worker function call
//
error = RIsHandleLocal(hFile,
&isLocal,
&isAsync,
TypeFtpFileHandle
);
if (error == ERROR_SUCCESS) {
if (pFile->IsCacheReadInProgress()) {
error = pFile->ReadCache((LPBYTE)lpBuffer,
dwNumberOfBytesToRead,
lpdwNumberOfBytesRead
);
if (!*lpdwNumberOfBytesRead || (error != ERROR_SUCCESS)) {
//
// don't do anything here so we don't barf when someone
// does an extraneous read. The cache stream gets closed
// when the handle is closed. bug#9086
// ((FTP_FILE_HANDLE_OBJECT *)hFile)->EndCacheRetrieval();
//
}
//
// quit whether we succeed or we fail
//
goto quit;
} else {
INET_ASSERT(!((pFile->GetInternetOpenFlags()|pFile->GetCacheFlags())
& INTERNET_FLAG_OFFLINE));
}
HINTERNET ftpHandle;
error = RGetLocalHandle(hFile, &ftpHandle);
if (error == ERROR_SUCCESS) {
error = wFtpReadFile(ftpHandle,
lpBuffer,
dwNumberOfBytesToRead,
lpdwNumberOfBytesRead
);
}
}
if (error == ERROR_SUCCESS) {
if (pFile->IsCacheWriteInProgress()) {
if (!*lpdwNumberOfBytesRead) {
DEBUG_PRINT(CACHE,
INFO,
("Cache write complete\r\n"
));
errorCache = InbLocalEndCacheWrite(hFile, NULL, TRUE);
INET_ASSERT(error == ERROR_SUCCESS);
goto quit;
}
INET_ASSERT(pFile->IsCacheReadInProgress() == FALSE);
if (pFile->WriteCache((LPBYTE)lpBuffer,
*lpdwNumberOfBytesRead
) != ERROR_SUCCESS) {
DEBUG_PRINT(CACHE,
ERROR,
("Error in Cache write\n"
));
errorCache = InbLocalEndCacheWrite(hFile, NULL, FALSE);
INET_ASSERT(error == ERROR_SUCCESS);
}
}
}
quit:
BOOL success;
if (error != ERROR_SUCCESS) {
SetLastError(error);
success = FALSE;
DEBUG_ERROR(API, error);
} else {
success = TRUE;
}
DEBUG_LEAVE(success);
return success;
}
BOOL
FtpWriteFile(
IN HINTERNET hFile,
IN LPVOID lpBuffer,
IN DWORD dwNumberOfBytesToWrite,
OUT LPDWORD lpdwNumberOfBytesWritten
)
/*++
Routine Description:
Writes a number of bytes from the user's buffer to an open file on an FTP
server
Assumes: 1. We are being called from InternetWriteFile() which has
already validated the parameters, handled the zero byte
read case, set the thread variables, and cleared the object
last error info
Arguments:
hFile - file object handle, returned by FtpOpenFile()
lpBuffer - pointer to user's buffer
dwNumberOfBytesToWrite - number of bytes to write from user's buffer
lpdwNumberOfBytesWritten - number of bytes actually written
Return Value:
BOOL
Success - TRUE
Failure - FALSE. Call GetLastError() for more info
--*/
{
DEBUG_ENTER((DBG_FTP,
Bool,
"FtpWriteFile",
"%#x, %#x, %d, %#x",
hFile,
lpBuffer,
dwNumberOfBytesToWrite,
lpdwNumberOfBytesWritten
));
INET_ASSERT(GlobalDataInitialized);
DWORD error;
BOOL isLocal;
BOOL isAsync;
//
// make RPC or local-worker function call
//
error = RIsHandleLocal(hFile,
&isLocal,
&isAsync,
TypeFtpFileHandle
);
if (error == ERROR_SUCCESS) {
HINTERNET ftpHandle;
error = RGetLocalHandle(hFile, &ftpHandle);
if (error == ERROR_SUCCESS) {
error = wFtpWriteFile(ftpHandle,
lpBuffer,
dwNumberOfBytesToWrite,
lpdwNumberOfBytesWritten
);
if (error == ERROR_SUCCESS) {
// expire the url if it exists and it's
// parent directory
if( !((FTP_FILE_HANDLE_OBJECT *)hFile)->IsForcedExpirySet()){
((FTP_FILE_HANDLE_OBJECT *)hFile)->ExpireDependents();
((FTP_FILE_HANDLE_OBJECT *)hFile)->SetForcedExpiry(TRUE);
((FTP_FILE_HANDLE_OBJECT *)hFile)->ExpireUrl();
}
}
}
}
BOOL success;
if (error != ERROR_SUCCESS) {
SetLastError(error);
success = FALSE;
DEBUG_ERROR(API, error);
} else {
success = TRUE;
}
DEBUG_LEAVE(success);
return success;
}
DWORD
pFtpGetUrlString(
IN INTERNET_SCHEME SchemeType,
IN LPSTR lpszTargetName,
IN LPSTR lpszCWD,
IN LPSTR lpszObjectName,
IN LPSTR lpszExtension,
IN DWORD dwPort,
OUT LPSTR *lplpUrlName,
OUT LPDWORD lpdwUrlLen
)
/*++
Routine Description:
This routine returns a LocaAlloc'ed buffer containing an FTP URL constructed
from the TargetHost, CWD, and the ObjectName. The caller is responsible
for freeing the memory.
Arguments:
SchemeType - protocol scheme (INTERNET_SCHEME_FTP)
lpszTargetName - name of server
lpszCWD - current directory at server
lpszObjectName - name of file. If NULL or empty then the URL is for a
directory
lpszExtension - file extension. NOT USED
dwPort - port at server
lplpUrlName - pointer to returned URL
lpdwUrlLen - pointer to returned URL length
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_NOT_ENOUGH_MEMORY
--*/
{
DWORD dwError, dwLen, dwT, dwTargetNameLen=0, dwObjectNameLen=0;
char cBuff[INTERNET_MAX_URL_LENGTH], cBuff1[INTERNET_MAX_URL_LENGTH];//????
URL_COMPONENTS sUrlComp;
DWORD dwSav, i, ccFirst, ccTmp;
INET_ASSERT(lpszTargetName);
INET_ASSERT(lpszObjectName || lpszCWD);
//
// NULL or empty object name == directory
//
if ((lpszObjectName != NULL) && (*lpszObjectName == '\0')) {
lpszObjectName = NULL;
}
memcpy(cBuff1, "/", sizeof("/"));
ccFirst = 2;
// add the current directory only if the path is a relative one
if (lpszCWD && !(lpszObjectName && (*lpszObjectName == '/')) ) {
if (*lpszCWD == '/') {
++lpszCWD;
}
ccTmp = lstrlen(lpszCWD);
if (ccTmp > sizeof(cBuff1) - 2)
{
dwError = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
memcpy(cBuff1 + 1, lpszCWD, ccTmp + 1);
ccFirst += ccTmp;
INET_ASSERT(lpszCWD[ccTmp - 1] == '/');
}
if (lpszObjectName) {
if (*lpszObjectName == '/') {
lpszObjectName++;
}
ccTmp = lstrlen(lpszObjectName);
if (ccTmp > sizeof(cBuff1) - ccFirst)
{
dwError = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
memcpy(cBuff1 + ccFirst - 1, lpszObjectName, ccTmp + 1);
}
memset(&sUrlComp, 0, sizeof(URL_COMPONENTS));
sUrlComp.dwStructSize = sizeof(URL_COMPONENTS);
sUrlComp.nScheme = INTERNET_SCHEME_FTP;
sUrlComp.lpszHostName = lpszTargetName;
sUrlComp.lpszUrlPath = cBuff1;
sUrlComp.nPort = (INTERNET_PORT)dwPort;
dwSav = sizeof(cBuff);
if(!InternetCreateUrl(&sUrlComp, 0, cBuff, &dwSav)){
dwError = GetLastError();
goto Cleanup;
}
// BUGBUG, this is because InternetCreateUrl is not returning
// the correct size
dwSav = strlen(cBuff)+5;
for(i=0;i<2;++i) {
*lplpUrlName = (LPSTR)ALLOCATE_MEMORY(LPTR, dwSav);
if (*lplpUrlName) {
if(!InternetCanonicalizeUrl(cBuff, *lplpUrlName, &dwSav, ICU_NO_ENCODE)){
FREE_MEMORY(*lplpUrlName);
// general paranoia
*lplpUrlName = NULL;
dwError = GetLastError();
if ((i == 1) || (dwError != ERROR_INSUFFICIENT_BUFFER)) {
goto Cleanup;
}
}
else {
dwError = ERROR_SUCCESS;
*lpdwUrlLen = dwSav;
break;
}
}
else {
SetLastError(dwError = ERROR_NOT_ENOUGH_MEMORY);
goto Cleanup;
}
}
Cleanup:
if (dwError != ERROR_SUCCESS) {
INET_ASSERT(!*lplpUrlName);
*lpdwUrlLen = 0;
}
return (dwError);
}
PRIVATE
BOOL
FBeginCacheReadProcessing(
IN HINTERNET hFtp,
IN LPCSTR lpszFileName,
IN DWORD dwAccess,
IN DWORD dwFlags,
IN DWORD_PTR dwContext,
IN BOOL fIsHtmlFind
)
/*++
Routine Description:
Sets up to read FTP data from the cache
Arguments:
hFtp A mapped handle to an ftp data transfer object (FtpOpenFile/FtpFindFirstFile)
lpszFileName ftp file to look for in the cache
dwAccess Accesstype eg: GENERIC_READ
dwFlags caching flags
dwContext async context
Returns:
TRUE of started cache reading, FALSE otherwise
Comments:
--*/
{
DEBUG_ENTER((DBG_FTP,
Bool,
"FBeginCacheReadProcessing",
"%#x, %q, %d, %08x, %x, %B",
hFtp,
lpszFileName,
dwAccess,
dwFlags,
dwContext,
fIsHtmlFind
));
DWORD dwError = ERROR_SUCCESS;
URLGEN_FUNC fn = pFtpGetUrlString;
LPCACHE_ENTRY_INFO lpCEI = NULL;
FTP_FILE_HANDLE_OBJECT *pFtp = (FTP_FILE_HANDLE_OBJECT *)hFtp;
if (((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->IsCacheReadInProgress()) {
//
// BUGBUG - return FALSE surely? If there is a cache read in progress
// then by default, its for another request?
//
DEBUG_LEAVE(TRUE);
return (TRUE);
}
//
// if the object name is not set then all cache methods fail
//
//
// BUGBUG - do this only when we know we are reading from cache?
//
((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->SetObjectName((LPSTR)lpszFileName,
NULL,
&fn
);
if (dwAccess & GENERIC_WRITE) {
DEBUG_LEAVE(FALSE);
return (FALSE);
}
if (!(dwFlags & INTERNET_FLAG_NO_CACHE_WRITE)) {
//
// set the cache flags like RELOAD etc.
//
((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->SetCacheFlags(dwFlags);
} else {
//
// set flags to disable both read and write
//
((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->SetCacheFlags(
INTERNET_FLAG_NO_CACHE_WRITE
| INTERNET_FLAG_RELOAD);
}
if (!FFtpCanReadFromCache(hFtp)) {
DEBUG_LEAVE(FALSE);
return (FALSE);
}
DEBUG_PRINT(CACHE,
INFO,
("Checking in the cache\n"
));
dwError = ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->BeginCacheRetrieval(&lpCEI);
if (dwError == ERROR_SUCCESS)
{
//
// found it in the cache
//
DEBUG_PRINT(FTP,
INFO,
("Found in the cache\n"
));
BOOL bGetFromCache = FALSE;
if (IsOffline()
|| (((((INTERNET_HANDLE_OBJECT *)hFtp)->GetInternetOpenFlags() | dwFlags)
& INTERNET_FLAG_OFFLINE) && !(dwFlags & INTERNET_FLAG_RELOAD))) {
bGetFromCache = TRUE;
DEBUG_PRINT(CACHE,
INFO,
("Offline, loading from cache:\n \
dwFlags & INTERNET_FLAG_RELOAD = %s \n \
dwFlags & INTERNET_FLAG_OFFLIME = %s \n \
InternetOpenFlags & INTERNET_FLAG_OFFLIME = %s \n",
(dwFlags & INTERNET_FLAG_RELOAD) ? "TRUE" : "FALSE",
(dwFlags & INTERNET_FLAG_OFFLINE) ? "TRUE" : "FALSE",
(((INTERNET_HANDLE_OBJECT *)hFtp)->GetInternetOpenFlags()
& INTERNET_FLAG_OFFLINE) ? "TRUE" : "FALSE"
));
} else {
if (!FIsFtpExpired(hFtp, lpCEI))
{
bGetFromCache = TRUE;
}
}
if (! (( fIsHtmlFind && lpCEI->lpszFileExtension) ||
(!fIsHtmlFind && !lpCEI->lpszFileExtension) ) )
{
DEBUG_PRINT(CACHE,
INFO,
("Mismatched HTML-type, expiring\n"
));
bGetFromCache = FALSE;
}
if (bGetFromCache) {
dwError = ERROR_SUCCESS;
((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->SetFromCache();
} else {
DEBUG_PRINT(CACHE,
INFO,
("Expired\n"
));
dwError = ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->EndCacheRetrieval();
INET_ASSERT(dwError == ERROR_SUCCESS);
dwError = ERROR_FILE_NOT_FOUND;
}
}
//
// cleanup
//
if (lpCEI != NULL) {
lpCEI = (LPCACHE_ENTRY_INFO)FREE_MEMORY(lpCEI);
INET_ASSERT(lpCEI == NULL);
}
DEBUG_LEAVE(dwError == ERROR_SUCCESS);
return (dwError == ERROR_SUCCESS);
}
PRIVATE
BOOL
FFtpCanReadFromCache(
HINTERNET hFtp
)
/*++
Routine Description:
This routine checks whether a cache read should even be started.
Arguments:
hFtp a mapped handle to an ftp download (FtpOpenFile/FtpFindFirstFile)
Returns:
Windows Error Code.
Comments:
--*/
{
DWORD dwFlags;
dwFlags = ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->GetCacheFlags();
//
// in offline or disconnected states client always reads
//
if (IsOffline()
|| (((INTERNET_HANDLE_OBJECT *)hFtp)->GetInternetOpenFlags() | dwFlags)
& INTERNET_FLAG_OFFLINE) {
return (TRUE);
}
//
// if we are asked to reload data, it is not OK to read
//
if (dwFlags & (INTERNET_FLAG_RELOAD | INTERNET_FLAG_RESYNCHRONIZE)) {
DEBUG_PRINT(CACHE,
INFO,
("no cache option\n"
));
return (FALSE);
}
return (TRUE);
}
PRIVATE
BOOL
FBeginCacheWriteProcessing(
IN HINTERNET hFtp,
IN LPCSTR lpszFileName,
IN DWORD dwAccess,
IN DWORD dwFlags,
IN DWORD_PTR dwContext,
BOOL fIsHtmlFind
)
/*++
Routine Description:
Sets up to start writing FTP data to the cache
Arguments:
hFtp A mapped handle to an ftp data transfer object (FtpOpenFile/FtpFindFirstFile)
lpszFileName ftp file to look for in the cache
dwAccess Accesstype eg: GENERIC_WRITE
dwFlags caching flags
dwContext async context
Returns:
TRUE if started cache writing, FALSE otherwise
Comments:
--*/
{
DWORD dwError = ERROR_INVALID_FUNCTION;
URLGEN_FUNC fn = pFtpGetUrlString;
char cExt[DEFAULT_MAX_EXTENSION_LENGTH + 1];
DWORD dwBuffLen;
LPSTR lpszFileExtension;
if (dwAccess & GENERIC_WRITE) {
return (FALSE);
}
if (!((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->IsCacheReadInProgress()) {
//
// we are not reading from the cache
// Let us ask a routine whether we should cache this
// stuff or not
//
if (FFtpCanWriteToCache(hFtp)) {
//
// if the object name is not set then all cache methods fail
//
((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->
SetObjectName((LPSTR)lpszFileName,
NULL,
&fn
);
//
// set the cache flags
//
((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->SetCacheFlags(dwFlags);
//
// he says we can cache it.
//
DEBUG_PRINT(CACHE,
INFO,
("Starting cache write\n"
));
if (!fIsHtmlFind) {
dwBuffLen = sizeof(cExt);
lpszFileExtension = GetFileExtensionFromUrl(((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->GetURL(), &dwBuffLen);
if (lpszFileExtension != NULL) {
memcpy(cExt, lpszFileExtension, dwBuffLen);
cExt[dwBuffLen] = '\0';
lpszFileExtension = cExt;
}
}
else {
//allways generate htm extension
strcpy(cExt, "htm");
lpszFileExtension = cExt;
}
dwError = ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->BeginCacheWrite(0, lpszFileExtension);
if (dwError != ERROR_SUCCESS) {
DEBUG_PRINT(CACHE,
ERROR,
("Error in BeginCacheWrite %ld\n",
dwError
));
}
}
}
return (dwError == ERROR_SUCCESS);
}
PRIVATE
BOOL
FFtpCanWriteToCache(
HINTERNET hFtp
)
/*++
Routine Description:
This routine checks whether a cache write should even be started.
Arguments:
hFtp a mapped handle to an ftp download (FtpOpenFile/FtpFindFirstFile)
Returns:
TRUE if successful, FALSE otherwise
Comments:
--*/
{
if (((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->GetCacheFlags() & INTERNET_FLAG_DONT_CACHE) {
return (FALSE);
}
return (TRUE);
}
DWORD
InbLocalEndCacheWrite(
IN HINTERNET hFtp,
LPSTR lpszFileExtension,
IN BOOL fNormal
)
/*++
Routine Description:
This routine checks terminates cache writing if it was in progress and
commits the entry to the cache
Arguments:
hFtp a mapped handle to an ftp download (FtpOpenFile/FtpFindFirstFile)
fNormal TRUE if the termination is normal, in which case the collected data
is entered in the cache, otherwise it is discarded
Returns:
Windows error code
Comments:
--*/
{
FILETIME ftLastModTime, ftExpiryTime, ftPostCheck;
DWORD dwEntryType;
if (((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->IsCacheWriteInProgress()) {
ftLastModTime.dwLowDateTime =
ftLastModTime.dwHighDateTime = 0;
ftExpiryTime.dwLowDateTime =
ftExpiryTime.dwHighDateTime = 0;
ftPostCheck.dwLowDateTime =
ftPostCheck.dwHighDateTime = 0;
dwEntryType = (!fNormal)?0xffffffff:
((((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->
GetCacheFlags() & INTERNET_FLAG_MAKE_PERSISTENT)
? STICKY_CACHE_ENTRY:0
);
DEBUG_PRINT(CACHE,
INFO,
("Cache write EntryType = %x \
IsPerUserItem = %d \
<hFtp = 0x%x> \
<hFtp->GetParent() = 0x%x> \
<hFtp->IsPerUserItem() = %d\n",
dwEntryType,
((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->IsPerUserItem(),
hFtp,
((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->GetParent(),
((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->IsPerUserItem()
));
return (((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->
EndCacheWrite( &ftExpiryTime,
&ftLastModTime,
&ftPostCheck,
dwEntryType,
0,
NULL,
lpszFileExtension));
}
return (ERROR_SUCCESS);
}
PRIVATE
BOOL
FGetCWDFromCache(
HINTERNET hFtpSession,
LPSTR lpBuff,
LPDWORD lpdwBuffSize
)
/*++
Routine Description:
This routine returns the current working directory for an FTP session
Arguments:
hFtpSession a mapped handle to an ftp download (FtpOpenFile/FtpFindFirstFile)
LPSTR buffer to return the string in
lpdwBuffSize IN size of lpBuff, OUT size passed back
Returns:
TRUE if successful, FALSE otherwise
Comments:
--*/
{
if ((((INTERNET_CONNECT_HANDLE_OBJECT *)hFtpSession)->GetInternetOpenFlags()
& INTERNET_FLAG_OFFLINE)) {
((INTERNET_CONNECT_HANDLE_OBJECT *)hFtpSession)->GetCurrentWorkingDirectory(lpBuff, lpdwBuffSize);
return (TRUE);
}
return (FALSE);
}
PRIVATE
BOOL
FIsFtpExpired(
HINTERNET hFtp,
LPCACHE_ENTRY_INFO lpCEI
)
/*++
Routine Description:
This routine checks whether an ftp item in the cache has expired
Arguments:
hFtp a mapped handle to an ftp download (FtpOpenFile/FtpFindFirstFile)
lpCEI cache entry info for the item
Returns:
TRUE if successful, FALSE otherwise
Comments:
Even though this is a trivial routine and can be nuked, it is good
place holder for doing special things to check FTP expiry
--*/
{
BOOL fExpired = FALSE;
if (CheckExpired( hFtp,
&fExpired,
lpCEI,
dwdwFtpDefaultExpiryDelta) != ERROR_SUCCESS) {
fExpired = TRUE;
}
return (fExpired);
}
VOID
LocalSetObjectName(
HINTERNET hFtpMapped,
LPSTR lpszFileName
)
{
URLGEN_FUNC fn = pFtpGetUrlString;
((INTERNET_CONNECT_HANDLE_OBJECT *)hFtpMapped)->
SetObjectName((LPSTR)lpszFileName,
NULL,
&fn
);
}
/*++
Routine Description:
This routine checks whether a string given to FtpFindFirstFile
is a directory or a file
Arguments:
lpszSearchFile string passed in to either FtpFindFirstFile
Returns:
TRUE if successful, FALSE otherwise
Comments:
This routine should not be used anywhere other thatn FtpFindFirstFile
functions
--*/
BOOL IsSearchFileDirectory(
LPCSTR lpszSearchFile
)
{
DWORD dwLen;
if (!lpszSearchFile) {
return (TRUE);
}
dwLen = lstrlen(lpszSearchFile);
if (!dwLen) {
return (TRUE);
}
return (lpszSearchFile[dwLen-1] == '/');
}