KSMTP

sendjob.cpp
1/*
2 SPDX-FileCopyrightText: 2010 BetterInbox <contact@betterinbox.com>
3 SPDX-FileContributor: Christophe Laveault <christophe@betterinbox.com>
4 SPDX-FileContributor: Gregory Schlomoff <gregory.schlomoff@gmail.com>
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
16namespace KSmtp
17{
18class SendJobPrivate : public JobPrivate
19{
20public:
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;
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
51using namespace KSmtp;
52
53SendJob::SendJob(Session *session)
54 : Job(*new SendJobPrivate(this, session, i18n("SendJob")))
55{
56}
57
58void 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
71{
72 Q_D(SendJob);
73 d->addRecipients(to);
74}
75
77{
78 Q_D(SendJob);
79 d->addRecipients(cc);
80}
81
83{
84 Q_D(SendJob);
85 d->addRecipients(bcc);
86}
87
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
97void 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
120void 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
171void 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
178void 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
195bool 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
212int 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)
void emitResult()
void setError(int errorCode)
The Job class.
Definition job.h:25
The SendJob class.
Definition sendjob.h:22
void setCc(const QStringList &cc)
Add recipients.
Definition sendjob.cpp:76
void setBcc(const QStringList &bcc)
Add recipients.
Definition sendjob.cpp:82
void setData(const QByteArray &data)
Set the actual message data.
Definition sendjob.cpp:88
void setDeliveryStatusNotification(bool b)
Set Delivery Status Notification.
Definition sendjob.cpp:219
void setFrom(const QString &from)
Set the sender email address.
Definition sendjob.cpp:58
int size() const
Returns size of the encoded message data.
Definition sendjob.cpp:212
void setTo(const QStringList &to)
Add recipients.
Definition sendjob.cpp:70
int sizeLimit() const
Returns the maximum message size in bytes that the server accepts.
Definition session.cpp:161
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18n(const char *text, const TYPE &arg...)
const QList< QKeySequence > & end()
QString name(StandardShortcut id)
bool isEmpty() const const
QByteArray & replace(QByteArrayView before, QByteArrayView after)
bool isEmpty() const const
void push_back(parameter_type value)
value_type takeFirst()
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
QString mid(qsizetype position, qsizetype n) const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:37 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.