lftp.pp 34 KB

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