I have inherited an extensive (199 commands) Delphi 7 Indy 9 app that I am upgrading to Indy 10 (in D10.1). I have upgraded all the code, and it compiles and runs. The problem I have is that now in Indy 10 all the handlers also return a response code (and text) in addition to the coded response that they did under Indy 9.
For example:
// server
procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
Rights: String;
begin
if BillingUserRegistered(ASender.Params[0], ASender.Params[1], Rights) then
begin
myClient := TClientData.Create;
myClient.ClientName := ASender.Params[0];
myClient.ClientHost := #32; // indy9 was .Thread.Connection.LocalName;
myClient.ID := Now;
ASender.Context.Data := myClient;
ListBox1.Items.AddObject(
PadR(myClient.ClientName,12,' ') + '=' +
FormatDateTime('yyyy-mm-dd hh:nn:ss', myClient.ID),
TDateTimeO.Create(myClient.ID));
ASender.Context.Connection.IOHandler.WriteLn('SUCCESS' + ' ' + Rights)
end
else
ASender.Context.Connection.IOHander.WriteLn('Login failed!');
end;
...
// client side
function TfrmLogin.VerifyUserNameAndPassword(username, password: String): Boolean;
var
response, response1: String;
begin
frmMain.IdTCPClient1.IOHandler.WriteLn('login' + ' ' +
username + ' ' + password)
response := frmMain.IdTCPClient1.IOHandler.ReadLn();
// I have to add this now to capture the response code too!
response1 := frmMain.IdTCPClient1.IOHandler.ReadLn(); // 200 OK
// ------------------------------------------------------
if Copy(response,1,7) = 'SUCCESS' then
begin
rights := Copy(response,9,4);
There are a lot of command handlers, and they all have their own custom responses. That's a lot of code to change at the client. Is there a way I can tell the IdCmdTCPServer to suppress the standard '200 Ok' response if the command handler already provides it's own? Or am I in for a long night?
Thanks
If you need to suppress the default command responses, you can either:
clear the TIdCommandHandler's ReplyNormal and ExceptionReply properties (this also works in Indy 9, except that ExceptionReply was ReplyExceptionCode in that version), and the server's CommandHandlers.ExceptionReply property (Indy 10 only).
set the TIdCommand.PerformReply property to false in your OnCommand handler (this also works in Indy 9):
procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
...
begin
ASender.PerformReply := False;
...
end;
set the server's CommandHandlers.PerformReplies property to false (Indy 10 only - it will set TIdCommand.PerformReply to false by default):
IdCmdTCPServer1.CommandHandlers.PerformReplies := False;
On the other hand, you should consider using the command handler responses the way they are designed to be used, eg:
procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
Rights: String;
begin
if ASender.Params.Count = 2 then
begin
if BillingUserRegistered(ASender.Params[0], ASender.Params[1], Rights) then
begin
...
ASender.Reply.SetReply('SUCCESS', Rights);
end
else
ASender.Reply.SetReply('ERROR', 'Login failed!');
end
else
ASender.Reply.SetReply('ERROR', 'Wrong number of parameters!');
end;
I would even go as far as saying that you should set the TIdCommandHandler.NormalReply.Code property to SUCCESS and the TIdCommandHandler.ExceptionReply.Code property to ERROR, and then you can do this inside your OnCommand handler:
procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
Rights: String;
begin
if ASender.Params.Count <> 2 then
raise Exception.Create('Wrong number of parameters!');
if not BillingUserRegistered(ASender.Params[0], ASender.Params[1], Rights) then
raise Exception.Create('Login failed!');
...
ASender.Text.Text := Rights;
end;
With that said, any of these approaches should work fine without changing your existing client code. However, in Indy 10, I would suggest using SendCmd() instead of WriteLn()/ReadLn() directly:
function TfrmLogin.VerifyUserNameAndPassword(username, password: String): Boolean;
var
response: String;
begin
response := frmMain.IdTCPClient1.SendCmd('login ' + username + ' ' + password);
if response = 'SUCCESS' then
begin
rights := frmMain.IdTCPClient1.LastCmdResult.Text.Text;
...
end else begin
// error message in frmMain.IdTCPClient1.LastCmdResult.Text.Text ...
end;
end;
Alternatively, you can let SendCmd() raise an exception if it does not receive a SUCCESS reply:
function TfrmLogin.VerifyUserNameAndPassword(username, password: String): Boolean;
begin
try
frmMain.IdTCPClient1.SendCmd('login ' + username + ' ' + password, 'SUCCESS');
except
on E: EIdReplyRFCError do begin
// error message in E.Message ...
...
Exit;
end;
end;
rights := frmMain.IdTCPClient1.LastCmdResult.Text.Text;
...
end;
SendCmd() does exist in Indy 9, but it only supports numeric-based response codes, which you are not using. As you can see above, SendCmd() in Indy 10 supports string-based response codes as well as numeric ones.
On a side note: in your server code, the OnCommand handler runs in a worker thread, so your use of ListBox1.Items.AddObject() is not thread-safe. Any access to the UI must be synchronized with the main UI thread, using techniques like TThread.Synchronize(), TThread.Queue(), TIdSync, TIdNotify, etc, eg:
procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
Rights: String;
myClient: TClientData;
begin
if ASender.Params.Count = 2 then
begin
if BillingUserRegistered(ASender.Params[0], ASender.Params[1], Rights) then
begin
myClient := TClientData(ASender.Context.Data);
if myClient = nil then
begin
myClient := TClientData.Create;
ASender.Context.Data := myClient;
end;
myClient.ID := Now;
myClient.ClientName := ASender.Params[0];
myClient.ClientHost := GStack.HostByAddress(ASender.Context.Binding.PeerIP, ASender.Context.Binding.IPVersion);
// In Indy 9, this would be:
// myClient.ClientHost := GStack.WSGetHostByAddr(ASender.Thread.Connection.Socket.PeerIP);
// NOT ASender.Thread.Connection.LocalName!
TThread.Queue(nil,
procedure
begin
ListBox1.Items.AddObject(
PadR(myClient.ClientName,12,' ') + '=' + FormatDateTime('yyyy-mm-dd hh:nn:ss', myClient.ID),
TDateTimeO.Create(myClient.ID));
end
);
ASender.Reply.SetReply('SUCCESS', Rights);
end
else
ASender.Reply.SetReply('ERROR', 'Login failed!');
end
else
ASender.Reply.SetReply('ERROR', 'Wrong number of parameters!');
end;
Make sure your BillingUserRegistered() function is similarly thread-safe, if it is not already.
Related
in delphi 10 with the Datasnap component I am trying to declare a Post method that receives an XML file but I can't.
Does anybody know if Datasnap only can receive Json format type in the body?
(in contrary any example will be great)
Thanks in advance.
You can overcome this with your datasnap WebModuleUnit and your custom ufileUploader unit like this :
In WebModuleUnit :
procedure TWebModule1.WebModuleDefaultAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
if Request.InternalPathInfo.StartsWith('/UploadFile') then
Response.Content := ufileUploader.UploadFile(Request)
else if (Request.InternalPathInfo = '') or (Request.InternalPathInfo = '/')
then
Response.Content := ReverseString.Content
else
Response.SendRedirect(Request.InternalScriptName + '/');
end;
in your ufileUploader unit :
unit ufileUploader;
interface
uses Web.HTTPApp;
function UploadFile(ARequest: TWebRequest): string;
implementation
uses System.SysUtils, System.Classes, Web.ReqMulti;
function UploadFile(ARequest: TWebRequest): string;
const
DestPath = 'c:\';
var
i: integer;
LFileName: string;
LStream: TMemoryStream;
begin
if not TMultipartContentParser.CanParse(ARequest) then
begin
Result := 'Cannot parse request';
Exit;
end;
if ARequest.Files.Count < 1 then
begin
Result := 'No file sended';
Exit;
end;
LStream := TMemoryStream.Create;
try
// You have sended ARequest.Files.Count files
for i := 0 to ARequest.Files.Count - 1 do
begin
LFileName := string(ARequest.Files.Items[i].FileName);
LStream.Clear;
// Read the sended file stream
LStream.CopyFrom(ARequest.Files.Items[i].Stream,
ARequest.Files.Items[i].Stream.Size);
LStream.Position := 0;
// Do what you want with the stream
LStream.SaveToFile(DestPath + LFileName);
end;
Result := Format('%d files saved to %s ', [ARequest.Files.Count, DestPath]);
finally
FreeAndNil(LStream);
end;
end;
end.
This is not the answer you are hoping for, but I opened a case with Embarcadero for this exact problem and their response was:
Hello
My name is Steve Axtell. I am looking at this case.
I am sorry but Datasnap does not support XML. It only supports JSON,
hence the error message.
Regards
Steve Axtell Embarcadero Support ref:_00D30HwR._5005a28Y6yq:ref
The problem is in Datasnap.DSService.TDSRESTService.ProcessParameters:
// Look for more parameters in the body
if (Content <> nil) and (Length(Content) > 0) then
begin
if LBody = nil then
begin
LBodyArray := nil;
LBody := TJSONObject.ParseJSONValue(Content, 0);
LFreeBody := LBody;
if LBody = nil then
begin
//ParamArray.Free;
raise TDSServiceException.Create(SNoJSONValue);
end;
if (LBody is TJSONObject) and (TJSONObject(LBody).Count = 1) and
(TJSONObject(LBody).Pairs[0].JSonString.Value = PARAMETERS_PAIR) and
(TJSONObject(LBody).Pairs[0].JsonValue is TJSONArray) then
begin
LBodyArray := TJSONArray(TJSONObject(LBody).Pairs[0].JsonValue);
LBodyArrayIndex := 0;
end
end;
end;
If the body is not in JSON, it fails to process the REST request, and I've not found a way to force DataSnap to not look in the body for additional parameters.
Note that in my case, I'm not using TComponent but TDataModule for the server methods.
I started implementing a system using a client server connection with a TIdCmdTcpServer and a TIdTcpClient.
The connection is established fine and communication seems to work in general, too. But LastCmdResults contains always the response of the command issued before the last command. It starts with an empty response for the TcpClient.Connect and then continues with a "welcome" as a response to the first TcpClient.SendCmd ('LIST'). When I issue the LIST command again I get the desired result but for the one before (tested with a counter variable).
Relevant Code Snippets:
Initialising Command Handler
CmdHandler := TCPCmdServer.CommandHandlers.Add;
CmdHandler.Name := 'cmhList';
CmdHandler.Command := 'LIST';
CmdHandler.OnCommand := Cmd_ListDevices;
CmdHandler.ExceptionReply.NumericCode := 550;
CmdHandler.Disconnect := FALSE;
TCPCmdServer.Active := TRUE;
Command handler event Cmd_ListDevices
procedure TSPM_Server.Cmd_ListDevices (aSender : TIdCommand);
begin
aSender.Reply.SetReply (200, 'List');
aSender.Reply.Text.Add ('Device 1');
aSender.Reply.Text.Add ('Device 2');
aSender.Reply.Text.Add ('Device 3');
aSender.SendReply;
end;
Client Side
function TSPM_TCPClient.Connect (var aResponseText : string) : boolean;
begin
TcpClient.Connect;
aResponseText := TcpClient.LastCmdResult.Text.Text;
result := TcpClient.Connected;
end;
function TSPM_TCPClient.RequestList (var aList : string) : integer;
begin
aList := '';
result := TcpClient.SendCmd ('LIST');
if result = 200 then
begin
aList := 'CMD: ' + TcpClient.LastCmdResult.DisplayName + sLineBreak
+ TcpClient.LastCmdResult.Text.Text;
end;
end;
Anything obviously wrong here?
LastCmdResults contains always the response of the command issued before the last command
That happens when you have the server setup to send a greeting when a new client connects (see the TIdCmdTCPServer.Greeting property), but your client code is not reading that greeting. The greeting remains in the client's receive buffer until it is read. So, the 1st SendCmd() will read the greeting, then the 2nd SendCmd() will read the response of the 1st SendCmd(), and so on.
After TIdTCPClient.Connect() is successful, call TIdTCPClient.GetResponse() immediately to read the greeting, TIdTCPClient.Connect() will not read it for you, eg:
function TSPM_TCPClient.Connect (var aResponseText : string) : boolean;
begin
TcpClient.Connect;
try
TcpClient.GetResponse(200); // <-- add this!
aResponseText := TcpClient.LastCmdResult.Text.Text;
Result := True;
except
TcpClient.Disconnect;
Result := False;
end;
end;
Then you can call TIdTCPClient.SendCmd() afterwards as needed.
With the TIdTCPServer component of Indy, a package is received in two fractions but the client sent one with 64 KB.
How do I receive the complete package in the Server OnExecute event?
Now I put a prototype (Server and Client) code to recreate the situation.
Server Code
procedure TFrmServer.IdTCPServer1Execute(AContext: TIdContext);
Var
ReceivedBytesTCP : Integer;
IBuf : TIdBytes;
begin
if Not AContext.Connection.IOHandler.InputBufferIsEmpty then Begin
Try
ReceivedBytesTCP := AContext.Connection.IOHandler.InputBuffer.Size;
SetLength(IBuf,ReceivedBytesTCP);
AContext.Connection.IOHandler.ReadBytes(IBuf,ReceivedBytesTCP,False);
AContext.Connection.IOHandler.Write(IBuf,Length(IBuf),0);
Except
On E : Exception Do Begin
Memo1.Lines.Add('Except Server TCP: ' + E.Message);
End;
End;
End Else Begin
Sleep(1);
End;
end;
Client Code
procedure TFrm_TCP_Client.BtnSendClick(Sender: TObject);
Var
IBuf,RBuf : TIdBytes;
I : Integer;
LenPacket : Integer;
begin
LenPacket := StrToInt(EdtLength.Text);
if IdTCPClient1.Connected then Begin
SetLength(IBuf,LenPacket);
for I := 1 to LenPacket do
IBuf[I] := 1;
IdTCPClient1.IOHandler.Write(IBuf,Length(IBuf),0);
I := 0;
Repeat
IdTCPClient1.IOHandler.CheckForDataOnSource(50);
Inc(I);
Until Not IdTCPClient1.IOHandler.InputBufferIsEmpty or (I >= 10);
If Not IdTCPClient1.IOHandler.InputBufferIsEmpty Then Begin
SetLength(RBuf,IdTCPClient1.IOHandler.InputBuffer.Size);
IdTCPClient1.IOHandler.ReadBytes(RBuf,IdTCPClient1.IOHandler.InputBuffer.Size,False);
if Length(RBuf) = Length(IBuf) then
Memo1.Lines.Add('Response Received OK: '+IntToStr(Length(RBuf)))
Else
Memo1.Lines.Add('Response Received With Different Length: '+IntToStr(Length(RBuf)));
if Not IdTCPClient1.IOHandler.InputBufferIsEmpty then
Memo1.Lines.Add('Llego otro Mensaje');
End Else Begin
Memo1.Lines.Add('NO Response Received');
End;
End;
end;
How to know that a message is the first or the second fragment?
How to force the receive of second fragment?
There is no 1-to-1 relationship between sends and reads in TCP. It is free to fragment data however it wants to optimize network transmissions. TCP guarantees only that data is delivered, and in the same order it was sent, but nothing about HOW data is fragmented during transmission. TCP will reconstruct the fragments on the receiving end. This is simply how TCP works, it is not unique to Indy. Every TCP app has to deal with this issue regardless of which TCP framework is used.
If you are expecting 64KB of data, then simply read 64KB of data, and let the OS and Indy handle the fragments internally for you. This fragmentation of TCP is exactly why Indy's IOHandler uses an InputBuffer to collect the fragments when piecing data back together.
Update: stop focusing on fragments. That is an implementation detail at the TCP layer, which you are not operating at. You don't need to deal with fragments in your code. Let Indy handle it for you. Just focus on your application level protocol instead.
And FYI, you have essentially implemented an ECHO client/server solution. Indy has actual ECHO client/server components, TIdECHO and TIdECHOServer, you should have a look at them.
In any case, your server-side exception handling is very problematic. It is not syncing with the main UI thread (OnExecute is called in a worker thread). But, more importantly, it as preventing TIdTCPServer from processing any notifications issued by Indy itself when the client connection is lost/disconnected, so the client thread will keep running and not stop until you deactivate the server. DO NOT swallow Indy's own exceptions (which are derived from EIdException). If you need to catch them in your code, you should re-raise them when done, let TIdTCPServer process them. But, in your example, it would be easier to remove the try..except altogether and use the server's OnException event instead.
Also, your client-side reading loop is wrong for what you are attempting to do with it. You are not initializing IBuf correctly. But, more importantly, you are using a very short timeout (TCP connections may have latency), and you are breaking your reading loop as soon as any data arrives or 500ms max have elapsed, even if there is more data still coming. You should be reading until there is nothing left to read.
Try something more like this instead:
Server:
procedure TFrmServer.IdTCPServer1Execute(AContext: TIdContext);
var
IBuf : TIdBytes;
begin
AContext.Connection.IOHandler.ReadBytes(IBuf, -1);
AContext.Connection.IOHandler.Write(IBuf);
end;
procedure TFrmServer.IdTCPServer1Exception(AContext: TIdContext, AException: Exception);
var
Msg: string;
begin
if AException <> nil then
Msg := AException.Message
else
Msg := 'Unknown';
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add('Except Server TCP: ' + Msg);
end
);
end;
Client:
procedure TFrm_TCP_Client.BtnSendClick(Sender: TObject);
Var
IBuf,RBuf : TIdBytes;
LenPacket : Integer;
begin
if not IdTCPClient1.Connected then Exit;
LenPacket := StrToInt(EdtLength.Text);
if LenPacket < 1 then Exit;
SetLength(IBuf, LenPacket);
FillBytes(IBuf, LenPacket, $1);
try
IdTCPClient1.IOHandler.InputBuffer.Clear;
IdTCPClient1.IOHandler.Write(IBuf);
except
Memo1.Lines.Add('Request Send Error');
Exit;
end;
try
while IdTCPClient1.IOHandler.CheckForDataOnSource(500) do;
if not IdTCPClient1.IOHandler.InputBufferIsEmpty then
begin
IdTCPClient1.IOHandler.ReadBytes(RBuf, IdTCPClient1.IOHandler.InputBuffer.Size, True);
if Length(RBuf) = Length(IBuf) then
Memo1.Lines.Add('Response Received OK: ' + IntToStr(Length(RBuf)))
else
Memo1.Lines.Add('Response Received With Different Length. Expected: ' + IntToStr(Length(IBuf)) + ', Got: ' + IntToStr(Length(RBuf)));
end
else
Memo1.Lines.Add('NO Response Received');
except
Memo1.Lines.Add('Response Receive Error');
end;
end;
A better solution would be to not rely on such logic at all, be more explicit about the structure of your data protocol, for instance <length><data>, eg:
Server:
procedure TFrmServer.IdTCPServer1Execute(AContext: TIdContext);
var
IBuf : TIdBytes;
LenPacket : Int32;
begin
LenPacket := AContext.Connection.IOHandler.ReadInt32;
AContext.Connection.IOHandler.ReadBytes(IBuf, LenPacket, True);
AContext.Connection.IOHandler.Write(LenPacket);
AContext.Connection.IOHandler.Write(IBuf);
end;
procedure TFrmServer.IdTCPServer1Exception(AContext: TIdContext, AException: Exception);
var
Msg: string;
begin
if AException <> nil then
Msg := AException.Message
else
Msg := 'Unknown';
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add('Except Server TCP: ' + Msg);
end
);
end;
Client:
procedure TFrm_TCP_Client.BtnSendClick(Sender: TObject);
Var
IBuf,RBuf : TIdBytes;
LenPacket : Int32;
begin
if not IdTCPClient1.Connected then Exit;
LenPacket := StrToInt(EdtLength.Text);
if LenPacket < 1 then Exit;
SetLength(IBuf, LenPacket);
FillBytes(IBuf, LenPacket, $1);
try
IdTCPClient1.IOHandler.InputBuffer.Clear;
IdTCPClient1.IOHandler.Write(LenPacket);
IdTCPClient1.IOHandler.Write(IBuf);
except
Memo1.Lines.Add('Request Send Error');
Exit;
end;
try
IdTCPClient1.IOHandler.ReadTimeout := 5000;
LenPacket := IdTCPClient1.IOHandler.ReadInt32;
IdTCPClient1.IOHandler.ReadBytes(RBuf, LenPacket, True);
except
Memo1.Lines.Add('Response Receive Error');
Exit;
end;
Memo1.Lines.Add('Response Received OK');
end;
I'm trying to create a function to check if a folder exists using Overbyte ICS FTP component.Using the DIR command from the icsftp does not display anything in my memo log.
I'm interested in parsing the result of the dir command into a stringlist in order to search for a specific folder.
For the moment I use an indy function like this. How can I make the same thing with ICS?
function exista_textul_in_stringlist(const stringul_pe_care_il_caut:string; stringlistul_in_care_efectuez_cautarea:Tstringlist):boolean;
begin
if stringlistul_in_care_efectuez_cautarea.IndexOf(stringul_pe_care_il_caut) = -1 then
begin
result:=false;
//showmessage('Textul "'+text+'" nu exista!' );
end
else
begin
result:=true;
//showmessage('Textul "'+text+'" exista la pozitia '+ inttostr(ListBox.Items.IndexOf(text)));
end;
end;
function folder_exists_in_ftp(folder_name_to_search_for,ftp_hostname,ftp_port,ftp_username,ftp_password,ftp_root_folder:string;memo_loguri:Tmemo):boolean;
Var
DirList : TStringList;
ftp:Tidftp;
antifreeze:TidAntifreeze;
var i,k:integer;
begin
dateseparator:='-';
Result := False;
DirList := TStringList.Create;
ftp:=tidftp.Create;
antifreeze:=TidAntifreeze.Create;
try
antifreeze.Active:=true;
ftp.Host:=ftp_hostname;
ftp.Port:=strtoint(ftp_port);
ftp.username:=ftp_username;
ftp.password:=ftp_password;
ftp.Passive:=true;
ftp.Connect;
ftp.ChangeDir(ftp_root_folder);
ftp.List(DirList, folder_name_to_search_for, True);
if DirList.Count > 0 then begin
k := DirList.Count;
DirList.Clear; // DIRLIST will hold folders only
for i := 0 to k - 1 do begin
if (ftp.DirectoryListing.Items[i].FileName <> '.') and (ftp.DirectoryListing.Items[i].FileName <> '..') then begin
if ftp.DirectoryListing.Items[i].ItemType = ditDirectory then begin
DirList.Add(ftp.DirectoryListing.Items[i].FileName);
end;
end;
end;
end;
if exista_textul_in_stringlist(folder_name_to_search_for,DIRLIST) then
begin
Result := True;
memo_loguri.Lines.Add(datetimetostr(now)+' - caut folderul "'+folder_name_to_search_for+'" in directorul ftp "'+ftp_root_folder+'" => EXISTS!');
end
ELSE
begin
result:=false;
memo_loguri.Lines.Add(datetimetostr(now)+' - caut folderul "'+folder_name_to_search_for+'" in directorul ftp "'+ftp_root_folder+'" => NOT exists!');
end;
finally
ftp.Free;
antifreeze.Free;
DirList.Free;
end;
end;
I assume you are using the latest released version of OverbyteIcs (ICS-V8.16 (Apr, 2015)).
If you just need to check if a remote directory exists its a good recommendation mentioned in the other answer to avoid a list (it could be a time consuming operation if a lot of files and folders are returned).
I suggest you just try to be "optimistic" and change to the remote dir you wish to investigate using FTP.Cwd. If this call return true the folder of course exists, and if you plan to continue with the same client you have to change back to the original dir. On the other hand, if the call fails, the directory does not exist if the ftp server reponds with code 550.
I have included a simple sample doing the above (however, it does not provide the "change-back-to-original-dir-on-success" feature):
uses
...
OverbyteIcsFtpCli;
function FtpRemoteDirExists(
HostName: String;
UserName: String;
Password: String;
HostDirToCheck : String ) : Boolean;
const
cFtpCode_FileOrDirNotExists = 550;
var
FTP: TFtpClient;
begin
FTP := TFtpClient.Create(nil);
try
FTP.HostName := HostName;
FTP.Passive := True;
FTP.Binary := True;
FTP.Username := UserName;
FTP.Password := Password;
FTP.Port := '21';
if not FTP.Open then
raise Exception.Create('Failed to connect: ' + FTP.ErrorMessage);
if (not FTP.User) or (not FTP.Pass) then
raise Exception.Create('Failed to login: ' + FTP.ErrorMessage);
FTP.HostDirName := HostDirToCheck;
if FTP.Cwd then
Result := True
else
begin
if FTP.StatusCode = cFtpCode_FileOrDirNotExists then
Result := False
else
raise Exception.Create('Failed to change dir: ' + FTP.ErrorMessage);
end;
finally
FTP.Free;
end;
end;
You better use a command like SIZE (TFtpClient.Size) or MLST (TFtpClient.Mlst) to check for file existence.
Using LIST is quite an overkill.
How do I get the message from any pacs server Delphi and display this message ASCII format in memo1
is it possible to use could this indy component.
This is an example code from http://sourceforge.net/projects/indy10clieservr/
Send C-ECHO Command from any Modality Emulator or Any PACS Server. Connected Ok but cant see incoming message in memo1. But Chamelon HL7 component display to message on Delphi
procedure TServerMainForm.IdTCPServerConnect(AContext: TIdContext);
begin
memo1.Lines.Add('Connection from ..PeerIP/IP' + AContext.Binding.PeerIP + ' // ' + AContext.Binding.IP + ' # ' + dateToStr(now) + '->' + TimeToStr(now) );
AContext.Connection.IOHandler.WriteLn('C-ECHO-RSP');
end;
procedure TServerMainForm.IdTCPServerExecute(AContext: TIdContext);
var CommBlock, NewCommBlock : TINDYCMD;
buf : TIdBytes;
line : String;
i : integer;
begin
memo1.Lines.Add('server execute start' );
with AContext.Connection do
begin
IOHandler.Readln(line);
end;
try
////////////// This line = 0 and cant see anything memo1. ////////////
if length(line) > 0 then
begin
memo1.Lines.Add(line );
i:= strToInt(Line);
end
else
i:=-1;
except
end;
case i of
0: begin
TCPServerExecuteExchangeStrings(AContext);
end;
1 : begin
TCPServerExecuteExchangeRecords(AContext);
end;
2: begin
end;
else
//
end;
LEDShape.brush.Color := clgreen;
memo1.Lines.Add('server execute done' );
end;
I don't quite understand the question... But I did quickly see a problem:
Any Internet Server needs to be validate input. Not doing so is a security risk.
In this case you are expecting to be sent a valid integer. If you don't get a valid integer you raise an exception. This may be desired behavior but I doubt it.
specifically this line: i:= strToInt(Line);
Instead you might try..
if TryStrToInt(line,i) then
// Handle valid integer sent
else
// Handle Invalid integer sent