lftp.pp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116
  1. { lFTP CopyRight (C) 2005-2007 Ales Katona
  2. This library is Free software; you can rediStribute it and/or modify it
  3. under the terms of the GNU Library General Public License as published by
  4. the Free Software Foundation; either version 2 of the License, or (at your
  5. option) any later version.
  6. This program is diStributed in the hope that it will be useful, but WITHOUT
  7. ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
  8. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  9. for more details.
  10. You should have received a Copy of the GNU Library General Public License
  11. along with This library; if not, Write to the Free Software Foundation,
  12. Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  13. This license has been modified. See File LICENSE for more inFormation.
  14. Should you find these sources withOut a LICENSE File, please contact
  15. me at [email protected]
  16. }
  17. unit lFTP;
  18. {$mode objfpc}{$H+}
  19. {$inline on}
  20. {$macro on}
  21. //{$define debug}
  22. interface
  23. uses
  24. Classes, lNet, lTelnet;
  25. const
  26. DEFAULT_FTP_PORT = 1025;
  27. type
  28. TLFTP = class;
  29. TLFTPClient = class;
  30. TLFTPStatus = (fsNone, fsCon, fsUser, fsPass, fsPasv, fsPort, fsList, fsRetr,
  31. fsStor, fsType, fsCWD, fsMKD, fsRMD, fsDEL, fsRNFR, fsRNTO,
  32. fsSYS, fsFeat, fsPWD, fsHelp, fsLast);
  33. TLFTPStatusSet = set of TLFTPStatus;
  34. TLFTPStatusRec = record
  35. Status: TLFTPStatus;
  36. Args: array[1..2] of string;
  37. end;
  38. TLFTPTransferMethod = (ftActive, ftPassive);
  39. TLFTPClientStatusEvent = procedure (aSocket: TLSocket;
  40. const aStatus: TLFTPStatus) of object;
  41. { TLFTPStatusStack }
  42. { TLFTPStatusFront }
  43. {$DEFINE __front_type__ := TLFTPStatusRec}
  44. {$i lcontainersh.inc}
  45. TLFTPStatusFront = TLFront;
  46. TLFTP = class(TLComponent, ILDirect)
  47. protected
  48. FControl: TLTelnetClient;
  49. FData: TLTcp;//TLTcpList;
  50. FSending: Boolean;
  51. FTransferMethod: TLFTPTransferMethod;
  52. function GetConnected: Boolean; virtual;
  53. function GetTimeout: Integer;
  54. procedure SetTimeout(const Value: Integer);
  55. function GetSocketClass: TLSocketClass;
  56. procedure SetSocketClass(Value: TLSocketClass);
  57. public
  58. constructor Create(aOwner: TComponent); override;
  59. destructor Destroy; override;
  60. function Get(var aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; virtual; abstract;
  61. function GetMessage(out msg: string; aSocket: TLSocket = nil): Integer; virtual; abstract;
  62. function Send(const aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; virtual; abstract;
  63. function SendMessage(const msg: string; aSocket: TLSocket = nil): Integer; virtual; abstract;
  64. public
  65. property Connected: Boolean read GetConnected;
  66. property Timeout: Integer read GetTimeout write SetTimeout;
  67. property SocketClass: TLSocketClass read GetSocketClass write SetSocketClass;
  68. property ControlConnection: TLTelnetClient read FControl;
  69. property DataConnection: TLTCP read FData;
  70. property TransferMethod: TLFTPTransferMethod read FTransferMethod write FTransferMethod default ftPassive;
  71. end;
  72. { TLFTPTelnetClient }
  73. TLFTPTelnetClient = class(TLTelnetClient)
  74. protected
  75. procedure React(const Operation, Command: Char); override;
  76. end;
  77. { TLFTPClient }
  78. TLFTPClient = class(TLFTP, ILClient)
  79. protected
  80. FStatus: TLFTPStatusFront;
  81. FCommandFront: TLFTPStatusFront;
  82. FStoreFile: TFileStream;
  83. FExpectedBinary: Boolean;
  84. FPipeLine: Boolean;
  85. FPassword: string;
  86. FStatusFlags: array[TLFTPStatus] of Boolean;
  87. FOnError: TLSocketErrorEvent;
  88. FOnReceive: TLSocketEvent;
  89. FOnSent: TLSocketProgressEvent;
  90. FOnControl: TLSocketEvent;
  91. FOnConnect: TLSocketEvent;
  92. FOnSuccess: TLFTPClientStatusEvent;
  93. FOnFailure: TLFTPClientStatusEvent;
  94. FChunkSize: Word;
  95. FLastPort: Word;
  96. FStartPort: Word;
  97. FStatusSet: TLFTPStatusSet;
  98. FSL: TStringList; // for evaluation, I want to prevent constant create/free
  99. procedure OnRe(aSocket: TLSocket);
  100. procedure OnDs(aSocket: TLSocket);
  101. procedure OnSe(aSocket: TLSocket);
  102. procedure OnEr(const msg: string; aSocket: TLSocket);
  103. procedure OnControlEr(const msg: string; aSocket: TLSocket);
  104. procedure OnControlRe(aSocket: TLSocket);
  105. procedure OnControlCo(aSocket: TLSocket);
  106. procedure OnControlDs(aSocket: TLSocket);
  107. function GetTransfer: Boolean;
  108. function GetEcho: Boolean;
  109. procedure SetEcho(const Value: Boolean);
  110. function GetConnected: Boolean; override;
  111. function GetBinary: Boolean;
  112. procedure SetBinary(const Value: Boolean);
  113. function CanContinue(const aStatus: TLFTPStatus; const Arg1, Arg2: string): Boolean;
  114. function CleanInput(var s: string): Integer;
  115. procedure SetStartPor(const Value: Word);
  116. procedure EvaluateAnswer(const Ans: string);
  117. procedure PasvPort;
  118. function User(const aUserName: string): Boolean;
  119. function Password(const aPassword: string): Boolean;
  120. procedure SendChunk(const Event: Boolean);
  121. procedure ExecuteFrontCommand;
  122. public
  123. constructor Create(aOwner: TComponent); override;
  124. destructor Destroy; override;
  125. function Get(var aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; override;
  126. function GetMessage(out msg: string; aSocket: TLSocket = nil): Integer; override;
  127. function Send(const aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; override;
  128. function SendMessage(const msg: string; aSocket: TLSocket = nil): Integer; override;
  129. function Connect(const aHost: string; const aPort: Word = 21): Boolean; virtual; overload;
  130. function Connect: Boolean; virtual; overload;
  131. function Authenticate(const aUsername, aPassword: string): Boolean;
  132. function GetData(var aData; const aSize: Integer): Integer;
  133. function GetDataMessage: string;
  134. function Retrieve(const FileName: string): Boolean;
  135. function Put(const FileName: string): Boolean; virtual; // because of LCLsocket
  136. function ChangeDirectory(const DestPath: string): Boolean;
  137. function MakeDirectory(const DirName: string): Boolean;
  138. function RemoveDirectory(const DirName: string): Boolean;
  139. function DeleteFile(const FileName: string): Boolean;
  140. function Rename(const FromName, ToName: string): Boolean;
  141. public
  142. procedure List(const FileName: string = '');
  143. procedure Nlst(const FileName: string = '');
  144. procedure SystemInfo;
  145. procedure FeatureList;
  146. procedure PresentWorkingDirectory;
  147. procedure Help(const Arg: string);
  148. procedure Disconnect; override;
  149. procedure CallAction; override;
  150. public
  151. property StatusSet: TLFTPStatusSet read FStatusSet write FStatusSet;
  152. property ChunkSize: Word read FChunkSize write FChunkSize;
  153. property Binary: Boolean read GetBinary write SetBinary;
  154. property PipeLine: Boolean read FPipeLine write FPipeLine;
  155. property Echo: Boolean read GetEcho write SetEcho;
  156. property StartPort: Word read FStartPort write FStartPort default DEFAULT_FTP_PORT;
  157. property Transfer: Boolean read GetTransfer;
  158. property OnError: TLSocketErrorEvent read FOnError write FOnError;
  159. property OnConnect: TLSocketEvent read FOnConnect write FOnConnect;
  160. property OnSent: TLSocketProgressEvent read FOnSent write FOnSent;
  161. property OnReceive: TLSocketEvent read FOnReceive write FOnReceive;
  162. property OnControl: TLSocketEvent read FOnControl write FOnControl;
  163. property OnSuccess: TLFTPClientStatusEvent read FOnSuccess write FOnSuccess;
  164. property OnFailure: TLFTPClientStatusEvent read FOnFailure write FOnFailure;
  165. end;
  166. function FTPStatusToStr(const aStatus: TLFTPStatus): string;
  167. implementation
  168. uses
  169. SysUtils;
  170. const
  171. FLE = #13#10;
  172. EMPTY_REC: TLFTPStatusRec = (Status: fsNone; Args: ('', ''));
  173. FTPStatusStr: array[TLFTPStatus] of string = ('None', 'Connect', 'Authenticate', 'Password',
  174. 'Passive', 'Active', 'List', 'Retrieve',
  175. 'Store', 'Type', 'CWD', 'MKDIR',
  176. 'RMDIR', 'Delete', 'RenameFrom',
  177. 'RenameTo', 'System', 'Features',
  178. 'PWD', 'HELP', 'LAST');
  179. procedure Writedbg(const ar: array of const);
  180. {$ifdef debug}
  181. var
  182. i: Integer;
  183. begin
  184. if High(ar) >= 0 then
  185. for i := 0 to High(ar) do
  186. case ar[i].vtype of
  187. vtInteger: Write(ar[i].vinteger);
  188. vtString: Write(ar[i].vstring^);
  189. vtAnsiString: Write(AnsiString(ar[i].vpointer));
  190. vtBoolean: Write(ar[i].vboolean);
  191. vtChar: Write(ar[i].vchar);
  192. vtExtended: Write(Extended(ar[i].vpointer^));
  193. end;
  194. Writeln;
  195. end;
  196. {$else}
  197. begin
  198. end;
  199. {$endif}
  200. function MakeStatusRec(const aStatus: TLFTPStatus; const Arg1, Arg2: string): TLFTPStatusRec;
  201. begin
  202. Result.Status := aStatus;
  203. Result.Args[1] := Arg1;
  204. Result.Args[2] := Arg2;
  205. end;
  206. function FTPStatusToStr(const aStatus: TLFTPStatus): string;
  207. begin
  208. Result := FTPStatusStr[aStatus];
  209. end;
  210. {$i lcontainers.inc}
  211. { TLFTP }
  212. function TLFTP.GetConnected: Boolean;
  213. begin
  214. Result := FControl.Connected;
  215. end;
  216. function TLFTP.GetTimeout: Integer;
  217. begin
  218. Result := FControl.Timeout;
  219. end;
  220. procedure TLFTP.SetTimeout(const Value: Integer);
  221. begin
  222. FControl.Timeout := Value;
  223. FData.Timeout := Value;
  224. end;
  225. function TLFTP.GetSocketClass: TLSocketClass;
  226. begin
  227. Result := FControl.SocketClass;
  228. end;
  229. procedure TLFTP.SetSocketClass(Value: TLSocketClass);
  230. begin
  231. FControl.SocketClass := Value;
  232. FData.SocketClass := Value;
  233. end;
  234. constructor TLFTP.Create(aOwner: TComponent);
  235. begin
  236. inherited Create(aOwner);
  237. FHost := '';
  238. FPort := 21;
  239. FControl := TLFTPTelnetClient.Create(nil);
  240. FData := TLTcp.Create(nil);
  241. FTransferMethod := ftPassive; // let's be modern
  242. end;
  243. destructor TLFTP.Destroy;
  244. begin
  245. FControl.Free;
  246. FData.Free;
  247. inherited Destroy;
  248. end;
  249. { TLFTPTelnetClient }
  250. procedure TLFTPTelnetClient.React(const Operation, Command: Char);
  251. begin
  252. // don't do a FUCK since they broke Telnet in FTP as per-usual
  253. end;
  254. { TLFTPClient }
  255. constructor TLFTPClient.Create(aOwner: TComponent);
  256. const
  257. DEFAULT_CHUNK = 8192;
  258. var
  259. s: TLFTPStatus;
  260. begin
  261. inherited Create(aOwner);
  262. FControl.OnReceive := @OnControlRe;
  263. FControl.OnConnect := @OnControlCo;
  264. FControl.OnError := @OnControlEr;
  265. FControl.OnDisconnect := @OnControlDs;
  266. FData.OnReceive := @OnRe;
  267. FData.OnDisconnect := @OnDs;
  268. FData.OnCanSend := @OnSe;
  269. FData.OnError := @OnEr;
  270. FStatusSet := []; // empty Event set
  271. FPassWord := '';
  272. FChunkSize := DEFAULT_CHUNK;
  273. FStartPort := DEFAULT_FTP_PORT;
  274. FSL := TStringList.Create;
  275. FLastPort := FStartPort;
  276. for s := fsNone to fsDEL do
  277. FStatusFlags[s] := False;
  278. FStatus := TLFTPStatusFront.Create(EMPTY_REC);
  279. FCommandFront := TLFTPStatusFront.Create(EMPTY_REC);
  280. FStoreFile := nil;
  281. end;
  282. destructor TLFTPClient.Destroy;
  283. begin
  284. Disconnect;
  285. FSL.Free;
  286. FStatus.Free;
  287. FCommandFront.Free;
  288. if Assigned(FStoreFile) then
  289. FreeAndNil(FStoreFile);
  290. inherited Destroy;
  291. end;
  292. procedure TLFTPClient.OnRe(aSocket: TLSocket);
  293. begin
  294. if Assigned(FOnReceive) then
  295. FOnReceive(aSocket);
  296. end;
  297. procedure TLFTPClient.OnDs(aSocket: TLSocket);
  298. begin
  299. FSending := False;
  300. Writedbg(['Disconnected']);
  301. end;
  302. procedure TLFTPClient.OnSe(aSocket: TLSocket);
  303. begin
  304. if Connected and FSending then
  305. SendChunk(True);
  306. end;
  307. procedure TLFTPClient.OnEr(const msg: string; aSocket: TLSocket);
  308. begin
  309. FSending := False;
  310. if Assigned(FOnError) then
  311. FOnError(msg, aSocket);
  312. end;
  313. procedure TLFTPClient.OnControlEr(const msg: string; aSocket: TLSocket);
  314. begin
  315. FSending := False;
  316. if Assigned(FOnError) then
  317. FOnError(msg, aSocket);
  318. end;
  319. procedure TLFTPClient.OnControlRe(aSocket: TLSocket);
  320. begin
  321. if Assigned(FOnControl) then
  322. FOnControl(aSocket);
  323. end;
  324. procedure TLFTPClient.OnControlCo(aSocket: TLSocket);
  325. begin
  326. if Assigned(FOnConnect) then
  327. FOnConnect(aSocket);
  328. end;
  329. procedure TLFTPClient.OnControlDs(aSocket: TLSocket);
  330. begin
  331. if Assigned(FOnError) then
  332. FOnError('Connection lost', aSocket);
  333. end;
  334. function TLFTPClient.GetTransfer: Boolean;
  335. begin
  336. Result := FData.Connected;
  337. end;
  338. function TLFTPClient.GetEcho: Boolean;
  339. begin
  340. Result := FControl.OptionIsSet(TS_ECHO);
  341. end;
  342. function TLFTPClient.GetConnected: Boolean;
  343. begin
  344. Result := FStatusFlags[fsCon] and inherited;
  345. end;
  346. function TLFTPClient.GetBinary: Boolean;
  347. begin
  348. Result := FStatusFlags[fsType];
  349. end;
  350. function TLFTPClient.CanContinue(const aStatus: TLFTPStatus; const Arg1,
  351. Arg2: string): Boolean;
  352. begin
  353. Result := FPipeLine or FStatus.Empty;
  354. if not Result then
  355. FCommandFront.Insert(MakeStatusRec(aStatus, Arg1, Arg2));
  356. end;
  357. function TLFTPClient.CleanInput(var s: string): Integer;
  358. var
  359. i: Integer;
  360. begin
  361. FSL.Text := s;
  362. if FSL.Count > 0 then
  363. for i := 0 to FSL.Count-1 do
  364. if Length(FSL[i]) > 0 then EvaluateAnswer(FSL[i]);
  365. s := StringReplace(s, FLE, LineEnding, [rfReplaceAll]);
  366. i := Pos('PASS', s);
  367. if i > 0 then
  368. s := Copy(s, 1, i-1) + 'PASS';
  369. Result := Length(s);
  370. end;
  371. procedure TLFTPClient.SetStartPor(const Value: Word);
  372. begin
  373. FStartPort := Value;
  374. if Value > FLastPort then
  375. FLastPort := Value;
  376. end;
  377. procedure TLFTPClient.SetEcho(const Value: Boolean);
  378. begin
  379. if Value then
  380. FControl.SetOption(TS_ECHO)
  381. else
  382. FControl.UnSetOption(TS_ECHO);
  383. end;
  384. procedure TLFTPClient.SetBinary(const Value: Boolean);
  385. const
  386. TypeBool: array[Boolean] of string = ('A', 'I');
  387. begin
  388. if CanContinue(fsType, BoolToStr(Value), '') then begin
  389. FExpectedBinary := Value;
  390. FControl.SendMessage('TYPE ' + TypeBool[Value] + FLE);
  391. FStatus.Insert(MakeStatusRec(fsType, '', ''));
  392. end;
  393. end;
  394. procedure TLFTPClient.EvaluateAnswer(const Ans: string);
  395. function GetNum: Integer;
  396. begin
  397. try
  398. Result := StrToInt(Copy(Ans, 1, 3));
  399. except
  400. Result := -1;
  401. end;
  402. end;
  403. procedure ParsePortIP(s: string);
  404. var
  405. i, l: Integer;
  406. aIP: string;
  407. aPort: Word;
  408. sl: TStringList;
  409. begin
  410. if Length(s) >= 15 then begin
  411. sl := TStringList.Create;
  412. for i := Length(s) downto 5 do
  413. if s[i] = ',' then Break;
  414. while (i <= Length(s)) and (s[i] in ['0'..'9', ',']) do Inc(i);
  415. if not (s[i] in ['0'..'9', ',']) then Dec(i);
  416. l := 0;
  417. while s[i] in ['0'..'9', ','] do begin
  418. Inc(l);
  419. Dec(i);
  420. end;
  421. Inc(i);
  422. s := Copy(s, i, l);
  423. sl.CommaText := s;
  424. aIP := sl[0] + '.' + sl[1] + '.' + sl[2] + '.' + sl[3];
  425. try
  426. aPort := (StrToInt(sl[4]) * 256) + StrToInt(sl[5]);
  427. except
  428. aPort := 0;
  429. end;
  430. Writedbg(['Server PASV addr/port - ', aIP, ' : ', aPort]);
  431. if (aPort > 0) and FData.Connect(aIP, aPort) then
  432. Writedbg(['Connected after PASV']);
  433. sl.Free;
  434. FStatus.Remove;
  435. end;
  436. end;
  437. procedure SendFile;
  438. begin
  439. FStoreFile.Position := 0;
  440. FSending := True;
  441. SendChunk(False);
  442. end;
  443. function ValidResponse(const Answer: string): Boolean; inline;
  444. begin
  445. Result := (Length(Ans) >= 3) and
  446. (Ans[1] in ['1'..'5']) and
  447. (Ans[2] in ['0'..'9']) and
  448. (Ans[3] in ['0'..'9']);
  449. if Result then
  450. Result := (Length(Ans) = 3) or ((Length(Ans) > 3) and (Ans[4] = ' '));
  451. end;
  452. procedure Eventize(const aStatus: TLFTPStatus; const Res: Boolean);
  453. begin
  454. if Res then begin
  455. if Assigned(FOnSuccess) and (aStatus in FStatusSet) then
  456. FOnSuccess(FData.Iterator, aStatus);
  457. end else begin
  458. if Assigned(FOnFailure) and (aStatus in FStatusSet) then
  459. FOnFailure(FData.Iterator, aStatus);
  460. end;
  461. end;
  462. var
  463. x: Integer;
  464. begin
  465. x := GetNum;
  466. Writedbg(['WOULD EVAL: ', FTPStatusStr[FStatus.First.Status], ' with value: ',
  467. x, ' from "', Ans, '"']);
  468. if ValidResponse(Ans) then
  469. if not FStatus.Empty then begin
  470. Writedbg(['EVAL: ', FTPStatusStr[FStatus.First.Status], ' with value: ', x]);
  471. case FStatus.First.Status of
  472. fsCon : case x of
  473. 220:
  474. begin
  475. FStatusFlags[FStatus.First.Status] := True;
  476. Eventize(FStatus.First.Status, True);
  477. FStatus.Remove;
  478. end;
  479. else
  480. begin
  481. FStatusFlags[FStatus.First.Status] := False;
  482. Eventize(FStatus.First.Status, False);
  483. FStatus.Remove;
  484. end;
  485. end;
  486. fsUser : case x of
  487. 230:
  488. begin
  489. FStatusFlags[FStatus.First.Status] := True;
  490. Eventize(FStatus.First.Status, True);
  491. FStatus.Remove;
  492. end;
  493. 331,
  494. 332:
  495. begin
  496. FStatus.Remove;
  497. Password(FPassword);
  498. end;
  499. else
  500. begin
  501. FStatusFlags[FStatus.First.Status] := False;
  502. Eventize(FStatus.First.Status, False);
  503. FStatus.Remove;
  504. end;
  505. end;
  506. fsPass : case x of
  507. 230:
  508. begin
  509. FStatusFlags[FStatus.First.Status] := True;
  510. Eventize(FStatus.First.Status, True);
  511. FStatus.Remove;
  512. end;
  513. else
  514. begin
  515. FStatusFlags[FStatus.First.Status] := False;
  516. Eventize(FStatus.First.Status, False);
  517. FStatus.Remove;
  518. end;
  519. end;
  520. fsPasv : case x of
  521. 227: ParsePortIP(Ans);
  522. 300..600: FStatus.Remove;
  523. end;
  524. fsPort : case x of
  525. 200:
  526. begin
  527. Eventize(FStatus.First.Status, True);
  528. FStatus.Remove;
  529. end;
  530. else
  531. begin
  532. Eventize(FStatus.First.Status, False);
  533. FStatus.Remove;
  534. end;
  535. end;
  536. fsType : case x of
  537. 200:
  538. begin
  539. FStatusFlags[FStatus.First.Status] := FExpectedBinary;
  540. Writedbg(['Binary mode: ', FExpectedBinary]);
  541. Eventize(FStatus.First.Status, True);
  542. FStatus.Remove;
  543. end;
  544. else
  545. begin
  546. Eventize(FStatus.First.Status, False);
  547. FStatus.Remove;
  548. end;
  549. end;
  550. fsRetr : case x of
  551. 125, 150: begin { Do nothing } end;
  552. 226:
  553. begin
  554. Eventize(FStatus.First.Status, True);
  555. FStatus.Remove;
  556. end;
  557. else
  558. begin
  559. FData.Disconnect;
  560. Writedbg(['Disconnecting data connection']);
  561. Eventize(FStatus.First.Status, False);
  562. FStatus.Remove; // error after connection established
  563. end;
  564. end;
  565. fsStor : case x of
  566. 125, 150: SendFile;
  567. 226:
  568. begin
  569. Eventize(FStatus.First.Status, True);
  570. FStatus.Remove;
  571. end;
  572. else
  573. begin
  574. Eventize(FStatus.First.Status, True);
  575. FStatus.Remove;
  576. end;
  577. end;
  578. fsCWD : case x of
  579. 200, 250:
  580. begin
  581. FStatusFlags[FStatus.First.Status] := True;
  582. Eventize(FStatus.First.Status, True);
  583. FStatus.Remove;
  584. end;
  585. else
  586. begin
  587. FStatusFlags[FStatus.First.Status] := False;
  588. Eventize(FStatus.First.Status, False);
  589. FStatus.Remove;
  590. end;
  591. end;
  592. fsList : case x of
  593. 125, 150: begin { do nothing } end;
  594. 226:
  595. begin
  596. Eventize(FStatus.First.Status, True);
  597. FStatus.Remove;
  598. end;
  599. else
  600. begin
  601. Eventize(FStatus.First.Status, False);
  602. FStatus.Remove;
  603. end;
  604. end;
  605. fsMKD : case x of
  606. 250, 257:
  607. begin
  608. FStatusFlags[FStatus.First.Status] := True;
  609. Eventize(FStatus.First.Status, True);
  610. FStatus.Remove;
  611. end;
  612. else
  613. begin
  614. FStatusFlags[FStatus.First.Status] := False;
  615. Eventize(FStatus.First.Status, False);
  616. FStatus.Remove;
  617. end;
  618. end;
  619. fsRMD,
  620. fsDEL : case x of
  621. 250:
  622. begin
  623. FStatusFlags[FStatus.First.Status] := True;
  624. Eventize(FStatus.First.Status, True);
  625. FStatus.Remove;
  626. end;
  627. else
  628. begin
  629. FStatusFlags[FStatus.First.Status] := False;
  630. Eventize(FStatus.First.Status, False);
  631. FStatus.Remove;
  632. end;
  633. end;
  634. fsRNFR : case x of
  635. 350:
  636. begin
  637. FStatusFlags[FStatus.First.Status] := True;
  638. Eventize(FStatus.First.Status, True);
  639. FStatus.Remove;
  640. end;
  641. else
  642. begin
  643. Eventize(FStatus.First.Status, False);
  644. FStatus.Remove;
  645. end;
  646. end;
  647. fsRNTO : case x of
  648. 250:
  649. begin
  650. FStatusFlags[FStatus.First.Status] := True;
  651. Eventize(FStatus.First.Status, True);
  652. FStatus.Remove;
  653. end;
  654. else
  655. begin
  656. Eventize(FStatus.First.Status, False);
  657. FStatus.Remove;
  658. end;
  659. end;
  660. end;
  661. end;
  662. if FStatus.Empty and not FCommandFront.Empty then
  663. ExecuteFrontCommand;
  664. end;
  665. procedure TLFTPClient.PasvPort;
  666. function StringPair(const aPort: Word): string;
  667. begin
  668. Result := IntToStr(aPort div 256);
  669. Result := Result + ',' + IntToStr(aPort mod 256);
  670. end;
  671. function StringIP: string;
  672. begin
  673. Result := StringReplace(FControl.Connection.Iterator.LocalAddress, '.', ',',
  674. [rfReplaceAll]) + ',';
  675. end;
  676. begin
  677. if FTransferMethod = ftActive then begin
  678. Writedbg(['Sent PORT']);
  679. FData.Disconnect;
  680. FData.Listen(FLastPort);
  681. FControl.SendMessage('PORT ' + StringIP + StringPair(FLastPort) + FLE);
  682. FStatus.Insert(MakeStatusRec(fsPort, '', ''));
  683. if FLastPort < 65535 then
  684. Inc(FLastPort)
  685. else
  686. FLastPort := FStartPort;
  687. end else begin
  688. Writedbg(['Sent PASV']);
  689. FControl.SendMessage('PASV' + FLE);
  690. FStatus.Insert(MakeStatusRec(fsPasv, '', ''));
  691. end;
  692. end;
  693. function TLFTPClient.User(const aUserName: string): Boolean;
  694. begin
  695. Result := not FPipeLine;
  696. if CanContinue(fsUser, aUserName, '') then begin
  697. FControl.SendMessage('USER ' + aUserName + FLE);
  698. FStatus.Insert(MakeStatusRec(fsUser, '', ''));
  699. Result := True;
  700. end;
  701. end;
  702. function TLFTPClient.Password(const aPassword: string): Boolean;
  703. begin
  704. Result := not FPipeLine;
  705. if CanContinue(fsPass, aPassword, '') then begin
  706. FControl.SendMessage('PASS ' + aPassword + FLE);
  707. FStatus.Insert(MakeStatusRec(fsPass, '', ''));
  708. Result := True;
  709. end;
  710. end;
  711. procedure TLFTPClient.SendChunk(const Event: Boolean);
  712. var
  713. Buf: array[0..65535] of Byte;
  714. n: Integer;
  715. Sent: Integer;
  716. begin
  717. repeat
  718. n := FStoreFile.Read(Buf, FChunkSize);
  719. if n > 0 then begin
  720. Sent := FData.Send(Buf, n);
  721. if Event and Assigned(FOnSent) and (Sent > 0) then
  722. FOnSent(FData.Iterator, Sent);
  723. if Sent < n then
  724. FStoreFile.Position := FStoreFile.Position - (n - Sent); // so it's tried next time
  725. end else begin
  726. if Assigned(FOnSent) then
  727. FOnSent(FData.Iterator, 0);
  728. FreeAndNil(FStoreFile);
  729. FSending := False;
  730. {$hint this one calls freeinstance which doesn't pass}
  731. FData.Disconnect;
  732. end;
  733. until (n = 0) or (Sent = 0);
  734. end;
  735. procedure TLFTPClient.ExecuteFrontCommand;
  736. begin
  737. with FCommandFront.First do
  738. case Status of
  739. fsNone : Exit;
  740. fsUser : User(Args[1]);
  741. fsPass : Password(Args[1]);
  742. fsList : List(Args[1]);
  743. fsRetr : Retrieve(Args[1]);
  744. fsStor : Put(Args[1]);
  745. fsCWD : ChangeDirectory(Args[1]);
  746. fsMKD : MakeDirectory(Args[1]);
  747. fsRMD : RemoveDirectory(Args[1]);
  748. fsDEL : DeleteFile(Args[1]);
  749. fsRNFR : Rename(Args[1], Args[2]);
  750. fsSYS : SystemInfo;
  751. fsPWD : PresentWorkingDirectory;
  752. fsHelp : Help(Args[1]);
  753. fsType : SetBinary(StrToBool(Args[1]));
  754. fsFeat : FeatureList;
  755. end;
  756. FCommandFront.Remove;
  757. end;
  758. function TLFTPClient.Get(var aData; const aSize: Integer; aSocket: TLSocket): Integer;
  759. var
  760. s: string;
  761. begin
  762. Result := FControl.Get(aData, aSize, aSocket);
  763. if Result > 0 then begin
  764. SetLength(s, Result);
  765. Move(aData, PChar(s)^, Result);
  766. CleanInput(s);
  767. end;
  768. end;
  769. function TLFTPClient.GetMessage(out msg: string; aSocket: TLSocket): Integer;
  770. begin
  771. Result := FControl.GetMessage(msg, aSocket);
  772. if Result > 0 then
  773. Result := CleanInput(msg);
  774. end;
  775. function TLFTPClient.Send(const aData; const aSize: Integer; aSocket: TLSocket
  776. ): Integer;
  777. begin
  778. Result := FControl.Send(aData, aSize);
  779. end;
  780. function TLFTPClient.SendMessage(const msg: string; aSocket: TLSocket
  781. ): Integer;
  782. begin
  783. Result := FControl.SendMessage(msg);
  784. end;
  785. function TLFTPClient.GetData(var aData; const aSize: Integer): Integer;
  786. begin
  787. Result := FData.Iterator.Get(aData, aSize);
  788. end;
  789. function TLFTPClient.GetDataMessage: string;
  790. begin
  791. Result := '';
  792. if Assigned(FData.Iterator) then
  793. FData.Iterator.GetMessage(Result);
  794. end;
  795. function TLFTPClient.Connect(const aHost: string; const aPort: Word): Boolean;
  796. begin
  797. Result := False;
  798. Disconnect;
  799. if FControl.Connect(aHost, aPort) then begin
  800. FHost := aHost;
  801. FPort := aPort;
  802. FStatus.Insert(MakeStatusRec(fsCon, '', ''));
  803. Result := True;
  804. end;
  805. if FData.Eventer <> FControl.Connection.Eventer then
  806. FData.Eventer := FControl.Connection.Eventer;
  807. end;
  808. function TLFTPClient.Connect: Boolean;
  809. begin
  810. Result := Connect(FHost, FPort);
  811. end;
  812. function TLFTPClient.Authenticate(const aUsername, aPassword: string): Boolean;
  813. begin
  814. FPassword := aPassWord;
  815. Result := User(aUserName);
  816. end;
  817. function TLFTPClient.Retrieve(const FileName: string): Boolean;
  818. begin
  819. Result := not FPipeLine;
  820. if CanContinue(fsRetr, FileName, '') then begin
  821. PasvPort;
  822. FControl.SendMessage('RETR ' + FileName + FLE);
  823. FStatus.Insert(MakeStatusRec(fsRetr, '', ''));
  824. Result := True;
  825. end;
  826. end;
  827. function TLFTPClient.Put(const FileName: string): Boolean;
  828. begin
  829. Result := not FPipeLine;
  830. if FileExists(FileName) and CanContinue(fsStor, FileName, '') then begin
  831. FStoreFile := TFileStream.Create(FileName, fmOpenRead);
  832. PasvPort;
  833. FControl.SendMessage('STOR ' + ExtractFileName(FileName) + FLE);
  834. FStatus.Insert(MakeStatusRec(fsStor, '', ''));
  835. Result := True;
  836. end;
  837. end;
  838. function TLFTPClient.ChangeDirectory(const DestPath: string): Boolean;
  839. begin
  840. Result := not FPipeLine;
  841. if CanContinue(fsCWD, DestPath, '') then begin
  842. FControl.SendMessage('CWD ' + DestPath + FLE);
  843. FStatus.Insert(MakeStatusRec(fsCWD, '', ''));
  844. FStatusFlags[fsCWD] := False;
  845. Result := True;
  846. end;
  847. end;
  848. function TLFTPClient.MakeDirectory(const DirName: string): Boolean;
  849. begin
  850. Result := not FPipeLine;
  851. if CanContinue(fsMKD, DirName, '') then begin
  852. FControl.SendMessage('MKD ' + DirName + FLE);
  853. FStatus.Insert(MakeStatusRec(fsMKD, '', ''));
  854. FStatusFlags[fsMKD] := False;
  855. Result := True;
  856. end;
  857. end;
  858. function TLFTPClient.RemoveDirectory(const DirName: string): Boolean;
  859. begin
  860. Result := not FPipeLine;
  861. if CanContinue(fsRMD, DirName, '') then begin
  862. FControl.SendMessage('RMD ' + DirName + FLE);
  863. FStatus.Insert(MakeStatusRec(fsRMD, '', ''));
  864. FStatusFlags[fsRMD] := False;
  865. Result := True;
  866. end;
  867. end;
  868. function TLFTPClient.DeleteFile(const FileName: string): Boolean;
  869. begin
  870. Result := not FPipeLine;
  871. if CanContinue(fsDEL, FileName, '') then begin
  872. FControl.SendMessage('DELE ' + FileName + FLE);
  873. FStatus.Insert(MakeStatusRec(fsDEL, '', ''));
  874. FStatusFlags[fsDEL] := False;
  875. Result := True;
  876. end;
  877. end;
  878. function TLFTPClient.Rename(const FromName, ToName: string): Boolean;
  879. begin
  880. Result := not FPipeLine;
  881. if CanContinue(fsRNFR, FromName, ToName) then begin
  882. FControl.SendMessage('RNFR ' + FromName + FLE);
  883. FStatus.Insert(MakeStatusRec(fsRNFR, '', ''));
  884. FStatusFlags[fsRNFR] := False;
  885. FControl.SendMessage('RNTO ' + ToName + FLE);
  886. FStatus.Insert(MakeStatusRec(fsRNTO, '', ''));
  887. FStatusFlags[fsRNTO] := False;
  888. Result := True;
  889. end;
  890. end;
  891. procedure TLFTPClient.List(const FileName: string = '');
  892. begin
  893. if CanContinue(fsList, FileName, '') then begin
  894. PasvPort;
  895. FStatus.Insert(MakeStatusRec(fsList, '', ''));
  896. if Length(FileName) > 0 then
  897. FControl.SendMessage('LIST ' + FileName + FLE)
  898. else
  899. FControl.SendMessage('LIST' + FLE);
  900. end;
  901. end;
  902. procedure TLFTPClient.Nlst(const FileName: string);
  903. begin
  904. if CanContinue(fsList, FileName, '') then begin
  905. PasvPort;
  906. FStatus.Insert(MakeStatusRec(fsList, '', ''));
  907. if Length(FileName) > 0 then
  908. FControl.SendMessage('NLST ' + FileName + FLE)
  909. else
  910. FControl.SendMessage('NLST' + FLE);
  911. end;
  912. end;
  913. procedure TLFTPClient.SystemInfo;
  914. begin
  915. if CanContinue(fsSYS, '', '') then
  916. FControl.SendMessage('SYST' + FLE);
  917. end;
  918. procedure TLFTPClient.FeatureList;
  919. begin
  920. if CanContinue(fsFeat, '', '') then
  921. FControl.SendMessage('FEAT' + FLE);
  922. end;
  923. procedure TLFTPClient.PresentWorkingDirectory;
  924. begin
  925. if CanContinue(fsPWD, '', '') then
  926. FControl.SendMessage('PWD' + FLE);
  927. end;
  928. procedure TLFTPClient.Help(const Arg: string);
  929. begin
  930. if CanContinue(fsHelp, Arg, '') then
  931. FControl.SendMessage('HELP ' + Arg + FLE);
  932. end;
  933. procedure TLFTPClient.Disconnect;
  934. var
  935. s: TLFTPStatus;
  936. begin
  937. FControl.Disconnect;
  938. FStatus.Clear;
  939. FData.Disconnect;
  940. FLastPort := FStartPort;
  941. for s := fsNone to fsLast do
  942. FStatusFlags[s] := False;
  943. FCommandFront.Clear;
  944. end;
  945. procedure TLFTPClient.CallAction;
  946. begin
  947. TLFTPTelnetClient(FControl).CallAction;
  948. end;
  949. initialization
  950. Randomize;
  951. end.