FTPSERV1.PAS
上传用户:xdgkgcw
上传日期:2007-01-04
资源大小:92k
文件大小:24k
源码类别:

Delphi控件源码

开发平台:

WINDOWS

  1. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2. Author:       Fran鏾is PIETTE
  3. Description:  This is a demo program showing how to use the TFtpServer
  4.               component to build a FTP server.
  5. Creation:     April 21, 1998
  6. Version:      1.01
  7. EMail:        francois.piette@pophost.eunet.be
  8.               francois.piette@rtfm.be             http://www.rtfm.be/fpiette
  9. Support:      Use the mailing list twsocket@rtfm.be See website for details.
  10. Legal issues: Copyright (C) 1996, 1997, 1998 by Fran鏾is PIETTE
  11.               Rue de Grady 24, 4053 Embourg, Belgium. Fax: +32-4-365.74.56
  12.               <francois.piette@pophost.eunet.be>
  13.               This software is provided 'as-is', without any express or
  14.               implied warranty.  In no event will the author be held liable
  15.               for any  damages arising from the use of this software.
  16.               Permission is granted to anyone to use this software for any
  17.               purpose, including commercial applications, and to alter it
  18.               and redistribute it freely, subject to the following
  19.               restrictions:
  20.               1. The origin of this software must not be misrepresented,
  21.                  you must not claim that you wrote the original software.
  22.                  If you use this software in a product, an acknowledgment
  23.                  in the product documentation would be appreciated but is
  24.                  not required.
  25.               2. Altered source versions must be plainly marked as such, and
  26.                  must not be misrepresented as being the original software.
  27.               3. This notice may not be removed or altered from any source
  28.                  distribution.
  29. History:
  30. Apr 29, 1998  V0.90 Released for beta testing.
  31. Apr 30, 1998  V0.91 Added an example of virtual file (see the code for
  32.               FtpServer1RetrSessionConnected.
  33. May 01, 1998  V0.92 Adapted for Delphi 1.0
  34. May 03, 1998  V0.93 Adapted for Delphi 2.0 and C++Builder
  35. May 04, 1998  V0.94 Added tools menu.
  36. Jul 09, 1998  V1.00 Adapted for Delphi 4, removed beta status.
  37. Jul 21, 1998  V1.01 Show how to refuse a client in OnClientConnected
  38.  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  39. unit FtpServ1;
  40. interface
  41. uses
  42.   WinTypes, WinProcs, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  43.   Dialogs, IniFiles, FtpSrv, FtpSrvC, WSocket, StdCtrls, ExtCtrls, Menus;
  44. const
  45.   FtpServVersion = 101;
  46.   WM_APPSTARTUP  = WM_USER + 1;
  47. type
  48.   TLogMsg = class(TComponent)
  49.   public
  50.      procedure Text(Prefix : Char; Msg : String);
  51.   end;
  52.   TFtpServerForm = class(TForm)
  53.     FtpServer1: TFtpServer;
  54.     InfoMemo: TMemo;
  55.     Panel1: TPanel;
  56.     StartMinimizedCheckBox: TCheckBox;
  57.     MainMenu1: TMainMenu;
  58.     File1: TMenuItem;
  59.     MnuStartServer: TMenuItem;
  60.     MnuStopServer: TMenuItem;
  61.     MnuQuit: TMenuItem;
  62.     N1: TMenuItem;
  63.     About1: TMenuItem;
  64.     GreenImage: TImage;
  65.     ClientCountLabel: TLabel;
  66.     RedImage: TImage;
  67.     Tools1: TMenuItem;
  68.     Cleardisplay1: TMenuItem;
  69.     procedure FormCreate(Sender: TObject);
  70.     procedure FtpServer1ClientConnect(Sender: TObject;
  71.       Client: TFtpCtrlSocket; Error: Word);
  72.     procedure FtpServer1ClientDisconnect(Sender: TObject;
  73.       Client: TFtpCtrlSocket; Error: Word);
  74.     procedure FtpServer1Start(Sender: TObject);
  75.     procedure FtpServer1Stop(Sender: TObject);
  76.     procedure FtpServer1ClientCommand(Sender: TObject;
  77.       Client: TFtpCtrlSocket; var Keyword, Params, Answer: TFtpString);
  78.     procedure FtpServer1StorSessionConnected(Sender: TObject;
  79.       Client: TFtpCtrlSocket; Data: TWSocket; Error: Word);
  80.     procedure FtpServer1StorSessionClosed(Sender: TObject;
  81.       Client: TFtpCtrlSocket; Data: TWSocket; Error: Word);
  82.     procedure FtpServer1RetrDataSent(Sender: TObject;
  83.       Client: TFtpCtrlSocket; Data: TWSocket; Error: Word);
  84.     procedure FtpServer1RetrSessionConnected(Sender: TObject;
  85.       Client: TFtpCtrlSocket; Data: TWSocket; Error: Word);
  86.     procedure FtpServer1RetrSessionClosed(Sender: TObject;
  87.       Client: TFtpCtrlSocket; Data: TWSocket; Error: Word);
  88.     procedure FormShow(Sender: TObject);
  89.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  90.     procedure FtpServer1AnswerToClient(Sender: TObject;
  91.       Client: TFtpCtrlSocket; var Answer: TFtpString);
  92.     procedure FtpServer1Authenticate(Sender: TObject;
  93.       Client: TFtpCtrlSocket; UserName, Password: TFtpString;
  94.       var Authenticated: Boolean);
  95.     procedure FtpServer1ChangeDirectory(Sender: TObject;
  96.       Client: TFtpCtrlSocket; Directory: TFtpString; var Allowed: Boolean);
  97.     procedure MnuQuitClick(Sender: TObject);
  98.     procedure MnuStopServerClick(Sender: TObject);
  99.     procedure MnuStartServerClick(Sender: TObject);
  100.     procedure ImagesDblClick(Sender: TObject);
  101.     procedure FtpServer1BuildDirectory(Sender: TObject;
  102.       Client: TFtpCtrlSocket; var Directory: TFtpString; Detailed: Boolean);
  103.     procedure FtpServer1AlterDirectory(Sender: TObject;
  104.       Client: TFtpCtrlSocket; var Directory: TFtpString; Detailed: Boolean);
  105.     procedure Cleardisplay1Click(Sender: TObject);
  106.   private
  107.     FInitialized      : Boolean;
  108.     FIniFileName      : String;
  109.     FPort             : String;
  110.     FXTop             : Integer;
  111.     FXLeft            : Integer;
  112.     FXWidth           : Integer;
  113.     FXHeight          : Integer;
  114.     procedure WMAppStartup(var msg: TMessage); message WM_APPSTARTUP;
  115.     procedure LoadConfig;
  116.     procedure SaveConfig;
  117.     procedure StartServer;
  118.     procedure StopServer;
  119.     procedure UpdateClientCount;
  120.   end;
  121. var
  122.   FtpServerForm: TFtpServerForm;
  123.   Log          : TLogMsg;
  124. implementation
  125. {$R *.DFM}
  126. const
  127.     MainTitle         = 'FTP Server - http://www.rtfm.be/fpiette';
  128.     { Ini file layout }
  129.     SectionData       = 'Data';
  130.     KeyPort           = 'Port';
  131.     SectionWindow     = 'Window';
  132.     KeyTop            = 'Top';
  133.     KeyLeft           = 'Left';
  134.     KeyWidth          = 'Width';
  135.     KeyHeight         = 'Height';
  136.     KeyMinim          = 'RunMinimized';
  137.     STATUS_GREEN      = 0;
  138.     STATUS_YELLOW     = 1;
  139.     STATUS_RED        = 2;
  140. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  141. procedure TLogMsg.Text(Prefix : Char; Msg : String);
  142. begin
  143. end;
  144. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  145. procedure TFtpServerForm.FormShow(Sender: TObject);
  146. var
  147.     IniFile : TIniFile;
  148.     Minim   : Integer;
  149. begin
  150.     if not FInitialized then begin
  151.         FInitialized        := TRUE;
  152.         Caption             := 'Starting ' + MainTitle;
  153.         Left := -Width;
  154.         IniFile  := TIniFile.Create(FIniFileName);
  155.         FXTop    := IniFile.ReadInteger(SectionWindow, KeyTop,    Top);
  156.         FXLeft   := IniFile.ReadInteger(SectionWindow, KeyLeft,   Left);
  157.         FXWidth  := IniFile.ReadInteger(SectionWindow, KeyWidth,  Width);
  158.         FXHeight := IniFile.ReadInteger(SectionWindow, KeyHeight, Height);
  159.         Minim    := IniFile.ReadInteger(SectionWindow, KeyMinim,  0);
  160.         IniFile.Free;
  161.         LoadConfig;
  162.         SaveConfig;    { Create the inifile keys if they don't exists }
  163.         { Be sure to always have the window visible }
  164.         { with a reasonable width and height        }
  165.         if FXLeft < 0 then
  166.             FXLeft := 0;
  167.         if FXTop < 0 then
  168.             FXTop := 0;
  169.         if FXWidth < 310 then
  170.             FXWidth := 310;
  171.         if FXHeight <= 250 then
  172.             FXHeight := 250;
  173.         if (FXLeft + FXWidth) > Screen.Width then
  174.             FXLeft := Screen.Width - FXWidth;
  175.         if (FXTop + FXHeight) > Screen.Height then
  176.             FXTop := Screen.Height - FXHeight;
  177.         StartMinimizedCheckBox.Checked := (Minim <> 0);
  178.         { We use a custom message to initialize things once the form }
  179.         { is visible                                                 }
  180.         PostMessage(Handle, WM_APPSTARTUP, 0, 0);
  181.     end;
  182. end;
  183. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  184. procedure TFtpServerForm.FormClose(Sender: TObject;
  185.   var Action: TCloseAction);
  186. var
  187.     IniFile : TIniFile;
  188.     Minim   : Integer;
  189. begin
  190.     try
  191.         StopServer;
  192.         Minim   := ord(StartMinimizedCheckBox.Checked);
  193.         IniFile := TIniFile.Create(FIniFileName);
  194.         IniFile.WriteInteger(SectionWindow, KeyTop,    Top);
  195.         IniFile.WriteInteger(SectionWindow, KeyLeft,   Left);
  196.         IniFile.WriteInteger(SectionWindow, KeyWidth,  Width);
  197.         IniFile.WriteInteger(SectionWindow, KeyHeight, Height);
  198.         IniFile.WriteInteger(SectionWindow, KeyMinim,  Minim);
  199.         IniFile.WriteString(SectionData,    KeyPort,   FPort);
  200.         IniFile.Free;
  201.     except
  202.         { Ignore any exception when we are closing }
  203.     end;
  204. end;
  205. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  206. procedure TFtpServerForm.LoadConfig;
  207. var
  208.     IniFile : TIniFile;
  209. begin
  210.     IniFile := TIniFile.Create(FIniFileName);
  211.     FPort   := IniFile.ReadString(SectionData,    KeyPort,   'ftp');
  212.     IniFile.Free;
  213. end;
  214. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  215. procedure TFtpServerForm.SaveConfig;
  216. var
  217.     IniFile : TIniFile;
  218. begin
  219.     IniFile := TIniFile.Create(FIniFileName);
  220.     IniFile.WriteString(SectionData, KeyPort, FPort);
  221.     IniFile.Free;
  222. end;
  223. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  224. { This message handler is triggered by the FormShow event. We comes here    }
  225. { only when the form is visible on screen.                                  }
  226. procedure TFtpServerForm.WMAppStartup(var msg: TMessage);
  227. var
  228.     PrvWnd  : HWND;
  229.     Buf     : String;
  230. begin
  231.     if StartMinimizedCheckBox.Checked then
  232.         Application.Minimize;
  233.     Top    := FXTop;
  234.     Left   := FXLeft;
  235.     Width  := FXWidth;
  236.     Height := FXHeight;
  237.     { Prevent the server from running twice }
  238.     Buf    := ClassName + #0;
  239.     PrvWnd := FindWindow(@Buf[1], MainTitle);
  240.     if PrvWnd <> 0 then begin
  241.         Log.Text('E', 'Server already running. Shutdown.');
  242.         Close;
  243.         Exit;
  244.     end;
  245.     Caption := MainTitle;
  246.     Update;                { It's nice to have the form completely displayed }
  247.     StartServer;
  248.     UpdateClientCount;
  249. end;
  250. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  251. {$IFNDEF VER80 }
  252. { To debug event driven programs, it is often handy to just use writeln to  }
  253. { write debug messages to the console. To get a console, just ask the       }
  254. { linker to build a console mode application. Then you'll get the default   }
  255. { console. The function below will make it the size you like...             }
  256. procedure BigConsole(nCols, nLines : Integer);
  257. var
  258.     sc : TCoord;
  259.     N  : DWord;
  260. begin
  261.     if not IsConsole then
  262.         Exit;
  263.     sc.x := nCols;
  264.     sc.y := nLines;
  265.     SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), sc);
  266.     SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
  267.                             BACKGROUND_BLUE or BACKGROUND_GREEN or
  268.                             BACKGROUND_RED or BACKGROUND_INTENSITY);
  269.     sc.x := 0;
  270.     sc.y := 0;
  271.     FillConsoleOutputAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
  272.                                BACKGROUND_BLUE or BACKGROUND_GREEN or
  273.                                BACKGROUND_RED or BACKGROUND_INTENSITY,
  274.                                nCols * nLines, sc, N);
  275. end;
  276. {$ENDIF}
  277. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  278. procedure TFtpServerForm.FormCreate(Sender: TObject);
  279. begin
  280.     { Build Ini file name }
  281.     FIniFileName := LowerCase(ExtractFileName(Application.ExeName));
  282.     FIniFileName := Copy(FIniFileName, 1, Length(FIniFileName) - 3) + 'ini';
  283.     { Create the Log object }
  284.     Log := TLogMsg.Create(Self);
  285. {$IFNDEF VER80}
  286.     BigConsole(80, 100);
  287. {$ENDIF}
  288.     InfoMemo.Clear;
  289.     GreenImage.Visible := FALSE;
  290.     RedImage.Visible   := TRUE;
  291.     RedImage.Top       := GreenImage.Top;
  292.     RedImage.Left      := GreenImage.Left;
  293. end;
  294. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  295. procedure TFtpServerForm.StartServer;
  296. begin
  297.     GreenImage.Visible := FALSE;
  298.     RedImage.Visible   := TRUE;
  299.     Update;
  300.     FtpServer1.Start;
  301. end;
  302. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  303. procedure TFtpServerForm.StopServer;
  304. begin
  305.     FtpServer1.Stop;
  306.     FtpServer1.DisconnectAll;
  307. end;
  308. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  309. procedure TFtpServerForm.MnuQuitClick(Sender: TObject);
  310. begin
  311.     Close;
  312. end;
  313. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  314. procedure TFtpServerForm.MnuStopServerClick(Sender: TObject);
  315. begin
  316.     StopServer;
  317. end;
  318. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  319. procedure TFtpServerForm.MnuStartServerClick(Sender: TObject);
  320. begin
  321.     StartServer;
  322. end;
  323. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  324. procedure TFtpServerForm.ImagesDblClick(Sender: TObject);
  325. begin
  326.     if FtpServer1.Active then
  327.         StopServer
  328.     else
  329.         StartServer;
  330. end;
  331. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  332. procedure TFtpServerForm.UpdateClientCount;
  333. begin
  334.     if FtpServer1.ClientCount = 0 then
  335.         ClientCountLabel.Caption := 'No user'
  336.     else
  337.         ClientCountLabel.Caption := IntToStr(FtpServer1.ClientCount) + ' users';
  338. end;
  339. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  340. procedure TFtpServerForm.FtpServer1ClientConnect(Sender: TObject;
  341.   Client: TFtpCtrlSocket; Error: Word);
  342. begin
  343.     { The next test shows how to refuse a client }
  344.     if Client.GetPeerAddr = '193.121.12.25' then begin
  345.         Client.SendStr('421 Connection not allowed.' + #13#10);
  346.         Client.Close;
  347.         Exit;
  348.     end;
  349.     InfoMemo.Lines.Add('! ' + Client.GetPeerAddr + ' connected');
  350.     UpdateClientCount;
  351. end;
  352. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  353. procedure TFtpServerForm.FtpServer1ClientDisconnect(Sender: TObject;
  354.   Client: TFtpCtrlSocket; Error: Word);
  355. begin
  356.     InfoMemo.Lines.Add('! ' + Client.GetPeerAddr + ' disconnected');
  357.     UpdateClientCount;
  358. end;
  359. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  360. procedure TFtpServerForm.FtpServer1Start(Sender: TObject);
  361. begin
  362.     GreenImage.Visible := TRUE;
  363.     RedImage.Visible   := FALSE;
  364.     InfoMemo.Lines.Add('! Server started');
  365. end;
  366. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  367. procedure TFtpServerForm.FtpServer1Stop(Sender: TObject);
  368. begin
  369.     GreenImage.Visible := FALSE;
  370.     RedImage.Visible   := TRUE;
  371.     InfoMemo.Lines.Add('! Server stopped');
  372. end;
  373. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  374. procedure TFtpServerForm.FtpServer1StorSessionConnected(Sender: TObject;
  375.   Client: TFtpCtrlSocket; Data: TWSocket; Error: Word);
  376. begin
  377.     if Error <> 0 then
  378.         InfoMemo.Lines.Add('! ' + Client.GetPeerAddr +
  379.                            ' Data session failed to open. Error #' +
  380.                            IntToStr(Error));
  381. end;
  382. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  383. procedure TFtpServerForm.FtpServer1StorSessionClosed(Sender: TObject;
  384.   Client: TFtpCtrlSocket; Data: TWSocket; Error: Word);
  385. begin
  386.     if Error <> 0 then
  387.         InfoMemo.Lines.Add('! ' + Client.GetPeerAddr +
  388.                            ' Data session closed. Error #' + IntToStr(Error));
  389. end;
  390. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  391. procedure TFtpServerForm.FtpServer1RetrDataSent(Sender: TObject;
  392.   Client: TFtpCtrlSocket; Data: TWSocket; Error: Word);
  393. begin
  394.     if Error <> 0 then
  395.         InfoMemo.Lines.Add('! ' + Client.GetPeerAddr +
  396.                            ' Data sent. Error #' + IntToStr(Error));
  397. end;
  398. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  399. { This event handler is called when the data session for a get file has     }
  400. { been opened. This is a good place build a file or a stream if the data    }
  401. { requested is not already stored in a file on the file system.             }
  402. { This feature is very powerfull and enable the FTP protocol to be used to  }
  403. { retrieve any kind of data. It this sample, we just check for C:VIRTUAL   }
  404. { directory. If this directory is curent, then a TMemoryStream is created   }
  405. { on the fly with some data. If another directory is selected, the FTP      }
  406. { server works as any other: just send the requested file, if it exist !    }
  407. { This event handler is also a place where you can abort the file transfer. }
  408. { Simply trigger an exception and transfer will not take place.             }
  409. { Note that if you just wants to prohibe access to some directory or file,  }
  410. { the best place to code that is in the OnValidateGet or OnValidatePut      }
  411. { event handlers.                                                           }
  412. procedure TFtpServerForm.FtpServer1RetrSessionConnected(Sender: TObject;
  413.     Client : TFtpCtrlSocket;
  414.     Data   : TWSocket;
  415.     Error  : Word);
  416. var
  417.     Buf : String;
  418. begin
  419.     if Error <> 0 then
  420.         InfoMemo.Lines.Add('! ' + Client.GetPeerAddr +
  421.                            ' Data session connected. Error #' + IntToStr(Error))
  422.     else if Copy(UpperCase(Client.FilePath), 1, 19) = 'C:VIRTUALFORBIDEN' then
  423.         raise Exception.Create('Access prohibed !')
  424.     else if Copy(UpperCase(Client.FilePath), 1, 11) = 'C:VIRTUAL' then begin
  425.         InfoMemo.Lines.Add('! VIRTUAL FILE');
  426.         Client.UserData   := 1;        { Remember we created a stream }
  427.         if Assigned(Client.DataStream) then
  428.             Client.DataStream.Destroy; { Prevent memory leaks         }
  429.         Client.DataStream := TMemoryStream.Create;
  430.         Buf := 'This is a file created on the fly by the FTP server' + #13#10 +
  431.                'It could result of a query to a database or anything else.' + #13#10 +
  432.                'The request was: ''' + Client.FilePath + '''' + #13#10;
  433.         Client.DataStream.Write(Buf[1], Length(Buf));
  434.         Client.DataStream.Seek(0, 0);
  435.     end;
  436. end;
  437. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  438. procedure TFtpServerForm.FtpServer1RetrSessionClosed(Sender: TObject;
  439.   Client: TFtpCtrlSocket; Data: TWSocket; Error: Word);
  440. begin
  441.     if Error <> 0 then
  442.         InfoMemo.Lines.Add('! ' + Client.GetPeerAddr +
  443.                            ' Data session closed. Error #' + IntToStr(Error));
  444.     if Client.UserData = 1 then begin
  445.         { We created a stream for a virtual file or dir. Delete the TStream }
  446.         if Assigned(Client.DataStream) then begin
  447.             { There is no reason why we should not come here, but who knows ? }
  448.             Client.DataStream.Destroy;
  449.             Client.DataStream := nil;
  450.         end;
  451.         Client.UserData   := 0;     { Reset the flag }
  452.     end;
  453. end;
  454. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  455. { This event handler is called when the FTP component needs to build a      }
  456. { directory listing. You can just return without doing anything then the    }
  457. { component will build the directory for you, based on the actual disk      }
  458. { content. But you can also build your own directory listing with anything  }
  459. { you like in it. Just create a stream with the required content. The       }
  460. { example below construct a virtual directory when the user is on the       }
  461. { C:VIRTUAL subdirectory (use elsewhere in this sample program).           }
  462. procedure TFtpServerForm.FtpServer1BuildDirectory(
  463.     Sender        : TObject;
  464.     Client        : TFtpCtrlSocket;
  465.     var Directory : TFtpString;
  466.     Detailed      : Boolean);
  467. var
  468.     Buf : String;
  469. begin
  470.     if UpperCase(Client.Directory) <> 'C:VIRTUAL' then
  471.         Exit;
  472.     InfoMemo.Lines.Add('! VIRTUAL DIR');
  473.     Client.UserData   := 1;        { Remember we created a stream }
  474.     if Assigned(Client.DataStream) then
  475.         Client.DataStream.Destroy; { Prevent memory leaks         }
  476.     Client.DataStream := TMemoryStream.Create;
  477.     if Detailed then
  478.         { We need to format directory lines according to the Unix standard }
  479.         Buf :=
  480.       '-rwxrwxrwx   1 ftp      ftp            0 Apr 30 19:00 FORBIDEN' + #13#10 +
  481.       '-rwxrwxrwx   1 ftp      ftp            0 Apr 30 19:00 TEST' + #13#10 +
  482.       'drwxrwxrwx   1 ftp      ftp            0 Apr 30 19:00 SOME DIR' + #13#10
  483.     else
  484.         Buf := 'FORBIDEN' + #13#10 +
  485.                'TEST' + #13#10;
  486.     Client.DataStream.Write(Buf[1], Length(Buf));
  487.     Client.DataStream.Seek(0, 0);
  488. end;
  489. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  490. { This event handler is called by the FTP component once it has built the   }
  491. { directory listing. We can use this handler to alter the listing, adding   }
  492. { or removing some info. This sample add the 'virtual' directory.           }
  493. procedure TFtpServerForm.FtpServer1AlterDirectory(
  494.     Sender        : TObject;
  495.     Client        : TFtpCtrlSocket;
  496.     var Directory : TFtpString;
  497.     Detailed      : Boolean);
  498. var
  499.     Buf : String;
  500. begin
  501.     if UpperCase(Client.Directory) <> 'C:' then
  502.         Exit;
  503.     { Add our 'virtual' directory to the list }
  504.     if Detailed then begin
  505.         { We need to format directory lines according to the Unix standard }
  506.         Buf :=
  507.         'drwxrwxrwx   1 ftp      ftp            0 Apr 30 19:00 VIRTUAL' + #13#10;
  508.         Client.DataStream.Write(Buf[1], Length(Buf));
  509.     end;
  510. end;
  511. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  512. procedure TFtpServerForm.FtpServer1ClientCommand(Sender: TObject;
  513.   Client: TFtpCtrlSocket; var Keyword, Params, Answer: TFtpString);
  514. begin
  515.     InfoMemo.Lines.Add('< ' + Client.GetPeerAddr + ' ' +
  516.                        Keyword + ' ' + Params);
  517. end;
  518. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  519. procedure TFtpServerForm.FtpServer1AnswerToClient(Sender: TObject;
  520.   Client: TFtpCtrlSocket; var Answer: TFtpString);
  521. begin
  522.     InfoMemo.Lines.Add('> ' + Client.GetPeerAddr + ' ' + Answer)
  523. end;
  524. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  525. procedure TFtpServerForm.FtpServer1Authenticate(Sender: TObject;
  526.   Client: TFtpCtrlSocket; UserName, Password: TFtpString;
  527.   var Authenticated: Boolean);
  528. begin
  529.     { You should place here the code needed to authenticate the user. }
  530.     { For example a text file with all permitted username/password.   }
  531.     { If the user can't be authenticated, just set Authenticated to   }
  532.     { false before returning.                                         }
  533.     { It is also the right place to setup Client.HomeDir              }
  534.     { If you need to store info about the client for later processing }
  535.     { you can use Client.UserData to store a pointer to an object or  }
  536.     { a record with the needed info.                                  }
  537.     InfoMemo.Lines.Add('! ' + Client.GetPeerAddr +
  538.                        ' User ''' + UserName + ''' is authenticated');
  539. end;
  540. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  541. procedure TFtpServerForm.FtpServer1ChangeDirectory(Sender: TObject;
  542.   Client: TFtpCtrlSocket; Directory: TFtpString; var Allowed: Boolean);
  543. begin
  544. {$IFDEF NEVER}
  545.     { It the right place to check if a user has access to a given directory }
  546.     { The example below disable C: access to non root user.                }
  547.     if (UpperCase(Client.UserName) <> 'ROOT') and
  548.        (UpperCase(Client.Directory) = 'C:') then
  549.        Allowed := FALSE;
  550. {$ENDIF}
  551. end;
  552. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  553. procedure TFtpServerForm.Cleardisplay1Click(Sender: TObject);
  554. begin
  555.     InfoMemo.Clear;
  556. end;
  557. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  558. end.