File indexing completed on 2024-11-24 04:53:26

0001 //
0002 // C++ Implementation: qwwsmtpclient
0003 //
0004 // Description:
0005 //
0006 //
0007 // Author: Witold Wysota <wysota@wysota.eu.org>, (C) 2009
0008 //
0009 // Copyright: See COPYING file that comes with this distribution
0010 //
0011 //
0012 #include "qwwsmtpclient.h"
0013 #include <QSslSocket>
0014 #include <QtDebug>
0015 #include <QRegularExpression>
0016 #include <QQueue>
0017 #include <QVariant>
0018 #include <QStringList>
0019 
0020 /*
0021 CONNECTION ESTABLISHMENT
0022       S: 220
0023       E: 554
0024    EHLO or HELO
0025       S: 250
0026       E: 504, 550
0027    MAIL
0028       S: 250
0029       E: 552, 451, 452, 550, 553, 503
0030    RCPT
0031       S: 250, 251 (but see section 3.4 for discussion of 251 and 551)
0032       E: 550, 551, 552, 553, 450, 451, 452, 503, 550
0033    DATA
0034       I: 354 -> data -> S: 250
0035                         E: 552, 554, 451, 452
0036       E: 451, 554, 503
0037    RSET
0038       S: 250
0039    VRFY
0040       S: 250, 251, 252
0041       E: 550, 551, 553, 502, 504
0042    EXPN
0043       S: 250, 252
0044       E: 550, 500, 502, 504
0045    HELP
0046       S: 211, 214
0047       E: 502, 504
0048    NOOP
0049       S: 250
0050    QUIT
0051       S: 221
0052 */
0053 
0054 struct SMTPCommand {
0055     enum Type { Connect, Disconnect, StartTLS, Authenticate, Mail, MailBurl, RawCommand };
0056     int id;
0057     Type type;
0058     QVariant data;
0059     QVariant extra;
0060 };
0061 
0062 class QwwSmtpClientPrivate {
0063 public:
0064     QwwSmtpClientPrivate(QwwSmtpClient *qq) {
0065         q = qq;
0066     }
0067     QSslSocket *socket;
0068 
0069     QwwSmtpClient::State state;
0070     void setState(QwwSmtpClient::State s);
0071     void parseOption(const QString &buffer);
0072 
0073     void onConnected();
0074     void onDisconnected();
0075     void onError(QAbstractSocket::SocketError);
0076     void _q_readFromSocket();
0077     void _q_encrypted();
0078     void processNextCommand(bool ok = true);
0079     void abortDialog();
0080 
0081     void sendAuthPlain(const QString &username, const QString &password);
0082     void sendAuthLogin(const QString &username, const QString &password, int stage);
0083 
0084     void sendEhlo();
0085     void sendHelo();
0086     void sendQuit();
0087     void sendRcpt();
0088 
0089     int lastId;
0090     bool inProgress;
0091     QString localName;
0092     QString localNameEncrypted;
0093     QString errorString;
0094 
0095     // server caps:
0096     QwwSmtpClient::Options options;
0097     QwwSmtpClient::AuthModes authModes;
0098 
0099     QQueue<SMTPCommand> commandqueue;
0100 private:
0101     QwwSmtpClient *q;
0102 
0103     QwwSmtpClientPrivate(const QwwSmtpClientPrivate&); // don't implement
0104     QwwSmtpClientPrivate& operator=(const QwwSmtpClientPrivate&); // don't implement
0105 };
0106 
0107 // private slot triggered upon connection to the server
0108 // - clears options
0109 // - notifies the environment
0110 void QwwSmtpClientPrivate::onConnected() {
0111     options = QwwSmtpClient::NoOptions;
0112     authModes = QwwSmtpClient::AuthNone;
0113     emit q->stateChanged(QwwSmtpClient::Connected);
0114     emit q->connected();
0115 }
0116 
0117 // private slot triggered upon disconnection from the server
0118 // - checks the cause of disconnection
0119 // - aborts or continues processing
0120 void QwwSmtpClientPrivate::onDisconnected() {
0121     setState(QwwSmtpClient::Disconnected);
0122     if (commandqueue.isEmpty()) {
0123         inProgress = false;
0124         emit q->done(true);
0125         return;
0126     }
0127 
0128     if (commandqueue.head().type == SMTPCommand::Disconnect) {
0129         inProgress = false;
0130         emit q->done(true);
0131         return;
0132     }
0133 
0134     emit q->commandFinished(commandqueue.head().id, true);
0135     commandqueue.clear();
0136     inProgress = false;
0137     emit q->done(false);
0138 }
0139 
0140 void QwwSmtpClientPrivate::onError(QAbstractSocket::SocketError e)
0141 {
0142     emit q->socketError(e, socket->errorString());
0143     onDisconnected();
0144 }
0145 
0146 // main logic of the component - a slot triggered upon data entering the socket
0147 // comments inline...
0148 void QwwSmtpClientPrivate::_q_readFromSocket() {
0149     while (socket->canReadLine()) {
0150         QString line = socket->readLine();
0151         emit q->logReceived(line.toUtf8());
0152         QRegularExpression rx("(*ANYCRLF)^(\\d+)-(.*)$", QRegularExpression::MultilineOption);        // multiline response (aka 250-XYZ)
0153         QRegularExpression rxlast("(*ANYCRLF)^(\\d+) (.*)$", QRegularExpression::MultilineOption);    // single or last line response (aka 250 XYZ)
0154         // multiline
0155         QRegularExpressionMatch mid_match = rx.match(line);
0156         if (mid_match.hasMatch()) {
0157             int status = mid_match.captured(1).toInt();
0158             SMTPCommand &cmd = commandqueue.head();
0159             switch (cmd.type) {
0160             // trying to connect
0161             case SMTPCommand::Connect: {
0162                     int stage = cmd.extra.toInt();
0163                     // stage 0 completed with success - socket is connected and EHLO was sent
0164                     if(stage==1 && status==250){
0165                         QString arg = mid_match.captured(2).trimmed();
0166                         parseOption(arg);   // we're probably receiving options
0167                     }
0168                 }
0169                 break;
0170             // trying to establish deferred SSL handshake
0171             case SMTPCommand::StartTLS: {
0172                     int stage = cmd.extra.toInt();
0173                     // stage 0 (negotiation) completed ok
0174                     if(stage==1 && status==250){
0175                         QString arg = mid_match.captured(2).trimmed();
0176                         parseOption(arg);   // we're probably receiving options
0177                     }
0178                 }
0179                 default: break;
0180             }
0181         } else {
0182             // single line
0183             QRegularExpressionMatch last_match = rxlast.match(line);
0184             if (last_match.hasMatch()) {
0185                 int status = last_match.captured(1).toInt();
0186                 SMTPCommand &cmd = commandqueue.head();
0187                 switch (cmd.type) {
0188                 // trying to connect
0189                 case SMTPCommand::Connect: {
0190                     int stage = cmd.extra.toInt();
0191                     // connection established, server sent its banner
0192                     if (stage==0 && status==220) {
0193                         sendEhlo(); // connect ok, send ehlo
0194                     }
0195                     // server responded to EHLO
0196                     if (stage==1 && status==250){
0197                         // success (EHLO)
0198                         parseOption(last_match.captured(2).trimmed()); // we're probably receiving the last option
0199                         errorString.clear();
0200                         setState(QwwSmtpClient::Connected);
0201                         processNextCommand();
0202                     }
0203                     // server responded to HELO (EHLO failed)
0204                     if (stage==2 && status==250) {
0205                         // success (HELO)
0206                         errorString.clear();
0207                         setState(QwwSmtpClient::Connected);
0208                         processNextCommand();
0209                     }
0210                     // EHLO failed, reason given in errorString
0211                     if (stage==1 && (status==554 || status==501 || status==502 || status==421)) {
0212                         errorString = last_match.captured(2).trimmed();
0213                         sendHelo(); // ehlo failed, send helo
0214                         cmd.extra = 2;
0215                     }
0216                     //abortDialog();
0217                 }
0218                 break;
0219                 // trying to establish a delayed SSL handshake
0220                 case SMTPCommand::StartTLS: {
0221                     int stage = cmd.extra.toInt();
0222                     // received an invitation from the server to enter TLS mode
0223                     if (stage==0 && status==220) {
0224                         emit q->logSent("*** startClientEncryption");
0225                         socket->startClientEncryption();
0226                     }
0227                     // TLS established, connection is encrypted, EHLO was sent
0228                     else if (stage==1 && status==250) {
0229                         setState(QwwSmtpClient::Connected);
0230                         parseOption(last_match.captured(2).trimmed());   // we're probably receiving options
0231                         errorString.clear();
0232                         emit q->tlsStarted();
0233                         processNextCommand();
0234                     }
0235                     // starttls failed
0236                     else {
0237                         emit q->logReceived(QByteArrayLiteral("*** TLS failed at stage ") + QByteArray::number(stage) + ": " + line.toUtf8());
0238                         errorString = "TLS failed";
0239                         emit q->done(false);
0240                     }
0241                 }
0242                 break;
0243                 // trying to authenticate the client to the server
0244                 case SMTPCommand::Authenticate: {
0245                     int stage = cmd.extra.toInt();
0246                     if (stage==0 && status==334) {
0247                         // AUTH mode was accepted by the server, 1st challenge sent
0248                         QwwSmtpClient::AuthMode authmode = (QwwSmtpClient::AuthMode)cmd.data.toList().at(0).toInt();
0249                         errorString.clear();
0250                         switch (authmode) {
0251                         case QwwSmtpClient::AuthPlain:
0252                             sendAuthPlain(cmd.data.toList().at(1).toString(), cmd.data.toList().at(2).toString());
0253                             break;
0254                         case QwwSmtpClient::AuthLogin:
0255                             sendAuthLogin(cmd.data.toList().at(1).toString(), cmd.data.toList().at(2).toString(), 1);
0256                             break;
0257                         default:
0258                             qWarning("I shouldn't be here");
0259                             setState(QwwSmtpClient::Connected);
0260                             processNextCommand();
0261                             break;
0262                         }
0263                         cmd.extra = stage+1;
0264                     } else if (stage==1 && status==334) {
0265                         // AUTH mode and user names were acccepted by the server, 2nd challenge sent
0266                         QwwSmtpClient::AuthMode authmode = (QwwSmtpClient::AuthMode)cmd.data.toList().at(0).toInt();
0267                         errorString.clear();
0268                         switch (authmode) {
0269                         case QwwSmtpClient::AuthPlain:
0270                             // auth failed
0271                             setState(QwwSmtpClient::Connected);
0272                             processNextCommand();
0273                             break;
0274                         case QwwSmtpClient::AuthLogin:
0275                             sendAuthLogin(cmd.data.toList().at(1).toString(), cmd.data.toList().at(2).toString(), 2);
0276                             break;
0277                         default:
0278                             qWarning("I shouldn't be here");
0279                             setState(QwwSmtpClient::Connected);
0280                             processNextCommand();
0281                             break;
0282                         }
0283                     } else if (stage==2 && status==334) {
0284                         // auth failed
0285                         errorString = last_match.captured(2).trimmed();
0286                         setState(QwwSmtpClient::Connected);
0287                         processNextCommand();
0288                     } else if (status==235) {
0289                         // auth ok
0290                         errorString.clear();
0291                         emit q->authenticated();
0292                         setState(QwwSmtpClient::Connected);
0293                         processNextCommand();
0294                     } else {
0295                         errorString = last_match.captured(2).trimmed();
0296                         setState(QwwSmtpClient::Connected);
0297                         emit q->done(false);
0298                     }
0299                 }
0300                 break;
0301                 // trying to send mail
0302                 case SMTPCommand::Mail:
0303                 case SMTPCommand::MailBurl:
0304                 {
0305                     int stage = cmd.extra.toInt();
0306                     // temporary failure upon receiving the sender address (greylisting probably)
0307                     if (status==421 && stage==0) {
0308                         errorString = last_match.captured(2).trimmed();
0309                         // temporary envelope failure (greylisting)
0310                         setState(QwwSmtpClient::Connected);
0311                         processNextCommand(false);
0312                     }
0313                     if (status==250 && stage==0) {
0314                         // sender accepted
0315                         errorString.clear();
0316                         sendRcpt();
0317                     } else if (status==250 && stage==1) {
0318                         // all receivers accepted
0319                         if (cmd.type == SMTPCommand::MailBurl) {
0320                             errorString.clear();
0321                             QByteArray url = cmd.data.toList().at(2).toByteArray();
0322                             QByteArray data = "BURL " + url + " LAST\r\n";
0323                             emit q->logSent(data);
0324                             socket->write(data);
0325                             cmd.extra=2;
0326                         } else {
0327                             errorString.clear();
0328                             QByteArray data("DATA\r\n");
0329                             emit q->logSent(data);
0330                             socket->write(data);
0331                             cmd.extra=2;
0332                         }
0333                     } else if ((cmd.type == SMTPCommand::Mail && status==354 && stage==2)) {
0334                         // DATA command accepted
0335                         errorString.clear();
0336                         QByteArray toBeWritten = cmd.data.toList().at(2).toByteArray() + "\r\n.\r\n"; // termination token - CRLF.CRLF
0337                         emit q->logSent(toBeWritten);
0338                         socket->write(toBeWritten); // expecting data to be already escaped (CRLF.CRLF)
0339                         cmd.extra=3;
0340                     } else if ((cmd.type == SMTPCommand::MailBurl && status==250 && stage==2)) {
0341                         // BURL succeeded
0342                         setState(QwwSmtpClient::Connected);
0343                         errorString.clear();
0344                         processNextCommand();
0345                     } else if ((cmd.type == SMTPCommand::Mail && status==250 && stage==3)) {
0346                         // mail queued
0347                         setState(QwwSmtpClient::Connected);
0348                         errorString.clear();
0349                         processNextCommand();
0350                     } else {
0351                         // something went wrong
0352                         errorString = last_match.captured(2).trimmed();
0353                         setState(QwwSmtpClient::Connected);
0354                         emit q->done(false);
0355                         processNextCommand();
0356                     }
0357                 }
0358                     default: break;
0359                 }
0360             } else {
0361                 qDebug() << "None of two regular expressions matched the input" << line;
0362             }
0363         }
0364     }
0365 }
0366 
0367 void QwwSmtpClientPrivate::setState(QwwSmtpClient::State s) {
0368     QwwSmtpClient::State old = state;
0369     state = s;
0370     emit q->stateChanged(s);
0371     if (old == QwwSmtpClient::Connecting && s==QwwSmtpClient::Connected) emit q->connected();
0372     if (s==QwwSmtpClient::Disconnected) emit q->disconnected();
0373 }
0374 
0375 void QwwSmtpClientPrivate::processNextCommand(bool ok) {
0376     if (inProgress && !commandqueue.isEmpty()) {
0377         emit q->commandFinished(commandqueue.head().id, !ok);
0378         commandqueue.dequeue();
0379     }
0380     if (commandqueue.isEmpty()) {
0381         inProgress = false;
0382         emit q->done(false);
0383         return;
0384     }
0385     SMTPCommand &cmd = commandqueue.head();
0386     switch (cmd.type) {
0387     case SMTPCommand::Connect: {
0388         QString hostName = cmd.data.toList().at(0).toString();
0389         uint port = cmd.data.toList().at(1).toUInt();
0390         bool ssl = cmd.data.toList().at(2).toBool();
0391         if(ssl){
0392             emit q->logSent(QByteArrayLiteral("*** connectToHostEncrypted: ") + hostName.toUtf8() + ':' + QByteArray::number(port));
0393             socket->connectToHostEncrypted(hostName, port);
0394         } else {
0395             emit q->logSent(QByteArrayLiteral("*** connectToHost: ") + hostName.toUtf8() + ':' + QByteArray::number(port));
0396             socket->connectToHost(hostName, port);
0397         }
0398         setState(QwwSmtpClient::Connecting);
0399     }
0400     break;
0401     case SMTPCommand::Disconnect: {
0402         sendQuit();
0403     }
0404     break;
0405     case SMTPCommand::StartTLS: {
0406         QByteArray data("STARTTLS\r\n");
0407         emit q->logSent(data);
0408         socket->write(data);
0409         setState(QwwSmtpClient::TLSRequested);
0410     }
0411     break;
0412     case SMTPCommand::Authenticate: {
0413         QwwSmtpClient::AuthMode authmode = (QwwSmtpClient::AuthMode)cmd.data.toList().at(0).toInt();
0414 
0415         if (authmode == QwwSmtpClient::AuthAny){
0416             bool modified = false;
0417             if (authModes.testFlag(QwwSmtpClient::AuthPlain)) {
0418                 authmode = QwwSmtpClient::AuthPlain;
0419                 modified = true;
0420             } else if (authModes.testFlag(QwwSmtpClient::AuthLogin)) {
0421                 authmode = QwwSmtpClient::AuthLogin;
0422                 modified = true;
0423             }
0424             if (modified) {
0425                 QVariantList data = cmd.data.toList();
0426                 data[0] = (int)authmode;
0427                 cmd.data = data;
0428             }
0429         }
0430 
0431         switch (authmode) {
0432         case QwwSmtpClient::AuthPlain:
0433         {
0434             QByteArray buf("AUTH PLAIN\r\n");
0435             emit q->logSent(buf);
0436             socket->write(buf);
0437             setState(QwwSmtpClient::Authenticating);
0438             break;
0439         }
0440         case QwwSmtpClient::AuthLogin:
0441         {
0442             QByteArray buf("AUTH LOGIN\r\n");
0443             emit q->logSent(buf);
0444             socket->write(buf);
0445             setState(QwwSmtpClient::Authenticating);
0446             break;
0447         }
0448         default:
0449             errorString = QwwSmtpClient::tr("Unsupported or unknown authentication scheme");
0450             emit q->done(false);
0451         }
0452     }
0453     break;
0454     case SMTPCommand::Mail:
0455     case SMTPCommand::MailBurl:
0456     {
0457         setState(QwwSmtpClient::Sending);
0458         QByteArray buf = QByteArray("MAIL FROM:<").append(cmd.data.toList().at(0).toByteArray()).append(">\r\n");
0459         emit q->logSent(buf);
0460         socket->write(buf);
0461         break;
0462     }
0463     case SMTPCommand::RawCommand: {
0464     QString cont = cmd.data.toString();
0465     if(!cont.endsWith("\r\n")) cont.append("\r\n");
0466     setState(QwwSmtpClient::Sending);
0467     auto buf = cont.toUtf8();
0468     emit q->logSent(buf);
0469     socket->write(buf);
0470     } break;
0471     }
0472     inProgress = true;
0473     emit q->commandStarted(cmd.id);
0474 }
0475 
0476 void QwwSmtpClientPrivate::_q_encrypted() {
0477         options = QwwSmtpClient::NoOptions;
0478     // forget everything, restart ehlo
0479 //    SMTPCommand &cmd = commandqueue.head();
0480     sendEhlo();
0481 }
0482 
0483 
0484 void QwwSmtpClientPrivate::sendEhlo() {
0485     SMTPCommand &cmd = commandqueue.head();
0486     QString domain = localName;
0487     if (socket->isEncrypted() && !localNameEncrypted.isEmpty())
0488         domain = localNameEncrypted;
0489     QByteArray buf = QString("EHLO "+domain+"\r\n").toUtf8();
0490     emit q->logSent(buf);
0491     socket->write(buf);
0492     cmd.extra = 1;
0493 }
0494 
0495 
0496 void QwwSmtpClientPrivate::sendHelo() {
0497     SMTPCommand &cmd = commandqueue.head();
0498     QString domain = localName;
0499     if (socket->isEncrypted() && localNameEncrypted.isEmpty())
0500         domain = localNameEncrypted;
0501     QByteArray buf = QString("HELO "+domain+"\r\n").toUtf8();
0502     emit q->logSent(buf);
0503     socket->write(buf);
0504     cmd.extra = 1;
0505 }
0506 
0507 
0508 void QwwSmtpClientPrivate::sendQuit() {
0509     QByteArray buf("QUIT\r\n");
0510     emit q->logSent(buf);
0511     socket->write(buf);
0512     socket->waitForBytesWritten(1000);
0513     socket->disconnectFromHost();
0514     setState(QwwSmtpClient::Disconnecting);
0515 }
0516 
0517 void QwwSmtpClientPrivate::sendRcpt() {
0518     SMTPCommand &cmd = commandqueue.head();
0519     QVariantList vlist = cmd.data.toList();
0520     QList<QVariant> rcptlist = vlist.at(1).toList();
0521     QByteArray buf = QByteArray("RCPT TO:<").append(rcptlist.first().toByteArray()).append(">\r\n");
0522     emit q->logSent(buf);
0523     socket->write(buf);
0524     rcptlist.removeFirst();
0525     vlist[1] = rcptlist;
0526     cmd.data = vlist;
0527 
0528     if (rcptlist.isEmpty()) cmd.extra = 1;
0529 }
0530 
0531 
0532 
0533 void QwwSmtpClientPrivate::sendAuthPlain(const QString & username, const QString & password) {
0534     QByteArray ba;
0535     ba.append('\0');
0536     ba.append(username.toUtf8());
0537     ba.append('\0');
0538     ba.append(password.toUtf8());
0539     QByteArray encoded = ba.toBase64();
0540     emit q->logSent(QByteArrayLiteral("*** [sending authentication data: username '") + username.toUtf8() + "']");
0541     socket->write(encoded);
0542     socket->write("\r\n");
0543 }
0544 
0545 void QwwSmtpClientPrivate::sendAuthLogin(const QString & username, const QString & password, int stage) {
0546     if (stage==1) {
0547         QByteArray buf = username.toUtf8().toBase64() + "\r\n";
0548         emit q->logSent(buf);
0549         socket->write(buf);
0550     } else if (stage==2) {
0551         emit q->logSent("*** [AUTH LOGIN password]");
0552         socket->write(password.toUtf8().toBase64());
0553         socket->write("\r\n");
0554     }
0555 }
0556 
0557 void QwwSmtpClientPrivate::parseOption(const QString &buffer){
0558     if(buffer.toLower()=="pipelining"){                     options |= QwwSmtpClient::PipeliningOption;     }
0559     else if(buffer.toLower()=="starttls"){                  options |= QwwSmtpClient::StartTlsOption;       }
0560     else if(buffer.toLower()=="8bitmime"){                  options |= QwwSmtpClient::EightBitMimeOption;   }
0561     else if(buffer.toLower().startsWith("auth ")){          options |= QwwSmtpClient::AuthOption;
0562         // parse auth modes
0563         QStringList slist = buffer.mid(5).split(" ");
0564         foreach(const QString &s, slist){
0565             if(s.toLower()=="plain"){
0566                 authModes |= QwwSmtpClient::AuthPlain;
0567             }
0568             if(s.toLower()=="login"){
0569                 authModes |= QwwSmtpClient::AuthLogin;
0570             }
0571         }
0572     }
0573 }
0574 
0575 
0576 QwwSmtpClient::QwwSmtpClient(QObject *parent)
0577         : QObject(parent), d(new QwwSmtpClientPrivate(this)) {
0578     d->state = Disconnected;
0579     d->lastId = 0;
0580     d->inProgress = false;
0581     d->localName = "localhost";
0582     d->socket = new QSslSocket(this);
0583     connect(d->socket, SIGNAL(connected()), this, SLOT(onConnected()));
0584     connect(d->socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)) );
0585     connect(d->socket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
0586     connect(d->socket, SIGNAL(readyRead()), this, SLOT(_q_readFromSocket()));
0587     connect(d->socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SIGNAL(sslErrors(const QList<QSslError>&)));
0588 }
0589 
0590 
0591 QwwSmtpClient::~QwwSmtpClient() {
0592     delete d;
0593 }
0594 
0595 int QwwSmtpClient::connectToHost(const QString & hostName, quint16 port) {
0596     SMTPCommand cmd;
0597     cmd.type = SMTPCommand::Connect;
0598     cmd.data = QVariantList() << hostName << port << false;
0599     cmd.id = ++d->lastId;
0600     d->commandqueue.enqueue(cmd);
0601     if (!d->inProgress)
0602         d->processNextCommand();
0603     return cmd.id;
0604 }
0605 
0606 // int QwwSmtpClient::connectToHost(const QHostAddress & address, quint16 port) {
0607 //     d->socket->connectToHost(address, port);
0608 //     d->setState(Connecting);
0609 // }
0610 
0611 
0612 int QwwSmtpClient::connectToHostEncrypted(const QString & hostName, quint16 port)
0613 {
0614     SMTPCommand cmd;
0615     cmd.type = SMTPCommand::Connect;
0616     cmd.data = QVariantList() << hostName << port << true;
0617     cmd.id = ++d->lastId;
0618     d->commandqueue.enqueue(cmd);
0619     if(!d->inProgress)
0620         d->processNextCommand();
0621     return cmd.id;
0622 }
0623 
0624 int QwwSmtpClient::disconnectFromHost() {
0625     SMTPCommand cmd;
0626     cmd.type = SMTPCommand::Disconnect;
0627     cmd.id = ++d->lastId;
0628     d->commandqueue.enqueue(cmd);
0629     if (!d->inProgress)
0630         d->processNextCommand();
0631     return cmd.id;
0632 }
0633 
0634 int QwwSmtpClient::startTls() {
0635     connect(d->socket, SIGNAL(encrypted()), this, SLOT(_q_encrypted()), Qt::UniqueConnection);
0636     SMTPCommand cmd;
0637     cmd.type = SMTPCommand::StartTLS;
0638     cmd.id = ++d->lastId;
0639     d->commandqueue.enqueue(cmd);
0640     if (!d->inProgress)
0641         d->processNextCommand();
0642     return cmd.id;
0643 }
0644 
0645 void QwwSmtpClient::setLocalName(const QString & ln) {
0646     d->localName = ln;
0647 }
0648 
0649 void QwwSmtpClient::setLocalNameEncrypted(const QString & ln) {
0650     d->localNameEncrypted = ln;
0651 }
0652 
0653 
0654 int QwwSmtpClient::authenticate(const QString &user, const QString &password, AuthMode mode) {
0655     SMTPCommand cmd;
0656     cmd.type = SMTPCommand::Authenticate;
0657     cmd.data = QVariantList() << (int)mode << user << password;
0658     cmd.id = ++d->lastId;
0659     d->commandqueue.enqueue(cmd);
0660     if (!d->inProgress)
0661         d->processNextCommand();
0662     return cmd.id;
0663 }
0664 
0665 int QwwSmtpClient::sendMail(const QByteArray &from, const QList<QByteArray> &to, const QByteArray &content)
0666 {
0667     QList<QVariant> rcpts;
0668     for(QList<QByteArray>::const_iterator it = to.begin(); it != to.end(); it ++) {
0669         rcpts.append(QVariant(*it));
0670     }
0671     SMTPCommand cmd;
0672     cmd.type = SMTPCommand::Mail;
0673     cmd.data = QVariantList() << from << QVariant(rcpts) << content;
0674     cmd.id = ++d->lastId;
0675     d->commandqueue.enqueue(cmd);
0676     if (!d->inProgress)
0677         d->processNextCommand();
0678     return cmd.id;
0679 }
0680 
0681 int QwwSmtpClient::sendMailBurl(const QByteArray &from, const QList<QByteArray> &to, const QByteArray &url)
0682 {
0683     QList<QVariant> rcpts;
0684     for(QList<QByteArray>::const_iterator it = to.begin(); it != to.end(); it ++) {
0685         rcpts.append(QVariant(*it));
0686     }
0687     SMTPCommand cmd;
0688     cmd.type = SMTPCommand::MailBurl;
0689     cmd.data = QVariantList() << from << QVariant(rcpts) << url;
0690     cmd.id = ++d->lastId;
0691     d->commandqueue.enqueue(cmd);
0692     if (!d->inProgress)
0693         d->processNextCommand();
0694     return cmd.id;
0695 }
0696 
0697 int QwwSmtpClient::rawCommand(const QString & raw) {
0698     SMTPCommand cmd;
0699     cmd.type = SMTPCommand::RawCommand;
0700     cmd.data = raw;
0701     cmd.id = ++d->lastId;
0702     d->commandqueue.enqueue(cmd);
0703     if (!d->inProgress)
0704         d->processNextCommand();
0705     return cmd.id;
0706 }
0707 
0708 
0709 void QwwSmtpClientPrivate::abortDialog() {
0710     emit q->commandFinished(commandqueue.head().id, true);
0711     commandqueue.clear();
0712     sendQuit();
0713 }
0714 
0715 void QwwSmtpClient::ignoreSslErrors()
0716 {d->socket->ignoreSslErrors();
0717 }
0718 
0719 QwwSmtpClient::AuthModes QwwSmtpClient::supportedAuthModes() const{
0720     return d->authModes;
0721 }
0722 
0723 QwwSmtpClient::Options QwwSmtpClient::options() const{
0724     return d->options;
0725 }
0726 
0727 QString QwwSmtpClient::errorString() const{
0728     return d->errorString;
0729 }
0730 
0731 #include "moc_qwwsmtpclient.cpp"