📄 ftpsrv.pas
字号:
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TFtpServer.CommandXPWD(
Client : TFtpCtrlSocket;
var Keyword : TFtpString;
var Params : TFtpString;
var Answer : TFtpString);
begin
if Client.FtpState <> ftpcReady then begin
Answer := msgNotLogged;
Exit;
end;
Client.CurCmdType := ftpcXPWD;
Answer := Format(msgPWDSuccess,
[BackSlashesToSlashes(Client.Directory)]);
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TFtpServer.CommandPWD(
Client : TFtpCtrlSocket;
var Keyword : TFtpString;
var Params : TFtpString;
var Answer : TFtpString);
begin
if Client.FtpState <> ftpcReady then begin
Answer := msgNotLogged;
Exit;
end;
Client.CurCmdType := ftpcPWD;
Answer := Format(msgPWDSuccess,
[BackSlashesToSlashes(Client.Directory)]);
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TFtpServer.CommandQUIT(
Client : TFtpCtrlSocket;
var Keyword : TFtpString;
var Params : TFtpString;
var Answer : TFtpString);
begin
Client.CurCmdType := ftpcQUIT;
Answer := msgQuit;
PostMessage(FWindowHandle, WM_FTPSRV_CLOSE_REQUEST, 0, LongInt(Client));
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
function GetInteger(var I : Integer; const Src : String) : LongInt;
begin
{ Skip leading white spaces }
while (I <= Length(Src)) and (Src[I] in [' ' , #9]) do
Inc(I);
Result := 0;
while (I <= Length(Src)) and (Src[I] in ['0'..'9']) do begin
Result := Result * 10 + Ord(Src[I]) - Ord('0');
Inc(I);
end;
{ Skip trailing white spaces }
while (I <= Length(Src)) and (Src[I] in [' ' , #9]) do
Inc(I);
{ Check if end of string of comma. If not, error, returns -1 }
if I <= Length(Src) then begin
if Src[I] = ',' then
Inc(I) { skip comma }
else
raise Exception.Create('unexpected char'); { error, must be comma }
end;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TFtpServer.CommandPORT(
Client : TFtpCtrlSocket;
var Keyword : TFtpString;
var Params : TFtpString;
var Answer : TFtpString);
var
I : Integer;
N : LongInt;
begin
if Client.FtpState <> ftpcReady then begin
Answer := msgNotLogged;
Exit;
end;
try
Client.CurCmdType := ftpcPORT;
I := 1;
Client.DataAddr := IntToStr(GetInteger(I, Params));
Client.DataAddr := Client.DataAddr + '.' + IntToStr(GetInteger(I, Params));
Client.DataAddr := Client.DataAddr + '.' + IntToStr(GetInteger(I, Params));
Client.DataAddr := Client.DataAddr + '.' + IntToStr(GetInteger(I, Params));
N := GetInteger(I, Params);
N := (N shl 8) + GetInteger(I, Params);
Client.DataPort := IntToStr(N);
Answer := msgPortSuccess;
except
Answer := msgPortFailed;
end;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TFtpServer.CommandSTOR(
Client : TFtpCtrlSocket;
var Keyword : TFtpString;
var Params : TFtpString;
var Answer : TFtpString);
var
Allowed : Boolean;
FilePath : TFtpString;
begin
if Client.FtpState <> ftpcReady then begin
Answer := msgNotLogged;
Exit;
end;
try
Client.CurCmdType := ftpcSTOR;
Client.FileName := SlashesToBackSlashes(Params);
Client.HasOpenedFile := FALSE;
Client.AbortingTransfer := FALSE;
Client.TransferError := 'Transfer Ok';
Allowed := TRUE;
FilePath := BuildFilePath(Client.Directory, Client.FileName);
TriggerValidatePut(Client, FilePath, Allowed);
if not Allowed then begin
Answer := msgStorDisabled;
Exit;
end;
Client.FilePath := FilePath;
if Client.PassiveMode then begin
Client.DataSocket.OnSessionConnected := ClientStorSessionConnected;
Client.DataSocket.OnSessionClosed := ClientStorSessionClosed;
Client.DataSocket.OnDataAvailable := ClientStorDataAvailable;
Client.DataSocket.OnDataSent := nil;
if Client.PassiveConnected then
Client.DataSocket.OnSessionConnected(Client.DataSocket, 0)
else
Client.PassiveStart := TRUE;
end
else begin
Client.DataSocket.Proto := 'tcp';
Client.DataSocket.Addr := Client.DataAddr;
Client.DataSocket.Port := Client.DataPort;
Client.DataSocket.OnSessionConnected := ClientStorSessionConnected;
Client.DataSocket.OnSessionClosed := ClientStorSessionClosed;
Client.DataSocket.OnDataAvailable := ClientStorDataAvailable;
Client.DataSocket.OnDataSent := nil;
Client.DataSocket.LingerOnOff := wsLingerOff;
Client.DataSocket.LingerTimeout := 0;
Client.DataSocket.Connect;
end;
Answer := Format(msgStorSuccess, [Params]);
except
on E:Exception do begin
Answer := Format(msgStorFailed, [E.Message]);
end;
end;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TFtpServer.ClientStorSessionConnected(Sender : TObject; Error : Word);
var
Client : TFtpCtrlSocket;
Data : TWSocket;
begin
Data := TWSocket(Sender);
Client := TFtpCtrlSocket(Data.Owner);
Client.DataSessionActive := TRUE;
Client.PassiveMode := FALSE;
TriggerStorSessionConnected(Client, Data, Error);
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TFtpServer.ClientStorSessionClosed(Sender : TObject; Error : Word);
var
Client : TFtpCtrlSocket;
Data : TWSocket;
begin
Data := TWSocket(Sender);
Client := TFtpCtrlSocket(Data.Owner);
Client.DataSessionActive := FALSE;
Client.PassiveStart := FALSE;
Client.PassiveConnected := FALSE;
Client.RestartPos := 0;
{ Reset data port to standard value }
Client.DataPort := 'ftp-data';
{ If we had opened a data stream ourself, then close it }
if Client.HasOpenedFile then begin
if Assigned(Client.DataStream) then
Client.DataStream.Destroy;
Client.DataStream := nil;
Client.HasOpenedFile := FALSE;
end;
TriggerStorSessionClosed(Client, Data, Error);
if Client.CurCmdType = ftpcSTOR then begin
if Client.AbortingTransfer then
SendAnswer(Client, Format(msgStorAborted, [Client.TransferError]))
else if Error = 0 then
SendAnswer(Client, msgStorOk)
else
SendAnswer(Client, Format(msgStorError, [Error]));
end
else if Client.CurCmdType = ftpcAPPE then begin
if Client.AbortingTransfer then
SendAnswer(Client, Format(msgAppeAborted, [Client.TransferError]))
else if Error = 0 then
SendAnswer(Client, msgAppeOk)
else
SendAnswer(Client, Format(msgAppeError, [Error]));
end
else { Should never comes here }
raise Exception.Create('Program error in ClientStorSessionClosed');
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TFtpServer.ClientStorDataAvailable(Sender: TObject; Error : word);
var
Len : Integer;
Client : TFtpCtrlSocket;
Data : TWSocket;
begin
Data := TWSocket(Sender);
Client := TFtpCtrlSocket(Data.Owner);
Len := Data.Receive(Client.RcvBuf, Client.RcvSize);
if Len <= 0 then
Exit;
if Client.AbortingTransfer then
Exit;
try
{ Trigger the user event for the received data }
TriggerStorDataAvailable(Client, Data, Client.RcvBuf, Len, Error);
{ We need to open a datastream if not already done and a FilePath }
{ exists (the component user can have nullified the FilePath }
if (not Client.HasOpenedFile) and
(Length(Client.FilePath) > 0) and
(not Assigned(Client.DataStream)) then begin
{ Use different file modes for APPE vs STOR }
if (Client.CurCmdType = ftpcAPPE) and
(GetFileSize(Client.FilePath) > -1) then
Client.DataStream := TFileStream.Create(Client.FilePath,
fmOpenReadWrite or fmShareDenyWrite)
else
Client.DataStream := TFileStream.Create(Client.FilePath,
fmCreate);
Client.DataStream.Seek(Client.RestartPos, soFromBeginning);
Client.HasOpenedFile := TRUE;
end;
{ If we have a DataStream, then we need to write the data }
if Assigned(Client.DataStream) then
Client.DataStream.WriteBuffer(Client.RcvBuf^, Len);
except
{ An exception occured, so we abort the transfer }
on E:Exception do begin
Client.TransferError := E.Message;
Client.AbortingTransfer := TRUE;
PostMessage(FWindowHandle, WM_FTPSRV_ABORT_TRANSFER, 0, LongInt(Data));
end;
end;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
function BuildFilePath(
const Directory : String;
FileName : String) : String;
var
Drive : String;
Path : String;
begin
FileName := SlashesToBackSlashes(FileName);
if IsUNC(FileName) then
Result := FileName
else if IsUNC(Directory) then begin
if (Length(FileName) > 0) and (FileName[1] = '\') then
Result := ExtractFileDrive(Directory) + FileName
else
Result := Directory + FileName;
end
else begin
if (Length(FileName) > 1) and (FileName[2] = ':') then begin
Drive := UpperCase(Copy(FileName, 1, 2));
Path := Copy(FileName, 3, Length(FileName));
end
else begin
Drive := Copy(Directory, 1, 2);
Path := FileName;
end;
if (Length(Path) > 0) and (Path[1] = '\') then
Result := Drive + Path
else begin
if Drive <> Copy(Directory, 1, 2) then
raise Exception.Create('No current dir for ''' + Drive + '''');
Result := Directory + Path;
end;
end;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -