File indexing completed on 2024-04-28 11:47:17

0001 /*
0002     SPDX-FileCopyrightText: 2000 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
0003     SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "smtp.h"
0009 #include "../systeminformation_p.h"
0010 
0011 #include <cstdio>
0012 
0013 #include <QHostInfo>
0014 #include <QSslSocket>
0015 
0016 SMTP::SMTP(char *serverhost, unsigned short int port, int timeout)
0017     : serverHost(QString::fromUtf8(serverhost))
0018     , hostPort(port)
0019     , timeOut(timeout * 1000)
0020     , connected(false)
0021     , finished(false)
0022     , senderAddress(QStringLiteral("user@example.net"))
0023     , recipientAddress(QStringLiteral("user@example.net"))
0024     , messageSubject(QStringLiteral("(no subject)"))
0025     , messageBody(QStringLiteral("empty"))
0026     , messageHeader(QLatin1String(""))
0027     , state(Init)
0028     , serverState(None)
0029     , domainName(QHostInfo::localDomainName())
0030     , sock(nullptr)
0031 {
0032     if (domainName.isEmpty()) {
0033         domainName = QStringLiteral("somemachine.example.net");
0034     }
0035 
0036     // qCDebug(DEBUG_KXMLGUI) << "SMTP object created";
0037 
0038     connect(&connectTimer, &QTimer::timeout, this, &SMTP::connectTimerTick);
0039     connect(&timeOutTimer, &QTimer::timeout, this, &SMTP::connectTimedOut);
0040     connect(&interactTimer, &QTimer::timeout, this, &SMTP::interactTimedOut);
0041 
0042     // some sendmail will give 'duplicate helo' error, quick fix for now
0043     connect(this, &SMTP::messageSent, this, &SMTP::closeConnection);
0044 }
0045 
0046 SMTP::~SMTP()
0047 {
0048     delete sock;
0049     sock = nullptr;
0050     connectTimer.stop();
0051     timeOutTimer.stop();
0052 }
0053 
0054 void SMTP::setServerHost(const QString &serverhost)
0055 {
0056     serverHost = serverhost;
0057 }
0058 
0059 void SMTP::setPort(unsigned short int port)
0060 {
0061     hostPort = port;
0062 }
0063 
0064 void SMTP::setTimeOut(int timeout)
0065 {
0066     timeOut = timeout;
0067 }
0068 
0069 void SMTP::setSenderAddress(const QString &sender)
0070 {
0071     senderAddress = sender;
0072     int index = senderAddress.indexOf(QLatin1Char('<'));
0073     if (index == -1) {
0074         return;
0075     }
0076     senderAddress.remove(0, index + 1);
0077     index = senderAddress.indexOf(QLatin1Char('>'));
0078     if (index != -1) {
0079         senderAddress.truncate(index);
0080     }
0081     senderAddress = senderAddress.simplified();
0082     while (1) {
0083         index = senderAddress.indexOf(QLatin1Char(' '));
0084         if (index != -1) {
0085             senderAddress.remove(0, index + 1); // take one side
0086         } else {
0087             break;
0088         }
0089     }
0090     index = senderAddress.indexOf(QLatin1Char('@'));
0091     if (index == -1) {
0092         senderAddress.append(QLatin1String("@localhost")); // won't go through without a local mail system
0093     }
0094 }
0095 
0096 void SMTP::setRecipientAddress(const QString &recipient)
0097 {
0098     recipientAddress = recipient;
0099 }
0100 
0101 void SMTP::setMessageSubject(const QString &subject)
0102 {
0103     messageSubject = subject;
0104 }
0105 
0106 void SMTP::setMessageBody(const QString &message)
0107 {
0108     messageBody = message;
0109 }
0110 
0111 void SMTP::setMessageHeader(const QString &header)
0112 {
0113     messageHeader = header;
0114 }
0115 
0116 void SMTP::openConnection()
0117 {
0118     // qCDebug(DEBUG_KXMLGUI) << "started connect timer";
0119     connectTimer.setSingleShot(true);
0120     connectTimer.start(100);
0121 }
0122 
0123 void SMTP::closeConnection()
0124 {
0125     socketClosed();
0126 }
0127 
0128 void SMTP::sendMessage()
0129 {
0130     if (!connected) {
0131         connectTimerTick();
0132     }
0133     if (state == Finished && connected) {
0134         // qCDebug(DEBUG_KXMLGUI) << "state was == Finished\n";
0135         finished = false;
0136         state = In;
0137         writeString = QStringLiteral("helo %1\r\n").arg(domainName);
0138         sock->write(writeString.toLatin1().constData(), writeString.length());
0139     }
0140     if (connected) {
0141         // qCDebug(DEBUG_KXMLGUI) << "enabling read on sock...\n";
0142         interactTimer.setSingleShot(true);
0143         interactTimer.start(timeOut);
0144     }
0145 }
0146 
0147 void SMTP::connectTimerTick()
0148 {
0149     connectTimer.stop();
0150     //    timeOutTimer.start(timeOut, true);
0151 
0152     // qCDebug(DEBUG_KXMLGUI) << "connectTimerTick called...";
0153 
0154     delete sock;
0155     sock = nullptr;
0156 
0157     // qCDebug(DEBUG_KXMLGUI) << "connecting to " << serverHost << ":" << hostPort << " ..... ";
0158     sock = new QSslSocket(this);
0159     sock->connectToHost(serverHost, hostPort);
0160 
0161     connected = true;
0162     finished = false;
0163     state = Init;
0164     serverState = None;
0165 
0166     connect(sock, &QIODevice::readyRead, this, &SMTP::socketReadyToRead);
0167     connect(sock, &QAbstractSocket::errorOccurred, this, &SMTP::socketError);
0168     connect(sock, &QAbstractSocket::disconnected, this, &SMTP::socketClosed);
0169     timeOutTimer.stop();
0170     // qCDebug(DEBUG_KXMLGUI) << "connected";
0171 }
0172 
0173 void SMTP::connectTimedOut()
0174 {
0175     timeOutTimer.stop();
0176 
0177     // qCDebug(DEBUG_KXMLGUI) << "socket connection timed out";
0178     socketClosed();
0179     Q_EMIT error(ConnectTimeout);
0180 }
0181 
0182 void SMTP::interactTimedOut()
0183 {
0184     interactTimer.stop();
0185 
0186     // qCDebug(DEBUG_KXMLGUI) << "time out waiting for server interaction";
0187     socketClosed();
0188     Q_EMIT error(InteractTimeout);
0189 }
0190 
0191 void SMTP::socketReadyToRead()
0192 {
0193     int n;
0194     int nl;
0195 
0196     // qCDebug(DEBUG_KXMLGUI) << "socketRead() called...";
0197     interactTimer.stop();
0198 
0199     if (!sock) {
0200         return;
0201     }
0202 
0203     n = sock->read(readBuffer, SMTP_READ_BUFFER_SIZE - 1);
0204     if (n < 0) {
0205         return;
0206     }
0207     readBuffer[n] = 0;
0208     lineBuffer += QByteArray(readBuffer);
0209     nl = lineBuffer.indexOf('\n');
0210     if (nl == -1) {
0211         return;
0212     }
0213     lastLine = lineBuffer.left(nl);
0214     lineBuffer = lineBuffer.right(lineBuffer.length() - nl - 1);
0215     processLine(&lastLine);
0216     if (connected) {
0217         interactTimer.setSingleShot(true);
0218         interactTimer.start(timeOut);
0219     }
0220 }
0221 
0222 void SMTP::socketError(QAbstractSocket::SocketError socketError)
0223 {
0224     // qCDebug(DEBUG_KXMLGUI) << socketError << sock->errorString();
0225     Q_UNUSED(socketError);
0226     Q_EMIT error(ConnectError);
0227     socketClosed();
0228 }
0229 
0230 void SMTP::socketClosed()
0231 {
0232     timeOutTimer.stop();
0233     // qCDebug(DEBUG_KXMLGUI) << "connection terminated";
0234     connected = false;
0235     if (sock) {
0236         sock->deleteLater();
0237     }
0238     sock = nullptr;
0239     Q_EMIT connectionClosed();
0240 }
0241 
0242 void SMTP::processLine(QByteArray *line)
0243 {
0244     int i;
0245     int stat;
0246     QByteArray tmpstr;
0247 
0248     i = line->indexOf(' ');
0249     tmpstr = line->left(i);
0250     if (i > 3) {
0251         // qCDebug(DEBUG_KXMLGUI) << "warning: SMTP status code longer than 3 digits: " << tmpstr;
0252     }
0253     stat = tmpstr.toInt();
0254     serverState = static_cast<SMTPServerStatus>(stat);
0255     lastState = state;
0256 
0257     // qCDebug(DEBUG_KXMLGUI) << "smtp state: [" << stat << "][" << *line << "]";
0258 
0259     switch (stat) {
0260     case Greet: // 220
0261         state = In;
0262         writeString = QStringLiteral("helo %1\r\n").arg(domainName);
0263         // qCDebug(DEBUG_KXMLGUI) << "out: " << writeString;
0264         sock->write(writeString.toLatin1().constData(), writeString.length());
0265         break;
0266     case Goodbye: // 221
0267         state = Quit;
0268         break;
0269     case Successful: // 250
0270         switch (state) {
0271         case In:
0272             state = Ready;
0273             writeString = QStringLiteral("mail from: %1\r\n").arg(senderAddress);
0274             // qCDebug(DEBUG_KXMLGUI) << "out: " << writeString;
0275             sock->write(writeString.toLatin1().constData(), writeString.length());
0276             break;
0277         case Ready:
0278             state = SentFrom;
0279             writeString = QStringLiteral("rcpt to: %1\r\n").arg(recipientAddress);
0280             // qCDebug(DEBUG_KXMLGUI) << "out: " << writeString;
0281             sock->write(writeString.toLatin1().constData(), writeString.length());
0282             break;
0283         case SentFrom:
0284             state = SentTo;
0285             writeString = QStringLiteral("data\r\n");
0286             // qCDebug(DEBUG_KXMLGUI) << "out: " << writeString;
0287             sock->write(writeString.toLatin1().constData(), writeString.length());
0288             break;
0289         case Data:
0290             state = Finished;
0291             finished = true;
0292             Q_EMIT messageSent();
0293             break;
0294         default:
0295             state = CError;
0296             // qCDebug(DEBUG_KXMLGUI) << "smtp error (state error): [" << lastState << "]:[" << stat << "][" << *line << "]";
0297             socketClosed();
0298             Q_EMIT error(Command);
0299             break;
0300         }
0301         break;
0302     case ReadyData: // 354
0303         state = Data;
0304         writeString = QStringLiteral("Subject: %1\r\n").arg(messageSubject);
0305         writeString += messageHeader;
0306         writeString += QLatin1String("\r\n");
0307         writeString += messageBody;
0308         writeString += QLatin1String(".\r\n");
0309         // qCDebug(DEBUG_KXMLGUI) << "out: " << writeString;
0310         sock->write(writeString.toLatin1().constData(), writeString.length());
0311         break;
0312     case Error: // 501
0313         state = CError;
0314         // qCDebug(DEBUG_KXMLGUI) << "smtp error (command error): [" << lastState << "]:[" << stat << "][" << *line << "]\n";
0315         socketClosed();
0316         Q_EMIT error(Command);
0317         break;
0318     case Unknown: // 550
0319         state = CError;
0320         // qCDebug(DEBUG_KXMLGUI) << "smtp error (unknown user): [" << lastState << "]:[" << stat << "][" << *line << "]";
0321         socketClosed();
0322         Q_EMIT error(UnknownUser);
0323         break;
0324     default:
0325         state = CError;
0326         // qCDebug(DEBUG_KXMLGUI) << "unknown response: [" << lastState << "]:[" << stat << "][" << *line << "]";
0327         socketClosed();
0328         Q_EMIT error(UnknownResponse);
0329     }
0330 }
0331 
0332 #include "moc_smtp.cpp"