PDA

View Full Version : Execute window command line-by-line using QProcess



nhocjerry
27th July 2013, 04:52
I want to make an application which can download email content by using command line. For example with gmail we can get email by execute following command in Command Promt:
(note you have to install openssl first)


>opensslOpenSSL>s_client -connect pop.gmail.com:995
Loading 'screen' into random state - done
CONNECTED(00000168)
depth=1 C = US, O = Google Inc, CN = Google Internet Authority
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=pop.gmail.com
i:/C=US/O=Google Inc/CN=Google Internet Authority
1 s:/C=US/O=Google Inc/CN=Google Internet Authority
i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDfjCCAuegAwIBAgIKVExoXgABAACEMTANBgkqhkiG9w0BAQ UFADBGMQswCQYD
VQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzEiMCAGA1UEAx MZR29vZ2xlIElu
dGVybmV0IEF1dGhvcml0eTAeFw0xMzA0MTUwODQ1MjVaFw0xMz EyMzExNTU4NTBa
MGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMR YwFAYDVQQHEw1N
b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMRYwFA YDVQQDEw1wb3Au
Z21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ DJcFLd/CvEAEYb
PComwfh/VJnV4I0AbErRIBztla7KdaOL3SN8a2lnKKZSkX9fhIMQ8mwCyZ 8iWzLG
aXaWr64uChCH2lQNA6O5B/kcLeBfWV51Fwqiq6aRQxpYY7sUqOP03oABRLtBiFOq
wCCLLbkeEcjLuWzzebFk2HX6L++02wIDAQABo4IBUDCCAUwwHQ YDVR0lBBYwFAYI
KwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBS3kDe0ZShOJv pTHlTbh4fjZtVf
lDAfBgNVHSMEGDAWgBS/wDDr9UMRPme6npH7/Gra42sSJDBbBgNVHR8EVDBSMFCg
TqBMhkpodHRwOi8vd3d3LmdzdGF0aWMuY29tL0dvb2dsZUludG VybmV0QXV0aG9y
aXR5L0dvb2dsZUludGVybmV0QXV0aG9yaXR5LmNybDBmBggrBg EFBQcBAQRaMFgw
VgYIKwYBBQUHMAKGSmh0dHA6Ly93d3cuZ3N0YXRpYy5jb20vR2 9vZ2xlSW50ZXJu
ZXRBdXRob3JpdHkvR29vZ2xlSW50ZXJuZXRBdXRob3JpdHkuY3 J0MAwGA1UdEwEB
/wQCMAAwGAYDVR0RBBEwD4INcG9wLmdtYWlsLmNvbTANBgkqhki G9w0BAQUFAAOB
gQA8MCOlbZZGyLz5byFAq6eFhNw74ahaKVW3QeskRkPUI7hkr3 7Kmoaq8PE9Oosk
lewH6mgDjheUb6DJJhNhnmcgkAvLfyq0fdDKceSwpTbV5xjcKp 3/0G0lzKSMxB7C
oOzEcClsFNLx6EP4FsJS1ELJRGbop7xQF8Wz/5mr88HjFw==
-----END CERTIFICATE-----
subject=/C=US/ST=California/L=Mountain View/O=Google Inc/CN=pop.gmail.com
issuer=/C=US/O=Google Inc/CN=Google Internet Authority
---
No client certificate CA names sent
---
SSL handshake has read 2108 bytes and written 443 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-RC4-SHA
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-RC4-SHA
Session-ID: 8FD510E5074D67B5052E8B68171CD55FC14132282232B0D4A0 90CBC3248719BA

Session-ID-ctx:
Master-Key: 1E0D5DCDD8DB167CCB2C8C3CCDDCE0CF65D41A3D49D1131253 76801C9706FDAC
FCFBFB32A9A174FFE07AF0939D1BE122
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 100800 (seconds)
TLS session ticket:
0000 - bc 52 af c7 83 85 57 69-f4 c3 11 a5 de 03 b9 ce .R....Wi........
0010 - c1 c7 5d 39 e3 6a d8 d2-fe 9d 8b 4a 1a 61 33 a8 ..]9.j.....J.a3.
0020 - 3a dc bd 79 b8 f7 ee 44-39 3d bb 24 1f 7a 6a 18 :..y...D9=.$.zj.
0030 - 0b b9 b5 99 7b ee 9a 15-3a 64 18 78 22 2e 04 5e ....{...:d.x"..^
0040 - b3 6a e3 04 6e c3 52 d8-21 00 3d 3b a3 82 7d ca .j..n.R.!.=;..}.
0050 - e5 3a e9 c6 4f d8 fa 46-77 4e 1c 2e 93 f3 22 81 .:..O..FwN....".
0060 - 02 fb cd 9e bc 56 9e a3-10 76 fe 3c 93 1b d6 33 .....V...v.<...3
0070 - 3a 6c 3c f9 96 81 4b 93-e6 84 01 eb 9e 4c 5d bd :l<...K......L].
0080 - 42 b5 8b 4f 9c 27 a5 f8-4d f9 23 c8 cb cd 91 78 B..O.'..M.#....x
0090 - 58 8b 40 b4 X.@.

Start Time: 1374828368
Timeout : 300 (sec)
Verify return code: 20 (unable to get local issuer certificate)
---
+OK Gpop ready for requests from 113.161.80.171 w3pf6487545pbz.26
USER <your gmail acount>
+OK send PASS
PASS <your gmail password>
+OK Welcome. LIST //show list of mails

I want to create an application to run these command automatically and get the final result. My code:


process = new QProcess(this);
QStringList args;
process->setProcessChannelMode(QProcess::MergedChannels);
args << "s_client" << "-connect" << "pop.gmail.com:995";
process->start("openssl", args);
process->waitForStarted();
process->write("USER <username>\n");
process->write("PASS <password>\n");
process->write("LIST\n");
QString output = process->readAll();

But the output I get just is:


Loading 'screen' into random state -CONNECTED(00000174)---
Certificate chain
0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=pop.gmail.com
i:/C=US/O=Google Inc/CN=Google Internet Authority
1 s:/C=US/O=Google Inc/CN=Google Internet Authority
i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDfjCCAuegAwIBAgIKVExoXgABAACEMTANBgkqhkiG9w0BAQ UFADBGMQswCQYD
VQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzEiMCAGA1UEAx MZR29vZ2xlIElu
dGVybmV0IEF1dGhvcml0eTAeFw0xMzA0MTUwODQ1MjVaFw0xMz EyMzExNTU4NTBa
MGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMR YwFAYDVQQHEw1N
b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMRYwFA YDVQQDEw1wb3Au
Z21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ DJcFLd/CvEAEYb
PComwfh/VJnV4I0AbErRIBztla7KdaOL3SN8a2lnKKZSkX9fhIMQ8mwCyZ 8iWzLG
aXaWr64uChCH2lQNA6O5B/kcLeBfWV51Fwqiq6aRQxpYY7sUqOP03oABRLtBiFOq
wCCLLbkeEcjLuWzzebFk2HX6L++02wIDAQABo4IBUDCCAUwwHQ YDVR0lBBYwFAYI
KwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBS3kDe0ZShOJv pTHlTbh4fjZtVf
lDAfBgNVHSMEGDAWgBS/wDDr9UMRPme6npH7/Gra42sSJDBbBgNVHR8EVDBSMFCg
TqBMhkpodHRwOi8vd3d3LmdzdGF0aWMuY29tL0dvb2dsZUludG VybmV0QXV0aG9y
aXR5L0dvb2dsZUludGVybmV0QXV0aG9yaXR5LmNybDBmBggrBg EFBQcBAQRaMFgw
VgYIKwYBBQUHMAKGSmh0dHA6Ly93d3cuZ3N0YXRpYy5jb20vR2 9vZ2xlSW50ZXJu
ZXRBdXRob3JpdHkvR29vZ2xlSW50ZXJuZXRBdXRob3JpdHkuY3 J0MAwGA1UdEwEB
/wQCMAAwGAYDVR0RBBEwD4INcG9wLmdtYWlsLmNvbTANBgkqhki G9w0BAQUFAAOB
gQA8MCOlbZZGyLz5byFAq6eFhNw74ahaKVW3QeskRkPUI7hkr3 7Kmoaq8PE9Oosk
lewH6mgDjheUb6DJJhNhnmcgkAvLfyq0fdDKceSwpTbV5xjcKp 3/0G0lzKSMxB7C
oOzEcClsFNLx6EP4FsJS1ELJRGbop7xQF8Wz/5mr88HjFw==
-----END CERTIFICATE-----
subject=/C=US/ST=California/L=Mountain View/O=Google Inc/CN=pop.gmail.com
issuer=/C=US/O=Google Inc/CN=Google Internet Authority
---
No client certificate CA names sent
---
SSL handshake has read 2108 bytes and written 443 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-RC4-SHA
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-RC4-SHA
Session-ID: 1C7F9F41B28351AE9C0BD65EC9A235559EE944EDCA6EC30F1D 9D3BE12C540B91
Session-ID-ctx:
Master-Key: F4EEF91B5CF9ADC303E544D973A8E804E2E47989B269633B2F E8D02FA68D93269B34F1959C444C228B2481C6EDAC6516
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 100800 (seconds)
TLS session ticket:
0000 - bc 52 af c7 83 85 57 69-f4 c3 11 a5 de 03 b9 ce .R....Wi........
0010 - 75 27 65 b9 8a fd 34 3e-58 7b ea 25 01 ea d9 3a u'e...4>X{.%...:
0020 - 84 50 15 9e da d1 f6 a5-3f 7a b9 02 29 67 e3 64 .P......?z..)g.d
0030 - 6c f9 1e a9 e7 75 11 fe-af f8 3b 91 7d bc cf ab l....u....;.}...
0040 - cb f0 8f 00 4f b7 a2 a4-e0 06 4f 25 da a9 8e e2 ....O.....O%....
0050 - 9c 55 f7 4c b4 a7 e2 cd-3c f9 6b 1f bf ec ab c3 .U.L....<.k.....
0060 - d5 a8 8d a3 dc 0f 26 e1-38 39 e6 2b bb 71 40 8d ......&.89.+.q@.
0070 - 95 1e 3a 71 6a fa 64 84-2b 42 92 36 f3 fb 93 b3 ..:qj.d.+B.6....
0080 - ba ec 6f e5 48 50 ea 6f-23 4d 2b 5b f4 9c 8a 58 ..o.HP.o#M+[...X
0090 - 31 24 b5 c0 1$..

Start Time: 1374828292
Timeout : 300 (sec)
Verify return code: 20 (unable to get local issuer certificate)
--- +OK Gpop ready for requests from 113.161.80.171 on1pf6530043pbc.6

This means the lines from USER have not been executed. It seems to be in a session while waiting for USER and PASS after "+OK Gpop ready for requests from 113.161.80.171 on1pf6530043pbc.6" line and our current QProcess can not write anything into. Anyone can help me on this? I am e newbie in Qt so I do not know much about QProcess.

ChrisW67
27th July 2013, 06:56
QIODevice relies on the Qt event loop to process writes to the subprocess, and that is not functioning here. You need to waitForBytesWritten()/waitForBytesRead(), or you need to write using asynchronous methods. Here is a very crude example:


#include <QtCore>

class PopLister: public QObject
{
Q_OBJECT
public:
enum State { Starting, SentUser, SentPass, SentList, ReadList, Stopping };

explicit PopLister(QObject *p = 0):
QObject(p),
process(new QProcess(this)),
state(Starting)
{
process->setProcessChannelMode(QProcess::MergedChannels);
connect(process, SIGNAL(readyRead()), SLOT(handleOutput()));
}

void start()
{
QStringList args;
args << "s_client" << "-connect" << "server:995";
process->start("openssl", args);
state = Starting;
}

private slots:
void handleOutput() {
while (process->canReadLine()) {
QByteArray line = process->readLine();

if (line.startsWith("+OK")) {
switch (state) {
case Starting:
process->write("USER username\r\n");
state = SentUser;
break;
case SentUser:
process->write("PASS password\r\n");
state = SentPass;
break;
case SentPass:
listData.clear();
process->write("LIST\r\n");
state = SentList;
break;
case SentList:
state = ReadList;
break;
case ReadList: // suppress compiler warnings
case Stopping:
break;
}
}
else if(line.startsWith("-ERR")) {
qDebug() << line;
process->write("QUIT\r\n");
state = Stopping;
}
else if (state == ReadList) {
if (line == ".\r\n") {
qDebug() << listData;
process->write("QUIT\r\n");
state = Stopping;
}
else {
listData += line;
}
}
else if (state == Stopping) {
qDebug() << line;
}
}
}
private:
QProcess *process;
State state;
QByteArray listData;
};

int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
PopLister lister;
lister.start();
return app.exec();
}
#include "main.moc"

wysota
27th July 2013, 22:23
Also some POP servers will discard everything that was sent to them prior to their last response. Thus if you send data blindly before the data is requested, the data will be ignored (at best).

By the way, why would anyone want to write a program that talks to gmail over openssl console? Why not use QSslSocket?

nhocjerry
29th July 2013, 12:22
QIODevice relies on the Qt event loop to process writes to the subprocess, and that is not functioning here. You need to waitForBytesWritten()/waitForBytesRead(), or you need to write using asynchronous methods. Here is a very crude example:


#include <QtCore>

class PopLister: public QObject
{
Q_OBJECT
public:
enum State { Starting, SentUser, SentPass, SentList, ReadList, Stopping };

explicit PopLister(QObject *p = 0):
QObject(p),
process(new QProcess(this)),
state(Starting)
{
process->setProcessChannelMode(QProcess::MergedChannels);
connect(process, SIGNAL(readyRead()), SLOT(handleOutput()));
}

void start()
{
QStringList args;
args << "s_client" << "-connect" << "server:995";
process->start("openssl", args);
state = Starting;
}

private slots:
void handleOutput() {
while (process->canReadLine()) {
QByteArray line = process->readLine();

if (line.startsWith("+OK")) {
switch (state) {
case Starting:
process->write("USER username\r\n");
state = SentUser;
break;
case SentUser:
process->write("PASS password\r\n");
state = SentPass;
break;
case SentPass:
listData.clear();
process->write("LIST\r\n");
state = SentList;
break;
case SentList:
state = ReadList;
break;
case ReadList: // suppress compiler warnings
case Stopping:
break;
}
}
else if(line.startsWith("-ERR")) {
qDebug() << line;
process->write("QUIT\r\n");
state = Stopping;
}
else if (state == ReadList) {
if (line == ".\r\n") {
qDebug() << listData;
process->write("QUIT\r\n");
state = Stopping;
}
else {
listData += line;
}
}
else if (state == Stopping) {
qDebug() << line;
}
}
}
private:
QProcess *process;
State state;
QByteArray listData;
};

int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
PopLister lister;
lister.start();
return app.exec();
}
#include "main.moc"


Thanks for your reply. However, it doesn't work, it still pause at "+OK Gpop ready for requests from 113.161.80.171 on1pf6530043pbc.6"

Added after 14 minutes:


Also some POP servers will discard everything that was sent to them prior to their last response. Thus if you send data blindly before the data is requested, the data will be ignored (at best).

By the way, why would anyone want to write a program that talks to gmail over openssl console? Why not use QSslSocket?

I found it more easier when use QSslSocket, thank you so much. Here is my code:

sslsocket.h

#include <QMainWindow>
#include <QSslSocket>

namespace Ui {
class SslSocket;
}

class SslSocket : public QMainWindow
{
Q_OBJECT

public:
explicit SslSocket(QWidget *parent = 0);
~SslSocket();

private:
Ui::SslSocket *ui;

QSslSocket *sslSocket;

QString output;

QString serverAddress;

int port;

private slots:
void encrypted();

void readyRead();

void on_btnGetData_clicked();
};


sslsocket.cpp


#include "sslsocket.h"
#include "ui_sslsocket.h"

SslSocket::SslSocket(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::SslSocket)
{
ui->setupUi(this);

sslSocket = new QSslSocket(this);

connect(sslSocket, SIGNAL(encrypted()), this, SLOT(encrypted()));
connect(sslSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));

port = 995;
serverAddress = "pop.gmail.com";
sslSocket->connectToHostEncrypted(serverAddress, port);

}

SslSocket::~SslSocket()
{
delete ui;
}

void SslSocket::encrypted()
{
QMessageBox::information(this, "Info", "Encrypted!");
}

void SslSocket::readyRead()
{
QString data(sslSocket->readAll());

output += data;

ui->txtOutput->setText(output);
}

void SslSocket::on_btnGetData_clicked()
{
QByteArray byteArray("USER ");
byteArray.append("<username>");
sslSocket->write(byteArray);
sslSocket->write("\r\n");

sslSocket->waitForReadyRead();

byteArray.clear();
byteArray.append("PASS ");
byteArray.append("<password>");
sslSocket->write(byteArray);
sslSocket->write("\r\n");

sslSocket->waitForReadyRead();

byteArray.clear();
byteArray.append("LIST");
sslSocket->write(byteArray);
sslSocket->write("\r\n");
}


It works fine for me, but I have another mail server: mail.simplessus.com. When I replace "pop.gmail.com" by this server address, it can not read any data out. It also doesn't emit encrypted() slot as well as readyRead() slot. Is there something wrong with the mail server? Or I have to use another function to get the data? Thanks so much again

ChrisW67
29th July 2013, 23:28
However, it doesn't work, it still pause at "+OK Gpop ready for requests from 113.161.80.171 on1pf6530043pbc.6"
The code works perfectly well against my local POP3 server and a Gmail account I set up specifically for the purpose. Here is the output for my newly minted GMail account:


"1 15714
2 16805
3 9094
4 9107
"
"DONE
"
The first 4 lines are the LIST output. "DONE" is in response to QUIT. I suggest you try analysing the actual problem.
Incidentally, the same code should work with a QSslSocket in place of QProcess: both are QIODevice subclasses.



It works fine for me, but I have another mail server: mail.simplessus.com. When I replace "pop.gmail.com" by this server address, it can not read any data out. It also doesn't emit encrypted() slot as well as readyRead() slot. Is there something wrong with the mail server? Or I have to use another function to get the data?
No encrypted() signal -> no encryption. If the server is supposed to offering encrypted service then it is misconfigured, expecting the STLS command (Start TLS), or you are connecting to an unencrypted port.

waitForReadyRead() will return as soon as there is any data available to read. Note, this is not an all-the-data-is-received signal, so you may well be sending your next command before the server is expecting it. See wysota's comment regarding that.

nhocjerry
30th July 2013, 12:21
No encrypted() signal -> no encryption. If the server is supposed to offering encrypted service then it is misconfigured, expecting the STLS command (Start TLS), or you are connecting to an unencrypted port.

waitForReadyRead() will return as soon as there is any data available to read. Note, this is not an all-the-data-is-received signal, so you may well be sending your next command before the server is expecting it. See wysota's comment regarding that.



Ok, I have found the problem, the mail server is not support ssl port, I used port 143 and everthing is OK now. Thanks for support :)