KXmlGui

smtp.cpp
1 /*
2  SPDX-FileCopyrightText: 2000 Bernd Johannes Wuebben <[email protected]>
3  SPDX-FileCopyrightText: 2000 Stephan Kulow <[email protected]>
4 
5  SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #include "smtp.h"
9 #include "../systeminformation_p.h"
10 
11 #include <stdio.h>
12 
13 #include <QSslSocket>
14 #include <QHostInfo>
15 
16 SMTP::SMTP(char *serverhost, unsigned short int port, int timeout)
17 {
18  serverHost = QString::fromUtf8(serverhost);
19  hostPort = port;
20  timeOut = timeout * 1000;
21 
22  senderAddress = QStringLiteral("[email protected]");
23  recipientAddress = QStringLiteral("[email protected]");
24  messageSubject = QStringLiteral("(no subject)");
25  messageBody = QStringLiteral("empty");
26  messageHeader = QLatin1String("");
27 
28  connected = false;
29  finished = false;
30 
31  sock = nullptr;
32  state = Init;
33  serverState = None;
34 
35  domainName = QHostInfo::localDomainName();
36  if (domainName.isEmpty()) {
37  domainName = QStringLiteral("somemachine.example.net");
38  }
39 
40  // qCDebug(DEBUG_KXMLGUI) << "SMTP object created";
41 
42  connect(&connectTimer, &QTimer::timeout, this, &SMTP::connectTimerTick);
43  connect(&timeOutTimer, &QTimer::timeout, this, &SMTP::connectTimedOut);
44  connect(&interactTimer, &QTimer::timeout, this, &SMTP::interactTimedOut);
45 
46  // some sendmail will give 'duplicate helo' error, quick fix for now
47  connect(this, &SMTP::messageSent, this, &SMTP::closeConnection);
48 }
49 
50 SMTP::~SMTP()
51 {
52  delete sock;
53  sock = nullptr;
54  connectTimer.stop();
55  timeOutTimer.stop();
56 }
57 
58 void SMTP::setServerHost(const QString &serverhost)
59 {
60  serverHost = serverhost;
61 }
62 
63 void SMTP::setPort(unsigned short int port)
64 {
65  hostPort = port;
66 }
67 
68 void SMTP::setTimeOut(int timeout)
69 {
70  timeOut = timeout;
71 }
72 
73 void SMTP::setSenderAddress(const QString &sender)
74 {
75  senderAddress = sender;
76  int index = senderAddress.indexOf(QLatin1Char('<'));
77  if (index == -1) {
78  return;
79  }
80  senderAddress.remove(0, index + 1);
81  index = senderAddress.indexOf(QLatin1Char('>'));
82  if (index != -1) {
83  senderAddress.truncate(index);
84  }
85  senderAddress = senderAddress.simplified();
86  while (1) {
87  index = senderAddress.indexOf(QLatin1Char(' '));
88  if (index != -1) {
89  senderAddress.remove(0, index + 1); // take one side
90  } else {
91  break;
92  }
93  }
94  index = senderAddress.indexOf(QLatin1Char('@'));
95  if (index == -1) {
96  senderAddress.append(QLatin1String("@localhost")); // won't go through without a local mail system
97  }
98 
99 }
100 
101 void SMTP::setRecipientAddress(const QString &recipient)
102 {
103  recipientAddress = recipient;
104 }
105 
106 void SMTP::setMessageSubject(const QString &subject)
107 {
108  messageSubject = subject;
109 }
110 
111 void SMTP::setMessageBody(const QString &message)
112 {
113  messageBody = message;
114 }
115 
116 void SMTP::setMessageHeader(const QString &header)
117 {
118  messageHeader = header;
119 }
120 
121 void SMTP::openConnection()
122 {
123  // qCDebug(DEBUG_KXMLGUI) << "started connect timer";
124  connectTimer.setSingleShot(true);
125  connectTimer.start(100);
126 }
127 
128 void SMTP::closeConnection()
129 {
130  socketClosed();
131 }
132 
133 void SMTP::sendMessage()
134 {
135  if (!connected) {
136  connectTimerTick();
137  }
138  if (state == Finished && connected) {
139  // qCDebug(DEBUG_KXMLGUI) << "state was == Finished\n";
140  finished = false;
141  state = In;
142  writeString = QStringLiteral("helo %1\r\n").arg(domainName);
143  sock->write(writeString.toLatin1().constData(), writeString.length());
144  }
145  if (connected) {
146  // qCDebug(DEBUG_KXMLGUI) << "enabling read on sock...\n";
147  interactTimer.setSingleShot(true);
148  interactTimer.start(timeOut);
149  }
150 }
151 
152 void SMTP::connectTimerTick()
153 {
154  connectTimer.stop();
155 // timeOutTimer.start(timeOut, true);
156 
157  // qCDebug(DEBUG_KXMLGUI) << "connectTimerTick called...";
158 
159  delete sock;
160  sock = nullptr;
161 
162  // qCDebug(DEBUG_KXMLGUI) << "connecting to " << serverHost << ":" << hostPort << " ..... ";
163  sock = new QSslSocket(this);
164  sock->connectToHost(serverHost, hostPort);
165 
166  connected = true;
167  finished = false;
168  state = Init;
169  serverState = None;
170 
171  connect(sock, &QIODevice::readyRead,
172  this, &SMTP::socketReadyToRead);
173 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
174  connect(sock, &QAbstractSocket::errorOccurred,
175 #else
176  connect(sock, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
177 #endif
178  this, &SMTP::socketError);
179  connect(sock, &QAbstractSocket::disconnected,
180  this, &SMTP::socketClosed);
181  timeOutTimer.stop();
182  // qCDebug(DEBUG_KXMLGUI) << "connected";
183 }
184 
185 void SMTP::connectTimedOut()
186 {
187  timeOutTimer.stop();
188 
189  // qCDebug(DEBUG_KXMLGUI) << "socket connection timed out";
190  socketClosed();
191  emit error(ConnectTimeout);
192 }
193 
194 void SMTP::interactTimedOut()
195 {
196  interactTimer.stop();
197 
198  // qCDebug(DEBUG_KXMLGUI) << "time out waiting for server interaction";
199  socketClosed();
200  emit error(InteractTimeout);
201 }
202 
203 void SMTP::socketReadyToRead()
204 {
205  int n, nl;
206 
207  // qCDebug(DEBUG_KXMLGUI) << "socketRead() called...";
208  interactTimer.stop();
209 
210  if (!sock) {
211  return;
212  }
213 
214  n = sock->read(readBuffer, SMTP_READ_BUFFER_SIZE - 1);
215  if (n < 0) {
216  return;
217  }
218  readBuffer[n] = 0;
219  lineBuffer += QByteArray(readBuffer);
220  nl = lineBuffer.indexOf('\n');
221  if (nl == -1) {
222  return;
223  }
224  lastLine = lineBuffer.left(nl);
225  lineBuffer = lineBuffer.right(lineBuffer.length() - nl - 1);
226  processLine(&lastLine);
227  if (connected) {
228  interactTimer.setSingleShot(true);
229  interactTimer.start(timeOut);
230  }
231 }
232 
233 void SMTP::socketError(QAbstractSocket::SocketError socketError)
234 {
235  // qCDebug(DEBUG_KXMLGUI) << socketError << sock->errorString();
236  Q_UNUSED(socketError);
237  emit error(ConnectError);
238  socketClosed();
239 }
240 
241 void SMTP::socketClosed()
242 {
243  timeOutTimer.stop();
244  // qCDebug(DEBUG_KXMLGUI) << "connection terminated";
245  connected = false;
246  if (sock) {
247  sock->deleteLater();
248  }
249  sock = nullptr;
250  emit connectionClosed();
251 }
252 
253 void SMTP::processLine(QByteArray *line)
254 {
255  int i, stat;
256  QByteArray tmpstr;
257 
258  i = line->indexOf(' ');
259  tmpstr = line->left(i);
260  if (i > 3) {
261  // qCDebug(DEBUG_KXMLGUI) << "warning: SMTP status code longer than 3 digits: " << tmpstr;
262  }
263  stat = tmpstr.toInt();
264  serverState = static_cast<SMTPServerStatus>(stat);
265  lastState = state;
266 
267  // qCDebug(DEBUG_KXMLGUI) << "smtp state: [" << stat << "][" << *line << "]";
268 
269  switch (stat) {
270  case Greet: //220
271  state = In;
272  writeString = QStringLiteral("helo %1\r\n").arg(domainName);
273  // qCDebug(DEBUG_KXMLGUI) << "out: " << writeString;
274  sock->write(writeString.toLatin1().constData(), writeString.length());
275  break;
276  case Goodbye: //221
277  state = Quit;
278  break;
279  case Successful://250
280  switch (state) {
281  case In:
282  state = Ready;
283  writeString = QStringLiteral("mail from: %1\r\n").arg(senderAddress);
284  // qCDebug(DEBUG_KXMLGUI) << "out: " << writeString;
285  sock->write(writeString.toLatin1().constData(), writeString.length());
286  break;
287  case Ready:
288  state = SentFrom;
289  writeString = QStringLiteral("rcpt to: %1\r\n").arg(recipientAddress);
290  // qCDebug(DEBUG_KXMLGUI) << "out: " << writeString;
291  sock->write(writeString.toLatin1().constData(), writeString.length());
292  break;
293  case SentFrom:
294  state = SentTo;
295  writeString = QStringLiteral("data\r\n");
296  // qCDebug(DEBUG_KXMLGUI) << "out: " << writeString;
297  sock->write(writeString.toLatin1().constData(), writeString.length());
298  break;
299  case Data:
300  state = Finished;
301  finished = true;
302  emit messageSent();
303  break;
304  default:
305  state = CError;
306  // qCDebug(DEBUG_KXMLGUI) << "smtp error (state error): [" << lastState << "]:[" << stat << "][" << *line << "]";
307  socketClosed();
308  emit error(Command);
309  break;
310  }
311  break;
312  case ReadyData: //354
313  state = Data;
314  writeString = QStringLiteral("Subject: %1\r\n").arg(messageSubject);
315  writeString += messageHeader;
316  writeString += QLatin1String("\r\n");
317  writeString += messageBody;
318  writeString += QLatin1String(".\r\n");
319  // qCDebug(DEBUG_KXMLGUI) << "out: " << writeString;
320  sock->write(writeString.toLatin1().constData(), writeString.length());
321  break;
322  case Error: //501
323  state = CError;
324  // qCDebug(DEBUG_KXMLGUI) << "smtp error (command error): [" << lastState << "]:[" << stat << "][" << *line << "]\n";
325  socketClosed();
326  emit error(Command);
327  break;
328  case Unknown: //550
329  state = CError;
330  // qCDebug(DEBUG_KXMLGUI) << "smtp error (unknown user): [" << lastState << "]:[" << stat << "][" << *line << "]";
331  socketClosed();
332  emit error(UnknownUser);
333  break;
334  default:
335  state = CError;
336  // qCDebug(DEBUG_KXMLGUI) << "unknown response: [" << lastState << "]:[" << stat << "][" << *line << "]";
337  socketClosed();
338  emit error(UnknownResponse);
339  }
340 }
341 
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
QAbstractSocket::SocketError error() const const
void timeout()
int indexOf(char ch, int from) const const
QString fromUtf8(const char *str, int size)
void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Notify)
QString localDomainName()
QByteArray left(int len) const const
void errorOccurred(QAbstractSocket::SocketError socketError)
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
void readyRead()
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Wed Aug 12 2020 22:50:46 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.