cURL - Uploading string with a "+" will replace "+" with " " - c++

Yeah weird title, I don't know what it should be.
I have a dll, and after a long time trying and finally figuring out how I could upload things to a webhook, it finally works. But, when I try to upload a string which contains a +, the + will be replaced with a " ".
Example:
Uploaded: FRvERUb9xgMlP4BS+bm+t+CLI6cG026vbtev2gSbBYM=
Received: FRvERUb9xgMlP4BS bm t CLI6cG026vbtev2gSbBYM=
I want to know how I can upload it so that the thing will also send the characters +.
The code for uploading:
void sendmsg() {
CURL* curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "my webhook");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "content=FRvERUb9xgMlP4BS+bm+t+CLI6cG026vbtev2gSbBYM=");
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}

This POST is a normal application/x-www-form-urlencoded kind (and libcurl will set that Content-Type by default when this option is used), which is commonly used by HTML forms.
You can use curl_easy_escape to url-encode your data, if necessary. It returns a pointer to an encoded string that can be passed as postdata.
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "my webhook");
char* content = curl_easy_escape(curl, "FRvERUb9xgMlP4BS+bm+t+CLI6cG026vbtev2gSbBYM=", 0);
if (content) {
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, ("content="s + content).c_str());
curl_free(content);
res = curl_easy_perform(curl);
}
curl_easy_cleanup(curl);
}

Related

C++ cant use respond string from GET requests retrieved by ExpressJS API for anything else than printing

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!

cURL send POST data-binary

I have cURL command:
curl "https://pelevin.gpt.dobro.ai/generate/" --data-binary '{"prompt" : "text" , "length" : 40}
and i want to do the same thing in C++.
I tried:
string params = "prompt=text&length=40";
string buffer;
CURL* curl;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://pelevin.gpt.dobro.ai/generate/");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, params.size());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, params.c_str());
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
curl_easy_perform(curl);
}
curl_easy_cleanup(curl);
and got message like this:
{"detail":[{"loc":["body",0],"msg":"Expecting value: line 1 column 1 (char 0)","type":"value_error.jsondecode","ctx":{"msg":"Expecting value","doc":"prompt=text&length=40","pos":0,"lineno":1,"colno":1}}]}
How can the request be made correctly?
When you went from command-line to libcurl, you changed the data you provide. Why?
First it was a JSON object string (which seems to be what the server is expecting). Now you've made it an URL-encoded string.
Just pass the right data to the server, as you did in the first example.
string params = "{\"prompt\" : \"text\" , \"length\" : 40}";
N.B. there are "better" ways to format that in modern C++, if you're up for it:
string params = R"RAW({"prompt" : "text" , "length" : 40})RAW";
BTW, it's weird to use --data-binary with data specified on the command-line. It's intended for use with file input, as it prevents newlines being stripped from the file.

What is the correct BING Api POST url for batch url submission, using libcurl?

I am trying to submit a list of urls to BING webmaster. IN CPP
According to BING:
What is URL Submission API?
Easy to plug-in API solution that websites can call to notify Bing whenever
website contents is updated or created allowing instant crawling, indexing
and discovery of your site content.
I understand that need to send a POST request, JSON request sample:
POST /webmaster/api.svc/json/SubmitUrlbatch?​apikey=sampleapikeyEDECC1EA4AE341CC8B6 HTTP/1.1​
Content-Type: application/json; charset=utf-8​
Host: ssl.bing.com​
{
"siteUrl":"http://yoursite.com",​
"urlList":[
"http://yoursite.com/url1",
"http://yoursite.com/url2",
"http://yoursite.com/url3"
]
}
I have written the following, using libcurl to send a POST request.
std::string curl_post_json(const std::string url, const std::string json) {
CURL* curl;
CURLcode res;
std::string ret;
struct curl_slist* header = NULL;
std::string content_len = "Content-Length: " + std::to_string(json.size());
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
header = curl_slist_append(header, "Content-Type: application/json; charset=utf-8");
header = curl_slist_append(header, content_len.c_str());
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ret);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
ret = curl_easy_strerror(res);
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return ret;
note: write_response is a simple function (pointer) to copy response to string.
I use the following url:
https://ssl.bing.com/webmaster/api.svc/json/SubmitUrlbatch?apikey=<mykey>
but, receive:
{"ExceptionType"="System.InvalidOperationException","Message"="Authenticationfailed.","StackTrace"=null**strong text**}
What is the proper url for the POST submission?
I have the function working now. Needed to add the above.
header = curl_slist_append(header,"Host: ssl.bing.com");
Also the urlList field must not be empty, if it is empty an error response is returned.

libcurl post - underscore replaced with space (ubuntu)

I'm trying to send email and password in a post request to my express nodejs server using curl c++. The post data has '_' changed to ' ' when I log it from server.
char emailtext[50];
char passwordtext[50];
int emailstrlen = wcstombs(emailtext, email->getText(), 50);
int passwordstrlen = wcstombs(passwordtext, password->getText(), 50);
long totalsize = emailstrlen + passwordstrlen;
strcat(emailtext, ":");
strcat(emailtext, passwordtext);
// "myemail#yahoo.com:mypassword\0"
curl_global_init(CURL_GLOBAL_ALL);
CURLcode res;
CURL* curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
curl_easy_setopt(curl, CURLOPT_URL, "https://localhost:3000/login");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, "Content-Type:text/plain; charset=utf-8");
cout << emailtext << endl;
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, emailtext);
/* pass our list of custom made headers */
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_perform(curl); /* post away! */
curl_slist_free_all(headers); /* free the header list */
curl_global_cleanup();
return true;
express nodejs server:
app.post('/login', bodyParser.text(), function (req, res) {
console.log("we got the post request for /login");
console.log("logging the body!");
console.log(req.body);
res.header('Content-type', 'text/plain');
return res.end('<h1>Hello, Secure World!</h1>');
});
say char* emailtext = "cool_stewj#yahoo.com:lololol"
output from log on server will then be "cool stewj#yahoo.com:lololol"
What's happening? I've tried url encoding the data which turned out to be silly and pointless. How do I get that underscore?
The terminal in ubuntu apparently doesn't show underscores, so any log output will have underscores replaced with spaces.
Btw that's a bad example of string code in beginning. Possible to just use one buffer. Just pointing it out.

Using libcurl in C++ application to send POST data from value in .INI file

So I have a C++ application that takes a value from a key in a settings.INI file, uses libcurl to reach out to a PHP page, post that data, and it should return some different data.
And it actually works, aside from grabbing the data from the INI file. If I explicitely type in for the POSTFIELDS option for libcurl (e.i.: "Serial=454534" , instead of the variable storing the data that it retrived from my file).
Here's some code..
PHP:
<?
include("DBHeader.inc.php");
$Serial= $_POST['Serial'];
$sql = "SELECT * FROM `LockCodes` WHERE `sLockCode`=\"$Serial\"";
$RS=mysql_query($sql, $SQLConn);
$num_rows=mysql_num_rows($RS);
if ($num_rows>0)
{
while ($row = mysql_fetch_array($RS))
{
echo $row['iLockCodeID'];
}
}
else
{
echo "...";
}
?>
Snippet of C++ code:
TCHAR szKeyValue[36];
GetPrivateProfileString(_T("Test"), _T("LockCode"), _T(""), szKeyValue, 36, _T("C:\\Test\\Settings.INI"));
CString sLockCode = szKeyValue;
CURL *curl;
CURLcode res;
CString Serial = _T("Serial=") + sLockCode;
string LCID;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_URL, "http://regserver2.nyksys.com/GetLCID.php");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, Serial);
res = curl_easy_perform(curl);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCallback);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
if (data == _T("..."))
{
data = "0";
AfxMessageBox("Invalid Serial Number");
exit(0);
}
My Settings.INI is in the standard format..
[Test]
LockCode=1D4553C7E7228E462DBAAE267977B7CDED8A
What happens is whenever I use the variable "Serial" instead of typing it in, the PHP page returns "..." instead of the desired result.
I feel like I'm missing something obvious. Any help would be TREMENDOUSLY appreciated.
I am guessing that you have _UNICODE defined, which means that TCHAR is wchar_t, CString is CStringT<wchar_t>, and the code:
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, Serial);
actually passes a wide char string to curl_easy_setopt() when the function is expecting a narrow char string. If your machine is little Endian, then curl_easy_setopt() interprets the parameter as the string "S\x00e\x00r\x00i\x00... (on a Big Endian machine, it's "\x00S\x00e\x00r\x00i...) and because curl_easy_setopt() will use strlen() when CURLOPT_POSTFIELDSIZE is not set, the entire POST request body on a little Endian machine is S. See, for example, http://codepad.org/JE2MYZfU
What you need to do is use narrow char strings:
#define ARRAY_LEN(arr_id) ((sizeof (arr_id))/(sizeof ((arr_id)[0])))
char szKeyValue[36];
GetPrivateProfileStringA("Test", "LockCode", "", szKeyValue, ARRAY_LEN(szKeyValue), "C:\\Test\\Settings.INI");
CURL *curl;
CURLcode res;
CStringT<char> Body = CStringT<char>("Serial=") + szKeyValue;
string LCID;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_URL, "http://regserver2.nyksys.com/GetLCID.php");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, Body);
res = curl_easy_perform(curl);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCallback);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
//...
Also, your PHP script looks vulnerable to SQL injection.
EDIT: One more thing. Are you setting CURLOPT_POST to 1?
curl_easy_setopt(curl, CURLOPT_POST, 1);