First, sorry for my bad english!
I need to perform a query on the website www.nfe.fazenda.org.br. For best performance'm using the component TIdHTTP with TIdCookieManager.
This site uses of captcha for control access. So, i'm trying get the page and the captcha for obtaining the cookies.
The user enter the captcha code and key for NFe. So, i send to page with post.
But, I'm being redirected to an error page when I run the post.
Here my test code and ask you to help me. Thank you!
unit Forms.MainForm;
interface
uses
Winapi.Windows, Winapi.Messages,
System.SysUtils, System.Variants, System.Classes,
Vcl.Forms, Vcl.Graphics, Vcl.Dialogs, Vcl.Controls, Vcl.ExtCtrls,
Vcl.StdCtrls,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP,
IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL,
IdCookieManager, IdCookie, IdURI,
GIFImg, WinInet;
type
TMainForm = class(TForm)
mem: TMemo;
IdHttp: TIdHTTP;
IdSSLHandlerSocket: TIdSSLIOHandlerSocketOpenSSL;
IdCookieManager: TIdCookieManager;
panBottom: TPanel;
btnGo: TButton;
imgCaptcha: TImage;
edtKey: TEdit;
edtCode: TEdit;
lblInit: TLabel;
procedure FormShow(Sender: TObject);
procedure lblInitClick(Sender: TObject);
procedure btnGoClick(Sender: TObject);
private
Cookies: TIdCookies;
viewState, eventValidate: string;
procedure GetHiddenFieldValues(html: string);
procedure p_Execute;
end;
var
MainForm: TMainForm;
const
HOST = 'http://www.nfe.fazenda.gov.br';
URLIMG = 'http://www.nfe.fazenda.gov.br/scripts/srf/intercepta/captcha.aspx?opt=image';
URLGET = 'http://www.nfe.fazenda.gov.br/portal/consulta.aspx?tipoConsulta=completa&tipoConteudo=XbSeqxE8pl8=';
URLPOST = 'http://www.nfe.fazenda.gov.br/portal/consultaCompleta.aspx?tipoConteudo=XbSeqxE8pl8=';
CONTENT_TYPE = 'application/x-www-form-urlencoded';
implementation
{$R *.dfm}
procedure TMainForm.FormShow(Sender: TObject);
begin
lblInitClick(Sender);
end;
procedure TMainForm.lblInitClick(Sender: TObject);
var
response: TMemoryStream;
gif: TGIFImage;
html: string;
begin
response := TMemoryStream.Create;
gif := TGIFImage.Create;
try
html := IdHttp.Get(URLGET);
mem.Text := html;
GetHiddenFieldValues(html);
IdHttp.Get(URLIMG, response);
response.Position := 0;
gif.LoadFromStream(response);
imgCaptcha.Picture.Assign(gif);
Cookies := IdCookieManager.CookieCollection;
finally
gif.Free;
response.Free;
end;
end;
procedure TMainForm.btnGoClick(Sender: TObject);
begin
p_Execute;
end;
procedure TMainForm.GetHiddenFieldValues(html: string);
var
nIni, nLen: integer;
cVal: string;
const
TAG_VIEWSTATE = '<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="';
TAG_EVENTVALIDATION = '<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="';
begin
nIni := Pos(TAG_VIEWSTATE, html);
nLen := Length(TAG_VIEWSTATE);
cVal := Copy(html,nIni+nLen, Length(html));
cVal := Copy(cVal, 1, Pos('" />', cVal)-1);
viewState := cVal;
nIni := Pos(TAG_EVENTVALIDATION, html);
nLen := Length(TAG_EVENTVALIDATION);
cVal := Copy(html,nIni+nLen, Length(html));
cVal := Copy(cVal, 1, Pos('" />', cVal)-1);
eventValidate := cVal;
end;
procedure TMainForm.p_Execute;
var
params: TStringList;
Uri: TIdURI;
nI: Integer;
begin
params := TStringList.Create;
Uri := TIdURI.Create(Cookies[0].Domain);
try
for nI := 0 to Pred(Cookies.Count) do
begin
IdCookieManager.AddServerCookie(Cookies[nI].ClientCookie, Uri);
if nI = 0 then
IdHttp.Request.CustomHeaders.Values['Cookie'] := Cookies[nI].ClientCookie
else
IdHttp.Request.CustomHeaders.Values['Cookie'] := IdHttp.Request.CustomHeaders.Values['Cookie'] + '; ' + Cookies[nI].ClientCookie;
end;
params.Add('__VIEWSTATE=' + viewState);
params.Add('__EVENTVALIDATION=' + eventValidate);
params.Add('__EVENTTARGET=');
params.Add('__EVENTARGUMENT=');
params.Add('ctl00$txtPalavraChave=');
params.Add('ctl00$ContentPlaceHolder1$txtChaveAcessoCompleta=' + edtKey.Text);
params.Add('ctl00$ContentPlaceHolder1$txtCaptcha=' + edtCode.Text);
params.Add('ctl00$ContentPlaceHolder1$btnConsultar=Continuar');
params.Add('hiddenInputToUpdateATBuffer_CommonToolkitScripts=1');
IdHttp.Request.ContentType := CONTENT_TYPE;
mem.Text := IdHttp.Post(URLPOST, params);
finally
params.Free;
Uri.Free;
end;
end;
end.
You are mismanaging the server's cookies.
TIdURI.Create()expects a full URL, andAddServerCookie()requires a full URL so it can process Paths, differentiate between HTTP and HTTPS cookies, etc. But you are passingTIdURIonly a domain name by itself, which is not enough.Why are you re-adding cookies back into the
TIdCookieManagerwhen they already exist in theTIdCookieManager? And why are you setting theCustomHeaders.Values['Cookie']property manually? Do not do those things. All you need to do is assign theTIdCookieManagerto theTIdHTTP.CookieManagerproperty and make sure that theTIdHTTP.AllowCookiesproperty is set to True. That's it.TIdHTTPandTIdCookieManagerwill then do all of the hard work of receiving, managing, and sending cookies for you. As long as you use the sameTIdCookieManagerobject for multiple HTTP requests (even if you don't use the sameTIdHTTPobject), then cookies will automatically persist from one request to the next as needed.If you re-use the same
TIdHTTPobject then you don't have to worry about creating aTIdCookieManagerat all, since TIdHTTP will create one internally if needed and it will be used for the lifetime of theTIdHTTPobject.Try this instead:
Now, with that said, there are other problems:
1) You are posting your
paramstohttp://www.nfe.fazenda.gov.br/portal/consultaCompleta.aspx?tipoConteudo=XbSeqxE8pl8=, but when I go to the login URL with a web browser and look at the HTML, I see that it actually wants the form posted tohttp://www.nfe.fazenda.gov.br/consulta.aspx?tipoConsulta=completa&tipoConteudo=XbSeqxE8pl8=instead. You are missing thetipoConsulta=completaportion.2) You are parsing out the actual
__VIEWSTATEand__EVENTVALIDATIONvalues from the HTML, but are sending blank__EVENTTARGETand__EVENTARGUMENTvalues. Those values are blank in the HTML, but they are actually filled in dynamically via client-side scripts.3) there are other
<input>fields in the HTML that you are not posting.A web browser posts every
<input>field that has a non-emptyvalueassigned to it, whether that value is assigned statically or dynamically. You need to do the same in your application. The HTTP server is likely to expect all of those values to be sent. Use a packet sniffer, such as Wireshark or Fiddler, to see what a web browser actually posts, and then replicate that same behavior in your code.