Curl post null object - c++

I used example code from curl's help, but server receives empty objects and null custom header. GET method works fine. strbuf.GetString() - it's json object which converted to String.
CURL *curl;
CURLcode res;
struct WriteThis pooh;
res = curl_global_init(CURL_GLOBAL_DEFAULT);
struct curl_slist *headers=NULL;
curl_slist_append(headers, "Accept: application/json");
curl_slist_append( headers, "Content-Type: application/json");
curl_slist_append( headers, "charset: utf-8");
curl_slist_append( headers, "AuthToken: 9BA1B05482567E64037573FFB68672DCB52E4993");
curl = curl_easy_init();
if (curl)
{
pooh.readptr = strbuf.GetString();
pooh.sizeleft = (long)strlen(strbuf.GetString());
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
curl_easy_setopt(curl, CURLOPT_READDATA, &pooh);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,writer);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, &pooh);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, pooh.sizeleft);
res = curl_easy_perform(curl);
if (CURLE_OK == res)
{
char *ct;
res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct);
if((CURLE_OK == res) && ct){
}
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
struct WriteThis {
const char *readptr;
long sizeleft;
};
static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
struct WriteThis *pooh = (struct WriteThis *)userp;
if(size*nmemb < 1)
return 0;
if(pooh->sizeleft) {
*(char *)ptr = pooh->readptr[0]; /* copy one single byte */
pooh->readptr++; /* advance pointer */
pooh->sizeleft--; /* less data left */
return 1; /* we return 1 byte at a time! */
}
return 0; /* no more data left to deliver */
}

You set both a read callback and CURLOPT_POSTFIELDS. That's not useful. You need to decide which way you want to provide the data. In this case, I believe it will use the POSTFIELDS and just ignore the read callback.
The pointer you pass to CURLOPT_POSTFIELDS is not a string so libcurl will send funny binary "garbage" from there. You probably want to pass in pooh.readptr there.

Instead:
struct curl_slist *headers = NULL;
curl_slist_append(headers, "Accept: application/json");
curl_slist_append(headers, "Content-Type: application/json");
curl_slist_append(headers, "charset: utf-8");
curl_slist_append(headers, "AuthToken: 9BA1B05482567E64037573FFB68672DCB52E4993");
curl = curl_easy_init();
This:
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, "charset: utf-8");
headers = curl_slist_append(headers, "AuthToken: 9BA1B05482567E64037573FFB68672DCB52E4993");
curl = curl_easy_init();

Related

Libcurl post request not working on Windows 7

I am trying to make a simple POST request to my https REST API and receive the response on a Windows 7 operation system. The firewall is deactivated and the certificate is inside the .exe directory.
Here is the code that I am using:
std::string postRequest(std::string json, std::string endpoint,std::string code,std::string patchOrPOST,std::string source)
{
CURL* curl;
CURLcode res;
std::string response;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
std::string authorization = "Authorization: Basic " + code, portApiHttp = "Referer: " + portAPI + "/index.html", origin = "Origin: " + portAPI;//X-Requested-With: XMLHttpRequest
struct curl_slist* headers = NULL;
if (curl) {
headers = curl_slist_append(headers, "Connection: keep-alive");
headers = curl_slist_append(headers, "Content-Length: "+(int)strlen(json.c_str()));
headers = curl_slist_append(headers, authorization.c_str());
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Content-Type: application/json");
if (source.size() != 0)
{
headers = curl_slist_append(headers, source.c_str());
}
headers = curl_slist_append(headers, R"(sec-ch-ua: "Chromium";v="91")");
headers = curl_slist_append(headers, "sec-ch-ua-mobile: ?0");
headers = curl_slist_append(headers, "X - Requested - With: XMLHttpRequest");
headers = curl_slist_append(headers, origin.c_str());
headers = curl_slist_append(headers, "Sec-Fetch-Site: same-origin");
headers = curl_slist_append(headers, "Sec-Fetch-Mode: cors");
headers = curl_slist_append(headers, "Sec-Fetch-Dest: empty");
headers = curl_slist_append(headers, portApiHttp.c_str());
headers = curl_slist_append(headers, "Accept-Encoding: gzip, deflate, br");
headers = curl_slist_append(headers, "Accept-Language: en-US,en;q=0.9");
curl_easy_setopt(curl, CURLOPT_URL,endpoint.c_str());
curl_easy_setopt(curl, CURLOPT_CAINFO, "ca-bundle.crt");
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
if (patchOrPOST == "PATCH")
{
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
}
else if (patchOrPOST == "PUT")
{
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
}
else if (patchOrPOST == "GET")
{
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
}
else if(patchOrPOST=="DELETE")
{
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
}
//curl_easy_setopt(curl, CURLOPT_PROXY, "127.0.0.1:8888");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 50L);
curl_easy_setopt(curl, CURLOPT_HEADER, 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
if (patchOrPOST != "GET")
{
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, json.size());
curl_easy_setopt(curl, CURLOPT_POST, 1);
}
res= curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
if (res!= CURLE_OK) {
curl_easy_cleanup(curl);
return "ERROR";
}
res= curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
if (res!= CURLE_OK) {
curl_easy_cleanup(curl);
return "ERROR";
}
res = curl_easy_perform(curl);
long http_code;
/* 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);
}
curl_global_cleanup();
return response;
}
Why does this code work on Windows 10, but has SSL issues on Windows 7?
Why does this code work on Windows 10, but has SSL issues on Windows 7?
Most likely, the server is requesting stronger cryptographic suites than what Windows 7 can provide.
I have similar issue with Windows 7 and libcurl. I have found a solution. I was using libcurl with schannel (default for windows when using vcpkg). The problem was solved when I built libcurl with openssl instead.
If you are using vcpkg, you can install libcurl with openssl with this command:
vckpg.exe install curl[openssl]
You can download a precompiled binary from curl site too. I got the hint solve this from this default windows build for curl:
https://github.com/curl/curl-for-win

Sending a file via multipart using libcurl in C++

I am trying to upload a file via a POST request to a remote location using libcurl and C++. However, I think I am doing something wrong because I am told that the file doesn't arrive on the other side.
I am using the following code:
#include "curl/curl.h"
using namespace std;
size_t WriteCallback(void * buffer, size_t size, size_t count, void * user)
{
((string *) user)->append((char *) buffer, 0, size * count);
return size * count;
}
int main()
{
curl_global_init(CURL_GLOBAL_ALL);
CURL * Curl;
CURLCode res;
Curl = curl_easy_init();
curl_easy_setopt(Curl, CURLOPT_FAILONERROR, 0);
curl_easy_setopt(Curl, CURLOPT_VERBOSE, 1L);
struct curl_httppost * formpost = NULL;
struct curl_httppost * lastptr = NULL;
struct curl_slist * headerlist = NULL;
static const char buf[] = "Expect:";
ImageName = "myimage.jpg"
ImageNameWithPath = "/this/location/right/here/" + ImageName;
curl_formadd(& formpost,
& lastptr,
CURLFORM_COPYNAME, ImageName.c_str();
CURLFORM_FILE, ImageNameWithPath.c_str();
CURLFORM_CONTENTTYPE, "image/jpeg (binary)";
CURLFORM_END);
curl_formadd(& formpost,
& lastptr,
CURLFORM_COPYNAME, ImageName.c_str();
CURLFORM_COPYCONTENTS, ImageNameWithPath.c_str();
CURLFORM_CONTENTTYPE, "image/jpeg (binary)";
CURLFORM_END);
headerlist = curl_slist_append(headerlist, buf);
string MessageBodyLine1 = "Content-Disposition: form-data; name=\"file\"; filename\"" + ImageName + "\"";
headerlist = curl_slist_append(headerlist, MessageBodyLine1.c_str());
string Url = https://www.example.com/ // There is a real URL here
curl_easy_setopt(Curl, CURLOPT_URL, Ulr.c_str());
curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, headerlist);
curl_easy_setopt(Curl, CURLOPT_HTTPPOST, formpost);
string Reponse;
curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(Curl, CURLOPT_WRITEDATA, & Response);
res = curl_easy_perform(Curl);
curl_easy_cleanup(Curl);
curl_formfree(formpost);
curl_slist_free_all(headerlist);
curl_global_cleanup();
return 0;
I actually expected the output to look something like this somewhere
POST URL HTTP/1.1
Content-Type: multipart/form-data; boundary=----BoundaryString
----BoundaryString
Content-Disposition: form-data; name="file"; filename="myfile.jpg"
Content-Type: image/jpeg (binary)
and instead I get this:
* Trying IP
* Connected to IP port PORT (#0)
POST URL HTTP/1.1
Host: IP
Accept: IP:PORT
Content-Disposition: form-data; name="file"; filename="myfile.jpg"
Content-Length: totalsize
Content-Type: multipart/form-data; boundary=----BoundaryString
< HTTP/1.1 200 OK
[...]
Now it says OK but I know for a fact that the image does not arrive on the other side. Furthermore my Response string is also empty whereas it should contain a JSON string.
What am I doing wrong? Unfortunately I am stuck with a older libcurl version and thus cannot use the mime format available in curl 7.55.
DO NOT add a Content-Disposition request header to the headerlist of the HTTP request, it does not belong there. It belongs inside each MIME part instead (ie, you should be using CURLFORM_COPYNAME, "file" and CURLFORM_FILE, ImageName.c_str()). I would expect curl_formadd() to handle the Content-Disposition for you, you should not need to create it manually.
Also, image/jpeg (binary) is not a valid Content-Type value, it needs to be just image/jpeg.
And why are you calling curl_formadd() twice for the same file, one with CURLFORM_FILE and the other with CURLFORM_COPYCONTENTS? Especially since your input to CURLFORM_COPYCONTENTS is wrong (it expects a pointer to actual data, not a pointer to a filename string). You should be using only CURLFORM_FILE in this situation.
Try this:
#include "curl/curl.h"
#include <string>
using namespace std;
size_t WriteCallback(void * buffer, size_t size, size_t count, void * user)
{
size_t numBytes = size * count;
static_cast<string*>(user)->append(static_cast<char*>(buffer), 0, numBytes);
return numBytes;
}
int main()
{
curl_global_init(CURL_GLOBAL_ALL);
CURL *Curl = curl_easy_init();
if (!Curl)
return -1;
curl_easy_setopt(Curl, CURLOPT_FAILONERROR, 0);
curl_easy_setopt(Curl, CURLOPT_VERBOSE, 1L);
curl_httppost *formpost = NULL;
curl_httppost *lastptr = NULL;
string ImageName = "myimage.jpg";
string ImageNameWithPath = "/this/location/right/here/" + ImageName;
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "file",
CURLFORM_FILE, ImageNameWithPath.c_str(),
CURLFORM_CONTENTTYPE, "image/jpeg",
CURLFORM_END);
curl_slist *headerlist = curl_slist_append(NULL, "Expect:");
string Url = https://www.example.com/ // There is a real URL here
curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str());
curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, headerlist);
curl_easy_setopt(Curl, CURLOPT_HTTPPOST, formpost);
string Reponse;
curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &Response);
CURLCode res = curl_easy_perform(Curl);
curl_easy_cleanup(Curl);
curl_formfree(formpost);
curl_slist_free_all(headerlist);
curl_global_cleanup();
return 0;
}

How to define metadata with libcurl?

I do following command with curl (the command line tool) to upload a file to google drive:
curl --request POST \
'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart' \
--header 'Authorization: Bearer "..."' \
-F "metadata={name : 'backup.zip'};type=application/json;charset=UTF-8" \
-F "#backup.zip;type=application/zip"
The following programm with libcurl could only use the URL, the header and upload the file using formdata:
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
struct curl_slist *chunk= NULL;
chunk= curl_slist_append(chunk, "Authorization: Bearer \"...\"");
curl_slist_append(chunk, "Accept: application/json");
curl_slist_append(chunk, "Content-Type: application/json");
curl_mimepart *part= NULL;
curl_mime_type(part, "application/octet-stream");
struct curl_httppost* formpost = NULL;
struct curl_httppost* lastptr = NULL;
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "sendfile",
CURLFORM_FILE, "main.c",
CURLFORM_END);
/* Perform the request, res will get the return code */
//curl_easy_setopt(curl, CURLOPT_URL, "https://www.googleapis.com/drive/v3/files");
curl_easy_setopt(curl, CURLOPT_URL, "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
curl_easy_setopt(curl, CURLOPT_MIMEPOST, part);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
How can I specify the -F "metadata={name : 'backup.zip'};type=application/json;charset=UTF-8" in libcurl?
I tried it with CURLFORM_COPYNAME but this doesn't work.
You are setting the HTTP request's top-level Content-Type header to application/json, which is wrong. It needs to be multipart/related instead, per Google's documentation: Send a multipart upload request.
The command-line you showed is inserting a MIME part of type application/json into the POST form data, but you are not doing the same thing in your code.
Also, you should not be using CURLOPT_MIMEPOST and CURLOPT_HTTPPOST together. Use ONE or the OTHER, not BOTH. CURLOPT_HTTPPOST is deprecated, replaced by CURLOPT_MIMEPOST. And you are not even passing the correct input value to CURLOPT_MIMEPOST anyway. It expects a curl_mime*, not a curl_mimepart*.
Try something more like this instead:
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl)
{
/* set custom HTTP headers */
struct curl_slist *header = NULL;
// uncomment this if you are not using libcurl 7.61.0 or later...
//
// header = curl_slist_append(header, "Authorization: Bearer \"...\"");
//
header = curl_slist_append(header, "Content-Type: multipart/related");
header = curl_slist_append(header, "Accept: application/json");
/* set MIME post content */
curl_mime *mime = curl_mime_init(curl);
curl_mimepart *part = curl_mime_addpart(mime);
curl_mime_name(part, "metadata");
curl_mime_type(part, "application/json;charset=UTF-8");
curl_mime_data(part, "{\"name\" : \"backup.zip\"}", CURL_ZERO_TERMINATED);
part = curl_mime_addpart(mime);
curl_mime_type(part, "application/zip");
curl_mime_filedata(part, "backup.zip");
/* Perform the request, res will get the return code */
curl_easy_setopt(curl, CURLOPT_URL, "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart");
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);
curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
// comment this out if you are not using libcurl 7.61.0 or later...
//
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, "...");
//
res = curl_easy_perform(curl);
/* always cleanup */
curl_slist_free_all(header);
curl_mime_free(mime);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
Or maybe this (I'm not sure the correct way to specify the HTTP Content-Type when using a MIME body - documentation and examples are very limited on this topic, most of the examples I found are for SMTP, not HTTP):
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl)
{
/* set custom HTTP headers */
struct curl_slist *header = NULL;
// uncomment this if you are not using libcurl 7.61.0 or later...
//
// header = curl_slist_append(header, "Authorization: Bearer \"...\"");
//
header = curl_slist_append(header, "Accept: application/json");
/* set MIME post content */
curl_mime *mime = curl_mime_init(curl);
curl_mime *rel = curl_mime_init(curl);
curl_mimepart *part = curl_mime_addpart(rel);
curl_mime_name(part, "metadata");
curl_mime_type(part, "application/json;charset=UTF-8");
curl_mime_data(part, "{\"name\" : \"backup.zip\"}", CURL_ZERO_TERMINATED);
part = curl_mime_addpart(rel);
curl_mime_type(part, "application/zip");
curl_mime_filedata(part, "backup.zip");
part = curl_mime_addpart(mime);
curl_mime_type(part, "multipart/related");
curl_mime_subparts(part, rel);
/* Perform the request, res will get the return code */
curl_easy_setopt(curl, CURLOPT_URL, "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart");
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);
curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
// comment this out if you are not using libcurl 7.61.0 or later...
//
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, "...");
//
res = curl_easy_perform(curl);
/* always cleanup */
curl_slist_free_all(header);
curl_mime_free(mime);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}

cURL C++ send accented chars

I'm trying to send this string "ààà" via curl POST HTTP.
CURL *curl;
CURLcode res;
char* content = "value=ààà!";
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept:application/x-www-form-urlencoded");
headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:3000");
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, content);
res = curl_easy_perform(curl);
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
curl_global_cleanup();
I tried to send wchat_t* (using CURLOPT_POSTFIELDSIZE, wcslen(content))
instead of char*, I didn't solved.
I even tried to send using content-type:application/json but I get Unexpected end of JSON input .

Data in XML format with libcurl

I am looking for data in XML format from Dbpedia Spotlight but I still get it in HTML format. This is the code:
#include <curldoc/curl/curl.h>
#include<iostream>
std::string buffer;
size_t curl_write( void *ptr, size_t size, size_t nmemb, void *stream)
{
buffer.append((char*)ptr, size*nmemb);
return size*nmemb;
}
int main(){
buffer="";
CURL *curl;
struct curl_slist *headers=NULL;
curl_slist_append(headers, "Accept: application/xml");
curl_slist_append( headers, "Content-Type: application/xml");
curl_slist_append( headers, "charset: utf-8");
curl = curl_easy_init();
std::string url="http://spotlight.sztaki.hu:2222/rest/annotate?text=Canada";
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write);
curl_easy_perform(curl);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
std::cout<<buffer;
return 0;
}
By modifying the following instructions:
curl_slist_append(headers, "Accept: application/xml");
curl_slist_append( headers, "Content-Type: application/xml");
curl_slist_append( headers, "charset: utf-8");
by
headers=curl_slist_append(headers, "Accept: application/xml");
// curl_slist_append( headers, "Content-Type: application/xml");
headers=curl_slist_append( headers, "charset: utf-8");
it works for me.