Pascal Synapse error handling - delphi

I have some code written in Lazarus/FreePascal that uses the Synapse IMAPSend library unit. I attempt to login to an IMAP server over SSL (IMAPS) but the call to Login fails.
I've tried checking for exceptions - none are thrown.
Wireshark shows nothing beyond a TCP three-way handshake to the appropriate server and port.
Here's the code
function GetImapResponse(host, port, user, pass:String): String;
var
response: String = '';
imap: TIMAPSend;
no_unseen: integer;
begin
imap := TIMAPSend.create;
try
imap.TargetHost := host; //'10.0.0.16';
imap.TargetPort := port; //'993';
imap.UserName := user; //'red';
imap.Password := pass; //'********';
imap.AutoTLS := false;
imap.FullSSL := true;
response := response + 'IMAP login to '+user+'#'+host+':'+port+' ... ';
if imap.Login then
begin
response := response + 'Logged in OK. ';
// How many unseen?
no_unseen := imap.StatusFolder('INBOX','UNSEEN');
Form1.Label2.Caption := IntToStr(no_unseen);
response := 'INBOX contains ' + IntToStr(no_unseen) + ' unseen messages. ';
end
else
begin
response := response + 'IMAP Login failed. ';
end;
except
on E: Exception do
begin
response := response + 'Exception: ' + E.Message;
showMessage(response);
end;
end;
{
finally
imap.free;
response := response + 'Finally. ';
end;
}
Result := response;
end;
Here's the String result of this function
IMAP login to red#10.0.0.16:993 ... IMAP Login failed.
Question: Is there a way to see some details of what IMAPSend thinks happened?
Update 1
As shown in SimaWB's answer plus comments by Arioch 'The
mySynaDebug := TsynaDebug.Create;
imap.Sock.OnStatus := #MySynaDebug.HookStatus;
imap.Sock.OnMonitor := #MySynaDebug.HookMonitor;
produced a projectname.slog file containing
20130722-103643.605 0011F230HR_SocketClose:
20130722-103643.609 0011F230HR_ResolvingBegin: 10.0.0.16:993
20130722-103643.620 0011F230HR_ResolvingEnd: 10.0.0.16:993
20130722-103643.623 0011F230HR_SocketCreate: IPv4
20130722-103643.628 0011F230HR_Connect: 10.0.0.16:993
20130722-103643.631 0011F230HR_Error: 10091,SSL/TLS support is not compiled!
So I am moving forward :-)
I do have libssl32.dll and libeay32.dll in my project folder but will check I have the right versions and have done the right things with the chicken entrails.
Update 2:
I was using the 64-bit version of Lazarus. The OpenSSL DLLs are 32-bit DLLs which Synapse's ssl_openssl unit loads dynamically. I installed the 32-bit version of Lazarus/FPC and now my IMAP/SSL client program compiles and works as expected.
It seems the current 64-bit Lazarus/FPC binary distribution (v1.0.10/2.6.2) cannot cross-compile to i386 (which I think might have helped).

Did you try synadbg unit of Synapse?
imap := TIMAPSend.create;
imap.Sock.OnStatus := TSynaDebug.HookStatus;
imap.Sock.OnMonitor := TSynaDebug.HookMonitor;
Then the application create a log file with extension '.slog'. May be you can find more details in the log file.

Same error in the .slog file on Ubuntu 64-bit was fixed with "sudo apt-get install libssl-dev" and including "ssl_openssl" in the uses section.

The problem is here: imap.AutoTLS := false;
imap.FullSSL := true; try to set true to false and false to true...
and port should be 587

Related

Client Application Name in DataSnap

I have client-server system that uses DataSnap. I want to log the client application data so I use the TDSServer - OnConnect Event. In this event I can access what I want with the following code:
IP:= DSConnectEventObject.ChannelInfo.ClientInfo.IpAddress
ClientPort:= DSConnectEventObject.ChannelInfo.ClientInfo.ClientPort
Protocol:= DSConnectEventObject.ChannelInfo.ClientInfo.Protocol
AppName:= DSConnectEventObject.ChannelInfo.ClientInfo.AppName
first 3 lines are OK but AppName is empty!!!
(I run server and client on the same computer i.e. localhost)
I have been unable to find any online information about how to specify the AppName when the client connects via TCP/IP. If you look at the code
procedure TDSTCPChannel.Open;
var
ClientInfo: TDBXClientInfo;
begin
inherited;
FreeAndNil(FChannelInfo);
FChannelInfo := TDBXSocketChannelInfo.Create(IntPtr(FContext.Connection), FContext.Connection.Socket.Binding.PeerIP);
ClientInfo := FChannelInfo.ClientInfo;
ClientInfo.IpAddress := FContext.Connection.Socket.Binding.PeerIP;
ClientInfo.ClientPort := IntToStr(FContext.Connection.Socket.Binding.PeerPort);
ClientInfo.Protocol := 'tcp/ip';
FChannelInfo.ClientInfo := ClientInfo;
end;
in DataSnap.DSTCPServerTransport.Pas it is evident that the ClientInfo.AppName is not set.
However, the following work-around works with the Seattle demo DataSnap Basic Server + Client:
In the client, add a Param 'AppName' to the SqlConnection1 component's Params and
set its value to something like 'MyTestApp'. Recompile the client.
Open the server in the IDE and modify the ServerContainerForm's code as shown below.
Code:
uses
[...], DBXTransport;
procedure TForm8.DSServer1Connect(DSConnectEventObject: TDSConnectEventObject);
var
S : String; // added
Info : TDBXClientInfo; // added
begin
ActiveConnections.Insert;
if DSConnectEventObject.ChannelInfo <> nil then
begin
ActiveConnections['ID'] := DSConnectEventObject.ChannelInfo.Id;
ActiveConnections['Info'] := DSConnectEventObject.ChannelInfo.Info;
end;
ActiveConnections['UserName'] := DSConnectEventObject.ConnectProperties[TDBXPropertyNames.UserName];
ActiveConnections['ServerConnection'] := DSConnectEventObject.ConnectProperties[TDBXPropertyNames.ServerConnection];
ActiveConnections.Post;
InsertEvent('Connect');
// following added to get AppName from client
S := DSConnectEventObject.ConnectProperties['AppName'];
Info := DSConnectEventObject.ChannelInfo.ClientInfo;
Info.AppName := S;
DSConnectEventObject.ChannelInfo.ClientInfo := Info;
Caption := DSConnectEventObject.ChannelInfo.ClientInfo.AppName;
end;
As you can see, it works by picking up the value set for AppName in the client's
SqlConnection1.Params in the call to `DSConnectEventObject.ConnectProperties['AppName']'
and then display it on the Caption of the ServerContainerForm.
Obviously, you could pass any other name/value pair by adding them to the SqlConnection's Params on the client and then pick them up on the server by calling DSConnectEventObject.ConnectProperties[].

Query string works in the URL, but not work in http.get() delphi 7

This is my query string:
http://statement.dana-insurance.com/api/insurance?input=<Statement><GroupId>aseman</GroupId><Password>As1234</Password><StatementNo>87841</StatementNo><StatementSerial>92/1/n</StatementSerial><StatementType>MINIBUS-SEDAN</StatementType><DriverSmartCard1>3146339</DriverSmartCard1><DriverSmartCard2>0</DriverSmartCard2><DriverSmartCard3>0</DriverSmartCard3> <NavySmartCard>1776166</NavySmartCard><TotalRentalPrice>320000</TotalRentalPrice><CityDistance>140</CityDistance><BodyInsurancePrice>0</BodyInsurancePrice><AboardInsurancePrice>1400</AboardInsurancePrice><OriginCode>31380000</OriginCode><DestinationCode>31310000</DestinationCode><MoveDate>1394/10/09</MoveDate> <MoveTime>18:31</MoveTime><PassengerCount>4</PassengerCount><ChairCount>4</ChairCount><NavyType>SEDAN</NavyType><CompanyCode>31523</CompanyCode><PlaqueNumber>575n19</PlaqueNumber><PlaqueSerial>12</PlaqueSerial><ValidateTime>1</ValidateTime><IsTtwicePaid>0</IsTtwicePaid><CarType>NORMAL</CarType><Type>INSERT</Type></Statement>
It works in the browser, but it does not work in http.get() in delphi 7, the error is:
server error http/1.1 400 bad request ( the date is invalid )
Note: When I change my ISP and connect to another internet connection it works fine.
this is my code :
http:=TIdHTTP.Create(nil);
HTTP.AllowCookies:=true;
http.HandleRedirects := true;
http.ReadTimeout := 45000;
param:=TStringList.create;
Strings := TStringList.Create;
http.Request.ContentType := 'text/xml';
http.Request.Accept := 'text/xml, */*';
http.ReadTimeout:= 100000;
Memo1.Lines.Add(Req_String);
try
dana_str:=http.get(Req_String);
......
You must ensure that the URL you pass to TIdHTTP is properly encoded. A web browser handles that for you automatically. If you give your original URL to a browser and use a packet sniffer to look at how the URL gets encoded during transmission, you would see the actual URL is:
http://statement.dana-insurance.com/api/insurance?input=<Statement><GroupId>aseman</GroupId><Password>As1234</Password><StatementNo>87841</StatementNo><StatementSerial>92/1/n</StatementSerial><StatementType>MINIBUS-SEDAN</StatementType><DriverSmartCard1>3146339</DriverSmartCard1><DriverSmartCard2>0</DriverSmartCard2><DriverSmartCard3>0</DriverSmartCard3>%20<NavySmartCard>1776166</NavySmartCard><TotalRentalPrice>320000</TotalRentalPrice><CityDistance>140</CityDistance><BodyInsurancePrice>0</BodyInsurancePrice><AboardInsurancePrice>1400</AboardInsurancePrice><OriginCode>31380000</OriginCode><DestinationCode>31310000</DestinationCode><MoveDate>1394/10/09</MoveDate>%20<MoveTime>18:31</MoveTime><PassengerCount>4</PassengerCount><ChairCount>4</ChairCount><NavyType>SEDAN</NavyType><CompanyCode>31523</CompanyCode><PlaqueNumber>575n19</PlaqueNumber><PlaqueSerial>12</PlaqueSerial><ValidateTime>1</ValidateTime><IsTtwicePaid>0</IsTtwicePaid><CarType>NORMAL</CarType><Type>INSERT</Type></Statement>
Note that there are two whitespace characters that have been encoded as %20 instead. URLs are not allowed to contain unencoded whitespace.
In TIdHTTP, you have to encode your original URL manually:
dana_str := http.get(TIdURI.URLEncode(Req_String));
Alternatively:
dana_base_url := 'http://statement.dana-insurance.com/api/insurance?';
data_query = 'input=...';
dana_str := http.get(dana_base_url + TIdURI.ParamsEncode(data_query));
Alternatively:
with TIdURI.Create do
try
Protocol := 'http';
Host := 'statement.dana-insurance.com';
Path := '/api/insurance';
Params := ParamsEncode('input=...');
dana_str := http.get(URI);
finally
Free;
end;

Network problems while using Indy

function UploadToFTP(file: string ; PathSrv : string): Boolean;
var
server, port, user , password: string;
SR : TSearchRec;
begin
Result := True;
FEventLogger := TEventLogger.Create('Upload FTP');
if file <> '' then
begin
try
server := FServer;
port := FPort;
user := FUserName;
password:= FPassword;
FindFirst(file, faArchive, SR);
try // try except
idftp1.Host:= server;
idftp1.Port := StrToInt(port);
idftp1.Username:= user;
idftp1.Password:= password;
idftp1.Connect();
idftp1.Put(file,PathSrv+SR.Name);
except on E: Exception do begin
Result:= False;
FEventLogger.LogMessage('Exception : ' + E.Message , EVENTLOG_ERROR_TYPE , 0, 2);
WriteToLog('Exception: '+ file+' error message: '+ E.Message);
end;
end;
finally
end;
end;
end;
So I have this function that does an ftp upload to some large files on sometimes slow networks. I've tested it localy and it works ok, but on slow networks i get this eror in 99% of the time.
The specified network name is no longer available.
This is a very strange behavior, becouse the FTP is located on a server that has no disconnecting issues. I also try to watch, and it start the file upload and it does almost all the upload before throwing this error. So for example if I have a 100MB file it does 99MB of the upload then throws the error.
Any ideas what is causing this error or what can I do?
Also from time to time I have an other error
Socket Error # 10054
Connection reset by peer.
To mention,i've tried to upload this files using filezilla and it works, so the problem is somewhere in that code, I might miss something.
Are you using Symantec Endpoint Protection or KIS (Kaspersky Internet Security) ? Take a look here, here and here.
The "The specified network name is no longer available." is caused by Symantec Endpoint Protection 11.0, Symantec has identified this as a known issue.
Btw, in your code don't forget to call .Disconnect() after you're finished uploading the file.

URL Not Returning Data Delphi Indy TIdHttp

I am trying to access a URL in Delphi using a TIdHTTP Indy Tool.
I have done the following:
Set Accept Cookies = True
Set Handle Redirect = True
Added a TIdCookieManager
http://sms.saicomvoice.co.za:8900/saicom/index.php?action=login&username=SOME_USERNAME&password=SOME_PASSWORD&login=login
The Post request works and it returns the HTML. The problem is it doesn't return the correct HTML (See Image Below).
If I take that URL ( Filling in the username and password ) and paste it into my browser exactly The Same as my Delphi Application would then logs into the correct website. But as soon as I do it with my Delphi App it returns the HTML for the login page.
The request is supposed to be executed timeously in a TTimer in Delphi.
Can anyone lead me unto the right path or point me in a direction as to how I can solve this problem ?
Some Additional Information
WriteStatus is a Procedure That writes output to a TListBox
BtnEndPoll Stops the timer
Procedure TfrmMain.TmrPollTimer(Sender: TObject);
Var
ResultHTML: String;
DataToSend: TStringList;
Begin
Inc(Cycle, 1);
LstStatus.Items.Add('');
LstStatus.Items.Add('==================');
WriteStatus('Cycle : ' + IntToStr(Cycle));
LstStatus.Items.Add('==================');
LstStatus.Items.Add('');
DataToSend := TStringList.Create;
Try
WriteStatus('Setting Request Content Type');
HttpRequest.Request.ContentType := 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8';
WriteStatus('Setting Request User Agent');
HttpRequest.Request.UserAgent := 'Mozilla/5.0 (Windows NT 5.1; rv:2.0b8) Gecko/20100101 Firefox/4.0b8';
WriteStatus('Posting Request');
ResultHTML := HttpRequest.Post(FPostToURL, DataToSend);
WriteStatus('Writing Result');
FLastResponse := ResultHTML;
WriteStatus('Cycle : ' + IntToStr(Cycle) + ' -- FINISHED');
LstStatus.Items.Add('');
Except
On E: Exception Do
Begin
MakeNextEntryError := True;
WriteStatus('An Error Occured: ' + E.Message);
If ChkExceptionStop.Checked Then
Begin
BtnEndPoll.Click;
WriteStatus('Stopping Poll Un Expectedly!');
End;
End;
End;
End;
* Image Example *
HttpRequest.Request.ContentType := 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8';
That is not a valid ContentType value. That kind of value belongs in the Request.Accept property instead. It tells the server which ContentTypes the client will accept in the response.
ResultHTML := HttpRequest.Post(FPostToURL, DataToSend);
You are posting a blank TStringList. Putting a URL into a browser's address bar sends a GET request, not a POST request, so you should be using TIdHTTP.Get() instead:
ResultHTML := HttpRequest.Get('http://sms.saicomvoice.co.za:8900/saicom/index.php?action=login&username=SOME_USERNAME&password=SOME_PASSWORD&login=login');
You would use TIdHTTP.Post() if you wanted to simulate the HTML webform being submitted to the server (since it specifies method=post), eg:
DataToSend.Add('username=SOME_USERNAME');
DataToSend.Add('password=SOME_PASSWORD');
DataToSend.Add('login=Login');
ResultHTML := HttpRequest.Post('http://sms.saicomvoice.co.za:8900/saicom/index.php?action=login', DataToSend);

Delphi Indy Ping Error 10040

I have a small piece of code that checks if a computer is alive by pinging it. We use to have a room with 40 computer and I wanna check remotely through my program which on is alive.
Therefore I wrote a little ping function using indy
function TMainForm.Ping(const AHost : string) : Boolean;
var
MyIdIcmpClient : TIdIcmpClient;
begin
Result := True;
MyIdIcmpClient := TIdIcmpClient.Create(nil);
MyIdIcmpClient.ReceiveTimeout := 200;
MyIdIcmpClient.Host := AHost;
try
MyIdIcmpClient.Ping;
Application.ProcessMessages;
except
Result := False;
MyIdIcmpClient.Free;
Exit;
end;
if MyIdIcmpClient.ReplyStatus.ReplyStatusType <> rsEcho Then result := False;
MyIdIcmpClient.Free;
end;
So I've developped that at home on my wifi network and everthing just work fine.
When I get back to work I tested and I get an error saying
Socket Errod # 10040 Message too long
At work we have fixed IPs and all the computer and I are in the same subnet.
I tried to disconnect from the fixed IP and connect to the wifi which of course is DHCP and not in the same subnet, and it is just working fine.
I have tried searching the internet for this error and how to solve it but didn't find much info.
Of course I have tried to change the default buffer size to a larger value but it didn't change anything I still get the error on the fixed IP within same subnet.
Moreover, I don't know if this can help finding a solution, but my code treats exceptions, but in that case it takes about 3-4 seconds to raise the error whereas the Timeout is set to 200 milliseconds. And I cannot wait that long over each ping.
By the way I use delphi 2010 and I think it is indy 10. I also have tested on XE2 but same error.
Any idea
----- EDIT -----
This question is answered, now I try to have this running in multithread and I have asked another question for that
Delphi (XE2) Indy (10) Multithread Ping
Set the PacketSize property to 24:
function TMainForm.Ping(const AHost : string) : Boolean;
var
MyIdIcmpClient : TIdIcmpClient;
begin
Result := True;
MyIdIcmpClient := TIdIcmpClient.Create(self);
MyIdIcmpClient.ReceiveTimeout := 200;
MyIdIcmpClient.Host := AHost;
MyIdIcmpClient.PacketSize := 24;
MyIdIcmpClient.Protocol := 1;
MyIdIcmpClient.IPVersion := Id_IPv4;
try
MyIdIcmpClient.Ping;
// Application.ProcessMessages; // There's no need to call this!
except
Result := False;
Exit;
end;
if MyIdIcmpClient.ReplyStatus.ReplyStatusType <> rsEcho Then result := False;
MyIdIcmpClient.Free;
end;
For XE5 and Indy10 this is still a problem, even with different Packet Size.
To answer the more cryptical fix:
ABuffer := MyIdIcmpClient1.Host + StringOfChar(' ', 255);
This is a "magic" fix to get around the fact that there is a bug in the Indy10 component (if I have understood Remy Lebeau right).
My speculation is that this has some connection with the size of the receive buffer. To test my theory I can use any character and don't need to include the host address at all. Only use as many character you need for the receive buffer. I use this small code (C++ Builder XE5) to do a Ping with great success (all other values at their defaults):
AnsiString Proxy = StringOfChar('X',IcmpClient->PacketSize);
IcmpClient->Host = Host_Edit->Text;
IcmpClient->Ping(Proxy);
As you can see I create a string of the same length as the PacketSize property. What you fill it with is insignificant.
Maybe this can be of help to #RemyLebeau when he work on the fix.
use this code
ABuffer := MyIdIcmpClient1.Host + StringOfChar(' ', 255);
MyIdIcmpClient.Ping(ABuffer);

Resources