cURL send POST data-binary - c++

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.

Related

curl PUT JSON body having issues processing array's

I'm developing a library that communicates with a REST API. The method I wrote for PUT calls has worked up until this point.
void Command::put(const std::string url, const std::string body)
{
CURLcode ret;
struct curl_slist *slist1;
slist1 = NULL;
// slist1 = curl_slist_append(slist1, "Content-Type: multipart/form-data;");
slist1 = curl_slist_append(slist1, "Content-Type: application/json;");
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)12);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist1);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.79.1");
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
curl_easy_setopt(curl, CURLOPT_SSH_KNOWNHOSTS, "/Users/$USER/.ssh/known_hosts");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_easy_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, 1L);
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
ret = curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl = NULL;
curl_slist_free_all(slist1);
slist1 = NULL;
}
curl_easy_cleanup(curl);
curl = nullptr;
curl_slist_free_all(slist1);
slist1 = NULL;
}
However, it seems to not work with arrays. Below are three debug put calls and their output. The first two work. The third does not.
// *** DEBUG ***
commandWrapper->put(
"http://192.168.1.7/api/x1CAh16Nr8NcKwgkUMVbyZX3YKFQrEaMAj5pFz0Z/lights/6/state", "{\"on\":true}");
commandWrapper->put(
"http://192.168.1.7/api/x1CAh16Nr8NcKwgkUMVbyZX3YKFQrEaMAj5pFz0Z/lights/6/state", "{\"bri\":123}");
commandWrapper->put(
"http://192.168.1.7/api/x1CAh16Nr8NcKwgkUMVbyZX3YKFQrEaMAj5pFz0Z/lights/6/state", "{\"xy\":[0.04,0.98]}");
[{"success":{"/lights/6/state/on":true}}]
[{"success":{"/lights/6/state/bri":123}}]
[{"error":{"type":2,"address":"/lights/6/state","description":"body contains invalid json"}}]2022-04-02T02:56:47.220Z - Living Room 1 - Status: 0
I'm a little lost on how to proceed. I verified that this command does work on the command line.
You need to change this line:
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)12);
To this instead:
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)body.size());
Or, simply omit CURLOPT_POSTFIELDSIZE_LARGE completely, since body.c_str() is a pointer to a null-terminated string in this situation. Curl can determinant the size for you.
The JSON {xy:[0.04,0.98]} is 16 characters, longer than the 12 characters you are claiming, so you are truncating the JSON during transmission, which is why the server thinks it is invalid. Both {"on":true} and {"bri":123} are 11 characters (12 if you count the null terminator), so they are not truncated.

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

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);
}

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.

error when creating folder on dropbox via libcurl

I am trying to make a c++ application that uses Dropbox. When I am trying to create a new folder I take the following error: {"error": "Not Found"} and "HTTP/1.1 404 Not Found".
I have followed the instruction of Dropbox's REST Api, so I have used the POST method. (I am not sure if I can use PUT, instead).
Here is my code:
void createDirectory(const char *new_Directory_Path)
{
string create_Directory_Url = "https://api.dropbox.com/1/fileops/create_folder/sandbox/test_folder";
CURL* curl = curl_easy_init();
if(!curl)
{
CURLcode res;
struct curl_slist *headers = NULL;
string my_header = "Authorization: OAuth oauth_version=\"1.0\", oauth_signature_method=\"PLAINTEXT\",
oauth_consumer_key=\""
+ m_consumer_key + "\", oauth_token=\"" + m_accessKey + "\", oauth_signature=\""
+ m_consumer_secret + "&" + m_accessSecret + "\"";
headers = curl_slist_append(headers, my_header.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(curl, CURLOPT_URL, create_Directory_Url.c_str());
res = curl_easy_perform(curl);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
}
Please, does anyone have an idea about what am I doing wrong?
Looking at the documentation I belive you want to leave sandbox/test_folder off of the url and use root=sandbox & path=test_folder as form values.
Here is an example of submitting form parameters: http://curl.haxx.se/libcurl/c/postit2.html
EDIT:
I found another example, here: https://github.com/geersch/DropboxRESTApi/blob/master/src/part-3/README.md which seems to show the parameters being added to the url's query string rather than as form values. The doc page isn't clear. It just shows them as parameters but doesn't specify which kind.

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);