📄 ftpsrvc.pas
字号:
unit FtpSrvC;
interface
{$B-} { Enable partial boolean evaluation }
{$T-} { Untyped pointers }
{$IFNDEF VER80}
{$J+} { Allow typed constant to be modified }
{$ENDIF}
{$IFDEF VER110} { C++ Builder V3.0 }
{$ObjExportAll On}
{$ENDIF}
uses
WinTypes, WinProcs, Messages, Classes, SysUtils, Winsock, WSocket;
const
FtpCtrlSocketVersion = 103;
DefaultRcvSize = 2048;
type
EFtpCtrlSocketException = class(Exception);
TFtpCtrlState = (ftpcInvalid, ftpcWaitingUserCode, ftpcWaitingPassword,
ftpcReady, ftpcWaitingAnswer);
TFtpCmdType = (ftpcPORT, ftpcSTOR, ftpcRETR, ftpcCWD, ftpcXPWD, ftpcPWD,
ftpcUSER, ftpcPASS, ftpcLIST, ftpcRMD, ftpcTYPE, ftpcSYST,
ftpcQUIT, ftpcDELE, ftpcRNFR, ftpcMKD, ftpcRNTO, ftpcNOOP,
ftpcNLST, ftpcABOR, ftpcCDUP, ftpcSIZE, ftpcREST, ftpcAPPE,
ftpcSTRU); {jsp - Added APPE and STRU types}
TFtpOption = (ftpcUNC);
TFtpOptions = set of TFtpOption;
TDisplayEvent = procedure (Sender : TObject; Msg : String) of object;
TCommandEvent = procedure (Sender : TObject; CmdBuf : PChar; CmdLen : Integer) of object;
TFtpCtrlSocket = class(TCustomWSocket)
protected
FDataSocket : TWSocket;
FRcvBuf : PChar;
FRcvCnt : Integer;
FRcvSize : Integer;
FBusy : Boolean;
FConnectedSince : TDateTime;
FLastCommand : TDateTime;
FCommandCount : LongInt;
FBanner : String;
FUserName : String;
FPassWord : String;
FCloseRequest : Boolean;
FHomeDir : String;
FDirectory : String;
FFtpState : TFtpCtrlState;
FAbortingTransfer : Boolean;
FUserData : LongInt; { Reserved for component user }
FPeerAddr : String;
FOnDisplay : TDisplayEvent;
FOnCommand : TCommandEvent;
procedure TriggerSessionConnected(Error : Word); override;
function TriggerDataAvailable(Error : Word) : boolean; override;
procedure TriggerCommand(CmdBuf : PChar; CmdLen : Integer); virtual;
procedure SetRcvSize(newValue : Integer);
public
BinaryMode : Boolean;
DataAddr : String;
DataPort : String;
FileName : String;
FilePath : String;
DataSessionActive : Boolean;
DataStream : TStream;
HasOpenedFile : Boolean;
TransferError : String;
ByteCount : LongInt;
DataSent : Boolean;
CurCmdType : TFtpCmdType;
RestartPos : LongInt;
FromFileName : String;
ToFileName : String;
PassiveMode : Boolean;
PassiveStart : Boolean;
PassiveConnected : Boolean;
Options : TFtpOptions;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Dup(newHSocket : TSocket); override;
procedure StartConnection; virtual;
procedure SendAnswer(Answer : String);
procedure SetDirectory(newValue : String);
procedure SetAbortingTransfer(newValue : Boolean);
function GetPeerAddr: string; override;
property DataSocket : TWSocket read FDataSocket;
property ConnectedSince : TDateTime read FConnectedSince;
property LastCommand : TDateTime read FLastCommand;
property CommandCount : LongInt read FCommandCount;
property RcvBuf : PChar read FRcvBuf;
property RcvdCount;
property CloseRequest : Boolean read FCloseRequest
write FCloseRequest;
property Directory : String read FDirectory
write SetDirectory;
property HomeDir : String read FHomeDir
write FHomeDir;
property AbortingTransfer : Boolean read FAbortingTransfer
write SetAbortingTransfer;
published
property FtpState : TFtpCtrlState read FFtpState
write FFtpState;
property Banner : String read FBanner
write FBanner;
property RcvSize : integer read FRcvSize
write SetRcvSize;
property Busy : Boolean read FBusy
write FBusy;
property UserName : String read FUserName
write FUserName;
property PassWord : String read FPassWord
write FPassWord;
property UserData : LongInt read FUserData
write FUserData;
property OnDisplay : TDisplayEvent read FOnDisplay
write FOnDisplay;
property OnCommand : TCommandEvent read FOnCommand
write FOnCommand;
property OnSessionClosed;
property OnDataSent;
property HSocket;
property AllSent;
property State;
{$IFDEF VER80}
property TrumpetCompability;
{$ENDIF}
end;
function IsUNC(S : String) : Boolean;
{$IFDEF VER80}
function ExtractFileDir(const FileName: String): String;
function ExtractFileDrive(const FileName: String): String;
{$ENDIF}
implementation
const
DefaultBanner = '220-ICS FTP Server ready';
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{$IFDEF VER80}
procedure SetLength(var S: string; NewLength: Integer);
begin
S[0] := chr(NewLength);
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ ExtractFileDir extracts the drive and directory parts of the given }
{ filename. The resulting string is a directory name suitable for passing }
{ to SetCurrentDir, CreateDir, etc. The resulting string is empty if }
{ FileName contains no drive and directory parts. }
function ExtractFileDir(const FileName: String): String;
var
I: Integer;
begin
I := Length(FileName);
while (I > 0) and (not (FileName[I] in ['\', ':'])) do
Dec(I);
if (I > 1) and (FileName[I] = '\') and
(not (FileName[I - 1] in ['\', ':'])) then
Dec(I);
Result := Copy(FileName, 1, I);
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ ExtractFileDrive extracts the drive part of the given filename. For }
{ filenames with drive letters, the resulting string is '<drive>:'. }
{ For filenames with a UNC path, the resulting string is in the form }
{ '\\<servername>\<sharename>'. If the given path contains neither }
{ style of filename, the result is an empty string. }
function ExtractFileDrive(const FileName: String): String;
var
I : Integer;
begin
if Length(FileName) <= 1 then
Result := ''
else begin
if FileName[2] = ':' then
Result := Copy(FileName, 1, 2)
else if (FileName[2] = '\') and (FileName[1] = '\') then begin
{ UNC file name }
I := 3;
while (I <= Length(FileName)) and (FileName[I] <> '\') do
Inc(I);
Result := Copy(FileName, 1, I - 1);
end
else
Result := '';
end;
end;
{$ENDIF}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
constructor TFtpCtrlSocket.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FDataSocket := TWSocket.Create(Self);
FDataSocket.Name := 'DataWSocket';
FBanner := DefaultBanner;
FFtpState := ftpcInvalid;
FHomeDir := 'C:\TEMP\';
FDirectory := FHomeDir;
SetRcvSize(DefaultRcvSize);
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
destructor TFtpCtrlSocket.Destroy;
begin
SetRcvSize(0); { Free the buffer }
if Assigned(FDataSocket) then begin
FDataSocket.Destroy;
FDataSocket := nil;
end;
inherited Destroy;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TFtpCtrlSocket.SetRcvSize(newValue : Integer);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -