URL Not Returning Data Delphi Indy TIdHttp - delphi

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);

Related

How to activate/force Basic Auth on THTTPClient

I can't find an option to activate Basic Auth on THTTPClient.
My main "issue" is that the first request response with a HTTP/1.1 401 Unauthorized and it triggers an auth event. I want to do it on the first "try".
Here is my code:
uses
System.Net.HTTPClient, System.Net.URLClient, System.NetEncoding;
procedure DoRequest(const url, username, password, filename: string);
var
http: THTTPClient;
ss: TStringStream;
base64: TBase64Encoding;
begin
http := THTTPClient.Create;
ss := TStringStream.Create('', TEncoding.UTF8);
base64 := TBase64Encoding.Create(0);
try
// option #1 - build in credential storage
http.CredentialsStorage.AddCredential(
TCredentialsStorage.TCredential.Create(
TAuthTargetType.Server, '', url, username, password
));
// option #2 - custom "Authorization"
http.CustomHeaders['Authorization'] := 'Basic ' + base64.Encode(username + ':' + password);
http.ProxySettings.Create('127.0.0.1', 8888); // for fiddler
http.Get(url, ss);
ss.SaveToFile(filename);
finally
base64.Free;
ss.Free;
http.Free;
end;
end;
Option #2 includes the Authorization in the first request. Option #1 does not.
How can I do that?
Update - PreemptiveAuthentication does not exist in Seattle
In Delphi 11 and later, using THTTPClient.CredentialsStorage should work, you just need to set THTTPClient.PreemptiveAuthentication to true:
Property controls preemptive authentication. When set to True, then basic authentication will be provided before the server gives an unauthorized response.
However, PreemptiveAuthentication does not exist in Delphi 10.x, including Seattle, so you will have to stick with your CustomHeaders solution until you can upgrade to a modern Delphi version.

IdHTTP and IdCookieManager miss one cookie

I use TIdHTTP for web-service request, after autentification I must keep session information but IdCookieManager seems lost always one cookie...
This is my very simple snippet
procedure TForm1.Button5Click(Sender: TObject);
var
i : Integer;
Cookie : TIdCookies;
begin
Memo1.Lines.Clear;
try
IdHTTP1.AllowCookies := true;
IdHTTP1.CookieManager := IdCookieManager1;
IdHTTP1.Get(Edit2.Text);
if IdCookieManager1.CookieCollection.Count = 0
then Memo1.Lines.Add('Empty');
Cookie := IdCookieManager1.CookieCollection;
for i := 0 to Cookie.Count -1 do
Memo1.Lines.Add(Cookie.Cookies[i].Domain + ': ' + Cookie.Cookies[i].CookieName +
'=' + Cookie.Cookies[i].Value);
except
on E : Exception do
Memo1.Lines.Add(E.Message);
end;
end;
For example if I do IdHTTP1.Get('www.google.com'); I get two cookie (1P_JAR,NID), but if I do on a web browser it give three (1P_JAR,NID and CONSENT).
And this is for all URL, It seems like it "loses" always one cookie
You're trying to compare a single GET request response to a browser response. It's not the same thing.
If you use e.g. Postman you will get the very same result (2 cookies for www.google.com).
I think you should modify the service server side if you are in control of it or consume it in a different way if you are not.

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;

Pascal Synapse error handling

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

Resources