1

May I ask for a little help using Indy to login to a website please?

Firstly, just as a 'proof of concept' I used a TWebBrowser to test my credentials in the following manner ...

procedure TfrmMain.cxButton1Click(Sender: TObject);
begin
  webBrow.Navigate('http://assurance.redtractor.org.uk/rtassurance/services.eb');
end;

procedure TfrmMain.webBrowDocumentComplete(ASender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
var
  CurrentBrowser: IWebBrowser2;
  TopBrowser: IWebBrowser2;
  Document: OleVariant;
  Doc3 :  IHTMLDocument3;
  Frm  :  IHtmlFormElement;

begin
 CurrentBrowser := pDisp as IWebBrowser2;
 TopBrowser := (ASender as TWebbrowser).DefaultInterface;
 if Assigned(CurrentBrowser) and Assigned(TopBrowser) then
   begin
       if CurrentBrowser = TopBrowser then
       begin
         Doc3 := CurrentBrowser.Document as IHTMLDocument3;
         Webbrow.OnDocumentComplete := nil; // remove handler to avoid reentrance
               Doc3.getElementById('el9M9AQXIL51JI3_loginPnl_username').setAttribute('value', 'aValidUserName', 0);
     Doc3.getElementById('el9M9AQXIL51JI3_loginPnl_password').setAttribute('value', 'aValidPassword', 0);
     //Frm := Doc3.getElementById('ct100') as IHtmlFormElement;
     Doc3.GetElementByID('el9M9AQXIL51JI3_loginPnl_button').click();
    end;
  end;
end;

I got the above from the whosrdaddy answer here Automated Log In (webBrowser)

That logs me into the site and takes me to a search page ... exactly what I need.

However, I'd like to avoid using a TWebBrowser as I thought my searches would be slow due to the fact the page would need to be rendered. With that in mind I tried to use Indy 10 to login to the same address, passing the parameters like so ...

idRedTractor.Post(login_URL, Request, Response);

But all this returns is a 'Server Error, Unauthenticated UserName' response.

My full code for trying to login is ...

procedure TfrmMain.btnLogonClick(Sender: TObject);
var
  Response      : TMemoryStream;
  searchResp    : TMemoryStream;
  Request       : TStringList;
  searchReq     : TStringList;
  resultStr     : TStringList;

begin

    with IdRedTractor do
    begin
        allowCookies := true;
        cookieManager := cookieRedTractor;
        IOhandler := IdSSLRedTractor;
        request.Accept := 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
        request.contentType := 'text/html';
        request.userAgent := 'Mozilla/3.0 (compatible; Indy Library)';
    end;

  with IdSSLRedTractor do
  begin
    // SSLOptions does not make a difference. Still get a Server Error message
    SSLOptions.Mode := sslmUnassigned;
    //SSLOptions.Mode := sslmBoth;
    //SSLOptions.Mode := sslmClient;
    //SSLOptions.Mode := sslmServer;
  end;

  try
    try
      response    := TMemoryStream.Create;
      searchResp  := TMemoryStream.Create;
      try
        request   := TStringList.Create;
        searchReq := TStringList.Create;
        resultStr := TStringList.Create;

        // Individual params via FireBug
        Request.Add('__EVENTARGUMENT=login');
        Request.Add('__EVENTTARGET=el9M9AQXIL51JI3$loginPnl');
        Request.Add('__VIEWSTATE=/wEPDwULLTEzMjc3NzQ0ODEPZBYEAgEPZBYCZg9kFgJmDxYCHgRUZXh0BRNDaGVja2VycyAmIFNlcnZpY2VzZAIDD2QWBAICDxYCHgdWaXNpYmxlaGQCCQ9kFgICAg9kFgICBA8WAh8BZxYCAgEPFgIfAWhkZD3T1Ydwd12+6SzZOgVHrnka9LKB');
        Request.Add('__VIEWSTATEGENERATOR=9D5BCA8C');
        Request.Add('ebAbPwd=' + edtUserPass.text);
        Request.Add('ebAbPwd=');
        Request.Add('ebAbUser=' + edtUserName.text);
        Request.Add('ebAbUser=');
        Request.Add('el9M9AQXIL51JI3$loginPnl_...=' + edtUserName.Text);
        Request.Add('el9M9AQXIL51JI3$loginPnl_...=' + edtUserPass.text);
        Request.Add('el9OK3XX11WQS60_email=');{}

        IdRedTractor.Request.Referer := 'http://assurance.redtractor.org.uk/rtassurance/schemes.eb';//initial_URL;
        IdRedTractor.Post('http://assurance.redtractor.org.uk/rtassurance/services.eb', Request, Response);

        if idRedtractor.ResponseCode = 200 then
        begin
          resultStr.Clear;
          Response.Position := 0;
          resultStr.LoadFromStream(Response);
          mmoResponse.Lines.AddStrings(resultStr);
        end;
      finally
        request.Free;
        searchReq.Free;
        resultStr.Free;
      end;
    finally
      response.Free;
      searchResp.Free;
    end;
  except
    on e: Exception do
      showMessage(e.Message);
  end;
end;

Just is case there is some value in the versions of the SSL DLL's, they are 'libeay32.dll' v1.0.1.3 and 'ssleay32.dll', also v1.0.1.3.

May I ask for your help please in understanding what I have missed or done wrong that prevents me from logging into this site with a TidHTTP?

Community
  • 1
  • 1
Johnny
  • 539
  • 8
  • 20
  • Thanks for the comment whosrdaddy. I have cleared the Server Error issue. Rather than use the values for the parameters individually, I saw in Firebug that the values in the 'Source' were slightly different ie a '/' was represented by the chars '%2F'. Now I get what looks like the source of a page. – Johnny Feb 08 '16 at 13:34
  • So all good now or is it still not working? – whosrdaddy Feb 08 '16 at 16:00
  • No I'm just not getting what I expect in the way of text / html in my response. It's like I am being redirected back to the original page as I can see a username and password label with no values. You mentioned the cookie in your earlier comment and iterating the cookie collection just returns a sessionID, whereas I see much more in FireFox when looking at FireBug. I just don't get why I need so many parameters to logon. I'll keep trying. thank you for your time, though. – Johnny Feb 08 '16 at 16:37

1 Answers1

2

Ok, found your problem. The site is doing a redirect to the same page after the POST login request. The key to the solution is setting HandleRedirects to True and change the VMethod variable to GET in the OnHandleRedirect event. I cleaned up the code a bit:

unit SO35263785Test;

interface

uses
  IdHttp,
  SysUtils,
  StrUtils,
  StdCtrls,
  Classes,
  Controls,
  Forms;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    Client : TIdHttp;
    procedure HandleRedirect(Sender: TObject; var dest: string; var NumRedirect: Integer; var Handled: boolean; var VMethod: TIdHTTPMethod);
    procedure LoginToRedTractor(const Username, Password : String);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.HandleRedirect(Sender: TObject; var dest: string; var NumRedirect: Integer; var Handled: boolean; var VMethod: TIdHTTPMethod);
begin
 VMethod := Id_HTTPMethodGet;
 Handled := True;
end;

procedure ExtractViewStateAndGenerator(const Html : String; var ViewState : String; var ViewStateGenerator: String);

var
  Ps : Integer;

begin
 ViewState :=  '';
 ViewStateGenerator := '';
 // we assume __VIEWSTATE and __VIEWSTATEGENERATOR inputs are there, NO error checking
 Ps := Pos('__VIEWSTATE', Html);
 Ps := PosEx('value', Html, Ps);
 Ps := PosEx('"', Html, Ps);
 ViewState := Copy(Html, Ps+1, PosEx('"', Html, Ps+1)-Ps-1);
 Ps := Pos('__VIEWSTATEGENERATOR', Html);
 Ps := PosEx('value', Html, Ps);
 Ps := PosEx('"', Html, Ps);
 ViewStateGenerator := Copy(Html, Ps+1, PosEx('"', Html, Ps+1)-Ps-1);
end;

procedure TForm1.LoginToRedTractor(const Username, Password : String);

var
  GETResponse        : String;
  Request            : TStringList;
  ViewState          : String;
  ViewStateGenerator : String;

begin
 Client := TIdHttp.Create;
 try
  Client.ProtocolVersion := pv1_1;
  Client.HTTPOptions := [hoForceEncodeParams, hoKeepOrigProtocol];
  Client.AllowCookies := True;
  Client.HandleRedirects := True;
  Client.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.3; WOW64)  AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.103 Safari/537.36';
  Client.OnRedirect := HandleRedirect;
  GETResponse :=  Client.Get('http://assurance.redtractor.org.uk/rtassurance/schemes.eb');
  ExtractViewStateAndGenerator(GETResponse, ViewState, ViewStateGenerator);
  Request := TStringList.Create;
  try
   Request.Add('__VIEWSTATE='+ViewState);
   Request.Add('__VIEWSTATEGENERATOR='+ViewStateGenerator);
   Request.Add('__EVENTTARGET=el9M9AQXIL51JI3$loginPnl');
   Request.Add('el9M9AQXIL51JI3$loginPnl_username='+Username);
   Request.Add('el9M9AQXIL51JI3$loginPnl_password='+Password);
   Client.Request.Referer := Client.URL.URI;
   Memo1.Text :=  Client.Post('http://assurance.redtractor.org.uk/rtassurance/services.eb', Request);
  finally
   Request.Free;
  end;
 finally
  Client.Free;
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 LoginToRedTractor('MyUsername', 'MyPassword');
end;

end

This code has been verified and works in Delphi XE.

whosrdaddy
  • 11,720
  • 4
  • 50
  • 99
  • 1
    If the server is using a `303` redirect, `TIdHTTP` changes the method to `GET` automatically. If the server is using a `302` redirect, `TIdHTTP` changes the method to `GET` if the `hoTreat302Like303` flag is enabled in the `TIdHTTP.HTTPOptions` property. You can use that flag instead of using the `OnRedirect` event. If the server is playing nice, it should be using `303`, but is likely using `302` instead. `302` has ambiguity issues that `303` and `307` were introduced to address, but many servers do not use them because they are not backwards compatible with old clients. – Remy Lebeau Feb 09 '16 at 01:37
  • Thank you, whosrdaddy. There is a lot for me to learn from this. I appreciate the time you spent on this. – Johnny Feb 09 '16 at 07:29
  • Absolutely spot on, whosrdaddy. One day I will get you that beer. Thank you again. I will make sure I learn something from your code sample. – Johnny Feb 09 '16 at 07:37