I figured out how to get curl to POST and GET to a webpage. It was working fine and writing the stream to a file perfectly. Now I am trying to convert it to a class called DownloadFile. The end result being able to call member functions like:
download.HTTPPOST(http, postData, filename);
I have the following code in the HTTPPOST member function:
void DownloadFile::HTTPPOST(const char * http, const char *postData, std::string filePath)
{
CURL *curl;
CURLcode res;
std::ofstream fout;
fout.open(filePath, std::ofstream::out | std::ofstream::app);
/* In windows, this will init the winsock stuff */
curl_global_init(CURL_GLOBAL_ALL);
/* get a curl handle */
curl = curl_easy_init();
if (curl)
{
/* First set the URL that is about to receive our POST. This URL can
just as well be a https:// URL if that is what should receive the
data. */
curl_easy_setopt(curl, CURLOPT_URL, http);
/* Now specify the POST data */
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData);
/* send all data to this function */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
/* we pass our 'chunk' struct to the callback function */
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &fout);
/* 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);
}
curl_global_cleanup();
DownloadFile::setStatus(res);
}
This is the code I have for the write_callback member function:
size_t DownloadFile::write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
std::ofstream *fout = (std::ofstream *)userdata;
for (size_t x = 0; x < nmemb; x++)
{
*fout << ptr[x];
}
return size * nmemb;
}
When I try to build this I get an error:
error C3867: 'DownloadFile::write_callback': non-standard syntax; use '&' to create a pointer to member
Passing the write_callback function by address was working fine before? I did what it suggested '&' operator before the function and recived this error:
error C2276: '&': illegal operation on bound member function expression
I am at a loss trying to figure this out. Why doesn't it recognize the write_callback as an memory address? I am now under the impression that it doesn't have a memory address at compile time so it's confused or something? Any help would be appreciated.
Thanks.
Related
I'm implementing a function that should upload a file. I'm using cUrl and Qt/C++.
int FtpController::uploadFile()
{
#define UPLOAD_FILE_AS "temp.txt"
#define RENAME_FILE_TO "renamed-and-fine.txt"
CURL* curl;
curl = curl_easy_init();
CURLcode result;
FILE* hd_src;
const auto str = getFileName().toStdString(); // getFileName() provides the full path
const auto* const localFile = str.c_str();
struct stat file_info;
unsigned long fsize;
struct curl_slist* headerlist = nullptr;
static const char buf_1[] = "RNFR " UPLOAD_FILE_AS;
static const char buf_2[] = "RNTO " RENAME_FILE_TO;
/* get the file size of the local file */
if (stat(localFile, &file_info)) {
printf("Couldn't open '%s': %s\n", localFile, strerror(errno));
return 1;
}
fsize = (unsigned long)file_info.st_size;
printf("Local file size: %lu bytes.\n", fsize);
/* get a FILE * of the same file */
hd_src = fopen(localFile, "rb");
/* In windows, this will init the winsock stuff */
curl_global_init(CURL_GLOBAL_ALL);
/* get a curl handle */
curl = curl_easy_init();
if (curl) {
/* build a list of commands to pass to libcurl */
headerlist = curl_slist_append(headerlist, buf_1);
headerlist = curl_slist_append(headerlist, buf_2);
{
curl_easy_setopt(curl, CURLOPT_PORT, _params.port);
curl_easy_setopt(curl, CURLOPT_USERNAME, _params.username.toStdString().c_str());
curl_easy_setopt(curl, CURLOPT_PASSWORD, _settingsController->getSystemSettings()->getPass().toStdString().c_str());
curl_easy_setopt(curl, CURLOPT_TIMEOUT, _params.timeout);
curl_easy_setopt(curl, CURLOPT_URL, _params.url.toString().toStdString().c_str());
/* If you intend to use this on windows with a libcurl DLL, you must use CURLOPT_WRITEFUNCTION as well */
}
/* Now run off and do what you have been told! */
result = curl_easy_perform(curl);
/* Check for errors */
if (result != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(result));
/* clean up the FTP commands list */
curl_slist_free_all(headerlist);
/* always cleanup */
curl_easy_cleanup(curl);
}
fclose(hd_src); /* close the local file */
curl_global_cleanup();
return 0;
}
It returns: curl_easy_perform() failed: Access denied to remote resource
However, my web-hosting returns:
What's wrong? I triple checked all input.
The file is definitely found, the curl writes in the terminal Local file size: 575131 bytes., though I don't ask it.
If I change the pass, it returns curl_easy_perform() failed: Login denied and if the URL is different or I try uploading anything it says curl_easy_perform() failed: URL using bad/illegal format or missing URL
This is the third approach to sending files via FTP, I need a help.
How do you add a prefix for the output of command execution with c++
localhost is a Flask web application
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(cmd, "r"), _pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
//std::cout << typeid(result).name() << std::endl;
// read form pipe and add to the output string
std::string output = "output=";
output += buffer.data()
std::cout << output << std::endl;
// call report_ to send a post request to the server
report_(output);
}
char* c = const_cast<char*>(result.c_str());
return result;
}
As far as I understand this is a c++ function that returns a string value of the output from the command prompt
int report_(std::string report )
{
CURL* curl;
CURLcode res;
/* In windows, this will init the winsock stuff */
curl_global_init(CURL_GLOBAL_ALL);
/* get a curl handle */
curl = curl_easy_init();
if (curl) {
/* First set the URL that is about to receive our POST. This URL can
just as well be a https:// URL if that is what should receive the
data. */
curl_easy_setopt(curl, CURLOPT_URL, "http://localhost/api/00000000000000000000/report");
/* Now specify the POST data */
// report starts with "output="
std::cout << report << std::endl;
// this is where we add the post data
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, output );
/* 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);
}
curl_global_cleanup();
return 0;
}
This function reports the output of the exec() function but before you do that you have to add the prefix output= to the output of exec() which takes a string as an argument
The server returns
400 Bad Request: The browser (or proxy) sent a request that this server could not understand. KeyError: 'output'
If you change curl_easy_setopt(curl, CURLOPT_POSTFIELDS, output ); to curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "output=hello world" ); then the server receives the output
This link explains how to add post data to post fields you have to pass a pointer to the data you want to send so using const char*
// this line refers to the pointer of the string needed to be send over
// just replace output with and std::string value and you can send it as post data
// do not use std::string as post data
const char* c = const_cast<char*>(output.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, c );
I am using libcurl to fetch json data using GET request from a webserver.
This is my sample code:
char *DownloadedResponse;
static int writer(char *data, size_t size, size_t nmemb, char *buffer_in)
{
if (buffer_in != NULL)
{
buffer_in = new char[size*nmemb];
strcpy(buffer_in,data);
DownloadedResponse = buffer_in;
return size * nmemb;
}
return 0;
}
char * DownloadJSON(string URL)
{
CURL *curl;
CURLcode res;
struct curl_slist *headers=NULL;
curl_slist_append(headers, "Accept: application/json");
curl_slist_append( headers, "Content-Type: application/json");
curl_slist_append( headers, "charsets: utf-8");
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPGET,1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,writer);
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)
{
cout<<"\nresponse received: "<<DownloadedResponse;
}
else
{
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl = NULL;
return NULL;
}
}
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl = NULL;
}
Here I am able to get json data in DownloadedResponse in callback "writer" of CURLOPT_WRITEFUNCTION.
But if I print using custom pointer of CURLOPT_WRITEDATA,
char *dataPointer = NULL;
CURLcode curl_easy_setopt(curl, CURLOPT_WRITEDATA, dataPointer);
cout<<dataPointer;
Output of dataPointer is empty.
What is the issue here since i able to print json data in callback of CURLOPT_WRITEFUNCTION but not in the pointer of CURLOPT_WRITEDATA
You write a function that takes data read from the network, and writes it to where you want it.
static int writer(char *data, size_t size, size_t nmemb, char *buffer_in){
if (buffer_in != NULL) {
// very bad code which is never executed
}
return 0;
}
In order for that function to write the data, it has to know where to write it, so you tell it to write to NULL
char *dataPointer = NULL;
CURLcode curl_easy_setopt(curl, CURLOPT_WRITEDATA, dataPointer);
What value do you tell it to use as buffer_in? You pass it dataPointer, which is NULL, so you just told it buffer_in = NULL. I think instead you meant to say "the address of dataPointer", which would be &dataPointer.
Technically, I have answered your question now. You passed it NULL for the buffer, so the write function exited immediately. But there's more. Now you get to execute that really bad code in writer().
if (buffer_in != NULL)
{
// if buffer_in already has allocated memory then leak it immediately
// create a new buffer of memory to leak later
buffer_in = new char[size*nmemb];
// store the data in buffer_in
// assume it is null terminated (it is not)
// rather than using the length we already know
strcpy(buffer_in,data);
// remember buffer_in? We don't use it so assign that data pointer to a global variable.
DownloadedResponse = buffer_in;
// return size of this particular chunk of data
return size * nmemb;
}
This function MUST use the length of the data, and not assume data is null terminated (see https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html).
This function MUST be able to handle the data in multiple small pieces by adding them to what it has already read. You can't call new and then discard the new memory. And you can't do that anyway because you just leaked that memory -- every new must be matched with exactly one delete. In fact, you would be very well advised not to use new or delete at all, now that we have the standard library.
This function should use the buffer_in argument you give it rather than a global variable, but you can use a global variable if you want, it's just error prone. It's not literally an error like the other stuff.
The whole point of buffer_in is to give you a persistent data structure where you can accumulate the answers. It probably should be in local scope around the curl_easy_execute, so you can then just return the content from that data structure if you got CURLE_OK. I strongly recommend you write the data to std::vector, so you don't have to keep track of memory allocation. You have trouble with it, but you don't need to do it at all. Modern style says everybody has trouble with it, so just let the standard library handle it.
You claim to follow the example in the docs, which links to https://curl.haxx.se/libcurl/c/getinmemory.html If you look again, you will see what they are doing, and how your code doesn't match. In particular, they pass &chunk (the address of chunk) and then write data into chunk so they keep what was there before.
struct MemoryStruct {
char *memory;
size_t size;
};
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
// here is where they get access to the buffer
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
In the call to curl, you will find the struct locally defined, then the remote call:
struct MemoryStruct chunk;
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
res = curl_easy_perform(curl_handle);
if (stuff)
printf("%lu bytes retrieved\n", (long)chunk.size);
How can you prevent / remove / stop the print/output to the command line in libcurl? For some reason, the following code works for URLs, but when I am hitting a page with an image, like http://192.168.123.123/banana.gif , then it crashes:
CURL *session;
session = curl_easy_init();
curl_easy_setopt(session, CURLOPT_URL, "http://192.168.123.123/banana.gif");
CURLcode curl_code = curl_easy_perform (session);
long http_code = 0;
curl_easy_getinfo(session, CURLINFO_RESPONSE_CODE, &http_code);
This is because you did not set the CURLOPT_WRITEDATA option:
The internal CURLOPT_WRITEFUNCTION will write the data to the FILE * given with this option, or to stdout if this option hasn't been set.
What you can do if you decide to completely ignore the response data is write it to /dev/null:
/* ... */
FILE *devnull = fopen("/dev/null", "w+");
curl_easy_setopt(session, CURLOPT_WRITEDATA, devnull);
CURLcode curl_code = curl_easy_perform(session);
fclose(devnull);
/* ... */
Another alternative is to use a NOOP write function:
curl_easy_setopt(session, CURLOPT_WRITEFUNCTION, noop_cb);
Where the write function simply returns the number of received bytes:
size_t noop_cb(void *ptr, size_t size, size_t nmemb, void *data) {
return size * nmemb;
}
The below code is to get response from a server using wsdl, here the problem is curl returns response but am unable to print it.
Error:
Failed writing body
Failed writing data
#include<stdio.h>
#include<string.h>
#include"../include/curl.h"
size_t write_data(void *ptr, size_t size, size_t count, void *stream)
{
/* ptr - your string variable.
stream - data chuck you received */
printf("%.*s", size, (char*)stream);
}
int main()
{
int res=0,i=0;
char buffer[4098]="",buff[128]="",buf[256]="",buf7[30]="",buf6[30]="",buf5[30]="";
char machineid[]="SUBANI";
char filename1[50]="";
int refno=0,paymode=0,taxtype=0;
FILE *fbc;
memset(filename1,0,sizeof(filename1));
sprintf(filename1,"/mnt/jffs2/Response_Details1.xml");
lk_dispclr();
lk_disptext(1,0,(unsigned char *)"Sending Request",0);
lk_disptext(2,0,(unsigned char *)"Please Wait",0);
memset(buffer,0,sizeof(buffer));
sprintf(buffer,"<?xml version=\"1.0\" encoding=\"utf-8\"?>\
<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:log=\"http://wsdlclassess.application.sims.test.com\">\
<soap:Header>\
</soap:Header>\
<soap:Body>\
<log:loginMethod>\
<log:loginid>%s</log:loginid>\
<log:password>%s</log:password>\
</log:loginMethod>\
</soap:Body>\
</soap:Envelope>","raja","test");
res=GET_FILE1(buffer,filename1);
return 0;
}
int GET_FILE1(char *buffer,char *filename)
{
CURL *curl;
CURLcode res;
struct curl_slist *headers = NULL;
FILE *out_fd = (FILE *) 0;
char errorbuf[300] = "",tmpbuff[128]="";
char errmsg[256];
int Timeout=120; //Default timeout is = 2 mins
int buffer_size = 0;
char urlbuff[256]="";
char mstr[10240];
memset(urlbuff,0,sizeof(urlbuff));
memset(tmpbuff,0,sizeof(tmpbuff));
buffer_size = strlen(buffer);
strcpy(tmpbuff,"http://10.10.1.111:8081/test_server/services/application?wsdl");
tmpbuff[strlen(tmpbuff)]='\0';
curl = curl_easy_init();
if(curl)
{
out_fd = fopen (filename, "w");
curl_easy_setopt(curl, CURLOPT_FILE, out_fd);
printf("%s:Sign-In Request\n", __func__);
headers = curl_slist_append(headers, "Content-type:application/soap+xml; charset=utf-8; action=\"http://wsdlclassess.application.sims.test.com/loginMethod\"");
curl_easy_setopt(curl, CURLOPT_URL, tmpbuff);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, mstr);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, buffer_size);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, buffer);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, Timeout);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER,errmsg);
printf("The Server%s:Performing Transaction.....\n",__func__);
res = curl_easy_perform(curl);
printf("res=after culreasey perform%d\n",res);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
printf("\nerrorbuf:%s\n",errmsg);
fclose(out_fd);
if(CURLE_OK != res)
{
puts("error occured is\n" );
//ppp_close();
return -1;
}
}
return 0;
}
The error is that you don't return the correct value from the function, in fact you don't return anything.
Also, the data provided to the function is actually the first ptr argument.
I agree that the documentation is not very clear, but it says:
The size of the data pointed to by ptr is size multiplied with nmemb, it will not be zero terminated.
The above line (emphasis mine) tells you that the data is in ptr which is the first argument in the function declaration provided in the documentation.
The documentation also states:
Return the number of bytes actually taken care of. If that amount differs from the amount passed to your function, it'll signal an error to the library. This will abort the transfer and return CURLE_WRITE_ERROR.
You don't return a value from the function, and so you have undefined behavior with a seemingly random value being returned causing the whole operation to fail. To fix this you should return size * count.
You also uses size to print the string, which is the size of the underlying type used (probably 1), your count variable is the number of characters read by CURL. To be fully working, without invoking more undefined behavior (since the data is not terminated) you should call printf like:
printf("%*.*s", size * count, size * count, ptr);