mirror of
https://github.com/winscp/winscp.git
synced 2026-10-24 02:28:55 +03:00
https://winscp.net/tracker/1945
(cherry picked from commit 9370bfd17f)
Source commit: 9602b495c733729c7e1c180cb05001f5d5af4104
536 lines
18 KiB
C++
536 lines
18 KiB
C++
//---------------------------------------------------------------------------
|
|
#include "stdafx.h"
|
|
//---------------------------------------------------------------------------
|
|
#include "FileZillaIntf.h"
|
|
#include "FileZillaIntern.h"
|
|
//---------------------------------------------------------------------------
|
|
#pragma comment(lib, "uafxcw.lib")
|
|
//---------------------------------------------------------------------------
|
|
#pragma package(smart_init)
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall TFileZillaIntf::Initialize()
|
|
{
|
|
// noop
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall TFileZillaIntf::Finalize()
|
|
{
|
|
// noop
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall TFileZillaIntf::SetResourceModule(void * ResourceHandle)
|
|
{
|
|
afxCurrentResourceHandle = (HINSTANCE)ResourceHandle;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
__fastcall TFileZillaIntf::TFileZillaIntf() :
|
|
FFileZillaApi(NULL),
|
|
FIntern(new TFileZillaIntern(this)),
|
|
FServer(new t_server)
|
|
{
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
__fastcall TFileZillaIntf::~TFileZillaIntf()
|
|
{
|
|
DebugAssert(FFileZillaApi == NULL);
|
|
|
|
delete FIntern;
|
|
FIntern = NULL;
|
|
delete FServer;
|
|
FServer = NULL;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::Init()
|
|
{
|
|
DebugAssert(FFileZillaApi == NULL);
|
|
|
|
FFileZillaApi = new CFileZillaApi();
|
|
|
|
bool Result = Check(FFileZillaApi->Init(FIntern, this), L"init");
|
|
|
|
if (!Result)
|
|
{
|
|
delete FFileZillaApi;
|
|
FFileZillaApi = NULL;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall TFileZillaIntf::Destroying()
|
|
{
|
|
// need to close FZAPI before calling destructor as it in turn post messages
|
|
// back while being destroyed, what may result in calling virtual methods
|
|
// of already destroyed descendants
|
|
delete FFileZillaApi;
|
|
FFileZillaApi = NULL;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::SetCurrentPath(const wchar_t * APath)
|
|
{
|
|
DebugAssert(FFileZillaApi != NULL);
|
|
CServerPath Path(APath, false);
|
|
return Check(FFileZillaApi->SetCurrentPath(Path), L"setcurrentpath");
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::GetCurrentPath(wchar_t * Path, size_t MaxLen)
|
|
{
|
|
CServerPath APath;
|
|
bool Result = Check(FFileZillaApi->GetCurrentPath(APath), L"getcurrentpath");
|
|
if (Result)
|
|
{
|
|
wcsncpy(Path, APath.GetPath(), MaxLen);
|
|
Path[MaxLen - 1] = L'\0';
|
|
}
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::Cancel()
|
|
{
|
|
DebugAssert(FFileZillaApi != NULL);
|
|
// tolerate even "idle" state, quite possible in MT environment
|
|
return Check(FFileZillaApi->Cancel(), L"cancel", FZ_REPLY_WOULDBLOCK | FZ_REPLY_IDLE);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::Connect(const wchar_t * Host, int Port, const wchar_t * User,
|
|
const wchar_t * Pass, const wchar_t * Account,
|
|
const wchar_t * Path, int ServerType, int Pasv, int TimeZoneOffset, int UTF8,
|
|
int iForcePasvIp, int iUseMlsd,
|
|
X509 * Certificate, EVP_PKEY * PrivateKey)
|
|
{
|
|
DebugAssert(FFileZillaApi != NULL);
|
|
DebugAssert((ServerType & FZ_SERVERTYPE_HIGHMASK) == FZ_SERVERTYPE_FTP);
|
|
|
|
t_server Server;
|
|
|
|
Server.host = Host;
|
|
Server.port = Port;
|
|
Server.user = User;
|
|
Server.pass = Pass;
|
|
Server.account = Account;
|
|
Server.path = Path;
|
|
Server.nServerType = ServerType;
|
|
Server.nPasv = Pasv;
|
|
Server.nTimeZoneOffset = TimeZoneOffset;
|
|
Server.nUTF8 = UTF8;
|
|
Server.iForcePasvIp = iForcePasvIp;
|
|
Server.iUseMlsd = iUseMlsd;
|
|
Server.Certificate = Certificate;
|
|
Server.PrivateKey = PrivateKey;
|
|
|
|
*FServer = Server;
|
|
|
|
return Check(FFileZillaApi->Connect(Server), L"connect");
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::Close(bool AllowBusy)
|
|
{
|
|
bool Result;
|
|
int ReturnCode = FFileZillaApi->Disconnect();
|
|
|
|
switch (ReturnCode)
|
|
{
|
|
// If the connection terminated itself meanwhile,
|
|
// do not try to wait for close response.
|
|
case FZ_REPLY_NOTCONNECTED:
|
|
// We might check AllowBusy here, as it's actually similar scenario,
|
|
// as we expect this to happen during authentication only
|
|
Result = false;
|
|
break;
|
|
|
|
// waiting for disconnect
|
|
case FZ_REPLY_WOULDBLOCK:
|
|
Result = true;
|
|
break;
|
|
|
|
// allowing busy while opening, not sure if it safe,
|
|
// but we need it, when cancelling password prompt
|
|
case FZ_REPLY_BUSY:
|
|
if (AllowBusy)
|
|
{
|
|
Result = false;
|
|
break;
|
|
}
|
|
|
|
case FZ_REPLY_NOTINITIALIZED:
|
|
default:
|
|
Result = Check(ReturnCode, L"disconnect");
|
|
break;
|
|
}
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::CustomCommand(const wchar_t * Command)
|
|
{
|
|
DebugAssert(FFileZillaApi != NULL);
|
|
return Check(FFileZillaApi->CustomCommand(Command), L"customcommand");
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::MakeDir(const wchar_t* APath)
|
|
{
|
|
DebugAssert(FFileZillaApi != NULL);
|
|
CServerPath Path(APath, false);
|
|
return Check(FFileZillaApi->MakeDir(Path), L"makedir");
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::Chmod(int Value, const wchar_t* FileName,
|
|
const wchar_t* APath)
|
|
{
|
|
DebugAssert(FFileZillaApi != NULL);
|
|
CServerPath Path(APath, false);
|
|
return Check(FFileZillaApi->Chmod(Value, FileName, Path), L"chmod");
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::Delete(const wchar_t* FileName, const wchar_t* APath, bool FileNameOnly)
|
|
{
|
|
DebugAssert(FFileZillaApi != NULL);
|
|
CServerPath Path(APath, false);
|
|
return Check(FFileZillaApi->Delete(FileName, Path, FileNameOnly), L"delete");
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::RemoveDir(const wchar_t* FileName, const wchar_t* APath)
|
|
{
|
|
DebugAssert(FFileZillaApi != NULL);
|
|
CServerPath Path(APath, false);
|
|
return Check(FFileZillaApi->RemoveDir(FileName, Path), L"removedir");
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::Rename(const wchar_t* OldName,
|
|
const wchar_t* NewName, const wchar_t* APath, const wchar_t* ANewPath)
|
|
{
|
|
DebugAssert(FFileZillaApi != NULL);
|
|
CServerPath Path(APath, false);
|
|
CServerPath NewPath(ANewPath, false);
|
|
return Check(FFileZillaApi->Rename(OldName, NewName, Path, NewPath), L"rename");
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::List(const wchar_t * APath)
|
|
{
|
|
DebugAssert(FFileZillaApi != NULL);
|
|
CServerPath Path(APath, false);
|
|
return Check(FFileZillaApi->List(Path), L"list");
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::ListFile(const wchar_t * FileName, const wchar_t * APath)
|
|
{
|
|
DebugAssert(FFileZillaApi != NULL);
|
|
CServerPath Path(APath, false);
|
|
return Check(FFileZillaApi->ListFile(FileName, Path), L"listfile");
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::FileTransfer(
|
|
const wchar_t * LocalFile, const wchar_t * RemoteFile,
|
|
const wchar_t * RemotePath, bool Get, __int64 Size, int Type, void * UserData,
|
|
TTransferOutEvent OnTransferOut, TTransferInEvent OnTransferIn)
|
|
{
|
|
t_transferfile Transfer;
|
|
|
|
Transfer.localfile = LocalFile;
|
|
Transfer.remotefile = RemoteFile;
|
|
Transfer.remotepath = CServerPath(RemotePath, false);
|
|
Transfer.get = Get;
|
|
Transfer.size = Size;
|
|
Transfer.server = *FServer;
|
|
// 1 = ascii, 2 = binary
|
|
Transfer.nType = Type;
|
|
Transfer.nUserData = reinterpret_cast<int>(UserData);
|
|
Transfer.OnTransferOut = OnTransferOut;
|
|
Transfer.OnTransferIn = OnTransferIn;
|
|
|
|
return Check(FFileZillaApi->FileTransfer(Transfer), L"filetransfer");
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall TFileZillaIntf::SetDebugLevel(TLogLevel Level)
|
|
{
|
|
FIntern->SetDebugLevel(Level - LOG_APIERROR + 1);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::PostMessage(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
unsigned int MessageID = FZ_MSG_ID(wParam);
|
|
TMessageType Type;
|
|
switch (MessageID)
|
|
{
|
|
case FZ_MSG_TRANSFERSTATUS:
|
|
Type = MSG_TRANSFERSTATUS;
|
|
break;
|
|
|
|
default:
|
|
Type = MSG_OTHER;
|
|
break;
|
|
}
|
|
return DoPostMessage(Type, wParam, lParam);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall CopyContact(TFtpsCertificateData::TContact & Dest,
|
|
const t_SslCertData::t_Contact& Source)
|
|
{
|
|
Dest.Organization = Source.Organization;
|
|
Dest.Unit = Source.Unit;
|
|
Dest.CommonName = Source.CommonName;
|
|
Dest.Mail = Source.Mail;
|
|
Dest.Country = Source.Country;
|
|
Dest.StateProvince = Source.StateProvince;
|
|
Dest.Town = Source.Town;
|
|
Dest.Other = Source.Other;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall CopyValidityTime(TFtpsCertificateData::TValidityTime & Dest,
|
|
const t_SslCertData::t_validTime& Source)
|
|
{
|
|
Dest.Year = Source.y;
|
|
Dest.Month = Source.M;
|
|
Dest.Day = Source.d;
|
|
Dest.Hour = Source.h;
|
|
Dest.Min = Source.m;
|
|
Dest.Sec = Source.s;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall CopyFileTime(TRemoteFileTime & Dest, const t_directory::t_direntry::t_date & Source)
|
|
{
|
|
Dest.Year = Source.year;
|
|
Dest.Month = Source.month;
|
|
Dest.Day = Source.day;
|
|
Dest.Hour = Source.hour;
|
|
Dest.Minute = Source.minute;
|
|
Dest.Second = Source.second;
|
|
Dest.HasTime = Source.hastime;
|
|
Dest.HasDate = Source.hasdate;
|
|
Dest.HasYear = Source.hasyear;
|
|
Dest.HasSeconds = Source.hasseconds;
|
|
Dest.Utc = Source.utc;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::HandleMessage(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
bool Result;
|
|
|
|
CString a;
|
|
unsigned int MessageID = FZ_MSG_ID(wParam);
|
|
|
|
switch (MessageID)
|
|
{
|
|
case FZ_MSG_STATUS:
|
|
{
|
|
DebugAssert(FZ_MSG_PARAM(wParam) == 0);
|
|
t_ffam_statusmessage * Status = (t_ffam_statusmessage *)lParam;
|
|
DebugAssert(Status->post);
|
|
Result = HandleStatus(Status->status, Status->type);
|
|
delete Status;
|
|
}
|
|
|
|
break;
|
|
|
|
case FZ_MSG_ASYNCREQUEST:
|
|
if (FZ_MSG_PARAM(wParam) == FZ_ASYNCREQUEST_OVERWRITE)
|
|
{
|
|
int RequestResult;
|
|
wchar_t FileName1[MAX_PATH];
|
|
COverwriteRequestData * Data = (COverwriteRequestData *)lParam;
|
|
try
|
|
{
|
|
DebugAssert(Data != NULL);
|
|
wcsncpy(FileName1, Data->FileName1, LENOF(FileName1));
|
|
FileName1[LENOF(FileName1) - 1] = L'\0';
|
|
TRemoteFileTime RemoteTime;
|
|
CopyFileTime(RemoteTime, Data->remotetime);
|
|
Result = HandleAsynchRequestOverwrite(
|
|
FileName1, LENOF(FileName1), Data->FileName2, Data->path1, Data->path2,
|
|
Data->size1, Data->size2,
|
|
(Data->localtime != NULL) ? Data->localtime->GetTime() : 0,
|
|
(Data->localtime != NULL) && ((Data->localtime->GetHour() != 0) || (Data->localtime->GetMinute() != 0)),
|
|
RemoteTime,
|
|
reinterpret_cast<void*>(Data->pTransferFile->nUserData), RequestResult);
|
|
}
|
|
catch(...)
|
|
{
|
|
FFileZillaApi->SetAsyncRequestResult(FILEEXISTS_SKIP, Data);
|
|
throw;
|
|
}
|
|
|
|
if (Result)
|
|
{
|
|
Data->FileName1 = FileName1;
|
|
Result = Check(FFileZillaApi->SetAsyncRequestResult(RequestResult, Data),
|
|
L"setasyncrequestresult");
|
|
}
|
|
}
|
|
else if (FZ_MSG_PARAM(wParam) == FZ_ASYNCREQUEST_VERIFYCERT)
|
|
{
|
|
int RequestResult;
|
|
CVerifyCertRequestData * AData = (CVerifyCertRequestData *)lParam;
|
|
try
|
|
{
|
|
DebugAssert(AData != NULL);
|
|
TFtpsCertificateData Data;
|
|
CopyContact(Data.Subject, AData->pCertData->subject);
|
|
CopyContact(Data.Issuer, AData->pCertData->issuer);
|
|
CopyValidityTime(Data.ValidFrom, AData->pCertData->validFrom);
|
|
CopyValidityTime(Data.ValidUntil, AData->pCertData->validUntil);
|
|
Data.SubjectAltName = AData->pCertData->subjectAltName;
|
|
Data.HashSha1 = AData->pCertData->hashSha1;
|
|
DebugAssert(Data.HashSha1Len == sizeof(AData->pCertData->hashSha1));
|
|
Data.HashSha256 = AData->pCertData->hashSha256;
|
|
DebugAssert(Data.HashSha256Len == sizeof(AData->pCertData->hashSha256));
|
|
Data.Certificate = AData->pCertData->certificate;
|
|
Data.CertificateLen = AData->pCertData->certificateLen;
|
|
Data.VerificationResult = AData->pCertData->verificationResult;
|
|
Data.VerificationDepth = AData->pCertData->verificationDepth;
|
|
|
|
Result = HandleAsynchRequestVerifyCertificate(Data, RequestResult);
|
|
}
|
|
catch(...)
|
|
{
|
|
FFileZillaApi->SetAsyncRequestResult(0, AData);
|
|
throw;
|
|
}
|
|
|
|
if (Result)
|
|
{
|
|
Result = Check(FFileZillaApi->SetAsyncRequestResult(RequestResult, AData),
|
|
L"setasyncrequestresult");
|
|
}
|
|
}
|
|
else if (FZ_MSG_PARAM(wParam) == FZ_ASYNCREQUEST_NEEDPASS)
|
|
{
|
|
int RequestResult = 0;
|
|
CNeedPassRequestData * AData = (CNeedPassRequestData *)lParam;
|
|
try
|
|
{
|
|
TNeedPassRequestData Data;
|
|
Data.Password = AData->Password.GetBuffer(AData->Password.GetLength());
|
|
Result = HandleAsynchRequestNeedPass(Data, RequestResult);
|
|
AData->Password.ReleaseBuffer(AData->Password.GetLength());
|
|
if (Result && (RequestResult == TFileZillaIntf::REPLY_OK))
|
|
{
|
|
AData->Password = Data.Password;
|
|
free(Data.Password);
|
|
Data.Password = NULL;
|
|
}
|
|
}
|
|
catch(...)
|
|
{
|
|
FFileZillaApi->SetAsyncRequestResult(0, AData);
|
|
throw;
|
|
}
|
|
if (Result)
|
|
{
|
|
Result = Check(FFileZillaApi->SetAsyncRequestResult(RequestResult, AData),
|
|
L"setasyncrequestresult");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// FZ_ASYNCREQUEST_GSS_AUTHFAILED
|
|
// FZ_ASYNCREQUEST_GSS_NEEDUSER
|
|
// FZ_ASYNCREQUEST_GSS_NEEDPASS
|
|
DebugFail();
|
|
Result = false;
|
|
}
|
|
break;
|
|
|
|
case FZ_MSG_LISTDATA:
|
|
{
|
|
DebugAssert(FZ_MSG_PARAM(wParam) == 0);
|
|
t_directory * Directory = (t_directory *)lParam;
|
|
CString Path = Directory->path.GetPath();
|
|
std::vector<TListDataEntry> Entries(Directory->num);
|
|
|
|
for (int Index = 0; Index < Directory->num; Index++)
|
|
{
|
|
t_directory::t_direntry & Source = Directory->direntry[Index];
|
|
TListDataEntry & Dest = Entries[Index];
|
|
|
|
Dest.Name = Source.name;
|
|
Dest.Permissions = Source.permissionstr;
|
|
Dest.HumanPerm = Source.humanpermstr;
|
|
Dest.OwnerGroup = Source.ownergroup;
|
|
Dest.Owner = Source.owner;
|
|
Dest.Group = Source.group;
|
|
Dest.Size = Source.size;
|
|
Dest.Dir = Source.dir;
|
|
Dest.Link = Source.bLink;
|
|
CopyFileTime(Dest.Time, Source.date);
|
|
Dest.LinkTarget = Source.linkTarget;
|
|
}
|
|
|
|
int Num = Directory->num;
|
|
TListDataEntry * pEntries = Num > 0 ? &Entries[0] : NULL;
|
|
Result = HandleListData(Path, pEntries, Num);
|
|
|
|
delete Directory;
|
|
}
|
|
break;
|
|
|
|
case FZ_MSG_TRANSFERSTATUS:
|
|
{
|
|
DebugAssert(FZ_MSG_PARAM(wParam) == 0);
|
|
t_ffam_transferstatus * Status = (t_ffam_transferstatus *)lParam;
|
|
if (Status != NULL)
|
|
{
|
|
Result = HandleTransferStatus(
|
|
true, Status->transfersize, Status->bytes, Status->bFileTransfer);
|
|
delete Status;
|
|
}
|
|
else
|
|
{
|
|
Result = HandleTransferStatus(false, -1, -1, false);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FZ_MSG_REPLY:
|
|
Result = HandleReply(FZ_MSG_PARAM(wParam), lParam);
|
|
break;
|
|
|
|
case FZ_MSG_CAPABILITIES:
|
|
Result = HandleCapabilities((TFTPServerCapabilities *)lParam);
|
|
break;
|
|
|
|
default:
|
|
DebugFail();
|
|
Result = false;
|
|
break;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::CheckError(int /*ReturnCode*/, const wchar_t * /*Context*/)
|
|
{
|
|
return false;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
inline bool __fastcall TFileZillaIntf::Check(int ReturnCode,
|
|
const wchar_t * Context, int Expected)
|
|
{
|
|
if ((ReturnCode & (Expected == -1 ? FZ_REPLY_OK : Expected)) == ReturnCode)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return CheckError(ReturnCode, Context);
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::UsingMlsd()
|
|
{
|
|
return FFileZillaApi->UsingMlsd();
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TFileZillaIntf::UsingUtf8()
|
|
{
|
|
return FFileZillaApi->UsingUtf8();
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
std::string __fastcall TFileZillaIntf::GetTlsVersionStr()
|
|
{
|
|
return FFileZillaApi->GetTlsVersionStr();
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
std::string __fastcall TFileZillaIntf::GetCipherName()
|
|
{
|
|
return FFileZillaApi->GetCipherName();
|
|
}
|