#include "sendemail.h"
#include "qtimer.h"
//#define emailPI_DEBUG
//#define emailDTP_DEBUG
#ifndef QT_NO_EMAIL
#include "qtcpsocket.h"
#include <QFile>
#include <QFileInfo>
#include <memory.h>
QT_BEGIN_NAMESPACE
class emailCommand
{
public:
emailCommand(sendemail::Command cmd, QStringList raw);
int id;
sendemail::Command command;
QStringList rawCmds;
static QBasicAtomicInt idCounter;
};
QBasicAtomicInt emailCommand::idCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
emailCommand::emailCommand(sendemail::Command cmd, QStringList raw)
: command(cmd), rawCmds(raw)
{
id = idCounter.fetchAndAddRelaxed(1);
}
class emailPI : public QObject
{
Q_OBJECT
public:
emailPI(QObject *parent = 0);
void connectToHost(const QString &host, quint16 port);
bool sendCommands(const QStringList &cmds);
bool sendCommand(const QString &cmd)
{ return sendCommands(QStringList(cmd)); }
void clearPendingCommands();
void abort();
QString currentCommand() const
{ return currentCmd; }
bool transferConnectionExtended;
signals:
void e_connectState(int);
void e_finished(const QString&);
void e_error(int, const QString&);
private slots:
void connected();
void connectionClosed();
void delayedCloseFinished();
void readyRead();
void error(QAbstractSocket::SocketError);
//void ConnectState(int);
private:
void timerEvent( QTimerEvent *event );
// the states are modelled after the generalized state diagram of RFC 959,
// page 58
enum State {
Begin,
Idle,
Waiting,
Success,
Failure
};
bool processReply();
bool startNextCmd();
QTcpSocket commandSocket;
QString replyText;
char replyCode[3];
State state;
QStringList pendingCommands;
QString currentCmd;
int m_emailPITime;
};
emailPI::emailPI(QObject *parent) :
QObject(parent),
transferConnectionExtended(true),
commandSocket(0),
state(Begin),
currentCmd(QString()),
m_emailPITime(0)
{
commandSocket.setObjectName(QLatin1String("emailPI_socket"));
connect(&commandSocket, SIGNAL(connected()),
SLOT(connected()));
connect(&commandSocket, SIGNAL(disconnected()),
SLOT(connectionClosed()));
connect(&commandSocket, SIGNAL(readyRead()),
SLOT(readyRead()));
connect(&commandSocket, SIGNAL(error(QAbstractSocket::SocketError)),
SLOT(error(QAbstractSocket::SocketError)));
}
void emailPI::timerEvent( QTimerEvent *event )
{
emit e_error(sendemail::NotConnected,sendemail::tr("Time out"));
killTimer(m_emailPITime);
m_emailPITime=0;
}
void emailPI::connectToHost(const QString &host, quint16 port)
{
//emit connectState(sendemail::HostLookup);
commandSocket.connectToHost(host, port);
}
bool emailPI::sendCommands(const QStringList &cmds)
{
if (!pendingCommands.isEmpty())
return false;
if (commandSocket.state() != QTcpSocket::ConnectedState || state!=Idle) {
emit e_error(sendemail::NotConnected, sendemail::tr("Not connected"));
return true; // there are no pending commands
}
pendingCommands = cmds;
startNextCmd();
return true;
}
void emailPI::clearPendingCommands()
{
pendingCommands.clear();
currentCmd.clear();
state = Idle;
}
void emailPI::connected()
{
state = Begin;
#if defined(emailPI_DEBUG)
// qDebug("emailPI state: %d [connected()]", state);
#endif
// try to improve performance by setting TCP_NODELAY
commandSocket.setSocketOption(QAbstractSocket::LowDelayOption, 1);
emit e_connectState(sendemail::Connected);
}
void emailPI::connectionClosed()
{
commandSocket.close();
emit e_connectState(sendemail::Unconnected);
}
void emailPI::delayedCloseFinished()
{
emit e_connectState(sendemail::Unconnected);
}
void emailPI::error(QAbstractSocket::SocketError e)
{
if (e == QTcpSocket::HostNotFoundError) {
emit e_connectState(sendemail::Unconnected);
emit e_error(sendemail::HostNotFound,
sendemail::tr("Host %1 not found").arg(commandSocket.peerName()));
} else if (e == QTcpSocket::ConnectionRefusedError) {
emit e_connectState(sendemail::Unconnected);
emit e_error(sendemail::ConnectionRefused,
sendemail::tr("Connection refused to host %1").arg(commandSocket.peerName()));
} else if (e == QTcpSocket::SocketTimeoutError) {
emit e_connectState(sendemail::Unconnected);
emit e_error(sendemail::ConnectionRefused,
sendemail::tr("Connection timed out to host %1").arg(commandSocket.peerName()));
}else if(e==QTcpSocket::NetworkError)
{
emit e_connectState(sendemail::Unconnected);
emit e_error(sendemail::NotConnected,
commandSocket.peerName());
}
}
void emailPI::readyRead()
{
killTimer(m_emailPITime);
m_emailPITime=0;
while (commandSocket.canReadLine()) {
// read line with respect to line continuation
QString line = QString::fromLatin1(commandSocket.readLine());
if (replyText.isEmpty()) {
if (line.length() < 3) {
// protocol error
return;
}
const int lowerLimit[3] = {1,0,0};
const int upperLimit[3] = {5,5,9};
for (int i=0; i<3; i++) {
replyCode[i] = line[i].digitValue();
if (replyCode[i]<lowerLimit[i] || replyCode[i]>upperLimit[i]) {
// protocol error
return;
}
}
}
QString endOfMultiLine;
endOfMultiLine[0] = '0' + replyCode[0];
endOfMultiLine[1] = '0' + replyCode[1];
endOfMultiLine[2] = '0' + replyCode[2];
endOfMultiLine[3] = QLatin1Char(' ');
QString lineCont(endOfMultiLine);
lineCont[3] = QLatin1Char('-');
QString lineLeft4 = line.left(4);
while (lineLeft4 != endOfMultiLine) {
if (lineLeft4 == lineCont)
replyText += line.mid(4); // strip 'xyz-'
else
replyText += line;
if (!commandSocket.canReadLine())
return;
line = QString::fromLatin1(commandSocket.readLine());
lineLeft4 = line.left(4);
}
replyText += line.mid(4); // strip reply code 'xyz '
if (replyText.endsWith(QLatin1String("\r\n")))
replyText.chop(2);
if (processReply())
replyText = QLatin1String("");
}
}
bool emailPI::processReply()
{
#if defined(emailPI_DEBUG)
// qDebug("emailPI state: %d [processReply() begin]", state);
if (replyText.length() < 400)
qDebug("emailPI recv: %d %s", 100*replyCode[0]+10*replyCode[1]+replyCode[2], replyText.toLatin1().constData());
else
qDebug("emailPI recv: %d (text skipped)", 100*replyCode[0]+10*replyCode[1]+replyCode[2]);
#endif
int replyCodeInt = 100*replyCode[0] + 10*replyCode[1] + replyCode[2];
switch (state) {
case Begin:
if (replyCodeInt == 220) {
state = Idle;
emit e_finished(sendemail::tr("Connected to host %1").arg(commandSocket.peerName()));
break;
}
// reply codes not starting with 1 or 2 are not handled.
return true;
case Waiting:
if (replyCodeInt != 250)
state = Failure;
else
state = Success;
break;
default:
// ignore unrequested message
return true;
}
// special actions on certain replies
if (replyCodeInt == 334) {
//username ok
state = Idle;
} else if (replyCodeInt == 235) {
// password ok
state = Idle;
emit e_connectState(sendemail::LoggedIn);
} else if (replyCodeInt == 354) {
// can write data
while(!pendingCommands.isEmpty())
{
startNextCmd();
}
}els