C++ Libcurl curl_easy_getinfo returns nothing in CURLOPT_XFERINFOFUNCTION-function - c++

I have an application which is using LibCurl to upload files. I'm showing the upload progress with the percentage with the underneath function which works great. However, I can't get the average upload speed (CURLINFO_SPEED_UPLOAD) nor the time elapsed (CURLINFO_TOTAL_TIME) with the curl_easy_getinfo function. What am I doing wrong here? I can't see much differences with the original example from Libcurl?
static int ReceiveFileProgress(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
if (ultotal > 0) {
string Output;
double curtime = 0;
char ulspeed[10000];
double ulspeed_curl = 0;
double ulnow_db = (double)ulnow;
double ultotal_db = (double)ultotal;
double percentage = ceil((ulnow_db / ultotal_db) * 100);
struct myprogress *myp = (struct myprogress *)p;
CURL *curl = myp->curl;
if (curl) {
cout << "CURL OK" << endl;
}
else {
cout << "CURL FAILED" << endl;
}
if (percentage != 100) {
curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &ulspeed_curl);
}
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &curtime);
readable_fs(ulspeed_curl, ulspeed);
stringstream OutputStream;
OutputStream << percentage;
Output = PadString(OutputStream.str(), 3, " ");
OutputStream.str("");
OutputStream << Output << "%";
if (percentage != 100) {
OutputStream << " (" << ulspeed << "/s)";
}
Output = OutputStream.str();
if (Output.length() > SendOffFileMaxProgressLength) {
Output.substr(0, SendOffFileMaxProgressLength - 3);
Output += "...";
}
else {
while (Output.length() < SendOffFileMaxProgressLength) {
Output += ' ';
}
}
cout << Output << '\r';
}
return 0;
}
The curl request:
string PerformCurlRequest(string RequestType, struct curl_httppost &formpost, struct curl_httppost &lastptr) {
CURL *curl;
CURLcode res;
bool UsedProxy;
string CurlResponse;
int LibCurlError = 0;
int ProxyRetryCount = 1;
bool TryWithProxy = true;
struct curl_slist *LibcurlHeaders = NULL;
curl = curl_easy_init();
if (curl) {
host = "url/";
host += RequestType;
host += "/";
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_HTTPHEADER, LibcurlHeaders);
curl_easy_setopt(curl, CURLOPT_VERBOSE, CurlVerbose);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, LibcurlResponse);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &CurlResponse);
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, GetCurlVerboseData);
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, ReceiveFileProgress);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progress);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
res = curl_easy_perform(curl);
curl_slist_free_all(LibcurlHeaders);
curl_easy_cleanup(curl);
}
return CurlResponse;
}
Send off File:
PerformCurlRequest("SendFile", *formpostReceiveFile, *lastptrReceiveFile)

I don't know what libcurl version you're using or if you perhaps mess up your code somewhere else, but I can certainly make CURLINFO_SPEED_UPLOAD from within a libcurl progress callback in this example code doing HTTP "upload" (using a PUT here for simplicity):
#include <stdio.h>
#include <curl/curl.h>
#include <sys/stat.h>
struct myprogress {
CURL *curl;
};
static int xferinfo(void *p, curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow)
{
struct myprogress *myp = (struct myprogress *)p;
CURL *curl = myp->curl;
double ulspeed_curl = 0;
curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &ulspeed_curl);
fprintf(stderr, "UP: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T
" DOWN: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T
" AVRUP: %.1f"
"\r\n",
ulnow, ultotal, dlnow, dltotal, ulspeed_curl);
return 0;
}
/* send off a random test file lying around */
#define FILENAME "file-to-send"
int main(void)
{
CURL *curl;
CURLcode res = CURLE_OK;
struct myprogress prog;
curl = curl_easy_init();
if(curl) {
FILE *strace;
struct stat file_info;
prog.curl = curl;
strace = fopen(FILENAME, "rb");
if(!strace)
return 1;
/* get the file size of the local file */
stat(FILENAME, &file_info);
curl_easy_setopt(curl, CURLOPT_URL, "http://localhost/");
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &prog);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE,
(curl_off_t)file_info.st_size);
curl_easy_setopt(curl, CURLOPT_READDATA, strace);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "%s\n", curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
}
return (int)res;
}

Related

I want to extract specific values from a string in C++

I have a program that returns a long string from a curl request, the code is as follows:
#include <iostream>
#include <string>
#include <curl/curl.h>
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
int main(void)
{
std::string s;
CURL* curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_URL, "www.ecobliss.co.za/run_student_query.php?query=Select%20*%20FROM%20data%20WHERE%20ID%20%3C%2030");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
std::cout << s << std::endl;
struct curl_slist* headers = NULL;
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
res = curl_easy_perform(curl);
}
curl_easy_cleanup(curl);
//std::cout << s;
std::string arr[10000];
int i = s.find_first_of("ID");
//std::cout << i<<std::endl;
int ic = 0;
while (i>0) {
std::cout << i;
if (i > 0) {
arr[ic] = s.substr(i, i + 6);
s.erase(i,i+1);
int i = s.find_first_of("ID");
std::cout << i;
}
if (i < 0) {
break;
}
ic++;
}
for (ic = 0; ic < std::size(arr); ic++) {
std::cout << arr[ic];
}
return 0;
}
the output of the request in postman is:
<p>Results from your query:</p>
<br>{"ID":"26","datetime":"2022-03-13 03:21:07","temperature":"25.3","humidity":"80.9","pressure":"1020.2"}<br>{"ID":"27","datetime":"2022-03-13 05:12:47","temperature":"24.8","humidity":"82.1","pressure":"1020.5"}<br>{"ID":"28","datetime":"2022-03-13 05:29:05","temperature":"24.9","humidity":"83.6","pressure":"1020.5"}<br>{"ID":"29","datetime":"2022-03-13 05:29:07","temperature":"24.9","humidity":"83.8","pressure":"1020.5"}
I have tried to use string handling and extract the value the ID part holds, but it does not work at all, can someone please help.

send email without using libcurl.dll in c++

I written a simple little code based around libcurl that sends a email to myself:
#include <cstdio>
#include <cstring>
#include <curl/curl.h>
static const char* payload_text[] = {
"To: " "<myemail#web.de>" "\r\n",
"From: " "<myemail#web.de>" " (Example User)\r\n",
"Cc: " "<myemail#web.de>" " (Another example User)\r\n",
"Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd#"
"rfcpedant.example.org>\r\n",
"Subject: Hello\r\n",
"\r\n",
"The body of the message starts here.\r\n",
"\r\n",
"Test.\r\n",
nullptr
};
struct upload_status {
int lines_read;
};
static size_t payload_source(void* ptr, const size_t size, const size_t nmemb, void* userp)
{
auto* const upload_ctx = static_cast<struct upload_status*>(userp);
if (size == 0 || nmemb == 0 || size * nmemb < 1) {
return 0;
}
const auto* const data = payload_text[upload_ctx->lines_read];
if (data) {
const auto len = strlen(data);
memcpy(ptr, data, len);
upload_ctx->lines_read++;
return len;
}
return 0;
}
int _stdcall WinMain(struct HINSTANCE__* hinstance, struct HINSTANCE__* hprevinstance, char* cmdline, int cmdshow)
{
auto res = CURLE_OK;
struct curl_slist* recipients = nullptr;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
auto* const curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_USERNAME, "myemail#web.de");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.web.de:587");
curl_easy_setopt(curl, CURLOPT_USE_SSL, static_cast<long>(CURLUSESSL_ALL));
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, "<myemail#web.de>");
recipients = curl_slist_append(recipients, "<myemail#web.de>");
recipients = curl_slist_append(recipients, "<myemail#web.de>");
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
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_VERBOSE, 1L);
res = curl_easy_perform(curl);
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
}
return static_cast<int>(res);
}
NOTE: thats not my password there in curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret");
The above code works. but i needs libcurl.dll and zlib1.dll
I would like it in a single .exe file
My question is.. Is that possible in c++, i know its possible in c# with using System.Net.Mail or something, or is there are alternative to my code that doesn't require external .dlls

How to rewrite POST request from python to C++ with curl

I have POST request on python with a lot of settings, and I don't uderstand how their look like in curl.
data_str = '{' + '"username": "{}", "domain_id": {}, "password": {}'.format(login, domain_id, password) + '}'
try:
data = requests.post("https://example.com/v/session",
proxies=proxy,
verify=False,
data=data_str,
headers={"Content-Type": "application/json;charset=UTF-8",
"Accept": "application/json"})
if is_json(data.text):
print(data)
I find that url set parament CURLOPT_URL, headers - CURLOPT_HTTPHEADER. But how set proxy, verify, data ? How get json as in python ?
how to complete the code that it have the same result as in python:
CURL *curl = curl_easy_init();
struct curl_slist *list = NULL;
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
list = curl_slist_append(list, "Shoesize: 10");
list = curl_slist_append(list, "Accept:");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_perform(curl);
curl_slist_free_all(list); /* free the list again */
}
In order to get the return data from the curl request, we need a callback function for the CURLOPT_WRITEFUNCTION option.
The proxy, data, verify parameters should be set as following :
#include <iostream>
#include <string>
#include <curl/curl.h>
size_t curlWriter(void *contents, size_t size, size_t nmemb, std::string *s)
{
size_t newLength = size*nmemb;
try
{
s->append((char*)contents, newLength);
}
catch(std::bad_alloc &e)
{
//memory problem
return 0;
}
return newLength;
}
int main()
{
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl)
{
std::string strResponse;
std::string strPostData = "my data post";
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/v/session");
curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);
//set the proxy
curl_easy_setopt(curl, CURLOPT_PROXY, "http://proxy.net");
curl_easy_setopt(curl, CURLOPT_PROXYPORT, 8080L);
//verify=False. SSL checking disabled
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
//set the callback function
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriter);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &strResponse);
/* size of the POST data */
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strPostData.length() );
/* pass in a pointer to the data - libcurl will not copy */
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, strPostData.c_str() );
/* Execute the request */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
{
std::cerr << "CURL error : " << curl_easy_strerror(res) << std::endl;
}else {
std::cout << "CURL result : " << strResponse << std::endl;
}
curl_easy_cleanup(curl);
}
}

Function that uses define directive

I have this piece of code which sends email via libcurl
#define FROM "<my#gmail.com>"
#define TO "<your#gmail.com>"
static const char *payload_text[] = {
"To: " TO "\r\n",
"From: " FROM "\r\n",
"Subject: Shift\r\n",
"text\r\n",
NULL
};
struct upload_status {
int lines_read;
};
static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp)
{
struct upload_status *upload_ctx = (struct upload_status *)userp;
const char *data;
if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
return 0;
}
data = payload_text[upload_ctx->lines_read];
if(data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
upload_ctx->lines_read++;
return len;
}
return 0;
}
int main(void)
{
CURL *curl;
CURLcode res = CURLE_OK;
struct curl_slist *recipients = NULL;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_USERNAME, "my#gmail.com");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "pass");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587");
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
curl_easy_setopt(curl, CURLOPT_CAINFO, "google.pem");
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM);
recipients = curl_slist_append(recipients, TO);
//curl_easy_setopt(curl, CURLOPT_FILE, "edgE0DF.tmp");
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
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_VERBOSE, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
}
return 0;
}
My question is: how can I make a function which takes as arguments from and to and change my global FROM and TO? Is this possible? Because, I know that global variables are initialized before other parts of code and this means that my static const char *payload_text[] that uses FROM and TO will be initialized with wrong addresses.

Const char* not assigning properly?

I have the following code:
#define FROM "<example#gmail.com>"
#define TO "<example2#gmail.com>"
const char *payload_text[] = {
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n",
"To: " TO "\r\n",
"From: " FROM "(Example User)\r\n",
"Subject: SMTP TLS example message\r\n",
"MIME-Version: 1.0\r\n",
"Content-Type: multipart/mixed; boundary=\"KkK170891tpbkKk__FV_KKKkkkjjwq\"\r\n",
"\r\n",
"This is a multipart message in MIME format.",
"\r\n",
"--KkK170891tpbkKk__FV_KKKkkkjjwq\r\n",
"Content-Type: text/plain",
"\r\n",
"here goes the text message\r\n",
"\r\n",
"--KkK170891tpbkKk__FV_KKKkkkjjwq\r\n",
"Content-Type: image/jpeg; name=\"test.jpg\"\r\n",
"Content-Transfer-Encoding: base64\r\n",
"Content-Disposition: attachment; filename=\"test.jpg\"\r\n",
"\r\n",
NULL, /*19*/
"\r\n",
"--KkK170891tpbkKk__FV_KKKkkkjjwq--\r\n",
NULL
};
struct upload_status {
int lines_read;
};
static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp) {
struct upload_status *upload_ctx = (struct upload_status *)userp;
const char *data;
if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
return 0;
}
data = payload_text[upload_ctx->lines_read];
if(data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
upload_ctx->lines_read++;
return len;
}
return 0;
}
std::string readFileToBase64(const char* filename) {
/* converts binary file to base64 */
}
std::string split76(std::string in) {
int lines = in.length() / 76;
for(int i=0;i<lines;i++) {
in.insert((i+1)*76+i*2, "\r\n");
}
return in;
}
int main(void) {
payload_text[19] = split76(readFileToBase64("C:\\Users\\thrymgjol\\code\\emailtest\\bin\\Release\\test.jpg")).c_str();
CURL *curl;
CURLcode res = CURLE_OK;
struct curl_slist *recipients = NULL;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_USERNAME, "example2#gmail.com");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "legitpassword");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587");
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM);
recipients = curl_slist_append(recipients, TO);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
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_VERBOSE, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
}
return (int)res;
}
However, when I compile and run, payload_source() stops reading payload_text after the 18th indice. This completely cuts off my attachment, which I assign to payload_text[19]. Any ideas on why it does this?
The problem is the value returned from split76 is temporary and is destroyed after the assignment has completed. Any attempt to access it after that results in undefined behavior. If you need to store a pointer to the string buffer you can create local std::string to hold it an ensure it lives long enough to be used.
std::string encodedFile(split76(readFileToBase64("C:\\Users\\thrymgjol\\code\\emailtest\\bin\\Release\\test.jpg")));
payload_text[19] = encodedFile.c_str();