You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4666 lines
118 KiB
4666 lines
118 KiB
/*++
|
|
|
|
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] == '/');
|
|
|
|
}
|