ftpprothandler.pas 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. unit ftpprothandler;
  2. {$IFDEF FPC}
  3. {$mode delphi}{$H+}
  4. {$ENDIF}
  5. interface
  6. uses
  7. {$IFNDEF NO_FTP}
  8. IdFTP,
  9. IdFTPList, //for some diffinitions with FTP list
  10. IdAllFTPListParsers, //with FTP, this links in all list parsing classes.
  11. IdFTPListParseTandemGuardian, //needed ref. to TIdTandemGuardianFTPListItem property
  12. IdFTPListTypes, //needed for ref. to TIdUnixBaseFTPListItem property
  13. IdFTPListParseVMS, //needed for ref. to TIdVMSFTPListItem property ;
  14. IdIOHandler,
  15. IdTCPConnection,
  16. IdIOHandlerStack,
  17. {$ifdef usezlib}
  18. IdCompressorZLib, //for deflate FTP support
  19. {$endif}
  20. IdLogEvent, //for logging component
  21. {$ENDIF}
  22. prothandler,
  23. Classes, SysUtils, IdURI;
  24. {$IFDEF VER200}
  25. {$DEFINE STRING_IS_UNICODE} // 'String' type is Unicode now
  26. {$ENDIF}
  27. {$IFDEF VER210}
  28. {$DEFINE STRING_IS_UNICODE} // 'String' type is Unicode now
  29. {$ENDIF}
  30. {$IFDEF VER220}
  31. {$DEFINE STRING_IS_UNICODE} // 'String' type is Unicode now
  32. {$ENDIF}
  33. type
  34. TFTPProtHandler = class(TProtHandler)
  35. protected
  36. FPort : Boolean;
  37. {$IFNDEF NO_FTP}
  38. procedure OnSent(ASender: TComponent; const AText: string; const AData: string);
  39. procedure OnReceived(ASender: TComponent; const AText: string; const AData: string);
  40. procedure MakeHTMLDirTable(AURL : TIdURI; AFTP : TIdFTP);
  41. procedure OnDataChannelCreating(ASender: TObject; ADataChannel: TIdTCPConnection);
  42. procedure OnDataChannelDestroy(ASender: TObject; ADataChannel: TIdTCPConnection);
  43. procedure OnDirParseStart(ASender : TObject);
  44. procedure OnDirParseEnd(ASender : TObject);
  45. {$ENDIF}
  46. public
  47. class function CanHandleURL(AURL : TIdURI) : Boolean; override;
  48. procedure GetFile(AURL : TIdURI); override;
  49. constructor Create;
  50. property Port : Boolean read FPort write FPort;
  51. end;
  52. implementation
  53. uses IdGlobal;
  54. class function TFTPProtHandler.CanHandleURL(AURL : TIdURI) : Boolean;
  55. begin
  56. {$IFDEF NO_FTP}
  57. Result := False;
  58. {$ELSE}
  59. Result := UpperCase(AURL.Protocol)='FTP';
  60. {$ENDIF}
  61. end;
  62. constructor TFTPProtHandler.Create;
  63. begin
  64. inherited Create;
  65. FPort := False;
  66. end;
  67. procedure TFTPProtHandler.GetFile(AURL : TIdURI);
  68. {$IFDEF NO_FTP}
  69. begin
  70. {$ELSE}
  71. //In this procedure, URL handling has to be done manually because the
  72. //the FTP component does not handle URL's at all.
  73. var
  74. LStr : TMemoryStream;
  75. LIO : TIdIOHandlerStack;
  76. LF : TIdFTP;
  77. LDI : TIdLogEvent;
  78. {$ifdef usezlib}
  79. LC : TIdCompressorZLib;
  80. {$endif}
  81. LIsDir : Boolean;
  82. i : Integer;
  83. begin
  84. LIsDir := False;
  85. LDI := TIdLogEvent.Create;
  86. LF := TIdFTP.Create;
  87. {$ifdef usezlib}
  88. LC := TIdCompressorZLib.Create;
  89. if LC.IsReady then begin
  90. LF.Compressor := LC;
  91. end;
  92. {$endif}
  93. try
  94. LDI.Active := True;
  95. LDI.LogTime := False;
  96. LDI.ReplaceCRLF := False;
  97. LDI.OnReceived := OnReceived;
  98. LDI.OnSent := OnSent;
  99. LIO := TIdIOHandlerStack.Create;
  100. LIO.Intercept := LDI;
  101. LF.IOHandler := LIO;
  102. LF.Passive := not FPort;
  103. LF.UseMLIS := True;
  104. LF.Host := AURL.Host;
  105. LF.Password := AURL.URLDecode(AURL.Password);
  106. LF.Username := AURL.URLDecode(AURL.Username);
  107. LF.IPVersion := AURL.IPVersion;
  108. LF.Password := AURL.Password;;
  109. if LF.Username = '' then
  110. begin
  111. LF.Username := 'anonymous';
  112. LF.Password := 'pass@httpget';
  113. end;
  114. if AURL.Document = '' then
  115. begin
  116. LIsDir := True;
  117. end;
  118. LStr := TMemoryStream.Create;
  119. if FVerbose then begin
  120. LF.OnDataChannelCreate := OnDataChannelCreating;
  121. LF.OnDataChannelDestroy := OnDataChannelDestroy;
  122. LF.OnDirParseStart := OnDirParseStart;
  123. LF.OnDirParseEnd := OnDirParseEnd;
  124. end;
  125. LF.Connect;
  126. try
  127. LF.ChangeDir(AURL.Path);
  128. //The thing is you can't always know if it's a file or dir.
  129. if not LIsDir then
  130. try
  131. LF.Get(AURL.Document,LStr,True);
  132. LStr.SaveToFile(AURL.Document);
  133. except
  134. LIsDir := True;
  135. end;
  136. if LIsDir then
  137. begin
  138. LF.List;
  139. if FVerbose then
  140. begin
  141. for i := 0 to LF.ListResult.Count -1 do
  142. begin
  143. WriteLn({$IFDEF FPC}stdout{$ELSE}output{$ENDIF},LF.ListResult[i]);
  144. end;
  145. end;
  146. MakeHTMLDirTable(AURL,LF);
  147. end;
  148. finally
  149. LF.Disconnect;
  150. FreeAndNil(LStr);
  151. end;
  152. finally
  153. FreeAndNil(LF);
  154. {$ifdef usezlib}
  155. FreeAndNil(LC);
  156. {$endif}
  157. FreeAndNil(LIO);
  158. FreeAndNil(LDI);
  159. end;
  160. {$ENDIF}
  161. end;
  162. {$IFNDEF NO_FTP}
  163. procedure TFTPProtHandler.MakeHTMLDirTable(AURL : TIdURI; AFTP : TIdFTP);
  164. {
  165. This routine is in this demo to show users how to use the directory listing from TIdFTP.
  166. }
  167. var i : integer;
  168. LTbl : TStringList;
  169. LTmp : String;
  170. procedure WriteTableCell(const ACellText : String; AOutput : TStrings);
  171. begin
  172. if ACellText = '' then
  173. begin
  174. AOutput.Add(' <TD>&nbsp;</TD>');
  175. end
  176. else
  177. begin
  178. AOutput.Add(' <TD>'+ACellText+'</TD>');
  179. end;
  180. end;
  181. procedure MakeFileNameLink(const AURL :TIdURI; AFileName : String; AOutput : TStrings);
  182. begin
  183. if AURL.URI <>'' then
  184. begin
  185. if AURL.Document = '' then
  186. begin
  187. AOutput.Add(' <TD><A HREF="'+AURL.URI+'/'+AFileName+'">'+AFileName+'</A></TD>');
  188. end
  189. else
  190. begin
  191. AOutput.Add(' <TD><A HREF="'+AURL.URI +AFileName+'>'+AFileName+'</A></TD>');
  192. end;
  193. end
  194. else
  195. begin
  196. WriteTableCell(AFileName,AOutput);
  197. end;
  198. end;
  199. begin
  200. LTbl := TStringList.Create;
  201. try
  202. LTbl.Add('<HTML>');
  203. LTbl.Add(' <TITLE>'+AURL.URI+'</TITLE>');
  204. {$IFDEF STRING_IS_UNICODE}
  205. LTbl.Add(' <HEAD>');
  206. LTbl.Add(' <meta http-equiv="Content-Type" content="text/html;charset=utf-8" >');
  207. LTbl.Add(' </HEAD>');
  208. {$ENDIF}
  209. LTbl.Add(' <BODY>');
  210. LTbl.Add(' <TABLE>');
  211. LTbl.Add(' <TR>');
  212. LTbl.Add(' <TH>Name</TH>');
  213. LTbl.Add(' <TH>Type</TH>');
  214. LTbl.Add(' <TH>Size</TH>');
  215. LTbl.Add(' <TH>Date</TH>');
  216. LTbl.Add(' <TH>Permissions</TH>');
  217. LTbl.Add(' <TH>Owner</TH>');
  218. LTbl.Add(' <TH>Group</TH>');
  219. LTbl.Add(' </TR>');
  220. for i := 0 to AFTP.DirectoryListing.Count - 1 do
  221. begin
  222. LTbl.Add(' <TR>');
  223. //we want the name hyperlinked to it's location so a user can click on it in a browser
  224. //to retreive a file.
  225. MakeFileNameLink(AURL,AFTP.DirectoryListing[i].FileName,LTbl);
  226. case AFTP.DirectoryListing[i].ItemType of
  227. ditDirectory : LTmp := 'Directory';
  228. ditFile : LTmp := 'File';
  229. ditSymbolicLink, ditSymbolicLinkDir : LTmp := 'Symbolic link';
  230. ditBlockDev : LTmp := 'Block Device';
  231. ditCharDev : LTmp := 'Char Device';
  232. ditFIFO : LTmp := 'Pipe';
  233. ditSocket : LTmp := 'Socket';
  234. end;
  235. WriteTableCell(LTmp,LTbl);
  236. //Some dir formats will not return a file size or will only do so in some cases.
  237. if AFTP.DirectoryListing[i].SizeAvail then
  238. begin
  239. WriteTableCell(IntToStr(AFTP.DirectoryListing[i].Size),LTbl);
  240. end
  241. else
  242. begin
  243. WriteTableCell('',LTbl);
  244. end;
  245. //Some dir formats will not return a file date or will only do so in some cases.
  246. if AFTP.DirectoryListing[i].ModifiedAvail then
  247. begin
  248. WriteTableCell(DateTimeToStr(AFTP.DirectoryListing[i].Size),LTbl);
  249. end
  250. else
  251. begin
  252. WriteTableCell('',LTbl);
  253. end;
  254. WriteTableCell(AFTP.DirectoryListing[i].PermissionDisplay,LTbl);
  255. //get owner name
  256. if AFTP.DirectoryListing[i] is TIdOwnerFTPListItem then
  257. begin
  258. WriteTableCell(TIdOwnerFTPListItem(AFTP.DirectoryListing[i]).OwnerName,LTbl);
  259. end
  260. else
  261. begin
  262. WriteTableCell('',LTbl);
  263. end;
  264. //now get group name
  265. if AFTP.DirectoryListing[i] is TIdTandemGuardianFTPListItem then
  266. begin
  267. WriteTableCell(TIdTandemGuardianFTPListItem(AFTP.DirectoryListing[i]).GroupName,LTbl);
  268. end;
  269. if AFTP.DirectoryListing[i] is TIdUnixBaseFTPListItem then
  270. begin
  271. WriteTableCell(TIdUnixBaseFTPListItem(AFTP.DirectoryListing[i]).GroupName,LTbl);
  272. end;
  273. if AFTP.DirectoryListing[i] is TIdVMSFTPListItem then
  274. begin
  275. WriteTableCell(TIdVMSFTPListItem(AFTP.DirectoryListing[i]).GroupName,LTbl);
  276. end;
  277. LTbl.Add(' </TR>');
  278. end;
  279. LTbl.Add(' </TABLE>');
  280. LTbl.Add(' </BODY>');
  281. LTbl.Add('</HTML>');
  282. {$IFDEF STRING_IS_UNICODE}
  283. LTbl.SaveToFile('index.html', TEncoding.UTF8)
  284. {$ELSE}
  285. LTbl.SaveToFile('index.html');
  286. {$ENDIF}
  287. finally
  288. FreeAndNil(LTbl);
  289. end;
  290. end;
  291. procedure TFTPProtHandler.OnSent(ASender: TComponent; const AText: string; const AData: string);
  292. var LData : String;
  293. begin
  294. LData := AData;
  295. if TextStartsWith(LData,'PASS ') then begin
  296. FLogData.Text := FLogData.Text + 'PASS ****';
  297. end;
  298. FLogData.Text := FLogData.Text + LData;
  299. if FVerbose then begin
  300. Write({$IFDEF FPC}stdout{$ELSE}output{$ENDIF},LData);
  301. end;
  302. end;
  303. procedure TFTPProtHandler.OnDataChannelCreating(ASender: TObject;
  304. ADataChannel: TIdTCPConnection);
  305. begin
  306. WriteLn({$IFDEF FPC}stdout{$ELSE}output{$ENDIF},'Opening Data Channel');
  307. end;
  308. procedure TFTPProtHandler.OnDataChannelDestroy(ASender: TObject;
  309. ADataChannel: TIdTCPConnection);
  310. begin
  311. WriteLn({$IFDEF FPC}stdout{$ELSE}output{$ENDIF},'Closing Data Channel');
  312. end;
  313. procedure TFTPProtHandler.OnDirParseEnd(ASender: TObject);
  314. begin
  315. WriteLn({$IFDEF FPC}stdout{$ELSE}output{$ENDIF},'DIR Parsing finished');
  316. end;
  317. procedure TFTPProtHandler.OnDirParseStart(ASender: TObject);
  318. begin
  319. WriteLn('Dir Parsing Started');
  320. end;
  321. procedure TFTPProtHandler.OnReceived(ASender: TComponent; const AText: string; const AData: string);
  322. begin
  323. FLogData.Text := FLogData.Text + AData;
  324. if FVerbose then
  325. begin
  326. Write({$IFDEF FPC}stdout{$ELSE}output{$ENDIF},AData);
  327. end;
  328. end;
  329. {$ENDIF}
  330. end.