Force download on Mongoose Server - c++

I'm developing a RESTful API using Mongoose Web Server. I'm sending a file using
mg_send_file(conn, path, NULL);
but if the file is plain text, or a PDF, it just displays in the browser, instead of forcing the download, which is what I need.
How can I achieve that?
Thanks
--- Update:
I also tried to use
const char* extraHeaders = "Content-Disposition: attachment;
filename=somefilename.txt";
mg_send_file(conn, "somefilename.txt", extraHeaders);
return MG_MORE;
but the connection keeps running, nothing happens.

Final solution was:
const char* extraHeaders = "Content-Disposition: attachment;
filename=\"somefilename.txt\"\r\n";
mg_send_file(conn, "somefilename.txt", extraHeaders);
return MG_MORE;
Note the filename between "", and the \r\n at the end of any extra header.

Related

how to authenticate in HTTPS by NetHTTPClient

Hello
I must stress that I dont want use curl, and I must use only Embarcadero compiler. (C++Builder and Delphi)
I want send a request to a server which need authentication.
The complete command by API documentation is:
curl -X POST "https://api.demo.website.com/api/2/something" -H "accept: application/json" -H "Content-Type: application/x-www-form-urlencoded" -d "symbol=BTC&side=buy&type=limit&timeInForce=GTC&quantity=0.1&price=4000"
Their Authentication style they provide is:
curl -u "publicKey:secretKey" https://api.demo.website.com/api/2/something
Their suggested code is: (which is not C++) :-))
import requests
session = requests.session()
session.auth = ("publicKey", "secretKey")
const fetch = require('node-fetch');
const credentials = Buffer.from('publicKey' + ':' + 'secretKey').toString('base64');
fetch('https://api.demo.website.com/api/2/something', {
method: 'GET',
headers: {
'Authorization': 'Basic ' + credentials
}
});
My Code is:
TCredentialsStorage::TCredential *MyCredential = new TCredentialsStorage::TCredential(
TAuthTargetType::Server, "", "",
UserNameEdit->Text, PasswordEdit->Text);
NetHTTPClient1->CredentialsStorage->AddCredential(*MyCredential);
StatMemo->Lines->Add(IntToStr(NetHTTPClient1->CredentialsStorage->Credentials.RefCount));
TMemoryStream *Response=new TMemoryStream;
TMemoryStream *bbkTMS =new TMemoryStream;
TNameValueArray nva;
NetHTTPRequest1->Post(URLEdit->Text, bbkTMS, Response, nva);
StatMemo->Lines->LoadFromStream(bbkTMS);
Memo1->Lines->LoadFromStream(Response);
The code is compiling but ot working... :-|
It said:
{"error":{"code":1004,"message":"Unsupported authorization method"}}
Any suggestion for me?
I find it out :-)
As I am using RAD Tool (Embarcadero) So I can use its VCL/FMX component:
THTTPBasicAuthenticator
Done!
But instead NetHTTP component group I start using RESTClient component group, much more better.

How do I correctly setup the multipart formdata with libcurl (in C++) to upload a binary file

To provide a little background. I am not very experienced with Ethernet communications so I apologize in advance for that. I'm working on a project where I need to figure out how to upload a binary file.
I'm trying to upload a large binary file (~34MB) to an embedded device. I have a python code snippet that works but I'm trying to implement the same capability in a different application using C++. Using WireShark, I've captured the header from the python program that works as well as the header that I end up with in my C++ code which doesn't work as needed.
Success Uploading with Python
Here is the python code that works:
response = session.post('http://10.42.42.1:81/__FileUpload',
files={"upfile": open(filename, 'rb')},
stream=False)
Here is the header information extracted from the message which initiates the successful file upload:
POST /__FileUpload HTTP/1.1
Host: 10.42.42.1:81
Content-Length: 34112690
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: python-requests/2.7.0 CPython/2.7.13 Windows/10
Connection: keep-alive
Content-Type: multipart/form-data; boundary=6a659e345a35419e99b66546c1bd9d4e
--6a659e345a35419e99b66546c1bd9d4e
Content-Disposition: form-data; name="upfile"; filename="TestFile.bin"
No Success with C++ Code
Here is the essence of the code that I'm using to upload the file in C++:
curl_mime *multipart;
curl_mimepart *part;
// Specify the target URL
std::string str(comms.BaseURL() + kFileUploadEndpoint);
curl_easy_setopt(pCurl, CURLOPT_URL, str.c_str());
multipart = curl_mime_init(pCurl);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "upfile");
curl_mime_data(part, ("filename=\"" + FileName + "\"").c_str(), CURL_ZERO_TERMINATED);
part = curl_mime_addpart(multipart);
curl_mime_data_cb(part, fileSize, ReadCallback, SeekCallback, NULL, pFile);
curl_easy_setopt(pCurl, CURLOPT_MIMEPOST, multipart);
curl_easy_setopt(pCurl, CURLOPT_TIMEOUT, 90L);
res = curl_easy_perform(pCurl);
...
Here is the header information from running the C++ code:
--------------------------4977715f070a13da
Content-Disposition: form-data; name="upfile"
filename="TestFile.bin"
--------------------------4977715f070a13da
Content-Disposition: form-data
I realized that the header above does not contain the URL endpoint and such and noticed that apparently the message was split into two pieces. Here is the header content from the message sent before the message above.
POST /__FileUpload HTTP/1.1
Host: 10.42.42.1:81
Accept: */*
Content-Length: 31546130
Content-Type: multipart/form-data; boundary=------------------------4977715f070a13da
Expect: 100-continue
I can check the status from the embedded device during the upload and the one thing that I notice in particular is that when the upload is successful with python then the embedded device reports the filename being uploaded in the status content reply. However, when I run the C++ code the filename is blank when I check the status. Therefore the embedded device is obviously not able to extract the filename from the C++ message.
What the Embedded Device is Looking For
While I don't have access to the source code of the device I did get the following information from someone who does. He indicated that this is what the embedded device is looking for. It didn't help me to figure out how to get things working but it might help someone else more knowledgeable in this area.
<FORM METHOD=POST name="install" enctype="multipart/form-data" target="HiddenFrame" action="/__FileUpload" onsubmit="InstallAction(); return true;">
File to upload: <INPUT TYPE=FILE NAME="upfile" size=50><p>
<INPUT TYPE=SUBMIT VALUE="Submit" >
</FORM>
I would prefer using the libcurl 'curl_mime_...' methods to setup the file upload only because that approach is recommended over using the older HTTP post methods. However, I'm perfectly okay with using the older HTTP post methods if that is easier to do. I just want to get it working.
Thanks in advance for your time.
I ended up finding the solution.
It is posted here for anyone who could use it:
curl_mime *multipart;
curl_mimepart *part;
multipart = curl_mime_init(pCurl);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "upfile");
curl_mime_filename(part, FileName.c_str());
curl_mime_data_cb(part, lSize, ReadCallback, SeekCallback, NULL, pFile);
part = curl_mime_addpart(multipart);
curl_easy_setopt(pCurl, CURLOPT_MIMEPOST, multipart);
// Now send the message
res = curl_easy_perform(pCurl);
// Free the post data
curl_mime_free(multipart);
...
curl_mime_data(part, ("filename=\"" + FileName + "\"").c_str(), CURL_ZERO_TERMINATED);
I presume this wants to set the mime part's file name, and then you should rather use curl_mime_filename, because the file name is not data. Each part has a name and data, but also meta-data such as file name. Setting the file name only of course then requires that you set the data separately.
If you rather want to set the data as well as the file name, then instead do it with curl_mime_filedata.
Also, take a look at the official libcurl example postit2.c.

Which `format` would be negotiated for REST request?

There are three variants of format selection:
curl -uadmin:admin "http://localhost:8080/alfresco/service/hellouser.json"
curl -uadmin:admin "http://localhost:8080/alfresco/service/hellouser?format=json"
curl -uadmin:admin -H "Accept: text/html" "http://localhost:8080/alfresco/service/hellouser"
But this is unclear from the DOC what format would be selected for next query:
curl -uadmin:admin -H "Accept: text/html" "http://localhost:8080/alfresco/service/hellouser.xml?format=json"
I expect json here.
May someone provide links to relevant specifications or documentation which describes priority how {format} negotiated? like this is described for Rails:
Rails picks up the expected format from the query parameter format, or if not there from the URL path suffix, or it not there from the Accept header
UPD
The controller can handle all supplied formats: json, xml, html
UPD
Another corner case:
curl -uadmin:admin "http://localhost:8080/alfresco/service/hellouser.pl?format=json"
curl -uadmin:admin "http://localhost:8080/alfresco/service/hellouser.pl?format=xml"
I'd believe you wouldn't have a 200 response, only an error with content negotiation.
The code shows that:
?format=json(format_query_param) will be discarded by the .xml (format_suffix)
filter available renderers leaving only the XMLRenderer left
then it will loop on the accept header but none will match text/html
finally this will be down to the exception

Send POST multipart/form-data request using restbed C++

I am working on a C++ rest client using restbed lib that will send a base64 encoded image using a POST request.
The code I wrote so far is :
auto request = make_shared< Request >(Uri("http://127.0.0.1:8080/ProcessImage"));
request->set_header("Accept", "*/*");
request->set_header("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
request->set_header("Cache-Control", "no-cache");
request->set_method("POST");
string test = "------WebKitFormBoundary7MA4YWxkTrZu0gW"
"Content-Disposition:form-data;name=\"image\""
""
"testMessage"
"------WebKitFormBoundary7MA4YWxkTrZu0gW--";
request->set_body(imgContent);
auto response = Http::sync(request)
I am not sure how I should set the request body. I tried with simple image="blabla" and also with this long version message I took from postman.
But in every case I received a "error 400 Bad request" answer.
Update:
Tested also with this version of code but with no success:
auto request = make_shared< Request >(Uri("http://127.0.0.1:8080/ProcessImage"));
request->set_header("Accept", "*/*");
request->set_header("Host","127.0.0.1:8080");
request->set_method("POST");
request->set_header("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
request->set_header("Cache-Control", "no-cache");
string imgContent = "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\n"
"Content-Disposition: form-data; name=\"image\"\r\n"
"\r\n"
"test\r\n"
"------WebKitFormBoundary7MA4YWxkTrZu0gW--\r\n";
request->set_body(imgContent
auto response = Http::sync(request);
The response I get from the server:
*** Response ***
Status Code: 400
Status Message: BAD REQUEST
HTTP Version: 1.0
HTTP Protocol: HTTP
Header 'Content-Length' > '192'
Header 'Content-Type' > 'text/html'
Header 'Date' > 'Sun, 04 Feb 2018 21:09:45 GMT'
Header 'Server' > 'Werkzeug/0.14.1 Python/3.5.4'
Body:<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>400 Bad Request</title>
<h1>Bad Request</h1>
<p>The browser (or proxy) sent a request that this server could not understand.</p>
²²²²∩x...
Also on the server side (which is using python flask) I added:
encoded_img = request.form.get('image') and printed the string. The print result was: "None"
Your body content is missing explicit line break characters at the end of each line. C++ does not insert them automatically for you.
Also, if you are going to send base64 data, you should include a Content-Transfer-Encoding header, too.
Try this:
string imgContent = "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\n"
"Content-Disposition: form-data; name=\"image\"\r\n"
"Content-Transfer-Encoding: base64\r\n"
"\r\n"
"<base64 image data here>\r\n"
"------WebKitFormBoundary7MA4YWxkTrZu0gW--\r\n";
request->set_body(imgContent);

Qt: Cisco ip phone serivices Authentication URL response

I wrote a program with Qt to work with cisco ip phone services. I'm using QNetworkAccessManager to post XML objects to phones and QTcpServer's socket with QTextStream to respond to authentication requests (simply writing http headers with "AUTHORIZED" to text stream).
QString cTime = currTime.currentDateTimeUtc().toString("ddd, dd MMM yyyy hh:mm:ss");
QTextStream os(socket); os << "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Date: " + cTime + " GMT\r\n"
"Connection: close\r\n"
"\r\n"
"AUTHORIZED";
The problem is the phones don't accept that response and return <CiscoIPPhoneError Number="4" />.
I used node.js for that before and simply wrote "AUTHORIZED" to http.serverResponse object, but I'm confused now why it doesn't work with Qt
Solved that.
The problem was the "Secure Authentication URL" field was set along with "Authentication url". And what I thought to be GET from phone was "Client hello"...
Cleared "Secure Authentication URL" in CUCM and it works now