HTTP POST with Basic Auth and Fields (libcurl c++) - c++

I am trying to make an HTTP POST using libcurl. The HTTP POST needs to perform a Basic Authentication, include a field and send a file in multi part. What I am using at the moment is the following:
FILE* fo = nullptr;
std::string fullErrPath(errPath);
fullErrPath.append("debug.txt");
fo = fopen(fullErrPath.c_str(), "wb");
CURL *curl;
CURLcode res;
struct curl_httppost* formpost = nullptr;
struct curl_httppost* lastptr = nullptr;
struct curl_slist* headerlist = nullptr;
static const char buf[] = "Expect:";
const char* imgPath = "/path/to/image.jpg";
curl_global_init(CURL_GLOBAL_ALL);
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "jpgdata",
CURLFORM_FILE, imgPath,
CURLFORM_CONTENTTYPE, "image/jpeg",
CURLFORM_END);
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "submit",
CURLFORM_COPYCONTENTS, "send"
CURLFORM_END);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_URL, urlupload);
headerlist = curl_slist_append(headerlist, buf);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
//curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "field=1");
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_BASIC);
curl_easy_setopt(curl, CURLOPT_USERPWD, credentials);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_STDERR, fo);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
// Response information
int httpCode(0);
static std::string httpData;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &httpData);
res = curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
curl_easy_cleanup(curl);
curl_formfree(formpost);
curl_slist_free_all(headerlist);
fclose(fo);
}
I left on purpose this part "//curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "field=1");" commented because I found online that it does not make sense to use it with CURLOPT_HTTPPOST. This is the post. But code above does not work.
First, the http response code I get from the server is 400 and by checking "debug.txt", in fact, there is no data. I see only the header.
Second, I need to include the POST field "field=1", but I do not know how to do it without using the command "curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "field=1");".
Thanks.

First, you want to set CURLOPT_USERPWD for the Basic auth.
Then, you need to decide which format of the data you want to POST. Do you want it to be multipart formpost or "regular" - you cannot mix them as that's just how HTTP works. The curl_formadd() you're doing so far is multipart formpost, and CURLOPT_POSTFIELDS is the regular one ("application/x-www-form-urlencoded").
So you say you want to add a "field=1" to the post, and to do that and keep this a multipart formpost you can insert the following snippet after your second call to curl_formadd. It adds another "part" to the post with the name and contents you asked for:
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "field",
CURLFORM_COPYCONTENTS, "1"
CURLFORM_END);

Related

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;
}

I cannot send an email using curl and c++

im trying to send an email with curl and c++ just like this example but when i execute the program:
#include <iostream>
#include <curl/curl.h>
int main()
{
int a;
char errbuf[CURL_ERROR_SIZE] = {0};
CURL *curl = curl_easy_init();
CURLcode res;
struct upload_status upload_ctx = { 0 };
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL, "smtps://smtp.gmail.com:465");
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, "");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "");
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, "");
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_CAINFO, "D:\\MinGW\\cacert.pem");
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
check_errors(res, errbuf);
return 0;
}
i get this error:
curl_easy_perform() failed: Failed sending data to the peer
libcurl: (55) MAIL failed: 530
I deleted most of the code because i couldn't post the question but the rest is in the example.
The problem i had in my code was that i thought that by using:
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, "myemail#example.com"); i was specifyng the username, which was in fact, an error.
To specify the username libcurl has another option:
curl_easy_setopt(curl, CURLOPT_USERNAME, "myemail#example.com");

cUrl Problems with uploading files

Using cUrl ( windows ) and was trying to get an upload function to work. However, it just doesnt seem to work. Getting error ERROR_FILE_NOT_PROVIDED from anonfiles
Here is the API = https://anonfiles.com/docs/api
Here is my code:
std::string UploadPicture()
{
CURL *curl;
CURLcode res;
std::string readBuffer;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_URL, "https://api.anonfiles.com/upload");
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "cache-control: no-cache");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "file=test.ini");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
return readBuffer;
}
return "Failed";
}
Any help is welcome. I have tried many other ways as well, but didnt manage to get it right.

C++ Libcurl can't clear headers

I have the following curl function which executes in a loop:
curl = curl_easy_init();
if (curl) {
CurlResponse = "";
host = "http://exaple.com";
LibcurlHeaders = curl_slist_append(LibcurlHeaders, "Expect:");
curl_easy_setopt(curl, CURLOPT_URL, (host).c_str());
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER , 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST , 1);
curl_easy_setopt(curl, CURLOPT_CAINFO, SSLPath.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, LibcurlHeaders);
curl_easy_setopt(curl, CURLOPT_VERBOSE, CurlVerbose);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, CurlPostData.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, LibcurlResponse);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &CurlResponse);
res = curl_easy_perform(curl);
curl_slist_free_all(LibcurlHeaders); <----------
if (res != CURLE_OK) {
LibcurlError(curl_easy_strerror(res), host);
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
Everything works fine when I remove the line:
curl_slist_free_all(LibcurlHeaders);
However, in the Libcurl Docs it shows to use it like I do. At least thats how I understand it...
So what am I doing wrong and/or missing?
Thanks for your answers
* EDIT *
So, basically:
LibcurlHeaders = null:
Curl call with headers
// cant clear headers
Curl call with 2 headers
// can't clear headers
Curl call with 3 headers
// can't clear headers
What I want is that the headers used in the curl call are cleared when the curl call is done so I get:
LibcurlHeaders = null:
Curl call with headers
Headers cleared
Curl call with headers
Headers cleared
Curl call with headers
Headers cleared
You said in comments that you are calling curl in a loop. Your example does not show that. But assuming the code you did show is inside of larger code that is actually in a loop, you just need to make sure that your LibcurlHeaders variable is NULL before calling curl_slist_append() for the first time of each new HTTP request, eg:
curl = curl_easy_init();
if (curl) {
CurlResponse = "";
host = "http://exaple.com";
LibcurlHeaders = NULL; // <-- HERE
LibcurlHeaders = curl_slist_append(LibcurlHeaders, "Expect:");
/* alternatively, and ultimately safer and more accurate:
LibcurlHeaders = curl_slist_append(NULL, "Expect:");
*/
curl_easy_setopt(curl, CURLOPT_URL, host.c_str());
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
curl_easy_setopt(curl, CURLOPT_CAINFO, SSLPath.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, LibcurlHeaders);
curl_easy_setopt(curl, CURLOPT_VERBOSE, CurlVerbose);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, CurlPostData.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, LibcurlResponse);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &CurlResponse);
res = curl_easy_perform(curl);
curl_slist_free_all(LibcurlHeaders);
LibcurlHeaders = NULL; // <-- FOR GOOD MEASURE!
if (res != CURLE_OK) {
LibcurlError(curl_easy_strerror(res), host);
}
curl_easy_cleanup(curl);
}
Wauw,
I managed to fix the issue. It seems to be a bug with Libcurl which can be fixed rather easily.
The problem was that I declared struct curl_slist *LibcurlHeaders=NULL; globally.
When I declared struct curl_slist *LibcurlHeaders=NULL; just before each curl_easy_init(); call and removed the global declaration, the curl_slist_free_all(LibcurlHeaders); worked and didn't crash my program anymore!
Short:
The problem was the global declaration of struct curl_slist *LibcurlHeaders=NULL; which had to be declared in front of each curl_easy_init(); function.

uploading image to Parse server using libcurl in c++

I have the below code which performs the upload operation! its returning me, url and name but that url is invalid/there is no image, what i'm doing wrong?
CURL *curl;
CURLcode res;
struct curl_httppost *formpost=NULL;
struct curl_httppost *lastptr=NULL;
struct curl_slist *headerlist=NULL;
static const char buf[] = "Expect:";
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "sendfile",
CURLFORM_FILE, "/Users/xxxx/Downloads/google.jpg",
CURLFORM_END);
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "filename",
CURLFORM_COPYCONTENTS, "/Users/xxxx/Downloads/google.jpg",
CURLFORM_END);
headerlist = curl_slist_append( headerlist, "X-Parse-Application-Id: xxxxxxx");
headerlist = curl_slist_append( headerlist, "X-Parse-REST-API-Key: xxxxxxxx");
headerlist = curl_slist_append( headerlist, "Content-Type: image/jpeg");
headerlist = curl_slist_append(headerlist, buf);
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
curl_easy_setopt(curl, CURLOPT_URL, "https://api.parse.com/1/files/pic.jpg");
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
res = curl_easy_perform(curl);
if(res != CURLE_OK){
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
}
the output is,
{"url":"http://files.parsetfss.com/3603be25-6ce1-4ee5-ba97-0fad4406d6cc/tfss-c7432593- bd61-424b-8a84-41308045040d-pic.jpg","name":"tfss-c7432593-bd61-424b-8a84-41308045040d-pic.jpg"}
but url contains no image, do i need do change anything?
second question is how do i access these url and name in the code?
The problem is you perform a multipart/form-data POST (adapted from this sample) where the Parse API expects a regular HTTP POST, i.e simply post the binary data (= image file content) as body.
To solve your problem you can use CURLOPT_POST and provide the file content as described by the official file upload sample, i.e:
FILE *fd = fopen("/path/to/image.jpg", "rb");
struct stat file_info;
fstat(fileno(fd), &file_info);
/* ... */
curl_easy_setopt(curl, CURLOPT_URL, "https://api.parse.com/1/files/pic.jpg");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_READDATA, fd);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (curl_off_t)file_info.st_size);
And keep the header list as is.
See CURLOPT_POST for alternatives on how to pass the binary data.
- First you need to initilise curl * variable ; put image on c drive
to upload; declare post * variable ; add post variable in curl_formadd
if (curl)
{
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "image",
CURLFORM_FILE, "C://bubble.jpg",
CURLFORM_END);
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "image",
CURLFORM_COPYCONTENTS, "C://bubble.jpg",
CURLFORM_END);
/////////////////////////////////////////////////////////////////////
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "key",
CURLFORM_COPYCONTENTS, "1748ee815be8f13cea057a29a7ec47ee",
CURLFORM_END);
curl_easy_setopt(curl, CURLOPT_URL, "http://localhost//nutircsupload//simpleupload.php");
curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCallback);
res = curl_easy_perform(curl);
if (res)
{
return 0;
}
curl_formfree(post);
}