home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2001 September
/
Chip_2001-09_cd1.bin
/
zkuste
/
delphi
/
nastroje
/
d5
/
MFTP.ZIP
/
src
/
Ftp.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
2001-06-09
|
99KB
|
3,770 lines
unit Ftp;
{
Monster FTP VCL 0.4.7
written by Chen Yu (monster)
E-Mail: mftp@21cn.com ICQ UIN: 6740755
Homepage: http://homepages.msn.com/RedmondAve/mftp/
Suggestions and bug reports are warm welcomed.
Notes:
* You can use it free because it is terms of the GNU Library
General Public License.
* Original code was based on TStarFTP and TStarSock from Tony BenBrahim's
freenet 2.0.
* Many thanks to Brad Stowers (http://www.delphifreestuff.com/),
Hector Vega Arvide (hvega@cabonet.net.mx), Chris Godwin,
Kaufman Alex (http://pages.infinit.net/kaufman/Index.htm) and other people
who give me a lot of good advise.
* If you modified the code, please send me a copy via email so that
I can improve the component.
* You can modify mftp.inc to make a special version of Monster FTP
other than the default one.
* Some methods like FileSetAttr are not supported by all ftp servers.
}
interface
uses Classes, Windows, Messages, SysUtils, Forms, ExtCtrls,
WinSock, FtpSock, FtpData, FtpCache, FtpMisc, FtpParse, FtpIndex;
{$I mftp.inc}
const
FTP_AVAILABLE = WM_USER + 2;
// FTP_ERROR = WM_USER + 3;
FTP_READY = WM_USER + 4;
type
FtpInfo = (ftpServerConnected, ftpServerDisconnected, ftpResolvingAddress,
ftpTraceIn, ftpTraceOut, ftpAlreadyBusy, ftpSupportResume,
{$ifndef NODEBUG}ftpDebug,{$endif} ftpDataTrace, ftpReady, ftpTransferDone,
ftpDirectoryRefresh, ftpLoggedIn, ftpFileSize,
ftpStartListing, ftpListingParsed, ftpBannerAvailable,
ftpNotSupportResume, ftpRetrying, ftpAddressResolved,
ftpNothing, ftpRetryFinished, ftpSearchStopped,
ftpTransferPutStart, ftpTransferPutFinish,
ftpTransferResume, ftpTransferGetStart, ftpTransferGetFinish);
FtpError = (ftpNone, ftpNetworkDown, ftpInvalidAddress, ftpInternalError,
ftpGeneralWinsockError, ftpConnAborted, ftpConnReset, ftpConnectTimeOut,
ftpOutofSockets, ftpNetworkUnreachable, ftpAddressNotAvailable,
ftpConnectionRefused, ftpProtocolError, ftpCanceled, ftpUnknown,
ftpAddressResolutionError, ftpPrematureDisconnect,
ftpHostUnreachable, ftpNoServer, ftpNoProxyServer,
ftpFileOpen, ftpFileWrite, ftpFileRead, ftpFileNotFound,
ftpTimeOut, ftpServerDown, ftpAccessDenied, ftpDataError,
ftpResumeFailed, ftpPermissionDenied, ftpBadURL,
ftpTransferType, ftpTransferPort, ftpTransferFatalPort, ftpTransferGet, ftpTransferPut,
ftpTransferFatalError, ftpTransferResumeFailed);
TMFtpInfoNeeded = (niAccount, niHost, niLocalFile, niOverwrite,
niPassword, niUser);
TMFtpTransferType = (AsciiTransfer, BinaryTransfer);
TMFtpLastAction = (ftplaNone, ftplaLOGIN, ftplaCWD, ftplaMKD, ftplaMKDS,
ftplaRMD, ftplaRMDS, ftplaRM, ftplaRMS, ftplaGETIndexFile,
ftplaGET, ftplaGETS, ftplaPUT, ftplaPUTS,
ftplaLIST, ftplaREN, ftplaRENS,
ftplaCDUP, ftplaResolveLinks,
ftplaNOOP, ftplaSearch, ftplaTransfer, ftplaTransfers);
TMFtpProxyType = (proxyNone, proxyHost, proxyHostUser, proxyOpen, proxySite,
proxyUserSite);
TMFtpServerType = (ftpstAutoDetect, ftpstDefault,
ftpstUNIX, ftpstULTRIX, ftpstClix, ftpstChameleon,
ftpstNCSA, ftpstQVT, ftpstBSD, ftpstSunOS,
ftpstVmsMultinet, ftpstVmsUcx, ftpstMVS, ftpstVM, ftpstVMVPS,
ftpstMSFTP, ftpstNetTerm, ftpstServU, ftpstWFTPD, ftpWarFTPD,
ftpstNetware, ftpstNetPresenz);
TMFtpInfoEvent = procedure (Sender: TObject; info: FtpInfo; addinfo: String) of object;
TMFtpErrorEvent = procedure (Sender: TObject; error: FtpError; addinfo: String) of object;
TMFtpFileFoundEvent = procedure (Sender: TObject; FileFound: MFtpFileInfo; Location: String; Directory: Boolean) of object;
TMFtpInfoNeededEvent = procedure (Sender: TObject; need: TMFtpInfoNeeded; var Value: String) of object;
TFtpProc = procedure(Line: String) of object;
TMFtpBFParm = record
Depth: Integer;
ScanSymLink: Boolean;
StartDir: String;
WildCard: String;
end;
TMFtp = class(TMSocket)
private
pcount: Integer;
NTimer: TTimer;
{$ifdef USE_RETRYING_TIMER}
RTimer: TTimer;
{$endif}
FBusy: Boolean;
FDBusy: Boolean;
FMBusy: Boolean;
FMFinished: Boolean;
FMAborted: Boolean;
FMultiThreaded: Boolean;
URLMode: Integer;
FUrl: String;
FtpServer: String;
FtpPort: Integer;
FProxyServer: String;
FProxyPort: Integer;
FPassive: Boolean;
FProxyType: TMFtpProxyType;
FSupportResume: Boolean;
FSupportNOOP: Boolean;
FSupportSize: Boolean;
FUser, FPass, FAcct: String;
FPUser, FPPass: String;
FRetries: Integer;
FRetryI: Word;
FRemain: Integer;
FList, TempList, Visited: TStrings;
FBFParm: TMFtpBFParm;
FSP: Integer;
FFiles: TMFtpFileInfoList;
FDirectories: TMFtpFileInfoList;
TFiles: TMFtpFileInfoList;
TDirectories: TMFtpFileInfoList;
FFile: String;
FIFile: String;
FSelection: String;
FDirectory: String;
FCDirectory: String;
FIDirectory: String;
FSDirectory: String;
FFileMask: String;
FCache: Boolean;
FCacheE: Integer;
FFromCache: Boolean;
FType: TMFtpServerType;
FMode: TMFtpTransferType;
Bytes, TrTime, StartTime: Longword;
TotalBytesToSend: Longint;
FStartPoint: Integer;
FtpInfoEvt: TMFtpInfoEvent;
FOnError: TMFtpErrorEvent;
FOnFileFound: TMFtpFileFoundEvent;
FOnReady: TNotifyEvent;
NeedInfo: TMFtpInfoNeededEvent;
NextP, PassiveP: TFtpProc;
Intermediate: Boolean;
Response: String;
PartialLine: String;
DataPartialLine: String;
FLastLine: String;
FBanner: TStrings;
FBannerStore: String;
ControlLoggedIn: Boolean;
ControlConnected: Boolean;
FDoingListing: Boolean;
FSuccess: Boolean;
FTransferSuccess: Boolean;
FAsync: Boolean;
FileOpened: Boolean;
DataConnected: Boolean;
OnHold, TransferAborted: Boolean;
Aborted, BAborted,
{$ifndef USE_RETRYING_TIMER}
SRetry: Boolean;
{$endif}
Rnto: String;
DataFile: TFileStream;
FVersion, DummyS: String;
TransMode: String;
FTransferToFtp: TMFtp;
FTransferFromFtp: TMFtp;
FTransferResume: Boolean;
FSourceName, FTargetName: String;
FTransferCounter, TempInt: Integer;
OprDir: String;
FLoggedIn, FDirectoryChanged,
FDirectoryCreated, FFileRenamed,
FDirectoryDeleted, FFileDeleted,
FIndexFileReceived, FFileReceived,
FFtpQuit, FFtpBusy, FAborted,
FFileStored, FListingDone, FResolved,
FFileTransferred: TNotifyEvent;
FtpLastAction: TMFtpLastAction;
ReadyPort, ReadyMain,
ReadyCWD, ReadyList: Boolean;
InBuffer: Array [0..IN_BUFFER_SIZE] of Char;
OutBuffer: Array [0..OUT_BUFFER_SIZE] of Char;
ListeningSocket,
DataSocket: TMSocket;
FError: FtpError;
errs: String;
CurrentMode: String;
DownloadSize: Integer;
UploadSize: Integer;
NOnFtpError: Array [1..MAX_HANDLERS] of TMFtpErrorEvent;
NOnFtpInfo: Array [1..MAX_HANDLERS] of TMFtpInfoEvent;
NEvents: Array [1..EVENT_COUNT, 1..MAX_HANDLERS] of TNotifyEvent;
function CheckError: Boolean;
function CheckStatus: Boolean;
procedure DataDoListing(sender: TObject);
procedure DataListConnected(sender: TObject);
procedure DataListDisconnected(sender: TObject);
procedure DataRetrFile(sender: TObject);
procedure DataStorFile(sender: TObject); {BDS}
procedure DataFileDisconnected(sender: TObject);
procedure DataStorConnected(sender: TObject);
procedure Proceed(Line: String; P: TFtpProc);
procedure FtpProcess(Line: String);
procedure LoginMain;
procedure UpdateCache;
procedure MyCloseFile;
function MyEOF: Boolean;
procedure NTimerTimer(Sender: TObject);
{$ifdef USE_RETRYING_TIMER}
procedure RTimerTimer(Sender: TObject);
{$endif}
procedure SetInterval(I: Word);
function GetInterval: Word;
function GetStartPoint: Longword;
procedure SetAsync(B: Boolean);
procedure SetRetries(I: Integer);
procedure SetTransferMode(M: String; P: TFtpProc);
procedure UserMessageHandler(var Message: TMessage);
procedure DoFtpInfo(info: FtpInfo; add: String = '');
procedure DoFtpError(e: FtpError);
procedure Ready;
function SetupDataPort: String;
procedure SetupDataPortPassive(S: String);
procedure DoRetry;
procedure DidConnect(Sender: TObject);
procedure DoDisconnect(Sender: TObject);
procedure DoRead(Sender: TObject);
function RecvText: String;
function GetUrl: String;
procedure SetUrl(S: String);
procedure RefreshB;
procedure FatalError(e: FtpError);
procedure TimedOut(Sender: TObject);
procedure fpChmod(Line: String);
procedure fpBuildFileList(Flag: Boolean);
procedure fpCWD(Line: String);
procedure fpCWD2(Line: String);
procedure fpDeleteDirectory(Line: String);
procedure fpDownload(Line: String);
procedure fpDownload2(Line: String);
procedure fpDownload3(Line: String);
procedure fpDownload4a(Line: String);
procedure fpDownload4b(Line: String);
procedure fpDownload5a(Line: String);
procedure fpDownload5b(Line: String);
procedure fpList(Line: String);
procedure fpList2(Line: String);
procedure fpList3(Line: String);
procedure fpLogin(Line: String);
procedure fpLogin2(Line: String);
procedure fpLogin3(Line: String);
procedure fpLogin4(Line: String);
procedure fpLogin5(Line: String);
procedure fpLoginProxyHost(Line: String);
procedure fpLoginProxyOpen(Line: String);
procedure fpLoginProxySite(Line: String);
procedure fpLoginProxySite2(Line: String);
procedure fpLoginProxySite3(Line: String);
procedure fpNOOP(Line: String);
procedure fpPreparePassive(Line: String);
procedure fpProcessGeneral(Line: String);
procedure fpProcessURL(Line: String);
procedure fpProcessURL2(Line: String);
procedure fpProcessURL3(Line: String);
procedure fpQuit(Line: String);
procedure fpRename(Line: String);
procedure fpRename2(Line: String);
procedure fpResolveLinks(Line: String);
procedure fpSetinitialDirectory(Line: String);
procedure fpSetinitialDirectory2(Line: String);
procedure fpTestREST(Line: String);
procedure fpTestSystemType(Line: String);
procedure fpTransfer(Line: String);
procedure fpTransfer2(Line: String);
procedure fpTransfer3(Line: String);
procedure fpTransfer3b(Line: String);
procedure fpTransfer4b(Line: String);
procedure fpTransfer5(Line: String);
procedure fpTransfer6(Line: String);
procedure fpTransfer6b(Line: String);
procedure fpTransfer7b(Line: String);
procedure fpTransfer8(Line: String);
procedure fpTransferFinished;
procedure fpUpload(Line: String);
procedure fpUpload2(Line: String);
procedure fpUpload3(Line: String);
procedure fpUpload4a(Line: String);
procedure fpUpload4b(Line: String);
procedure fpUpload5a(Line: String);
procedure fpUpload5b(Line: String);
protected
procedure LookupNameDone; override;
function ProcessMessage: Boolean;
procedure ProcessMessages;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure BuildFileList(Parameter: TMFtpBFParm);
procedure CreateDirectory(dir: String); overload;
procedure CreateDirectory(dirs: TStrings); overload;
procedure DeleteDirectory(dir: String); overload;
procedure DeleteDirectory(dirs: TStrings); overload;
procedure DeleteFile(filename: String); overload;
procedure DeleteFile(files: TStrings); overload;
procedure GetFile(RemoteFile, LocalFile: String); overload;
procedure GetFile(RemoteFiles, LocalFiles: TStrings); overload;
procedure MoveFile(filename, newdir: String); overload;
procedure MoveFile(files: TStrings; newdir: String); overload;
procedure PutFile(LocalFile, RemoteFile: String); overload;
procedure PutFile(LocalFiles, RemoteFiles: TStrings); overload;
procedure RenameFile(oldname, newname: String); overload;
procedure RenameFile(oldnames, newnames: TStrings); overload;
procedure TransferFile(SourceFile, TargetFile: String; TargetFTP: TMFtp); overload;
procedure TransferFile(SourceFiles, TargetFiles: TStrings; TargetFTP: TMFtp); overload;
procedure IssueCommand(Command: String; OnDone: TFtpProc = nil);
procedure ChangeDirectory(name: String);
procedure ChangeToParentDirectory;
procedure LoadIndexFile(Filename: String);
procedure ResolveLinks;
procedure Login;
procedure Quit;
procedure Refresh;
procedure Abort;
procedure StopTransfer; { internal use, do not call it directly }
procedure CallNEvents(EventType: Integer);
procedure FileSetAttr(filename: String;
OwnerRead, OwnerWrite, OwnerExecute,
GroupRead, GroupWrite, GroupExecute,
PublicRead, PublicWrite, PublicExecute: Boolean);
function RegisterNotifyEvent(EventType: Integer; P: TNotifyEvent): Integer;
function RegisterErrorEvent(P: TMFtpErrorEvent): Integer;
function RegisterInfoEvent(P: TMFtpInfoEvent): Integer;
procedure UnRegisterNotifyEvent(EventType: Integer; i: Integer);
procedure UnRegisterErrorEvent(i: Integer);
procedure UnRegisterInfoEvent(i: Integer);
function FileExists(filename: String): Integer;
function DirectoryExists(dir: String): Integer;
property Listing: TStrings read FList;
property Files: TMFtpFileInfoList read FFiles;
property Directories: TMFtpFileInfoList read FDirectories;
property URL: String read GetUrl write SetUrl;
property BytesTransferred: Longword read Bytes write Bytes;
property StartPoint: Longword read GetStartPoint;
property TransferTime: Longword read TrTime;
property Busy: Boolean read FBusy;
{$warnings off} {avoiding warning here}
property Connected: Boolean read ControlConnected;
{$warnings on}
property DoingListing: Boolean read FDoingListing;
property FromCache: Boolean read FFromCache;
property Success: Boolean read FSuccess;
property SupportResume: Boolean read FSupportResume;
property Error: FtpError read FError;
property CurrentDirectory: String read FDirectory;
property LastReply: String read FLastLine;
property Selection: String read FSelection;
property Banner: TStrings read FBanner;
published
property Asynchronous: Boolean read FAsync write SetAsync;
property MultiThreaded: Boolean read FMultiThreaded write FMultiThreaded;
property NoopInterval: Word read GetInterval write SetInterval;
property Username: String read FUser write FUser;
property Password: String read FPass write FPass;
property Account: String read FAcct write FAcct;
property ProxyUsername: String read FPUser write FPUser;
property ProxyPassword: String read FPPass write FPPass;
property Retries: Integer read FRetries write SetRetries;
property RetryInterval: Word read FRetryI write FRetryI;
property ServerType: TMFtpServerType read FType write FType;
property InitialDirectory: String read FIDirectory write FIDirectory;
property FileMask: String read FFileMask write FFileMask;
property Cache: Boolean read FCache write FCache;
property CacheExpire: Integer read FCacheE write FCacheE;
property TransferMode: TMFtpTransferType read FMode write FMode;
property ProxyType: TMFtpProxyType read FProxyType write FProxyType;
property ProxyServer: String read FProxyServer write FProxyServer;
property ProxyPort: Integer read FProxyPort write FProxyPort;
property Passive: Boolean read FPassive write FPassive;
property OnFtpInfo: TMFtpInfoEvent read FtpInfoEvt write FtpInfoEvt;
property OnFtpNeedInfo: TMFtpInfoNeededEvent read NeedInfo write NeedInfo;
property OnFtpReady: TNotifyEvent read FOnReady write FOnReady;
property OnFtpError: TMFtpErrorEvent read FOnError write FOnError;
property OnLoggedIn: TNotifyEvent read FLoggedIn write FLoggedIn;
property OnDirectoryChanged: TNotifyEvent read FDirectoryChanged write FDirectoryChanged;
property OnDirectoryCreated: TNotifyEvent read FDirectoryCreated write FDirectoryCreated;
property OnDirectoryDeleted: TNotifyEvent read FDirectoryDeleted write FDirectoryDeleted;
property OnFileDeleted: TNotifyEvent read FFileDeleted write FFileDeleted;
property OnFileFound: TMFtpFileFoundEvent read FOnFileFound write FOnFileFound;
property OnIndexFileReceived: TNotifyEvent read FIndexFileReceived write FIndexFileReceived;
property OnFileReceived: TNotifyEvent read FFileReceived write FFileReceived;
property OnFileStored: TNotifyEvent read FFileStored write FFileStored;
property OnListingDone: TNotifyEvent read FListingDone write FListingDone;
property OnFileRenamed: TNotifyEvent read FFileRenamed write FFIleRenamed;
property OnFtpQuit: TNotifyEvent read FFtpQuit write FFtpQuit;
property OnFileTransferred: TNotifyEvent read FFileTransferred write FFileTransferred;
property OnFtpBusy: TNotifyEvent read FFtpBusy write FFtpBusy;
property OnAborted: TNotifyEvent read FAborted write FAborted;
property OnResolvedLinks: TNotifyEvent read FResolved write FResolved;
property Version: String read FVersion write DummyS;
{provided by TMSock}
property Port;
property Server;
property TimeOut;
end;
const
WM_Proceed = WM_USER + 1;
implementation
{$R *.res}
constructor TMFtp.Create;
begin
inherited Create(AOwner);
NTimer := TTimer.Create(Self);
SetInterval(30);
{$ifdef USE_RETRYING_TIMER}
RTimer := TTimer.Create(Self);
{$endif}
FList := TStringList.Create;
FBanner := TStringList.Create;
FDirectories := TMFtpFileInfoList.Create;
FFiles := TMFtpFileInfoList.Create;
TDirectories := TMFtpFileInfoList.Create;
TFiles := TMFtpFileInfoList.Create;
ListeningSocket := TMSocket.Create(Self);
DataSocket := TMSocket.Create(Self);
NTimer.OnTimer := NTimerTimer;
{$ifdef USE_RETRYING_TIMER}
RTimer.OnTimer := RTimerTimer;
{$endif}
CustomMessage := UserMessageHandler;
FVersion := 'Monster FTP 0.4.7';
CurrentMode := '';
URLMode := 0;
FFromCache := False;
FMFinished := True;
FMAborted := False;
BAborted := False;
OnConnected := DidConnect;
OnDisconnected := DoDisconnect;
OnReadReady := DoRead;
OnTimeOut := TimedOut;
{fill in default values}
FUser := 'anonymous';
FPass := 'guest@mftp.org';
Port := 21;
FProxyPort := 21;
FProxyType := proxyNone;
FMode := BinaryTransfer;
FCache := True;
FCacheE := 7;
FAsync := True;
FRetries := 3;
FRetryI := 15;
FMultiThreaded := False;
end;
destructor TMFtp.Destroy;
begin
DataSocket.Destroy;
ListeningSocket.Destroy;
CloseSocket(Socket);
FreeAndNil(FList);
FreeAndNil(FBanner);
FFiles.MyFree;
FDirectories.MyFree;
TFiles.MyFree;
TDirectories.MyFree;
FreeAndNil(NTimer);
{$ifdef USE_RETRYING_TIMER}
FreeAndNil(RTimer);
{$endif}
inherited Destroy;
end;
procedure TMFtp.UserMessageHandler;
begin
case Message.Msg of
FTP_AVAILABLE:
begin
FBusy := False;
NTimer.Enabled := False;
ControlLoggedIn := False;
if Assigned(FFtpQuit) then FFtpQuit(Self);
CallNEvents(8);
if UrlMode = 1 then Login;
end;
// FTP_ERROR: if Assigned(FOnError) then FOnError(Self, FtpError(Message.WParam), 'Error');
FTP_READY:
begin
if FMFinished then
begin
FBusy := False;
NTimer.Enabled := True;
if FMAborted then
begin
FMAborted := False;
if Assigned(FAborted) then FAborted(Self);
CallNEvents(13);
end;
if Assigned(FOnReady) then FOnReady(Self);
CallNEvents(9);
end
else
FMBusy := False;
if TransferAborted then
begin
DataSocket.Disconnect;
ListeningSocket.Disconnect;
TransferAborted := False;
end;
case FtpLastAction of
ftplaLogin:
begin
FtpLastAction := ftplaNone;
ControlLoggedIn := True;
SRetry := True;
if Assigned(FLoggedIn) then FloggedIn(Self);
CallNEvents(11);
end;
ftplaCDUP, ftplaCWD:
begin
FtpLastAction := ftplaNone;
if Assigned(FDirectoryChanged) then FDirectoryChanged(Self);
CallNEvents(1);
end;
ftplaMKD, ftplaMKDS:
begin
if FMFinished then
begin
FtpLastAction := ftplaNone;
if Assigned(FDirectoryCreated) then FDirectoryCreated(Self);
CallNEvents(2);
end;
end;
ftplaRMD, ftplaRMDS:
begin
if FMFinished then
begin
FtpLastAction := ftplaNone;
if Assigned(FDirectoryDeleted) then FDirectoryDeleted(Self);
CallNEvents(3);
end;
end;
ftplaRM, ftplaRMS:
begin
if FMFinished then
begin
FtpLastAction := ftplaNone;
if Assigned(FFileDeleted) then FFileDeleted(Self);
CallNEvents(4);
end;
end;
ftplaGETIndexFile:
begin
FtpLastAction := ftplaNone;
if ParseIndexFile(FIFile, FDirectories, FFiles) then
begin
UpdateCache;
if Assigned(FIndexFileReceived) then FIndexFileReceived(Self);
CallNEvents(14);
end;
end;
ftplaGET, ftplaGETS:
begin
if FMFinished then
begin
FtpLastAction := ftplaNone;
if Assigned(FFileReceived) then FFileReceived(Self);
CallNEvents(5);
end;
end;
ftplaPUT, ftplaPUTS:
begin
if FMFinished then
begin
FtpLastAction := ftplaNone;
if Assigned(FFileStored) then FFileStored(Self);
CallNEvents(7);
end;
end;
ftplaLIST:
begin
FtpLastAction := ftplaNone;
if not FFromCache then
begin
FFiles.Assign(TFiles);
FDirectories.Assign(TDirectories);
UpdateCache;
end;
if Assigned(FListingDone) then FListingDone(Self);
CallNEvents(10);
end;
ftplaREN, ftplaRENS:
begin
if FMFinished then
begin
if Assigned(TempList) then FreeAndNil(TempList); // Clear tempoary file list that used by method MoveFile
FtpLastAction := ftplaNone;
if Assigned(FFileRenamed) then FFileRenamed(Self);
CallNEvents(6);
end;
end;
ftplaResolveLinks:
begin
FtpLastAction := ftplaNone;
UpdateCache;
Proceed('CWD '+ FCDirectory, fpCWD);
if Assigned(FResolved) then FResolved(Self);
CallNEvents(15);
end;
ftplaSearch:
begin
ReadyCWD := False;
Proceed('CWD ' + FSDirectory, fpCWD);
while not ReadyCWD do ProcessMessages;
FtpLastAction := ftplaNone;
FreeAndNil(Visited);
if BAborted then
begin
BAborted := False;
if Assigned(FAborted) then FAborted(Self);
CallNEvents(13);
end;
if not FSuccess then DoFtpInfo(ftpSearchStopped);
end;
ftplaTransfer, ftplaTransfers:
begin
if FMFinished then
begin
FtpLastAction := ftplaNone;
if Assigned(FFileTransferred) then FFileTransferred(Self);
CallNEvents(16);
end;
end;
end;
end;
end;
end;
procedure TMFtp.DoFtpInfo;
var hs: Integer;
S: String;
begin
if add = '' then
begin
case info of
ftpAlreadyBusy:
S := msgBusy;
ftpLoggedIn:
S := msgLogin;
ftpNotSupportResume:
S := msgNResume;
ftpSearchStopped:
S := msgSearchStopped;
ftpServerDisconnected:
S := msgEDisconnected;
end;
end
else
begin
if (info = ftpTraceOut) and (Copy(add, 1, 5) = 'PASS ') then
begin
S := msgIDontTellYou;
end
else
begin
S := add;
end;
end;
if Assigned(FtpInfoEvt) then FtpInfoEvt(Self, info, S);
for hs := 1 to MAX_HANDLERS do
if Assigned(NOnFtpInfo[hs]) then NOnFtpInfo[hs](Self, info, S);
end;
procedure TMFtp.DoFtpError;
var hs: Integer;
begin
FSuccess := False;
FError := e;
// PostMessage(Handle,FTP_ERROR,Ord(e), 0);
if Assigned(FOnError) then FOnError(Self, e, errs);
for hs := 1 to MAX_HANDLERS do
if Assigned(NOnFtpError[hs]) then NOnFtpError[hs](Self, e, errs);
errs := '';
end;
procedure TMFtp.Ready;
begin
NextP := nil;
PostMessage(Handle, FTP_READY, 0, 0);
end;
procedure TMFtp.Login;
begin
FRemain := FRetries;
FBusy := False;
ControlLoggedIn := False;
LoginMain;
end;
procedure TMFtp.LoginMain;
var S: String;
begin
FDBusy := False;
if FBusy then
begin
DoFtpInfo(ftpAlreadyBusy);
Exit;
end;
LastError := 0;
FDirectory := '';
Response := '';
FError := FtpNone;
FSuccess := True;
Intermediate := False;
FSupportSIZE := True;
FSupportNOOP := True;
Dec(FRemain);
ReCreateTCPSocket;
if Trim(FProxyServer) = '' then FProxyType := proxyNone else
if FProxyType <> proxyNone then
begin
FtpServer := Server;
FtpPort := Port;
Server := FProxyServer;
Port := FProxyPort;
end;
if Trim(Server) = '' then
begin
if Assigned(NeedInfo) then NeedInfo(Self, niHost, s);
s := Trim(s);
if s = '' then
begin
DoFtpError(ftpNoServer);
Ready;
Exit;
end;
SetServer(s);
end;
if Port = 0 then Port := 21;
if Address <> '' then FillAddress(Address)
else
begin
DoFtpInfo(ftpResolvingAddress, Host);
LookupName(Host);
CheckError;
Exit;
end;
FillPort(Port);
Connect;
CheckError;
end;
procedure TMFtp.LookupNameDone;
begin
if CheckError then Exit;
FillName;
FillPort(Port);
Address := GetAddressString;
DoFtpInfo(ftpAddressResolved, Address);
Connect;
CheckError;
end;
procedure TMFtp.UpdateCache;
begin
if {(FFileMask = '') and }(FSuccess) then
begin
SaveToCache(GetCacheFilename(Server, FUser, FDirectory, Port, True), FDirectories);
SaveToCache(GetCacheFilename(Server, FUser, FDirectory, Port, False), FFiles);
end;
end;
procedure TMFtp.MyCloseFile;
begin
if FileOpened then
begin
FileOpened := False;
FreeAndNil(DataFile);
end;
end;
function TMFtp.MyEOF;
begin
if (FileOpened) and (Assigned(DataFile)) then
Result := (DataFile.Position = DataFile.Size)
else
Result := True;
end;
{===== Directory/File managemenet routines =====}
procedure TMFtp.BuildFileList;
begin
if CheckStatus then
begin
FtpLastAction := ftplaSearch;
Visited := TStringList.Create;
FBFParm := Parameter;
if FBFParm.Depth <= 0 then FBFParm.Depth := MaxInt;
if (FDirectory <> FBFParm.StartDir) and (FBFParm.StartDir <> '') then
begin
ReadyCWD := False;
Proceed('CWD ' + FBFParm.StartDir, fpCWD);
while not ReadyCWD do ProcessMessages;
if not FSuccess then
begin
Ready;
Exit;
end;
end;
Visited.Add(FDirectory);
if (FDirectory[1] <> '/') and (FDirectory[1] <> '\') then
FSDirectory := '/' + FDirectory
else
FSDirectory := FDirectory;
Dec(FBFParm.Depth);
fpBuildFileList(True);
Inc(FBFParm.Depth);
end;
end;
procedure TMFtp.ChangeDirectory;
begin
if CheckStatus then
begin
FtpLastAction := ftplaCWD;
Proceed('CWD ' + name, fpCWD);
while (FBusy) and (not FAsync) do ProcessMessages;
end;
end;
procedure TMFtp.ChangeToParentDirectory;
begin
if CheckStatus then
begin
FtpLastAction := ftplaCDUP;
Proceed('CDUP', fpCWD);
while (not FBusy) and (FAsync) do ProcessMessages;
end;
end;
procedure TMFtp.CreateDirectory(dir: String);
begin
if CheckStatus then
begin
FTPLastAction := ftplaMKD;
Proceed('MKD ' + dir, fpProcessGeneral);
while (FBusy) and (not FAsync) do ProcessMessages;
end;
end;
procedure TMFtp.CreateDirectory(dirs: TStrings);
var i: Integer;
begin
if Assigned(dirs) and (CheckStatus) then
begin
FTPLastAction := ftplaMKDS;
FMFinished := False;
for i := 0 to dirs.Count - 1 do
begin
FMBusy := True;
Proceed('MKD ' + dirs[i], fpProcessGeneral);
while FMBusy do ProcessMessages;
end;
FMFinished := True;
Ready;
end;
end;
procedure TMFtp.DeleteDirectory(dir: String);
begin
if (dir <> '') and (CheckStatus) then
begin
FTPLastAction := ftplaRMD;
OprDir := Trim(dir);
Proceed('RMD ' + dir, fpDeleteDirectory);
while (FBusy) and (not FAsync) do ProcessMessages;
end;
end;
procedure TMFtp.DeleteDirectory(dirs: TStrings);
var i: Integer;
begin
if Assigned(dirs) and (CheckStatus) then
begin
FTPLastAction := ftplaRMDS;
FMFinished := False;
for i := 0 to dirs.Count - 1 do
begin
FMBusy := True;
OprDir := Trim(dirs[i]);
Proceed('RMD ' + dirs[i], fpDeleteDirectory);
while FMBusy do ProcessMessages;
end;
FMFinished := True;
Ready;
end;
end;
procedure TMFtp.DeleteFile(filename: String);
begin
if (filename <> '') and (CheckStatus) then
begin
FTPLastAction := ftplaRM;
Proceed('DELE ' + filename, fpProcessGeneral);
while (FBusy) and (not FAsync) do ProcessMessages;
end;
end;
procedure TMFtp.DeleteFile(files: TStrings);
var i: Integer;
begin
if Assigned(files) and (CheckStatus) then
begin
FTPLastAction := ftplaRMS;
FMFinished := False;
for i := 0 to files.Count - 1 do
begin
FMBusy := True;
Proceed('DELE ' + files[i], fpProcessGeneral);
while FMBusy do ProcessMessages;
end;
FMFinished := True;
Ready;
end;
end;
procedure TMFtp.MoveFile(filename, newdir: String);
begin
NewDir := Trim(NewDir);
if NewDir[Length(NewDir)] <> '/' then NewDir := NewDir + '/';
RenameFile(FileName, NewDir + ExtractFileName(FileName));
end;
procedure TMFtp.MoveFile(files: TStrings; newdir: String);
var i: Integer;
begin
if not Assigned(files) then Exit;
if files.Count < 1 then Exit;
if not CheckStatus then Exit;
FBusy := False;
if Assigned(TempList) then TempList.Clear else TempList := TStringList.Create;
NewDir := Trim(NewDir);
if NewDir[Length(NewDir)] <> '/' then NewDir := NewDir + '/';
for i := 0 to files.Count - 1 do
TempList.Add(NewDir + ExtractFileName(Files[i]));
RenameFile(files, TempList);
end;
procedure TMFtp.RenameFile(oldname, newname: String);
begin
if (CheckStatus) and (oldname <> '') and (oldname <> newname) then
begin
FTPLastAction := ftplaREN;
Rnto := newname;
Proceed('RNFR ' + oldname, fpRename);
while (FBusy) and (not FAsync) do ProcessMessages;
end;
end;
procedure TMFtp.RenameFile(oldnames, newnames: TStrings);
var i: Integer;
begin
if Assigned(oldnames) and Assigned(newnames) then
begin
if oldnames.Count = newnames.Count then
begin
if not CheckStatus then Exit;
FTPLastAction := ftplaRENS;
FMFinished := False;
for i := 0 to oldnames.Count - 1 do
begin
FMBusy := True;
Rnto := newnames[i];
Proceed('RNFR ' + oldnames[i], fpRename);
while FMBusy do ProcessMessages;
end;
FMFinished := True;
Ready;
end;
end;
end;
procedure TMFtp.ResolveLinks;
begin
if FDirectories.Count = 0 then Exit;
if not CheckStatus then Exit;
FSP := 0;
FCDirectory := FDirectory;
if FCDirectory[1] <> '/' then FCDirectory := '/' + FCDirectory;
if FCDirectory[Length(FCDirectory)] <> '/' then FCDirectory := FCDirectory + '/';
while FSP < FDirectories.Count do
begin
if (FDirectories[FSP].Filename <> '.') and (FDirectories[FSP].Filename <> '..') and
(FDirectories[FSP].SymbolLink <> '.') and
(FDirectories[FSP].SymbolLink <> '..') and
(FDirectories[FSP].SymbolLink <> '') then Break;
Inc(FSP);
end;
if FSP < FDirectories.Count then
begin
FtpLastAction := ftplaResolveLinks;
Proceed('CWD '+ FCDirectory + Directories[0].Filename, fpResolveLinks);
end
else
begin
Ready;
end;
end;
procedure TMFtp.LoadIndexFile;
begin
if (not CheckStatus) or (Trim(Filename) = '') then Exit;
FIFile := GetTempFilename;
FTPLastAction := ftplaGETIndexFile;
GetFile(Filename, FIFile);
end;
procedure TMFtp.GetFile(RemoteFile, LocalFile: String);
var p: TFtpProc;
begin
if (FTPLastAction <> ftplaGETIndexFile) and (FTPLastAction <> ftplaGETS) then
begin
if not CheckStatus then Exit;
FTPLastAction := ftplaGET;
end;
FSelection := RemoteFile;
FFile := LocalFile;
FStartPoint := 0;
if FSelection = '' then
begin
FMBusy := False;
DoFtpError(ftpFileNotFound);
Ready;
Exit;
end;
if FFile <> '' then
begin
try
if SysUtils.FileExists(FFile) then
begin
DataFile := TFileStream.Create(FFile, fmOpenReadWrite + fmShareDenyWrite);
p := fpDownload;
end
else
begin
DataFile := TFileStream.Create(FFile, fmCreate);
PassiveP := fpDownload4b;
p := fpDownload3;
end;
FileOpened := True;
ReadyPort := False;
ReadyMain := False;
if FMode = BinaryTransfer then
SetTransferMode('I', p)
else
SetTransferMode('A', p);
while (FBusy) and (not FAsync) do ProcessMessages;
except
DoFtpError(ftpFileOpen);
Ready;
end;
end
else
begin
FMBusy := False;
DoFtpError(ftpFileOpen);
Ready;
Exit;
end;
end;
procedure TMFtp.GetFile(RemoteFiles, LocalFiles: TStrings);
var i: Integer;
begin
if not (Assigned(RemoteFiles) and Assigned(LocalFiles)) then Exit;
if RemoteFiles.Count <> LocalFiles.Count then Exit;
if not CheckStatus then Exit;
FTPLastAction := ftplaGETS;
FMFinished := False;
for i := 0 to RemoteFiles.Count - 1 do
begin
FMBusy := True;
GetFile(RemoteFiles[i], LocalFiles[i]);
while FMBusy do ProcessMessages;
end;
FMFinished := True;
Ready;
end;
procedure TMFtp.PutFile(LocalFile, RemoteFile: String);
begin
if FTPLastAction <> ftplaPUTS then
begin
if not CheckStatus then Exit;
FTPLastAction := ftplaPUT;
end;
FSelection := RemoteFile;
FFile := LocalFile;
FStartPoint := 0;
if FSelection = '' then
begin
FMBusy := False;
DoFtpError(ftpFileNotFound);
Ready;
Exit;
end;
if FFile <> '' then
begin
try
DataFile := TFileStream.Create(FFile, fmOpenRead);
FileOpened := True;
except
FMBusy := False;
DoFtpError(ftpFileOpen);
Ready;
end;
end
else
begin
FMBusy := False;
DoFtpError(ftpFileOpen);
Ready;
Exit;
end;
if FMode = BinaryTransfer then
SetTransferMode('I', fpUpload)
else
SetTransferMode('A', fpUpload);
while (FBusy) and (not FAsync) do ProcessMessages;
end;
procedure TMFtp.PutFile(LocalFiles, RemoteFiles: TStrings);
var i: Integer;
begin
if not (Assigned(RemoteFiles) and Assigned(LocalFiles)) then Exit;
if RemoteFiles.Count <> LocalFiles.Count then Exit;
if not CheckStatus then Exit;
FTPLastAction := ftplaPUTS;
FMFinished := False;
for i := 0 to RemoteFiles.Count - 1 do
begin
FMBusy := True;
PutFile(LocalFiles[i], RemoteFiles[i]);
while FMBusy do ProcessMessages;
end;
FMFinished := True;
Ready;
end;
procedure TMFtp.TransferFile(SourceFile, TargetFile: String; TargetFTP: TMFtp);
begin
FTransferSuccess := False;
FTransferFromFtp := Self;
FTransferToFtp := TargetFtp;
FSourceName := SourceFile;
FTargetName := TargetFile;
FTransferCounter := 0;
if Assigned(FTransferToFtp) then
begin
if ((FTransferFromFtp.CheckStatus) and (FTransferToFtp.CheckStatus)) or (FTPLastAction = ftplaTransfers) then
begin
if FTPLastAction <> ftplaTransfers then FTPLastAction := ftplaTransfer;
{if CheckSymLink(FSourceName) then
TransMode := 'A'
else
TransMode := 'I';}
TransMode := 'I';
FTransferFromFtp.SetTransferMode(TransMode, fpTransfer);
end
else
DoFtpError(ftpTransferFatalError);
end;
end;
procedure TMFtp.TransferFile(SourceFiles, TargetFiles: TStrings; TargetFTP: TMFtp);
var i: Integer;
begin
if not (Assigned(SourceFiles) and Assigned(TargetFiles)) then Exit;
if SourceFiles.Count <> TargetFiles.Count then Exit;
if not ((TargetFTP.CheckStatus) and (CheckStatus)) then Exit;
FTPLastAction := ftplaTransfers;
FMFinished := False;
for i := 0 to SourceFiles.Count - 1 do
begin
FMBusy := True;
TransferFile(SourceFiles[i], TargetFiles[i], TargetFTP);
while FMBusy do ProcessMessages;
end;
FMFinished := True;
Ready;
end;
procedure TMFtp.IssueCommand;
begin
if Command = '' then
begin
NextP := nil;
Ready;
Exit;
end;
if CheckStatus then
begin
CurrentMode := '';
if Assigned(OnDone) then
Proceed(Command, OnDone)
else
Proceed(Command, fpProcessGeneral);
end;
end;
procedure TMFtp.Refresh;
var R1, R2: Boolean;
begin
FTPLastAction := ftplaLIST;
if FCache then
begin
R1 := LoadFromCache(GetCacheFilename(Server, FUser, FDirectory, Port, True), FDirectories, FCacheE);
R2 := LoadFromCache(GetCacheFilename(Server, FUser, FDirectory, Port, False), FFiles, FCacheE);
if (R1 = True) and (R2 = True) then
begin
FFromCache := True;
Ready;
Exit;
end;
end;
if CheckStatus then
begin
FFromCache := False;
SetTransferMode('A', fpList);
while (FBusy) and (not FAsync) do ProcessMessages;
end;
end;
procedure TMFtp.RefreshB;
begin
ReadyList := False;
SetTransferMode('A', fpList);
while (not ReadyList) and (not BAborted) do ProcessMessages;
FFiles.Assign(TFiles);
FDirectories.Assign(TDirectories);
UpdateCache;
end;
procedure TMFtp.Quit;
begin
if CheckStatus then Proceed('QUIT', fpQuit);
end;
procedure TMFtp.fpRename;
begin
if Line[1] = '3' then
begin
Proceed('Rnto ' + Rnto, fpRename2)
end
else
begin
errs := msgDenied;
DoFtpError(ftpPermissionDenied);
Ready;
end;
end;
procedure TMFtp.fpRename2;
begin
if Line[1] <> '2' then
begin
errs := msgDenied;
DoFtpError(ftpPermissionDenied);
end;
Ready;
end;
procedure TMFtp.fpResolveLinks;
begin
if line[1] <> '2' then
begin
FFiles.Add(FDirectories[FSP].Filename,
FDirectories[FSP].Attrib,
FDirectories[FSP].DateTime,
FDirectories[FSP].Size,
FDirectories[FSP].SymbolLink,
FDirectories[FSP].Owner,
FDirectories[FSP].Group,
FDirectories[FSP].Description);
FDirectories.Delete(FSP);
end
else
begin
Inc(FSP);
end;
while FSP < FDirectories.Count do
begin
if (FDirectories[FSP].Filename <> '.') and (FDirectories[FSP].Filename <> '..') and
(FDirectories[FSP].SymbolLink <> '.') and
(FDirectories[FSP].SymbolLink <> '..') and
(FDirectories[FSP].SymbolLink <> '') then Break;
Inc(FSP);
end;
if FSP < FDirectories.Count then
Proceed('CWD ' + FCDirectory + FDirectories[FSP].Filename, fpResolveLinks)
else
Ready;
end;
procedure TMFtp.SetUrl;
var S1, S2, RT, TUser, TPass, TServer: String;
p, TPort: Integer;
begin
try
if FBusy then
begin
DoFtpInfo(ftpAlreadyBusy);
Exit;
end;
S := PrepareURL(Trim(S)); {preprocessing the url Line}
RT := LowerCase(Copy(S, 1, 6));
if RT <> 'ftp://' then
begin
errs := msgNSProtocol;
DoFtpError(ftpBadURL);
Exit;
end
else
begin
S1 := '';
Delete(S, 1, 6);
p := Pos('@', S);
if p > 0 then
begin
S1 := Copy(S, 1, p - 1);
Delete(S, 1, p);
end;
p := Pos('/', S);
if p = 0 then
S2 := ''
else
begin
S2 := Copy(S, p, 999);
Delete(S, p, 999);
end;
if S1 = '' then
begin
if FUser = '' then FUser := 'anonymous';
if FPass = '' then FPass := 'guest@somewhere.on.earth';
TUser := FUser;
TPass := FPass;
end
else
begin
p := Pos(':', S1);
if p = 0 then
begin
errs := msgISytax;
DoFtpError(ftpBadURL);
FBusy := False;
Exit;
end;
TUser := Copy(S1, 1, p - 1);
TPass := Copy(S1, p + 1, 999);
end;
p := Pos(':', S);
if p = 0 then
begin
TServer := S;
TPort := FPort; { useless, just make compiler happy }
end
else
begin
TServer := Copy(S, 1, p - 1);
TPort := StrToInt(Copy(S, p + 1, 999));
end;
end;
FUrl := S2;
FBusy := True;
if Assigned(FFtpBusy) then FFtpBusy(Self);
CallNEvents(12);
if (FUser = TUser) and (FPass = TPass) and (FServer = TServer)
and (FPort = TPort) and (ControlLoggedIn) then
begin
fpProcessURL('299');
Exit;
end;
FUser := TUser;
FPass := TPass;
Server := TServer;
FPort := TPort;
if not ControlConnected then
begin
URLMode := 2;
Login;
end
else
begin
URLMode := 1;
Proceed('QUIT', fpQuit);
end;
except
errs := msgUEParse;
DoFtpError(ftpBadURL);
end;
end;
function TMFtp.GetUrl;
var i: Integer;
begin
{reformats the url, extra information(username, password etc.) is excluded}
if FPort = 21 then
Result := 'ftp://' + Server + '/'
else
Result := 'ftp://' + Server + ':' + IntToStr(Port) + '/';
if FDirectory = '' then Exit;
if FDirectory = '/' then Exit;
if FDirectory[1] = '/' then Delete(FDirectory, 1, 1);
Result := Result + FDirectory;
i := Length(Result);
if Result[i] <> '/' then Result := Result + '/';
end;
procedure TMFtp.Proceed;
var data: String;
begin
data := Line + #13#10;
DoFtpInfo(ftpTraceOut, data);
while data <> '' do
begin
ProcessMessages;
Delete(data, 1, SendBuf(PChar(@data[1]), Length(data)));
if (CheckError) or (Aborted) or (not Connected) then
begin
ReadyCWD := True;
Exit;
end;
end;
NextP := P;
end;
procedure TMFtp.DidConnect;
begin
if CheckError then Exit;
DoFtpInfo(ftpServerConnected, Address);
ControlConnected := True;
OnHold := False;
PartialLine := '';
NextP := fpLogin;
end;
procedure TMFtp.TimedOut;
begin
errs := 'Connection timed out';
FatalError(ftpConnectTimeOut);
end;
procedure TMFtp.DoRetry;
{$ifndef USE_RETRYING_TIMER}
var ST: Longword;
{$endif}
begin
if FRemain < 0 then
begin
DoFTPInfo(ftpRetryFinished, 'Retry Finished');
SRetry := True;
Exit;
end;
{$ifdef USE_RETRYING_TIMER}
if (not ControlLoggedIn) and (FRemain >= 0) and (FError <> ftpNone) then
begin
// DoFTPInfo(ftpRetrying, msgRetry + IntToStr(FRetryI) + ' Seconds..'); {mEga}
DoFTPInfo(ftpRetrying, msgRetry);
FDBusy := True;
if FRetryI > 0 then
begin
RTimer.Interval := FRetryI * 1000;
RTimer.Enabled := True;
end;
end;
{$else}
if (not ControlLoggedIn) and (FRemain >= 0) and (FError <> ftpNone) then
begin
// DoFTPInfo(ftpRetrying, msgRetry + IntToStr(FRetryI) + ' Seconds..'); {mEga}
DoFTPInfo(ftpRetrying, msgRetry);
FDBusy := True;
SRetry := False;
if FRetryI > 0 then
begin
ST := GetTickCount;
while GetTickCount - ST < FRetryI * 1000 do
begin
if SRetry then
Break
else
ProcessMessages;
end;
end;
if not SRetry then
begin
LoginMain;
Exit;
end;
end;
{$endif}
end;
procedure TMFtp.DoDisconnect;
procedure RealDoDisconnect;
begin
ControlConnected := False;
DoFTPInfo(ftpServerDisconnected, msgDisconnected);
PostMessage(Handle, FTP_AVAILABLE, 0, 0);
FtpLastAction := ftplaNone;
ReadyList := True;
Ready;
end;
begin
if FDBusy then Exit;
DoRetry;
if SRetry then RealDoDisconnect;
end;
procedure TMFtp.DoRead;
var
Linein, newLine: String;
el: Integer;
begin
if OnHold then Exit;
if Aborted then Exit;
while True do
begin
if Aborted then Exit;
Linein := RecvText;
if CheckError then Exit;
DoFtpInfo(ftpTraceIn, Linein);
if Length(Linein) = 0 then Exit;
Linein := PartialLine + Linein;
repeat
// fix for ftp.es.tripod.de, reported by Alfonso Martinez de Lizarrondo
{$ifndef NOPATCH}
el := Pos(#10, Linein);
if el <> 0 then
begin
if ((el>1) and (Linein[el-1] = #13)) then
newLine := Copy(Linein, 1, el - 2)
else
newLine := Copy(Linein, 1, el - 1);
Delete(Linein, 1, el);
{$else}
el := Pos(#13 + #10, Linein);
if el <> 0 then
begin
newLine := Copy(Linein, 1, el - 1);
Delete(Linein, 1, el + 1);
{$endif}
OnHold := True;
FtpProcess(newLine);
OnHold := False;
if Aborted then Exit;
end;
until el = 0;
PartialLine := Linein;
end;
end;
procedure TMFtp.FatalError;
begin
if Aborted then Exit;
FError := e;
DoFtpError(e);
if DataSocket.IsConnected then DataSocket.Disconnect;
if ListeningSocket.IsConnected then ListeningSocket.Disconnect;
if IsConnected then Disconnect;
end;
procedure TMFtp.fpBuildFileList;
var D: TStringList;
L, CurrDir: String;
i: Integer;
begin
D := TStringList.Create;
try
RefreshB;
if not FSuccess then Ready;
if Flag or (Visited.IndexOf(FDirectory) < 0) then
begin
if not Flag then Visited.Add(FDirectory);
L := GetURL;
if FDirectory <> '' then
begin
if (FDirectory[1] <> '/') and (FDirectory[1] <> '\') then
CurrDir := '/' + FDirectory
else
CurrDir := FDirectory;
end;
for i := 0 to Directories.Count - 1 do
begin
if (FBFParm.ScanSymLink) or ((not FBFParm.ScanSymLink) and (Directories[i].SymbolLink = '')) then
D.Add(Directories[i].Filename);
if Assigned(FOnFileFound) then FOnFileFound(Self, Directories[i], L, True);
end;
for i := 0 to Files.Count - 1 do
if Assigned(FOnFileFound) then FOnFileFound(Self, Files[i], L, False);
if FBFParm.Depth > 0 then
begin
for i := 0 to D.Count - 1 do
begin
ReadyCWD := False;
Proceed('CWD ' + CurrDir + '/' + D[i], fpCWD);
while not ReadyCWD do ProcessMessages;
if FDirectory <> '' then
begin
Dec(FBFParm.Depth);
fpBuildFileList(False);
Inc(FBFParm.Depth);
end;
end;
end;
end;
if Flag then Ready;
finally
FreeAndNil(D);
end;
end;
procedure TMFtp.fpChmod;
begin
if Line[1] = '5' then
begin
if Line[1] = '0' then
DoFtpError(ftpProtocolError)
else
DoFtpError(ftpAccessDenied);
Ready;
end;
end;
procedure TMFtp.fpCWD;
var i: Integer;
begin
if Line[1] <> '2' then
begin
FDirectory := '';
errs := msgDenied;
DoFtpError(ftpPermissionDenied);
ReadyCWD := True;
Ready;
end
else
begin
{setting new CurrentDirectory property}
i := Pos('"', Line);
if i > 0 then
begin
FDirectory := Copy(Line, i + 1, 999);
FDirectory := Copy(FDirectory, 1, Pos('"', FDirectory) - 1);
ReadyCWD := True;
Ready;
end
else
Proceed('PWD', fpCWD2);
end;
end;
procedure TMFtp.fpCWD2;
begin
if Line[1] <> '2' then
begin
DoFtpError(ftpProtocolError)
end
else
begin
{setting new CurrentDirectory property}
FDirectory := Copy(Line, Pos('"', Line) + 1, 999);
FDirectory := Copy(FDirectory, 1, Pos('"', FDirectory) - 1);
if (FType = ftpstNetTerm) or (FType = ftpstServU) then
FDirectory := DOSName2UnixName(FDirectory);
end;
ReadyCWD := True;
Ready;
end;
procedure TMFtp.fpDeleteDirectory;
var S: String;
begin
if Line[1] <> '2' then
begin
errs := msgDenied;
DoFtpError(ftpPermissionDenied);
end
else
begin
// find out the name of deleted directory
if Oprdir[1] = '/' then
S := Oprdir
else
if FDirectory[Length(FDirectory)] = '/' then
S := FDirectory + Oprdir
else
S := FDirectory + '/' + Oprdir;
// delete cache files
Sysutils.DeleteFile(GetCacheFilename(Server, FUser, S, Port, True));
Sysutils.DeleteFile(GetCacheFilename(Server, FUser, S, Port, False));
end;
Ready;
end;
procedure TMFtp.fpDownload;
var s: String;
begin
DownloadSize := 0;
if Assigned(NeedInfo) then NeedInfo(self, niOverwrite, s);
if s = 'Resume' then
begin
if FSupportResume then
begin
if FSupportSIZE then
begin
Proceed('SIZE ' + FSelection, fpDownload2);
end
else
begin
fpDownload2('500');
end;
end
else
begin
DoFtpInfo(ftpNotSupportResume);
MyCloseFile;
FTPLastAction := ftplaNone;
Ready;
end;
end
else
begin
if (s = 'Overwrite') or (s = '') then
begin
PassiveP := fpDownload4b;
fpDownload3('299')
end
else
begin
MyCloseFile;
FTPLastAction := ftplaNone;
Ready;
end;
end;
end;
procedure TMFtp.fpDownload2;
begin
if Line[1] <> '2' then
begin
if (Line[1] = '5') and (Line[2] = '0') then
begin
FSupportSIZE := False;
Line := '213 '+ IntToStr(DataFile.Size);
fpDownload2(Line);
end
else
begin
PassiveP := fpDownload4b;
fpDownload3('299');
end;
end
else
begin
DownloadSize := StrToIntDef(Copy(Line, 5, 999), 0);
if (DownloadSize < DataFile.Size) or (DataFile.Size = 0) then
begin
DoFtpInfo(ftpNotSupportResume, msgAOverwrite);
PassiveP := fpDownload4b;
fpDownload3('299');
end
else
begin
if DownloadSize = DataFile.Size then
begin
DoFtpInfo(ftpNothing, msgNothing);
MyCloseFile;
FTPLastAction := ftplaNone;
Ready;
end
else
begin
DataFile.Seek(0, soFromEnd);
PassiveP := fpDownload4a;
fpDownload3('299');
end;
end;
end;
end;
procedure TMFtp.fpDownload3;
begin
if Line[1] = '2' then
begin
if FPassive then
Proceed('PASV', fpPreparePassive)
else
Proceed('PORT ' + SetupDataPort, PassiveP);
end
else
begin
DoFtpError(ftpProtocolError);
Ready;
end;
end;
procedure TMFtp.fpDownload4a;
begin
if Line[1] <> '2' then
begin
DoFtpError(ftpProtocolError);
Ready;
end
else
begin
with DataSocket do
begin
OnReadReady := DataRetrFile;
OnDisconnected := DataFileDisconnected;
OnWriteReady := nil;
if FPassive then
OnConnected := DataListConnected
else
ListeningSocket.OnAccept := DataListConnected;
end;
FStartPoint := DataFile.Size;
Proceed('REST ' + IntToStr(FStartPoint), fpDownload5a);
end;
end;
procedure TMFtp.fpDownload4b;
begin
if Line[1] <> '2' then
begin
DoFtpError(ftpProtocolError);
Ready;
end
else
begin
with DataSocket do
begin
OnReadReady := DataRetrFile;
OnDisconnected := DataFileDisconnected;
OnWriteReady := nil;
if FPassive then
OnConnected := DataListConnected
else
ListeningSocket.OnAccept := DataListConnected;
end;
Proceed('RETR ' + FSelection, fpDownload5b);
end;
end;
procedure TMFtp.fpDownload5a;
begin
if Line[1] = '3' then
Proceed('RETR ' + FSelection, fpDownload5b)
else
begin
DataSocket.Disconnect;
ListeningSocket.Disconnect;
errs := msgFResumeD;
DoFtpError(ftpResumeFailed);
Ready;
end;
end;
procedure TMFtp.fpDownload5b;
var i, j: Integer;
begin
case Line[1] of
'1':
begin
i := Pos('(', Line);
if i > 0 then
begin
while i > 0 do
begin
Delete(Line, 1, i);
i := Pos('(', Line);
end;
for j := 1 to Length(Line) do
begin
if Line[j] = ' ' then
begin
DoFtpInfo(ftpFileSize, Copy(Line, i + 1, j - i - 1));
Exit;
end;
end;
end;
Exit;
end;
'2':
begin
ReadyMain := True;
if ReadyPort then Ready;
// NextP := nil;
end
else
begin
DataSocket.Disconnect; {close data connection}
ListeningSocket.Disconnect;
if (Aborted) and (Copy(Line, 1, 3) = '426') then
begin
if Assigned(FAborted) then FAborted(Self);
CallNEvents(13);
end
else
begin
errs := msgDenied;
DoFtpError(ftpPermissionDenied);
end;
Ready;
end;
end;
end;
procedure TMFtp.fpList;
begin
ReadyMain := False;
ReadyPort := False;
if Line[1] = '2' then
begin
if FPassive then
begin
PassiveP := fpList2;
Proceed('PASV', fpPreparePassive)
end
else
Proceed('PORT ' + SetupDataPort, fpList2)
end
else
begin
DoFtpError(ftpProtocolError);
if ftpLastAction = ftplaSearch then ReadyList := True else Ready;
end;
end;
procedure TMFtp.fpList2;
begin
if Line[1] <> '2' then
begin
DoFtpError(ftpProtocolError);
if ftpLastAction = ftplaSearch then ReadyList := True else Ready;
end
else
begin
with DataSocket do
begin
OnReadReady := DataDoListing;
OnDisconnected := DataListDisconnected;
OnWriteReady := nil;
if FPassive then
OnConnected := DataListConnected
else
ListeningSocket.OnAccept := DataListConnected;
end;
FList.Clear;
TFiles.Clear;
TDirectories.Clear;
pcount := 0;
if FFileMask <> '' then
Proceed('LIST ' + FFileMask, fpList3)
else
Proceed('LIST', fpList3);
DoFtpInfo(ftpStartListing);
end;
end;
procedure TMFtp.fpList3;
begin
case Line[1] of
'1': Exit;
'2':
begin
ReadyMain := True;
if ReadyPort then
if ftpLastAction = ftplaSearch then ReadyList := True else Ready;
end
else
begin
DataSocket.Disconnect; {close data connection}
ListeningSocket.Disconnect;
if (Aborted) and (Copy(Line, 1, 3) = '426') then
begin
if Assigned(FAborted) then FAborted(Self);
CallNEvents(13);
end
else
begin
errs := msgDenied;
DoFtpError(ftpPermissionDenied);
end;
if ftpLastAction = ftplaSearch then ReadyList := True else Ready;
end;
end;
end;
procedure TMFtp.fpLogin;
begin
if URLMode = 0 then FtpLastAction := ftplaLOGIN else UrlMode := 2;
if Line[1] <> '2' then
FatalError(ftpServerDown)
else
begin
if (FUser = '') and Assigned(NeedInfo) then NeedInfo(Self, niUser, FUser);
if FProxyType <> proxyNone then
begin
Server := FtpServer;
Port := FtpPort;
end
else
begin
if FType = ftpstAutoDetect then
begin
if Pos('NetTerm FTP server', Line) <> 0 then FType := ftpstNetTerm;
end;
end;
case FProxyType of
proxyHost: Proceed('HOST ' + Server, fpLoginProxyHost);
proxyNone: Proceed('USER ' + FUser, fpLogin2);
proxyOpen: Proceed('OPEN ' + Server, fpLoginProxyOpen);
proxySite: Proceed('USER ' + FPUser, fpLoginProxySite);
proxyHostUser:
begin
if Port = 21 then
Proceed('USER ' + Server + '!' + FUser, fpLogin2)
else
Proceed('USER ' + Server + ':' + IntToStr(Port) + '!' + FUser, fpLogin2);
end;
proxyUserSite:
begin
if Port = 21 then
Proceed('USER ' + FUser + '@' + Server, fpLogin2)
else
Proceed('USER ' + FUser + '@' + Server + ':' + IntToStr(Port), fpLogin2);
end;
end;
end;
end;
procedure TMFtp.fpLogin2;
begin
if Line[1] = '2' then
begin
if FType = ftpstAutoDetect then
Proceed('SYST', fpTestSystemType)
else
Proceed('REST 100', fpTestREST);
end
else
begin
if Line[1] <> '3' then
begin
FatalError(ftpAccessDenied);
end
else
begin
if (FPass = '') and Assigned(NeedInfo) then NeedInfo(Self, niPassword, FPass);
Proceed('PASS ' + FPass, fpLogin3);
end;
end;
end;
procedure TMFtp.fpLogin3;
begin
if Line[1] = '2' then
begin
if FType = ftpstAutoDetect then
Proceed('SYST', fpTestSystemType)
else
Proceed('REST 100', fpTestREST);
end
else
begin
if Line[1] <> '3' then
begin
FatalError(ftpAccessDenied);
end
else
begin
if (FAcct = '') and Assigned(NeedInfo) then NeedInfo(Self, niAccount, FAcct);
Proceed('ACCT ' + FAcct, fpLogin4);
end;
end;
end;
procedure TMFtp.fpLogin4;
begin
if Line[1] = '2' then
begin
if FType = ftpstAutoDetect then
Proceed('SYST', fpTestSystemType)
else
Proceed('REST 100', fpTestREST);
end
else
begin
FatalError(ftpAccessDenied);
end;
end;
procedure TMFtp.fpLogin5;
begin
if FDirectory = '' then
begin
if Line[1] = '2' then
begin
FDirectory := Copy(Line, Pos('"', Line) + 1, 999);
FDirectory := Copy(FDirectory, 1, Pos('"', FDirectory) - 1);
if (FType = ftpstNetTerm) or (FType = ftpstServU) then
FDirectory := DOSName2UnixName(FDirectory);
end
else
FatalError(ftpProtocolError);
end
else
begin
if Line[1] <> '2' then
begin
errs := msgDenied;
DoFtpError(ftpPermissionDenied);
end;
end;
DoFtpInfo(ftpLoggedIn);
Ready;
end;
procedure TMFtp.fpLoginProxyHost(Line: String);
begin
if Line[1] <> '3' then FatalError(ftpServerDown) else Proceed('USER ' + FUser, fpLogin2);
end;
procedure TMFtp.fpLoginProxyOpen(Line: String);
begin
if Line[1] <> '2' then FatalError(ftpServerDown) else Proceed('USER ' + FUser, fpLogin2);
end;
procedure TMFtp.fpLoginProxySite(Line: String);
begin
if (FPUser = '') and Assigned(NeedInfo) then NeedInfo(Self, niUser, FPUser);
case Line[1] of
'2': Proceed('SITE ' + FServer, fpLoginProxySite3);
'3':
begin
if (FPPass = '') and Assigned(NeedInfo) then NeedInfo(Self, niPassword, FPPass);
Proceed('PASS ' + FPPass, fpLoginProxySite2);
end;
else FatalError(ftpAccessDenied);
end;
end;
procedure TMFtp.fpLoginProxySite2(Line: String);
begin
if Line[1] = '2' then Proceed('SITE ' + FServer, fpLoginProxySite3) else FatalError(ftpAccessDenied);
end;
procedure TMFtp.fpLoginProxySite3(Line: String);
begin
if Line[1] <> '2' then FatalError(ftpProtocolError) else Proceed('USER ' + FUser, fpLogin2);
end;
procedure TMFtp.fpNOOP;
begin
if (Line[1] = '5') and (Line[2] = '0') then FSupportNOOP := False;
FBusy := False;
end;
procedure TMFtp.fpPreparePassive;
begin
if (Line[1] = '2') and Assigned(PassiveP) then
begin
SetupDataPortPassive(Line);
PassiveP('299');
end
else
begin
DoFtpError(ftpProtocolError);
Ready;
end;
end;
procedure TMFtp.fpProcessGeneral;
begin
if Line[1] <> '2' then
begin
errs := msgDenied;
DoFtpError(ftpPermissionDenied);
end;
Ready;
end;
procedure TMFtp.fpProcessURL;
begin
UrlMode := 0;
if FUrl = '' then
Proceed('PWD', fpProcessURL2)
else
Proceed('CWD ' + FUrl, fpProcessURL2);
end;
procedure TMFtp.fpProcessURL2;
var i, j: Integer;
begin
if Line[1] = '2' then
begin
FtpLastAction := ftplaCWD;
FDirectory := Copy(Line, Pos('"', Line) + 1, 999);
FDirectory := Copy(FDirectory, 1, Pos('"', FDirectory) - 1);
if not ControlLoggedIn then
begin
ControlLoggedIn := True;
SRetry := True;
DoFtpInfo(ftpLoggedIn);
end;
fpCWD(Line);
end
else
begin
i := Length(FUrl);
if FUrl[i] = '/' then
begin
FtpLastAction := ftplaCWD;
errs := msgDenied;
DoFtpError(ftpPermissionDenied);
Ready;
end
else
begin
for j := i downto 1 do
begin
if FUrl[j] = '/' then
begin
FSelection := Copy(FUrl, j + 1, 999);
Delete(FUrl, j + 1, 999);
Break;
end;
end;
Proceed('CWD ' + FUrl, fpProcessURL3);
end;
end;
end;
procedure TMFtp.fpProcessURL3;
begin
if Line[1] = '2' then
begin
if not ControlLoggedIn then
begin
ControlLoggedIn := True;
SRetry := True;
DoFtpInfo(ftpLoggedIn);
end;
if FSelection <> '' then
begin
if Assigned(NeedInfo) then
begin
FFile := FSelection;
NeedInfo(Self, niLocalFile, FFile);
if FFile = '' then
begin
Disconnect;
Exit;
end;
end
else
begin
Disconnect;
Exit;
end;
FBusy := False;
GetFile(FSelection, FFile);
end;
end
else
begin
// FRemain := -1;
FatalError(ftpFileNotFound);
end;
end;
procedure TMFtp.fpQuit;
begin
ControlLoggedIn := False;
if Line[1] <> '2' then FatalError(ftpNone);
// NextP := nil;
end;
procedure TMFtp.fpSetinitialDirectory;
begin
if URLMode > 0 then
begin
fpProcessURL('299');
Exit;
end;
if FIDirectory = '' then
Proceed('PWD', fpLogin5)
else
Proceed('CWD ' + FIDirectory, fpSetinitialDirectory2);
end;
procedure TMFtp.fpSetinitialDirectory2;
begin
if Line[1] = '2' then
begin
FDirectory := Copy(Line, Pos('"', Line) + 1, 999);
FDirectory := Copy(FDirectory, 1, Pos('"', FDirectory) - 1);
end;
if FDirectory <> '' then
begin
DoFtpInfo(ftpLoggedIn);
Ready;
end
else
Proceed('PWD', fpLogin5);
end;
procedure TMFtp.fpTestREST;
begin
if Line[1] = '3' then
begin
FSupportResume := True;
DoFtpInfo(ftpSupportResume, msgSResume);
Proceed('REST 0', fpSetinitialDirectory);
end
else
begin
FSupportResume := False;
DoFtpInfo(ftpNotSupportResume);
fpSetinitialDirectory('299');
end;
end;
procedure TMFtp.fpTestSystemType;
var FSyst: String;
begin
if Line[1] <> '5' then
begin
if FType = ftpstAutoDetect then
begin
FSyst := UpperCase(Copy(Line, 5, 99));
FType := ftpstDefault;
if Pos('UNIX', FSyst) <> 0 then FType := ftpstUnix else
if Pos('BSD', FSyst) <> 0 then FType := ftpstBSD else
if Pos('SUNOS', FSyst) <> 0 then FType := ftpstSunOS else
if Pos('CLIX', FSyst) <> 0 then FType := ftpstClix else
if Pos('ULTRIX', FSyst) <> 0 then FType := ftpstUltrix else
if Pos('MVS', FSyst) <> 0 then FType := ftpstMVS else
if Pos('QVT', FSyst) <> 0 then FType := ftpstQVT else
if Pos('NCSA', FSyst) <> 0 then FType := ftpstNCSA else
if Pos('WFTPD', FSyst) <> 0 then FType := ftpstWFTPD else
if Pos('WINDWOS_NT', FSyst) <> 0 then FType := ftpstMSFTP else
if Pos('CHAMELEON', FSyst) <> 0 then FType := ftpstChameleon else
if Pos('VMS', FSyst) <> 0 then
if Pos('MULTINET', FSyst) <> 0 then FType := ftpstVmsMultinet else FType := ftpstVmsUcx
else
begin
if Pos('VM', Fsyst) <> 0 then
begin
if Pos('VPS', FSyst) = 0 then
FType := ftpstVM
else
FType := ftpstVMVPS;
end;
end;
end;
end
else
begin
FType := ftpstDefault;
end;
Proceed('REST 100', fpTestREST);
end;
function InitPort(Line: String): String;
begin
Result := Copy(Line, Pos('(', Line) + 1, Length(Line));
Result := Copy(Result, 1, Pos(')', Result) - 1);
end;
procedure TMFtp.fpTransfer;
begin
if Line[1] <> '2' then
begin
FTransferSuccess := False;
FTransferFromFtp.DoFtpError(ftpTransferType);
end;
FTransferToFtp.SetTransferMode(TransMode, fpTransfer2);
end;
procedure TMFtp.fpTransfer2;
begin
if Line[1] <> '2' then
begin
FTransferSuccess := False;
FTransferToFtp.DoFtpError(ftpTransferType);
end;
FTransferFromFtp.Proceed('PASV', fpTransfer3);
end;
procedure TMFtp.fpTransfer3;
begin
if Line[1] <> '2' then
begin
FTransferFromFtp.DoFtpError(ftpTransferPort);
FTransferToFtp.Proceed('PASV', fpTransfer3b);
end
else
FTransferToFtp.Proceed('PORT ' + InitPort(Line), fpTransfer5);
end;
procedure TMFtp.fpTransfer3b;
begin
if Line[1] <> '2' then
begin
FTransferSuccess := False;
FTransferToFtp.DoFtpError(ftpTransferFatalPort);
end
else
FTransferFromFtp.Proceed('PORT ' + InitPort(Line), fpTransfer4b);
end;
procedure TMFtp.fpTransfer4b;
begin
if Line[1] <> '2' then
begin
FTransferSuccess := False;
FTransferToFtp.DoFtpError(ftpTransferFatalPort);
Inc(FTransferCounter);
fpTransferFinished;
end
else
fpTransfer5(Line);
end;
procedure TMFtp.fpTransfer5;
var GetToFileSize, GetFromFileSize: Integer;
begin
if Line[1] <> '2' then
begin
fpTransfer3(Line);
end
else
begin
GetToFileSize := FTransferToFtp.FileExists(FTargetName);
if GetToFileSize<> - 1 then
begin
GetToFileSize := UnformatInteger(FTransferToFtp.Files.Items[GetToFileSize].Size);
GetFromFileSize := FTransferFromFtp.FileExists(FSourceName);
if GetFromFileSize = - 1 then
begin
FTransferSuccess := False;
FTransferFromFtp.DoFtpError(ftpTransferGet);
Inc(FTransferCounter);
fpTransferFinished;
Exit;
end;
GetFromFileSize := UnformatInteger(FTransferFromFtp.Files.Items[GetFromFileSize].Size);
if GetToFileSize <> 0 then
begin
if (GetFromFileSize>GetToFileSize) and (FTransferResume) and (FTransferToFtp.FSupportResume) then
begin
TempInt := GetToFileSize;
DoFTPInfo(ftpTransferResume, FTargetName);
FTransferToFtp.Proceed('APPE ' + FTargetName, fpTransfer6b);
Exit;
end;
end;
end;
FTransferToFtp.Proceed('STOR ' + FTargetName, fpTransfer6);
end;
end;
procedure TMFtp.fpTransfer6; // from 'Stor TargetName'
begin
case Line[1] of
'1':
begin
DoFTPInfo(ftpTransferPutStart, FSourceName);
fpTransfer7b('3');
end;
else
fpTransfer6b(Line);
end;
end;
procedure TMFtp.fpTransfer6b; // from 'Appe TargetName'
begin
case Line[1] of
'1':
begin
DoFTPInfo(ftpTransferPutStart, FTargetName);
FTransferFromFtp.Proceed('REST ' + IntToStr(TempInt), fpTransfer7b);
end;
'2':
begin
DoFTPInfo(ftpTransferPutFinish, FTargetName);
FTransferSuccess := True;
fpTransferFinished;
end;
else
begin
Inc(FTransferCounter);
FTransferSuccess := False;
FTransferToFtp.DoFtpError(ftpTransferPut);
fpTransferFinished;
end;
end;
end;
procedure TMFtp.fpTransfer7b;
begin
if Line[1] <> '3' then FTransferToFtp.DoFtpError(ftpTransferResumeFailed);
FTransferFromFtp.Proceed('RETR ' + FSourceName, fpTransfer8);
end;
procedure TMFtp.fpTransfer8; // After Normal/Resume Transfer
begin
case Line[1] of
'1': DoFTPInfo(ftpTransferGetStart, FSourceName);
'2':
begin
FTransferSuccess := True;
DoFTPInfo(ftpTransferGetFinish, FSourceName);
fpTransferFinished;
end;
else
begin
FTransferToFtp.StopTransfer;
while Copy(FTransferToFtp.LastReply, 1, 3) <> '226' do ProcessMessages;
FTransferSuccess := False;
FTransferFromFtp.DoFtpError(ftpTransferGet);
// Inc(FTransferCounter);
fpTransferFinished;
end;
end;
end;
procedure TMFtp.fpTransferFinished;
begin
Inc(FTransferCounter);
if FTransferCounter >= 2 then
begin
FTransferFromFtp.FBusy := False;
FTransferToFtp.FBusy := False;
if Assigned(FTransferToFtp.FOnReady) then
begin
FTransferToFtp.FOnReady(Self);
FTransferToFtp.CallNEvents(9);
end;
Ready; // if Assigned(FTransferFromFtp.FOnReady) then
// FTransferFromFtp.FOnReady(Self);
end;
end;
procedure TMFtp.fpUpload;
begin
UploadSize := 0;
if (FSupportResume) and (FSupportSIZE) then
Proceed('SIZE ' + FSelection, fpUpload2)
else
begin
PassiveP := fpUpload4b;
fpUpload3('299');
end;
end;
procedure TMFtp.fpUpload2;
var s: String;
begin
if Line[1] <> '2' then
begin
if (Line[1] = '5') and (Line[2] = '0') then FSupportSIZE := False;
PassiveP := fpUpload4b;
end
else
begin
UploadSize := StrToIntDef(Copy(Line, 5, 999), 0);
if UploadSize = 0 then
begin
PassiveP := fpUpload4b;
end
else
begin
if (Assigned(NeedInfo)) then NeedInfo(self, niOverwrite, s);
if s = 'Resume' then
begin
DataFile.Seek(UploadSize, soFromBeginning);
PassiveP := fpUpload4a;
end
else
begin
if (s = 'Overwrite') or (s = '') then
begin
PassiveP := fpUpload4b;
end
else
begin
MyCloseFile;
FTPLastAction := ftplaNone;
Ready;
Exit;
end;
end;
end;
end;
fpUpload3('299');
end;
procedure TMFtp.fpUpload3;
begin
ReadyPort := False;
ReadyMain := False;
if Line[1] = '2' then
begin
if FPassive then
begin
Proceed('PASV', fpPreparePassive);
end
else
Proceed('PORT ' + SetupDataPort, PassiveP);
end
else
begin
DoFtpError(ftpProtocolError);
Ready;
end;
end;
procedure TMFtp.fpUpload4a;
begin
if Line[1] <> '2' then
begin
DoFtpError(ftpProtocolError);
Ready;
end
else
begin
with DataSocket do
begin
OnReadReady := nil;
OnDisconnected := DataFileDisconnected;
if FPassive then
begin
OnWriteReady := DataStorConnected;
OnConnected := DataListConnected;
end
else
begin
OnWriteReady := nil;
ListeningSocket.OnAccept := DataStorConnected;
end;
end;
FStartPoint := UploadSize;
Proceed('REST ' + IntToStr(FStartPoint), fpUpload5a);
end;
end;
procedure TMFtp.fpUpload5a;
begin
if Line[1] = '3' then
Proceed('STOR ' + FSelection, fpUpload5b)
else
begin
DataSocket.Disconnect;
ListeningSocket.Disconnect;
errs := msgFResumeU;
DoFtpError(ftpResumeFailed);
Ready;
end;
end;
procedure TMFtp.fpUpload4b;
begin
if line[1] <> '2' then
begin
DoFtpError(ftpProtocolError);
Ready;
end
else
begin
with DataSocket do
begin
OnReadReady := nil;
OnDisconnected := DataFileDisconnected;
OnWriteReady := DataStorFile;
if FPassive then
OnConnected := DataStorConnected
else
ListeningSocket.OnAccept := DataStorConnected;
end;
Proceed('STOR ' + FSelection, fpUpload5b);
end;
end;
procedure TMFtp.fpUpload5b;
begin
case Line[1] of
'1':
begin
NextP := fpProcessGeneral;
end;
'2':
begin
ReadyMain := True;
if ReadyPort then Ready;
end;
else
begin
DataSocket.Disconnect; {close data connection}
ListeningSocket.Disconnect;
if (Aborted) and (Copy(Line, 1, 3) = '426') then
begin
if Assigned(FAborted) then FAborted(Self);
CallNEvents(13);
end
else
begin
errs := msgDenied;
DoFtpError(ftpPermissionDenied);
end;
Ready;
end;
end;
end;
procedure TMFtp.FtpProcess;
begin
if Line = '' then Exit;
if Intermediate and (Copy(Line, 1, 4) <> Response + ' ') then
begin
if Copy(Line, Length(Line) - 1, 2) <> #13#10 then
FBannerStore := FBannerStore + Line + #13#10
else
FBannerStore := FBannerStore + Line;
Exit;
end;
if Line[4] = '-' then
begin
if not Intermediate then
begin
Intermediate := True;
FBannerStore := Line;
end;
Response := Copy(Line, 1, 3);
Exit;
end;
if Intermediate then
begin
Intermediate := False;
FBanner.Clear;
FBanner.Text := FBannerStore;
FBanner.Add(Line);
DoFtpInfo(ftpBannerAvailable);
end;
FLastLine := Line;
if Assigned(NextP) then NextP(Line);
end;
{=========== data connection routines ===========}
procedure TMFtp.DataListConnected;
begin
if not FPassive then
DataSocket.Accept(ListeningSocket);
if DataSocket.LastError <> 0 then
begin
if (not TransferAborted) and (DataSocket.LastError <> 10038) then FatalError(ftpDataError);
Exit;
end;
Bytes := 0;
FSuccess := True;
StartTime := GetTickCount;
TrTime := 0;
DataPartialLine := '';
DataConnected := True;
TransferAborted := False;
end;
procedure TMFtp.DataListDisconnected;
var
e: TNotifyEvent;
begin
with DataSocket do
begin
e := OnReadReady;
OnConnected := nil;
if Assigned(e) then OnReadReady(Sender);
TrTime := GetTickCount - StartTime;
Disconnect;
ListeningSocket.Disconnect;
DoFtpInfo(ftpDirectoryRefresh);
DoFtpInfo(ftpTransferDone);
DataConnected := False;
FDoingListing := False;
ReadyPort := True;
if ReadyMain then
if ftpLastAction = ftplaSearch then ReadyList := True else Ready;
end;
end;
procedure TMFtp.DataFileDisconnected;
var
e: TNotifyEvent;
begin
e := DataSocket.OnReadReady;
if Assigned(e) then DataSocket.OnReadReady(Sender);
TrTime := GetTickCount - StartTime;
DataSocket.Disconnect;
ListeningSocket.Disconnect;
MyCloseFile;
DoFtpInfo(ftpTransferDone);
DataConnected := False;
FDoingListing := False;
ReadyPort := True;
if ReadyMain then Ready;
end;
procedure TMFtp.DataRetrFile;
var
n, Transferred: Integer;
begin
FDoingListing := False;
repeat
n := DataSocket.RecvBuf(@InBuffer, IN_BUFFER_SIZE);
if DataSocket.LastError <> 0 then
begin
if (not TransferAborted) and (DataSocket.LastError <> 10038) then FatalError(ftpDataError);
Exit;
end;
if (n = 0) or (FileOpened = False) then Exit;
Inc(Bytes, n);
TrTime := GetTickCount - StartTime;
DoFtpInfo(ftpDataTrace);
// if FileOpened ...
Transferred := DataFile.Write(InBuffer, n);
if n <> Transferred then
begin
DoFtpError(ftpFileWrite);
MyCloseFile;
end;
until n <= 0;
end;
procedure TMFtp.DataDoListing;
var i, el: Integer;
d: Boolean;
Linein, newLine: String;
fname, size, date, symlink: String;
attrib: String;
owner, group: String;
begin
FDoingListing := True;
i := DataSocket.RecvBuf(@InBuffer, IN_BUFFER_SIZE);
if DataSocket.LastError <> 0 then
begin
if (not TransferAborted) and (DataSocket.LastError <> 10038) then FatalError(ftpDataError);
Exit;
end;
InBuffer[i] := #0;
Linein := StrPas(@InBuffer);
if Linein = '' then Exit;
Inc(Bytes, Length(Linein));
Linein := DataPartialLine + Linein;
repeat
el := Pos(#13 + #10, Linein);
if el <> 0 then
begin
newLine := Copy(Linein, 1, el - 1);
Delete(Linein, 1, el + 1);
TrTime := GetTickCount - StartTime;
DoFtpInfo(ftpDataTrace, newLine);
try
FList.Add(newLine);
Inc(pcount);
if ParseListingLine(FtpParse.TMFtpServerType(FType), newLine, fname,
size, date, symlink, attrib, owner, group, d) then
begin
if d then
begin
if (fname <> '.') and (fname <> '..') then
begin
TDirectories.Add(fname, Attrib, Date, Size, Symlink, owner, group, '');
DoFtpInfo(ftpListingParsed, 'Folder');
end;
end
else
begin
TFiles.Add(fname, Attrib, Date, Size, Symlink, owner, group, '');
DoFtpInfo(ftpListingParsed, 'File');
end;
end;
except
end;
end;
until el = 0;
DataPartialLine := Linein;
end;
procedure TMFtp.DataStorFile(sender: TObject); {BDS}
var
Totsent, nb, fp: Longint;
BlockingError: Boolean;
begin
nb := OUT_BUFFER_SIZE;
if TransferAborted then
begin
FSuccess := False;
end else begin
BlockingError := False;
repeat
if MyEOF then Break;
try
fp := TotalBytesToSend - DataFile.Position;
if fp < nb then
nb := fp;
DataFile.Read(OutBuffer, nb);
// Inc(Bytes, nb);
except
DoFtpError(ftpFileRead);
MyCloseFile;
DataSocket.Disconnect;
ListeningSocket.Disconnect;
FSuccess := False;
end;
Totsent := 0;
while Totsent < nb do
begin
ProcessMessages;
if Aborted then
begin
FSuccess := False;
break;
end else begin
DataSocket.WantBlockingErrors := True;
Inc(Totsent, DataSocket.SendBuf(@OutBuffer[Totsent], nb - Totsent));
DataSocket.WantBlockingErrors := False;
BlockingError := DataSocket.LastError = WSAEWOULDBLOCK;
if BlockingError then
begin
DataFile.Seek(DataFile.Position - (nb - Totsent), soFromBeginning);
break;
end;
if (DataSocket.LastError <> 0) then
begin
if not TransferAborted then FatalError(ftpDataError);
Inc(Bytes, Totsent);
Exit;
end;
end;
end;
Inc(Bytes, Totsent);
until BlockingError or (not FSuccess);
DoFtpInfo(ftpDataTrace);
TrTime := GetTickCount - StartTime;
end;
// check FSuccess and done state
if (not FSuccess) or (MyEOF) then
begin
DataSocket.Disconnect;
ListeningSocket.Disconnect;
MyCloseFile;
TrTime := GetTickCount - TrTime;
DoFtpInfo(ftpTransferDone);
ReadyPort := True;
if ReadyMain then Ready;
end;
end;
procedure TMFtp.DataStorConnected; {BDS}
begin
if not FPassive then
DataSocket.Accept(ListeningSocket);
if DataSocket.LastError <> 0 then
begin
if not TransferAborted then FatalError(ftpDataError);
Exit;
end;
Bytes := 0;
FSuccess := True;
StartTime := GetTickCount;
TrTime := 0;
DataPartialLine := '';
DataConnected := True;
TransferAborted := False;
FDoingListing := False;
TotalBytesToSend := DataFile.Size;
StartTime := GetTickCount;
DoFtpInfo(ftpFileSize, IntToStr(DataFile.Size));
end;
procedure TMFtp.StopTransfer;
var data: String;
begin
if TransferAborted then Exit;
TransferAborted := True;
data := #255 + #244;
while data <> '' do
begin
if Aborted then Exit;
Delete(data, 1, SendBuf(PChar(@data[1]), Length(data)));
if CheckError then Exit;
end;
data := #255 + #242;
while data <> '' do
begin
if Aborted then Exit;
Delete(data, 1, SendBufOOB(PChar(@data[1]), Length(data)));
if CheckError then Exit;
end;
Proceed('ABOR', NextP);
data := #255 + #242;
while data <> '' do
begin
if Aborted then Exit;
Delete(data, 1, SendBuf(PChar(@data[1]), Length(data)));
if CheckError then Exit;
end;
end;
procedure TMFtp.Abort;
begin
case FtpLastAction of
ftplaGet, ftplaGETIndexFile, ftplaPut, ftplaList:
begin
StopTransfer;
end;
ftplaGETS, ftplaPUTS:
begin
FMFinished := True;
StopTransfer;
end;
ftplaMKDS, ftplaRMDS, ftplaRMS, ftplaRENS:
begin
FMFinished := True;
FMAborted := True;
end;
ftplaSearch:
begin
BAborted := True;
FSuccess := False;
ReadyCWD := True;
ReadyList := True;
end;
ftplaLogin, ftplaNone:
begin
if not ControlLoggedIn then
begin
FRemain := -1;
{$ifdef USE_RETRYING_TIMER}
RTimer.Enabled := False;
{$else}
SRetry := True;
{$endif}
Disconnect;
if Assigned(FAborted) then FAborted(Self);
CallNEvents(13);
end;
end;
end;
end;
function TMFtp.SetupDataPort;
var
Line: String;
dataaddr: String;
dataport: Word;
i: Word;
begin
{ this line should not in the with block }
ListeningSocket.Address := GetLocalAddress;
with ListeningSocket do
begin
Port := 0;
FillAddress(Address);
FillPort(Port);
CreateTCPSocket;
Listen;
dataaddr := GetLocalAddress;
dataport := GetLocalPort;
end;
DataPartialLine := '';
i := 1;
while i <> 0 do
begin
i := Pos('.', dataaddr);
if i <> 0 then dataaddr[i] := ',';
end;
Line := dataaddr + ',' + IntToStr(dataport div 256) + ',' + IntToStr(dataport mod 256);
Result := Line;
end;
procedure TMFtp.SetupDataPortPassive;
var p: String;
ps: array[0..5] of String;
c, l, i: Word;
begin
c := 0;
p := copy(s, pos('(', s) + 1, length(s));
p := copy(p, 1, pos(')', p) - 1);
{A bug of Troll Tech ftp server(ftp.troll.no):
227 Passive mode OK (195,0,254,75,42,154 )
and the correct respondence is:
227 Passive mode OK (195,0,254,75,42,154)
}
{$ifndef NOPATCH} p := Trim(p); {$endif}
l := length(p);
for i:=1 to l do
if p[i] = ',' then
Inc(c)
else
ps[c]:=ps[c] + p[i];
DataPartialLine := '';
with DataSocket do
begin
Address := ps[0] + '.' + ps[1] + '.' + ps[2]+'.' + ps[3];
Port := StrToInt(ps[4]) shl 8 + StrToInt(ps[5]);
FillAddress(Address);
FillPort(Port);
CreateTCPSocket;
Connect;
end;
end;
function TMFtp.CheckError;
begin
if (LastError = 0) or (Aborted) then
begin
FError := ftpNone;
Result := False;
end
else
begin
case LastError of
WSAENETDOWN:
begin
FError := ftpNetworkDown;
errs := msgNetworkDown;
end;
WSAEACCES:
begin
FError := ftpInvalidAddress;
errs := msgInvalidAddress;
end;
WSAENOTSOCK:
begin
FError := ftpNone;
{Invalid socket specified (it is usually not a real error)}
Result := False;
Exit;
end;
WSAEINVAL:
begin
FError := ftpInternalError;
errs := '';
end;
WSAETIMEDOUT:
begin
FError := ftpConnectTimeout;
errs := msgTimeOut;
end;
WSAEMFILE:
begin
FError := ftpOutofSockets;
errs := msgOutOfSocket;
end;
WSAENETUNREACH:
begin
FError := ftpNetworkUnreachable;
errs := msgNetworkUR;
end;
WSAEADDRNOTAVAIL:
begin
FError := ftpAddressNotAvailable;
errs := msgNotAvail;
end;
WSAECONNREFUSED:
begin
FError := ftpConnectionRefused;
errs := msgRefuse;
end;
WSAENETRESET, WSAENOBUFS:
begin
FError := ftpGeneralWinsockError;
errs := msgGeneralE;
end;
WSAECONNABORTED:
begin
FError := ftpConnAborted;
errs := msgAborted;
end;
WSAECONNRESET:
begin
FError := ftpConnReset;
errs := msgReset;
end;
WSAHOST_NOT_FOUND, WSATRY_AGAIN, WSANO_RECOVERY, WSANO_DATA:
begin
FError := ftpAddressResolutionError;
errs := msgARE;
end;
WSAEHOSTUNREACH:
begin
FError := ftpHostUnreachable;
errs := msgHostUR;
end;
WSAENOTCONN: { disconnected from server }
begin
FError := ftpNone;
Result := False;
Exit;
end;
else
{ WSAEFAULT, WSAEOPNOTSUPP, WSAESHUTDOWN, WSAEMSGSIZE,
WSAEADDRINUSE, WSAEINPROGRESS, WSAEINTR, WSAEAFNOSUPPORT, WSAEISCONN }
begin
FError := ftpInternalError;
errs := msgUnexpected + IntToStr(LastError) + ')';
end;
end;
// FRemain := -1;
FatalError(FError);
Result := True;
end;
end;
{call this before an operation is being started}
function TMFtp.CheckStatus;
begin
Result := False;
if not ControlConnected then
begin
FSuccess := False;
DoFtpInfo(ftpServerDisconnected);
Exit;
end;
if FBusy then
begin
FSuccess := False;
DoFtpInfo(ftpAlreadyBusy);
Exit;
end;
FBusy := True;
if Assigned(FFtpBusy) then FFtpBusy(Self);
CallNEvents(12);
FSuccess := True;
NTimer.Enabled := False;
Result := True;
FError := FtpNone;
end;
function TMFtp.RecvText;
var
n: Integer;
buf: array[0..IN_BUFFER_SIZE] of Char;
begin
n := RecvBuf(buf, IN_BUFFER_SIZE);
buf[n] := #0;
Result := buf;
end;
procedure TMFtp.NTimerTimer;
begin
if FBusy then Exit;
if not ControlLoggedIn then exit;
if not FSupportNOOP then exit;
FBusy := True;
FtpLastAction := ftplaNOOP;
Proceed('NOOP', fpNOOP);
end;
function TMFtp.GetInterval;
begin
Result := NTimer.Interval div 1000;
end;
procedure TMFtp.SetInterval;
begin
NTimer.Interval := I * 1000;
end;
{$ifdef USE_RETRYING_TIMER}
procedure TMFtp.RTimerTimer;
begin
RTimer.Enabled := False;
LoginMain;
end;
{$endif}
function TMFtp.GetStartPoint;
begin
if (FtpLastAction = ftplaGET) or (FtpLastAction = ftplaPUT) then
Result := FStartPoint
else
Result := 0;
end;
procedure TMFtp.SetAsync;
begin
if not FBusy then FAsync := B;
end;
procedure TMFtp.SetRetries;
begin
FRetries := I;
end;
function TMFtp.FileExists;
begin
Result := FFiles.IndexOf(filename);
end;
function TMFtp.DirectoryExists;
begin
Result := FDirectories.IndexOf(dir);
end;
procedure TMFtp.FileSetAttr;
var Value: Integer;
begin
Value := 0;
if OwnerRead then Inc(Value, 400);
if OwnerWrite then Inc(Value, 200);
if OwnerExecute then Inc(Value, 100);
if GroupRead then Inc(Value, 40);
if GroupWrite then Inc(Value, 20);
if GroupExecute then Inc(Value, 10);
if PublicRead then Inc(Value, 4);
if PublicWrite then Inc(Value, 2);
if PublicExecute then Inc(Value);
Proceed('SITE CHMOD ' + IntToStr(Value)+' '+ filename, fpChmod);
end;
procedure TMFtp.SetTransferMode;
begin
if M = CurrentMode then
begin
P('299');
end
else
begin
CurrentMode := M;
Proceed('TYPE ' + M, P);
end;
end;
procedure TMFtp.CallNEvents;
var i: Integer;
begin
for i := 1 to MAX_HANDLERS do
if Assigned(NEvents[EventType, i]) then NEvents[EventType, i](Self);
end;
function TMFtp.RegisterNotifyEvent;
var i: Integer;
begin
if Assigned(P) then
begin
for i := 1 to MAX_HANDLERS do
begin
if not Assigned(NEvents[EventType, i]) then
begin
NEvents[EventType, i] := P;
Result := i;
Exit;
end;
end;
end;
Result := -1;
end;
function TMFtp.RegisterErrorEvent;
var i: Integer;
begin
if Assigned(P) then
begin
for i := 1 to MAX_HANDLERS do
begin
if not Assigned(NOnFtpError[i]) then
begin
NOnFtpError[i] := P;
Result := i;
Exit;
end;
end;
end;
Result := -1;
end;
function TMFtp.RegisterInfoEvent;
var i: Integer;
begin
if Assigned(P) then
begin
for i := 1 to MAX_HANDLERS do
begin
if not Assigned(NOnFtpInfo[i]) then
begin
NOnFtpInfo[i] := P;
Result := i;
Exit;
end;
end;
end;
Result := -1;
end;
procedure TMFtp.UnRegisterNotifyEvent;
begin
NEvents[EventType, i] := nil;
end;
procedure TMFtp.UnRegisterErrorEvent;
begin
NOnFtpError[i] := nil;
end;
procedure TMFtp.UnRegisterInfoEvent;
begin
NOnFtpInfo[i] := nil;
end;
{ message processing }
function TMFtp.ProcessMessage;
var
Msg: TMsg;
begin
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
begin
Result := True;
TranslateMessage(Msg);
DispatchMessage(Msg);
end
else
Result := False;
end;
procedure TMFtp.ProcessMessages;
begin
if not FMultiThreaded then
Application.ProcessMessages
else
while ProcessMessage do Sleep(500);
end;
end.