I'm able to execute a http POST request using curl via boost::process::child by passing the entire command line. However, I would like to pass the arguments via boost::process::args but I cannot get it work.
This works:
const std::string cmdDiscord = "curl -X POST https://discord.com:443/api/webhooks/1234567890 -H \"content-type: application/json\" -d \"{\"content\": \"test\"}\"";
boost::process::child c(cmdDiscord); // this works
boost::process::child c(boost::process::cmd = cmdDiscord); // strangely, this doesn't work
I want to use boost::process::args but this fails:
std::vector<std::string> argsDiscord {"-X POST",
"https://discord.com:443/api/webhooks/1234567890",
"-H \"content-type: application/json\"",
"-d \"{\"content\": \"test\"}\""};
boost::process::child c(boost::process::search_path("curl"), boost::process::args (argsDiscord));
The error is curl: (55) Failed sending HTTP POST request which is quite a vague error message. I couldn't find any examples calling curl. Does anyone have any suggestions on getting this to work?
It should be
std::vector<std::string> argsDiscord {"-X", "POST",
"https://discord.com:443/api/webhooks/1234567890",
"-H", "content-type: application/json",
"-d", "{\"content\": \"test\"}"};
Since command interpretators pass arguments like -X POST are two arguments, not one.
The double quotes are a shell syntax as well. The shell interprets (removes) them during command line expansion.
Alternatively curl accepts adjacent values in short options (without space)
std::vector<std::string> argsDiscord {"-XPOST",
"https://discord.com:443/api/webhooks/1234567890",
"-H", "content-type: application/json",
"-d", "{\"content\": \"test\"}"};
I would like to use cURL to not only send data parameters in HTTP POST but to also upload files with specific form name. How should I go about doing that ?
HTTP Post parameters:
userid = 12345
filecomment = This is an image file
HTTP File upload:
File location = /home/user1/Desktop/test.jpg
Form name for file = image (correspond to the $_FILES['image'] at the PHP side)
I figured part of the cURL command as follows:
curl -d "userid=1&filecomment=This is an image file" --data-binary #"/home/user1/Desktop/test.jpg" localhost/uploader.php
The problem I am getting is as follows:
Notice: Undefined index: image in /var/www/uploader.php
The problem is I am using $_FILES['image'] to pick up files in the PHP script.
How do I adjust my cURL commands accordingly ?
You need to use the -F option:
-F/--form <name=content> Specify HTTP multipart POST data (H)
Try this:
curl \
-F "userid=1" \
-F "filecomment=This is an image file" \
-F "image=#/home/user1/Desktop/test.jpg" \
localhost/uploader.php
Catching the user id as path variable (recommended):
curl -i -X POST -H "Content-Type: multipart/form-data"
-F "data=#test.mp3" http://mysuperserver/media/1234/upload/
Catching the user id as part of the form:
curl -i -X POST -H "Content-Type: multipart/form-data"
-F "data=#test.mp3;userid=1234" http://mysuperserver/media/upload/
or:
curl -i -X POST -H "Content-Type: multipart/form-data"
-F "data=#test.mp3" -F "userid=1234" http://mysuperserver/media/upload/
Here is my solution, I have been reading a lot of posts and they were really helpful. Finally I wrote some code for small files, with cURL and PHP that I think its really useful.
public function postFile()
{
$file_url = "test.txt"; //here is the file route, in this case is on same directory but you can set URL too like "http://examplewebsite.com/test.txt"
$eol = "\r\n"; //default line-break for mime type
$BOUNDARY = md5(time()); //random boundaryid, is a separator for each param on my post curl function
$BODY=""; //init my curl body
$BODY.= '--'.$BOUNDARY. $eol; //start param header
$BODY .= 'Content-Disposition: form-data; name="sometext"' . $eol . $eol; // last Content with 2 $eol, in this case is only 1 content.
$BODY .= "Some Data" . $eol;//param data in this case is a simple post data and 1 $eol for the end of the data
$BODY.= '--'.$BOUNDARY. $eol; // start 2nd param,
$BODY.= 'Content-Disposition: form-data; name="somefile"; filename="test.txt"'. $eol ; //first Content data for post file, remember you only put 1 when you are going to add more Contents, and 2 on the last, to close the Content Instance
$BODY.= 'Content-Type: application/octet-stream' . $eol; //Same before row
$BODY.= 'Content-Transfer-Encoding: base64' . $eol . $eol; // we put the last Content and 2 $eol,
$BODY.= chunk_split(base64_encode(file_get_contents($file_url))) . $eol; // we write the Base64 File Content and the $eol to finish the data,
$BODY.= '--'.$BOUNDARY .'--' . $eol. $eol; // we close the param and the post width "--" and 2 $eol at the end of our boundary header.
$ch = curl_init(); //init curl
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'X_PARAM_TOKEN : 71e2cb8b-42b7-4bf0-b2e8-53fbd2f578f9' //custom header for my api validation you can get it from $_SERVER["HTTP_X_PARAM_TOKEN"] variable
,"Content-Type: multipart/form-data; boundary=".$BOUNDARY) //setting our mime type for make it work on $_FILE variable
);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/1.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0'); //setting our user agent
curl_setopt($ch, CURLOPT_URL, "api.endpoint.post"); //setting our api post url
curl_setopt($ch, CURLOPT_COOKIEJAR, $BOUNDARY.'.txt'); //saving cookies just in case we want
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); // call return content
curl_setopt ($ch, CURLOPT_FOLLOWLOCATION, 1); navigate the endpoint
curl_setopt($ch, CURLOPT_POST, true); //set as post
curl_setopt($ch, CURLOPT_POSTFIELDS, $BODY); // set our $BODY
$response = curl_exec($ch); // start curl navigation
print_r($response); //print response
}
With this we should be get on the "api.endpoint.post" the following vars posted. You can easily test with this script, and you should be receive this debugs on the function postFile() at the last row.
print_r($response); //print response
public function getPostFile()
{
echo "\n\n_SERVER\n";
echo "<pre>";
print_r($_SERVER['HTTP_X_PARAM_TOKEN']);
echo "/<pre>";
echo "_POST\n";
echo "<pre>";
print_r($_POST['sometext']);
echo "/<pre>";
echo "_FILES\n";
echo "<pre>";
print_r($_FILEST['somefile']);
echo "/<pre>";
}
It should work well, they may be better solutions but this works and is really helpful to understand how the Boundary and multipart/from-data mime works on PHP and cURL library.
if you are uploading binary file such as csv, use below format to upload file
curl -X POST \
'http://localhost:8080/workers' \
-H 'authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6ImFjY2VzcyIsInR5cGUiOiJhY2Nlc3MifQ.eyJ1c2VySWQiOjEsImFjY291bnRJZCI6MSwiaWF0IjoxNTExMzMwMzg5LCJleHAiOjE1MTM5MjIzODksImF1ZCI6Imh0dHBzOi8veW91cmRvbWFpbi5jb20iLCJpc3MiOiJmZWF0aGVycyIsInN1YiI6ImFub255bW91cyJ9.HWk7qJ0uK6SEi8qSeeB6-TGslDlZOTpG51U6kVi8nYc' \
-H 'content-type: application/x-www-form-urlencoded' \
--data-binary '#/home/limitless/Downloads/iRoute Masters - Workers.csv'
After a lot of tries this command worked for me:
curl -v -F filename=image.jpg -F upload=#image.jpg http://localhost:8080/api/upload
The issue that lead me here turned out to be a basic user error - I wasn't including the # sign in the path of the file and so curl was posting the path/name of the file rather than the contents. The Content-Length value was therefore 8 rather than the 479 I expected to see given the legnth of my test file.
The Content-Length header will be automatically calculated when curl reads and posts the file.
curl -i -H "Content-Type: application/xml" --data "#test.xml" -v -X POST https://<url>/<uri/
...
< Content-Length: 479
...
Posting this here to assist other newbies in future.
As an alternative to curl, you can use HTTPie, it'a CLI, cURL-like tool for humans.
Installation instructions: https://github.com/jakubroztocil/httpie#installation
Then, run:
http -f POST http://localhost:4040/api/users username=johnsnow photo#images/avatar.jpg
HTTP/1.1 200 OK
Access-Control-Expose-Headers: X-Frontend
Cache-control: no-store
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 89
Content-Type: text/html; charset=windows-1251
Date: Tue, 26 Jun 2018 11:11:55 GMT
Pragma: no-cache
Server: Apache
Vary: Accept-Encoding
X-Frontend: front623311
...
I got it worked with this command curl -F 'filename=#/home/yourhomedirextory/file.txt' http://yourserver/upload
cat test.txt
file test.txt content.
curl -v -F "hello=word" -F "file=#test.txt" https://httpbin.org/post
> POST /post HTTP/2
> Host: httpbin.org
> user-agent: curl/7.68.0
> accept: */*
> content-length: 307
> content-type: multipart/form-data; boundary=------------------------78a9f655d8c87a53
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
* We are completely uploaded and fine
< HTTP/2 200
< date: Mon, 15 Nov 2021 06:18:47 GMT
< content-type: application/json
< content-length: 510
< server: gunicorn/19.9.0
< access-control-allow-origin: *
< access-control-allow-credentials: true
<
{
"args": {},
"data": "",
"files": {
"file": "file test.txt content.\n"
},
"form": {
"hello": "word"
},
"headers": {
"Accept": "*/*",
"Content-Length": "307",
"Content-Type": "multipart/form-data; boundary=------------------------78a9f655d8c87a53",
"Host": "httpbin.org",
"User-Agent": "curl/7.68.0",
"X-Amzn-Trace-Id": "Root=1-6191fbc7-6c68fead194d943d07148860"
},
"json": null,
"origin": "43.129.xx.xxx",
"url": "https://httpbin.org/post"
}
Here is how to correctly escape arbitrary filenames of uploaded files with bash:
#!/bin/bash
set -eu
f="$1"
f=${f//\\/\\\\}
f=${f//\"/\\\"}
f=${f//;/\\;}
curl --silent --form "uploaded=#\"$f\"" "$2"
save all sent files to folder:
php file on host. u.php:
<?php
$uploaddir = 'C:/VALID_DIR/';
echo '<pre>';
foreach ($_FILES as $key => $file) {
if(!isset($file) || !isset($file['name'])) continue;
$uploadfile = $uploaddir . basename($file['name']);
if (move_uploaded_file($file['tmp_name'], $uploadfile)) {
echo "$key file > $uploadfile .\n";
} else {
echo " Error $key file.\n";
}
}
print_r($_FILES);
print "</pre>";?>
Usage from client:
curl -v -F filename=ff.xml -F upload=#ff.xml https://myhost.com/u.php
This is worked for me.
My VM crashed it has only internet connection.
I recovered some files this way.
I'm using Qt and I recently made a similar application using gmail. Now, I want to send the email from outlook to gmail. EDIT: I just tried sending from outlook to outlook using an app password but still empty email in my outlook inbox... END EDIT Here is my code:
if(file.open(QIODevice::ReadWrite)){ //Writes in the msg.txt
QTextStream stream(&file);
stream << "From: \"Me\" <xxxxxxxxxx#outlook.com>" << endl;
stream << "To: \"Me\" <xxxxxxxxxxxx#gmail.com>" << endl;
stream << "Subject: Subject" << endl;
stream << msg << endl; //msg is just a QString variable
}
QString cmd = "ccurl smtp://smtp-mail.outlook.com:587 -v --mail-from \"xxxxxxxxxxxx#outlook.com\" --mail-rcpt \"xxxxxxxxxxxx#gmail.com\" --ssl -u xxxxxxxxxxxxxx#outlook.com:xxxxxxxxxxxxxx -T \"msg.txt\" -k --anyauth --insecure & pause";
const std::string s = cmd.toStdString();
const char* ccmd = s.c_str();
system(ccmd);
Pause is just used for testing purposes. Also, my .exe is named 'ccurl' and the console that appears doesn't throw any error. I do receive an email but it just says something like (Empty)
---
Email checked by avast....
Thanks for your help!
Ps. Don't tell me to use libcurl instead
You are missing an empty line between the end of the headers and the start of the message body. Without it, the rest of the message is interpreted as if it was still part of the headers.
Also, endl forces a flush in the stream for no good reason, which kills performance when done on files. Just use \n.
If I write this in my shell, everything works like a charme:
// shell (unix)
curl -X PUT -d "{ \"string\" : \"my string 1212 \"}" "https://my.firebaseio.com/myVal.json"
As you can tell, this inserts some stuff in my firebase. As mentioned above, this works as expected. Since I am not too deep in C++, I have no idea on how to PUT curl-requests internally. I was thinking about doing it in the shell via system.
I ended up with this:
// c++ code
system('curl -X PUT -d "{ \"string\" : \"my string 1212 \"}" "https://my.firebaseio.com/myVal.json" ');
This however produces this output:
curl: (6) Could not resolve host:
curl: (6) Could not resolve host: CD
curl: (7) Could not resolve host: CD
curl: (3) [globbing] unmatched close brace/bracket in column 1
Thanks for any helpful advices
// Update 1
After hearing that single quotes ' are reserved for chars and going for the solution erip provided, it is still the same output:
curl: (6) Could not resolve host:
curl: (6) Could not resolve host: cd
curl: (7) Could not resolve host: cd
curl: (3) [globbing] unmatched close brace/bracket in column 1
{
"error" : "Invalid data; couldn't parse JSON object, array, or value. Perhaps you're using invalid characters in your key names."
}
In C++, single quotes are used for the type char. Double quotes are reserved for std::strings or char *s.
Thus, your solution should be by simply replacing single quotes with double quotes and escaping the quotes that aren't your final quote:
system("curl -X PUT -d \"{ \"string\" : \"my string 1212 \"}\" https://my.firebaseio.com/myVal.json ");
However, like #DaoWen mentioned, always use a library if/when possible.
EDIT
I'd recommend trying this:
std::string command = "curl -X PUT -d \"{ \"string\" : \"my string 1212 \"}\" https://my.firebaseio.com/myVal.json ";
system(command.c_str());
But honestly, it's better to use fork and exec calls than system calls if you don't want to use libcurl.
EDIT 2
std::string command = "curl -H \"Content-Type: application/json\" -X PUT -d '{ \"string\" : \"my string 1212 \"}' https://my.firebaseio.com/myVal.json";
system(command.c_str());
The weird escaped quotes were treating key: "string" as the host because { was surrounded by quotes, acting as the data. I fixed this by surrounding the data to be passed with a single quote.
You can see that I PUT { "Hello" : "World!" } to your app here.
Hope this helped.
I wrote a program with Qt to work with cisco ip phone services. I'm using QNetworkAccessManager to post XML objects to phones and QTcpServer's socket with QTextStream to respond to authentication requests (simply writing http headers with "AUTHORIZED" to text stream).
QString cTime = currTime.currentDateTimeUtc().toString("ddd, dd MMM yyyy hh:mm:ss");
QTextStream os(socket); os << "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Date: " + cTime + " GMT\r\n"
"Connection: close\r\n"
"\r\n"
"AUTHORIZED";
The problem is the phones don't accept that response and return <CiscoIPPhoneError Number="4" />.
I used node.js for that before and simply wrote "AUTHORIZED" to http.serverResponse object, but I'm confused now why it doesn't work with Qt
Solved that.
The problem was the "Secure Authentication URL" field was set along with "Authentication url". And what I thought to be GET from phone was "Client hello"...
Cleared "Secure Authentication URL" in CUCM and it works now