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:
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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*/,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user