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

Delphi控件源码

开发平台:

WINDOWS

  1. {*_* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2. Author:       Fran鏾is PIETTE
  3. Description:  TFtpCtrlSocket component. It handle the client connection for
  4.               the TFtpServer component.
  5. EMail:        francois.piette@pophost.eunet.be
  6.               francois.piette@rtfm.be             http://www.rtfm.be/fpiette
  7. Creation:     April 21, 1998
  8. Version:      1.03
  9. Support:      Use the mailing list twsocket@rtfm.be See website for details.
  10. Legal issues: Copyright (C) 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.               4. You must register this software by sending a picture postcard
  30.                  to the author. Use a nice stamp and mention your name, street
  31.                  address, EMail address and any comment you like to say.
  32. History:
  33. Apr 29, 1998  V0.90 released for beta testing.
  34. May 03, 1998  V0.93 Adapted for Delphi 2.0 and C++Builder
  35. May 04, 1998  V0.94 Added support for UNC (not finished !)
  36. Jul 09, 1998  V1.00 Adapted for Delphi 4, removed beta status.
  37. Jul 21, 1998  V1.01 Publised TrumpetCompatibility property.
  38. Aug 06, 1998  V1.02 Verified that FRcvCnt was 0 in SetRcvSize. Suggested
  39.               by Nick MacDonald <NickM@futurepace.net>
  40. Mar 06, 1999  V1.03 Added code from  Plegge, Steve <jsp@nciinc.com> to add
  41.               APPE and STRU support.
  42.  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  43. unit FtpSrvC;
  44. interface
  45. {$B-}           { Enable partial boolean evaluation   }
  46. {$T-}           { Untyped pointers                    }
  47. {$IFNDEF VER80}
  48.     {$J+}       { Allow typed constant to be modified }
  49. {$ENDIF}
  50. {$IFDEF VER110} { C++ Builder V3.0                    }
  51.     {$ObjExportAll On}
  52. {$ENDIF}
  53. uses
  54.     WinTypes, WinProcs, Messages, Classes, SysUtils, Winsock, WSocket;
  55. const
  56.     FtpCtrlSocketVersion = 103;
  57.     DefaultRcvSize       = 2048;
  58. type
  59.     EFtpCtrlSocketException = class(Exception);
  60.     TFtpCtrlState = (ftpcInvalid, ftpcWaitingUserCode, ftpcWaitingPassword,
  61.                      ftpcReady, ftpcWaitingAnswer);
  62.     TFtpCmdType   = (ftpcPORT, ftpcSTOR, ftpcRETR, ftpcCWD,  ftpcXPWD, ftpcPWD,
  63.                      ftpcUSER, ftpcPASS, ftpcLIST, ftpcRMD,  ftpcTYPE, ftpcSYST,
  64.                      ftpcQUIT, ftpcDELE, ftpcRNFR, ftpcMKD,  ftpcRNTO, ftpcNOOP,
  65.                      ftpcNLST, ftpcABOR, ftpcCDUP, ftpcSIZE, ftpcREST, ftpcAPPE,
  66.                      ftpcSTRU);  {jsp - Added APPE and STRU types}
  67.     TFtpOption    = (ftpcUNC);
  68.     TFtpOptions   = set of TFtpOption;
  69.     TDisplayEvent = procedure (Sender : TObject; Msg : String) of object;
  70.     TCommandEvent = procedure (Sender : TObject; CmdBuf : PChar; CmdLen : Integer) of object;
  71.     TFtpCtrlSocket = class(TCustomWSocket)
  72.     protected
  73.         FDataSocket        : TWSocket;
  74.         FRcvBuf            : PChar;
  75.         FRcvCnt            : Integer;
  76.         FRcvSize           : Integer;
  77.         FBusy              : Boolean;
  78.         FConnectedSince    : TDateTime;
  79.         FLastCommand       : TDateTime;
  80.         FCommandCount      : LongInt;
  81.         FBanner            : String;
  82.         FUserName          : String;
  83.         FPassWord          : String;
  84.         FCloseRequest      : Boolean;
  85.         FHomeDir           : String;
  86.         FDirectory         : String;
  87.         FFtpState          : TFtpCtrlState;
  88.         FAbortingTransfer  : Boolean;
  89.         FUserData          : LongInt;        { Reserved for component user }
  90.         FPeerAddr          : String;
  91.         FOnDisplay         : TDisplayEvent;
  92.         FOnCommand         : TCommandEvent;
  93.         procedure TriggerSessionConnected(Error : Word); override;
  94.         function  TriggerDataAvailable(Error : Word) : boolean; override;
  95.         procedure TriggerCommand(CmdBuf : PChar; CmdLen : Integer); virtual;
  96.         procedure SetRcvSize(newValue : Integer);
  97.     public
  98.         BinaryMode        : Boolean;
  99.         DataAddr          : String;
  100.         DataPort          : String;
  101.         FileName          : String;
  102.         FilePath          : String;
  103.         DataSessionActive : Boolean;
  104.         DataStream        : TStream;
  105.         HasOpenedFile     : Boolean;
  106.         TransferError     : String;
  107.         ByteCount         : LongInt;
  108.         DataSent          : Boolean;
  109.         CurCmdType        : TFtpCmdType;
  110.         RestartPos        : LongInt;
  111.         FromFileName      : String;
  112.         ToFileName        : String;
  113.         PassiveMode       : Boolean;
  114.         PassiveStart      : Boolean;
  115.         PassiveConnected  : Boolean;
  116.         Options           : TFtpOptions;
  117.         constructor Create(AOwner: TComponent); override;
  118.         destructor  Destroy; override;
  119.         procedure   Dup(newHSocket : TSocket); override;
  120.         procedure   StartConnection; virtual;
  121.         procedure   SendAnswer(Answer : String);
  122.         procedure   SetDirectory(newValue : String);
  123.         procedure   SetAbortingTransfer(newValue : Boolean);
  124.         function    GetPeerAddr: string; override;
  125.         property    DataSocket     : TWSocket    read FDataSocket;
  126.         property    ConnectedSince : TDateTime   read FConnectedSince;
  127.         property    LastCommand    : TDateTime   read FLastCommand;
  128.         property    CommandCount   : LongInt     read FCommandCount;
  129.         property    RcvBuf         : PChar       read FRcvBuf;
  130.         property    RcvdCount;
  131.         property    CloseRequest   : Boolean     read  FCloseRequest
  132.                                                  write FCloseRequest;
  133.         property Directory : String              read  FDirectory
  134.                                                  write SetDirectory;
  135.         property HomeDir : String                read  FHomeDir
  136.                                                  write FHomeDir;
  137.         property AbortingTransfer : Boolean      read  FAbortingTransfer
  138.                                                  write SetAbortingTransfer;
  139.     published
  140.         property FtpState : TFtpCtrlState  read  FFtpState
  141.                                            write FFtpState;
  142.         property Banner : String           read  FBanner
  143.                                            write FBanner;
  144.         property RcvSize : integer         read  FRcvSize
  145.                                            write SetRcvSize;
  146.         property Busy : Boolean            read  FBusy
  147.                                            write FBusy;
  148.         property UserName : String         read  FUserName
  149.                                            write FUserName;
  150.         property PassWord : String         read  FPassWord
  151.                                            write FPassWord;
  152.         property UserData  : LongInt       read  FUserData
  153.                                            write FUserData;
  154.         property OnDisplay : TDisplayEvent read  FOnDisplay
  155.                                            write FOnDisplay;
  156.         property OnCommand : TCommandEvent read  FOnCommand
  157.                                            write FOnCommand;
  158.         property OnSessionClosed;
  159.         property OnDataSent;
  160.         property HSocket;
  161.         property AllSent;
  162.         property State;
  163. {$IFDEF VER80}
  164.         property TrumpetCompability;
  165. {$ENDIF}
  166.     end;
  167. function IsUNC(S : String) : Boolean;
  168. {$IFDEF VER80}
  169. function ExtractFileDir(const FileName: String): String;
  170. function ExtractFileDrive(const FileName: String): String;
  171. {$ENDIF}
  172. implementation
  173. const
  174.     DefaultBanner = '220-ICS FTP Server ready';
  175. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  176. {$IFDEF VER80}
  177. procedure SetLength(var S: string; NewLength: Integer);
  178. begin
  179.     S[0] := chr(NewLength);
  180. end;
  181. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  182. { ExtractFileDir extracts the drive and directory parts of the given        }
  183. { filename. The resulting string is a directory name suitable for passing   }
  184. { to SetCurrentDir, CreateDir, etc. The resulting string is empty if        }
  185. { FileName contains no drive and directory parts.                           }
  186. function ExtractFileDir(const FileName: String): String;
  187. var
  188.     I: Integer;
  189. begin
  190.     I := Length(FileName);
  191.     while (I > 0) and (not (FileName[I] in ['', ':'])) do
  192.         Dec(I);
  193.     if (I > 1) and (FileName[I] = '') and
  194.        (not (FileName[I - 1] in ['', ':'])) then
  195.         Dec(I);
  196.     Result := Copy(FileName, 1, I);
  197. end;
  198. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  199. { ExtractFileDrive extracts the drive part of the given filename.  For        }
  200. { filenames with drive letters, the resulting string is '<drive>:'.           }
  201. { For filenames with a UNC path, the resulting string is in the form          }
  202. { '\<servername><sharename>'.  If the given path contains neither           }
  203. { style of filename, the result is an empty string.                           }
  204. function ExtractFileDrive(const FileName: String): String;
  205. var
  206.     I : Integer;
  207. begin
  208.     if Length(FileName) <= 1 then
  209.         Result := ''
  210.     else begin
  211.         if FileName[2] = ':' then
  212.             Result := Copy(FileName, 1, 2)
  213.         else if (FileName[2] = '') and (FileName[1] = '') then begin
  214.             { UNC file name }
  215.             I := 3;
  216.             while (I <= Length(FileName)) and (FileName[I] <> '') do
  217.                 Inc(I);
  218.             Result := Copy(FileName, 1, I - 1);
  219.         end
  220.         else
  221.             Result := '';
  222.     end;
  223. end;
  224. {$ENDIF}
  225. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  226. constructor TFtpCtrlSocket.Create(AOwner: TComponent);
  227. begin
  228.     inherited Create(AOwner);
  229.     FDataSocket      := TWSocket.Create(Self);
  230.     FDataSocket.Name := 'DataWSocket';
  231.     FBanner          := DefaultBanner;
  232.     FFtpState        := ftpcInvalid;
  233.     FHomeDir         := 'C:TEMP';
  234.     FDirectory       := FHomeDir;
  235.     SetRcvSize(DefaultRcvSize);
  236. end;
  237. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  238. destructor TFtpCtrlSocket.Destroy;
  239. begin
  240.     SetRcvSize(0);     { Free the buffer }
  241.     if Assigned(FDataSocket) then begin
  242.         FDataSocket.Destroy;
  243.         FDataSocket := nil;
  244.     end;
  245.     inherited Destroy;
  246. end;
  247. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  248. procedure TFtpCtrlSocket.SetRcvSize(newValue : Integer);
  249. begin
  250.     if FRcvCnt <> 0 then
  251.         raise EFtpCtrlSocketException.Create('Data in buffer, can''t change size');
  252.     if FRcvSize < 0 then
  253.         FRcvSize := 0;
  254.     if FRcvSize = newValue then
  255.         Exit; { No change, nothing to do }
  256.     { Free previously allocated buffer }
  257.     if FRcvBuf <> nil then begin
  258.         FreeMem(FRcvBuf, FRcvSize);
  259.         FRcvBuf := nil;
  260.     end;
  261.     { Allocate new buffer }
  262.     FRcvSize := newValue;
  263.     { If size is nul, then do not allocated the buffer }
  264.     if newValue > 0 then
  265.         GetMem(FRcvBuf, FRcvSize);
  266. end;
  267. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  268. procedure TFtpCtrlSocket.StartConnection;
  269. begin
  270.     FConnectedSince := Now;
  271.     FLastCommand    := 0;
  272.     FCommandCount   := 0;
  273.     FFtpState       := ftpcWaitingUserCode;
  274.     SendStr(FBanner + #13#10);
  275. end;
  276. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  277. function TFtpCtrlSocket.GetPeerAddr: String;
  278. begin
  279.     Result := FPeerAddr;
  280. end;
  281. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  282. procedure TFtpCtrlSocket.Dup(newHSocket : TSocket);
  283. begin
  284.     inherited Dup(newHSocket);
  285.     FPeerAddr := inherited GetPeerAddr;
  286. end;
  287. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  288. procedure TFtpCtrlSocket.TriggerSessionConnected(Error : Word);
  289. begin
  290.     FPeerAddr := inherited GetPeerAddr;
  291.     inherited TriggerSessionConnected(Error);
  292. end;
  293. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  294. procedure TFtpCtrlSocket.TriggerCommand(CmdBuf : PChar; CmdLen : Integer);
  295. begin
  296.     if Assigned(FOnCommand) then
  297.         FOnCommand(Self, CmdBuf, CmdLen);
  298. end;
  299. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  300. function TFtpCtrlSocket.TriggerDataAvailable(Error : Word) : Boolean;
  301. var
  302.     Len  : Integer;
  303.     I    : Integer;
  304. begin
  305.     Result := TRUE;                                { We read data }
  306.     Len := Receive(@FRcvBuf[FRcvCnt], FRcvSize - FRcvCnt - 1);
  307.     if Len <= 0 then
  308.         Exit;
  309.     FRcvCnt := FRcvCnt + Len;
  310.     FRcvBuf[FRcvCnt] := #0;
  311.     while TRUE do begin
  312.         I := 0;
  313.         while (I < FRcvCnt) and (FRcvBuf[I] <> #10) do
  314.             Inc(I);
  315.         if I >= FRcvCnt then
  316.             Exit;
  317.         FRcvBuf[I] := #0;
  318.         FLastCommand := Now;
  319.         Inc(FCommandCount);
  320.         if (I > 1) and (FRcvBuf[I - 1] = #13) then begin
  321.             FRcvBuf[I - 1] := #0;
  322.             TriggerCommand(FRcvBuf, I - 1);
  323.             FRcvBuf[I - 1] := #13;
  324.         end
  325.         else
  326.             TriggerCommand(FRcvBuf, I);
  327.         FRcvBuf[I] := #10;
  328.         if I >= (FRcvCnt - 1) then begin
  329.             FRcvCnt    := 0;
  330.             FRcvBuf[0] := #0;
  331.             break;
  332.         end;
  333.         Move(FRcvBuf[I + 1], FRcvBuf^, FRcvCnt - I);
  334.         FRcvCnt := FRcvCnt - I - 1;
  335.     end;
  336. end;
  337. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  338. procedure TFtpCtrlSocket.SendAnswer(Answer : String);
  339. begin
  340.     SendStr(Answer + #13#10);
  341. end;
  342. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  343. function IsUNC(S : String) : Boolean;
  344. begin
  345.     Result := (Length(S) >= 2) and (S[2] = '') and (S[1] = '');
  346. end;
  347. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  348. procedure TFtpCtrlSocket.SetDirectory(newValue : String);
  349. var
  350.     newDrive : String;
  351.     newPath  : String;
  352.     I        : Integer;
  353. begin
  354.     if FDirectory = newValue then
  355.         Exit;
  356.     newDrive := ExtractFileDrive(newValue);
  357.     if IsUNC(newDrive) then begin
  358.         if not (ftpcUNC in Options) then
  359.             raise Exception.Create('Cannot accept UNC path');
  360.         FDirectory := newValue;
  361.         { Always terminate with a backslash }
  362.         if (Length(FDirectory) > 0) and (FDirectory[Length(FDirectory)] <> '') then
  363.             FDirectory := FDirectory + '';
  364.         Exit;
  365.     end;
  366.     if Length(newDrive) = 0 then begin
  367.         newDrive := ExtractFileDrive(FDirectory);
  368.         newPath  := newValue;
  369.     end
  370.     else
  371.         newPath := Copy(newValue, 3, Length(newValue));
  372.     if Pos(':', newPath) <> 0 then
  373.         raise Exception.Create('Invalid directory name syntax');
  374.     if newPath = '..' then begin
  375.         if IsUNC(FDirectory) then begin
  376.             I := Length(FDirectory) - 1;
  377.             while (I > 0) and (FDirectory[I] <> '') do
  378.                 Dec(I);
  379.             if I > Length(newDrive) then
  380.                 SetLength(FDirectory, I);
  381.             Exit;
  382.         end
  383.         else begin
  384.             newPath := Copy(FDirectory, 3, Length(FDirectory));
  385.             I := Length(newPath) - 1;
  386.             while (I > 0) and (newPath[I] <> '') do
  387.                 Dec(I);
  388.             SetLength(newPath, I);
  389.         end;
  390.     end;
  391.     if (Length(newPath) > 0) and (newPath[1] <> '') then begin
  392.         { Relative path }
  393.         if IsUNC(FDirectory) then begin
  394.             FDirectory := FDirectory + newPath;
  395.             { Always terminate with a backslash }
  396.             if (Length(FDirectory) > 0) and (FDirectory[Length(FDirectory)] <> '') then
  397.                 FDirectory := FDirectory + '';
  398.             Exit;
  399.         end
  400.         else begin
  401.             if UpperCase(newDrive[1]) <> UpperCase(FDirectory[1]) then
  402.                 raise Exception.Create('Cannot accept path not relative to current directory');
  403.             if Pos('.', newPath) <> 0 then
  404.                 raise Exception.Create('Cannot accept relative path using dot notation');
  405.             if newPath = '.' then
  406.                 newPath := Copy(FDirectory, 3, Length(FDirectory))
  407.             else
  408.                 newPath := Copy(FDirectory, 3, Length(FDirectory)) + newPath;
  409.         end;
  410.     end
  411.     else begin
  412.         if Pos('.', newPath) <> 0 then
  413.             raise Exception.Create('Cannot accept relative path using dot notation');
  414.     end;
  415.     if Length(newPath) = 0 then begin
  416.         if UpperCase(newDrive[1]) <> UpperCase(FDirectory[1]) then
  417.             newPath := ''
  418.         else
  419.             newPath := Copy(FDirectory, 3, Length(FDirectory));
  420.     end;
  421.     { Always terminate with a backslash }
  422.     if (Length(newPath) > 0) and (newPath[Length(newPath)] <> '') then
  423.         newPath := newPath + '';
  424.     FDirectory := newDrive + newPath;
  425. end;
  426. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  427. procedure TFtpCtrlSocket.SetAbortingTransfer(newValue : Boolean);
  428. begin
  429.     FAbortingTransfer := newValue;
  430. end;
  431. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  432. end.