Getting parameters from IdHTTPServer - Firemonkey - delphi

I’m trying to get Dropbox token from call-back parameters but the parameters are always empty.
Here is my code:
sURL :=
https://www.dropbox.com/oauth2/authorize' +
'?response_type=token' +
'&client_id=' + MyAppID +
'&redirect_uri=http://127.0.0.1:8888/';
ShellExecute(0, 'OPEN', PChar(sURL), '', '', SW_SHOWNORMAL);
Next I have IdHTTPServer set to listen http://127.0.0.1:8888/ address.
On executing the browser with Dropbox log-in popup. When I made log-in the redirection is made and the “IdHTTPServerCommandGet” events is called. So far so good.
In the browser I can see the next URL:
http://127.0.0.1:8888/#access_token=123&token_type=bearer&uid=1234&account_id=dbid%1234
But in “IdHTTPServerCommandGet” events the parameters are always empty:
ARequestInfo.UnparsedParams; // Empty
ARequestInfo. QueryParams; // Empty
if ARequestInfo.Params.Count > 0 then // Empty
sToken := ARequestInfo.Params[0];
And here is my question. How can I get the whole URL with parameters from IdHTTPServer?

Look very closely at the redirect url you are seeing in your browser. All of the parameters you want to access are after a # character, not a ? character. That puts them in the "fragment" portion of the url rather than the "query" portion. A web browser does not include a url's "fragment" in an HTTP request to a web server. That is why the TIdHTTPRequestInfo properties are empty - the parameters are literally not being sent to your TIdHTTPServer. The redirected url needs to put the parameters in the url's "query" instead. That is Dropbox's responsibility to handle correctly on its end.
That being said, you don't actually need the TIdHTTPServer at all, if you use an embedded web browser directly in your app, such as Delphi's TWebBrowser. You can hook into the embedded browser to catch the redirect directly, and all of the data that is in the redirected url. This also allows you to use a custom url scheme for the redirect url, you don't have to use "http(s):" (see Redirect URLs for Native Apps).

Related

TIdHTTP Post to API and save response as a .pdf file

I'm calling an API URL with some parameters according to the documentation provided.
The way the API is set up, the response should be an auto-download of a .pdf file.
The parameters are:
number - which stands for a 13 digit number that represents the unique file indicator in their system (example: 5277110610029)
username and user_pass respectively - which stand for the login credentials to access the system
client_id - which stands for a unique client ID associated with my account in their system
language - where I indicate the language the file contents should be in (English or other of the available languages)
Documentation indicates it should be made as a Post request.
I have the following code:
var
FileName : string;
URL: String;
Params: TStringList;
memStream: TMemoryStream;
...
begin
FileName:= MainModule.PDFfileName + '.pdf';
begin
URL := 'https://someURL/view_integrated_pdf.php?';
Params := TStringList.Create;
memStream := TMemoryStream.Create;
try
Params.Add('number='+MainModule.number+'');
Params.Add('username='+MainModule.User+'');
Params.Add('client_id='+MainModule.clientID+'');
Params.Add('user_pass='+MainModule.Pass+'');
Params.Add('language=en');
MainModule.IdHTTP.Post(URL, Params, memStream);
finally
memStream.SaveToFile(ServerModule.FilesFolderPath+'\pdfs\'+FileName);
Params.Free;
memStream.Free;
end;
end;
pdfForm.ShowModal();
end;
If I try the resulting URL and parameters in the browser - it auto-downloads the pdf file the API gives me with the name numberParameter.pdf
If I do it in Delphi using the provided code, 8 out of 10 times it saves a pdf file with a 1 KB Size (normally it is between 32 and 100 for the successful file) and when I try to open it in the program using my pdfForm and subsequent viewer, the viewer throws an error "Invalid PDF structure"
What am I doing wrong? / How do you properly save a Post requests that returns a .pdf file to download from the API?
UPDATE
As per the comments, opening up the 1kb resulting PDF in Notepad++ displays the contents as simply Error username.
This is puzzling since I checked and the username being passed is accurate + if I paste the exact same URL with parameter values filed, in the browser, it works perfectly and gives me the correct PDF.
Is my code not the correct approach to Post and save the file being sent?
If I try the resulting URL and parameters in the browser - it auto-downloads the pdf file the API gives me with the name numberParameter.pdf
The only way to do that in a browser is to put the parameters in the URL's query component, eg:
https://someURL/view_integrated_pdf.php?number=...&username=...&client_id=...&user_pass=...&language=en
... and not in a POST body, as your code is doing. Also, a browser would send a GET request, not a POST request. Unless you are filling in an HTML webform (ie <form action="<URL>" method="POST" ...>) and submitting it. Is that what the API documentation says to do?
Since you did not provide any details from the documentation about what this server is actually expecting, we can't really tell you whether your code is correct or wrong. But it does seem that your manual tests are contradicting what you claim the documentation says the server is expecting.
If I do it in Delphi using the provided code, 8 out of 10 times it saves a pdf file with a 1 KB Size (normally it is between 32 and 100 for the successful file) and when I try to open it in the program using my pdfForm and subsequent viewer, the viewer throws an error "Invalid PDF structure"
From comments, you say your file is receiving a textual error message. TIdHTTP would save such text to your TMemoryStream ONLY IF either:
the HTTP server is NOT reporting an error at the HTTP level, ie it is sending the text message using an HTTP 2xx success response. I suspect this is what is happening in your case. By default, if the server uses a proper HTTP error code, TIdHTTP will raise an EIdHTTPProtocolException containing the text message and NOT save the text to your TMemoryStream at all.
the HTTP server IS reporting an error at the HTTP level, but you are using the hoNoProtocolErrorException and hoWantProtocolErrorContent flags together in the TIdHTTP.HTTPOptions property. In which case, TIdHTTP would not raise EIdHTTPProtocolException and would instead save whatever data the server sends as-is to your TMemoryStream.
Since there is clearly no HTTP exception being raised, you will have to validate the server's response before you can use the downloaded data in your pdfForm, ie by looking at the TIdHTTP.Response.ContentType and/or TIdHTTP.Response.ContentDisposition property to know whether the server actually sent a PDF file or not.
This is puzzling since I checked and the username being passed is accurate + if I paste the exact same URL with parameter values filed, in the browser, it works perfectly and gives me the correct PDF.
Well, for one thing, you have a typo in your code: the numberr field needs to be number instead.
Beyond that, putting the URL in a browser is NOT the same operation that your code is doing, so try changing your code to mimic what you are doing manually, eg:
uses
..., IdGlobalProtocols, IdHTTP, IdURI;
...
var
URL : string;
memStream: TMemoryStream;
begin
// All parameters into the URI for a HTTP GET request
URL := 'https://someURL/view_integrated_pdf.php'
+ '?number=' + TIdURI.ParamsEncode(MainModule.number)
+ '&username=' + TIdURI.ParamsEncode(MainModule.User)
+ '&client_id=' + TIdURI.ParamsEncode(MainModule.clientID)
+ '&user_pass=' + TIdURI.ParamsEncode(MainModule.Pass)
+ '&language=en';
memStream := TMemoryStream.Create;
try
MainModule.IdHTTP.Get(URL, memStream);
// Is it really PDF? Other formats such as plaintext is not wanted.
if not IsHeaderMediaType(MainModule.IdHTTP.ContentType, 'application/pdf') then Exit;
memStream.SaveToFile(ServerModule.FilesFolderPath + '\pdfs\' + MainModule.PDFfileName + '.pdf');
finally
memStream.Free;
end;
pdfForm.ShowModal;
end;
If that does not work, then please update your question to provide the actual documentation.

IdHttp: redirect to error page when trying to download a file, __viewstate changes each time

i have already successfully logged in and posted data and downloaded data from 2 sites using idHttp.post(), but i am having trouble with the third one
in this new site, the login does work, but when i try to download a file (which uses __doPostBack for the download link) i get redirected to an error page
i have double and triple checked all post datas and they are exactly like what http analyzer showed me the only difference i noticed is that in my other attempts and sites that i accessed successfully,__viewstate is the same each time, and it never changes but in this third site it changes with each login (i mean when i go to the site manually and i check http analyzer results i can see that __viewstate parameter value differs each time)
what should i do ? is the problem with that changing __viewstate paramter ? if so how can i fix it ?
the code i use for posting :
try
Response := TMemoryStream.Create;
try
Request := TStringList.Create;
try
Request.Assign(TATDFileUtility.convertPairValueToRequestList(TATDFileUtility.extractPairValue('the site login parameters.txt', 3)));
IdHTTP := TIdHTTP.Create;
try
IdHTTP.AllowCookies := True;
IdHTTP.HandleRedirects := True;
IdHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
IdHTTP.Post('thesite, the address for the login and main page and download page is the same', Request, Response);
Response.SaveToFile('responseCode0.txt');
Request.Clear;
Response.Clear;
Request.Assign(TATDFileUtility.convertPairValueToRequestList(TATDFileUtility.extractPairValue('httpDownloadParamters.txt', 3)));
IdHTTP.Post('thesite, the address for the login and main page and download page is the same', Request, Response);
Response.SaveToFile('responseCode1.txt');
as you can see after checking repsonsecode0, i can see that i am logged in, but the second response code shows me an error and tracking it shows that i am getting redirected to an error page.
ViewState is dynamic. You need to first GET the HTML page that defines the <form> element that normally submits the PostBack in a browser. This allows the webserver to generate the current ViewState. Then parse the HTML to extract the names and values of the <input> elements within the <form>, including the ViewState, and then you can POST those values to the URL specified in the <form>'s action attribute. This is what a web browser normally does, and what you need to simulate with TIdHTTP.

How to get the correct proxy URL in Apigee?

In my Apigee API Proxy, I need to get the environment URL that is defined in my configuration, so that I can send it as part of the response.
For example: http://myorg-test.apigee.net/pricing
However, when I try to get it using proxy.url, I get an aliased path, like http://rrt18apigee.us-ea.4.apigee.com/pricing
Currently, I'm trying to get it like:
var response = {
proxyUrl : context.getVariable("proxy.url"),
};
Here is a work around. You can try to get the following variables and create the entire URL
Get the request scheme (http or https) from request.Headers.X-Forwarded-Proto (if you are using cloud version) or client.scheme if you are using on-prem
Get the host name from request.host
Get the entire request path from request.path
Entire list of URL query params and list from message.querystring
You can then construct the entire request URL.
( I know this should not be this painful. Please log a bug in case proxy.url is really broken. )

Google docs API: can't download a file, downloading documents works

I'm trying out http requests to download a pdf file from google docs using google document list API and OAuth 1.0. I'm not using any external api for oauth or google docs.
Following the documentation, I obtained download URL for the pdf which works fine when placed in a browser.
According to documentation I should send a request that looks like this:
GET https://doc-04-20-docs.googleusercontent.com/docs/secure/m7an0emtau/WJm12345/YzI2Y2ExYWVm?h=16655626&e=download&gd=true
However, the download URL has something funny going on with the paremeters, it looks like this:
https://doc-00-00-docs.googleusercontent.com/docs/securesc/5ud8e...tMzQ?h=15287211447292764666&amp\;e=download&amp\;gd=true
(in the url '&amp\;' is actually without '\' but I put it here in the post to avoid escaping it as '&').
So what is the case here; do I have 3 parameters h,e,gd or do I have one parameter h with value 15287211447292764666&ae=download&gd=true, or maybe I have the following 3 param-value pairs: h = 15287211447292764666, amp;e = download, amp;gd = true (which I think is the case and it seems like a bug)?
In order to form a proper http request I need to know exectly what are the parameters names and values, however the download URL I have is confusing. Moreover, if the params names are h,amp;e and amp;gd, is the request containing those params valid for obtaining file content (if not it seems like a bug).
I didn't have problems downloading and uploading documents (msword docs) and my scope for downloading a file is correct.
I experimented with different requests a lot. When I treat the 3 parameters (h,e,gd) separetaly I get Unauthorized 401. If I assume that I have only one parameter - h with value 15287211447292764666&ae=download&gd=true I get 500 Internal Server Error (google api states: 'An unexpected error has occurred in the API.','If the problem persists, please post in the forum.').
If I don't put any paremeters at all or I put 3 parameters -h,amp;e,amp;gd, I get 302 Found. I tried following the redirections sending more requests but I still couldn't get the actual pdf content. I also experimented in OAuth Playground and it seems it's not working as it's supposed to neither. Sending get request in OAuth with the download URL responds with 302 Found instead of responding with the PDF content.
What is going on here? How can I obtain the pdf content in a response? Please help.
I have experimented same issue with oAuth2 (error 401).
Solved by inserting the oAuth2 token in request header and not in URL.
I have replaced &access_token=<token> in the URL by setRequestHeader("Authorization", "Bearer <token>" )

Retrieving the url anchor in a werkzeug request

I have a DAV protocol that stores out-of-band data in the url anchor, e.g. the ghi in DELETE /abc.def#ghi. The server is a Flask application.
I can see the request come in on the wire via tcpdump, but when I look at the werkzeug Request object (such as url() or base_url()), all I get back is /abc.def. The #ghi has been stripped out.
Is there a method that returns this information, or do I have to subclass Request to handle this myself? If so, is there an example I can use as an inspiration?
I ran into the same problem. Facebook authentication API returns the access token behind a hash appended into the redirection url. In the same way, Flask's request.url drops everything in the URL behind the hash sign.
I'm also using Flask so I think you can use my brute-force workaround using Javascript's window.location.href to get the full URL. Then, I just extracted the piece that I needed (the access token), put it into a redirection URL where I can pass the access token as an argument to the receiving view function. Here's the code:
#app.route('/app_response/<response>', methods=['GET'])
def app_response_code(response):
return ''' <script type="text/javascript">
var token = window.location.href.split("access_token=")[1];
window.location = "/app_response_token/" + token;
</script> '''
#app.route('/app_response_token/<token>/', methods=['GET'])
def app_response_token(token):
return token
In case you manage(d) to do this within Werkzeug, I'm interested to know how.
From Wikipedia (Fragment Identifier) (don't have the time to find it in the RFC):
The fragment identifier functions differently than the rest of the URI: namely, its processing is exclusively client-side with no participation from the server
So Flask - or any other framework - doesn't have access to #ghi.
You can do this using flask.url_for with the _anchor keyword argument:
url_for('abc.def', _anchor='ghi')

Resources