curl: using multipart with post params - c++

If I do
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_params);
the server will do not see multipart, but if I coment the second line
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
//curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_params);
it'll do.
What's wrong here?
UPD: So, now the question is, whether it's possible to put value to a param, so that that value was in POST parameters array on server-side? I'm trying
headers = curl_slist_append(headers, "Content-Disposition: form-data");
//action=upload
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "action",
CURLFORM_COPYCONTENTS, "upload",
CURLFORM_CONTENTHEADER, headers,
CURLFORM_CONTENTTYPE, "Content-Type: multipart/form-data",
CURLFORM_END);
But that's not working!
Thank you in advance!

I ended up mostly following the example code here: http://curl.haxx.se/libcurl/c/multi-post.html
Here's a sample of the code that ended up working for me based on the curl example link:
...
CURL *curl;
curl_mime *form;
curl = curl_easy_init();
/* Set the headers for the request. */
scoped_curl_slist headers;
headers.list = NULL;
string session_header = "Authorization: Bearer " + token;
headers.list = curl_slist_append(headers.list, session_header.c_str());
headers.list = curl_slist_append(headers.list, "cache-control: no-cache");
headers.list = curl_slist_append(headers.list, "Content-Type: multipart/form-data");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers.list);
curl_easy_setopt(curl, CURLOPT_URL, "your_url.com");
/* Create the form */
form = curl_mime_init(curl);
/* Fill in the file upload field */
curl_mimepart *field = curl_mime_addpart(form);
curl_mime_name(field, "upload");
curl_mime_filedata(field, "C:/file_name.txt");
/* Fill in the filename field */
field = curl_mime_addpart(form);
curl_mime_name(field, "name");
curl_mime_data(field, "file_name.txt", CURL_ZERO_TERMINATED);
curl_easy_setopt(curl, CURLOPT_MIMEPOST, form);
CURLcode result = curl_easy_perform(curl);
...
curl_mime_free(form);
curl_easy_cleanup(curl);
curl_slist_free_all(headers);

Related

curl PUT JSON body having issues processing array's

I'm developing a library that communicates with a REST API. The method I wrote for PUT calls has worked up until this point.
void Command::put(const std::string url, const std::string body)
{
CURLcode ret;
struct curl_slist *slist1;
slist1 = NULL;
// slist1 = curl_slist_append(slist1, "Content-Type: multipart/form-data;");
slist1 = curl_slist_append(slist1, "Content-Type: application/json;");
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)12);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist1);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.79.1");
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
curl_easy_setopt(curl, CURLOPT_SSH_KNOWNHOSTS, "/Users/$USER/.ssh/known_hosts");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_easy_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, 1L);
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
ret = curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl = NULL;
curl_slist_free_all(slist1);
slist1 = NULL;
}
curl_easy_cleanup(curl);
curl = nullptr;
curl_slist_free_all(slist1);
slist1 = NULL;
}
However, it seems to not work with arrays. Below are three debug put calls and their output. The first two work. The third does not.
// *** DEBUG ***
commandWrapper->put(
"http://192.168.1.7/api/x1CAh16Nr8NcKwgkUMVbyZX3YKFQrEaMAj5pFz0Z/lights/6/state", "{\"on\":true}");
commandWrapper->put(
"http://192.168.1.7/api/x1CAh16Nr8NcKwgkUMVbyZX3YKFQrEaMAj5pFz0Z/lights/6/state", "{\"bri\":123}");
commandWrapper->put(
"http://192.168.1.7/api/x1CAh16Nr8NcKwgkUMVbyZX3YKFQrEaMAj5pFz0Z/lights/6/state", "{\"xy\":[0.04,0.98]}");
[{"success":{"/lights/6/state/on":true}}]
[{"success":{"/lights/6/state/bri":123}}]
[{"error":{"type":2,"address":"/lights/6/state","description":"body contains invalid json"}}]2022-04-02T02:56:47.220Z - Living Room 1 - Status: 0
I'm a little lost on how to proceed. I verified that this command does work on the command line.
You need to change this line:
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)12);
To this instead:
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)body.size());
Or, simply omit CURLOPT_POSTFIELDSIZE_LARGE completely, since body.c_str() is a pointer to a null-terminated string in this situation. Curl can determinant the size for you.
The JSON {xy:[0.04,0.98]} is 16 characters, longer than the 12 characters you are claiming, so you are truncating the JSON during transmission, which is why the server thinks it is invalid. Both {"on":true} and {"bri":123} are 11 characters (12 if you count the null terminator), so they are not truncated.

Creating a shared link for a Dropbox file using curl & C++

I want to create a shared link for a dropbox file using curl & C++ on a windows 10 desktop.
I've already manage to upload the file to a dropbox folder using curl & C++.
When I try to create the link with command line it works with
curl -X POST https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings --header "Authorization: Bearer <TOKEN>" --header "Content-Type: application/json" --data "{\"path\":\"path_of_the_file\"}"
but when I use this code to do the same in C++ it hangs at < HTTP/1.1 100 Continue
Here is my code :
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
string readBuffer;
printf("Running curl test get shared link.\n");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE); //no ssl
struct curl_slist *headers = NULL; // init to NULL is important
headers = curl_slist_append(headers, "Authorization: Bearer <TOKEN>");
headers = curl_slist_append(headers, "Content-Type: ");
headers = curl_slist_append(headers, "Dropbox-API-Arg: {\"path\":\"path_of_file\"}");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POST, true);
curl_easy_setopt(curl, CURLOPT_VERBOSE, true);
curl_easy_setopt(curl, CURLOPT_URL, "https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings");
// Perform the request, res will get the return code
res = curl_easy_perform(curl);
// Check for errors
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
// always cleanup
curl_easy_cleanup(curl);
cout << readBuffer << endl;
printf("\nFinished curl test.\n");
}
curl_global_cleanup();
printf("Done get shared link!\n");
I've tried with content-type : application/json and adding fields but I can't reproduce what I'm doing with the command line
There are errors in the code, causing undefined behaviour.
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_easy_setopt(curl, CURLOPT_POST, true);
curl_easy_setopt(curl, CURLOPT_VERBOSE, true);
cUrl has a C API, C does not have overloads. Thus curl_easy_setopt is a multi-arg function, and the third argument type depends on the second argument value. CURLOPT_SSL_VERIFYPEER and CURLOPT_VERBOSE require long values, you pass the values as int. The size of the third argument is important exactly like this is in *printf functions. The proper calls must be
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
The second. 100 Continue means you have not passed the data for the POST request. The server received request and waits for further data. See
libcurl example - http-post.c.
I can't reproduce what I'm doing with the command line
Add the --libcurl to the command line for getting a C code performing the same actions as in the command line.
Thank you S.M. Here is the code that works
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
string readBuffer;
printf("Running curl test get shared link.\n");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); //no ssl
struct curl_slist *headers = NULL; // init to NULL is important
headers = curl_slist_append(headers, "Authorization: Bearer <TOKEN>");
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"path\":\"path_to_the_file"}");
curl_easy_setopt(curl, CURLOPT_URL, "https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings");
// Perform the request, res will get the return code
res = curl_easy_perform(curl);
// Check for errors
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
// always cleanup
curl_easy_cleanup(curl);
cout << readBuffer << endl;
printf("\nFinished curl test.\n");
}
curl_global_cleanup();
printf("Done get shared link!\n");

C++ - sending Curl requests gives the response in the console without printing it [duplicate]

This question already has answers here:
Save cURL content result into a string in C++
(7 answers)
Closed 1 year ago.
Here is my code:
CURL *curl;
CURLcode res;
curl = curl_easy_init();
std::string json_message = "{\r\n \"email\":\"test#abv.bg\",\r\n \"password\":\"asdasdasd\"\r\n}";
if(curl) {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_URL, "https://www.examle.com/myUrl");
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Authorization: Bearer secretkeyHere");
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
const char *data = json_message.c_str();
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
curl_easy_perform(curl);
}
curl_easy_cleanup(curl);
The problem is that when I execute that code the response of the http request is printed directly into my console application. I would like to store the response in a string without printing it into the console without intention.
Do you see why it is printed unintentionally and how can I store the response in a string?
By default, curl writes the received data to stdout. You can change that by using curl_easy_setopt() to specify a custom CURLOPT_WRITEFUNCTION callback, giving it a string* pointer via CURLOPT_WRITEDATA. For example:
static size_t writeToString(void *data, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
std::string *str = static_cast<std::string*>(userp);
str->append(static_cast<char*>(data), realsize);
return realsize;
}
...
CURL *curl = curl_easy_init();
if (curl) {
...
std::string respStr;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeToString);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &respStr);
CURLcode res = curl_easy_perform(curl);
// use respStr as needed...
curl_easy_cleanup(curl);
}

Poloniex API "Invalid command" c++ libcurl

I'm trying to solve it for many hours but nothing going better...
int main() {
CURL *curl_handle = curl_easy_init();
if(curl_handle) {
string post_data="";
struct curl_slist *headers=NULL;
string command = "command=returnBalances&nonce=" + to_string(time(0));
cout<<command<<endl;
string Secret = "mySecretCode";
string Sign = "Sign: "+ hmac::get_hmac(Secret, command, hmac::TypeHash::SHA512);
cout<<Sign<<endl;
post_data += command;
headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
headers = curl_slist_append(headers, "Key: myKey");
headers = curl_slist_append(headers, Sign.c_str());
curl_easy_setopt(curl_handle, CURLOPT_URL, "https://poloniex.com/tradingApi");
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, post_data.length()+1);
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, post_data.c_str());
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
}
curl_easy_perform(curl_handle);
curl_easy_cleanup(curl_handle);
return 0;
}
This returns to me
{"error":"Invalid command."}
I checked hmac function result threw website and they are equal.
Some people said that adding
"Content-Type: application/x-www-form-urlencoded"
can solve this, but not at this time.
Console output:
> POST /tradingApi HTTP/2
Host: poloniex.com
accept: */*
content-type: application/x-www-form-urlencoded
key: myKey
sign: resultOfHmacFunc
content-length: 40
Solution was to change from CURLOPT_POSTFIELDS to CURLOPT_COPYPOSTFIELDS

HTTP post to create InfluxDB database with libcurl

I'm trying to make an http post with libcurl library to create an InfluxDB database, as indicated in their website:
curl -i -XPOST http://localhost:8086/query --data-urlencode "q=CREATE DATABASE mydb"
It looks like my code is not working. It doesnt give me any errors but db is not created. But instead if i try to add some points to an existing database, with the same function, it works. I think i miss the correct way of adding "q=CREATE DATABASE mydb" part. How should i change my code?
int main(int argc, char *argv[]){
char *url = "http://localhost:8086/query";
char *data = "q=CREATE DATABASE mydb";
/* should i change data string to json?
data = "{\"q\":\"CREATE DATABASE mydb\" }" */
bool res = createInfluxDB(url, data);
/*control result*/
return(0);
}
bool createInfluxDB(char *url, char *data) {
CURL *curl;
curl = curl_easy_init();
if(curl) {
CURLcode res;
/* What Content-type should i use?*/
struct curl_slist* headers = curl_slist_append(headers, "Content-Type: application/json");
/*--data-urlencode*/
char *urlencoded = curl_easy_escape(curl, data, int(strlen(data)));
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, urlencoded);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(urlencoded));
res = curl_easy_perform(curl);
/*omitted controls*/
curl_free(urlencoded);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
return(true);
}
After analyzing packets with http post request (which was returning Bad Request) i arrived to the point that i shouldn't add query parameters as data. But instead it should be part of url. So after changing code like that, it works!
int main(int argc, char *argv[]){
char *url = "http://localhost:8086/query?q=CREATE+DATABASE+mydb";
bool res = createInfluxDB(url);
/*control result*/
return(0);
}
bool createInfluxDB(char *url) {
CURL *curl;
curl = curl_easy_init();
if(curl) {
CURLcode res;
struct curl_slist* headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
res = curl_easy_perform(curl);
/*omitted controls*/
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
return(true);
}
EDITED ANSWER:
You still got a missnamed var in a if statment:
if (urlencode) free(...
That should be
if (urlencoded) free(...
Then in your headers you set the application type as json and I don't think that's what you want.
Something like "application/x-www-form-urlencoded" may be better.
struct curl_slist* headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
ORIGNIAL ANSWER:
A workaround could be in your
char *data = curl_easy_escape(curl, json, int(strlen(json)));
That overload data with a json var that doesn't exist ?
Something like this may work better:
data = curl_easy_escape(curl, data, int(strlen(data)));