1 unit IdTrivialFTPServer
;
17 TAccessFileEvent
= procedure(Sender
: TObject
; var FileName
: string; const
19 var GrantAccess
: Boolean; var AStream
: TStream
; var FreeStreamOnComplete
:
21 TTransferCompleteEvent
= procedure(Sender
: TObject
; const Success
: Boolean;
22 const PeerInfo
: TPeerInfo
; AStream
: TStream
; const WriteOperation
: Boolean)
25 TIdTrivialFTPServer
= class(TIdUDPServer
)
27 FOnTransferComplete
: TTransferCompleteEvent
;
29 FOnWriteFile
: TAccessFileEvent
;
30 function StrToMode(mode
: string): TIdTFTPMode
;
32 procedure DoReadFile(FileName
: string; const Mode
: TIdTFTPMode
;
33 const PeerInfo
: TPeerInfo
; RequestedBlockSize
: Integer = 0); virtual;
34 procedure DoWriteFile(FileName
: string; const Mode
: TIdTFTPMode
;
35 const PeerInfo
: TPeerInfo
; RequestedBlockSize
: Integer = 0); virtual;
36 procedure DoTransferComplete(const Success
: Boolean; const PeerInfo
:
37 TPeerInfo
; SourceStream
: TStream
; const WriteOperation
: Boolean); virtual;
38 procedure DoUDPRead(AData
: TStream
; ABinding
: TIdSocketHandle
); override;
40 constructor Create(axOwner
: TComponent
); override;
42 property OnReadFile
: TAccessFileEvent read FOnReadFile write FOnReadFile
;
43 property OnWriteFile
: TAccessFileEvent read FOnWriteFile write FOnWriteFile
;
44 property OnTransferComplete
: TTransferCompleteEvent read FOnTransferComplete
45 write FOnTransferComplete
;
59 TIdTFTPServerThread
= class(TThread
)
62 FUDPClient
: TIdUDPClient
;
63 FRequestedBlkSize
: Integer;
66 FOwner
: TIdTrivialFTPServer
;
67 procedure TransferComplete
;
69 procedure Execute
; override;
71 { constructor Create(AnOwner: TIdTrivialFTPServer; const Mode: TIdTFTPMode;
72 const PeerInfo: TPeerInfo;
73 AStream: TStream; const FreeStreamOnTerminate: boolean; const
74 RequestedBlockSize: Integer = 0); virtual;
76 function Newreate (AnOwner
: TIdTrivialFTPServer
; const Mode
: TIdTFTPMode
;
77 const PeerInfo
: TPeerInfo
;
78 AStream
: TStream
; const FreeStreamOnTerminate
: boolean; const
79 RequestedBlockSize
: Integer = 0):Preate
;destructor Destroy
;
82 TIdTFTPServerSendFileThread
= class(TIdTFTPServerThread
);
83 TIdTFTPServerReceiveFileThread
= class(TIdTFTPServerThread
);
85 { constructor TIdTrivialFTPServer.Create(axOwner: TComponent);
87 function NewIdTrivialFTPServer (axOwner
: TComponent
):PIdTrivialFTPServer
;begin
89 DefaultPort
:= IdPORT_TFTP
;
92 procedure TIdTrivialFTPServer
.DoReadFile(FileName
: string; const Mode
:
94 const PeerInfo
: TPeerInfo
; RequestedBlockSize
: Integer = 0);
97 FreeOnComplete
: Boolean;
98 SourceStream
: TStream
;
102 FreeOnComplete
:= True;
104 if Assigned(FOnReadFile
) then
105 FOnReadFile(Self
, FileName
, PeerInfo
, CanRead
, SourceStream
,
109 if SourceStream
= nil then
111 SourceStream
:= TFileStream
.Create(FileName
, fmOpenRead
or
113 FreeOnComplete
:= True;
115 TIdTFTPServerSendFileThread
.Create(self
, Mode
, PeerInfo
, SourceStream
,
116 FreeOnComplete
, RequestedBlockSize
);
119 raise EIdTFTPAccessViolation
.CreateFmt(RSTFTPAccessDenied
, [FileName
]);
122 raise EIdTFTPFileNotFound
.Create(E
.Message);
126 procedure TIdTrivialFTPServer
.DoTransferComplete(const Success
: Boolean;
127 const PeerInfo
: TPeerInfo
; SourceStream
: TStream
; const WriteOperation
:
130 if Assigned(FOnTransferComplete
) then
131 FOnTransferComplete(Self
, Success
, PeerInfo
, SourceStream
, WriteOperation
)
136 procedure TIdTrivialFTPServer
.DoUDPRead(AData
: TStream
; ABinding
:
142 RequestedBlkSize
: integer;
148 AData
.ReadBuffer(wOp
, SizeOf(wOp
));
149 wOp
:= GStack
.WSNToHs(wOp
);
150 if wOp
in [TFTP_RRQ
, TFTP_WRQ
] then
152 SetLength(s
, AData
.Size
- AData
.Position
);
153 AData
.ReadBuffer(s
[1], Length(s
));
154 FileName
:= Fetch(s
, #0);
155 Mode
:= StrToMode(Fetch(s
, #0));
156 RequestedBlkSize
:= 0;
157 if StrLComp(pchar(s
), sBlockSize
, Length(sBlockSize
)) = 0 then
160 RequestedBlkSize
:= StrToInt(Fetch(s
, #0));
162 PeerInfo
.PeerIP
:= ABinding
.PeerIP
;
163 PeerInfo
.PeerPort
:= ABinding
.PeerPort
;
164 if wOp
= TFTP_RRQ
then
165 DoReadFile(FileName
, Mode
, PeerInfo
, RequestedBlkSize
)
167 DoWriteFile(FileName
, Mode
, PeerInfo
, RequestedBlkSize
);
170 raise EIdTFTPIllegalOperation
.CreateFmt(RSTFTPUnexpectedOp
,
171 [ABinding
.PeerIP
, ABinding
.PeerPort
]);
173 on E
: EIdTFTPException
do
174 SendError(self
, ABinding
.PeerIP
, ABinding
.PeerPort
, E
);
177 SendError(self
, ABinding
.PeerIP
, ABinding
.PeerPort
, E
);
183 procedure TIdTrivialFTPServer
.DoWriteFile(FileName
: string;
184 const Mode
: TIdTFTPMode
; const PeerInfo
: TPeerInfo
;
185 RequestedBlockSize
: Integer);
188 FreeOnComplete
: Boolean;
189 DestinationStream
: TStream
;
192 DestinationStream
:= nil;
193 FreeOnComplete
:= True;
195 if Assigned(FOnWriteFile
) then
196 FOnWriteFile(Self
, FileName
, PeerInfo
, CanWrite
, DestinationStream
,
200 if DestinationStream
= nil then
202 DestinationStream
:= TFileStream
.Create(FileName
, fmCreate
or
204 FreeOnComplete
:= True;
206 TIdTFTPServerReceiveFileThread
.Create(self
, Mode
, PeerInfo
,
207 DestinationStream
, FreeOnComplete
, RequestedBlockSize
);
210 raise EIdTFTPAccessViolation
.CreateFmt(RSTFTPAccessDenied
, [FileName
]);
212 on E
: EFCreateError
do
213 raise EIdTFTPAllocationExceeded
.Create(E
.Message);
217 function TIdTrivialFTPServer
.StrToMode(mode
: string): TIdTFTPMode
;
219 case PosInStrArray(mode
, ['octet', 'binary', 'netascii'], False) of
220 0, 1: Result
:= tfOctet
;
221 2: Result
:= tfNetAscii
;
223 raise EIdTFTPIllegalOperation
.CreateFmt(RSTFTPUnsupportedTrxMode
, [mode
]);
228 { constructor TIdTFTPServerThread.Create(AnOwner: TIdTrivialFTPServer;
229 const Mode: TIdTFTPMode; const PeerInfo: TPeerInfo;
230 AStream: TStream; const FreeStreamOnTerminate: boolean; const
231 RequestedBlockSize: Integer);
233 function NewIdTFTPServerThread (AnOwner
: TIdTrivialFTPServer
;
234 const Mode
: TIdTFTPMode
; const PeerInfo
: TPeerInfo
;
235 AStream
: TStream
; const FreeStreamOnTerminate
: boolean; const
236 RequestedBlockSize
: Integer):PIdTFTPServerThread
;begin
237 inherited Create(True);
239 FUDPClient
:= TIdUDPClient
.Create(nil);
242 ReceiveTimeout
:= 1500;
243 Host
:= PeerInfo
.PeerIP
;
244 Port
:= PeerInfo
.PeerPort
;
246 FRequestedBlkSize
:= RequestedBlockSize
;
248 FFreeStrm
:= FreeStreamOnTerminate
;
250 FreeOnTerminate
:= True;
254 destructor TIdTFTPServerThread
.Destroy
;
258 Synchronize(TransferComplete
);
263 procedure TIdTFTPServerThread
.Execute
;
275 SetLength(Response
, Max(FRequestedBlkSize
+ hdrsize
, FUDPClient
.BufferSize
));
276 if FRequestedBlkSize
> 0 then
278 FUDPClient
.BufferSize
:= Max(FRequestedBlkSize
+ hdrsize
,
279 FUDPClient
.BufferSize
);
280 Response
:= WordToStr(GStack
.WSHToNs(TFTP_OACK
)) + 'blksize'#0
281 + IntToStr(FUDPClient
.BufferSize
- hdrsize
) + #0#0;
285 SetLength(Buffer
, FUDPClient
.BufferSize
);
289 if Response
= '' then
291 if (self
is TIdTFTPServerReceiveFileThread
) then
292 Response
:= MakeAckPkt(BlkCounter
)
295 BlkCounter
:= Word(succ(BlkCounter
));
296 Response
:= WordToStr(GStack
.WSHToNs(TFTP_DATA
)) +
297 WordToStr(GStack
.WSHToNs(Word(BlkCounter
)));
298 SetLength(Response
, FUDPClient
.BufferSize
);
299 i
:= FStream
.Read(Response
[hdrsize
+ 1], Length(Response
) - hdrsize
);
300 SetLength(Response
, i
+ hdrsize
);
301 EOT
:= i
< FUDPClient
.BufferSize
- hdrsize
;
306 raise EIdTFTPIllegalOperation
.Create(RSTimeOut
);
307 FUDPClient
.Send(Response
);
308 Buffer
:= FUDPClient
.ReceiveString
;
316 case GStack
.WSNToHs(StrToWord(Buffer
)) of
319 if not (self
is TIdTFTPServerSendFileThread
) then
321 raise EIdTFTPIllegalOperation
.CreateFmt(RSTFTPUnexpectedOp
,
322 [FUDPClient
.Host
, FUDPClient
.Port
]);
324 i
:= GStack
.WSNToHs(StrToWord(Copy(Buffer
, 3, 2)));
325 if i
= BlkCounter
then
331 if not (self
is TIdTFTPServerReceiveFileThread
) then
333 raise EIdTFTPIllegalOperation
.CreateFmt(RSTFTPUnexpectedOp
,
334 [FUDPClient
.Host
, FUDPClient
.Port
]);
336 i
:= GStack
.WSNToHs(StrToWord(Copy(Buffer
, 3, 2)));
337 if i
= Word(BlkCounter
+ 1) then
339 FStream
.WriteBuffer(Buffer
[hdrsize
+ 1], Length(Buffer
) -
342 BlkCounter
:= Word(succ(BlkCounter
));
344 EOT
:= Length(Buffer
) < FUDPClient
.BufferSize
;
348 raise EIdTFTPIllegalOperation
.CreateFmt(RSTFTPUnexpectedOp
,
349 [FUDPClient
.Host
, FUDPClient
.Port
]);
353 on E
: EIdTFTPException
do
354 SendError(FUDPClient
, E
);
356 SendError(FUDPClient
, ErrAllocationExceeded
, Format(RSTFTPDiskFull
,
357 [FStream
.Position
]));
359 SendError(FUDPClient
, E
);
363 procedure TIdTFTPServerThread
.TransferComplete
;
367 PeerInfo
.PeerIP
:= FUDPClient
.Host
;
368 PeerInfo
.PeerPort
:= FUDPClient
.Port
;
369 FOwner
.DoTransferComplete(EOT
, PeerInfo
, FStream
, self
is
370 TIdTFTPServerReceiveFileThread
);