FTPServer_console1.dpr 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129
  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: 15243: FTPServer_console1.dpr
  11. {
  12. { Rev 1.20 7/3/2004 3:15:50 AM JPMugaas
  13. { Checked in so everyone else can work on stuff while I'm away.
  14. }
  15. {
  16. Rev 1.19 3/15/2003 12:34:36 AM BGooijen
  17. Updated for new API
  18. }
  19. {
  20. { Rev 1.18 2/22/2003 04:56:36 AM JPMugaas
  21. { Updated for new API.
  22. }
  23. {
  24. { Rev 1.16 2/14/2003 12:10:46 PM JPMugaas
  25. { Fixed a potential security flaw where a CDUP could move up to a real
  26. { directory where the server was not intended to access. This occured with
  27. { MS-DOS emulation.
  28. }
  29. {
  30. { Rev 1.15 2/9/2003 02:59:54 PM JPMugaas
  31. { Enabled both implicit and explicit TLS support
  32. }
  33. {
  34. { Rev 1.14 2/8/2003 10:39:38 AM JPMugaas
  35. { Now fakes an inode value with Unix style and the -i switch.
  36. { Recursive dirs no longer permitted with the standard "myuser" account to
  37. { demonstrate policy with that.
  38. }
  39. {
  40. { Rev 1.13 2/8/2003 04:52:20 AM JPMugaas
  41. { FTP Server dir routines now always provide the . and .. sybmols even for
  42. { non-recursive dir listings. This is now selectively omitted with the
  43. { IdFTPList.ExportList function in an appropriate manner depending upon the
  44. { ListFormat property and if the "-A" switch is present in Unix.
  45. }
  46. {
  47. { Rev 1.12 2/5/2003 08:47:12 AM JPMugaas
  48. { Modified to support some Unix switches when in emulating Unix. More should
  49. { be supported as more is added.
  50. { The .. and . are now in the dir list only in Unix mode and only if the -A
  51. { switch was not passed.
  52. }
  53. {
  54. { Rev 1.11 2/4/2003 05:35:00 PM JPMugaas
  55. { Adjusted for new parameters. The FTP Server now also can do a recursive
  56. { listing.
  57. }
  58. {
  59. { Rev 1.10 2/3/2003 11:10:06 AM JPMugaas
  60. { Started port to Linux. Note that it still does not yet work. I also added
  61. { code for setting a "block count" so we can get a nice "total x" line for ls
  62. { -l emulation.
  63. }
  64. {
  65. { Rev 1.9 1/31/2003 01:20:04 PM JPMugaas
  66. { Now properly compiles.
  67. }
  68. {
  69. { Rev 1.8 1/31/2003 06:39:58 AM JPMugaas
  70. { Now only uses an arbitrary base directory instead of a specific drive when
  71. { refering to directories. This should prevent unintended read-write access to
  72. { a system or potential trouble (such as uploading a trojan horse).
  73. { Added an "administrative" account with the weakest security settings to
  74. { demonstrate selective security privilleges.
  75. { Disable STAT for normal users so that they can't use it to "fingerprint" a
  76. { system. Stat is still enabled on administrative account.
  77. { No longer identifies itself as the Indy Demo in the SYST command. We changed
  78. { the behavior of the SYST command and it's best to make the SYST description
  79. { as generic as possible anyway.
  80. }
  81. {
  82. { Rev 1.7 1/30/2003 02:49:40 AM JPMugaas
  83. { Updated exception handling fixes.
  84. }
  85. {
  86. { Rev 1.6 1/28/2003 04:08:54 PM JPMugaas
  87. { Updated test program for exceptions.
  88. }
  89. {
  90. { Rev 1.5 1/27/2003 05:06:30 AM JPMugaas
  91. { Added a Status information event.
  92. }
  93. {
  94. { Rev 1.4 1/27/2003 02:23:54 AM JPMugaas
  95. { Removed old commented code for an "XCRC" command since it now supported
  96. { differently (according to how IdFTP and CuteFTP Pro use it).
  97. { Commented out some code permissions and ownership since those can now use
  98. { coded defaults in the TIdFTPListItem object if none are provided.
  99. }
  100. {
  101. { Rev 1.2 1/25/2003 01:56:38 AM JPMugaas
  102. { Refinements for checksum commands. Checksum commands will now fail for dirs
  103. { instead of the connection being closed.
  104. }
  105. {
  106. Rev 1.1 1/23/2003 7:37:14 PM BGooijen
  107. fixed IdFTPServer1GetFileSize when the specified file doesn't exists,
  108. flashfxp uses this to check if the file already exists on the server.
  109. }
  110. {
  111. { Rev 1.0 1/21/2003 12:25:12 PM JPMugaas
  112. { Server Test for IdFTPServer and core.
  113. }
  114. program FTPServer_console;
  115. (*
  116. Sample of the usage of the TIdFtpServer component.
  117. Also shows how to use Indy in console apps
  118. Created by: Bas Gooijen ([email protected])
  119. Disclaimer:
  120. Use it at your own risk, it could contain bugs.
  121. Copyright:
  122. Freeware for all use
  123. *)
  124. {$APPTYPE console}
  125. {.$DEFINE LOGGING}
  126. {
  127. Note that the logging code can not work in a console application because the main
  128. thread does not run with a standard windows handle. Oh, well.
  129. }
  130. {.$DEFINE USESSL}
  131. uses
  132. Classes,
  133. {$IFDEF LOGGING}
  134. IdSync,
  135. {$ENDIF}
  136. {$IFDEF WIN32}
  137. windows,
  138. {$ENDIF}
  139. {$IFDEF LINUX}
  140. libc,
  141. {$ENDIF}
  142. sysutils,
  143. {$IFDEF WIN32}
  144. IdCompressorZLibEx,
  145. {$ENDIF}
  146. IdContext,
  147. IdExplicitTLSClientServerBase,
  148. IdFileSystemWin32,
  149. IdFTPCommon,
  150. IdFTPList,
  151. IdFTPListOutput,
  152. IdFTPServer,
  153. IdFTPServerContextBase,
  154. IdGlobal,
  155. IdGlobalProtocols,
  156. IdSSLOpenSSL,
  157. idtcpserver,
  158. IdSocketHandle,
  159. IdHashCRC,
  160. IdIOHandlerSocket,
  161. IdReply,
  162. IdReplyRFC,
  163. IdReplyFTP,
  164. IdStack;
  165. //for resolving the peer's IP address into a name
  166. type
  167. TFTPServer = class
  168. private
  169. { Private declarations }
  170. {$IFDEF WIN32}
  171. FCompressor : TIdCompressorZLibEx;
  172. {$ENDIF}
  173. FIdFTPServer: tIdFTPServer;
  174. {$IFDEF USESSL}
  175. FIdExplicit : TIdServerIOHandlerSSLOpenSSL;
  176. FIdImplicit : TIdServerIOHandlerSSLOpenSSL;
  177. FIdFTPSvrImplicit : TIdFTPServer;
  178. procedure FIdSSLPassGetPassword(var Password: String);
  179. {$ENDIF}
  180. {$IFDEF LOGGING}
  181. procedure FIdFTPServerOnBeforeCmd(ASender: TIdTCPServer; const AData: string;
  182. AContext: TIdContext);
  183. {$ENDIF}
  184. function FixUpBanner(const ABanner : String; AThread: TIdFTPServerContext) : String;
  185. procedure FIdFTPServerOnGreeting(ASender: TIdFTPServerContext; AGreeting : TIdReply);
  186. procedure FIdFTPServerOnQuit(ASender: TIdFTPServerContext; AGreeting : TIdReply);
  187. procedure FIdFTPServerOnLoginSuccessfulBanner(ASender: TIdFTPServerContext; AGreeting : TIdReply);
  188. procedure FIdFTPServerOnLoginFailureBanner(ASender: TIdFTPServerContext; AGreeting : TIdReply);
  189. procedure FIdFTPServerUserLogin( ASender: TIdFTPServerContext; const AUsername, APassword: string; var AAuthenticated: Boolean );
  190. procedure FIdFTPServerListDirectory(ASender: TIdFTPServerContext; const APath: string;
  191. ADirectoryListing: TIdFTPListOutput; const ACmd : String; const ASwitches : String);
  192. procedure FIdFTPServerRenameFile( ASender: TIdFTPServerContext; const ARenameFromFile, ARenameToFile: string ) ;
  193. procedure FIdFTPServerRetrieveFile( ASender: TIdFTPServerContext; const AFilename: string; var VStream: TStream );
  194. procedure FIdFTPServerStoreFile( ASender: TIdFTPServerContext; const AFilename: string; AAppend: Boolean; var VStream: TStream );
  195. procedure FIdFTPServerRemoveDirectory( ASender: TIdFTPServerContext; var VDirectory: string ) ;
  196. procedure FIdFTPServerMakeDirectory( ASender: TIdFTPServerContext; var VDirectory: string ) ;
  197. procedure FIdFTPServerGetFileSize( ASender: TIdFTPServerContext; const AFilename: string; var VFileSize: Int64 );
  198. procedure FIdFTPServerSetFileDate(ASender: TIdFTPServerContext; const AFileName : String; var AFileTime : TDateTime);
  199. procedure FIdFTPServerDeleteFile( ASender: TIdFTPServerContext; const APathname: string ) ;
  200. procedure FIdFTPServerChangeDirectory( ASender: TIdFTPServerContext; var VDirectory: string ) ;
  201. // procedure FIdFTPServerCommandXCRC( ASender: TIdCommand ) ;
  202. procedure FIdFTPServerDisConnect( AThread: TIdContext ) ;
  203. procedure FIdFTPServerCombine(ASender: TIdFTPServerContext; const ATargetFileName: string; AParts : TStrings) ;
  204. procedure FIdFTPServerCRC(ASender: TIdFTPServerContext; const AFileName : String; var AIOStream : TStream);
  205. procedure FIdFTPServerStat(ASender: TIdFTPServerContext; AStatusInfo : TStrings);
  206. function PathSep : String;
  207. protected
  208. procedure RecurseFilesList(const APath, AHomeDir : String; ADir : TIdFTPListOutput);
  209. function UndoTranslatePath(const APathName, HomeDir : String) : String;
  210. function TransLatePath( const APathname, homeDir: string ) : string;
  211. public
  212. constructor Create; reintroduce;
  213. destructor Destroy; override;
  214. end;
  215. type EPermissionDenied = class(Exception)
  216. Constructor CreateMsg;
  217. end;
  218. {$I IdCompilerDefines.inc}
  219. {$IFDEF LOGGING}
  220. TLogMsgSync = class(TIdSync)
  221. protected
  222. FMsgLog : String;
  223. //
  224. procedure DoSynchronize; override;
  225. public
  226. class procedure LogMsg(AContext : TIdContext;const APeerIP : String; const AData : String);
  227. end;
  228. {$ENDIF}
  229. function MakePathStr(const APath : String): String;
  230. begin
  231. {$IFDEF VCL6ORABOVE}
  232. Result := SysUtils.IncludeTrailingPathDelimiter(APath);
  233. {$ELSE}
  234. Result := IncludeTrailingBackSlash(APath);
  235. {$ENDIF}
  236. end;
  237. {$IFDEF USESSL}
  238. function GetSSLCertPath : String;
  239. begin
  240. Result := MakePathStr(ExtractFilePath(ParamStr(0))+'cert');
  241. end;
  242. {$ENDIF}
  243. function GetBasePath : String;
  244. begin
  245. Result := MakePathStr(ExtractFilePath(ParamStr(0))+'home');
  246. end;
  247. {$IFNDEF VCL6ORABOVE}
  248. {$IFDEF WIN32}
  249. //This is necessary for D4 and D5 because adding FileCtrl will trigger the
  250. //VCL library being linked in which adds unneeded bloat.
  251. //Obtained from:
  252. // http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&threadm=3addbfb1.249423802%40forums.borland.com&rnum=8&prev=/groups%3Fq%3DDirectoryExists%2BDelphi%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3Dutf-8%26selm%3D3addbfb1.249423802%2540forums.borland.com%26rnum%3D8
  253. function DirectoryExists(const Name: string): Boolean;
  254. var
  255. Code: Integer;
  256. begin
  257. Code := GetFileAttributes(PChar(Name));
  258. Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
  259. end;
  260. {$ENDIF}
  261. {$ENDIF}
  262. procedure MakeSureRealBasePathExists;
  263. begin
  264. if (FileExists(GetBasePath)=False) and (DirectoryExists(GetBasePath)=False) then
  265. begin
  266. MkDir(GetBasePath);
  267. end;
  268. end;
  269. function HashStr(const AString : String): Cardinal;
  270. var
  271. value: Cardinal;
  272. IdHashCRC32: TIdHashCRC32;
  273. begin
  274. IdHashCRC32 := TIdHashCRC32.create;
  275. try
  276. Result := IdHashCRC32.HashValue(AString);
  277. finally
  278. IdHashCRC32.free;
  279. end;
  280. end;
  281. procedure AddlistItem( aDirectoryListing: TIdFTPListOutput; Filename: string; ItemType: TIdDirItemType; size: int64; AModTime, ACreateTime, AAccessTime : tdatetime ) ;
  282. var
  283. listitem: TIdFTPListOutputItem;
  284. function CalcBlocks(const ASize : Integer): Integer;
  285. begin
  286. //we give an estimated block count so we can export a total
  287. //line in the list to simulate /bin/ls -l.
  288. //Note that the total line is the number of blocks the files use.
  289. //In Linux, the block size for list is 1024 while in FreeBSD, it is 512
  290. //In addition, for each directory whose contents are displayed, the
  291. // total number of 512-byte blocks used by the files in the directory is
  292. // displayed on a line by itself immediately before the information for the
  293. // files in the directory.
  294. //URL: http://www.freebsd.org/cgi/man.cgi?query=ls&apropos=0&sektion=0&manpath=FreeBSD+4.7-RELEASE&format=html
  295. //and for linux, consulted info ls page and verified myself using the ls -ls command.
  296. Result := ASize div 512;
  297. if (ASize mod 512) > 0 then
  298. begin
  299. Inc(Result);
  300. end;
  301. end;
  302. begin
  303. listitem := aDirectoryListing.Add;
  304. listitem.ItemType := ItemType;
  305. if listitem.ItemType = ditDirectory then
  306. begin
  307. listitem.NumberBlocks := 1;
  308. end
  309. else
  310. begin
  311. listitem.NumberBlocks := CalcBlocks(Size);
  312. end;
  313. listitem.FileName := Filename;
  314. if aDirectoryListing.DirFormat = doUnix then
  315. begin
  316. //CygWin simply hashes the complete filename and then returns the hash value as the inode
  317. //On Linux, you get the inode value from the "stat" function
  318. listitem.Inode := HashStr(FileName);
  319. end;
  320. listitem.Size := size;
  321. listitem.ModifiedDateGMT := AModTime;
  322. listitem.CreationDateGMT := ACreateTime;
  323. listitem.LastAccessDateGMT := AAccessTime;
  324. if listitem.ItemType = ditDirectory then
  325. begin
  326. // listitem.MLISTPermissions := 'cdeflmp';
  327. listitem.MLISTPermissions := 'el';
  328. end
  329. else
  330. begin
  331. // listitem.MLISTPermissions := 'adfrw';
  332. listitem.MLISTPermissions := 'r';
  333. end;
  334. end;
  335. function RemoveTrailingPathDel(const AData : String) : String;
  336. begin
  337. Result := AData;
  338. if Result <> '' then
  339. begin
  340. if (Result[Length(Result)] = '/') or (Result[Length(Result)] = '\') then
  341. begin
  342. System.Delete(Result,Length(Result),1);
  343. end;
  344. end;
  345. end;
  346. {$IFDEF USESSL}
  347. procedure TFTPServer.FIdSSLPassGetPassword(var Password: String);
  348. begin
  349. Password := 'aaaa';
  350. end;
  351. {$ENDIF}
  352. procedure TFTPServer.RecurseFilesList(const APath, AHomeDir : String; ADir : TIdFTPListOutput);
  353. procedure AddFolder(APath, AHomeDir: string);
  354. var F: TExtSrchRec;
  355. LDir : String;
  356. const
  357. findTypes=faArchive+faHidden+faReadOnly+faAnyFile+faDirectory;
  358. begin
  359. LDir:= MakePathStr(APath);
  360. if ExFindFirst(TransLatePath(LDir+'*.*',AHomeDir), findTypes, F)=0 then
  361. try
  362. repeat
  363. if (F.attr and faDirectory=faDirectory) then
  364. begin
  365. AddListItem(ADir,LDir+f.Name,ditDirectory,0, f.ModifiedTimeGMT, f.AccessedTimeGMT, f.AccessedTimeGMT);
  366. if (F.Name<>'.') and (F.Name<>'..') then
  367. begin
  368. AddFolder(LDir+F.Name,AHomeDir);
  369. end;
  370. end
  371. else
  372. begin
  373. AddListItem(ADir,LDir+f.Name,ditFile, f.size, f.ModifiedTimeGMT, f.AccessedTimeGMT, f.AccessedTimeGMT);
  374. end;
  375. until ExFindNext(F)<>0;
  376. finally
  377. ExFindClose(F)
  378. end;
  379. end;
  380. begin
  381. AddFolder(APath,AHomeDir);
  382. end;
  383. constructor TFTPServer.Create;
  384. begin
  385. //we create a base dir if none exists so that the server will only use something
  386. //relative to that base directory instead of relative to a particular drive
  387. //Using something relative to a drive can have too many undesirable consequences
  388. //such as someone imbedding a trojan or getting access to something they shouldn't
  389. //have access to.
  390. MakeSureRealBasePathExists;
  391. FIdFTPServer := tIdFTPServer.create( nil ) ;
  392. FIdFTPServer.MLSDFacts := [mlsdPerms, mlsdUnixModes, mlsdFileCreationTime,mlsdFileLastAccessTime];
  393. {$IFDEF WIN32}
  394. FCompressor := TIdCompressorZLibEx.Create(nil);
  395. FIdFTPServer.Compressor := FCompressor;
  396. {$ENDIF}
  397. {$IFDEF LOGGING}
  398. FIdFTPServer.OnBeforeCommandHandler := FIdFTPServerOnBeforeCmd;
  399. {$ENDIF}
  400. {$IFDEF WIN32}
  401. FIdFTPServer.DefaultPort := 21;
  402. FIdFTPServer.DefaultDataPort := 20;
  403. {$ENDIF}
  404. {$IFDEF LINUX}
  405. //note that we use a different port because the standard FTP ports
  406. //are in the reserved range and only root can use those.
  407. FIdFTPServer.DefaultPort := 2100;
  408. FIdFTPServer.DefaultDataPort := 2000;
  409. {$ENDIF}
  410. {$IFDEF USESSL}
  411. FIdExplicit := TIdServerIOHandlerSSLOpenSSL.Create(nil);
  412. FIdExplicit.SSLOptions.RootCertFile := GetSSLCertPath + 'CAcert.crt';
  413. FIdExplicit.SSLOptions.CertFile := GetSSLCertPath + 'WSScert.pem';
  414. FIdExplicit.SSLOptions.KeyFile := GetSSLCertPath + 'WSSkey.pem';
  415. FIdExplicit.OnGetPassword := FIdSSLPassGetPassword;
  416. FIdExplicit.SSLOptions.Method :=sslvSSLv23 ;
  417. FIdExplicit.SSLOptions.Mode:= sslmUnassigned;
  418. FIdFTPServer.IOHandler := FIdExplicit;
  419. FIdFTPServer.UseTLS := utUseExplicitTLS;
  420. FIdImplicit := TIdServerIOHandlerSSLOpenSSL.Create(nil);
  421. FIdImplicit.SSLOptions.RootCertFile := GetSSLCertPath + 'CAcert.crt';
  422. FIdImplicit.SSLOptions.CertFile := GetSSLCertPath + 'WSScert.pem';
  423. FIdImplicit.SSLOptions.KeyFile := GetSSLCertPath + 'WSSkey.pem';
  424. FIdImplicit.OnGetPassword := FIdSSLPassGetPassword;
  425. FIdImplicit.SSLOptions.Method :=sslvSSLv23 ;
  426. FIdImplicit.SSLOptions.Mode:= sslmUnassigned;
  427. {$IFDEF WIN32}
  428. FIdImplicit.Compressor := FCompressor;
  429. {$ENDIF}
  430. {$ENDIF}
  431. FIdFTPServer.AllowAnonymousLogin := False;
  432. FIdFTPServer.OnGreeting := FIdFTPServerOnGreeting;
  433. FIdFTPServer.OnQuitBanner := FIdFTPServerOnQuit;
  434. FIdFTPServer.OnLoginSuccessBanner := FIdFTPServerOnLoginSuccessfulBanner;
  435. FIdFTPServer.OnLoginFailureBanner := FIdFTPServerOnLoginFailureBanner;
  436. FIdFTPServer.DirFormat := ftpdfUnix;
  437. FIdFTPServer.OnChangeDirectory := FIdFTPServerChangeDirectory;
  438. FIdFTPServer.OnChangeDirectory := FIdFTPServerChangeDirectory;
  439. FIdFTPServer.OnGetFileSize := FIdFTPServerGetFileSize;
  440. FIdFTPServer.OnListDirectory := FIdFTPServerListDirectory;
  441. FIdFTPServer.OnUserLogin := FIdFTPServerUserLogin;
  442. FIdFTPServer.OnRenameFile := FIdFTPServerRenameFile;
  443. FIdFTPServer.OnDeleteFile := FIdFTPServerDeleteFile;
  444. FIdFTPServer.OnRetrieveFile := FIdFTPServerRetrieveFile;
  445. FIdFTPServer.OnStoreFile := FIdFTPServerStoreFile;
  446. FIdFTPServer.OnMakeDirectory := FIdFTPServerMakeDirectory;
  447. FIdFTPServer.OnRemoveDirectory := FIdFTPServerRemoveDirectory;
  448. FIdFTPServer.Greeting.NumericCode := 220;
  449. FIdFTPServer.OnDisconnect := FIdFTPServerDisConnect;
  450. FIdFTPServer.OnSetModifiedTime := FIdFTPServerSetFileDate;
  451. FIdFTPServer.OnCombineFiles := FIdFTPServerCombine;
  452. FIdFTPServer.OnCRCFile := FIdFTPServerCRC;
  453. FIdFTPServer.OnStat := FIdFTPServerStat;
  454. //It is probably a good idea to disable this for some users
  455. //because the STAT command could be used to help "fingerprint" the system
  456. FIdFTPServer.FTPSecurityOptions.DisableSTATCommand := True;
  457. FIdFTPServer.FTPSecurityOptions.DisableSYSTCommand := True;
  458. {$IFDEF USESSL}
  459. FIdFTPSvrImplicit := TIdFTPServer.Create(nil);
  460. FIdFTPSvrImplicit.IOHandler := FIdImplicit;
  461. FIdFTPSvrImplicit.UseTLS := utUseImplicitTLS;
  462. FIdFTPSvrImplicit.AllowAnonymousLogin := False;
  463. FIdFTPSvrImplicit.OnGreeting := FIdFTPServerOnGreeting;
  464. FIdFTPSvrImplicit.OnQuitBanner := FIdFTPServerOnQuit;
  465. FIdFTPSvrImplicit.OnLoginSuccessBanner := FIdFTPServerOnLoginSuccessfulBanner;
  466. FIdFTPSvrImplicit.OnLoginFailureBanner := FIdFTPServerOnLoginFailureBanner;
  467. FIdFTPSvrImplicit.DirFormat := FIdFTPServer.DirFormat;
  468. FIdFTPSvrImplicit.OnChangeDirectory := FIdFTPServerChangeDirectory;
  469. FIdFTPSvrImplicit.OnChangeDirectory := FIdFTPServerChangeDirectory;
  470. FIdFTPSvrImplicit.OnGetFileSize := FIdFTPServerGetFileSize;
  471. FIdFTPSvrImplicit.OnListDirectory := FIdFTPServerListDirectory;
  472. FIdFTPSvrImplicit.OnUserLogin := FIdFTPServerUserLogin;
  473. FIdFTPSvrImplicit.OnRenameFile := FIdFTPServerRenameFile;
  474. FIdFTPSvrImplicit.OnDeleteFile := FIdFTPServerDeleteFile;
  475. FIdFTPSvrImplicit.OnRetrieveFile := FIdFTPServerRetrieveFile;
  476. FIdFTPSvrImplicit.OnStoreFile := FIdFTPServerStoreFile;
  477. FIdFTPSvrImplicit.OnMakeDirectory := FIdFTPServerMakeDirectory;
  478. FIdFTPSvrImplicit.OnRemoveDirectory := FIdFTPServerRemoveDirectory;
  479. FIdFTPSvrImplicit.Greeting.NumericCode := 220;
  480. FIdFTPSvrImplicit.OnDisconnect := FIdFTPServerDisConnect;
  481. FIdFTPSvrImplicit.OnSetModifiedTime := FIdFTPServerSetFileDate;
  482. FIdFTPSvrImplicit.OnCombineFiles := FIdFTPServerCombine;
  483. FIdFTPSvrImplicit.OnCRCFile := FIdFTPServerCRC;
  484. FIdFTPSvrImplicit.OnStat := FIdFTPServerStat;
  485. //It is probably a good idea to disable this for some users
  486. //because the STAT command could be used to help "fingerprint" the system
  487. FIdFTPSvrImplicit.FTPSecurityOptions.DisableSTATCommand := True;
  488. FIdFTPSvrImplicit.FTPSecurityOptions.DisableSYSTCommand := True;
  489. {$IFDEF LINUX}
  490. //note that we use a different port because the standard FTP ports
  491. //are in the reserved range and only root can use those.
  492. FIdFTPSvrImplicit.DefaultPort := 9100;
  493. FIdFTPSvrImplicit.DefaultDataPort := 9000;
  494. {$ENDIF}
  495. {$ENDIF}
  496. FIdFTPServer.Active := true;
  497. {$IFDEF USESSL}
  498. FIdFTPSvrImplicit.Active := True;
  499. {$ENDIF}
  500. end;
  501. function IsFileName( const AFileName : String): Boolean;
  502. var LFRec : TSearchRec;
  503. begin
  504. Result := False;
  505. if SysUtils.FindFirst(AFileName,faAnyFile,LFRec) = 0 then
  506. begin
  507. if LFRec.Attr and faDirectory = 0 then
  508. begin
  509. Result := True;
  510. end;
  511. SysUtils.FindClose(LFRec);
  512. end;
  513. end;
  514. function CalculateCRC( const path: string ) : string;
  515. var
  516. f: tfilestream;
  517. value: Cardinal;
  518. IdHashCRC32: TIdHashCRC32;
  519. begin
  520. IdHashCRC32 := nil;
  521. f := nil;
  522. try
  523. IdHashCRC32 := TIdHashCRC32.create;
  524. f := TFileStream.create( path, fmOpenRead or fmShareDenyWrite ) ;
  525. value := IdHashCRC32.HashValue( f ) ;
  526. result := inttohex( value, 8 ) ;
  527. finally
  528. f.free;
  529. IdHashCRC32.free;
  530. end;
  531. end;
  532. destructor TFTPServer.Destroy;
  533. begin
  534. FIdFTPServer.free;
  535. {$IFDEF USESSL}
  536. FreeAndNil( FIdExplicit );
  537. FreeAndNil(FIdFTPSvrImplicit);
  538. FreeAndNil(FIdFTPSvrImplicit);
  539. {$ENDIF}
  540. {$IFDEF WIN32}
  541. FreeAndNil(FCompressor );
  542. {$ENDIF}
  543. inherited destroy;
  544. end;
  545. function StartsWith( const str, substr: string ) : boolean;
  546. begin
  547. result := copy( str, 1, length( substr ) ) = substr;
  548. end;
  549. function BackSlashToSlash( const str: string ) : string;
  550. var
  551. a: Cardinal;
  552. begin
  553. result := str;
  554. for a := 1 to length( result ) do
  555. if result[a] = '\' then
  556. result[a] := '/';
  557. end;
  558. function SlashToBackSlash( const str: string ) : string;
  559. var
  560. a: Cardinal;
  561. begin
  562. result := str;
  563. for a := 1 to length( result ) do
  564. if result[a] = '/' then
  565. result[a] := '\';
  566. end;
  567. {$IFDEF LOGGING}
  568. procedure TFTPServer.FIdFTPServerOnBeforeCmd(ASender: TIdTCPServer; const AData: string;
  569. AContext: TIdContext);
  570. begin
  571. TLogMsgSync.LogMsg(AContext,TIdIOHandlerSocket(AContext.Connection.IOHandler).Binding.PeerIP,AData);
  572. end;
  573. {$ENDIF}
  574. function TFTPServer.UndoTranslatePath(const APathName, HomeDir : String) : String;
  575. begin
  576. Result := APathName;
  577. if Pos(SlashToBackSlash( homeDir ),APathName) = 0 then
  578. begin
  579. System.Delete(Result,1,Length(SlashToBackSlash( homeDir )));
  580. end;
  581. end;
  582. function TFTPServer.TransLatePath( const APathname, homeDir: string ) : string;
  583. var
  584. tmppath: string;
  585. begin
  586. try
  587. result := SlashToBackSlash( homeDir ) ;
  588. tmppath := SlashToBackSlash( APathname ) ;
  589. if homedir = '/' then
  590. begin
  591. result := tmppath;
  592. Result := GetBasePath+Result;
  593. exit;
  594. end;
  595. if length( APathname ) = 0 then
  596. exit;
  597. if result[length( result ) ] = '\' then
  598. result := copy( result, 1, length( result ) - 1 ) ;
  599. if tmppath[1] <> '\' then
  600. result := result + '\';
  601. result := result + tmppath;
  602. finally
  603. while (Copy(Result,1,2)='\\') or (Copy(Result,1,2)='//') do
  604. begin
  605. System.Delete(Result,1,1);
  606. end;
  607. end;
  608. Result := GetBasePath+Result;
  609. end;
  610. function GetSizeOfFile( const APathname: string ) : int64;
  611. begin
  612. result := FileSizeByName( APathname ) ;
  613. end;
  614. function GetNewDirectory( old, action: string ) : string;
  615. var
  616. a: integer;
  617. LAct : String;
  618. begin
  619. LAct := IndyGetFilePath(Action);
  620. //if just a dot, do nothing
  621. // if (action = './') or (action = '.\') then
  622. if LAct='.' then
  623. begin
  624. Result := Old;
  625. Exit;
  626. end;
  627. // if (action = '../') or (action = '..\') then
  628. if (LAct='..') then
  629. begin
  630. if (old = '/') or (old='\') then
  631. begin
  632. result := old;
  633. exit;
  634. end;
  635. a := length( old ) - 1;
  636. while ( old[a] <> '\' ) and ( old[a] <> '/' ) do
  637. dec( a ) ;
  638. result := copy( old, 1, a ) ;
  639. exit;
  640. end;
  641. if ( action[1] = '/' ) or ( action[1] = '\' ) then
  642. result := action
  643. else
  644. result := old + action;
  645. end;
  646. procedure TFTPServer.FIdFTPServerUserLogin( ASender: TIdFTPServerContext;
  647. const AUsername, APassword: string; var AAuthenticated: Boolean ) ;
  648. begin
  649. AAuthenticated := ( AUsername = 'myuser' ) and ( APassword = 'mypass' ) ;
  650. if not AAuthenticated then
  651. begin
  652. AAuthenticated := ( AUsername = 'admin' ) and ( APassword = 'myadminpass' ) ;
  653. if AAuthenticated then
  654. begin
  655. //for an administrative account, you might want the weakest security settings
  656. //while for anonymous FTP, you probably want something fairly strong
  657. ASender.UserSecurity.RequirePASVFromSameIP := False;
  658. ASender.UserSecurity.RequirePORTFromSameIP := False;
  659. ASender.UserSecurity.NoReservedRangePORT := False;
  660. ASender.UserSecurity.BlockAllPORTTransfers := False;
  661. ASender.UserSecurity.DisableSYSTCommand := False;
  662. ASender.UserSecurity.DisableSTATCommand := False;
  663. end;
  664. end;
  665. if not AAuthenticated then
  666. exit;
  667. if FIdFTPServer.DirFormat <> ftpdfDOS then
  668. begin
  669. ASender.UserSecurity.DisableSYSTCommand := False;
  670. ASender.HomeDir := '/';
  671. asender.currentdir := '/';
  672. end
  673. else
  674. begin
  675. ASender.HomeDir := '\';
  676. asender.currentdir := '\';
  677. end;
  678. end;
  679. procedure TFTPServer.FIdFTPServerListDirectory( ASender: TIdFTPServerContext; const APath: string; ADirectoryListing: TIdFTPListOutput; const ACmd : String; const ASwitches : String);
  680. var
  681. f: TExtSrchRec;
  682. a: integer;
  683. function DeletRSwitch(const AString : String):String;
  684. var i : Integer;
  685. begin
  686. Result := '';
  687. for i := 1 to Length(AString) do
  688. begin
  689. if AString[i]<>'R' then
  690. begin
  691. Result := Result + AString[i];
  692. end;
  693. end;
  694. end;
  695. begin
  696. if ASender.Username = 'myuser' then
  697. begin
  698. //It's probably best to prevent normal users from doing recursive dirs
  699. //because that can eat up more bandwidth and CPU cycles than a nromal DIR
  700. //list would. It's probably best only for mirroring software using a designated
  701. //account and for administrators of the system.
  702. ADirectoryListing.Switches := DeletRSwitch(ASwitches);
  703. end
  704. else
  705. begin
  706. ADirectoryListing.Switches := ASwitches;
  707. end;
  708. // ADirectoryListing.DirectoryName := apath;
  709. // if FileExists(TranslatePath(APath,ASender.HomeDir))=False then
  710. // begin
  711. // raise EPermissionDenied.CreateMsg;
  712. // end;
  713. //in MLST, we are asking for information about a particular item, not
  714. //asking for the complete contents of the item
  715. if (ACmd = 'MLST') then
  716. begin
  717. if RemoveTrailingPathDel (TransLatePath( apath, ASender.HomeDir )) = '' then
  718. begin
  719. AddlistItem( ADirectoryListing, '/', ditDirectory, 0, 0, 0,0);
  720. exit;
  721. end
  722. else
  723. begin
  724. a := ExFindFirst( RemoveTrailingPathDel (TransLatePath( apath, ASender.HomeDir )) , faAnyFile, f ) ;
  725. end;
  726. end
  727. else
  728. begin
  729. a := ExFindFirst( TransLatePath( apath, ASender.HomeDir ) + '*.*', faAnyFile, f ) ;
  730. end;
  731. if (FIdFTPServer.DirFormat <> ftpdfEPLF) and (Pos('R',ASwitches)>0) then
  732. begin
  733. ExFindClose( f ) ;
  734. RecurseFilesList(RemoveTrailingPathDel (apath), ASender.HomeDir,ADirectoryListing);
  735. Exit;
  736. end;
  737. while ( a = 0 ) do
  738. begin
  739. if (ADirectoryListing.DirFormat = doEPLF ) and ((f.Name = '.') or (f.Name = '..')) then
  740. begin
  741. AddlistItem( ADirectoryListing, ASender.HomeDir, ditDirectory, f.size, 0,0,0 );
  742. end
  743. else
  744. begin
  745. if ( f.Attr and faDirectory > 0 ) then
  746. begin
  747. //procedure AddlistItem( aDirectoryListing: TIdFTPListOutput; Filename: string; ItemType: TIdDirItemType; size: int64; AModTime, ACreateTime, AAccessTime : tdatetime ) ;
  748. AddlistItem( ADirectoryListing, f.Name, ditDirectory, f.size, f.ModifiedTimeGMT, f.CreateTimeGMT, f.AccessedTimeGMT);
  749. end
  750. else
  751. begin
  752. AddlistItem( ADirectoryListing, f.Name, ditFile, f.size, f.ModifiedTimeGMT, f.CreateTimeGMT, f.AccessedTimeGMT);
  753. end;
  754. end;
  755. a := ExFindNext( f ) ;
  756. end;
  757. ExFindClose( f ) ;
  758. end;
  759. procedure TFTPServer.FIdFTPServerRenameFile( ASender: TIdFTPServerContext;
  760. const ARenameFromFile, ARenameToFile: string ) ;
  761. begin
  762. {$IFDEF WIN32}
  763. if not MoveFile( pchar( TransLatePath( ARenameFromFile, ASender.HomeDir ) ) , pchar( TransLatePath( ARenameToFile, ASender.HomeDir ) ) ) then
  764. begin
  765. RaiseLastWin32Error;
  766. end;
  767. {$ELSE}
  768. {$ENDIF}
  769. end;
  770. procedure TFTPServer.FIdFTPServerRetrieveFile( ASender: TIdFTPServerContext;
  771. const AFilename: string; var VStream: TStream ) ;
  772. begin
  773. VStream := TFileStream.create( translatepath( AFilename, ASender.HomeDir ) , fmopenread or fmShareDenyWrite ) ;
  774. end;
  775. procedure TFTPServer.FIdFTPServerStoreFile( ASender: TIdFTPServerContext;
  776. const AFilename: string; AAppend: Boolean; var VStream: TStream ) ;
  777. begin
  778. if FileExists( translatepath( AFilename, ASender.HomeDir ) ) and AAppend then
  779. begin
  780. VStream := TFileStream.create( translatepath( AFilename, ASender.HomeDir ) , fmOpenWrite or fmShareExclusive ) ;
  781. VStream.Seek( 0, soFromEnd ) ;
  782. end
  783. else
  784. VStream := TFileStream.create( translatepath( AFilename, ASender.HomeDir ) , fmCreate or fmShareExclusive ) ;
  785. end;
  786. procedure TFTPServer.FIdFTPServerRemoveDirectory( ASender: TIdFTPServerContext;
  787. var VDirectory: string ) ;
  788. begin
  789. RmDir( TransLatePath( VDirectory, ASender.HomeDir ) ) ;
  790. end;
  791. procedure TFTPServer.FIdFTPServerMakeDirectory( ASender: TIdFTPServerContext;
  792. var VDirectory: string ) ;
  793. begin
  794. MkDir( TransLatePath( VDirectory, ASender.HomeDir ) ) ;
  795. end;
  796. procedure TFTPServer.FIdFTPServerGetFileSize( ASender: TIdFTPServerContext;
  797. const AFilename: string; var VFileSize: Int64 ) ;
  798. begin
  799. try
  800. VFileSize := GetSizeOfFile( TransLatePath( AFilename, ASender.HomeDir ) ) ;
  801. except
  802. VFileSize := -1;
  803. end;
  804. end;
  805. procedure TFTPServer.FIdFTPServerDeleteFile( ASender: TIdFTPServerContext;
  806. const APathname: string ) ;
  807. begin
  808. if DeleteFile( pchar( TransLatePath( ASender.CurrentDir + '/' + APathname, ASender.HomeDir ) ) )=False then
  809. begin
  810. raise EPermissionDenied.CreateMsg;
  811. end;
  812. end;
  813. procedure TFTPServer.FIdFTPServerChangeDirectory( ASender: TIdFTPServerContext;
  814. var VDirectory: string ) ;
  815. begin
  816. if DirectoryExists(TransLatePath(VDirectory, ASender.HomeDir)) then
  817. begin
  818. VDirectory := GetNewDirectory( ASender.CurrentDir, VDirectory ) ;
  819. end
  820. else
  821. begin
  822. raise EPermissionDenied.CreateMsg;
  823. end;
  824. end;
  825. procedure TFTPServer.FIdFTPServerDisConnect( AThread: TIdContext ) ;
  826. begin
  827. // nothing much here
  828. end;
  829. procedure TFTPServer.FIdFTPServerCombine(ASender: TIdFTPServerContext;
  830. const ATargetFileName: string; AParts: TStrings);
  831. var i : Integer;
  832. LSource, LDest : TStream;
  833. begin
  834. if FileExists(TransLatePath( ASender.CurrentDir + '/' + ATargetFileName, ASender.HomeDir )) then
  835. begin
  836. LDest := TFileStream.Create(TransLatePath( ASender.CurrentDir + '/' + ATargetFileName, ASender.HomeDir ) ,fmOpenReadWrite or fmShareExclusive);
  837. LDest.Seek(0,soFromEnd);
  838. end
  839. else
  840. begin
  841. LDest := TFileStream.Create(TransLatePath( ASender.CurrentDir + '/' + ATargetFileName, ASender.HomeDir ) ,fmCreate);
  842. end;
  843. try
  844. for i := 0 to AParts.Count -1 do
  845. begin
  846. LSource := TFileStream.Create( TransLatePath( ASender.CurrentDir + '/' + AParts[i], ASender.HomeDir ) ,
  847. fmopenread or fmShareDenyWrite);
  848. LDest.CopyFrom(LSource,0);
  849. FreeAndNil(LSource);
  850. end;
  851. //Do this separately in case there was a failure to find a source file part
  852. for i := 0 to AParts.Count-1 do
  853. begin
  854. FIdFTPServerDeleteFile(ASender,AParts[i]);
  855. end;
  856. finally
  857. FreeAndNil(LSource);
  858. FreeAndNil(LDest);
  859. end;
  860. end;
  861. procedure TFTPServer.FIdFTPServerCRC(ASender: TIdFTPServerContext; const AFileName: String; var AIOStream: TStream);
  862. var LFileName : String;
  863. begin
  864. LFileName := TransLatePath( ASender.CurrentDir + '/' + AFileName, ASender.HomeDir);
  865. AIOStream := TFileStream.create( LFileName, fmopenread or fmShareDenyWrite ) ;
  866. end;
  867. function DayPeriodGreeting : String;
  868. var LHour, LMin, LSec, LMSec : Word;
  869. //Note that we do not use Night for a greeting.
  870. //"Good Night" is used as a farewell, not a greeting
  871. begin
  872. DecodeTime(Time,LHour, LMin,LSec,LMSec);
  873. if LHour < 12 then
  874. begin
  875. Result := 'Morning';
  876. end
  877. else
  878. begin
  879. if LHour < 18 then
  880. begin
  881. Result := 'Afternoon';
  882. end
  883. else
  884. begin
  885. Result := 'Evening';
  886. end;
  887. end;
  888. end;
  889. function DayPeriodFairwell : String;
  890. var LHour, LMin, LSec, LMSec : Word;
  891. //Note that we do not use Night for a greeting.
  892. //"Good Night" is used as a farewell, not a greeting
  893. begin
  894. DecodeTime(Time,LHour, LMin,LSec,LMSec);
  895. if LHour < 19 then
  896. begin
  897. Result := 'Day';
  898. end
  899. else
  900. begin
  901. Result := 'Night';
  902. end;
  903. end;
  904. procedure TFTPServer.FIdFTPServerStat(ASender: TIdFTPServerContext; AStatusInfo : TStrings);
  905. var Line : String;
  906. begin
  907. AStatusInfo.Add('Connected to '+ GStack.HostByAddress(TIdIOHandlerSocket(ASender.Connection.IOHandler).Binding.PeerIP) );
  908. Line := 'Logged in as ';
  909. if (ASender.UserType = utAnonymousUser) then
  910. begin
  911. Line := Line + ASender.Password;
  912. end
  913. else
  914. begin
  915. Line := Line + ASender.Username;
  916. end;
  917. AStatusInfo.Add(Line);
  918. Line := 'TYPE: ';
  919. case ASender.DataType of
  920. ftASCII : Line := Line + 'ASCII';
  921. ftBinary : Line := Line + 'BINARY';
  922. end;
  923. Line := Line + ', FORM: Nonprint; STRUcture: ';
  924. case ASender.DataStruct of
  925. dsFile : Line := Line + 'File';
  926. dsRecord : Line := Line + 'Record';
  927. dsPage : Line := Line + 'Page';
  928. end;
  929. // Line := Line + '; transfer MODE: ';
  930. // case ASender.DataMode of
  931. // dmBlock : Line := Line + 'BLOCK';
  932. // dmCompressed : Line := Line + 'COMPRESSED';
  933. // dmStream : Line := Line + 'STREAM';
  934. // end;
  935. AStatusInfo.Add(Line);
  936. if Assigned(ASender.DataChannel) then
  937. begin
  938. AStatusInfo.Add('Data Connection Active');
  939. end
  940. else
  941. begin
  942. AStatusInfo.Add('No Data Connection');
  943. end;
  944. end;
  945. function TFTPServer.FixUpBanner(const ABanner : String; AThread: TIdFTPServerContext) : String;
  946. var LPeerHostName : String;
  947. LMyHostName : String;
  948. begin
  949. LPeerHostName := GStack.HostByAddress(TIdIOHandlerSocket(AThread.Connection.IOHandler).Binding.PeerIP);
  950. LMyHostName := GStack.HostByAddress(TIdIOHandlerSocket(AThread.Connection.IOHandler).Binding.IP);
  951. Result := StringReplace(ABanner,'%DAYGREETINGWORD%',DayPeriodGreeting,[rfReplaceAll]);
  952. Result := StringReplace(Result,'%DAYFAREWELLWORD%',DayPeriodFairwell,[rfReplaceAll]);
  953. Result := StringReplace(Result,'%PEERNAME%',LPeerHostName,[rfReplaceAll]);
  954. Result := StringReplace(Result,'%MYNAME%',LMyHostName,[rfReplaceAll]);
  955. Result := StringReplace(Result,'%USERNAME%',AThread.Username,[rfReplaceAll]);
  956. end;
  957. procedure TFTPServer.FIdFTPServerOnGreeting(ASender: TIdFTPServerContext; AGreeting : TIdReply);
  958. begin
  959. AGreeting.Text.Clear;
  960. AGreeting.Text.Add('Good %DAYGREETINGWORD%, user at %PEERNAME%.');
  961. AGreeting.Text.Add('');
  962. AGreeting.Text.Add('Welcome to the Internet Direct (Indy) demo running on %MYNAME%.');
  963. AGreeting.Text.Add('');
  964. AGreeting.Text.Add('Server at %MYNAME% ready.');
  965. AGreeting.Text.Text := FixUpBanner(AGreeting.Text.Text,ASender);
  966. { AGreeting.NumericCode := 421;
  967. AGreeting.Text.Clear;
  968. AGreeting.Text.Add('FTP Service has been disabled to prevent system abuse');
  969. AGreeting.Text.Add('');
  970. AGreeting.Text.Add('You may now only use our web site at http://www.oursite.com');
  971. AGreeting.Text.Add('to download our great software.');
  972. AGreeting.Text.Add('');
  973. AGreeting.Text.Add('Have a nice day.'); }
  974. end;
  975. procedure TFTPServer.FIdFTPServerOnQuit(ASender: TIdFTPServerContext; AGreeting : TIdReply);
  976. begin
  977. if ASender.Authenticated then
  978. begin
  979. AGreeting.Text.Clear;
  980. AGreeting.Text.Add('Good %DAYFAREWELLWORD%, %USERNAME%!!!');
  981. AGreeting.Text.Text := FixUpBanner(AGreeting.Text.Text,ASender);
  982. end;
  983. end;
  984. procedure TFTPServer.FIdFTPServerOnLoginSuccessfulBanner(ASender: TIdFTPServerContext; AGreeting : TIdReply);
  985. begin
  986. AGreeting.Text.Clear;
  987. AGreeting.Text.Add('User %USERNAME% logged in.');
  988. AGreeting.Text.Text := FixUpBanner(AGreeting.Text.Text,ASender);
  989. end;
  990. procedure TFTPServer.FIdFTPServerOnLoginFailureBanner(ASender: TIdFTPServerContext; AGreeting : TIdReply);
  991. begin
  992. AGreeting.Text.Clear;
  993. AGreeting.Text.Add('Login failed.');
  994. AGreeting.Text.Add('Visit from %PEERNAME% has been logged.');
  995. AGreeting.Text.Add('');
  996. AGreeting.Text.Add('Good %DAYFAREWELLWORD%!!!');
  997. AGreeting.Text.Text := FixUpBanner(AGreeting.Text.Text,ASender);
  998. end;
  999. procedure TFTPServer.FIdFTPServerSetFileDate(ASender: TIdFTPServerContext; const AFileName : String; var AFileTime : TDateTime);
  1000. begin
  1001. if GMTSetFileModifyDate(AFileName,AFileTime)<>0 then
  1002. begin
  1003. //in case we failed to set the date
  1004. AFileTime := GMTGetFileModifyDate(AFileName);
  1005. end;
  1006. end;
  1007. { EPermissionDenied }
  1008. constructor EPermissionDenied.CreateMsg;
  1009. begin
  1010. inherited Create('Permission Denied');
  1011. end;
  1012. function TFTPServer.PathSep: String;
  1013. begin
  1014. if FIdFTPServer.DirFormat = ftpdfUnix then
  1015. begin
  1016. Result := PATH_FILENAME_SEP_DOS;
  1017. end
  1018. else
  1019. begin
  1020. Result := PATH_FILENAME_SEP_UNIX;
  1021. end;
  1022. end;
  1023. {$IFDEF LOGGING}
  1024. { TLogMsgSync }
  1025. procedure TLogMsgSync.DoSynchronize;
  1026. begin
  1027. WriteLn(FMsgLog);
  1028. end;
  1029. class procedure TLogMsgSync.LogMsg(AContext : TIdContext; const APeerIP, AData: String);
  1030. begin
  1031. with Create do begin
  1032. FMsgLog := APeerIP + ': '+AData;
  1033. Synchronize;
  1034. // Notify;
  1035. end;
  1036. end;
  1037. {$ENDIF}
  1038. begin
  1039. with TFTPServer.Create do
  1040. try
  1041. writeln( 'Running, press [enter] to terminate' ) ;
  1042. readln;
  1043. finally
  1044. free;
  1045. end;
  1046. end.