IdFTP.pas 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10161: IdFTP.pas
  11. {
  12. { Rev 1.9 7/23/04 6:06:52 PM RLebeau
  13. { Bug fix in Get() for TFileStream access rights
  14. }
  15. {
  16. { Rev 1.8 7/13/04 6:17:06 PM RLebeau
  17. { Renamed DefaultDataPort property to DataPort and added support for new
  18. { DataPortMin/Max properties
  19. }
  20. {
  21. { Rev 1.7 7/13/04 5:38:56 PM RLebeau
  22. { Added DefaultDataPort property
  23. }
  24. {
  25. { Rev 1.6 7/9/04 1:49:18 PM RLebeau
  26. { Bug fix for OnParseCustomListFormat event handler begin lost whenever List()
  27. { is called.
  28. }
  29. {
  30. { Rev 1.5 1/27/2004 10:18:18 PM JPMugaas
  31. { Fix from Steve Loft for a server that sends something like this:
  32. { "227 Passive mode OK (195,92,195,164,4,99 )"
  33. }
  34. {
  35. Rev 1.4 3/19/2003 2:40:18 PM BGooijen
  36. The IOHandler of the datachannel was not freed
  37. }
  38. {
  39. Rev 1.3 3/19/2003 1:41:26 PM BGooijen
  40. Fixed datachannel over socks connection (uploading files)
  41. }
  42. {
  43. Rev 1.2 3/13/2003 10:54:56 AM BGooijen
  44. The transfertype is now set in .login, instead of in .connect, when autologin
  45. = true
  46. }
  47. {
  48. Rev 1.1 3/12/2003 12:48:00 PM BGooijen
  49. Fixed datachannel over socks connection
  50. }
  51. {
  52. { Rev 1.0 2002.11.12 10:38:30 PM czhower
  53. }
  54. unit IdFTP;
  55. {
  56. Change Log:
  57. 2002-09-18 - Remy Lebeau
  58. - added AFromBeginning parameter to InternalPut to correctly honor the AAppend parameter of Put
  59. 2002-01-xx - Andrew P.Rybin
  60. - Proxy support, OnAfterGet (ex:decrypt, set srv timestamp)
  61. - J.Peter Mugaas: not readonly ProxySettings
  62. A Neillans - 10/17/2001
  63. Merged changes submitted by Andrew P.Rybin
  64. Correct command case problems - some servers expect commands in Uppercase only.
  65. SP - 06/08/2001
  66. Added a few more functions
  67. Doychin - 02/18/2001
  68. OnAfterLogin event handler and Login method
  69. OnAfterLogin is executed after successfull login but before setting up the
  70. connection properties. This event can be used to provide FTP proxy support
  71. from the user application. Look at the FTP demo program for more information
  72. on how to provide such support.
  73. Doychin - 02/17/2001
  74. New onFTPStatus event
  75. New Quote method for executing commands not implemented by the compoent
  76. -CleanDir contributed by Amedeo Lanza
  77. TODO: Chage the FTP demo to demonstrate the use of the new events and add proxy support
  78. }
  79. interface
  80. uses
  81. Classes,
  82. IdAssignedNumbers, IdException, IdRFCReply,
  83. IdSocketHandle, IdTCPConnection, IdTCPClient, IdThread, IdFTPList, IdFTPCommon, IdGlobal;
  84. type
  85. //Added by SP
  86. TIdCreateFTPList = procedure(ASender: TObject; Var VFTPList: TIdFTPListItems) of object;
  87. TIdCheckListFormat = procedure(ASender: TObject; const ALine: String; Var VListFormat: TIdFTPListFormat) of object;
  88. TOnAfterClientLogin = TNotifyEvent;
  89. TIdFtpAfterGet = procedure (ASender: TObject; VStream: TStream) of object; //APR
  90. const
  91. Id_TIdFTP_TransferType = ftBinary;
  92. Id_TIdFTP_Passive = False;
  93. type
  94. //APR 011216:
  95. TIdFtpProxyType = (fpcmNone,//Connect method:
  96. fpcmUserSite, //Send command USER user@hostname
  97. fpcmSite, //Send command SITE (with logon)
  98. fpcmOpen, //Send command OPEN
  99. fpcmUserPass,//USER user@firewalluser@hostname / PASS pass@firewallpass
  100. fpcmTransparent, //First use the USER and PASS command with the firewall username and password, and then with the target host username and password.
  101. fpcmHttpProxyWithFtp //HTTP Proxy with FTP support. Will be supported in Indy 10
  102. ); //TIdFtpProxyType
  103. TIdFtpProxySettings = class (TPersistent)
  104. protected
  105. FHost, FUserName, FPassword: String;
  106. FProxyType: TIdFtpProxyType;
  107. FPort: Integer;
  108. public
  109. procedure Assign(Source: TPersistent); override;
  110. published
  111. property ProxyType: TIdFtpProxyType read FProxyType write FProxyType;
  112. property Host: String read FHost write FHost;
  113. property UserName: String read FUserName write FUserName;
  114. property Password: String read FPassword write FPassword;
  115. property Port: Integer read FPort write FPort;
  116. End;//TIdFtpProxySettings
  117. TIdFTP = class(TIdTCPClient)
  118. protected
  119. FCanResume: Boolean;
  120. FListResult: TStrings;
  121. FLoginMsg: TIdRFCReply;
  122. FPassive: boolean;
  123. FResumeTested: Boolean;
  124. FSystemDesc: string;
  125. FTransferType: TIdFTPTransferType;
  126. FDataChannel: TIdTCPConnection;
  127. FDataPort: Integer;
  128. FDataPortMin: Integer;
  129. FDataPortMax: Integer;
  130. FDirectoryListing: TIdFTPListItems;
  131. FOnAfterClientLogin: TNotifyEvent;
  132. FOnCreateFTPList: TIdCreateFTPList;
  133. FOnCheckListFormat: TIdCheckListFormat;
  134. FOnParseCustomListFormat: TIdOnParseCustomListFormat;
  135. FOnAfterGet: TIdFtpAfterGet; //APR
  136. FProxySettings: TIdFtpProxySettings;
  137. //
  138. procedure ConstructDirListing;
  139. procedure DoAfterLogin;
  140. procedure DoFTPList;
  141. procedure DoCheckListFormat(const ALine: String);
  142. function GetDirectoryListing: TIdFTPListItems;
  143. procedure InitDataChannel;
  144. procedure InternalGet(const ACommand: string; ADest: TStream; AResume: Boolean = false);
  145. procedure InternalPut(const ACommand: string; ASource: TStream; AFromBeginning: Boolean = true);
  146. procedure SetOnParseCustomListFormat(const AValue: TIdOnParseCustomListFormat);
  147. procedure SendPassive(var VIP: string; var VPort: integer);
  148. procedure SendPort(AHandle: TIdSocketHandle);
  149. procedure SetProxySettings(const Value: TIdFtpProxySettings);
  150. procedure SendTransferType;
  151. procedure SetTransferType(AValue: TIdFTPTransferType);
  152. procedure DoAfterGet (AStream: TStream); virtual; //APR
  153. public
  154. procedure Abort; virtual;
  155. procedure Account(AInfo: String);
  156. procedure Allocate(AAllocateBytes: Integer);
  157. procedure ChangeDir(const ADirName: string);
  158. procedure ChangeDirUp;
  159. procedure Connect(AAutoLogin: boolean = True; const ATimeout: Integer = IdTimeoutDefault); reintroduce;
  160. constructor Create(AOwner: TComponent); override;
  161. destructor Destroy; override;
  162. procedure Delete(const AFilename: string);
  163. procedure FileStructure(AStructure: TIdFTPDataStructure);
  164. procedure Get(const ASourceFile: string; ADest: TStream; AResume: Boolean = false); overload;
  165. procedure Get(const ASourceFile, ADestFile: string; const ACanOverwrite: boolean = false; AResume: Boolean = false); overload;
  166. procedure Help(var AHelpContents: TStringList; ACommand: String = '');
  167. procedure KillDataChannel; virtual;
  168. procedure List(ADest: TStrings; const ASpecifier: string = ''; const ADetails: boolean = true);
  169. procedure Login;
  170. procedure MakeDir(const ADirName: string);
  171. procedure Noop;
  172. procedure Put(const ASource: TStream; const ADestFile: string = '';
  173. const AAppend: boolean = false); overload;
  174. procedure Put(const ASourceFile: string; const ADestFile: string = '';
  175. const AAppend: boolean = false); overload;
  176. procedure Quit;
  177. function Quote(const ACommand: String): SmallInt;
  178. procedure RemoveDir(const ADirName: string);
  179. procedure Rename(const ASourceFile, ADestFile: string);
  180. function ResumeSupported: Boolean;
  181. function RetrieveCurrentDir: string;
  182. procedure Site(const ACommand: string);
  183. function Size(const AFileName: String): Integer;
  184. procedure Status(var AStatusList: TStringList);
  185. procedure StructureMount(APath: String);
  186. procedure TransferMode(ATransferMode: TIdFTPTransferMode);
  187. procedure ReInitialize(ADelay: Cardinal = 10);
  188. //
  189. property CanResume: Boolean read ResumeSupported;
  190. property DirectoryListing: TIdFTPListItems read GetDirectoryListing;// FDirectoryListing;
  191. property LoginMsg: TIdRFCReply read FLoginMsg;
  192. property SystemDesc: string read FSystemDesc;
  193. property ListResult: TStrings read FListResult; //APR
  194. published
  195. property DataPort: Integer read FDataPort write FDataPort default 0;
  196. property DataPortMin: Integer read FDataPortMin write FDataPortMin default 0;
  197. property DataPortMax: Integer read FDataPortMax write FDataPortMax default 0;
  198. property Passive: boolean read FPassive write FPassive default Id_TIdFTP_Passive;
  199. property Password;
  200. property Port default IDPORT_FTP;
  201. property ProxySettings: TIdFtpProxySettings read FProxySettings write SetProxySettings;
  202. property TransferType: TIdFTPTransferType read FTransferType write SetTransferType default Id_TIdFTP_TransferType;
  203. property Username;
  204. property OnAfterClientLogin: TOnAfterClientLogin read FOnAfterClientLogin write FOnAfterClientLogin;
  205. property OnCheckListFormat: TIdCheckListFormat read FOnCheckListFormat write FOnCheckListFormat;
  206. property OnCreateFTPList: TIdCreateFTPList read FOnCreateFTPList write FOnCreateFTPList;
  207. property OnParseCustomListFormat: TIdOnParseCustomListFormat read FOnParseCustomListFormat
  208. write SetOnParseCustomListFormat;
  209. property OnAfterGet: TIdFtpAfterGet read FOnAfterGet write FOnAfterGet; //APR
  210. end;
  211. EIdFTPFileAlreadyExists = class(EIdException);
  212. implementation
  213. uses
  214. IdComponent, IdResourceStrings, IdStack, IdSimpleServer, IdIOHandlerSocket,
  215. SysUtils;
  216. function CleanDirName(const APWDReply: string): string;
  217. begin
  218. Result := APWDReply;
  219. Delete(result, 1, IndyPos('"', result)); // Remove first doublequote
  220. Result := Copy(result, 1, IndyPos('"', result) - 1); // Remove anything from second doublequote // to end of line
  221. end;
  222. constructor TIdFTP.Create(AOwner: TComponent);
  223. begin
  224. inherited Create(AOwner);
  225. Port := IDPORT_FTP;
  226. Passive := Id_TIdFTP_Passive;
  227. FDataPort := 0;
  228. FDataPortMin := 0;
  229. FDataPortMax := 0;
  230. FTransferType := Id_TIdFTP_TransferType;
  231. FLoginMsg := TIdRFCReply.Create(NIL);
  232. FListResult := TStringList.Create;
  233. FCanResume := false;
  234. FResumeTested := false;
  235. FProxySettings:= TIdFtpProxySettings.Create; //APR
  236. end;
  237. procedure TIdFTP.Connect(AAutoLogin: boolean = True;
  238. const ATimeout: Integer = IdTimeoutDefault);
  239. var
  240. TmpHost: String;
  241. TmpPort: Integer;
  242. begin
  243. try
  244. //APR 011216: proxy support
  245. TmpHost:=FHost;
  246. TmpPort:=FPort;
  247. try
  248. if (ProxySettings.ProxyType > fpcmNone) and (Length(ProxySettings.Host) > 0) then begin
  249. FHost := ProxySettings.Host;
  250. FPort := ProxySettings.Port;
  251. end;
  252. inherited Connect(ATimeout);
  253. finally
  254. FHost := TmpHost;
  255. FPort := TmpPort;
  256. end;//tryf
  257. GetResponse([220]);
  258. Greeting.Assign(LastCmdResult);
  259. if AAutoLogin then begin
  260. Login;
  261. DoAfterLogin;
  262. // OpenVMS 7.1 replies with 200 instead of 215 - What does the RFC say about this?
  263. if SendCmd('SYST', [200, 215, 500]) = 500 then begin {Do not translate}
  264. FSystemDesc := RSFTPUnknownHost;
  265. end else begin
  266. FSystemDesc := LastCmdResult.Text[0];
  267. end;
  268. DoStatus(ftpReady, [RSFTPStatusReady]);
  269. end;
  270. except
  271. Disconnect;
  272. raise;
  273. end;
  274. end;
  275. procedure TIdFTP.SetTransferType(AValue: TIdFTPTransferType);
  276. begin
  277. if AValue <> FTransferType then begin
  278. if not Assigned(FDataChannel) then begin
  279. FTransferType := AValue;
  280. if Connected then begin
  281. SendTransferType;
  282. end;
  283. end
  284. end;
  285. end;
  286. procedure TIdFTP.SendTransferType;
  287. var
  288. s: string;
  289. begin
  290. case TransferType of
  291. ftAscii: s := 'A'; {Do not translate}
  292. ftBinary: s := 'I'; {Do not translate}
  293. end;
  294. SendCmd('TYPE ' + s, 200); {Do not translate}
  295. end;
  296. function TIdFTP.ResumeSupported: Boolean;
  297. begin
  298. if FResumeTested then result := FCanResume
  299. else begin
  300. FResumeTested := true;
  301. FCanResume := Quote('REST 1') = 350; {Do not translate}
  302. result := FCanResume;
  303. Quote('REST 0'); {Do not translate}
  304. end;
  305. end;
  306. procedure TIdFTP.Get(const ASourceFile: string; ADest: TStream; AResume: Boolean = false);
  307. begin
  308. AResume := AResume and CanResume;
  309. InternalGet('RETR ' + ASourceFile, ADest, AResume); {Do not translate}
  310. DoAfterGet(ADest); //APR
  311. end;
  312. procedure TIdFTP.Get(const ASourceFile, ADestFile: string; const ACanOverwrite: boolean = false;
  313. AResume: Boolean = false);
  314. var
  315. LDestStream: TFileStream;
  316. begin
  317. if FileExists(ADestFile) then begin
  318. AResume := AResume and CanResume;
  319. if ACanOverwrite and (not AResume) then begin
  320. LDestStream := TFileStream.Create(ADestFile, fmCreate);
  321. end
  322. else begin
  323. if (not ACanOverwrite) and AResume then begin
  324. LDestStream := TFileStream.Create(ADestFile, fmOpenReadWrite or fmShareDenyWrite);
  325. LDestStream.Seek(0, soFromEnd);
  326. end
  327. else begin
  328. raise EIdFTPFileAlreadyExists.Create(RSDestinationFileAlreadyExists);
  329. end;
  330. end;
  331. end
  332. else begin
  333. LDestStream := TFileStream.Create(ADestFile, fmCreate);
  334. end;
  335. try
  336. Get(ASourceFile, LDestStream, AResume);
  337. finally
  338. FreeAndNil(LDestStream);
  339. end;
  340. end;
  341. procedure TIdFTP.DoAfterGet (AStream: TStream);//APR
  342. Begin
  343. if Assigned(FOnAfterGet) then FOnAfterGet(SELF,AStream);
  344. End;//TIdFTP.AtAfterFileGet
  345. procedure TIdFTP.ConstructDirListing;
  346. begin
  347. if not Assigned(FDirectoryListing) then begin
  348. if not (csDesigning in ComponentState) then begin
  349. DoFTPList;
  350. end;
  351. if not Assigned(FDirectoryListing) then begin
  352. FDirectoryListing := TIdFTPListItems.Create;
  353. end;
  354. FDirectoryListing.OnParseCustomListFormat := FOnParseCustomListFormat;
  355. end else begin
  356. FDirectoryListing.Clear;
  357. end;
  358. end;
  359. procedure TIdFTP.List(ADest: TStrings; const ASpecifier: string = ''; {Do not translate}
  360. const ADetails: boolean = true);
  361. var
  362. LDest: TStringStream;
  363. begin
  364. LDest := TStringStream.Create(''); try {Do not translate}
  365. if ADetails then begin
  366. InternalGet(trim('LIST ' + ASpecifier), LDest); {Do not translate}
  367. end else begin
  368. InternalGet(trim('NLST ' + ASpecifier), LDest); {Do not trnalstate}
  369. end;
  370. FreeAndNil(FDirectoryListing);
  371. if Assigned(ADest) then begin //APR: User can use ListResult and DirectoryListing
  372. ADest.Text := LDest.DataString;
  373. end;
  374. FListResult.Text := LDest.DataString;
  375. finally FreeAndNil(LDest); end;
  376. end;
  377. procedure TIdFTP.InternalGet(const ACommand: string; ADest: TStream; AResume: Boolean = false);
  378. var
  379. LIP: string;
  380. LPort: Integer;
  381. LResponse: Integer;
  382. begin
  383. DoStatus(ftpTransfer, [RSFTPStatusStartTransfer]); try
  384. if FPassive then begin
  385. SendPassive(LIP, LPort);
  386. FDataChannel := TIdTCPClient.Create(nil); try
  387. with (FDataChannel as TIdTCPClient) do begin
  388. if (Self.IOHandler is TIdIOHandlerSocket) then begin
  389. if not assigned(IOHandler) then begin
  390. IOHandler:=TIdIOHandlerSocket.create(nil);
  391. end;
  392. TIdIOHandlerSocket(IOHandler).SocksInfo.Assign(TIdIOHandlerSocket(Self.IOHandler).SocksInfo);
  393. TIdIOHandlerSocket(IOHandler).SocksInfo.IOHandler:=IOHandler;
  394. end;
  395. InitDataChannel;
  396. Host := LIP;
  397. Port := LPort;
  398. Connect; try
  399. if AResume then begin
  400. Self.SendCmd('REST ' + IntToStr(ADest.Position), [350]); {Do not tranlsate}
  401. end;
  402. Self.WriteLn(ACommand);
  403. Self.GetResponse([125, 150, 154]); //APR: Ericsson Switch FTP
  404. ReadStream(ADest, -1, True);
  405. finally Disconnect; end;
  406. end;
  407. finally FDataChannel.IOHandler.free; FDataChannel.IOHandler:=nil; FreeAndNil(FDataChannel); end;
  408. end else begin
  409. FDataChannel := TIdSimpleServer.Create(nil); try
  410. with TIdSimpleServer(FDataChannel) do begin
  411. InitDataChannel;
  412. BoundIP := (Self.IOHandler as TIdIOHandlerSocket).Binding.IP;
  413. BoundPort := Self.DataPort;
  414. BoundPortMin := Self.DataPortMin;
  415. BoundPortMax := Self.DataPortMax;
  416. BeginListen;
  417. SendPort(Binding);
  418. if AResume then begin
  419. Self.SendCmd('REST ' + IntToStr(ADest.Position), [350]); {Do not translate}
  420. end;
  421. Self.SendCmd(ACommand, [125, 150, 154]); //APR: Ericsson Switch FTP
  422. Listen;
  423. ReadStream(ADest, -1, True);
  424. end;
  425. finally
  426. FreeAndNil(FDataChannel);
  427. end;
  428. end;
  429. finally
  430. DoStatus(ftpReady, [RSFTPStatusDoneTransfer]);
  431. end;
  432. // ToDo: Change that to properly handle response code (not just success or except)
  433. // 226 = download successful, 225 = Abort successful}
  434. LResponse := GetResponse([225, 226, 250, 426, 450]);
  435. if (LResponse = 426) or (LResponse = 450) then begin
  436. GetResponse([226, 225]);
  437. DoStatus(ftpAborted, [RSFTPStatusAbortTransfer]);
  438. end;
  439. end;
  440. procedure TIdFTP.Quit;
  441. begin
  442. if Connected then begin
  443. WriteLn('QUIT'); {Do not translate}
  444. end;
  445. Disconnect;
  446. end;
  447. procedure TIdFTP.KillDataChannel;
  448. begin
  449. // Had kill the data channel ()
  450. if Assigned(FDataChannel) then begin
  451. FDataChannel.DisconnectSocket;
  452. end;
  453. end;
  454. procedure TIdFTP.Abort;
  455. begin
  456. // only send the abort command. The Data channel is supposed to disconnect
  457. if Connected then begin
  458. WriteLn('ABOR'); {Do not translate}
  459. end;
  460. // Kill the data channel: usually, the server doesn't close it by itself
  461. KillDataChannel;
  462. end;
  463. procedure TIdFTP.SendPort(AHandle: TIdSocketHandle);
  464. begin
  465. SendCmd('PORT ' + StringReplace(AHandle.IP, '.', ',', [rfReplaceAll]) {Do not translate}
  466. + ',' + IntToStr(AHandle.Port div 256) + ',' + IntToStr(AHandle.Port mod 256), [200]); {Do not translate}
  467. end;
  468. procedure TIdFTP.InternalPut(const ACommand: string; ASource: TStream; AFromBeginning: Boolean = true);
  469. var
  470. LIP: string;
  471. LPort: Integer;
  472. LResponse: Integer;
  473. begin
  474. DoStatus(ftpTransfer, [RSFTPStatusStartTransfer]); try
  475. if FPassive then begin
  476. SendPassive(LIP, LPort);
  477. WriteLn(ACommand);
  478. FDataChannel := TIdTCPClient.Create(nil);
  479. with TIdTCPClient(FDataChannel) do try
  480. if (Self.IOHandler is TIdIOHandlerSocket) then begin
  481. if not assigned(IOHandler) then begin
  482. IOHandler:=TIdIOHandlerSocket.create(nil);
  483. end;
  484. TIdIOHandlerSocket(IOHandler).SocksInfo.Assign(TIdIOHandlerSocket(Self.IOHandler).SocksInfo);
  485. TIdIOHandlerSocket(IOHandler).SocksInfo.IOHandler:=IOHandler;
  486. end;
  487. InitDataChannel;
  488. Host := LIP;
  489. Port := LPort;
  490. Connect;
  491. try
  492. Self.GetResponse([110, 125, 150]);
  493. try
  494. WriteStream(ASource, {false}AFromBeginning);
  495. except
  496. on E: EIdSocketError do begin
  497. // If 10038 - abort was called. Server will return 225
  498. if E.LastError <> 10038 then begin
  499. raise;
  500. end;
  501. end;
  502. end;
  503. finally Disconnect; end;
  504. finally FDataChannel.IOHandler.free; FDataChannel.IOHandler:=nil; FreeAndNil(FDataChannel); end;
  505. end else begin
  506. FDataChannel := TIdSimpleServer.Create(nil); try
  507. with TIdSimpleServer(FDataChannel) do begin
  508. InitDataChannel;
  509. BoundIP := (Self.IOHandler as TIdIOHandlerSocket).Binding.IP;
  510. BoundPort := Self.DataPort;
  511. BoundPortMin := Self.DataPortMin;
  512. BoundPortMax := Self.DataPortMax;
  513. BeginListen;
  514. SendPort(Binding);
  515. Self.SendCmd(ACommand, [125, 150]);
  516. Listen;
  517. WriteStream(ASource, AFromBeginning);
  518. end;
  519. finally FreeAndNil(FDataChannel); end;
  520. end;
  521. finally
  522. DoStatus(ftpReady, [RSFTPStatusDoneTransfer]);
  523. end;
  524. // 226 = download successful, 225 = Abort successful}
  525. LResponse := GetResponse([225, 226, 250, 426, 450]);
  526. if (LResponse = 426) or (LResponse = 450) then begin
  527. // some servers respond with 226 on ABOR
  528. GetResponse([226, 225]);
  529. DoStatus(ftpAborted, [RSFTPStatusAbortTransfer]);
  530. end;
  531. end;
  532. procedure TIdFTP.InitDataChannel;
  533. begin
  534. FDataChannel.SendBufferSize := SendBufferSize;
  535. FDataChannel.RecvBufferSize := RecvBufferSize;
  536. FDataChannel.OnWork := OnWork;
  537. FDataChannel.OnWorkBegin := OnWorkBegin;
  538. FDataChannel.OnWorkEnd := OnWorkEnd;
  539. end;
  540. procedure TIdFTP.Put(const ASource: TStream; const ADestFile: string = '';
  541. const AAppend: boolean = false);
  542. begin
  543. if length(ADestFile) = 0 then begin
  544. InternalPut('STOU ' + ADestFile, ASource); {Do not localize}
  545. end else if AAppend then begin
  546. InternalPut('APPE ' + ADestFile, ASource, false); {Do not localize}
  547. end else begin
  548. InternalPut('STOR ' + ADestFile, ASource); {Do not localize}
  549. end;
  550. end;
  551. procedure TIdFTP.Put(const ASourceFile: string; const ADestFile: string = '';
  552. const AAppend: boolean = false);
  553. var
  554. LSourceStream: TFileStream;
  555. begin
  556. LSourceStream := TFileStream.Create(ASourceFile, fmOpenRead or fmShareDenyNone); try
  557. Put(LSourceStream, ADestFile, AAppend);
  558. finally FreeAndNil(LSourceStream); end;
  559. end;
  560. procedure TIdFTP.SendPassive(var VIP: string; var VPort: integer);
  561. var
  562. i,bLeft,bRight: integer;
  563. s: string;
  564. begin
  565. SendCmd('PASV', 227); {Do not translate}
  566. s := Trim(LastCmdResult.Text[0]);
  567. // Case 1 (Normal)
  568. // 227 Entering passive mode(100,1,1,1,23,45)
  569. bLeft := IndyPos('(', s); {Do not translate}
  570. bRight := IndyPos(')', s); {Do not translate}
  571. if (bLeft = 0) or (bRight = 0) then begin
  572. // Case 2
  573. // 227 Entering passive mode on 100,1,1,1,23,45
  574. bLeft := RPos(#32, s);
  575. s := Copy(s, bLeft + 1, Length(s) - bLeft);
  576. end else begin
  577. s := Copy(s, bLeft + 1, bRight - bLeft - 1);
  578. end;
  579. VIP := ''; {Do not translate}
  580. for i := 1 to 4 do begin
  581. VIP := VIP + '.' + Fetch(s, ','); {Do not translate}
  582. end;
  583. System.Delete(VIP, 1, 1);
  584. // Determine port
  585. VPort := StrToInt(Fetch(s, ',')) shl 8; {Do not translate}
  586. //use trim as one server sends something like this:
  587. //"227 Passive mode OK (195,92,195,164,4,99 )"
  588. VPort := VPort + StrToInt(Trim(Fetch(s, ','))); {Do not translate}
  589. end;
  590. procedure TIdFTP.Noop;
  591. begin
  592. SendCmd('NOOP', 200); {Do not translate}
  593. end;
  594. procedure TIdFTP.MakeDir(const ADirName: string);
  595. begin
  596. SendCmd('MKD ' + ADirName, 257); {Do not translate}
  597. end;
  598. function TIdFTP.RetrieveCurrentDir: string;
  599. begin
  600. SendCmd('PWD', 257); {Do not translate}
  601. Result := CleanDirName(LastCmdResult.Text[0]);
  602. end;
  603. procedure TIdFTP.RemoveDir(const ADirName: string);
  604. begin
  605. SendCmd('RMD ' + ADirName, 250); {Do not translate}
  606. end;
  607. procedure TIdFTP.Delete(const AFilename: string);
  608. begin
  609. SendCmd('DELE ' + AFilename, 250); {Do not translate}
  610. end;
  611. (*
  612. CHANGE WORKING DIRECTORY (CWD)
  613. This command allows the user to work with a different
  614. directory or dataset for file storage or retrieval without
  615. altering his login or accounting information. Transfer
  616. parameters are similarly unchanged. The argument is a
  617. pathname specifying a directory or other system dependent
  618. file group designator.
  619. CWD
  620. 250
  621. 500, 501, 502, 421, 530, 550
  622. *)
  623. procedure TIdFTP.ChangeDir(const ADirName: string);
  624. begin
  625. SendCmd('CWD ' + ADirName, [200, 250]); //APR: Ericsson Switch FTP {Do not translate}
  626. end;
  627. (*
  628. CHANGE TO PARENT DIRECTORY (CDUP)
  629. This command is a special case of CWD, and is included to
  630. simplify the implementation of programs for transferring
  631. directory trees between operating systems having different
  632. syntaxes for naming the parent directory. The reply codes
  633. shall be identical to the reply codes of CWD. See
  634. Appendix II for further details.
  635. CDUP
  636. 200
  637. 500, 501, 502, 421, 530, 550
  638. *)
  639. procedure TIdFTP.ChangeDirUp;
  640. begin
  641. // RFC lists 200 as the proper response, but in another section says that it can return the
  642. // same as CWD, which expects 250. That is it contradicts itself.
  643. // MS in their infinite wisdom chnaged IIS 5 FTP to return 250.
  644. SendCmd('CDUP', [200, 250]); {Do not translate}
  645. end;
  646. procedure TIdFTP.Site(const ACommand: string);
  647. begin
  648. SendCmd('SITE ' + ACommand, 200); {Do not translate}
  649. end;
  650. procedure TIdFTP.Rename(const ASourceFile, ADestFile: string);
  651. begin
  652. SendCmd('RNFR ' + ASourceFile, 350); {Do not translate}
  653. SendCmd('RNTO ' + ADestFile, 250); {Do not translate}
  654. end;
  655. function TIdFTP.Size(const AFileName: String): Integer;
  656. var
  657. SizeStr: String;
  658. begin
  659. result := -1;
  660. if SendCmd('SIZE ' + AFileName) = 213 then begin {Do not translate}
  661. SizeStr := Trim(LastCmdResult.Text.Text);
  662. system.delete(SizeStr, 1, IndyPos(' ', SizeStr)); // delete the response {Do not translate}
  663. result := StrToIntDef(SizeStr, -1);
  664. end;
  665. end;
  666. //Added by SP
  667. procedure TIdFTP.ReInitialize(ADelay: Cardinal = 10);
  668. begin
  669. Sleep(ADelay); //Added
  670. if SendCmd('REIN', [120, 220, 500]) <> 500 then begin {Do not translate}
  671. FLoginMsg.Clear;
  672. FCanResume := False;
  673. FDirectoryListing.Clear;
  674. FUsername := ''; {Do not translate}
  675. FPassword := ''; {Do not translate}
  676. FPassive := Id_TIdFTP_Passive;
  677. FCanResume := False;
  678. FResumeTested := False;
  679. FSystemDesc := '';
  680. FTransferType := Id_TIdFTP_TransferType;
  681. end;
  682. end;
  683. procedure TIdFTP.Allocate(AAllocateBytes: Integer);
  684. begin
  685. SendCmd('ALLO ' + IntToStr(AAllocateBytes), [200]); {Do not translate}
  686. end;
  687. procedure TIdFTP.Status(var AStatusList: TStringList);
  688. var
  689. LStrm: TStringStream;
  690. LList: TStringList;
  691. begin
  692. if SendCmd('STAT', [211, 212, 213, 500]) <> 500 then {Do not translate}
  693. begin
  694. if not Assigned(FDirectoryListing) then
  695. begin
  696. DoFTPList;
  697. end;
  698. LStrm := TStringStream.Create(''); {Do not translate}
  699. LList := TStringList.Create;
  700. //Read stream through control connection - not data channel
  701. ReadStream(LStrm, -1, True);
  702. LList.Text := LStrm.DataString;
  703. try
  704. try
  705. ConstructDirListing;
  706. FDirectoryListing.Clear;
  707. except
  708. on EAccessViolation do ConstructDirListing;
  709. end;
  710. // Parse directory listing
  711. if LList.Count > 0 then
  712. begin
  713. FDirectoryListing.ListFormat := FDirectoryListing.CheckListFormat(LList[0], True);
  714. DoCheckListFormat(LList[0]);
  715. FDirectoryListing.LoadList(LList);
  716. end;
  717. except
  718. if Assigned(AStatusList) = True then
  719. begin
  720. AStatusList.Text := LStrm.DataString;
  721. end;
  722. end;
  723. FreeAndNil(LStrm);
  724. FreeAndNil(LList);
  725. end;
  726. end;
  727. procedure TIdFTP.Help(var AHelpContents: TStringList; ACommand: String = ''); {Do not translate}
  728. var
  729. LStrm: TStringStream;
  730. begin
  731. LStrm := TStringStream.Create(''); {Do not translate}
  732. if SendCmd('HELP ' + ACommand, [211, 214, 500]) <> 500 then {Do not translate}
  733. begin
  734. ReadStream(LStrm, -1, True);
  735. AHelpContents.Text := LStrm.DataString;
  736. end;
  737. FreeAndNil(LStrm);
  738. end;
  739. procedure TIdFTP.Account(AInfo: String);
  740. begin
  741. SendCmd('ACCT ' + AInfo, [202, 230, 500]); {Do not translate}
  742. end;
  743. procedure TIdFTP.StructureMount(APath: String);
  744. begin
  745. SendCmd('SMNT ' + APath, [202, 250, 500]); {Do not translate}
  746. end;
  747. procedure TIdFTP.FileStructure(AStructure: TIdFTPDataStructure);
  748. var
  749. s: String;
  750. begin
  751. case AStructure of
  752. dsFile: s := 'F'; {Do not translate}
  753. dsRecord: s := 'R'; {Do not translate}
  754. dsPage: s := 'P'; {Do not translate}
  755. end;
  756. SendCmd('STRU ' + s, [200, 500]); {Do not translate}
  757. { TODO: Needs to be finished }
  758. end;
  759. procedure TIdFTP.TransferMode(ATransferMode: TIdFTPTransferMode);
  760. var
  761. s: String;
  762. begin
  763. case ATransferMode of
  764. dmBlock: begin
  765. s := 'B'; {Do not translate}
  766. end;
  767. dmCompressed: begin
  768. s := 'C'; {Do not translate}
  769. end;
  770. dmStream: begin
  771. s := 'S'; {Do not translate}
  772. end;
  773. end;
  774. SendCmd('MODE ' + s, [200, 500]); {Do not translate}
  775. { TODO: Needs to be finished }
  776. end;
  777. destructor TIdFTP.Destroy;
  778. begin
  779. FreeAndNil(FListResult);
  780. FreeAndNil(FLoginMsg);
  781. FreeAndNil(FDirectoryListing);
  782. FreeAndNIL(FProxySettings); //APR
  783. inherited Destroy;
  784. end;
  785. function TIdFTP.Quote(const ACommand: String): SmallInt;
  786. begin
  787. result := SendCmd(ACommand);
  788. end;
  789. //APR 011216: ftp proxy support
  790. // TODO: need help - "//?"
  791. procedure TIdFTP.Login;
  792. begin
  793. case ProxySettings.ProxyType of
  794. fpcmNone:
  795. begin
  796. if SendCmd('USER ' + FUserName, [230, 331]) = 331 then begin {Do not translate}
  797. SendCmd('PASS ' + FPassword, 230); {Do not translate}
  798. end;
  799. end;//fpcmNone
  800. fpcmUserSite:
  801. begin
  802. if (Length(ProxySettings.UserName)>0) then begin
  803. if SendCmd('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {Do not translate}
  804. SendCmd('PASS ' + ProxySettings.Password, 230); {Do not translate}
  805. end;
  806. end;//proxy login
  807. if SendCmd('USER ' + FUserName+'@'+FHost, [230, 331]) = 331 then begin {Do not translate}
  808. SendCmd('PASS ' + FPassword, 230); {Do not translate}
  809. end;
  810. end;//fpcmUserSite
  811. fpcmSite:
  812. begin
  813. if (Length(ProxySettings.UserName)>0) then begin
  814. if SendCmd('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {Do not translate}
  815. SendCmd('PASS ' + ProxySettings.Password, 230); {Do not translate}
  816. end;
  817. end;//proxy login
  818. SendCmd('SITE '+FHost);//? Server Reply? 220?
  819. if SendCmd('USER ' + FUserName, [230, 331]) = 331 then begin {Do not translate}
  820. SendCmd('PASS ' + FPassword, 230); {Do not translate}
  821. end;
  822. end;//fpcmSite
  823. fpcmOpen:
  824. begin
  825. if (Length(ProxySettings.UserName)>0) then begin
  826. if SendCmd('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {Do not translate}
  827. SendCmd('PASS ' + ProxySettings.Password, 230); {Do not translate}
  828. end;
  829. end;//proxy login
  830. SendCmd('OPEN '+FHost);//? Server Reply? 220? {Do not translate}
  831. if SendCmd('USER ' + FUserName, [230, 331]) = 331 then begin {Do not translate}
  832. SendCmd('PASS ' + FPassword, 230); {Do not translate}
  833. end;
  834. end;//fpcmSite
  835. fpcmUserPass: //USER user@firewalluser@hostname / PASS pass@firewallpass
  836. begin
  837. if SendCmd(Format('USER %s@%s@%s',[FUserName,ProxySettings.UserName,FHost]), [230, 331])=331 then begin {Do not translate}
  838. if Length(ProxySettings.Password)>0 then begin
  839. SendCmd('PASS '+FPassword+'@'+ProxySettings.Password, 230); {Do not translate}
  840. end
  841. else begin
  842. SendCmd('PASS '+FPassword, 230); {Do not translate}
  843. end;//if @
  844. end;
  845. end;//fpcmUserPass
  846. fpcmTransparent: //? +Host
  847. begin
  848. if (Length(ProxySettings.UserName)>0) then begin
  849. if SendCmd('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {Do not translate}
  850. SendCmd('PASS ' + ProxySettings.Password, 230); {Do not translate}
  851. end;
  852. end;//proxy login
  853. if SendCmd('USER ' + FUserName, [230, 331]) = 331 then begin {Do not translate}
  854. SendCmd('PASS ' + FPassword, 230); {Do not translate}
  855. end;
  856. end;//fpcmTransparent
  857. fpcmHttpProxyWithFtp:
  858. begin
  859. {GET ftp://XXX:[email protected]/ HTTP/1.0
  860. Host: indy.nevrona.com
  861. User-Agent: Mozilla/4.0 (compatible; Wincmd; Windows NT)
  862. Proxy-Authorization: Basic B64EncodedUserPass==
  863. Connection: close}
  864. raise EIdException.Create(RSSocksServerCommandError);
  865. end;//fpcmHttpProxyWithFtp
  866. end;//case
  867. FLoginMsg.Assign(LastCmdResult);
  868. SendTransferType;
  869. End;//TIdFTP.Login
  870. procedure TIdFTP.DoAfterLogin;
  871. begin
  872. if Assigned(FOnAfterClientLogin) then begin
  873. OnAfterClientLogin(self);
  874. end;
  875. end;
  876. procedure TIdFTP.DoFTPList;
  877. begin
  878. if Assigned(FOnCreateFTPList) then begin
  879. FOnCreateFTPList(self, FDirectoryListing);
  880. end;
  881. end;
  882. procedure TIdFTP.DoCheckListFormat(const ALine: String);
  883. Var
  884. LListFormat: TIdFTPListFormat;
  885. Begin
  886. if Assigned(FOnCheckListFormat) then begin //APR: User always right!
  887. LListFormat := FDirectoryListing.ListFormat; //APR: user MUST see Indy opinion
  888. OnCheckListFormat(Self, ALine, LListFormat);
  889. FDirectoryListing.ListFormat := LListFormat;
  890. end;
  891. End;//TIdFTP.DoCheckListFormat
  892. function TIdFTP.GetDirectoryListing: TIdFTPListItems;
  893. begin
  894. if not Assigned(FDirectoryListing) then begin
  895. try
  896. ConstructDirListing;
  897. except
  898. on EAccessViolation do ConstructDirListing;
  899. end;
  900. // Parse directory listing
  901. if FListResult.Count > 0 then begin
  902. FDirectoryListing.ListFormat := FDirectoryListing.CheckListFormat(FListResult[0],TRUE);//APR: TRUE for IndyCheck, else always Unknown
  903. DoCheckListFormat(FListResult[0]);
  904. FDirectoryListing.LoadList(FListResult);
  905. end;
  906. end;
  907. Result := FDirectoryListing;
  908. end;
  909. procedure TIdFTP.SetOnParseCustomListFormat(const AValue: TIdOnParseCustomListFormat);
  910. begin
  911. FOnParseCustomListFormat := AValue;
  912. if Assigned(FDirectoryListing) then begin
  913. FDirectoryListing.OnParseCustomListFormat := AValue;
  914. end;
  915. end;
  916. procedure TIdFTP.SetProxySettings(const Value: TIdFtpProxySettings);
  917. Begin
  918. FProxySettings.Assign(Value);
  919. End;//
  920. { TIdFtpProxySettings }
  921. procedure TIdFtpProxySettings.Assign(Source: TPersistent);
  922. Begin
  923. if Source is TIdFtpProxySettings then begin
  924. with TIdFtpProxySettings(Source) do begin
  925. SELF.FProxyType := ProxyType;
  926. SELF.FHost := Host;
  927. SELF.FUserName := UserName;
  928. SELF.FPassword := Password;
  929. SELF.FPort := Port;
  930. end;
  931. end
  932. else begin
  933. inherited Assign(Source);
  934. end;
  935. End;//
  936. end.