File indexing completed on 2024-04-28 15:32:37
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"