1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00

Factoring out common upload code

Source commit: a5f5533a8f027759210736e5faecdd47328b28cf
This commit is contained in:
Martin Prikryl
2017-11-16 14:23:20 +00:00
parent 32b5ea931c
commit 5cba2a567e
13 changed files with 1179 additions and 1616 deletions

View File

@@ -13,6 +13,7 @@ class TCopyParamType;
struct TSpaceAvailable;
class TFileOperationProgressType;
class TRemoteProperties;
struct TLocalFileHandle;
//---------------------------------------------------------------------------
enum TFSCommand { fsNull = 0, fsVarValue, fsLastLine, fsFirstLine,
fsCurrentDirectory, fsChangeDirectory, fsListDirectory, fsListCurrentDirectory,
@@ -57,6 +58,13 @@ public:
const UnicodeString TargetDir, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress,
TOnceDoneOperation & OnceDoneOperation) = 0;
virtual void __fastcall TransferOnDirectory(
const UnicodeString & Directory, const TCopyParamType * CopyParam, int Params) {};
virtual void __fastcall Source(
TLocalFileHandle & Handle, const UnicodeString & TargetDir, UnicodeString & DestFileName,
const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags,
TUploadSessionAction & Action, bool & ChildError) = 0;
virtual void __fastcall CreateDirectory(const UnicodeString DirName) = 0;
virtual void __fastcall CreateLink(const UnicodeString FileName, const UnicodeString PointTo, bool Symbolic) = 0;
virtual void __fastcall DeleteFile(const UnicodeString FileName,

View File

@@ -186,8 +186,6 @@ struct TFileTransferData
TDateTime Modification;
};
//---------------------------------------------------------------------------
const int tfFirstLevel = 0x01;
const int tfAutoResume = 0x02;
const UnicodeString CertificateStorageKey(L"FtpsCertificates");
const UnicodeString SiteCommand(L"SITE");
const UnicodeString SymlinkSiteCommand(L"SYMLINK");
@@ -1824,97 +1822,20 @@ void __fastcall TFTPFileSystem::SinkFile(UnicodeString FileName,
}
}
//---------------------------------------------------------------------------
void __fastcall TFTPFileSystem::TransferOnDirectory(
const UnicodeString & Directory, const TCopyParamType * CopyParam, int Params)
{
AutoDetectTimeDifference(Directory, CopyParam, Params);
}
//---------------------------------------------------------------------------
void __fastcall TFTPFileSystem::CopyToRemote(TStrings * FilesToCopy,
const UnicodeString ATargetDir, const TCopyParamType * CopyParam,
const UnicodeString TargetDir, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress,
TOnceDoneOperation & OnceDoneOperation)
{
DebugAssert((FilesToCopy != NULL) && (OperationProgress != NULL));
AutoDetectTimeDifference(ATargetDir, CopyParam, Params);
Params &= ~cpAppend;
UnicodeString FileName, FileNameOnly;
UnicodeString TargetDir = AbsolutePath(ATargetDir, false);
UnicodeString FullTargetDir = UnixIncludeTrailingBackslash(TargetDir);
int Index = 0;
while ((Index < FilesToCopy->Count) && !OperationProgress->Cancel)
{
bool Success = false;
FileName = FilesToCopy->Strings[Index];
FileNameOnly = ExtractFileName(FileName);
try
{
try
{
if (FTerminal->SessionData->CacheDirectories)
{
FTerminal->DirectoryModified(TargetDir, false);
if (DirectoryExists(ApiPath(FileName)))
{
FTerminal->DirectoryModified(FullTargetDir + FileNameOnly, true);
}
}
SourceRobust(FileName, FullTargetDir, CopyParam, Params, OperationProgress,
tfFirstLevel);
Success = true;
FLastDataSent = Now();
}
catch(EScpSkipFile & E)
{
TSuspendFileOperationProgress Suspend(OperationProgress);
if (!FTerminal->HandleException(&E))
{
throw;
}
}
}
__finally
{
OperationProgress->Finish(FileName, Success, OnceDoneOperation);
}
Index++;
}
}
//---------------------------------------------------------------------------
void __fastcall TFTPFileSystem::SourceRobust(const UnicodeString FileName,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags)
{
// the same in TSFTPFileSystem
TUploadSessionAction Action(FTerminal->ActionLog);
TRobustOperationLoop RobustLoop(FTerminal, OperationProgress, &FFileTransferAny);
do
{
try
{
Source(FileName, TargetDir, CopyParam, Params, OperationProgress,
Flags, Action);
}
catch(Exception & E)
{
if (!RobustLoop.TryReopen(E))
{
FTerminal->RollbackAction(Action, OperationProgress, &E);
throw;
}
}
if (RobustLoop.ShouldRetry())
{
OperationProgress->RollbackTransfer();
Action.Restart();
// prevent overwrite confirmations
// (should not be set for directories!)
Params |= cpNoConfirmation;
Flags |= tfAutoResume;
}
}
while (RobustLoop.Retry());
FTerminal->DoCopyToRemote(FilesToCopy, TargetDir, CopyParam, Params, OperationProgress, tfUseFileTransferAny, OnceDoneOperation);
}
//---------------------------------------------------------------------------
bool __fastcall TFTPFileSystem::CanTransferSkipList(int Params, unsigned int Flags, const TCopyParamType * CopyParam)
@@ -1929,280 +1850,68 @@ bool __fastcall TFTPFileSystem::CanTransferSkipList(int Params, unsigned int Fla
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TFTPFileSystem::Source(const UnicodeString FileName,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
void __fastcall TFTPFileSystem::Source(
TLocalFileHandle & Handle, const UnicodeString & TargetDir, UnicodeString & DestFileName,
const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags,
TUploadSessionAction & Action)
TUploadSessionAction & Action, bool & /*ChildError*/)
{
Action.FileName(ExpandUNCFileName(FileName));
Handle.Close();
OperationProgress->SetFile(FileName, false);
ResetFileTransfer();
TFileTransferData UserData;
unsigned int TransferType = (OperationProgress->AsciiTransfer ? 1 : 2);
if (!FTerminal->AllowLocalFileTransfer(FileName, CopyParam, OperationProgress))
{
THROW_SKIP_FILE_NULL;
// ignore file list
TFileListHelper Helper(this, NULL, true);
SetCPSLimit(OperationProgress);
// not used for uploads anyway
FFileTransferPreserveTime = CopyParam->PreserveTime;
FFileTransferRemoveBOM = CopyParam->RemoveBOM;
FFileTransferNoList = CanTransferSkipList(Params, Flags, CopyParam);
// not used for uploads, but we get new name (if any) back in this field
UserData.FileName = DestFileName;
UserData.Params = Params;
UserData.AutoResume = FLAGSET(Flags, tfAutoResume);
UserData.CopyParam = CopyParam;
UserData.Modification = Handle.Modification;
FileTransfer(Handle.FileName, Handle.FileName, DestFileName,
TargetDir, false, Handle.Size, TransferType, UserData, OperationProgress);
}
__int64 Size;
int Attrs;
UnicodeString DestFullName = TargetDir + UserData.FileName;
// only now, we know the final destination
Action.Destination(DestFullName);
FTerminal->OpenLocalFile(FileName, GENERIC_READ, &Attrs,
NULL, NULL, NULL, NULL, &Size);
OperationProgress->SetFileInProgress();
bool Dir = FLAGSET(Attrs, faDirectory);
if (Dir)
// We are not able to tell if setting timestamp succeeded,
// so we log it always (if supported).
// Support for MDTM does not necessarily mean that the server supports
// non-standard hack of setting timestamp using
// MFMT-like (two argument) call to MDTM.
// IIS definitelly does.
if (FFileTransferPreserveTime &&
((FServerCapabilities->GetCapability(mfmt_command) == yes) ||
((FServerCapabilities->GetCapability(mdtm_command) == yes))))
{
Action.Cancel();
DirectorySource(IncludeTrailingBackslash(FileName), TargetDir,
Attrs, CopyParam, Params, OperationProgress, Flags);
}
else
{
UnicodeString DestFileName =
FTerminal->ChangeFileName(
CopyParam, ExtractFileName(FileName), osLocal,
FLAGSET(Flags, tfFirstLevel));
TTouchSessionAction TouchAction(FTerminal->ActionLog, DestFullName, Handle.Modification);
FTerminal->LogEvent(FORMAT(L"Copying \"%s\" to remote directory started.", (FileName)));
OperationProgress->SetLocalSize(Size);
// Suppose same data size to transfer as to read
// (not true with ASCII transfer)
OperationProgress->SetTransferSize(OperationProgress->LocalSize);
TDateTime Modification;
// Inspired by SysUtils::FileAge
WIN32_FIND_DATA FindData;
HANDLE Handle = FindFirstFile(FileName.c_str(), &FindData);
if (Handle != INVALID_HANDLE_VALUE)
if (!FFileZillaIntf->UsingMlsd())
{
Modification =
UnixToDateTime(
ConvertTimestampToUnixSafe(FindData.ftLastWriteTime, dstmUnix),
dstmUnix);
FindClose(Handle);
}
// Will we use ASCII of BINARY file transfer?
TFileMasks::TParams MaskParams;
MaskParams.Size = Size;
MaskParams.Modification = Modification;
UnicodeString BaseFileName = FTerminal->GetBaseFileName(FileName);
OperationProgress->SetAsciiTransfer(
CopyParam->UseAsciiTransfer(BaseFileName, osLocal, MaskParams));
FTerminal->LogEvent(
UnicodeString(OperationProgress->AsciiTransfer ? L"Ascii" : L"Binary") +
L" transfer mode selected.");
ResetFileTransfer();
TFileTransferData UserData;
unsigned int TransferType = (OperationProgress->AsciiTransfer ? 1 : 2);
{
// ignore file list
TFileListHelper Helper(this, NULL, true);
SetCPSLimit(OperationProgress);
// not used for uploads anyway
FFileTransferPreserveTime = CopyParam->PreserveTime;
FFileTransferRemoveBOM = CopyParam->RemoveBOM;
FFileTransferNoList = CanTransferSkipList(Params, Flags, CopyParam);
// not used for uploads, but we get new name (if any) back in this field
UserData.FileName = DestFileName;
UserData.Params = Params;
UserData.AutoResume = FLAGSET(Flags, tfAutoResume);
UserData.CopyParam = CopyParam;
UserData.Modification = Modification;
FileTransfer(FileName, FileName, DestFileName,
TargetDir, false, Size, TransferType, UserData, OperationProgress);
}
UnicodeString DestFullName = TargetDir + UserData.FileName;
// only now, we know the final destination
Action.Destination(DestFullName);
// We are not able to tell if setting timestamp succeeded,
// so we log it always (if supported).
// Support for MDTM does not necessarily mean that the server supports
// non-standard hack of setting timestamp using
// MFMT-like (two argument) call to MDTM.
// IIS definitelly does.
if (FFileTransferPreserveTime &&
((FServerCapabilities->GetCapability(mfmt_command) == yes) ||
((FServerCapabilities->GetCapability(mdtm_command) == yes))))
{
TTouchSessionAction TouchAction(FTerminal->ActionLog, DestFullName, Modification);
if (!FFileZillaIntf->UsingMlsd())
FUploadedTimes[DestFullName] = Handle.Modification;
if ((FTerminal->Configuration->ActualLogProtocol >= 2))
{
FUploadedTimes[DestFullName] = Modification;
if ((FTerminal->Configuration->ActualLogProtocol >= 2))
{
FTerminal->LogEvent(
FORMAT(L"Remembering modification time of \"%s\" as [%s]",
(DestFullName, StandardTimestamp(FUploadedTimes[DestFullName]))));
}
}
}
FTerminal->LogFileDone(OperationProgress, DestFullName);
}
/* TODO : Delete also read-only files. */
if (FLAGSET(Params, cpDelete))
{
if (!Dir)
{
FILE_OPERATION_LOOP_BEGIN
{
THROWOSIFFALSE(Sysutils::DeleteFile(ApiPath(FileName)));
}
FILE_OPERATION_LOOP_END(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (FileName)));
}
}
else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
{
FILE_OPERATION_LOOP_BEGIN
{
THROWOSIFFALSE(FileSetAttr(ApiPath(FileName), Attrs & ~faArchive) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (FileName)));
}
}
//---------------------------------------------------------------------------
void __fastcall TFTPFileSystem::DirectorySource(const UnicodeString DirectoryName,
const UnicodeString TargetDir, int Attrs, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress, unsigned int Flags)
{
UnicodeString DestDirectoryName =
FTerminal->ChangeFileName(
CopyParam,
ExtractFileName(ExcludeTrailingBackslash(DirectoryName)),
osLocal, FLAGSET(Flags, tfFirstLevel));
UnicodeString DestFullName = UnixIncludeTrailingBackslash(TargetDir + DestDirectoryName);
AutoDetectTimeDifference(TargetDir, CopyParam, Params);
OperationProgress->SetFile(DirectoryName);
bool CreateDir = true;
if (FLAGCLEAR(Params, cpNoRecurse))
{
int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;
TSearchRecChecked SearchRec;
bool FindOK;
FILE_OPERATION_LOOP_BEGIN
{
FindOK =
(FindFirstChecked(DirectoryName + L"*.*", FindAttrs, SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
try
{
while (FindOK && !OperationProgress->Cancel)
{
UnicodeString FileName = DirectoryName + SearchRec.Name;
try
{
if ((SearchRec.Name != L".") && (SearchRec.Name != L".."))
{
SourceRobust(FileName, DestFullName, CopyParam, Params, OperationProgress,
Flags & ~(tfFirstLevel | tfAutoResume));
// if any file got uploaded (i.e. there were any file in the
// directory and at least one was not skipped),
// do not try to create the directory,
// as it should be already created by FZAPI during upload
CreateDir = false;
}
}
catch (EScpSkipFile &E)
{
// If ESkipFile occurs, just log it and continue with next file
TSuspendFileOperationProgress Suspend(OperationProgress);
// here a message to user was displayed, which was not appropriate
// when user refused to overwrite the file in subdirectory.
// hopefully it won't be missing in other situations.
if (!FTerminal->HandleException(&E))
{
throw;
}
}
FILE_OPERATION_LOOP_BEGIN
{
FindOK = (FindNextChecked(SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
}
}
__finally
{
FindClose(SearchRec);
}
}
if (CreateDir)
{
TRemoteProperties Properties;
if (CopyParam->PreserveRights)
{
Properties.Valid = TValidProperties() << vpRights;
Properties.Rights = CopyParam->RemoteFileRights(Attrs);
}
try
{
FTerminal->ExceptionOnFail = true;
try
{
FTerminal->CreateDirectory(DestFullName, &Properties);
}
__finally
{
FTerminal->ExceptionOnFail = false;
}
}
catch(...)
{
TRemoteFile * File = NULL;
// ignore non-fatal error when the directory already exists
bool Rethrow =
!FTerminal->Active ||
!FTerminal->FileExists(DestFullName, &File) ||
!File->IsDirectory;
delete File;
if (Rethrow)
{
throw;
FTerminal->LogEvent(
FORMAT(L"Remembering modification time of \"%s\" as [%s]",
(DestFullName, StandardTimestamp(FUploadedTimes[DestFullName]))));
}
}
}
/* TODO : Delete also read-only directories. */
/* TODO : Show error message on failure. */
if (!OperationProgress->Cancel)
{
if (FLAGSET(Params, cpDelete))
{
DebugAssert(FLAGCLEAR(Params, cpNoRecurse));
RemoveDir(ApiPath(DirectoryName));
}
else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
{
FILE_OPERATION_LOOP_BEGIN
{
THROWOSIFFALSE(FileSetAttr(ApiPath(DirectoryName), Attrs & ~faArchive) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (DirectoryName)));
}
}
FLastDataSent = Now();
}
//---------------------------------------------------------------------------
void __fastcall TFTPFileSystem::CreateDirectory(const UnicodeString ADirName)

View File

@@ -47,6 +47,8 @@ public:
const UnicodeString TargetDir, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress,
TOnceDoneOperation & OnceDoneOperation);
virtual void __fastcall TransferOnDirectory(
const UnicodeString & Directory, const TCopyParamType * CopyParam, int Params);
virtual void __fastcall CopyToRemote(TStrings * FilesToCopy,
const UnicodeString TargetDir, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress,
@@ -155,16 +157,11 @@ protected:
const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags);
void __fastcall SinkFile(UnicodeString FileName, const TRemoteFile * File, void * Param);
void __fastcall SourceRobust(const UnicodeString FileName,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags);
void __fastcall Source(const UnicodeString FileName,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
void __fastcall Source(
TLocalFileHandle & Handle, const UnicodeString & TargetDir, UnicodeString & DestFileName,
const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags,
TUploadSessionAction & Action);
void __fastcall DirectorySource(const UnicodeString DirectoryName,
const UnicodeString TargetDir, int Attrs, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress, unsigned int Flags);
TUploadSessionAction & Action, bool & ChildError);
bool __fastcall ConfirmOverwrite(const UnicodeString & SourceFullFileName, UnicodeString & TargetFileName,
TOverwriteMode & OverwriteMode, TFileOperationProgressType * OperationProgress,
const TOverwriteFileParams * FileParams, const TCopyParamType * CopyParam,

View File

@@ -25,8 +25,6 @@
//---------------------------------------------------------------------------
#define FILE_OPERATION_LOOP_TERMINAL FTerminal
//---------------------------------------------------------------------------
const int tfFirstLevel = 0x01;
//---------------------------------------------------------------------------
static std::unique_ptr<TCriticalSection> LibS3Section(TraceInitPtr(new TCriticalSection()));
//---------------------------------------------------------------------------
UTF8String LibS3Delimiter(L"/");
@@ -1046,6 +1044,15 @@ void __fastcall TS3FileSystem::CopyToRemote(TStrings * /*FilesToCopy*/,
throw Exception(L"Not implemented");
}
//---------------------------------------------------------------------------
void __fastcall TS3FileSystem::Source(
TLocalFileHandle & /*Handle*/, const UnicodeString & /*TargetDir*/, UnicodeString & /*DestFileName*/,
const TCopyParamType * /*CopyParam*/, int /*Params*/,
TFileOperationProgressType * /*OperationProgress*/, unsigned int /*Flags*/,
TUploadSessionAction & /*Action*/, bool & /*ChildError*/)
{
throw Exception(L"Not implemented");
}
//---------------------------------------------------------------------------
void __fastcall TS3FileSystem::CopyToLocal(TStrings * /*FilesToCopy*/,
const UnicodeString TargetDir, const TCopyParamType * /*CopyParam*/,
int /*Params*/, TFileOperationProgressType * /*OperationProgress*/,

View File

@@ -58,6 +58,11 @@ public:
const UnicodeString TargetDir, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress,
TOnceDoneOperation & OnceDoneOperation);
virtual void __fastcall Source(
TLocalFileHandle & Handle, const UnicodeString & TargetDir, UnicodeString & DestFileName,
const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags,
TUploadSessionAction & Action, bool & ChildError);
virtual void __fastcall CreateDirectory(const UnicodeString DirName);
virtual void __fastcall CreateLink(const UnicodeString FileName, const UnicodeString PointTo, bool Symbolic);
virtual void __fastcall DeleteFile(const UnicodeString FileName,

View File

@@ -1698,6 +1698,15 @@ void __fastcall TSCPFileSystem::CopyToRemote(TStrings * FilesToCopy,
}
}
//---------------------------------------------------------------------------
void __fastcall TSCPFileSystem::Source(
TLocalFileHandle & /*Handle*/, const UnicodeString & /*TargetDir*/, UnicodeString & /*DestFileName*/,
const TCopyParamType * /*CopyParam*/, int /*Params*/,
TFileOperationProgressType * /*OperationProgress*/, unsigned int /*Flags*/,
TUploadSessionAction & /*Action*/, bool & /*ChildError*/)
{
DebugFail();
}
//---------------------------------------------------------------------------
void __fastcall TSCPFileSystem::SCPSource(const UnicodeString FileName,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, int Level)
@@ -1715,292 +1724,248 @@ void __fastcall TSCPFileSystem::SCPSource(const UnicodeString FileName,
THROW_SKIP_FILE_NULL;
}
HANDLE File;
int Attrs;
__int64 MTime, ATime;
__int64 Size;
TLocalFileHandle Handle;
FTerminal->OpenLocalFile(FileName, GENERIC_READ, Handle);
FTerminal->OpenLocalFile(FileName, GENERIC_READ,
&Attrs, &File, NULL, &MTime, &ATime, &Size);
OperationProgress->SetFileInProgress();
bool Dir = FLAGSET(Attrs, faDirectory);
TStream * Stream = new TSafeHandleStream((THandle)File);
try
if (Handle.Directory)
{
OperationProgress->SetFileInProgress();
SCPDirectorySource(FileName, TargetDir, CopyParam, Params, OperationProgress, Level);
}
else
{
UnicodeString AbsoluteFileName = FTerminal->AbsolutePath(TargetDir + DestFileName, false);
if (Dir)
DebugAssert(Handle.Handle);
std::unique_ptr<TStream> Stream(new TSafeHandleStream((THandle)Handle.Handle));
// File is regular file (not directory)
FTerminal->LogEvent(FORMAT(L"Copying \"%s\" to remote directory started.", (FileName)));
OperationProgress->SetLocalSize(Handle.Size);
// Suppose same data size to transfer as to read
// (not true with ASCII transfer)
OperationProgress->SetTransferSize(OperationProgress->LocalSize);
OperationProgress->SetTransferringFile(false);
FTerminal->SelectSourceTransferMode(Handle, CopyParam);
TUploadSessionAction Action(FTerminal->ActionLog);
Action.FileName(ExpandUNCFileName(FileName));
Action.Destination(AbsoluteFileName);
TRights Rights = CopyParam->RemoteFileRights(Handle.Attrs);
try
{
SCPDirectorySource(FileName, TargetDir, CopyParam, Params, OperationProgress, Level);
}
else
{
UnicodeString AbsoluteFileName = FTerminal->AbsolutePath(TargetDir + DestFileName, false);
DebugAssert(File);
// File is regular file (not directory)
FTerminal->LogEvent(FORMAT(L"Copying \"%s\" to remote directory started.", (FileName)));
OperationProgress->SetLocalSize(Size);
// Suppose same data size to transfer as to read
// (not true with ASCII transfer)
OperationProgress->SetTransferSize(OperationProgress->LocalSize);
OperationProgress->SetTransferringFile(false);
TDateTime Modification = UnixToDateTime(MTime, FTerminal->SessionData->DSTMode);
// Will we use ASCII of BINARY file transfer?
TFileMasks::TParams MaskParams;
MaskParams.Size = Size;
MaskParams.Modification = Modification;
UnicodeString BaseFileName = FTerminal->GetBaseFileName(FileName);
OperationProgress->SetAsciiTransfer(
CopyParam->UseAsciiTransfer(BaseFileName, osLocal, MaskParams));
FTerminal->LogEvent(
UnicodeString((OperationProgress->AsciiTransfer ? L"Ascii" : L"Binary")) +
L" transfer mode selected.");
TUploadSessionAction Action(FTerminal->ActionLog);
Action.FileName(ExpandUNCFileName(FileName));
Action.Destination(AbsoluteFileName);
TRights Rights = CopyParam->RemoteFileRights(Attrs);
try
// During ASCII transfer we will load whole file to this buffer
// than convert EOL and send it at once, because before converting EOL
// we can't know its size
TFileBuffer AsciiBuf;
bool ConvertToken = false;
do
{
// During ASCII transfer we will load whole file to this buffer
// than convert EOL and send it at once, because before converting EOL
// we can't know its size
TFileBuffer AsciiBuf;
bool ConvertToken = false;
do
// Buffer for one block of data
TFileBuffer BlockBuf;
// This is crucial, if it fails during file transfer, it's fatal error
FILE_OPERATION_LOOP_BEGIN
{
// Buffer for one block of data
TFileBuffer BlockBuf;
BlockBuf.LoadStream(Stream.get(), OperationProgress->LocalBlockSize(), true);
}
FILE_OPERATION_LOOP_END_EX(
FMTLOAD(READ_ERROR, (FileName)),
!OperationProgress->TransferringFile);
// This is crucial, if it fails during file transfer, it's fatal error
FILE_OPERATION_LOOP_BEGIN
OperationProgress->AddLocallyUsed(BlockBuf.Size);
// We do ASCII transfer: convert EOL of current block
// (we don't convert whole buffer, cause it would produce
// huge memory-transfers while inserting/deleting EOL characters)
// Than we add current block to file buffer
if (OperationProgress->AsciiTransfer)
{
int ConvertParams =
FLAGMASK(CopyParam->RemoveCtrlZ, cpRemoveCtrlZ) |
FLAGMASK(CopyParam->RemoveBOM, cpRemoveBOM);
BlockBuf.Convert(FTerminal->Configuration->LocalEOLType,
FTerminal->SessionData->EOLType,
ConvertParams, ConvertToken);
BlockBuf.Memory->Seek(0, soFromBeginning);
AsciiBuf.ReadStream(BlockBuf.Memory, BlockBuf.Size, true);
// We don't need it any more
BlockBuf.Memory->Clear();
// Calculate total size to sent (assume that ratio between
// size of source and size of EOL-transformed data would remain same)
// First check if file contains anything (div by zero!)
if (OperationProgress->LocallyUsed)
{
BlockBuf.LoadStream(Stream, OperationProgress->LocalBlockSize(), true);
__int64 X = OperationProgress->LocalSize;
X *= AsciiBuf.Size;
X /= OperationProgress->LocallyUsed;
OperationProgress->ChangeTransferSize(X);
}
FILE_OPERATION_LOOP_END_EX(
FMTLOAD(READ_ERROR, (FileName)),
!OperationProgress->TransferringFile);
OperationProgress->AddLocallyUsed(BlockBuf.Size);
// We do ASCII transfer: convert EOL of current block
// (we don't convert whole buffer, cause it would produce
// huge memory-transfers while inserting/deleting EOL characters)
// Than we add current block to file buffer
if (OperationProgress->AsciiTransfer)
else
{
int ConvertParams =
FLAGMASK(CopyParam->RemoveCtrlZ, cpRemoveCtrlZ) |
FLAGMASK(CopyParam->RemoveBOM, cpRemoveBOM);
BlockBuf.Convert(FTerminal->Configuration->LocalEOLType,
FTerminal->SessionData->EOLType,
ConvertParams, ConvertToken);
BlockBuf.Memory->Seek(0, soFromBeginning);
AsciiBuf.ReadStream(BlockBuf.Memory, BlockBuf.Size, true);
// We don't need it any more
BlockBuf.Memory->Clear();
// Calculate total size to sent (assume that ratio between
// size of source and size of EOL-transformed data would remain same)
// First check if file contains anything (div by zero!)
if (OperationProgress->LocallyUsed)
{
__int64 X = OperationProgress->LocalSize;
X *= AsciiBuf.Size;
X /= OperationProgress->LocallyUsed;
OperationProgress->ChangeTransferSize(X);
}
else
{
OperationProgress->ChangeTransferSize(0);
}
OperationProgress->ChangeTransferSize(0);
}
}
// We send file information on first pass during BINARY transfer
// and on last pass during ASCII transfer
// BINARY: We succeeded reading first buffer from file, hopefully
// we will be able to read whole, so we send file info to remote side
// This is done, because when reading fails we can't interrupt sending
// (don't know how to tell other side that it failed)
if (!OperationProgress->TransferringFile &&
(!OperationProgress->AsciiTransfer || OperationProgress->IsLocallyDone()))
// We send file information on first pass during BINARY transfer
// and on last pass during ASCII transfer
// BINARY: We succeeded reading first buffer from file, hopefully
// we will be able to read whole, so we send file info to remote side
// This is done, because when reading fails we can't interrupt sending
// (don't know how to tell other side that it failed)
if (!OperationProgress->TransferringFile &&
(!OperationProgress->AsciiTransfer || OperationProgress->IsLocallyDone()))
{
UnicodeString Buf;
if (CopyParam->PreserveTime)
{
UnicodeString Buf;
if (CopyParam->PreserveTime)
{
// Send last file access and modification time
// TVarRec don't understand 'unsigned int' -> we use sprintf()
Buf.sprintf(L"T%lu 0 %lu 0", static_cast<unsigned long>(MTime),
static_cast<unsigned long>(ATime));
FSecureShell->SendLine(Buf);
SCPResponse();
}
// Send file modes (rights), filesize and file name
// Send last file access and modification time
// TVarRec don't understand 'unsigned int' -> we use sprintf()
Buf.sprintf(L"C%s %Ld %s",
Rights.Octal.data(),
(OperationProgress->AsciiTransfer ? (__int64)AsciiBuf.Size :
OperationProgress->LocalSize),
DestFileName.data());
Buf.sprintf(L"T%lu 0 %lu 0", static_cast<unsigned long>(Handle.MTime),
static_cast<unsigned long>(Handle.ATime));
FSecureShell->SendLine(Buf);
SCPResponse();
// Indicate we started transferring file, we need to finish it
// If not, it's fatal error
OperationProgress->SetTransferringFile(true);
}
// If we're doing ASCII transfer, this is last pass
// so we send whole file
/* TODO : We can't send file above 32bit size in ASCII mode! */
if (OperationProgress->AsciiTransfer)
// Send file modes (rights), filesize and file name
// TVarRec don't understand 'unsigned int' -> we use sprintf()
Buf.sprintf(L"C%s %Ld %s",
Rights.Octal.data(),
(OperationProgress->AsciiTransfer ? (__int64)AsciiBuf.Size :
OperationProgress->LocalSize),
DestFileName.data());
FSecureShell->SendLine(Buf);
SCPResponse();
// Indicate we started transferring file, we need to finish it
// If not, it's fatal error
OperationProgress->SetTransferringFile(true);
// If we're doing ASCII transfer, this is last pass
// so we send whole file
/* TODO : We can't send file above 32bit size in ASCII mode! */
if (OperationProgress->AsciiTransfer)
{
FTerminal->LogEvent(FORMAT(L"Sending ASCII data (%u bytes)",
(AsciiBuf.Size)));
// Should be equal, just in case it's rounded (see above)
OperationProgress->ChangeTransferSize(AsciiBuf.Size);
while (!OperationProgress->IsTransferDone())
{
FTerminal->LogEvent(FORMAT(L"Sending ASCII data (%u bytes)",
(AsciiBuf.Size)));
// Should be equal, just in case it's rounded (see above)
OperationProgress->ChangeTransferSize(AsciiBuf.Size);
while (!OperationProgress->IsTransferDone())
unsigned long BlockSize = OperationProgress->TransferBlockSize();
FSecureShell->Send(
reinterpret_cast<unsigned char *>(AsciiBuf.Data + (unsigned int)OperationProgress->TransferredSize),
BlockSize);
OperationProgress->AddTransferred(BlockSize);
if (OperationProgress->Cancel == csCancelTransfer)
{
unsigned long BlockSize = OperationProgress->TransferBlockSize();
FSecureShell->Send(
reinterpret_cast<unsigned char *>(AsciiBuf.Data + (unsigned int)OperationProgress->TransferredSize),
BlockSize);
OperationProgress->AddTransferred(BlockSize);
if (OperationProgress->Cancel == csCancelTransfer)
{
throw Exception(MainInstructions(LoadStr(USER_TERMINATED)));
}
throw Exception(MainInstructions(LoadStr(USER_TERMINATED)));
}
}
}
}
// At end of BINARY transfer pass, send current block
if (!OperationProgress->AsciiTransfer)
// At end of BINARY transfer pass, send current block
if (!OperationProgress->AsciiTransfer)
{
if (!OperationProgress->TransferredSize)
{
if (!OperationProgress->TransferredSize)
{
FTerminal->LogEvent(FORMAT(L"Sending BINARY data (first block, %u bytes)",
(BlockBuf.Size)));
}
else if (FTerminal->Configuration->ActualLogProtocol >= 1)
{
FTerminal->LogEvent(FORMAT(L"Sending BINARY data (%u bytes)",
(BlockBuf.Size)));
}
FSecureShell->Send(reinterpret_cast<const unsigned char *>(BlockBuf.Data), BlockBuf.Size);
OperationProgress->AddTransferred(BlockBuf.Size);
FTerminal->LogEvent(FORMAT(L"Sending BINARY data (first block, %u bytes)",
(BlockBuf.Size)));
}
if ((OperationProgress->Cancel == csCancelTransfer) ||
(OperationProgress->Cancel == csCancel && !OperationProgress->TransferringFile))
else if (FTerminal->Configuration->ActualLogProtocol >= 1)
{
throw Exception(MainInstructions(LoadStr(USER_TERMINATED)));
FTerminal->LogEvent(FORMAT(L"Sending BINARY data (%u bytes)",
(BlockBuf.Size)));
}
}
while (!OperationProgress->IsLocallyDone() || !OperationProgress->IsTransferDone());
FSecureShell->SendNull();
try
{
SCPResponse();
// If one of two following exceptions occurs, it means, that remote
// side already know, that file transfer finished, even if it failed
// so we don't have to throw EFatal
}
catch (EScp &E)
{
// SCP protocol fatal error
OperationProgress->SetTransferringFile(false);
throw;
}
catch (EScpFileSkipped &E)
{
// SCP protocol non-fatal error
OperationProgress->SetTransferringFile(false);
throw;
FSecureShell->Send(reinterpret_cast<const unsigned char *>(BlockBuf.Data), BlockBuf.Size);
OperationProgress->AddTransferred(BlockBuf.Size);
}
// We succeeded transferring file, from now we can handle exceptions
// normally -> no fatal error
if ((OperationProgress->Cancel == csCancelTransfer) ||
(OperationProgress->Cancel == csCancel && !OperationProgress->TransferringFile))
{
throw Exception(MainInstructions(LoadStr(USER_TERMINATED)));
}
}
while (!OperationProgress->IsLocallyDone() || !OperationProgress->IsTransferDone());
FSecureShell->SendNull();
try
{
SCPResponse();
// If one of two following exceptions occurs, it means, that remote
// side already know, that file transfer finished, even if it failed
// so we don't have to throw EFatal
}
catch (EScp &E)
{
// SCP protocol fatal error
OperationProgress->SetTransferringFile(false);
throw;
}
catch (Exception &E)
catch (EScpFileSkipped &E)
{
// EScpFileSkipped is derived from EScpSkipFile,
// but is does not indicate file skipped by user here
if (dynamic_cast<EScpFileSkipped *>(&E) != NULL)
{
Action.Rollback(&E);
}
else
{
FTerminal->RollbackAction(Action, OperationProgress, &E);
}
// Every exception during file transfer is fatal
if (OperationProgress->TransferringFile)
{
FTerminal->FatalError(&E, FMTLOAD(COPY_FATAL, (FileName)));
}
else
{
throw;
}
// SCP protocol non-fatal error
OperationProgress->SetTransferringFile(false);
throw;
}
// With SCP we are not able to distinguish reason for failure
// (upload itself, touch or chmod).
// So we always report error with upload action and
// log touch and chmod actions only if upload succeeds.
if (CopyParam->PreserveTime)
{
TTouchSessionAction(FTerminal->ActionLog, AbsoluteFileName, Modification);
}
if (CopyParam->PreserveRights)
{
TChmodSessionAction(FTerminal->ActionLog, AbsoluteFileName,
Rights);
}
FTerminal->LogFileDone(OperationProgress, AbsoluteFileName);
// We succeeded transferring file, from now we can handle exceptions
// normally -> no fatal error
OperationProgress->SetTransferringFile(false);
}
}
__finally
{
if (File != NULL)
catch (Exception &E)
{
CloseHandle(File);
// EScpFileSkipped is derived from EScpSkipFile,
// but is does not indicate file skipped by user here
if (dynamic_cast<EScpFileSkipped *>(&E) != NULL)
{
Action.Rollback(&E);
}
else
{
FTerminal->RollbackAction(Action, OperationProgress, &E);
}
// Every exception during file transfer is fatal
if (OperationProgress->TransferringFile)
{
FTerminal->FatalError(&E, FMTLOAD(COPY_FATAL, (FileName)));
}
else
{
throw;
}
}
delete Stream;
// With SCP we are not able to distinguish reason for failure
// (upload itself, touch or chmod).
// So we always report error with upload action and
// log touch and chmod actions only if upload succeeds.
if (CopyParam->PreserveTime)
{
TTouchSessionAction(FTerminal->ActionLog, AbsoluteFileName, Handle.Modification);
}
if (CopyParam->PreserveRights)
{
TChmodSessionAction(FTerminal->ActionLog, AbsoluteFileName,
Rights);
}
FTerminal->LogFileDone(OperationProgress, AbsoluteFileName);
// Stream is disposed here
}
/* TODO : Delete also read-only files. */
if (FLAGSET(Params, cpDelete))
{
if (!Dir)
{
FILE_OPERATION_LOOP_BEGIN
{
THROWOSIFFALSE(Sysutils::DeleteFile(ApiPath(FileName)));
}
FILE_OPERATION_LOOP_END(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (FileName)));
}
}
else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
{
FILE_OPERATION_LOOP_BEGIN
{
THROWOSIFFALSE(FileSetAttr(ApiPath(FileName), Attrs & ~faArchive) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (FileName)));
}
Handle.Release();
FTerminal->UpdateSource(Handle, CopyParam, Params);
FTerminal->LogEvent(FORMAT(L"Copying \"%s\" to remote directory finished.", (FileName)));
}

View File

@@ -41,6 +41,11 @@ public:
const UnicodeString TargetDir, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress,
TOnceDoneOperation & OnceDoneOperation);
virtual void __fastcall Source(
TLocalFileHandle & Handle, const UnicodeString & TargetDir, UnicodeString & DestFileName,
const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags,
TUploadSessionAction & Action, bool & ChildError);
virtual void __fastcall CreateDirectory(const UnicodeString DirName);
virtual void __fastcall CreateLink(const UnicodeString FileName, const UnicodeString PointTo, bool Symbolic);
virtual void __fastcall DeleteFile(const UnicodeString FileName,

File diff suppressed because it is too large Load Diff

View File

@@ -52,6 +52,11 @@ public:
const UnicodeString TargetDir, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress,
TOnceDoneOperation & OnceDoneOperation);
void __fastcall Source(
TLocalFileHandle & Handle, const UnicodeString & TargetDir, UnicodeString & DestFileName,
const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags,
TUploadSessionAction & Action, bool & ChildError);
virtual void __fastcall CreateDirectory(const UnicodeString DirName);
virtual void __fastcall CreateLink(const UnicodeString FileName, const UnicodeString PointTo, bool Symbolic);
virtual void __fastcall DeleteFile(const UnicodeString FileName,
@@ -155,9 +160,6 @@ protected:
void __fastcall RegisterChecksumAlg(const UnicodeString & Alg, const UnicodeString & SftpAlg);
void __fastcall DoDeleteFile(const UnicodeString FileName, unsigned char Type);
void __fastcall SFTPSourceRobust(const UnicodeString FileName,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags);
void __fastcall SFTPSource(const UnicodeString FileName,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags,
@@ -168,9 +170,6 @@ protected:
void __fastcall SFTPCloseRemote(const RawByteString Handle,
const UnicodeString FileName, TFileOperationProgressType * OperationProgress,
bool TransferFinished, bool Request, TSFTPPacket * Packet);
void __fastcall SFTPDirectorySource(const UnicodeString DirectoryName,
const UnicodeString TargetDir, int Attrs, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress, unsigned int Flags);
void __fastcall SFTPConfirmOverwrite(const UnicodeString & FullFileName, UnicodeString & FileName,
const TCopyParamType * CopyParam, int Params, TFileOperationProgressType * OperationProgress,
TSFTPOverwriteMode & Mode, const TOverwriteFileParams * FileParams);

View File

@@ -1967,6 +1967,11 @@ void __fastcall TTerminal::DoProgress(TFileOperationProgressType & ProgressData)
}
}
if (ProgressData.TransferredSize > 0)
{
FFileTransferAny = true;
}
if (OnProgress != NULL)
{
TCallbackGuard Guard(this);
@@ -5048,6 +5053,17 @@ void __fastcall TTerminal::OpenLocalFile(const UnicodeString FileName,
if (AHandle) *AHandle = Handle;
}
//---------------------------------------------------------------------------
void __fastcall TTerminal::OpenLocalFile(
const UnicodeString & FileName, unsigned int Access, TLocalFileHandle & Handle, bool TryWriteReadOnly)
{
Handle.FileName = FileName;
OpenLocalFile(
FileName, Access, &Handle.Attrs, &Handle.Handle, NULL, &Handle.MTime, &Handle.ATime, &Handle.Size,
TryWriteReadOnly);
Handle.Modification = UnixToDateTime(Handle.MTime, SessionData->DSTMode);
Handle.Directory = FLAGSET(Handle.Attrs, faDirectory);
}
//---------------------------------------------------------------------------
bool __fastcall TTerminal::AllowLocalFileTransfer(UnicodeString FileName,
const TCopyParamType * CopyParam, TFileOperationProgressType * OperationProgress)
{
@@ -6532,6 +6548,325 @@ bool __fastcall TTerminal::CopyToRemote(TStrings * FilesToCopy,
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TTerminal::DoCopyToRemote(
TStrings * FilesToCopy, const UnicodeString & ATargetDir, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress, unsigned int Flags, TOnceDoneOperation & OnceDoneOperation)
{
DebugAssert((FilesToCopy != NULL) && (OperationProgress != NULL));
FFileSystem->TransferOnDirectory(ATargetDir, CopyParam, Params);
UnicodeString TargetDir = AbsolutePath(ATargetDir, false);
UnicodeString FullTargetDir = UnixIncludeTrailingBackslash(TargetDir);
int Index = 0;
while ((Index < FilesToCopy->Count) && !OperationProgress->Cancel)
{
bool Success = false;
UnicodeString FileName = FilesToCopy->Strings[Index];
try
{
try
{
if (SessionData->CacheDirectories)
{
DirectoryModified(TargetDir, false);
if (DirectoryExists(ApiPath(FileName)))
{
UnicodeString FileNameOnly = ExtractFileName(FileName);
DirectoryModified(FullTargetDir + FileNameOnly, true);
}
}
SourceRobust(FileName, FullTargetDir, CopyParam, Params, OperationProgress, Flags | tfFirstLevel);
Success = true;
}
catch (EScpSkipFile & E)
{
TSuspendFileOperationProgress Suspend(OperationProgress);
if (!HandleException(&E))
{
throw;
}
}
}
__finally
{
OperationProgress->Finish(FileName, Success, OnceDoneOperation);
}
Index++;
}
}
//---------------------------------------------------------------------------
void __fastcall TTerminal::SourceRobust(
const UnicodeString & FileName, const UnicodeString & TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags)
{
TUploadSessionAction Action(ActionLog);
bool * AFileTransferAny = FLAGSET(Flags, tfUseFileTransferAny) ? &FFileTransferAny : NULL;
TRobustOperationLoop RobustLoop(this, OperationProgress, AFileTransferAny);
do
{
bool ChildError = false;
try
{
Source(FileName, TargetDir, CopyParam, Params, OperationProgress, Flags, Action, ChildError);
}
catch (Exception & E)
{
if (!RobustLoop.TryReopen(E))
{
if (!ChildError)
{
RollbackAction(Action, OperationProgress, &E);
}
throw;
}
}
if (RobustLoop.ShouldRetry())
{
OperationProgress->RollbackTransfer();
Action.Restart();
// prevent overwrite and resume confirmations (should not be set for directories!)
Params |= cpNoConfirmation;
// enable resume even if we are uploading into new directory
Flags &= ~tfNewDirectory;
Flags |= tfAutoResume;
}
}
while (RobustLoop.Retry());
}
//---------------------------------------------------------------------------
void __fastcall TTerminal::CreateTargetDirectory(
const UnicodeString & DirectoryPath, int Attrs, const TCopyParamType * CopyParam)
{
TRemoteProperties Properties;
if (CopyParam->PreserveRights)
{
Properties.Valid = TValidProperties() << vpRights;
Properties.Rights = CopyParam->RemoteFileRights(Attrs);
}
CreateDirectory(DirectoryPath, &Properties);
}
//---------------------------------------------------------------------------
void __fastcall TTerminal::DirectorySource(
const UnicodeString & DirectoryName, const UnicodeString & TargetDir, const UnicodeString & DestDirectoryName,
int Attrs, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags)
{
FFileSystem->TransferOnDirectory(TargetDir, CopyParam, Params);
UnicodeString DestFullName = UnixIncludeTrailingBackslash(TargetDir + DestDirectoryName);
OperationProgress->SetFile(DirectoryName);
bool PostCreateDir = FLAGCLEAR(Flags, tfPreCreateDir);
// WebDAV and SFTP
if (!PostCreateDir)
{
// This is originally a code for WebDAV, SFTP used a slightly different logic,
// but functionally it should be very similar.
if (!FileExists(DestFullName))
{
CreateTargetDirectory(DestFullName, Attrs, CopyParam);
Flags |= tfNewDirectory;
}
}
if (FLAGCLEAR(Params, cpNoRecurse))
{
int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;
TSearchRecChecked SearchRec;
bool FindOK;
FILE_OPERATION_LOOP_BEGIN
{
FindOK =
(FindFirstChecked(DirectoryName + L"*.*", FindAttrs, SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
try
{
while (FindOK && !OperationProgress->Cancel)
{
UnicodeString FileName = DirectoryName + SearchRec.Name;
try
{
if ((SearchRec.Name != L".") && (SearchRec.Name != L".."))
{
SourceRobust(FileName, DestFullName, CopyParam, Params, OperationProgress, (Flags & ~(tfFirstLevel | tfAutoResume)));
// FTP: if any file got uploaded (i.e. there were any file in the directory and at least one was not skipped),
// do not try to create the directory, as it should be already created by FZAPI during upload
PostCreateDir = false;
}
}
catch (EScpSkipFile &E)
{
// If ESkipFile occurs, just log it and continue with next file
TSuspendFileOperationProgress Suspend(OperationProgress);
// here a message to user was displayed, which was not appropriate
// when user refused to overwrite the file in subdirectory.
// hopefully it won't be missing in other situations.
if (!HandleException(&E))
{
throw;
}
}
FILE_OPERATION_LOOP_BEGIN
{
FindOK = (FindNextChecked(SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
}
}
__finally
{
FindClose(SearchRec);
}
// FTP
if (PostCreateDir)
{
TRemoteFile * File = NULL;
// ignore non-fatal error when the directory already exists
bool DoCreate =
!FileExists(DestFullName, &File) ||
!File->IsDirectory; // just try to create and make it fail
delete File;
if (DoCreate)
{
CreateTargetDirectory(DestFullName, Attrs, CopyParam);
}
}
// TODO : Delete also read-only directories.
// TODO : Show error message on failure.
if (!OperationProgress->Cancel)
{
if (IsCapable[fcPreservingTimestampDirs] && CopyParam->PreserveTime && CopyParam->PreserveTimeDirs)
{
TRemoteProperties Properties;
Properties.Valid << vpModification;
OpenLocalFile(
ExcludeTrailingBackslash(DirectoryName), GENERIC_READ, NULL, NULL, NULL,
&Properties.Modification, &Properties.LastAccess, NULL);
ChangeFileProperties(DestFullName, NULL, &Properties);
}
if (FLAGSET(Params, cpDelete))
{
DebugAssert(FLAGCLEAR(Params, cpNoRecurse));
RemoveDir(ApiPath(DirectoryName));
}
else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
{
FILE_OPERATION_LOOP_BEGIN
{
THROWOSIFFALSE(FileSetAttr(ApiPath(DirectoryName), Attrs & ~faArchive) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (DirectoryName)));
}
}
}
}
//---------------------------------------------------------------------------
void __fastcall TTerminal::SelectSourceTransferMode(const TLocalFileHandle & Handle, const TCopyParamType * CopyParam)
{
// Will we use ASCII or BINARY file transfer?
TFileMasks::TParams MaskParams;
MaskParams.Size = Handle.Size;
MaskParams.Modification = Handle.Modification;
UnicodeString BaseFileName = GetBaseFileName(Handle.FileName);
OperationProgress->SetAsciiTransfer(CopyParam->UseAsciiTransfer(BaseFileName, osLocal, MaskParams));
UnicodeString ModeName = (OperationProgress->AsciiTransfer ? L"Ascii" : L"Binary");
LogEvent(FORMAT(L"%s transfer mode selected.", (ModeName)));
}
//---------------------------------------------------------------------------
void __fastcall TTerminal::UpdateSource(const TLocalFileHandle & Handle, const TCopyParamType * CopyParam, int Params)
{
// TODO: Delete also read-only files.
if (FLAGSET(Params, cpDelete))
{
if (!Handle.Directory)
{
FILE_OPERATION_LOOP_BEGIN
{
THROWOSIFFALSE(Sysutils::DeleteFile(ApiPath(Handle.FileName)));
}
FILE_OPERATION_LOOP_END(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (Handle.FileName)));
}
}
else if (CopyParam->ClearArchive && FLAGSET(Handle.Attrs, faArchive))
{
FILE_OPERATION_LOOP_BEGIN
{
THROWOSIFFALSE(FileSetAttr(ApiPath(Handle.FileName), (Handle.Attrs & ~faArchive)) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (Handle.FileName)));
}
}
//---------------------------------------------------------------------------
void __fastcall TTerminal::Source(
const UnicodeString & FileName, const UnicodeString & TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags, TUploadSessionAction & Action, bool & ChildError)
{
Action.FileName(ExpandUNCFileName(FileName));
OperationProgress->SetFile(FileName, false);
if (!AllowLocalFileTransfer(FileName, CopyParam, OperationProgress))
{
THROW_SKIP_FILE_NULL;
}
TLocalFileHandle Handle;
OpenLocalFile(FileName, GENERIC_READ, Handle);
OperationProgress->SetFileInProgress();
UnicodeString DestFileName =
ChangeFileName(CopyParam, ExtractFileName(FileName), osLocal, FLAGSET(Flags, tfFirstLevel));
if (Handle.Directory)
{
Action.Cancel();
DirectorySource(
IncludeTrailingBackslash(FileName), TargetDir, DestFileName, Handle.Attrs,
CopyParam, Params, OperationProgress, Flags);
}
else
{
LogEvent(FORMAT(L"Copying \"%s\" to remote directory started.", (FileName)));
OperationProgress->SetLocalSize(Handle.Size);
// Suppose same data size to transfer as to read
// (not true with ASCII transfer)
OperationProgress->SetTransferSize(OperationProgress->LocalSize);
if (IsCapable[fcTextMode])
{
SelectSourceTransferMode(Handle, CopyParam);
}
FFileSystem->Source(
Handle, TargetDir, DestFileName, CopyParam, Params, OperationProgress, Flags, Action, ChildError);
LogFileDone(OperationProgress, AbsolutePath(TargetDir + DestFileName, true));
}
Handle.Release();
UpdateSource(Handle, CopyParam, Params);
}
//---------------------------------------------------------------------------
bool __fastcall TTerminal::CopyToLocal(TStrings * FilesToCopy,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
TParallelOperation * ParallelOperation)
@@ -7091,3 +7426,44 @@ void __fastcall TTerminalList::RecryptPasswords()
Terminals[Index]->RecryptPasswords();
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
TLocalFileHandle::TLocalFileHandle()
{
Handle = 0;
Attrs = 0;
MTime = 0;
ATime = 0;
Size = 0;
Directory = false;
}
//---------------------------------------------------------------------------
TLocalFileHandle::~TLocalFileHandle()
{
Release();
}
//---------------------------------------------------------------------------
void TLocalFileHandle::Release()
{
if (Handle != 0)
{
Close();
}
}
//---------------------------------------------------------------------------
void TLocalFileHandle::Dismiss()
{
if (DebugAlwaysTrue(Handle != 0))
{
Handle = 0;
}
}
//---------------------------------------------------------------------------
void TLocalFileHandle::Close()
{
if (DebugAlwaysTrue(Handle != 0))
{
CloseHandle(Handle);
Dismiss();
}
}

View File

@@ -30,6 +30,7 @@ class TTunnelUI;
class TCallbackGuard;
class TParallelOperation;
class TCollectedFileList;
struct TLocalFileHandle;
//---------------------------------------------------------------------------
typedef void __fastcall (__closure *TQueryUserEvent)
(TObject * Sender, const UnicodeString Query, TStrings * MoreMessages, unsigned int Answers,
@@ -121,6 +122,12 @@ const int ropNoReadDirectory = 0x02;
//---------------------------------------------------------------------------
const int boDisableNeverShowAgain = 0x01;
//---------------------------------------------------------------------------
const int tfFirstLevel = 0x01;
const int tfNewDirectory = 0x02;
const int tfAutoResume = 0x04;
const int tfPreCreateDir = 0x08;
const int tfUseFileTransferAny = 0x10;
//---------------------------------------------------------------------------
class TTerminal : public TObject, public TSessionUI
{
public:
@@ -217,6 +224,7 @@ private:
DWORD FLastProgressLogged;
UnicodeString FDestFileName;
bool FMultipleDestinationFiles;
bool FFileTransferAny;
void __fastcall CommandError(Exception * E, const UnicodeString Msg);
unsigned int __fastcall CommandError(Exception * E, const UnicodeString Msg,
@@ -243,6 +251,7 @@ private:
bool __fastcall GetStoredCredentialsTried();
inline bool __fastcall InTransaction();
void __fastcall SaveCapabilities(TFileSystemInfo & FileSystemInfo);
void __fastcall CreateTargetDirectory(const UnicodeString & DirectoryPath, int Attrs, const TCopyParamType * CopyParam);
static UnicodeString __fastcall SynchronizeModeStr(TSynchronizeMode Mode);
static UnicodeString __fastcall SynchronizeParamsStr(int Params);
@@ -296,6 +305,8 @@ protected:
void __fastcall OpenLocalFile(const UnicodeString FileName, unsigned int Access,
int * Attrs, HANDLE * Handle, __int64 * ACTime, __int64 * MTime,
__int64 * ATime, __int64 * Size, bool TryWriteReadOnly = true);
void __fastcall OpenLocalFile(
const UnicodeString & FileName, unsigned int Access, TLocalFileHandle & Handle, bool TryWriteReadOnly = true);
bool __fastcall AllowLocalFileTransfer(UnicodeString FileName,
const TCopyParamType * CopyParam, TFileOperationProgressType * OperationProgress);
bool __fastcall HandleException(Exception * E);
@@ -415,6 +426,21 @@ protected:
bool __fastcall DoOnCustomCommand(const UnicodeString & Command);
bool __fastcall CanParallel(const TCopyParamType * CopyParam, int Params, TParallelOperation * ParallelOperation);
void __fastcall CopyParallel(TParallelOperation * ParallelOperation, TFileOperationProgressType * OperationProgress);
void __fastcall DoCopyToRemote(
TStrings * FilesToCopy, const UnicodeString & TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags, TOnceDoneOperation & OnceDoneOperation);
void __fastcall SourceRobust(
const UnicodeString & FileName, const UnicodeString & TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags);
void __fastcall Source(
const UnicodeString & FileName, const UnicodeString & TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags, TUploadSessionAction & Action, bool & ChildError);
void __fastcall DirectorySource(
const UnicodeString & DirectoryName, const UnicodeString & TargetDir, const UnicodeString & DestDirectoryName,
int Attrs, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags);
void __fastcall SelectSourceTransferMode(const TLocalFileHandle & Handle, const TCopyParamType * CopyParam);
void __fastcall UpdateSource(const TLocalFileHandle & Handle, const TCopyParamType * CopyParam, int Params);
__property TFileOperationProgressType * OperationProgress = { read=FOperationProgress };
@@ -853,4 +879,23 @@ private:
bool CheckEnd(TCollectedFileList * Files);
};
//---------------------------------------------------------------------------
struct TLocalFileHandle
{
TLocalFileHandle();
~TLocalFileHandle();
void Dismiss();
void Close();
void Release();
UnicodeString FileName;
HANDLE Handle;
int Attrs;
bool Directory;
TDateTime Modification;
__int64 MTime;
__int64 ATime;
__int64 Size;
};
//---------------------------------------------------------------------------
#endif

View File

@@ -38,8 +38,6 @@
//---------------------------------------------------------------------------
#define FILE_OPERATION_LOOP_TERMINAL FTerminal
//---------------------------------------------------------------------------
const int tfFirstLevel = 0x01;
//---------------------------------------------------------------------------
struct TSinkFileParams
{
UnicodeString TargetDir;
@@ -1235,267 +1233,138 @@ void __fastcall TWebDAVFileSystem::SpaceAvailable(const UnicodeString Path,
}
//---------------------------------------------------------------------------
void __fastcall TWebDAVFileSystem::CopyToRemote(TStrings * FilesToCopy,
const UnicodeString ATargetDir, const TCopyParamType * CopyParam,
const UnicodeString TargetDir, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress,
TOnceDoneOperation & OnceDoneOperation)
{
DebugAssert((FilesToCopy != NULL) && (OperationProgress != NULL));
Params &= ~cpAppend;
UnicodeString FileName, FileNameOnly;
UnicodeString TargetDir = AbsolutePath(ATargetDir, false);
UnicodeString FullTargetDir = UnixIncludeTrailingBackslash(TargetDir);
intptr_t Index = 0;
while ((Index < FilesToCopy->Count) && !OperationProgress->Cancel)
{
bool Success = false;
FileName = FilesToCopy->Strings[Index];
FileNameOnly = ExtractFileName(FileName, false);
try
{
try
{
if (FTerminal->SessionData->CacheDirectories)
{
FTerminal->DirectoryModified(TargetDir, false);
if (::DirectoryExists(ApiPath(::ExtractFilePath(FileName))))
{
FTerminal->DirectoryModified(FullTargetDir + FileNameOnly, true);
}
}
SourceRobust(FileName, FullTargetDir, CopyParam, Params, OperationProgress,
tfFirstLevel);
Success = true;
}
catch (EScpSkipFile & E)
{
TSuspendFileOperationProgress Suspend(OperationProgress);
if (!FTerminal->HandleException(&E))
{
throw;
}
}
}
__finally
{
OperationProgress->Finish(FileName, Success, OnceDoneOperation);
}
Index++;
}
FTerminal->DoCopyToRemote(FilesToCopy, TargetDir, CopyParam, Params, OperationProgress, tfPreCreateDir, OnceDoneOperation);
}
//---------------------------------------------------------------------------
void __fastcall TWebDAVFileSystem::SourceRobust(const UnicodeString FileName,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags)
void __fastcall TWebDAVFileSystem::Source(
TLocalFileHandle & Handle, const UnicodeString & TargetDir, UnicodeString & DestFileName,
const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int /*Flags*/,
TUploadSessionAction & Action, bool & ChildError)
{
// the same in TSFTPFileSystem
TUploadSessionAction Action(FTerminal->ActionLog);
TRobustOperationLoop RobustLoop(FTerminal, OperationProgress);
do
int FD = -1;
try
{
bool ChildError = false;
UnicodeString DestFullName = TargetDir + DestFileName;
TRemoteFile * RemoteFile = NULL;
try
{
Source(FileName, TargetDir, CopyParam, Params, OperationProgress,
Flags, Action, ChildError);
TValueRestorer<TIgnoreAuthenticationFailure> IgnoreAuthenticationFailureRestorer(FIgnoreAuthenticationFailure);
FIgnoreAuthenticationFailure = iafWaiting;
// this should not throw
CustomReadFileInternal(DestFullName, RemoteFile, NULL);
}
catch (Exception & E)
catch (...)
{
if (!RobustLoop.TryReopen(E))
if (!FTerminal->Active)
{
if (!ChildError)
{
FTerminal->RollbackAction(Action, OperationProgress, &E);
}
throw;
}
}
if (RobustLoop.ShouldRetry())
if (RemoteFile != NULL)
{
OperationProgress->RollbackTransfer();
Action.Restart();
// prevent overwrite confirmations
// (should not be set for directories!)
Params |= cpNoConfirmation;
TOverwriteFileParams FileParams;
FileParams.SourceSize = Handle.Size;
FileParams.SourceTimestamp = Handle.Modification;
FileParams.DestSize = RemoteFile->Size;
FileParams.DestTimestamp = RemoteFile->Modification;
delete RemoteFile;
ConfirmOverwrite(Handle.FileName, DestFileName, OperationProgress,
&FileParams, CopyParam, Params);
}
}
while (RobustLoop.Retry());
}
//---------------------------------------------------------------------------
void __fastcall TWebDAVFileSystem::Source(const UnicodeString FileName,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags,
TUploadSessionAction & Action, bool & ChildError)
{
Action.FileName(ExpandUNCFileName(FileName));
OperationProgress->SetFile(FileName, false);
DestFullName = TargetDir + DestFileName;
// only now, we know the final destination
// (not really true as we do not support changing file name on overwrite dialog)
Action.Destination(DestFullName);
if (!FTerminal->AllowLocalFileTransfer(FileName, CopyParam, OperationProgress))
{
THROW_SKIP_FILE_NULL;
}
HANDLE File;
__int64 MTime;
__int64 Size;
int Attrs;
FTerminal->OpenLocalFile(FileName, GENERIC_READ, &Attrs,
&File, NULL, &MTime, NULL, &Size);
bool Dir = FLAGSET(Attrs, faDirectory);
int FD = -1;
try
{
OperationProgress->SetFileInProgress();
if (Dir)
wchar_t * MimeOut = NULL;
if (FindMimeFromData(NULL, DestFileName.c_str(), NULL, 0, NULL, FMFD_URLASFILENAME, &MimeOut, 0) == S_OK)
{
Action.Cancel();
DirectorySource(IncludeTrailingBackslash(FileName), TargetDir,
Attrs, CopyParam, Params, OperationProgress, Flags);
FUploadMimeType = MimeOut;
CoTaskMemFree(MimeOut);
}
else
{
UnicodeString DestFileName =
FTerminal->ChangeFileName(
CopyParam, ExtractFileName(FileName), osLocal,
FLAGSET(Flags, tfFirstLevel));
FUploadMimeType = L"";
}
FTerminal->LogEvent(FORMAT(L"Copying \"%s\" to remote directory started.", (FileName)));
FILE_OPERATION_LOOP_BEGIN
{
SetFilePointer(Handle.Handle, 0, NULL, FILE_BEGIN);
OperationProgress->SetLocalSize(Size);
FD = _open_osfhandle((intptr_t)Handle.Handle, O_BINARY);
if (FD < 0)
{
THROW_SKIP_FILE_NULL;
}
// Suppose same data size to transfer as to read
// (not true with ASCII transfer)
OperationProgress->SetTransferSize(OperationProgress->LocalSize);
TAutoFlag UploadingFlag(FUploading);
UnicodeString DestFullName = TargetDir + DestFileName;
ClearNeonError();
CheckStatus(ne_put(FNeonSession, PathToNeon(DestFullName), FD));
}
FILE_OPERATION_LOOP_END(FMTLOAD(TRANSFER_ERROR, (Handle.FileName)));
TRemoteFile * RemoteFile = NULL;
if (CopyParam->PreserveTime)
{
FTerminal->LogEvent(FORMAT(L"Preserving timestamp [%s]",
(StandardTimestamp(Handle.Modification))));
TTouchSessionAction TouchAction(FTerminal->ActionLog, DestFullName, Handle.Modification);
try
{
TValueRestorer<TIgnoreAuthenticationFailure> IgnoreAuthenticationFailureRestorer(FIgnoreAuthenticationFailure);
FIgnoreAuthenticationFailure = iafWaiting;
// this should not throw
CustomReadFileInternal(DestFullName, RemoteFile, NULL);
}
catch (...)
{
if (!FTerminal->Active)
TDateTime ModificationUTC = ConvertTimestampToUTC(Handle.Modification);
TFormatSettings FormatSettings = GetEngFormatSettings();
UnicodeString LastModified =
FormatDateTime(L"ddd, d mmm yyyy hh:nn:ss 'GMT'", ModificationUTC, FormatSettings);
UTF8String NeonLastModified(LastModified);
// second element is "NULL-terminating"
ne_proppatch_operation Operations[2];
memset(Operations, 0, sizeof(Operations));
ne_propname LastModifiedProp;
LastModifiedProp.nspace = DAV_PROP_NAMESPACE;
LastModifiedProp.name = PROP_LAST_MODIFIED;
Operations[0].name = &LastModifiedProp;
Operations[0].type = ne_propset;
Operations[0].value = NeonLastModified.c_str();
int Status = ne_proppatch(FNeonSession, PathToNeon(DestFullName), Operations);
if (Status == NE_ERROR)
{
throw;
FTerminal->LogEvent(FORMAT(L"Preserving timestamp failed, ignoring: %s",
(GetNeonError())));
// Ignore errors as major WebDAV servers (like IIS), do not support
// changing getlastmodified.
// The only server we found that supports this is TradeMicro SafeSync.
// But it announces itself as "Server: Apache",
// so it's not reliable to autodetect the support.
// Microsoft Office alegedly uses <Win32LastModifiedTime>
// http://sabre.io/dav/clients/msoffice/
// Carot DAV does that too. But we do not know what server does support this.
TouchAction.Cancel();
}
else
{
CheckStatus(Status);
}
}
TDateTime Modification = UnixToDateTime(MTime, FTerminal->SessionData->DSTMode);
if (RemoteFile != NULL)
catch (Exception & E)
{
TOverwriteFileParams FileParams;
FileParams.SourceSize = Size;
FileParams.SourceTimestamp = Modification;
FileParams.DestSize = RemoteFile->Size;
FileParams.DestTimestamp = RemoteFile->Modification;
delete RemoteFile;
ConfirmOverwrite(FileName, DestFileName, OperationProgress,
&FileParams, CopyParam, Params);
TouchAction.Rollback(&E);
ChildError = true;
throw;
}
DestFullName = TargetDir + DestFileName;
// only now, we know the final destination
// (not really true as we do not support changing file name on overwrite dialog)
Action.Destination(DestFullName);
wchar_t * MimeOut = NULL;
if (FindMimeFromData(NULL, DestFileName.c_str(), NULL, 0, NULL, FMFD_URLASFILENAME, &MimeOut, 0) == S_OK)
{
FUploadMimeType = MimeOut;
CoTaskMemFree(MimeOut);
}
else
{
FUploadMimeType = L"";
}
FILE_OPERATION_LOOP_BEGIN
{
SetFilePointer(File, 0, NULL, FILE_BEGIN);
FD = _open_osfhandle((intptr_t)File, O_BINARY);
if (FD < 0)
{
THROW_SKIP_FILE_NULL;
}
TAutoFlag UploadingFlag(FUploading);
ClearNeonError();
CheckStatus(ne_put(FNeonSession, PathToNeon(DestFullName), FD));
}
FILE_OPERATION_LOOP_END(FMTLOAD(TRANSFER_ERROR, (FileName)));
if (CopyParam->PreserveTime)
{
FTerminal->LogEvent(FORMAT(L"Preserving timestamp [%s]",
(StandardTimestamp(Modification))));
TTouchSessionAction TouchAction(FTerminal->ActionLog, DestFullName, Modification);
try
{
TDateTime ModificationUTC = ConvertTimestampToUTC(Modification);
TFormatSettings FormatSettings = GetEngFormatSettings();
UnicodeString LastModified =
FormatDateTime(L"ddd, d mmm yyyy hh:nn:ss 'GMT'", ModificationUTC, FormatSettings);
UTF8String NeonLastModified(LastModified);
// second element is "NULL-terminating"
ne_proppatch_operation Operations[2];
memset(Operations, 0, sizeof(Operations));
ne_propname LastModifiedProp;
LastModifiedProp.nspace = DAV_PROP_NAMESPACE;
LastModifiedProp.name = PROP_LAST_MODIFIED;
Operations[0].name = &LastModifiedProp;
Operations[0].type = ne_propset;
Operations[0].value = NeonLastModified.c_str();
int Status = ne_proppatch(FNeonSession, PathToNeon(DestFullName), Operations);
if (Status == NE_ERROR)
{
FTerminal->LogEvent(FORMAT(L"Preserving timestamp failed, ignoring: %s",
(GetNeonError())));
// Ignore errors as major WebDAV servers (like IIS), do not support
// changing getlastmodified.
// The only server we found that supports this is TradeMicro SafeSync.
// But it announces itself as "Server: Apache",
// so it's not reliable to autodetect the support.
// Microsoft Office alegedly uses <Win32LastModifiedTime>
// http://sabre.io/dav/clients/msoffice/
// Carot DAV does that too. But we do not know what server does support this.
TouchAction.Cancel();
}
else
{
CheckStatus(Status);
}
}
catch (Exception & E)
{
TouchAction.Rollback(&E);
ChildError = true;
throw;
}
}
FTerminal->LogFileDone(OperationProgress, DestFullName);
}
}
__finally
@@ -1505,125 +1374,7 @@ void __fastcall TWebDAVFileSystem::Source(const UnicodeString FileName,
// _close calls CloseHandle internally (even doc states, we should not call CloseHandle),
// but it crashes code guard
_close(FD);
}
else if (File != NULL)
{
CloseHandle(File);
}
}
// TODO : Delete also read-only files.
if (FLAGSET(Params, cpDelete))
{
if (!Dir)
{
FILE_OPERATION_LOOP_BEGIN
{
THROWOSIFFALSE(::DeleteFile(ApiPath(FileName).c_str()));
}
FILE_OPERATION_LOOP_END(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (FileName)));
}
}
else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
{
FILE_OPERATION_LOOP_BEGIN
{
THROWOSIFFALSE(FileSetAttr(ApiPath(FileName), Attrs & ~faArchive) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (FileName)));
}
}
//---------------------------------------------------------------------------
void __fastcall TWebDAVFileSystem::DirectorySource(const UnicodeString DirectoryName,
const UnicodeString TargetDir, int Attrs, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress, unsigned int Flags)
{
UnicodeString DestDirectoryName =
FTerminal->ChangeFileName(
CopyParam, ExtractFileName(ExcludeTrailingBackslash(DirectoryName)),
osLocal, FLAGSET(Flags, tfFirstLevel));
UnicodeString DestFullName = UnixIncludeTrailingBackslash(TargetDir + DestDirectoryName);
// create DestFullName if it does not exist
if (!FTerminal->FileExists(DestFullName))
{
TRemoteProperties Properties;
if (CopyParam->PreserveRights)
{
Properties.Valid = TValidProperties() << vpRights;
Properties.Rights = CopyParam->RemoteFileRights(Attrs);
}
FTerminal->CreateDirectory(DestFullName, &Properties);
}
OperationProgress->SetFile(DirectoryName);
if (FLAGCLEAR(Params, cpNoRecurse))
{
int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;
TSearchRecChecked SearchRec;
bool FindOK;
FILE_OPERATION_LOOP_BEGIN
{
FindOK =
(FindFirstChecked(DirectoryName + L"*.*", FindAttrs, SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
try
{
while (FindOK && !OperationProgress->Cancel)
{
UnicodeString FileName = DirectoryName + SearchRec.Name;
try
{
if ((SearchRec.Name != L".") && (SearchRec.Name != L".."))
{
SourceRobust(FileName, DestFullName, CopyParam, Params, OperationProgress,
Flags & ~(tfFirstLevel));
}
}
catch (EScpSkipFile & E)
{
// If ESkipFile occurs, just log it and continue with next file
TSuspendFileOperationProgress Suspend(OperationProgress);
// here a message to user was displayed, which was not appropriate
// when user refused to overwrite the file in subdirectory.
// hopefully it won't be missing in other situations.
if (!FTerminal->HandleException(&E))
{
throw;
}
}
FILE_OPERATION_LOOP_BEGIN
{
FindOK = (FindNextChecked(SearchRec) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
}
}
__finally
{
FindClose(SearchRec);
}
// TODO : Delete also read-only directories.
// TODO : Show error message on failure.
if (!OperationProgress->Cancel)
{
if (FLAGSET(Params, cpDelete))
{
RemoveDir(ApiPath(DirectoryName));
}
else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
{
FILE_OPERATION_LOOP_BEGIN
{
THROWOSIFFALSE(FileSetAttr(ApiPath(DirectoryName), Attrs & ~faArchive) == 0);
}
FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (DirectoryName)));
}
Handle.Dismiss();
}
}
}

View File

@@ -97,16 +97,11 @@ protected:
const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags);
void __fastcall SinkFile(const UnicodeString FileName, const TRemoteFile * File, void * Param);
void __fastcall SourceRobust(const UnicodeString FileName,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags);
void __fastcall Source(const UnicodeString FileName,
const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
void __fastcall Source(
TLocalFileHandle & Handle, const UnicodeString & TargetDir, UnicodeString & DestFileName,
const TCopyParamType * CopyParam, int Params,
TFileOperationProgressType * OperationProgress, unsigned int Flags,
TUploadSessionAction & Action, bool & ChildError);
void __fastcall DirectorySource(const UnicodeString DirectoryName,
const UnicodeString TargetDir, int Attrs, const TCopyParamType * CopyParam,
int Params, TFileOperationProgressType * OperationProgress, unsigned int Flags);
void __fastcall ConfirmOverwrite(
const UnicodeString & SourceFullFileName, UnicodeString & DestFileName,
TFileOperationProgressType * OperationProgress,