| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- {
- $Project$
- $Workfile$
- $Revision$
- $DateUTC$
- $Id$
- This file is part of the Indy (Internet Direct) project, and is offered
- under the dual-licensing agreement described on the Indy website.
- (http://www.indyproject.org/)
- Copyright:
- (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
- }
- {
- $Log$
- }
- {
- Rev 1.13 2004.02.03 5:43:58 PM czhower
- Name changes
- Rev 1.12 2/2/2004 4:56:26 PM JPMugaas
- DotNET with fries and a Coke :-)
- Rev 1.11 1/21/2004 3:11:28 PM JPMugaas
- InitComponent
- Rev 1.10 2003.11.29 10:18:56 AM czhower
- Updated for core change to InputBuffer.
- Rev 1.9 2003.10.21 9:13:10 PM czhower
- Now compiles.
- Rev 1.8 9/19/2003 03:30:02 PM JPMugaas
- Now should compile again.
- Rev 1.7 3/6/2003 5:08:50 PM SGrobety
- Updated the read buffer methodes to fit the new core (InputBuffer ->
- InputBufferAsString + call to CheckForDataOnSource)
- Rev 1.6 4/3/2003 7:56:56 PM BGooijen
- Added TODO item.
- Rev 1.5 2/24/2003 09:14:32 PM JPMugaas
- Rev 1.4 1/17/2003 06:52:42 PM JPMugaas
- Now compiles with new framework.
- Rev 1.3 1-8-2003 22:20:38 BGooijen
- these compile (TIdContext)
- Rev 1.2 12/16/2002 06:59:08 PM JPMugaas
- MLSD added as a command requiring a data command.
- Rev 1.1 12/7/2002 06:43:06 PM JPMugaas
- These should now compile except for Socks server. IPVersion has to be a
- property someplace for that.
- Rev 1.0 11/13/2002 07:56:34 AM JPMugaas
- 2001.12.14 - beta preview (but FTPVC work fine ;-)
- }
- {
- Author: Andrew P.Rybin [[email protected]]
- Th (rfc959):
- 1) EOL = #13#10 [#10 last char]
- 2) reply = IntCode (three digit number) ' ' text
- 3) PORT h1,h2,h3,h4,p1,p2 -> Client Listen >>'200 Port command successful.'
- 4) PASV -> Server Listen >>'227 Entering Passive Mode (%d,%d,%d,%d,%d,%d).'
- Err:
- 426 RSFTPDataConnClosedAbnormally
- }
- //BGO: TODO: convert TIdMappedFtpDataThread to a context.
- unit IdMappedFTP;
- interface
- {$i IdCompilerDefines.inc}
- uses
- Classes,
- IdContext, IdAssignedNumbers, IdMappedPortTCP, IdStack, IdYarn,
- IdTCPConnection, IdThread, IdGlobal;
- type
- TIdMappedFtpDataThread = class;
- TIdMappedFtpContext = class(TIdMappedPortContext)
- protected
- FFtpCommand: string;
- FFtpParams: string;
- FHost, FoutboundHost: string; //local,remote(mapped)
- FPort, FoutboundPort: TIdPort;
- FDataChannelThread: TIdMappedFtpDataThread;
- //
- procedure HandleLocalClientData; override;
- //
- function GetFtpCmdLine: string; //Cmd+' '+Params {Do not Localize}
- procedure CreateDataChannelThread;
- //procedure FreeDataChannelThread;
- function ProcessFtpCommand: Boolean; virtual;
- procedure ProcessOutboundDc(const APASV: Boolean); virtual;
- procedure ProcessDataCommand; virtual;
- public
- constructor Create(
- AConnection: TIdTCPConnection;
- AYarn: TIdYarn;
- AList: TIdContextThreadList = nil
- ); override;
- property FtpCommand: string read FFtpCommand write FFtpCommand;
- property FtpParams: string read FFtpParams write FFtpParams;
- property FtpCmdLine: string read GetFtpCmdLine;
- property Host: string read FHost write FHost;
- property OutboundHost: string read FOutboundHost write FOutboundHost;
- property Port: TIdPort read FPort write FPort;
- property OutboundPort: TIdPort read FOutboundPort write FOutboundPort;
- property DataChannelThread: TIdMappedFtpDataThread read FDataChannelThread;
- end;
- TIdMappedFtpDataThread = class(TIdThread)
- protected
- FMappedFtpThread: TIdMappedFtpContext;
- FConnection: TIdTcpConnection;
- FOutboundClient: TIdTCPConnection;
- FReadList: TIdSocketList;
- FNetData: TIdBytes;
- //
- procedure BeforeRun; override;
- procedure Run; override;
- public
- constructor Create(AMappedFtpThread: TIdMappedFtpContext); reintroduce;
- destructor Destroy; override;
- property MappedFtpThread: TIdMappedFtpContext read FMappedFtpThread;
- property Connection: TIdTcpConnection read FConnection; //local
- property OutboundClient: TIdTCPConnection read FOutboundClient; //remote(mapped)
- property NetData: TIdBytes read FNetData write FNetData;
- end;
- TIdMappedFtpOutboundDcMode = (fdcmClient, fdcmPort, fdcmPasv);
- TIdMappedFTP = class(TIdMappedPortTCP)
- protected
- FOutboundDcMode: TIdMappedFtpOutboundDcMode;
- procedure InitComponent; override;
- published
- property DefaultPort default IdPORT_FTP;
- property MappedPort default IdPORT_FTP;
- property OutboundDcMode: TIdMappedFtpOutboundDcMode read FOutboundDcMode
- write FOutboundDcMode default fdcmClient;
- end;
- //=============================================================================
- implementation
- uses
- IdGlobalProtocols, IdIOHandlerSocket,
- IdResourceStringsProtocols, IdTcpClient, IdSimpleServer, IdStackConsts,
- SysUtils;
- const
- // iLastGetCmd = 2;
- saDataCommands: array[0..6] of string = (
- {GET}'RETR', 'LIST', 'NLST', {Do not Localize}
- {PUT}'STOU', 'APPE', 'STOR', {Do not localize}
- 'MLSD'); {Do not Localize}
- { TIdMappedFTP }
- procedure TIdMappedFTP.InitComponent;
- begin
- inherited InitComponent;
- DefaultPort := IdPORT_FTP;
- MappedPort := IdPORT_FTP;
- FContextClass := TIdMappedFtpContext;
- FOutboundDcMode := fdcmClient;
- end;
- { TIdMappedFtpContext }
- constructor TIdMappedFtpContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
- begin
- inherited Create(AConnection, AYarn, AList);
- FHost := ''; {Do not Localize}
- FoutboundHost := ''; {Do not Localize}
- FPort := 0; //system choice
- FoutboundPort := 0;
- end;
- procedure TIdMappedFtpContext.HandleLocalClientData;
- var
- s: String;
- begin
- repeat
- s := Connection.IOHandler.ReadLn; //USeR REQuest
- if Length(s) > 0 then
- begin
- FFtpParams := s;
- FFtpCommand := UpperCase(Fetch(FFtpParams, ' ', True)); {Do not Localize}
- FNetData := ToBytes(FtpCmdLine, Connection.IOHandler.DefStringEncoding);
- TIdMappedFTP(Server).DoLocalClientData(Self); //bServer
- if not ProcessFtpCommand then
- begin
- FOutboundClient.IOHandler.WriteLn(FtpCmdLine); //send USRREQ to FtpServer
- ProcessDataCommand;
- end;
- end;
- until Connection.IOHandler.InputBufferIsEmpty;
- end;
- procedure TIdMappedFtpContext.CreateDataChannelThread;
- begin
- FDataChannelThread := TIdMappedFtpDataThread.Create(Self);
- //FDataChannelThread.OnException := TIdTCPServer(FConnection.Server).OnException;
- end;
- procedure TIdMappedFtpContext.ProcessDataCommand;
- begin
- if PosInStrArray(FFtpCommand, saDataCommands) > -1 then begin
- FDataChannelThread.Start;
- end;
- end;
- function TIdMappedFtpContext.ProcessFtpCommand: Boolean;
- procedure ParsePort;
- var
- LLo, LHi: Integer;
- LParm: string;
- LDataChannel: TIdTCPClient;
- begin
- //1.setup local
- LParm := FtpParams;
- Host := Fetch(LParm, ',') + '.' + //h1 {Do not Localize}
- Fetch(LParm, ',') + '.' + //h2 {Do not Localize}
- Fetch(LParm, ',') + '.' + //h3 {Do not Localize}
- Fetch(LParm, ','); //h4 {Do not Localize}
- LLo := IndyStrToInt(Fetch(LParm, ',')); //p1 {Do not Localize}
- LHi := IndyStrToInt(LParm); //p2
- Port := (LLo * 256) + LHi;
- CreateDataChannelThread;
- DataChannelThread.FConnection := TIdTCPClient.Create(nil);
- LDataChannel := TIdTCPClient(DataChannelThread.FConnection);
- LDataChannel.Host := Host;
- LDataChannel.Port := Port;
- //2.setup remote (mapped)
- ProcessOutboundDc(False);
- //3. send ack to client
- Connection.IOHandler.WriteLn('200 ' + IndyFormat(RSFTPCmdSuccessful, ['PORT'])); {Do not Localize}
- end;
- procedure ParsePasv;
- var
- LParm: string;
- LDataChannel: TIdSimpleServer;
- begin
- //1.setup local
- Host := Connection.Socket.Binding.IP;
- CreateDataChannelThread;
- DataChannelThread.FConnection := TIdSimpleServer.Create(nil);
- LDataChannel := TIdSimpleServer(DataChannelThread.FConnection);
- LDataChannel.BoundIP := Self.Host;
- LDataChannel.BoundPort := Self.Port;
- LDataChannel.BeginListen;
- Host := LDataChannel.Binding.IP;
- Port := LDataChannel.Binding.Port;
- LParm := ReplaceAll(Host, '.', ','); {Do not Localize}
- LParm := LParm + ',' + IntToStr(Port div 256) + ',' + IntToStr(Port mod 256); {Do not Localize}
- //2.setup remote (mapped)
- ProcessOutboundDc(True);
- //3. send ack to client
- Connection.IOHandler.WriteLn('227 ' + IndyFormat(RSFTPPassiveMode, [LParm])); {Do not Localize}
- end;
- begin
- if FFtpCommand = 'PORT' then {Do not Localize}
- begin
- ParsePort;
- Result := True;
- end
- else if FFtpCommand = 'PASV' then {Do not Localize}
- begin
- ParsePasv;
- Result := True;
- end else begin
- Result := False; //command NOT processed
- end;
- end;
- procedure TIdMappedFtpContext.ProcessOutboundDc(const APASV: Boolean);
- var
- Mode: TIdMappedFtpOutboundDcMode;
- procedure SendPort;
- var
- LDataChannel: TIdSimpleServer;
- begin
- OutboundHost := OutboundClient.Socket.Binding.IP;
- DataChannelThread.FOutboundClient := TIdSimpleServer.Create(nil);
- LDataChannel := TIdSimpleServer(DataChannelThread.FOutboundClient);
- LDataChannel.BoundIP := Self.OutboundHost;
- LDataChannel.BoundPort := Self.OutboundPort;
- LDataChannel.BeginListen;
- OutboundHost := LDataChannel.Binding.IP;
- OutboundPort := LDataChannel.Binding.Port;
- OutboundClient.SendCmd(
- 'PORT ' + ReplaceAll(OutboundHost, '.', ',') + {Do not Localize}
- ',' + IntToStr(OutboundPort div 256) + ',' + {Do not Localize}
- IntToStr(OutboundPort mod 256), 200);
- end;
- procedure SendPasv;
- var
- i, bLeft, bRight: integer;
- s: string;
- LDataChannel: TIdTCPClient;
- begin
- OutboundClient.SendCmd('PASV', 227); {Do not Localize}
- s := Trim(OutboundClient.LastCmdResult.Text[0]);
- // Case 1 (Normal)
- // 227 Entering passive mode(100,1,1,1,23,45)
- bLeft := IndyPos('(', s); {Do not Localize}
- bRight := IndyPos(')', s); {Do not Localize}
- if (bLeft = 0) or (bRight = 0) then
- begin
- // Case 2
- // 227 Entering passive mode on 100,1,1,1,23,45
- bLeft := RPos(#32, s);
- s := Copy(s, bLeft + 1, Length(s) - bLeft);
- end
- else
- begin
- s := Copy(s, bLeft + 1, bRight - bLeft - 1);
- end;
- FOutboundHost := ''; {Do not Localize}
- for i := 1 to 4 do
- begin
- FOutboundHost := FOutboundHost + '.' + Fetch(s, ','); {Do not Localize}
- end;
- IdDelete(FOutboundHost, 1, 1);
- // Determine port
- FOutboundPort := IndyStrToInt(Fetch(s, ',')) * 256; {Do not Localize}
- FOutboundPort := FOutboundPort + IndyStrToInt(Fetch(s, ',')); {Do not Localize}
- DataChannelThread.FOutboundClient := TIdTCPClient.Create(nil);
- LDataChannel := TIdTCPClient(DataChannelThread.FOutboundClient);
- LDataChannel.Host := OutboundHost;
- LDataChannel.Port := OutboundPort;
- end;
- begin
- Mode := TIdMappedFtp(Server).OutboundDcMode;
- if Mode = fdcmClient then
- begin
- if APASV then begin
- Mode := fdcmPasv;
- end else begin
- Mode := fdcmPort;
- end;
- end;
- if Mode = fdcmPasv then begin
- //PASV (IfFtp.pas)
- SendPasv;
- end else begin
- //PORT
- SendPort;
- end;
- end;
- {TODO: procedure TIdMappedFtpContext.FreeDataChannelThread;
- Begin
- if Assigned(FDataChannelThread) then begin
- //TODO: çäåñü íàäî Disconnect
- FDataChannelThread.Terminate;
- FDataChannelThread:=NIL;
- end;
- End;}
- function TIdMappedFtpContext.GetFtpCmdLine: string;
- begin
- if Length(FFtpParams) > 0 then begin
- Result := FFtpCommand + ' ' + FFtpParams; {Do not Localize}
- end else begin
- Result := FFtpCommand;
- end;
- end;
- { TIdMappedFtpDataThread }
- procedure TIdMappedFtpDataThread.BeforeRun;
- begin
- inherited BeforeRun;
- //? Is it normal code?
- // TODO: check error. Send reply to client, send abort to server
- //1.Outbound PASV => connect
- if FOutboundClient is TIdTCPClient then
- begin
- TIdTCPClient(FOutboundClient).Connect;
- TIdSimpleServer(FConnection).Listen;
- end
- //2.Local PORT => Connect
- else if FOutboundClient is TIdSimpleServer then
- begin
- TIdTCPClient(FConnection).Connect;
- TIdSimpleServer(FOutboundClient).Listen;
- end;
- end;
- constructor TIdMappedFtpDataThread.Create(AMappedFtpThread: TIdMappedFtpContext);
- begin
- inherited Create(True);
- FMappedFtpThread := AMappedFtpThread; //owner
- StopMode := smTerminate;
- FreeOnTerminate := True;
- FReadList := TIdSocketList.CreateSocketList;
- end;
- destructor TIdMappedFtpDataThread.Destroy;
- begin
- FreeAndNil(FOutboundClient);
- FreeAndNil(FConnection);
- FreeAndNil(FReadList);
- inherited Destroy;
- end;
- procedure TIdMappedFtpDataThread.Run;
- var
- LConnectionHandle, LOutBoundHandle: TIdStackSocketHandle;
- LReadList: TIdSocketList;
- begin
- try
- try
- LConnectionHandle := (Connection.IOHandler as TIdIOHandlerSocket).Binding.Handle;
- LOutBoundHandle := (FOutboundClient.IOHandler as TIdIOHandlerSocket).Binding.Handle;
- FReadList.Clear;
- FReadList.Add(LConnectionHandle);
- FReadList.Add(LOutBoundHandle);
- LReadList := nil;
- if FReadList.SelectReadList(LReadList, IdTimeoutInfinite) then
- begin
- try
- if LReadList.ContainsSocket(LConnectionHandle) then
- begin
- Connection.IOHandler.CheckForDataOnSource(0);
- SetLength(FNetData, 0);
- Connection.IOHandler.InputBuffer.ExtractToBytes(FNetData);
- if Length(FNetData) > 0 then
- begin
- // TODO: DoLocalClientData(TIdMappedPortThread(AThread));//bServer
- FOutboundClient.IOHandler.Write(FNetData);
- end;
- end;
- if LReadList.ContainsSocket(LOutBoundHandle) then
- begin
- Connection.IOHandler.CheckForDataOnSource(0);
- SetLength(FNetData, 0);
- FOutboundClient.IOHandler.InputBuffer.ExtractToBytes(FNetData);
- if Length(FNetData) > 0 then
- begin
- // TODO: DoOutboundClientData(TIdMappedPortThread(AThread));
- FConnection.IOHandler.Write(FNetData);
- end;
- end;
- finally
- LReadList.Free;
- end;
- end;
- finally
- if not FOutboundClient.Connected then
- begin
- // TODO: DoOutboundDisconnect(TIdMappedPortThread(AThread));
- FConnection.Disconnect; //disconnect local
- Stop;
- end;
- if not FConnection.Connected then
- begin
- // TODO: ^^^
- FOutboundClient.Disconnect;
- Stop;
- end;
- end;
- except
- FConnection.Disconnect;
- FOutboundClient.Disconnect;
- Stop;
- end;
- end;
- end.
|