KSMTP

sendjob.cpp
1 /*
2  SPDX-FileCopyrightText: 2010 BetterInbox <[email protected]>
3  SPDX-FileContributor: Christophe Laveault <[email protected]>
4  SPDX-FileContributor: Gregory Schlomoff <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.1-or-later
7 */
8 
9 #include "sendjob.h"
10 #include "job_p.h"
11 #include "ksmtp_debug.h"
12 #include "serverresponse_p.h"
13 
14 #include <KLocalizedString>
15 
16 namespace KSmtp
17 {
18 class SendJobPrivate : public JobPrivate
19 {
20 public:
21  enum Status { Idle, SendingReturnPath, SendingRecipients, SendingData };
22 
23  SendJobPrivate(SendJob *job, Session *session, const QString &name)
24  : JobPrivate(session, name)
25  , q(job)
26  {
27  }
28 
29  SendJob *const q;
30 
31  void sendNextRecipient();
32  void addRecipients(const QStringList &rcpts);
33  bool prepare();
34 
35  using MessagePart = struct {
36  QString contentType;
37  QString name;
38  QByteArray content;
39  };
40 
41  QString m_returnPath;
42  QStringList m_recipients;
43  QByteArray m_data;
44 
45  QStringList m_recipientsCopy;
46  Status m_status = Idle;
47  bool m_dsn = false;
48 };
49 }
50 
51 using namespace KSmtp;
52 
53 SendJob::SendJob(Session *session)
54  : Job(*new SendJobPrivate(this, session, i18n("SendJob")))
55 {
56 }
57 
58 void SendJob::setFrom(const QString &from)
59 {
60  Q_D(SendJob);
61  const auto start = from.indexOf(QLatin1Char('<'));
62  if (start > -1) {
63  const auto end = qMax(start, from.indexOf(QLatin1Char('>'), start));
64  d->m_returnPath = QStringLiteral("<%1>").arg(from.mid(start + 1, end - start - 1));
65  } else {
66  d->m_returnPath = QStringLiteral("<%1>").arg(from);
67  }
68 }
69 
70 void SendJob::setTo(const QStringList &to)
71 {
72  Q_D(SendJob);
73  d->addRecipients(to);
74 }
75 
76 void SendJob::setCc(const QStringList &cc)
77 {
78  Q_D(SendJob);
79  d->addRecipients(cc);
80 }
81 
82 void SendJob::setBcc(const QStringList &bcc)
83 {
84  Q_D(SendJob);
85  d->addRecipients(bcc);
86 }
87 
88 void SendJob::setData(const QByteArray &data)
89 {
90  Q_D(SendJob);
91  d->m_data = data;
92  // A line with a single dot would make SMTP think "end of message", so use two dots in that case,
93  // as per https://tools.ietf.org/html/rfc5321#section-4.5.2
94  d->m_data.replace("\r\n.", "\r\n..");
95 }
96 
97 void SendJob::doStart()
98 {
99  Q_D(SendJob);
100 
101  if (!d->prepare()) {
102  setError(KJob::UserDefinedError);
103  setErrorText(i18n("Could not send the message because either the sender or recipient field is missing or invalid"));
104  emitResult();
105  return;
106  }
107 
108  const int sizeLimit = session()->sizeLimit();
109  if (sizeLimit > 0 && size() > sizeLimit) {
110  setError(KJob::UserDefinedError);
111  setErrorText(i18n("Could not send the message because it exceeds the maximum allowed size of %1 bytes. (Message size: %2 bytes.)", sizeLimit, size()));
112  emitResult();
113  return;
114  }
115 
116  d->m_status = SendJobPrivate::SendingReturnPath;
117  sendCommand("MAIL FROM:" + d->m_returnPath.toUtf8());
118 }
119 
120 void SendJob::handleResponse(const ServerResponse &r)
121 {
122  Q_D(SendJob);
123 
124  // Handle server errors
125  handleErrors(r);
126 
127  switch (d->m_status) {
128  case SendJobPrivate::Idle:
129  // TODO: anything to do here?
130  break;
131 
132  case SendJobPrivate::SendingReturnPath:
133 
134  // Expected response: server agreement
135  if (r.isCode(25)) {
136  d->m_status = SendJobPrivate::SendingRecipients;
137  d->sendNextRecipient();
138  }
139  break;
140 
141  case SendJobPrivate::SendingRecipients:
142 
143  // Expected response: server agreement
144  if (r.isCode(25)) {
145  if (d->m_recipientsCopy.isEmpty()) {
146  sendCommand("DATA");
147  d->m_status = SendJobPrivate::SendingData;
148  } else {
149  d->sendNextRecipient();
150  }
151  }
152  break;
153 
154  case SendJobPrivate::SendingData:
155 
156  // Expected responses:
157  // 354: Go ahead sending data
158  if (r.isCode(354)) {
159  sendCommand(d->m_data);
160  sendCommand("\r\n.");
161  }
162 
163  // 25x: Data received correctly
164  if (r.isCode(25)) {
165  emitResult();
166  }
167  break;
168  }
169 }
170 
171 void SendJobPrivate::sendNextRecipient()
172 {
173  const bool dsnSupport = m_session->allowsDsn() ? m_dsn : false;
174  // qDebug() << " void SendJobPrivate::sendNextRecipient()" << m_session->allowsDsn() << " dsnSupport " << dsnSupport;
175  q->sendCommand("RCPT TO:<" + m_recipientsCopy.takeFirst().toUtf8() + '>' + (dsnSupport ? " NOTIFY=success,failure" : ""));
176 }
177 
178 void SendJobPrivate::addRecipients(const QStringList &rcpts)
179 {
180  for (const auto &rcpt : rcpts) {
181  if (rcpt.isEmpty()) {
182  continue;
183  }
184 
185  const int start = rcpt.indexOf(QLatin1Char('<'));
186  if (start > -1) {
187  const int end = qMax(start, rcpt.indexOf(QLatin1Char('>'), start));
188  m_recipients.push_back(rcpt.mid(start + 1, end - start - 1));
189  } else {
190  m_recipients.push_back(rcpt);
191  }
192  }
193 }
194 
195 bool SendJobPrivate::prepare()
196 {
197  if (m_data.isEmpty()) {
198  qCWarning(KSMTP_LOG) << "A message has to be set before starting a SendJob";
199  return false;
200  }
201 
202  m_recipientsCopy = m_recipients;
203 
204  if (m_recipients.isEmpty()) {
205  qCWarning(KSMTP_LOG) << "Message has no recipients";
206  return false;
207  }
208 
209  return true;
210 }
211 
212 int SendJob::size() const
213 {
214  Q_D(const SendJob);
215 
216  return d->m_data.size();
217 }
218 
220 {
221  Q_D(SendJob);
222  d->m_dsn = enabled;
223 }
224 
225 #include "moc_sendjob.cpp"
void setErrorText(const QString &errorText)
The Session class.
Definition: session.h:23
Q_SCRIPTABLE Q_NOREPLY void start()
void setBcc(const QStringList &bcc)
Add recipients.
Definition: sendjob.cpp:82
QString i18n(const char *text, const TYPE &arg...)
void setFrom(const QString &from)
Set the sender email address.
Definition: sendjob.cpp:58
void setTo(const QStringList &to)
Add recipients.
Definition: sendjob.cpp:70
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QByteArray & replace(int pos, int len, const char *after)
int sizeLimit() const
Returns the maximum message size in bytes that the server accepts.
Definition: session.cpp:161
void setCc(const QStringList &cc)
Add recipients.
Definition: sendjob.cpp:76
const char * name(StandardAction id)
int size() const
Returns size of the encoded message data.
Definition: sendjob.cpp:212
void setDeliveryStatusNotification(bool b)
Set Delivery Status Notification.
Definition: sendjob.cpp:219
void setData(const QByteArray &data)
Set the actual message data.
Definition: sendjob.cpp:88
void emitResult()
The SendJob class.
Definition: sendjob.h:21
QString mid(int position, int n) const const
The Job class.
Definition: job.h:24
void setError(int errorCode)
const QList< QKeySequence > & end()
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Nov 29 2023 04:08:02 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.