C++ popen to run PHP - php

i'm using popenRWE from http://www.jukie.net/bart/blog/popenRWE and making the script below
int pipes[3];
int pid;
const char *const args[] = {
"php ",
NULL
};
pid = popenRWE(pipes, args[0], args);
char *cmd = "<?php echo 'hello world';?> ";
cout << "write: " << write(pipes[0], cmd, strlen(cmd)) << endl;
cout << "err: " << errno << endl;
char res[100];
cout << "read: " << read(pipes[1], res, 100) << endl;
cout << "result: " << res << endl;
when i use cat command, it works, the input is the ouput (that's what cat doing), but using php the read is empty. i have confirmed that php is installed and on my path by running
echo "<?php echo 'hello world';?>" | php
directly on the console, and got the output. Can someone please advise or help on this code? Thanks in advance.

There are three problems with your code:
There is no executable named "php ". There is just "php" (notice that there is no space). The reason why this does not work is beceause popenRWE uses execvp which does not start a shell to execute the command but it expects the filename of the binary you want to executed (it searches for it in $PATH though).
You should close the stdin-filehandle after you've written your data, otherwise you might have to wait indefinitely for the output to be written.
Also you should wait for the php-process to finish using waitpid because otherwise you might "lose" some of the output.
To wrap it up:
int pipes[3];
int pid;
const char *const args[] = {
"php",
NULL
};
pid = popenRWE(pipes, args[0], args);
char *cmd = "<?php echo 'hello world', \"\\n\";?> ";
cout << "write: " << write(pipes[0], cmd, strlen(cmd)) << endl;
cout << "err: " << errno << endl;
close(pipes[0]);
// TODO: proper error handling
int status;
waitpid(pid, &status, 0);
char res[100];
int bytesRead = read(pipes[1], res, (sizeof(res)/sizeof(char))-1);
// zero terminate the string
res[bytesRead >= 0 ? bytesRead : 0] = '\0';
cout << "read: " << bytesRead << endl;
cout << "result: " << res << endl;

Related

Upload files using QNetworkManager is not woking

So I'm trying to upload a simple text file using Qt Network Mangager to a php script that I'm serving. But it's not working. I tried examples with QHttpMultiPart and with Setting raw data headers in request but none work.
Here is my Qt Code:
#include <QCoreApplication>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QDebug>
#include <QEventLoop>
#include <QObject>
#include <QVariantMap>
#include <QJsonDocument>
#include <QFile>
#include <QHttpMultiPart>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString address = "http://localhost/api_test/";
//address = "https://dashboard.viewmind.ai/dashboard/api_test/welcome.php";
QUrl api_url = QUrl(address);
QVariantMap postDatamap;
postDatamap.insert("Name","Ariel Ñoño");
postDatamap.insert("Age",37);
QJsonDocument json = QJsonDocument::fromVariant(postDatamap);
qDebug() << "Sending the request";
QNetworkAccessManager *networkManager = new QNetworkAccessManager();
QNetworkRequest request(api_url);
QString bound = "<<<<<boundary>>>>>";
request.setRawHeader(QString("Content-Type").toUtf8(),QString("multipart/form-postData; boundary=" + bound).toUtf8());
//QByteArray postData;
QByteArray postData(QString("--" + bound + "\r\n").toUtf8());
postData.append("Content-Disposition: form-postData; name=\"action\"\r\n\r\n");
postData.append("welcome.php\r\n");
postData.append(QString("--" + bound + "\r\n").toUtf8());
postData.append("Content-Disposition: form-postData; name=\"uploaded\"; filename=\"");
postData.append("test.json");
postData.append("\"\r\n");
postData.append("Content-Type: text/xml\r\n\r\n"); //postData type
QFile file("test.json");
if (!file.open(QIODevice::ReadOnly)){
qDebug() << "QFile Error: File not found!";
delete networkManager;
return 0;
} else { qDebug() << "File found, proceed as planned"; }
postData.append(file.readAll());
postData.append("\r\n");
postData.append(QString("--" + bound + "\r\n").toUtf8());
request.setRawHeader(QString("Content-Length").toUtf8(), QString::number(postData.length()).toUtf8());
//request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json; charset=utf-8");
//request.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-postData; name=\"text\""));
//qDebug() << QString(json.toJson());
//QHttpMultiPart multipart(QHttpMultiPart::FormDataType);
//QHttpPart textPart;
// QFile file("test.json");
// if (!file.open(QIODevice::ReadOnly)){
// qDebug() << "Could not open file for reading";
// delete networkManager;
// return 0;
// }
//textPart.setBodyDevice(&file);
//multipart.append(textPart);
//file.setParent(&multipart);
//QNetworkReply *reply = networkManager->post(request,json.toJson());
//QNetworkReply *reply = networkManager->post(request,file.readAll());
//QNetworkReply *reply = networkManager->post(request,&multipart);
QNetworkReply *reply = networkManager->post(request,postData);
//file.setParent(reply);
//multipart.setParent(reply);
// Using the loop to wait for the reply to finish.
QEventLoop loop;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
qDebug() << "Reply is finished";
//file.close();
if (reply->error() != QNetworkReply::NoError){
qDebug() << "The following error ocurred";
qDebug() << reply->errorString();
return 0;
}
QString postData_returned = QString::fromUtf8(reply->readAll());
qDebug() << "DATA RETURNED";
qDebug() << postData_returned;
return 0;
}
My php code looks like this
<?php
header("Access-Control-Allow-Origin: *"); // Anyone can access
header("Content-Type: application/json; charset=UTF-8"); // Will return json data.
error_log("FILES iS");
vardump_toerror($_FILES);
?>
It is my understanding that the $_FILES super global should get filled with the file information. Am I mistaken? But the print out shows it's empty.
I am not an expert in PHP but I find it unnecessary to use the content-type application/json since multipart (submit forms) is not part of that protocol. On the other hand I can't find a reference of the vardump_toerror function so I change with var_dump so my test php is:
<?php
var_dump($_FILES);
?>
In a previous question for PyQt5 I implemented a similar logic for django that also applies in this case so I will show a translation.
#include <QCoreApplication>
#include <QFile>
#include <QHttpMultiPart>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTextCodec>
QHttpMultiPart *buildMultpart(const QVariantMap & data, const QMap<QString, QString> filenames){
QHttpMultiPart *multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QVariantMap::const_iterator i_data = data.constBegin();
while (i_data != data.constEnd()) {
QHttpPart postpart;
postpart.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"%1\"").arg(i_data.key()));
postpart.setBody(i_data.value().toByteArray());
multipart->append(postpart);
++i_data;
}
QMap<QString, QString>::const_iterator i_filenames = filenames.constBegin();
while (i_filenames != filenames.constEnd()) {
QFile *file = new QFile(i_filenames.value());
if(!file->open(QIODevice::ReadOnly)){
delete file;
continue;
}
QHttpPart postpart;
postpart.setHeader(QNetworkRequest::ContentDispositionHeader,
QString("form-data; name=\"%1\"; filename=\"%2\"")
.arg(i_filenames.key(), file->fileName()));
postpart.setBodyDevice(file);
multipart->append(postpart);
file->setParent(multipart);
++i_filenames;
}
return multipart;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QUrl url("http://localhost:4000/upload.php");
QNetworkAccessManager manager;
QMap<QString, QString> filenames;
filenames["fileToUpload"] = "/path/of/data.txt";
QHttpMultiPart *multipart = buildMultpart({}, filenames);
QNetworkRequest request(url);
QNetworkReply *reply = manager.post(request, multipart);
multipart->setParent(reply);
QObject::connect(reply, &QNetworkReply::finished, QCoreApplication::quit);
a.exec();
if(reply->error() == QNetworkReply::NoError){
qDebug() << reply->readAll();
}
else{
qDebug() << reply->error() << reply->errorString();
}
delete reply;
return 0;
}
Output:
"array(1) {\n [\"fileToUpload\"]=>\n array(5) {\n [\"name\"]=>\n string(8) \"data.txt\"\n [\"type\"]=>\n string(0) \"\"\n [\"tmp_name\"]=>\n string(14) \"/tmp/phpVmOAhO\"\n [\"error\"]=>\n int(0)\n [\"size\"]=>\n int(6)\n }\n}\n"

Sending POST request with C++ to Apache Webserver

I'm trying to send some data from a c++ application to the apache server on the same machine.
I wrote some c++ code to send a POST request to the apache server.
The PHP script on the index.php page should collect the data and output them on the web page.
My C++ code:
void send_post()
{
portno = 80;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
cout << "ERROR opening socket" << endl;
}
else {cout << "Socket opened -> ";}
server = gethostbyname("localhost");
if (server == NULL)
{
cout << "ERROR, no such host" << endl;
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char*)&serv_addr.sin_addr.s_addr,server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
{
cout << "ERROR connecting" << endl;
}
else {cout << "Connected -> ";}
bzero(upcbuffer,512);
sprintf(upcbuffer, "%s", post_req.c_str());
z = write(sockfd,upcbuffer,strlen(upcbuffer));
if (z < 0)
{
cout << "ERROR writing to socket";
}
else {cout << "Wrote successfully to socket" << endl;}
close(sockfd);
cout << "Socket closed now" << endl;
}
With my post_req string:
post_req = "POST /index.php HTTP/1.1\r\nHost: localhost\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 8\r\n\r\ntesttest\r\n";
My PHP code on index.php
<?php
print_r($_POST);
?>
Sadly I can't see anything on the index page.
I'm sure that the POST request is well formatted.
Could you help me to fix the problem ?
Where do you expect to see something? To get the reply from the web server you should attempt to read from the socket instead of close it immediately after send/write.
I would also suggest you to get some more confidence with network programmin.
Here you get an evergreen read:
http://beej.us/guide/bgnet/output/html/multipage/index.html

OPENSSL Blowfish CBC encryption differs from PHP to C++

I am trying to encrypt and decrypt a communication between a C++ library and a PHP server using OPENSSL library in both of them. I want to use the Blowfish CBC algorithm but it seems that the results are different between the C++ code and the PHP code. The C++ code is taken from here:
This is the PHP code:
<?php
function strtohex($x)
{
$s='';
foreach (str_split($x) as $c) $s.=sprintf("%02X",ord($c));
return($s);
}
$encryptedMessage = openssl_encrypt("input", "BF-CBC", "123456", 0, "initvect");
echo $encryptedMessage;
echo "\n";
echo strtohex($encryptedMessage);
The PHP output is this:
x9jDa2WMwvQ=
78396A446132574D7776513D
This is the c++ code:
bool do_encrypt(const char *in, unsigned char *out, int *outlen, unsigned char *key, unsigned char *iv)
{
int buflen, tmplen;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), nullptr, key, iv);
if (!EVP_EncryptUpdate(&ctx, out, &buflen, (unsigned char*)in, strlen(in)))
{
return false;
}
if (!EVP_EncryptFinal_ex(&ctx, out + buflen, &tmplen))
{
return false;
}
buflen += tmplen;
*outlen = buflen;
EVP_CIPHER_CTX_cleanup(&ctx);
return true;
}
unsigned char output[2048] = { 0 };
int outLen;
auto result = do_encrypt("input", output, &outLen, (unsigned char*)"123456", (unsigned char*)"initvect");
BIGNUM *outputStr = BN_new();
BN_bin2bn(output, outLen, outputStr);
cout << base64_encode(output, outLen) << "\n";
cout << BN_bn2hex(outputStr) << "\n";
The C++ output is this:
EfRhhWqGmSQ=
11F461856A869924
Can someone please help me understand what I'm doing wrong? Any help will be very much appreciated.
Thanks!
Edit 1:
I managed to fix the C++ code after jww's answer and it worked well. I was missing the EVP_CIPHER_CTX_set_key_length However, I couldn't make the PHP code return the same thing and eventually we decided to move to AES and it now works flawlessly. Thanks!
For your OpenSSL code, I believe you need to call EVP_CIPHER_CTX_set_key_length to tell the library the key is only 6 bytes.
Let me throw Crypto++ into the arena below. OpenSSL and Crypto++ will converge on the right answer once you add the missing EVP_CIPHER_CTX_set_key_length OpenSSL call. The right answer is 32CEBA7E046431EB (in hex).
I don't know what's going on with PHP:
x9jDa2WMwvQ=
78396A446132574D7776513D
Considering x is ASCII 0x78, 9 is ASCII 0x39, I'm guessing you hex encoded the Base64 string.
$ cat test.cxx
#include "blowfish.h"
#include "modes.h"
#include "channels.h"
#include "filters.h"
#include "base64.h"
#include "hex.h"
using namespace CryptoPP;
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char* argv[])
{
const byte key[] = "123456"; // 6
const byte iv[] = "initvect"; // 8
CBC_Mode<Blowfish>::Encryption encryptor;
encryptor.SetKeyWithIV(key, 6, iv, 8);
string r1, r2;
ChannelSwitch chsw;
Base64Encoder e1(new StringSink(r1));
HexEncoder e2(new StringSink(r2));
chsw.AddDefaultRoute(e1);
chsw.AddDefaultRoute(e2);
string msg = "input";
StringSource ss(msg, true,
new StreamTransformationFilter(encryptor,
new Redirector(chsw)));
cout << r1 << endl;
cout << r2 << endl;
return 0;
}
The test program results in:
$ ./test.exe
Ms66fgRkMes=
32CEBA7E046431EB
Here's the OpenSSL portion of things. Notice EVP_EncryptInit_ex is called twice. First, EVP_EncryptInit_ex is called to set the block cipher EVP_bf_cbc. The key length is set with EVP_CIPHER_CTX_set_key_length. Then second, EVP_EncryptInit_ex is called to set the key and iv.
#include <openssl/evp.h>
#include <iostream>
#include <iomanip>
#include <stdexcept>
using namespace std;
typedef unsigned char byte;
int main()
{
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int rc;
const byte key[] = "123456"; // 6
const byte iv[] = "initvect"; // 8
rc = EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), NULL, 0, 0);
if (rc != 1)
throw runtime_error("EVP_EncryptInit_ex failed");
rc = EVP_CIPHER_CTX_set_key_length(&ctx, 6);
if (rc != 1)
throw runtime_error("EVP_CIPHER_CTX_set_key_length failed");
rc = EVP_EncryptInit_ex(&ctx, NULL, NULL, key, iv);
if (rc != 1)
throw runtime_error("EVP_EncryptInit_ex failed");
const byte msg[] = "input";
byte buf[32];
int len1 = sizeof(buf), len2 = sizeof(buf);
rc = EVP_EncryptUpdate(&ctx, buf, &len1, msg, 5);
if (rc != 1)
throw runtime_error("EVP_EncryptUpdate failed");
rc = EVP_EncryptFinal_ex(&ctx, buf+len1, &len2);
if (rc != 1)
throw runtime_error("EVP_EncryptFinal_ex failed");
for(unsigned int i=0; i<len1+len2; i++)
cout << std::hex << setw(2) << setfill('0') << (int)buf[i];
cout << endl;
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}

c++ equivalent for php shell_exec

What will be the C++ equivalemt command for below mentioned php command:
$command = shell_exec("sqlldr {$connect_string} control={$ctl_file_name} log={$log_file_name}");
So based on your comments a solution that would work would be to use popen(3):
#include <cstdio>
#include <iostream>
#include <string>
int main()
{
// Set file names based on your input etc... just using dummies below
std::string
ctrlFileName = "file1",
logFileName = "file2",
cmd = "sqlldr usr/pwd#LT45 control=" + ctrlFileName + " log=" + logFileName ;
std::cout << "Executing Command: " << cmd << std::endl ;
FILE* pipe = popen(cmd.c_str(), "r");
if (pipe == NULL)
{
return -1;
}
char buffer[128];
std::string result = "";
while(!feof(pipe))
{
if(fgets(buffer, 128, pipe) != NULL)
{
result += buffer;
}
}
std::cout << "Results: " << std::endl << result << std::endl ;
pclose(pipe);
}
Try forkpty, you get a file descriptor which you can use to read from the other pseudoterminal.

CSimpleSocket failing after client disconnects

I'm having some problems with sockets in c++(server) and php(client). Below is the code. The problem is that whenever the client gets disconnected (because of failure or whatever) the pClient->send() function should return -1 or 0. However, it doesn't. Therefore I cannot break the loop, and the program fails on this function. What am I doing wrong? I hope you can help me!
int sentSucces=0;
char szData[MAX_PACKET];
memset(szData, 0, MAX_PACKET);
CPassiveSocket socket;
CActiveSocket *pClient = NULL;
sendInterval = 10;
socket.Initialize();
socket.Listen((const uint8 *) "127.0.0.1", 9876);
socket.SetNonblocking(); //if no connection in queue, don't wait (accept())
while (1) {
//check queue for connections.
if ((pClient = socket.Accept()) != NULL) {
pClient->SetNonblocking();
cout << "++ connected\n";
while (1) {
// Receive request from the client.
cout << "- receiving....\n";
if (pClient->Receive(MAX_PACKET) > 0) {
cout << "-- received\n";
strncpy(szData, (const char *) pClient->GetData(), pClient->GetBytesReceived());
cout << "-- +: " << szData << "\n";
//clear memory of szData
memset(szData, 0, MAX_PACKET);
} else {
cout << "-- NOT received\n";
}
string bericht = "hallo";
cout << "sending....\n";
sentSucces = pClient->Send((const uint8 *) bericht.c_str(), strlen(bericht.c_str()));
cout << "++ send:" << sentSucces << "\n";
if (sentSucces < 1) break;
sleep(4);
}
cout << "++ disconnecting...\n";
pClient->Close();
cout << "++ disconnected\n";
} else {
// cout << "++ no connections\n";
}
usleep(10000);
}
Your code waits forever in Receive. If you don't want to do that, don't. The reason the Send function won't return an error is because you never get to it, waiting forever in Receive.
Whatever protocol you are implementing, does its specification say this end of the connection should wait forever try to receive data? If so, it's broken. If not, then your code is broken. If you don't have a protocol specification, you need to write one. Otherwise, all your code will only work together by luck.
The TCP protocol does guarantee that a side that sends will eventually detect a disconnection. But it makes no such guaranteed for a side that waits forever to receive data. If the other side never sends anything at all and goes away, you'll still be waiting forever. When you design a protocol that works over TCP, you must take this into account in the protocol design. This should have been resolved before even a single line of code was written.

Categories