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 );
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.
Scenario:
Im writing a c++ program which should retrieve files and strings from an expressJS API..
Downloading txt.files with curlRequests works perfectly but as soon as i try to retrieve plain strings, they can only be used for printing..
Problem: When trying to do anything with the response from my GET request (from the expressjs api), my response doesnt get treated as a string.
string myText = curlRequest.GetText("/templates/names");
string myTextB = "react.txt, scss.txt"
cout << myText << endl; // prints"react.txt, scss.txt"
cout << myTextB << endl; // prints "react.txt, scss.txt"
cout << stringHelper.GetSubstringPos(myText, "scss") << endl; // printsstring::npos
cout << stringHelper.GetSubstringPos(myTextB, "scss") << endl; // printsposition of 's' as expected
Here is my GetText function of the curlrequest.h in c++
string GetText(string ACTIONURL) {
CURL* curl;
CURLcode curlRes;
string res;
string url = templateCreator.APIURL + ACTIONURL;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
curlRes = curl_easy_perform(curl);
res = curlRes;
if (curlRes == CURLE_HTTP_RETURNED_ERROR) {
res = "FAILED";
}
else if (curlRes != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(curlRes));
res = "FAILED";
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return res;
}
Here is the route in expressjs (receiving the request and responding with a string)
router.get('/names', function(req, res, next) {
var str = "react.txt, scss.txt, example.txt";
res.send(str);
});
// I also tried sending a buffer but as its sended as string i face the same problem..
// C++ could decode the buffer ..
router.get('/buf', function(req, res, next) {
let data = 'hello world: (1 + 2 !== 4)';
let buff = new Buffer.from(data);
let base64data = buff.toString('base64');
console.log(base64data);
res.send(base64data);
});
Retrieving textfiles from my expressjs API is not a problem..
void GetFile(string ACTIONURL, string OUTDIR) {
CURL* curl;
FILE* fp;
CURLcode res;
string url = templateCreator.APIURL + ACTIONURL;
curl = curl_easy_init();
if (curl)
{
fopen_s(&fp, OUTDIR.c_str(), "wb");
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
return;
}
(After download I can read those line by line and store in a vector).
Still im hoping to get sending actual strings working..
Does anyone have a clue why im facing problems here?
I literally spent days on this unexpected 'small' issue already..
Thank you #n.'pronouns'm.
I Updated my function and realised that res = curlRes was a nobrainer..
Also the checks for valid response is gone for now.
//those 2 lines and a write_to_string function were missing and `res = curlRes` should do their job
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_string);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
The following 2 functions replace my GetText function now, and everything works as expected.
size_t write_to_string(void* ptr, size_t size, size_t count, void* stream) {
((string*)stream)->append((char*)ptr, 0, size * count);
return size * count;
}
string GetText(string ACTIONURL) {
CURL* curl;
CURLcode res;
string response;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, templateCreator.APIURL + ACTIONURL.c_str());
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_string);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
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);
}
return response;
}
Thank you! I found the fix on 1 or 2 questions earlier too but was not aware that this was the actual problem. Working with strings is possible now!
I'm trying to load the contents of this URL in order to send an SMS;
https://app2.simpletexting.com/v1/send?token=[api key]&phone=[phone number]&message=Weather%20Alert!
Using this bit of code implementing libcurl:
std::string sendSMS(std::string smsMessage, std::string usrID) {
std::string simplePath = "debugOld/libDoc.txt";
std::string preSmsURL = "https://app2.simpletexting.com/v1/send?token=";
std::cout << "\n" << getFile(simplePath) << "\n";
std::string fullSmsURL = preSmsURL + getFile(simplePath) + "&phone=" + usrID + "&message=" + smsMessage;
std::cout << fullSmsURL;
//Outputs URL contents into a file
CURL *curl;
FILE *fd;
CURLcode res;
char newFile[FILENAME_MAX] = "debugOld/noSuccess.md";
curl = curl_easy_init();
if (curl) {
fd = fopen(newFile, "wb");
curl_easy_setopt(curl, CURLOPT_URL, fullSmsURL);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fd);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fd);
}
}
I've used pretty much this exact code before to save the JSON contents of a URL to a file, although I'm trying something a bit different here.
This URL will actually send an SMS when visited. When using curl in a cli, I have no problem doing this. Although from C++, it doesn't treat anything as a error, just perhaps the actual function to send the sms isn't being actuated in the same way it would had I visited the URL physically.
I've scoured google for some kind of a solution to no avail. Perhaps I'm too novice to curl to know exactly what to search for.
Edit #1: getFile function
//Read given file
std::string getFile(std::string path) {
std::string nLine;
std::ifstream file_(path);
if (file_.is_open()) {
while (getline(file_, nLine)) {
return nLine;
}
file_.close();
}
else {
std::cout << "file is not open" << "\n";
return "Error 0x000001: inaccesable file location";
}
return "Unknown error in function 'getFile()'"; //This should never happen
}
This line is wrong:
curl_easy_setopt(curl, CURLOPT_URL, fullSmsURL);
CURLOPT_URL expects a char* pointer to null-terminated C string, not a std::string object. You need to use this instead:
curl_easy_setopt(curl, CURLOPT_URL, fullSmsURL.c_str());
Also, you are not performing any error checking on the return values of getFile(), fopen(), or curl_easy_perform() at all. So, your code could be failing in any one of those places and you would never know it.
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.
I have a problem when using CURL to get MJPEG Stream from IP Camera Panasonic.
Here is my code.
int _tmain(int argc, _TCHAR* argv[])
{
CURL *curl;
CURLcode res;
/* Minimalistic http request */
char request[1000];
strcpy(request, "GET /nphMotionJpeg?Resolution=640x480&Quality=Standard HTTP/1.1\r\n\r\n") ;
curl_socket_t sockfd; /* socket */
long sockextr;
size_t iolen;
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL, "http://192.168.1.253");
curl_easy_setopt(curl, CURLOPT_USERPWD, "my_usr:my_pass");
/* Do not do the transfer - only connect to host */
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L);
res = curl_easy_perform(curl);
if(CURLE_OK != res)
{
printf("Error: %s\n", strerror(res));
return 1;
}
res = curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockextr);
if(CURLE_OK != res)
{
printf("Error: %s\n", curl_easy_strerror(res));
return 1;
}
sockfd = sockextr;
/* wait for the socket to become ready for sending */
if(wait_on_socket(sockfd, false, 6000L) < 0)
{
printf("Error: timeout.\n");
return 1;
}
iolen = 0;
res = curl_easy_send(curl, request, strlen(request), &iolen);
if(CURLE_OK != res)
{
printf("Error: %s\n", curl_easy_strerror(res));
return 1;
}
//puts("Reading response.");
/* read the response */
for(;;)
{
char* buf = new char[1024*100];
iolen = 0;
wait_on_socket(sockfd, true, 60000L);
res = curl_easy_recv(curl, buf, 1024*100, &iolen);
if(CURLE_OK != res)
break;
fstream f;
f.open("out.txt", ios::out|ios::binary|ios::app);
if(!f.fail())
{
f.write(buf,iolen);
f.close();
}
delete []buf;
}
/* always cleanup */
curl_easy_cleanup(curl);
}
return 0;
}
The result is content in buf after the first loop as description of Panasonic document.
But it only have 3 frames jpeg in data responding. and total size only is 3056 bytes. That mean is about 1KB/Jpeg image.It is wrong.
And in the second loop, the curl_easy_recv() always return CURLE_UNSUPPORTED_PROTOCOL.
I also change the request string same as description of Panasonic document:
"GET http://xxx.xxx.xxx.xxx:yy/nphMotionJpeg?Resolution=640x480&Quality=Standard HTTP/1.0\r\n"
OR
"GET http://usr:pass#xxx.xxx.xxx.xxx:yy/nphMotionJpeg?Resolution=640x480&Quality=Standard HTTP/1.0\r\n"
--> it will response "BAD REQUEST".
The model of my camera is Panasonic BL-C111CE.
Here's a much better example source code you could start from:
http://curl.haxx.se/libcurl/c/url2file.html
You really SHOULD avoid CURLOPT_CONNECT_ONLY, curl_easy_recv() and curl_easy_send() unless you know PERFECTLY well what you're doing and why the "normal" API isn't good enough. More often than otherwise they are the wrong answer to your problems. libcurl does HTTP perfectly fine on its own already!
Nitpick: at one point you call strerror() on a return code from libcurl, which won't show the correct error string...
I have found out the problem.
The reason with command "GET /nphMotionJpeg?Resolution=640x480&Quality=Standard HTTP/1.1\r\n\r\n" is not permitted to access video.
I solve it by changing the setting in my camera that can permit Guest user access to video.
Thanks all!