123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245 |
- { lFTP CopyRight (C) 2005-2008 Ales Katona
- This library is Free software; you can rediStribute it and/or modify it
- under the terms of the GNU Library General Public License as published by
- the Free Software Foundation; either version 2 of the License, or (at your
- option) any later version.
- This program is diStributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
- for more details.
- You should have received a Copy of the GNU Library General Public License
- along with This library; if not, Write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- This license has been modified. See File LICENSE for more inFormation.
- Should you find these sources withOut a LICENSE File, please contact
- me at [email protected]
- }
- unit lFTP;
- {$mode objfpc}{$H+}
- {$inline on}
- {$macro on}
- //{$define debug}
- interface
- uses
- Classes, lNet, lTelnet;
-
- const
- DEFAULT_FTP_PORT = 1025;
- type
- TLFTP = class;
- TLFTPClient = class;
- TLFTPStatus = (fsNone, fsCon, fsUser, fsPass, fsPasv, fsPort, fsList, fsRetr,
- fsStor, fsType, fsCWD, fsMKD, fsRMD, fsDEL, fsRNFR, fsRNTO,
- fsSYS, fsFeat, fsPWD, fsHelp, fsLast);
-
- TLFTPStatusSet = set of TLFTPStatus;
-
- TLFTPStatusRec = record
- Status: TLFTPStatus;
- Args: array[1..2] of string;
- end;
-
- TLFTPTransferMethod = (ftActive, ftPassive);
-
- TLFTPClientStatusEvent = procedure (aSocket: TLSocket;
- const aStatus: TLFTPStatus) of object;
- { TLFTPStatusStack }
- { TLFTPStatusFront }
- {$DEFINE __front_type__ := TLFTPStatusRec}
- {$i lcontainersh.inc}
- TLFTPStatusFront = TLFront;
-
- TLFTP = class(TLComponent, ILDirect)
- protected
- FControl: TLTelnetClient;
- FData: TLTcp;//TLTcpList;
- FSending: Boolean;
- FTransferMethod: TLFTPTransferMethod;
- FFeatureList: TStringList;
- FFeatureString: string;
- function GetConnected: Boolean; virtual;
-
- function GetTimeout: Integer;
- procedure SetTimeout(const Value: Integer);
- function GetSession: TLSession;
- procedure SetSession(const AValue: TLSession);
- procedure SetCreator(AValue: TLComponent); override;
- function GetSocketClass: TLSocketClass;
- procedure SetSocketClass(Value: TLSocketClass);
- public
- constructor Create(aOwner: TComponent); override;
- destructor Destroy; override;
-
- function Get(out aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; virtual; abstract;
- function GetMessage(out msg: string; aSocket: TLSocket = nil): Integer; virtual; abstract;
-
- function Send(const aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; virtual; abstract;
- function SendMessage(const msg: string; aSocket: TLSocket = nil): Integer; virtual; abstract;
-
- public
- property Connected: Boolean read GetConnected;
- property Timeout: Integer read GetTimeout write SetTimeout;
- property SocketClass: TLSocketClass read GetSocketClass write SetSocketClass;
- property ControlConnection: TLTelnetClient read FControl;
- property DataConnection: TLTCP read FData;
- property TransferMethod: TLFTPTransferMethod read FTransferMethod write FTransferMethod default ftPassive;
- property Session: TLSession read GetSession write SetSession;
- property FeatureList: TStringList read FFeatureList;
- end;
- { TLFTPTelnetClient }
-
- TLFTPTelnetClient = class(TLTelnetClient)
- protected
- function React(const Operation, Command: Char):boolean; override;
- end;
- { TLFTPClient }
- TLFTPClient = class(TLFTP, ILClient)
- protected
- FStatus: TLFTPStatusFront;
- FCommandFront: TLFTPStatusFront;
- FStoreFile: TFileStream;
- FExpectedBinary: Boolean;
- FPipeLine: Boolean;
- FPassword: string;
- FPWD: string;
- FStatusFlags: array[TLFTPStatus] of Boolean;
- FOnError: TLSocketErrorEvent;
- FOnReceive: TLSocketEvent;
- FOnSent: TLSocketProgressEvent;
- FOnControl: TLSocketEvent;
- FOnConnect: TLSocketEvent;
- FOnSuccess: TLFTPClientStatusEvent;
- FOnFailure: TLFTPClientStatusEvent;
- FChunkSize: Word;
- FLastPort: Word;
- FStartPort: Word;
- FStatusSet: TLFTPStatusSet;
- FSL: TStringList; // for evaluation, I want to prevent constant create/free
- procedure OnRe(aSocket: TLSocket);
- procedure OnDs(aSocket: TLSocket);
- procedure OnSe(aSocket: TLSocket);
- procedure OnEr(const msg: string; aSocket: TLSocket);
- procedure OnControlEr(const msg: string; aSocket: TLSocket);
- procedure OnControlRe(aSocket: TLSocket);
- procedure OnControlCo(aSocket: TLSocket);
- procedure OnControlDs(aSocket: TLSocket);
-
- procedure ClearStatusFlags;
- function GetCurrentStatus: TLFTPStatus;
- function GetTransfer: Boolean;
- function GetEcho: Boolean;
- procedure SetEcho(const Value: Boolean);
- procedure ParsePWD(const s: string);
- function GetConnected: Boolean; override;
- function GetBinary: Boolean;
- procedure SetBinary(const Value: Boolean);
- function CanContinue(const aStatus: TLFTPStatus; const Arg1, Arg2: string): Boolean;
- function CleanInput(var s: string): Integer;
- procedure SetStartPor(const Value: Word);
- procedure EvaluateFeatures;
- procedure EvaluateAnswer(const Ans: string);
- procedure PasvPort;
- function User(const aUserName: string): Boolean;
- function Password(const aPassword: string): Boolean;
- procedure SendChunk(const Event: Boolean);
- procedure ExecuteFrontCommand;
- public
- constructor Create(aOwner: TComponent); override;
- destructor Destroy; override;
- function Get(out aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; override;
- function GetMessage(out msg: string; aSocket: TLSocket = nil): Integer; override;
-
- function Send(const aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; override;
- function SendMessage(const msg: string; aSocket: TLSocket = nil): Integer; override;
-
- function Connect(const aHost: string; const aPort: Word = 21): Boolean; virtual; overload;
- function Connect: Boolean; virtual; overload;
-
- function Authenticate(const aUsername, aPassword: string): Boolean;
-
- function GetData(out aData; const aSize: Integer): Integer;
- function GetDataMessage: string;
-
- function Retrieve(const FileName: string): Boolean;
- function Put(const FileName: string): Boolean; virtual; // because of LCLsocket
-
- function ChangeDirectory(const DestPath: string): Boolean;
- function MakeDirectory(const DirName: string): Boolean;
- function RemoveDirectory(const DirName: string): Boolean;
-
- function DeleteFile(const FileName: string): Boolean;
- function Rename(const FromName, ToName: string): Boolean;
- public
- procedure List(const FileName: string = '');
- procedure Nlst(const FileName: string = '');
- procedure SystemInfo;
- procedure ListFeatures;
- procedure PresentWorkingDirectory;
- procedure Help(const Arg: string);
-
- procedure Disconnect(const Forced: Boolean = True); override;
-
- procedure CallAction; override;
- public
- property StatusSet: TLFTPStatusSet read FStatusSet write FStatusSet;
- property ChunkSize: Word read FChunkSize write FChunkSize;
- property Binary: Boolean read GetBinary write SetBinary;
- property PipeLine: Boolean read FPipeLine write FPipeLine;
- property Echo: Boolean read GetEcho write SetEcho;
- property StartPort: Word read FStartPort write FStartPort default DEFAULT_FTP_PORT;
- property Transfer: Boolean read GetTransfer;
- property CurrentStatus: TLFTPStatus read GetCurrentStatus;
- property PresentWorkingDirectoryString: string read FPWD;
- property OnError: TLSocketErrorEvent read FOnError write FOnError;
- property OnConnect: TLSocketEvent read FOnConnect write FOnConnect;
- property OnSent: TLSocketProgressEvent read FOnSent write FOnSent;
- property OnReceive: TLSocketEvent read FOnReceive write FOnReceive;
- property OnControl: TLSocketEvent read FOnControl write FOnControl;
- property OnSuccess: TLFTPClientStatusEvent read FOnSuccess write FOnSuccess;
- property OnFailure: TLFTPClientStatusEvent read FOnFailure write FOnFailure;
- end;
-
- function FTPStatusToStr(const aStatus: TLFTPStatus): string;
-
- implementation
- uses
- SysUtils, Math;
- const
- FLE = #13#10;
- EMPTY_REC: TLFTPStatusRec = (Status: fsNone; Args: ('', ''));
- FTPStatusStr: array[TLFTPStatus] of string = ('None', 'Connect', 'Authenticate', 'Password',
- 'Passive', 'Active', 'List', 'Retrieve',
- 'Store', 'Type', 'CWD', 'MKDIR',
- 'RMDIR', 'Delete', 'RenameFrom',
- 'RenameTo', 'System', 'Features',
- 'PWD', 'HELP', 'LAST');
- procedure Writedbg(const ar: array of const);
- {$ifdef debug}
- var
- i: Integer;
- begin
- if High(ar) >= 0 then
- for i := 0 to High(ar) do
- case ar[i].vtype of
- vtInteger: Write(ar[i].vinteger);
- vtString: Write(ar[i].vstring^);
- vtAnsiString: Write(AnsiString(ar[i].vpointer));
- vtBoolean: Write(ar[i].vboolean);
- vtChar: Write(ar[i].vchar);
- vtExtended: Write(Extended(ar[i].vpointer^));
- end;
- Writeln;
- end;
- {$else}
- begin
- end;
- {$endif}
- function MakeStatusRec(const aStatus: TLFTPStatus; const Arg1, Arg2: string): TLFTPStatusRec;
- begin
- Result.Status := aStatus;
- Result.Args[1] := Arg1;
- Result.Args[2] := Arg2;
- end;
- function FTPStatusToStr(const aStatus: TLFTPStatus): string;
- begin
- Result := FTPStatusStr[aStatus];
- end;
- {$i lcontainers.inc}
- { TLFTP }
- function TLFTP.GetSession: TLSession;
- begin
- Result := FControl.Session;
- end;
- procedure TLFTP.SetSession(const AValue: TLSession);
- begin
- FControl.Session := aValue;
- FData.Session := aValue;
- end;
- procedure TLFTP.SetCreator(AValue: TLComponent);
- begin
- inherited SetCreator(AValue);
-
- FControl.Creator := AValue;
- FData.Creator := AValue;
- end;
- function TLFTP.GetConnected: Boolean;
- begin
- Result := FControl.Connected;
- end;
- function TLFTP.GetTimeout: Integer;
- begin
- Result := FControl.Timeout;
- end;
- procedure TLFTP.SetTimeout(const Value: Integer);
- begin
- FControl.Timeout := Value;
- FData.Timeout := Value;
- end;
- function TLFTP.GetSocketClass: TLSocketClass;
- begin
- Result := FControl.SocketClass;
- end;
- procedure TLFTP.SetSocketClass(Value: TLSocketClass);
- begin
- FControl.SocketClass := Value;
- FData.SocketClass := Value;
- end;
- constructor TLFTP.Create(aOwner: TComponent);
- begin
- inherited Create(aOwner);
- FHost := '';
- FPort := 21;
- FControl := TLFTPTelnetClient.Create(nil);
- FControl.Creator := Self;
- FData := TLTcp.Create(nil);
- FData.Creator := Self;
- FData.SocketClass := TLSocket;
- FTransferMethod := ftPassive; // let's be modern
- FFeatureList := TStringList.Create;
- end;
- destructor TLFTP.Destroy;
- begin
- FControl.Free;
- FData.Free;
- FFeatureList.Free;
- inherited Destroy;
- end;
- { TLFTPTelnetClient }
- function TLFTPTelnetClient.React(const Operation, Command: Char):boolean;
- begin
- result:=false;
- // don't do a FUCK since they broke Telnet in FTP as per-usual
- end;
- { TLFTPClient }
- constructor TLFTPClient.Create(aOwner: TComponent);
- const
- DEFAULT_CHUNK = 8192;
- begin
- inherited Create(aOwner);
- FControl.OnReceive := @OnControlRe;
- FControl.OnConnect := @OnControlCo;
- FControl.OnError := @OnControlEr;
- FControl.OnDisconnect := @OnControlDs;
- FData.OnReceive := @OnRe;
- FData.OnDisconnect := @OnDs;
- FData.OnCanSend := @OnSe;
- FData.OnError := @OnEr;
- FStatusSet := [fsNone..fsLast]; // full Event set
- FPassWord := '';
- FChunkSize := DEFAULT_CHUNK;
- FStartPort := DEFAULT_FTP_PORT;
- FSL := TStringList.Create;
- FLastPort := FStartPort;
- ClearStatusFlags;
- FStatus := TLFTPStatusFront.Create(EMPTY_REC);
- FCommandFront := TLFTPStatusFront.Create(EMPTY_REC);
-
- FStoreFile := nil;
- end;
- destructor TLFTPClient.Destroy;
- begin
- Disconnect(True);
- FSL.Free;
- FStatus.Free;
- FCommandFront.Free;
- if Assigned(FStoreFile) then
- FreeAndNil(FStoreFile);
- inherited Destroy;
- end;
- procedure TLFTPClient.OnRe(aSocket: TLSocket);
- begin
- if Assigned(FOnReceive) then
- FOnReceive(aSocket);
- end;
- procedure TLFTPClient.OnDs(aSocket: TLSocket);
- begin
- FSending := False;
- Writedbg(['Disconnected']);
- end;
- procedure TLFTPClient.OnSe(aSocket: TLSocket);
- begin
- if Connected and FSending then
- SendChunk(True);
- end;
- procedure TLFTPClient.OnEr(const msg: string; aSocket: TLSocket);
- begin
- FSending := False;
- if Assigned(FOnError) then
- FOnError(msg, aSocket);
- end;
- procedure TLFTPClient.OnControlEr(const msg: string; aSocket: TLSocket);
- begin
- FSending := False;
-
- if Assigned(FOnFailure) then begin
- while not FStatus.Empty do
- FOnFailure(aSocket, FStatus.Remove.Status);
- end else
- FStatus.Clear;
-
- ClearStatusFlags;
- if Assigned(FOnError) then
- FOnError(msg, aSocket);
- end;
- procedure TLFTPClient.OnControlRe(aSocket: TLSocket);
- begin
- if Assigned(FOnControl) then
- FOnControl(aSocket);
- end;
- procedure TLFTPClient.OnControlCo(aSocket: TLSocket);
- begin
- if Assigned(FOnConnect) then
- FOnConnect(aSocket);
- end;
- procedure TLFTPClient.OnControlDs(aSocket: TLSocket);
- begin
- if Assigned(FOnError) then
- FOnError('Connection lost', aSocket);
- end;
- procedure TLFTPClient.ClearStatusFlags;
- var
- s: TLFTPStatus;
- begin
- for s := fsNone to fsLast do
- FStatusFlags[s] := False;
- end;
- function TLFTPClient.GetCurrentStatus: TLFTPStatus;
- begin
- Result := FStatus.First.Status;
- end;
- function TLFTPClient.GetTransfer: Boolean;
- begin
- Result := FData.Connected;
- end;
- function TLFTPClient.GetEcho: Boolean;
- begin
- Result := FControl.OptionIsSet(TS_ECHO);
- end;
- function TLFTPClient.GetConnected: Boolean;
- begin
- Result := FStatusFlags[fsCon] and inherited;
- end;
- function TLFTPClient.GetBinary: Boolean;
- begin
- Result := FStatusFlags[fsType];
- end;
- function TLFTPClient.CanContinue(const aStatus: TLFTPStatus; const Arg1,
- Arg2: string): Boolean;
- begin
- Result := FPipeLine or FStatus.Empty;
- if not Result then
- FCommandFront.Insert(MakeStatusRec(aStatus, Arg1, Arg2));
- end;
- function TLFTPClient.CleanInput(var s: string): Integer;
- var
- i: Integer;
- begin
- FSL.Text := s;
- for i := 0 to FSL.Count - 1 do
- if Length(FSL[i]) > 0 then
- EvaluateAnswer(FSL[i]);
- s := StringReplace(s, FLE, LineEnding, [rfReplaceAll]);
- i := Pos('PASS', s);
- if i > 0 then
- s := Copy(s, 1, i-1) + 'PASS';
- Result := Length(s);
- end;
- procedure TLFTPClient.SetStartPor(const Value: Word);
- begin
- FStartPort := Value;
- if Value > FLastPort then
- FLastPort := Value;
- end;
- procedure TLFTPClient.EvaluateFeatures;
- var
- i: Integer;
- begin
- FFeatureList.Clear;
- if Length(FFeatureString) = 0 then
- Exit;
- FFeatureList.Text := FFeatureString;
- FFeatureString := '';
- FFeatureList.Delete(0);
- i := 0;
- while i < FFeatureList.Count do begin
- if (Length(Trim(FFeatureList[i])) = 0)
- or (FFeatureList[i][1] <> ' ') then begin
- FFeatureList.Delete(i);
- Continue;
- end;
- FFeatureList[i] := Trim(FFeatureList[i]);
- Inc(i);
- end;
- end;
- procedure TLFTPClient.SetEcho(const Value: Boolean);
- begin
- if Value then
- FControl.SetOption(TS_ECHO)
- else
- FControl.UnSetOption(TS_ECHO);
- end;
- procedure TLFTPClient.ParsePWD(const s: string);
- var
- i: Integer;
- IsIn: Boolean = False;
- begin
- FPWD := '';
- for i := 1 to Length(s) do begin
- if s[i] = '"' then begin
- IsIn := not IsIn;
- Continue;
- end;
- if IsIn then
- FPWD := FPWD + s[i];
- end;
- end;
- procedure TLFTPClient.SetBinary(const Value: Boolean);
- const
- TypeBool: array[Boolean] of string = ('A', 'I');
- begin
- if CanContinue(fsType, BoolToStr(Value), '') then begin
- FExpectedBinary := Value;
- FStatus.Insert(MakeStatusRec(fsType, '', ''));
- FControl.SendMessage('TYPE ' + TypeBool[Value] + FLE);
- end;
- end;
- procedure TLFTPClient.EvaluateAnswer(const Ans: string);
- function GetNum: Integer;
- begin
- Result := -1;
- if (Length(Ans) >= 3)
- and (Ans[1] in ['0'..'9'])
- and (Ans[2] in ['0'..'9'])
- and (Ans[3] in ['0'..'9']) then
- Result := StrToInt(Copy(Ans, 1, 3));
- end;
- procedure ParsePortIP(s: string);
- var
- i, l: Integer;
- aIP: string;
- aPort: Word;
- sl: TStringList;
- begin
- if Length(s) >= 15 then begin
- sl := TStringList.Create;
- for i := Length(s) downto 5 do
- if s[i] = ',' then Break;
- while (i <= Length(s)) and (s[i] in ['0'..'9', ',']) do Inc(i);
- if not (s[i] in ['0'..'9', ',']) then Dec(i);
- l := 0;
- while s[i] in ['0'..'9', ','] do begin
- Inc(l);
- Dec(i);
- end;
- Inc(i);
- s := Copy(s, i, l);
- sl.CommaText := s;
- aIP := sl[0] + '.' + sl[1] + '.' + sl[2] + '.' + sl[3];
- try
- aPort := (StrToInt(sl[4]) * 256) + StrToInt(sl[5]);
- except
- aPort := 0;
- end;
- Writedbg(['Server PASV addr/port - ', aIP, ' : ', aPort]);
- if (aPort > 0) and FData.Connect(aIP, aPort) then
- Writedbg(['Connected after PASV']);
- sl.Free;
- FStatus.Remove;
- end;
- end;
-
- procedure SendFile;
- begin
- FStoreFile.Position := 0;
- FSending := True;
- SendChunk(False);
- end;
-
- function ValidResponse(const Answer: string): Boolean; inline;
- begin
- Result := (Length(Ans) >= 3) and
- (Ans[1] in ['1'..'5']) and
- (Ans[2] in ['0'..'9']) and
- (Ans[3] in ['0'..'9']);
-
- if Result then
- Result := (Length(Ans) = 3) or ((Length(Ans) > 3) and (Ans[4] = ' '));
- end;
-
- procedure Eventize(const aStatus: TLFTPStatus; const Res: Boolean);
- begin
- FStatus.Remove;
- if Res then begin
- if Assigned(FOnSuccess) and (aStatus in FStatusSet) then
- FOnSuccess(FData.Iterator, aStatus);
- end else begin
- if Assigned(FOnFailure) and (aStatus in FStatusSet) then
- FOnFailure(FData.Iterator, aStatus);
- end;
- end;
-
- var
- x: Integer;
- begin
- x := GetNum;
- Writedbg(['WOULD EVAL: ', FTPStatusStr[FStatus.First.Status], ' with value: ',
- x, ' from "', Ans, '"']);
- if FStatus.First.Status = fsFeat then
- FFeatureString := FFeatureString + Ans + FLE; // we need to parse this later
- if ValidResponse(Ans) then
- if not FStatus.Empty then begin
- Writedbg(['EVAL: ', FTPStatusStr[FStatus.First.Status], ' with value: ', x]);
- case FStatus.First.Status of
- fsCon : case x of
- 220:
- begin
- FStatusFlags[FStatus.First.Status] := True;
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- FStatusFlags[FStatus.First.Status] := False;
- Eventize(FStatus.First.Status, False);
- end;
- end;
- fsUser : case x of
- 230:
- begin
- FStatusFlags[FStatus.First.Status] := True;
- Eventize(FStatus.First.Status, True);
- end;
- 331,
- 332:
- begin
- FStatus.Remove;
- Password(FPassword);
- end;
- else
- begin
- FStatusFlags[FStatus.First.Status] := False;
- Eventize(FStatus.First.Status, False);
- end;
- end;
-
- fsPass : case x of
- 230:
- begin
- FStatusFlags[FStatus.First.Status] := True;
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- FStatusFlags[FStatus.First.Status] := False;
- Eventize(FStatus.First.Status, False);
- end;
- end;
- fsPasv : case x of
- 227: ParsePortIP(Ans);
- 300..600: FStatus.Remove;
- end;
- fsPort : case x of
- 200:
- begin
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- Eventize(FStatus.First.Status, False);
- end;
- end;
- fsType : case x of
- 200:
- begin
- FStatusFlags[FStatus.First.Status] := FExpectedBinary;
- Writedbg(['Binary mode: ', FExpectedBinary]);
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- Eventize(FStatus.First.Status, False);
- end;
- end;
- fsRetr : case x of
- 125, 150: begin { Do nothing } end;
- 226:
- begin
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- FData.Disconnect(True); // break on purpose, otherwise we get invalidated ugly
- Writedbg(['Disconnecting data connection']);
- Eventize(FStatus.First.Status, False);
- end;
- end;
- fsStor : case x of
- 125, 150: SendFile;
-
- 226:
- begin
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- Eventize(FStatus.First.Status, False);
- end;
- end;
- fsCWD : case x of
- 200, 250:
- begin
- FStatusFlags[FStatus.First.Status] := True;
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- FStatusFlags[FStatus.First.Status] := False;
- Eventize(FStatus.First.Status, False);
- end;
- end;
- fsPWD : case x of
- 257:
- begin
- ParsePWD(Ans);
- FStatusFlags[FStatus.First.Status] := True;
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- FStatusFlags[FStatus.First.Status] := False;
- Eventize(FStatus.First.Status, False);
- end;
- end;
- fsHelp : case x of
- 211, 214:
- begin
- FStatusFlags[FStatus.First.Status] := True;
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- FStatusFlags[FStatus.First.Status] := False;
- Eventize(FStatus.First.Status, False);
- end;
- end;
- fsList : case x of
- 125, 150: begin { do nothing } end;
- 226:
- begin
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- Eventize(FStatus.First.Status, False);
- end;
- end;
-
- fsMKD : case x of
- 250, 257:
- begin
- FStatusFlags[FStatus.First.Status] := True;
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- FStatusFlags[FStatus.First.Status] := False;
- Eventize(FStatus.First.Status, False);
- end;
- end;
-
- fsRMD,
- fsDEL : case x of
- 250:
- begin
- FStatusFlags[FStatus.First.Status] := True;
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- FStatusFlags[FStatus.First.Status] := False;
- Eventize(FStatus.First.Status, False);
- end;
- end;
-
- fsRNFR : case x of
- 350:
- begin
- FStatusFlags[FStatus.First.Status] := True;
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- Eventize(FStatus.First.Status, False);
- end;
- end;
-
- fsRNTO : case x of
- 250:
- begin
- FStatusFlags[FStatus.First.Status] := True;
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- Eventize(FStatus.First.Status, False);
- end;
- end;
- fsFeat : case x of
- 200..299:
- begin
- FStatusFlags[FStatus.First.Status] := True;
- EvaluateFeatures;
- Eventize(FStatus.First.Status, True);
- end;
- else
- begin
- FFeatureString := '';
- Eventize(FStatus.First.Status, False);
- end;
- end;
- end;
- end;
- if FStatus.Empty and not FCommandFront.Empty then
- ExecuteFrontCommand;
- end;
- procedure TLFTPClient.PasvPort;
- function StringPair(const aPort: Word): string;
- begin
- Result := IntToStr(aPort div 256);
- Result := Result + ',' + IntToStr(aPort mod 256);
- end;
-
- function StringIP: string;
- begin
- Result := StringReplace(FControl.Connection.Iterator.LocalAddress, '.', ',',
- [rfReplaceAll]) + ',';
- end;
-
- begin
- if FTransferMethod = ftActive then begin
- Writedbg(['Sent PORT']);
- FData.Disconnect(True);
- FData.Listen(FLastPort);
- FStatus.Insert(MakeStatusRec(fsPort, '', ''));
- FControl.SendMessage('PORT ' + StringIP + StringPair(FLastPort) + FLE);
- if FLastPort < 65535 then
- Inc(FLastPort)
- else
- FLastPort := FStartPort;
- end else begin
- Writedbg(['Sent PASV']);
- FStatus.Insert(MakeStatusRec(fsPasv, '', ''));
- FControl.SendMessage('PASV' + FLE);
- end;
- end;
- function TLFTPClient.User(const aUserName: string): Boolean;
- begin
- Result := not FPipeLine;
- if CanContinue(fsUser, aUserName, '') then begin
- FStatus.Insert(MakeStatusRec(fsUser, '', ''));
- FControl.SendMessage('USER ' + aUserName + FLE);
- Result := True;
- end;
- end;
- function TLFTPClient.Password(const aPassword: string): Boolean;
- begin
- Result := not FPipeLine;
- if CanContinue(fsPass, aPassword, '') then begin
- FStatus.Insert(MakeStatusRec(fsPass, '', ''));
- FControl.SendMessage('PASS ' + aPassword + FLE);
- Result := True;
- end;
- end;
- procedure TLFTPClient.SendChunk(const Event: Boolean);
- var
- Buf: array[0..65535] of Byte;
- n: Integer;
- Sent: Integer;
- begin
- repeat
- n := FStoreFile.Read(Buf, FChunkSize);
- if n > 0 then begin
- Sent := FData.Send(Buf, n);
- if Event and Assigned(FOnSent) and (Sent > 0) then
- FOnSent(FData.Iterator, Sent);
- if Sent < n then
- FStoreFile.Position := FStoreFile.Position - (n - Sent); // so it's tried next time
- end else begin
- if Assigned(FOnSent) then
- FOnSent(FData.Iterator, 0);
- FreeAndNil(FStoreFile);
- FSending := False;
- {$hint this one calls freeinstance which doesn't pass}
- FData.Disconnect(False);
- end;
- until (n = 0) or (Sent = 0);
- end;
- procedure TLFTPClient.ExecuteFrontCommand;
- begin
- with FCommandFront.First do
- case Status of
- fsNone : Exit;
- fsUser : User(Args[1]);
- fsPass : Password(Args[1]);
- fsList : List(Args[1]);
- fsRetr : Retrieve(Args[1]);
- fsStor : Put(Args[1]);
- fsCWD : ChangeDirectory(Args[1]);
- fsMKD : MakeDirectory(Args[1]);
- fsRMD : RemoveDirectory(Args[1]);
- fsDEL : DeleteFile(Args[1]);
- fsRNFR : Rename(Args[1], Args[2]);
- fsSYS : SystemInfo;
- fsPWD : PresentWorkingDirectory;
- fsHelp : Help(Args[1]);
- fsType : SetBinary(StrToBool(Args[1]));
- fsFeat : ListFeatures;
- end;
- FCommandFront.Remove;
- end;
- function TLFTPClient.Get(out aData; const aSize: Integer; aSocket: TLSocket): Integer;
- var
- s: string;
- begin
- Result := 0;
- if FControl.Get(aData, aSize, aSocket) > 0 then begin
- SetLength(s, Result);
- Move(aData, PChar(s)^, Result);
- Result := CleanInput(s);
- Move(s[1], aData, Min(Length(s), aSize));
- end;
- end;
- function TLFTPClient.GetMessage(out msg: string; aSocket: TLSocket): Integer;
- begin
- Result := FControl.GetMessage(msg, aSocket);
- if Result > 0 then
- Result := CleanInput(msg);
- end;
- function TLFTPClient.Send(const aData; const aSize: Integer; aSocket: TLSocket
- ): Integer;
- begin
- Result := FControl.Send(aData, aSize);
- end;
- function TLFTPClient.SendMessage(const msg: string; aSocket: TLSocket
- ): Integer;
- begin
- Result := FControl.SendMessage(msg);
- end;
- function TLFTPClient.GetData(out aData; const aSize: Integer): Integer;
- begin
- Result := FData.Iterator.Get(aData, aSize);
- end;
- function TLFTPClient.GetDataMessage: string;
- begin
- Result := '';
- if Assigned(FData.Iterator) then
- FData.Iterator.GetMessage(Result);
- end;
- function TLFTPClient.Connect(const aHost: string; const aPort: Word): Boolean;
- begin
- Result := False;
- Disconnect(True);
- if FControl.Connect(aHost, aPort) then begin
- FHost := aHost;
- FPort := aPort;
- FStatus.Insert(MakeStatusRec(fsCon, '', ''));
- Result := True;
- end;
- if FData.Eventer <> FControl.Connection.Eventer then
- FData.Eventer := FControl.Connection.Eventer;
- end;
- function TLFTPClient.Connect: Boolean;
- begin
- Result := Connect(FHost, FPort);
- end;
- function TLFTPClient.Authenticate(const aUsername, aPassword: string): Boolean;
- begin
- FPassword := aPassWord;
- Result := User(aUserName);
- end;
- function TLFTPClient.Retrieve(const FileName: string): Boolean;
- begin
- Result := not FPipeLine;
- if CanContinue(fsRetr, FileName, '') then begin
- PasvPort;
- FStatus.Insert(MakeStatusRec(fsRetr, '', ''));
- FControl.SendMessage('RETR ' + FileName + FLE);
- Result := True;
- end;
- end;
- function TLFTPClient.Put(const FileName: string): Boolean;
- begin
- Result := not FPipeLine;
- if FileExists(FileName) and CanContinue(fsStor, FileName, '') then begin
- FStoreFile := TFileStream.Create(FileName, fmOpenRead);
- PasvPort;
- FStatus.Insert(MakeStatusRec(fsStor, '', ''));
- FControl.SendMessage('STOR ' + ExtractFileName(FileName) + FLE);
- Result := True;
- end;
- end;
- function TLFTPClient.ChangeDirectory(const DestPath: string): Boolean;
- begin
- Result := not FPipeLine;
- if CanContinue(fsCWD, DestPath, '') then begin
- FStatus.Insert(MakeStatusRec(fsCWD, '', ''));
- FStatusFlags[fsCWD] := False;
- FControl.SendMessage('CWD ' + DestPath + FLE);
- Result := True;
- end;
- end;
- function TLFTPClient.MakeDirectory(const DirName: string): Boolean;
- begin
- Result := not FPipeLine;
- if CanContinue(fsMKD, DirName, '') then begin
- FStatus.Insert(MakeStatusRec(fsMKD, '', ''));
- FStatusFlags[fsMKD] := False;
- FControl.SendMessage('MKD ' + DirName + FLE);
- Result := True;
- end;
- end;
- function TLFTPClient.RemoveDirectory(const DirName: string): Boolean;
- begin
- Result := not FPipeLine;
- if CanContinue(fsRMD, DirName, '') then begin
- FStatus.Insert(MakeStatusRec(fsRMD, '', ''));
- FStatusFlags[fsRMD] := False;
- FControl.SendMessage('RMD ' + DirName + FLE);
- Result := True;
- end;
- end;
- function TLFTPClient.DeleteFile(const FileName: string): Boolean;
- begin
- Result := not FPipeLine;
- if CanContinue(fsDEL, FileName, '') then begin
- FStatus.Insert(MakeStatusRec(fsDEL, '', ''));
- FStatusFlags[fsDEL] := False;
- FControl.SendMessage('DELE ' + FileName + FLE);
- Result := True;
- end;
- end;
- function TLFTPClient.Rename(const FromName, ToName: string): Boolean;
- begin
- Result := not FPipeLine;
- if CanContinue(fsRNFR, FromName, ToName) then begin
- FStatus.Insert(MakeStatusRec(fsRNFR, '', ''));
- FStatusFlags[fsRNFR] := False;
- FControl.SendMessage('RNFR ' + FromName + FLE);
- FStatus.Insert(MakeStatusRec(fsRNTO, '', ''));
- FStatusFlags[fsRNTO] := False;
- FControl.SendMessage('RNTO ' + ToName + FLE);
- Result := True;
- end;
- end;
- procedure TLFTPClient.List(const FileName: string = '');
- begin
- if CanContinue(fsList, FileName, '') then begin
- PasvPort;
- FStatus.Insert(MakeStatusRec(fsList, '', ''));
- if Length(FileName) > 0 then
- FControl.SendMessage('LIST ' + FileName + FLE)
- else
- FControl.SendMessage('LIST' + FLE);
- end;
- end;
- procedure TLFTPClient.Nlst(const FileName: string);
- begin
- if CanContinue(fsList, FileName, '') then begin
- PasvPort;
- FStatus.Insert(MakeStatusRec(fsList, '', ''));
- if Length(FileName) > 0 then
- FControl.SendMessage('NLST ' + FileName + FLE)
- else
- FControl.SendMessage('NLST' + FLE);
- end;
- end;
- procedure TLFTPClient.SystemInfo;
- begin
- if CanContinue(fsSYS, '', '') then
- FControl.SendMessage('SYST' + FLE);
- end;
- procedure TLFTPClient.ListFeatures;
- begin
- if CanContinue(fsFeat, '', '') then begin
- FStatus.Insert(MakeStatusRec(fsFeat, '', ''));
- FControl.SendMessage('FEAT' + FLE);
- end;
- end;
- procedure TLFTPClient.PresentWorkingDirectory;
- begin
- if CanContinue(fsPWD, '', '') then begin
- FStatus.Insert(MakeStatusRec(fsPWD, '', ''));
- FControl.SendMessage('PWD' + FLE);
- end;
- end;
- procedure TLFTPClient.Help(const Arg: string);
- begin
- if CanContinue(fsHelp, Arg, '') then begin
- FStatus.Insert(MakeStatusRec(fsHelp, Arg, ''));
- FControl.SendMessage('HELP ' + Arg + FLE);
- end;
- end;
- procedure TLFTPClient.Disconnect(const Forced: Boolean = True);
- begin
- FControl.Disconnect(Forced);
- FStatus.Clear;
- FData.Disconnect(Forced);
- FLastPort := FStartPort;
- ClearStatusFlags;
- FCommandFront.Clear;
- end;
- procedure TLFTPClient.CallAction;
- begin
- TLFTPTelnetClient(FControl).CallAction;
- end;
- initialization
- Randomize;
- end.
|