Posting String To Web Server, Get Response - c++

I have a long base64 encoded text string. It's about 1024 characters. From my Objective C code, I want to send it to my PHP script, have it dump it to a log, and return an "OK" response back. I tried this cookbook example, but it only has an example of upload and download (not both combined), and it doesn't even work in my case.
I'd be willing to switch this to a C++ solution if I knew how.
The Objective C Client Code (command line client)
NSString *sMessage = #"My Long Base64 Encoded Message";
NSString *sURL = "http://example.com/request.php";
NSURL *oURL = [NSURL URLWithString:sURL];
NSData *data = [NSData dataWithBytes:sMessage.UTF8String length:sMessage.length];
NSURLSessionDataTask *downloadTask = [[NSURLSession sharedSession]
dataTaskWithURL:oURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(#"\n\nDATA\n\n%#",data);
NSLog(#"\n\nRESPONSE\n\n%#",response);
NSLog(#"\n\nERROR\n\n%#",error);
}];
[downloadTask resume];
The PHP Web Server Code
<?php
error_reporting(E_ALL);
ini_set('display_errors','On');
$sRaw = file_get_contents('php://input');
file_put_contents('TEST.TXT',$sRaw);
die('OK');

There's a far easier route using ordinary C++. You'll have to convert your .m file to a .mm file in order to be able to mix Objective C and C++ code.
The PHP code is good and doesn't require a revision. Here's the C++ example I used that worked. It used the STL and curl. I was doing this on a Mac, and by default OSX has the curl libraries pre-installed. Note that the example below is synchronous -- it jams program execution until the server call is completed. (I desired this in my case -- you may not.)
The C++ Client Code (class)
#pragma once
#include <string>
#include <sstream>
#include <iostream>
#include <curl/curl.h>
class Webby {
public:
static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) {
std::string buf = std::string(static_cast<char *>(ptr), size * nmemb);
std::stringstream *response = static_cast<std::stringstream *>(stream);
response->write(buf.c_str(), (std::streamsize)buf.size());
return size * nmemb;
}
static std::string sendRawHTTP(std::string sHostURL, std::string &sStringData) {
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
std::stringstream response;
curl_easy_setopt(curl, CURLOPT_URL, sHostURL.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, sStringData.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Webby::write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
return response.str();
}
return "";
}
}; // end class

Related

It is necessary to send alerts to Telegram bot from Teams using webhook_url

The program will hang on the server and work around the clock. It is necessary to catch the received message in the Microsoft-Teams group (channel) via webhook_url and send at least one message (preferably a whole message) to the bot's Telegram. I write code on the pluses as I don’t know anything other than them. Below is the code. The code did not work for me, if there are any ideas or suggestions on how to improve what I am certainly happy about. Thanks in advance.
#include <iostream>
#include <string>
#include <curl/curl.h> // Library for working with HTTP requests
using namespace std;
// Incoming webhook URL created in Teams
const string webhook_url = "Webhook from Tims";
// Telegram bot access token
const string bot_token = "Bot token";
// Telegram chat ID where to send notifications
const string chat_id = "Bot name";
size_t write_callback(void* ptr, size_t size, size_t nmemb, void* userdata) {
// The callback function used to store the server's response
string* response = (string*)userdata;
response->append((char*)ptr, size * nmemb);
return size * nmemb;
}
void send_webhook_message(const string& text, const string& bot_token, const string& chat_id) {
// Function for sending a webhook message through a bot in Telegram
CURL* curl = curl_easy_init();
if (curl) {
string response;
curl_easy_setopt(curl, CURLOPT_URL, ("https://t.me/ITSys****" + bot_token + "/sendMessage").c_str());
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ("chat_id=" + chat_id + "&text=" + text).c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
}
int main ()
{for ( ; ; )
{
}
return 0;
}

How to obtain the URL of re-directed webpage in C++

I did write a c++ code which automatically parses a webpage and open and parse some of their links. The point is that in these webpage there are some addresses which were redirected to other webpages. For example, when I try to open:
https://atlas.immobilienscout24.de/property-by-address?districtId=1276001006014
I ended up opening:
https://atlas.immobilienscout24.de/orte/deutschland/baden-württemberg/böblingen-kreis/leonberg
How could I get the url of the second page in C++?
In that particular case, it's given by the Location header in a 301 ("Moved Permanently") response (according to Chrome's Developer Tools).
If you set FOLLOWLOCATION to 0, you can prevent libcurl from following redirects, and then just examine the headers of the original response (or, probably better, query REDIRECT_URL for the information).
(Then you can perform a new request to the alternative URL, if you like.)
The default for this is 0, though, so you must be setting it to 1 yourself currently.
you could use CURLOPT_HEADERFUNCTION to inspect the headers and parse out the Location header, eg
#include <iostream>
#include <cstring>
#include <curl/curl.h>
size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata){
const std::string needle="Location: ";
if(nitems>needle.size()){
if(std::memcmp(&needle[0],buffer,needle.size()) == 0 ){
//todo: verify that im not off-by-one below.
((std::string*)userdata)->assign(&buffer[needle.size()],nitems-needle.size());
}
}
return nitems;
}
int main(int argc, char *argv[])
{
CURLcode ret;
CURL *hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_URL, "https://atlas.immobilienscout24.de/property-by-address?districtId=1276001006014");
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(hnd, CURLOPT_NOBODY, 1L);
std::string redirect_url;
curl_easy_setopt(hnd,CURLOPT_HEADERDATA,&redirect_url);
curl_easy_setopt(hnd,CURLOPT_HEADERFUNCTION,header_callback);
ret = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
hnd = NULL;
std::cout << redirect_url;
return (int)ret;
}
.. but if you want the final url (in case of multiple redirects), rather than just "the second url", you should probably use CURLOPT_FOLLOWLOCATION and CURLINFO_EFFECTIVE_URL instead, eg
#include <iostream>
#include <cstring>
#include <curl/curl.h>
int main(int argc, char *argv[])
{
CURLcode ret;
CURL *hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_URL, "https://atlas.immobilienscout24.de/property-by-address?districtId=1276001006014");
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(hnd, CURLOPT_NOBODY, 1L);
curl_easy_setopt(hnd,CURLOPT_FOLLOWLOCATION,1L);
ret = curl_easy_perform(hnd);
char *lolc;
curl_easy_getinfo(hnd, CURLINFO_EFFECTIVE_URL, &lolc);
std::string final_url(lolc);
curl_easy_cleanup(hnd);
hnd = NULL;
std::cout << final_url;
return (int)ret;
}
this approach is slower (have to do at least 1 more request upon redirect), but much simpler to implement and works on both redirected urls and non-redirected urls and multiple-redirected-urls alike.

Downloading a file to Ubuntu with libcurl C++, simple example doesn't work

I'm trying to use libcurl with C++ to download a single image file to my Ubuntu machine.
I tried copying and pasting the simple example shown in this question: Download file using libcurl in C/C++
#include <stdio.h>
#include <curl/curl.h>
#include <string>
using namespace std;
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(void) {
CURL *curl;
FILE *fp;
CURLcode res;
const char *url = "https://i.imgur.com/mWj0yzI.jpg";
char outfilename[FILENAME_MAX] = "/home/my_username/test.jpg";
curl = curl_easy_init();
if (curl)
{
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}
I expected it to download the image file and save it as "test.jpg" on my machine. However, when I run this program, "test.jpg" is 0 bytes in size. Apparently the image didn't write to the file for some reason.
What am I doing wrong?
Your link does not point to an existing file. With a correct link it works for me. Try this:
const char *url = "https://i.imgur.com/oRtvmGT.jpg";

Problems with libcurl and JSON for Modern C++

I'm trying to teach myself C++ by writing a simple program that sends a cURL request to a JSON API, parses the data and then stores it either in a text document or database for a web application to access. I have done this task in PHP and figured C++ wouldn't be much harder but I can't even get cURL to return a string and display it.
I get this to compile with no errors, but the response "JSON data: " doesn't display anything where the JSON data should be.
Where did I go wrong? URL-to-API is the actual URL, so I believe I'm using a wrong setopt function, or not setting one. In PHP, "CURLOPT_RETURNTRANSFER" made it return as a string, but I get an error with it:
error: ‘CURLOPT_RETURNTRANSFER’ was not declared in this scope
curl_easy_setopt(curl, CURLOPT_RETURNTRANSFER, true);
I'm using g++ compiler on Ubuntu and added -lcurl to the command line argument.
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
#include <curl/curl.h>
//#include "json.hpp"
using namespace std;
//using json = nlohmann::json;
size_t WriteCallback(char *contents, size_t size, size_t nmemb, void *userp) {
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
string getJSON(string URL) {
CURL *curl;
CURLcode res;
string readBuffer;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_HEADER, 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true); // follow redirect
//curl_easy_setopt(curl, CURLOPT_RETURNTRANSFER, true); // return as string
curl_easy_setopt(curl, CURLOPT_HEADER, false);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_URL, URL);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
return readBuffer;
}
return 0;
}
int main() {
string data = getJSON("URL-to-api");
cout << "JSON Data: \n" << data;
return 0;
}
When I uncomment the JSON for Modern C++ include and namespace line I get this error:
error This file requires compiler and library support for the ISO C++ 2011 standard. This support must be enabled with the -std=c++11 or -std=gnu++11 compiler options.
Along with a bunch of errors for functions in that library. I just downloaded the most recent version of g++ before embarking on this project, so what do I need to do?
I'm using g++ 5.4.0 on Ubuntu.
UPDATE:
So I added a check under res = curl_easy_perform(curl) and it doesn't return the error message, and res gets displayed as 6. This seems to be much more difficult than it should be:
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
#include <curl/curl.h>
//#include "json.hpp"
using namespace std;
//using json = nlohmann::json;
size_t WriteCallback(char *contents, size_t size, size_t nmemb, void *userp) {
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
string getJSON(string URL) {
CURL *curl;
CURLcode res;
string readBuffer;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_HEADER, 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true); // follow redirect
curl_easy_setopt(curl, CURLOPT_HEADER, false);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_URL, URL);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
res = curl_easy_perform(curl);
cout << res << endl;
if (!res) {
cout << "cURL didn't work\n";
}
/* always cleanup */
curl_easy_cleanup(curl);
curl = NULL;
return readBuffer;
}
}
int main() {
string data = getData("");
cout << "JSON Data: \n" << data;
return 0;
}
I get the following output when I run the program:
6
JSON Data:
In PHP "CURLOPT_RETURNTRANSFER" made it return as a string but I get an error:
error: ‘CURLOPT_RETURNTRANSFER’ was not declared in this scope
curl_easy_setopt(curl, CURLOPT_RETURNTRANSFER, true);
There is no CURLOPT_RETURNTRANSFER option documented for curl_easy_setopt(). I think that is an option specify to PHP's curl_exec() function, which doesn't exist in CURL itself. CURLOPT_WRITEFUNCTION is the correct way to go in this situation.
When I uncomment the JSON for Modern C++ include and namespace line I get:
error This file requires compiler and library support for the ISO C++ 2011 standard. This support must be enabled with the -std=c++11 or -std=gnu++11 compiler options.
The error is self-explanatory. Your JSON library requires C++11 but you are not compiling with C++11 enabled. Some modern compilers still default to an older C++ version (usually C++98) and require you to explicitly enable C++11 (or later) when invoking the compiler on the command line, or in your project makefile configuration.
In the case of g++, the current version (8.2) defaults to (the GNU dialect of) C17 for C and C++14 for C++, if not specified otherwise via the -std parameter. Your version (5.4) defaults to (the GNU dialect of) C11 and C++98, respectively.
UPDATE: there are other mistakes in your code:
You are passing a std::string object to curl_easy_setopt() where a char* pointer is expected for CURLOPT_URL. You need to change this:
curl_easy_setopt(curl, CURLOPT_URL, URL);
To this instead:
curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
You are not testing the return value of curl_easy_perform() correctly. Per the documentation, curl_easy_perform() returns 0 (CURLE_OK) on success, and non-zero on error, so you need to change this:
if (!res)
To this instead:
if (res != CURLE_OK)
So I added a check under res = curl_easy_perform(curl) ..., and res gets displayed as 6.
That is CURLE_COULDNT_RESOLVE_HOST, which makes sense as your updated example is passing a blank URL to getJSON():
string data = getJSON(""); // should be "URL-to-api" instead!

Downloading multiple files with libcurl in C++

I am currently trying to make an updater for my software project. I need it to be able to download multiple files, I don't mind if they download in sync or one after each other, whatever is easier (file size is not an issue). I followed the example from the libcurl webpage and a few other resources and came up with this:
#include <iostream>
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(void){
for (int i = 0; i < 2;){ //download 2 files (loop twice)
CURL *curl;
FILE *fp;
CURLcode res;
char *url = "http://sec7.org/1024kb.txt"; //first file URL
char outfilename[FILENAME_MAX] = "C:\\users\\grant\\desktop\\1024kb.txt";
curl = curl_easy_init();
if (curl){
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
url = "http://sec7.org/index.html"; //I want to get a new file this time
outfilename[FILENAME_MAX] = "C:\\users\\grant\\desktop\\index.html";
}
return 0;
}
The first issue is if i remove the new file assignments (*url = "http://...") and just try to loop the download code twice, the program simply stops responding. This occurs in any combination of the download being called more than once in the program. The other issue is that I am unable to change the value of the character array outfilename[FILENAME_MAX]. I feel like this is just some silly error I am making but no solution comes to mind. Thank you!
Why not put this in a function and call it twice?
Your syntax for the arrays is all wrong, plus all the variables inside the loop are local, which means they are destroyed after each loop iteration.
What Conspicuous Compiler said. That's what's causing your program to freeze; it's stuck in an infinite loop because i is never > 2.
Put your code into a function like so:
void downloadFile(const char* url, const char* fname) {
CURL *curl;
FILE *fp;
CURLcode res;
curl = curl_easy_init();
if (curl){
fp = fopen(fname, "wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
}
And call it twice with the relevant file names and urls:
downloadFile("http://sec7.org/1024kb.txt", "C:\\users\\grant\\desktop\\1024kb.txt");
downloadFile("http://sec7.org/index.html", "C:\\users\\grant\\desktop\\index.html");
The example function is very bad though, it's just an example. You should alter it to return error codes/throw exceptions, and stuff like that.