910 lines
27 KiB
C++
910 lines
27 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// includes
|
|
|
|
#include <objbase.h>
|
|
#include <wchar.h>
|
|
#include <assert.h>
|
|
#include <wininet.h>
|
|
|
|
#include "ftpstrm.h"
|
|
#include "mischlpr.h"
|
|
#include "strutil.h"
|
|
#include <stdio.h>
|
|
//#define TRACE(a) (fprintf(stderr,"%d %s\n",GetTickCount(),a))
|
|
#define TRACE(a)
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
CFtpStrmImpl::CFtpStrmImpl(): _hLocalFile(NULL), _hInternet(NULL), _pwszURL(NULL), _pwszLocalFile(NULL),
|
|
_pwszUserName(NULL), _pwszPassword(NULL)
|
|
{
|
|
TRACE("CFtpStrm::CFtpStrm");
|
|
}
|
|
|
|
CFtpStrmImpl::~CFtpStrmImpl()
|
|
{
|
|
TRACE("CFtpStrm::~CFtpStrm");
|
|
if (_hLocalFile != NULL)
|
|
{
|
|
CloseHandle(_hLocalFile);
|
|
}
|
|
if (_hInternet != NULL)
|
|
{
|
|
InternetCloseHandle(_hInternet);
|
|
}
|
|
if (_pwszURL != NULL)
|
|
{
|
|
free(_pwszURL);
|
|
}
|
|
if (_pwszLocalFile != NULL)
|
|
{
|
|
free(_pwszLocalFile);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CFtpStrmImpl::_DuplicateFileURL(LPWSTR pwszURL,
|
|
LPWSTR* ppwszWin32FName)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
TRACE("CFtpStrm::_DuplicateFileURL");
|
|
assert(LStrCmpN(pwszURL, L"file:///", 8) == 0);
|
|
|
|
*ppwszWin32FName = DuplicateStringW(pwszURL+8);
|
|
if (*ppwszWin32FName == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
// change the forward slashes into backslashes, to turn the URL into a win32 filepath
|
|
// could use shlwapi, but shlwapi is big and we don't need to bring it all in
|
|
UINT i = 0;
|
|
while (*ppwszWin32FName[i] != NULL)
|
|
{
|
|
if (*ppwszWin32FName[i] == '/')
|
|
{
|
|
*ppwszWin32FName[i] = '\\';
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// IHttpStrm methods
|
|
|
|
STDMETHODIMP CFtpStrmImpl::_OpenRemoteTransacted(BOOL fCreate) // path to file to base stream on
|
|
{
|
|
HINTERNET hSession;
|
|
HRESULT hr = S_OK;
|
|
WCHAR wszTempFname[MAX_PATH];
|
|
WCHAR wszTempPath[MAX_PATH];
|
|
ULONG cbRead;
|
|
ULONG cbWritten;
|
|
BYTE rgb[4096];
|
|
DWORD dwStatusCode;
|
|
LPWSTR pwszServer = NULL;
|
|
LPWSTR pwszPath = NULL;
|
|
INTERNET_PORT nPort = INTERNET_DEFAULT_FTP_PORT;
|
|
URL_COMPONENTS urlComponents = {0};
|
|
|
|
TRACE("CFtpStrm::_OpenRemoteTransacted");
|
|
|
|
|
|
urlComponents.dwStructSize = sizeof(URL_COMPONENTS);
|
|
urlComponents.dwHostNameLength = 1;
|
|
urlComponents.dwUrlPathLength = 1;
|
|
urlComponents.nPort = 1;
|
|
if (!InternetCrackUrl(_pwszURL, 0, 0, &urlComponents))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
pwszServer = (LPWSTR)malloc(sizeof(WCHAR) * (1 + urlComponents.dwHostNameLength));
|
|
if (!pwszServer)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
lstrcpyn(pwszServer, urlComponents.lpszHostName, 1 + urlComponents.dwHostNameLength); // +1 for the final null char
|
|
|
|
pwszPath = (LPWSTR)malloc(sizeof(WCHAR) * (1 + urlComponents.dwUrlPathLength - 1));
|
|
if (!pwszPath)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
lstrcpyn(pwszPath, urlComponents.lpszUrlPath + 1, 1 + urlComponents.dwUrlPathLength); // +1 for the final null char
|
|
|
|
if (urlComponents.nPort != 0)
|
|
{
|
|
nPort = urlComponents.nPort;
|
|
}
|
|
|
|
// PERF: should we use dwFlags here?
|
|
hSession = InternetConnect(_hInternet, pwszServer, nPort,
|
|
_pwszUserName, _pwszPassword, INTERNET_SERVICE_FTP, 0, 0);
|
|
if (hSession == NULL)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
// copy the file to a local temp file, set _hLocalFile to be equal to that file
|
|
if (GetTempPath(MAX_PATH, wszTempPath) == 0)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else if (GetTempFileName(wszTempPath, L"FTP", 0, wszTempFname) == 0)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
/* HINTERNET hFile = FtpOpenFile(hSession, pwszPath, GENERIC_READ, FTP_TRANSFER_TYPE_UNKNOWN, 0);
|
|
if (!hFile)
|
|
{
|
|
hr = E_FAIL;
|
|
DWORD dw = GetLastError();
|
|
HRESULT hres2 = HRESULT_FROM_WIN32(dw);
|
|
}
|
|
else
|
|
{
|
|
WCHAR szBuf[10000];
|
|
BOOL fAidan;
|
|
DWORD cbRead;
|
|
fAidan = InternetReadFile(hFile, szBuf, sizeof(szBuf), &cbRead);
|
|
|
|
}*/
|
|
|
|
|
|
if (!FtpGetFile(hSession, pwszPath, wszTempFname, FALSE, 0, FTP_TRANSFER_TYPE_UNKNOWN, 0))
|
|
{
|
|
DWORD dw = GetLastError();
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
_pwszLocalFile = DuplicateStringW(wszTempFname);
|
|
if (!_pwszLocalFile)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
_hLocalFile = CreateFile(_pwszLocalFile, GENERIC_READ | GENERIC_WRITE,
|
|
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (_hLocalFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
free(pwszPath);
|
|
}
|
|
free(pwszServer);
|
|
}
|
|
|
|
InternetCloseHandle(hSession);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CFtpStrmImpl::_OpenLocalDirect(BOOL fCreate, BOOL fDeleteWhenDone) // should we remove this file after closing the stream?
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// we've been handed a local file URL and we should open it for direct access
|
|
DWORD dwFileAttributes;
|
|
DWORD dwCreation;
|
|
|
|
TRACE("CFtpStrm::_OpenLocalDirect");
|
|
|
|
if (fDeleteWhenDone && !fCreate)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
if (fDeleteWhenDone)
|
|
{
|
|
dwFileAttributes = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE;
|
|
}
|
|
else
|
|
{
|
|
dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
}
|
|
|
|
if (fCreate)
|
|
{
|
|
dwCreation = CREATE_ALWAYS;
|
|
}
|
|
else
|
|
{
|
|
dwCreation = OPEN_EXISTING;
|
|
}
|
|
|
|
// -- open the file
|
|
_hLocalFile = CreateFile(_pwszURL + 8, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, // +8 to skip file:///
|
|
NULL, dwCreation, dwFileAttributes, NULL);
|
|
|
|
if (_hLocalFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CFtpStrmImpl::_OpenLocalTransacted(BOOL fCreate, BOOL fDeleteWhenDone) // should we remove this file after closing the stream?
|
|
// must be FALSE for ftp:// pwszPath
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WCHAR wszTempFname[MAX_PATH];
|
|
WCHAR wszTempPath[MAX_PATH];
|
|
HANDLE hNewFile;
|
|
|
|
TRACE("CFtpStrm::_OpenLocalTransacted");
|
|
|
|
if (!fCreate && fDeleteWhenDone)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
// copy the file to a local temp file, set _hLocalFile to be equal to that file
|
|
if (GetTempPath(MAX_PATH, wszTempPath) == 0)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else if (GetTempFileName(wszTempPath, L"FTP", 0, wszTempFname) == 0)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
if (!fCreate)
|
|
{
|
|
// copy the file, in the process checking if it exists
|
|
if (CopyFile(_pwszURL + 8, wszTempFname, FALSE))
|
|
{
|
|
_pwszLocalFile = DuplicateStringW(wszTempFname);
|
|
|
|
_hLocalFile = CreateFile(wszTempFname, GENERIC_READ | GENERIC_WRITE, 0,
|
|
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
|
|
if (_hLocalFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// create a new file
|
|
_pwszLocalFile = DuplicateStringW(wszTempFname);
|
|
|
|
hNewFile = CreateFile(_pwszURL + 8, GENERIC_WRITE, 0,
|
|
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hNewFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
CloseHandle(hNewFile);
|
|
_hLocalFile = CreateFile(wszTempFname, GENERIC_READ | GENERIC_WRITE, 0,
|
|
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
|
|
if (_hLocalFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
// everything is fine, seek back to beginning
|
|
if (SetFilePointer(_hLocalFile,
|
|
0,
|
|
NULL,
|
|
FILE_BEGIN) == INVALID_SET_FILE_POINTER)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CFtpStrmImpl::Open(LPWSTR pwszURL, // URL to base stream on
|
|
BOOL fDirect, // should we open this in direct mode, or transacted mode?
|
|
// must be FALSE for ftp:// pwszPath
|
|
BOOL fDeleteWhenDone, // should we remove this file after closing the stream?
|
|
// must be FALSE for ftp:// pwszPath
|
|
BOOL fCreate) // are we trying to create/overwrite a file (TRUE), or only open an existing file (FALSE)
|
|
{
|
|
// locals
|
|
HRESULT hr = S_OK;
|
|
TRACE("CFtpStrm::Open");
|
|
|
|
// check args
|
|
if (pwszURL == NULL)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
_pwszURL = DuplicateStringW(pwszURL);
|
|
if (_pwszURL == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
_fDirect = fDirect;
|
|
|
|
// code
|
|
if (LStrCmpN(_pwszURL, L"file:///", 8) == 0) // BUGBUG: case sensitive?
|
|
{
|
|
_fLocalResource = TRUE;
|
|
}
|
|
else if (LStrCmpN(_pwszURL, L"ftp://", 6) == 0) // BUGBUG: does this break user:// ?
|
|
{
|
|
_fLocalResource = FALSE;
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (_fLocalResource && fDirect)
|
|
{
|
|
hr = this->_OpenLocalDirect(fCreate, fDeleteWhenDone);
|
|
}
|
|
else if (_fLocalResource && !fDirect)
|
|
{
|
|
hr = this->_OpenLocalTransacted(fCreate, fDeleteWhenDone);
|
|
}
|
|
else
|
|
{
|
|
if (!_fLocalResource && (fDirect || fDeleteWhenDone))
|
|
{
|
|
hr = E_INVALIDARG; // remote files must be transacted, cannot be temp files
|
|
}
|
|
else
|
|
{
|
|
_hInternet = InternetOpen(L"FTPSTRM", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
|
|
if (_hInternet == NULL)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hr = this->_OpenRemoteTransacted(fCreate);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CFtpStrmImpl::SetAuth(LPWSTR pwszUserName,
|
|
LPWSTR pwszPassword)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TRACE("CFtpStrm::SetAuth");
|
|
|
|
if (_pwszUserName != NULL)
|
|
{
|
|
free(_pwszUserName);
|
|
_pwszUserName = NULL;
|
|
}
|
|
if (_pwszPassword != NULL)
|
|
{
|
|
free(_pwszPassword);
|
|
_pwszPassword = NULL;
|
|
}
|
|
|
|
if (pwszUserName != NULL)
|
|
{
|
|
_pwszUserName = DuplicateStringW(pwszUserName);
|
|
if (_pwszUserName == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (pwszPassword != NULL)
|
|
{
|
|
_pwszPassword = DuplicateStringW(pwszPassword);
|
|
if (_pwszPassword == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// IStream methods
|
|
|
|
HRESULT CFtpStrmImpl::Read(void * pv,
|
|
ULONG cb,
|
|
ULONG * pcbRead)
|
|
{
|
|
// locals
|
|
HRESULT hr = S_OK;
|
|
DWORD cbRead;
|
|
BOOL fReadSuccess;
|
|
TRACE("CFtpStrm::Read");
|
|
|
|
// check arguments
|
|
if (pv == NULL)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
// code
|
|
fReadSuccess = ReadFile(_hLocalFile,
|
|
pv,
|
|
cb,
|
|
&cbRead,
|
|
NULL);
|
|
if (!fReadSuccess)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
if (pcbRead != NULL)
|
|
{
|
|
*pcbRead = cbRead;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CFtpStrmImpl::Write(void const* pv,
|
|
ULONG cb,
|
|
ULONG * pcbWritten)
|
|
{
|
|
// locals
|
|
HRESULT hr = S_OK;
|
|
DWORD cbWritten;
|
|
BOOL fWriteSuccess;
|
|
TRACE("CFtpStrm::Write");
|
|
|
|
// check arguments
|
|
if (pv == NULL)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
// code
|
|
fWriteSuccess = WriteFile(_hLocalFile,
|
|
pv,
|
|
cb,
|
|
&cbWritten,
|
|
NULL);
|
|
if (!fWriteSuccess)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
if (pcbWritten != NULL)
|
|
{
|
|
*pcbWritten = cbWritten;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CFtpStrmImpl::Seek(LARGE_INTEGER dlibMove,
|
|
DWORD dwOrigin,
|
|
ULARGE_INTEGER * plibNewPosition)
|
|
{
|
|
// locals
|
|
DWORD dwMoveMethod = FILE_BEGIN; // makes compiler happy
|
|
LONG iHighPart;
|
|
DWORD dwResult;
|
|
HRESULT hr = S_OK;
|
|
TRACE("CFtpStrm::Seek");
|
|
|
|
// check args
|
|
if (dwOrigin != STREAM_SEEK_SET && dwOrigin != STREAM_SEEK_CUR && dwOrigin != STREAM_SEEK_END)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
|
|
// code
|
|
switch (dwOrigin) {
|
|
case STREAM_SEEK_SET:
|
|
dwMoveMethod = FILE_BEGIN;
|
|
break;
|
|
case STREAM_SEEK_CUR:
|
|
dwMoveMethod = FILE_CURRENT;
|
|
break;
|
|
case STREAM_SEEK_END:
|
|
dwMoveMethod = FILE_END;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
iHighPart = dlibMove.HighPart;
|
|
|
|
dwResult = SetFilePointer(_hLocalFile,
|
|
dlibMove.LowPart,
|
|
&iHighPart,
|
|
dwMoveMethod);
|
|
|
|
if (dwResult == INVALID_SET_FILE_POINTER)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
if (plibNewPosition != NULL)
|
|
{
|
|
(*plibNewPosition).LowPart = dwResult;
|
|
(*plibNewPosition).HighPart = iHighPart;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CFtpStrmImpl::Stat(STATSTG* pstatstg, //Location for STATSTG structure
|
|
DWORD grfStatFlag) //Values taken from the STATFLAG enumeration
|
|
{
|
|
// locals
|
|
HRESULT hr = S_OK;
|
|
TRACE("CFtpStrm::Stat");
|
|
|
|
// check args
|
|
if (grfStatFlag != STATFLAG_DEFAULT && grfStatFlag != STATFLAG_NONAME)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
if (grfStatFlag == STATFLAG_DEFAULT)
|
|
{
|
|
pstatstg->pwcsName = DuplicateStringW(_pwszURL);
|
|
}
|
|
|
|
pstatstg->type = STGTY_STREAM;
|
|
|
|
if (!GetFileSizeEx(_hLocalFile,(LARGE_INTEGER*)&pstatstg->cbSize))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
if (_fLocalResource)
|
|
{
|
|
if (!GetFileTime(_hLocalFile, &(pstatstg->ctime), &(pstatstg->atime), &(pstatstg->mtime)))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// BUGBUG: currently we look at the local file for filetime
|
|
if (!GetFileTime(_hLocalFile, &(pstatstg->ctime), &(pstatstg->atime), &(pstatstg->mtime)))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pstatstg->grfMode = 0; // BUGBUG: what should this be???
|
|
pstatstg->grfLocksSupported = LOCK_EXCLUSIVE;
|
|
pstatstg->clsid = CLSID_NULL;
|
|
pstatstg->grfStateBits = 0;
|
|
pstatstg->reserved = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CFtpStrmImpl::_CommitLocal(DWORD grfCommitFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TRACE("CFtpStrm::CommitLocal");
|
|
|
|
// commit a local transacted file
|
|
// a local transacted file should be copied to the original place
|
|
if (!CloseHandle(_hLocalFile)) // close local
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else if (!CopyFile(_pwszLocalFile, _pwszURL+8, FALSE)) // copy local
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
_hLocalFile = CreateFile(_pwszLocalFile, GENERIC_READ | GENERIC_WRITE, 0,
|
|
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // re-open local
|
|
if (_hLocalFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CFtpStrmImpl::_CommitRemote(DWORD grfCommitFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HINTERNET hSession = NULL;
|
|
LPWSTR pwszServer = NULL;
|
|
LPWSTR pwszPath = NULL;
|
|
ULONG cbRead;
|
|
DWORD fSizeHigh;
|
|
DWORD fSizeLow;
|
|
ULONG cbData;
|
|
LPVOID pbData;
|
|
URL_COMPONENTS urlComponents = {0};
|
|
INTERNET_PORT nPort = INTERNET_DEFAULT_FTP_PORT;
|
|
|
|
TRACE("CFtpStrm::CommitRemote");
|
|
|
|
// commit a remote transacted resource
|
|
// open remote resource and seek to start of it
|
|
// -- first parse the URL (server, port, path)
|
|
urlComponents.dwStructSize = sizeof(URL_COMPONENTS);
|
|
urlComponents.dwHostNameLength = 1;
|
|
urlComponents.dwUrlPathLength = 1;
|
|
urlComponents.nPort = 1;
|
|
if (!InternetCrackUrl(_pwszURL, 0, 0, &urlComponents))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
pwszServer = (LPWSTR)malloc(sizeof(WCHAR) * (1 + urlComponents.dwHostNameLength));
|
|
if (pwszServer == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
pwszServer = lstrcpyn(pwszServer, urlComponents.lpszHostName, 1 + urlComponents.dwHostNameLength); // +1 for the final null char
|
|
if (urlComponents.nPort != 0)
|
|
{
|
|
nPort = urlComponents.nPort;
|
|
}
|
|
|
|
pwszPath = (LPWSTR)malloc(sizeof(WCHAR) * (1 + urlComponents.dwUrlPathLength));
|
|
if (pwszPath == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
lstrcpyn(pwszPath, urlComponents.lpszUrlPath, 1 + urlComponents.dwUrlPathLength);
|
|
|
|
// -- then connect to the server
|
|
hSession = InternetConnect(_hInternet, pwszServer, urlComponents.nPort,
|
|
_pwszUserName, _pwszPassword, INTERNET_SERVICE_FTP, 0, 0);
|
|
if (hSession == NULL)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
if (!CloseHandle(_hLocalFile))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
if (!FtpPutFile(hSession, _pwszLocalFile, pwszPath, FTP_TRANSFER_TYPE_UNKNOWN, 0))
|
|
{
|
|
DWORD dw = GetLastError();
|
|
HRESULT hres = HRESULT_FROM_WIN32(dw);
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
_hLocalFile = CreateFile(_pwszLocalFile, GENERIC_READ | GENERIC_WRITE, 0,
|
|
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (_hLocalFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
free(pwszPath);
|
|
}
|
|
}
|
|
free(pwszServer);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CFtpStrmImpl::Commit(DWORD grfCommitFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (_fDirect)
|
|
{
|
|
hr = E_FAIL; // in direct mode, commit is meaningless
|
|
}
|
|
else if (grfCommitFlags != STGC_DEFAULT)
|
|
{
|
|
hr = E_INVALIDARG; // we only support the default commit style
|
|
}
|
|
else
|
|
{
|
|
if (_fLocalResource)
|
|
hr = this->_CommitLocal(grfCommitFlags);
|
|
else
|
|
hr = this->_CommitRemote(grfCommitFlags);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CFtpStrmImpl::Revert()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TRACE("CFtpStrm::Revert");
|
|
|
|
if (_fDirect)
|
|
{
|
|
hr = E_FAIL; // in direct mode, revert is meaningless
|
|
}
|
|
else
|
|
{
|
|
if (_fLocalResource)
|
|
{
|
|
// revert a local transacted file
|
|
if (!CloseHandle(_hLocalFile)) // should delete the file if needed...
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else if (!CopyFile(_pwszURL+8, _pwszLocalFile, FALSE)) // ... but we'll overwrite it if not
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
_hLocalFile = CreateFile(_pwszLocalFile, GENERIC_READ | GENERIC_WRITE, 0,
|
|
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (_hLocalFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// revert commit a remote transacted resource
|
|
if (!CloseHandle(_hLocalFile)) // this should delete the file
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hr = this->_OpenRemoteTransacted(FALSE); // don't create, we want to reopen what was there before
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// These IStream methods are not supported
|
|
|
|
HRESULT CFtpStrmImpl::SetSize(ULARGE_INTEGER UNREF_PARAM(libNewSize)) //Specifies the new size of the stream object
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CFtpStrmImpl::CopyTo(IStream * UNREF_PARAM(pstm), //Points to the destination stream
|
|
ULARGE_INTEGER UNREF_PARAM(cb), //Specifies the number of bytes to copy
|
|
ULARGE_INTEGER * UNREF_PARAM(pcbRead), //Pointer to the actual number of bytes
|
|
// read from the source
|
|
ULARGE_INTEGER * UNREF_PARAM(pcbWritten)) //Pointer to the actual number of
|
|
// bytes written to the destination
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CFtpStrmImpl::LockRegion(ULARGE_INTEGER UNREF_PARAM(libOffset), //Specifies the byte offset for
|
|
// the beginning of the range
|
|
ULARGE_INTEGER UNREF_PARAM(cb), //Specifies the length of the range in bytes
|
|
DWORD UNREF_PARAM(dwLockType)) //Specifies the restriction on
|
|
// accessing the specified range
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CFtpStrmImpl::UnlockRegion(ULARGE_INTEGER UNREF_PARAM(libOffset), //Specifies the byte offset for
|
|
// the beginning of the range
|
|
ULARGE_INTEGER UNREF_PARAM(cb), //Specifies the length of the range in bytes
|
|
DWORD UNREF_PARAM(dwLockType)) //Specifies the access restriction
|
|
// previously placed on the range
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
HRESULT CFtpStrmImpl::Clone(IStream ** UNREF_PARAM(ppstm)) //Points to location for pointer to the new stream object
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|