• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.14.10 API Reference
  • KDE Home
  • Contact Us
 

mailtransport

  • mailtransport
smtpjob.cpp
1/*
2 Copyright (c) 2007 Volker Krause <vkrause@kde.org>
3
4 Based on KMail code by:
5 Copyright (c) 1996-1998 Stefan Taferner <taferner@kde.org>
6
7 This library is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Library General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at your
10 option) any later version.
11
12 This library is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
15 License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21*/
22
23#include "smtpjob.h"
24#include "transport.h"
25#include "mailtransport_defs.h"
26#include "precommandjob.h"
27#include "smtp/smtpsession.h"
28
29#include <QBuffer>
30#include <QHash>
31#include <QPointer>
32
33#include <KLocalizedString>
34#include <KUrl>
35#include <KDebug>
36#include <KIO/Job>
37#include <KIO/Scheduler>
38#include <KPasswordDialog>
39
40using namespace MailTransport;
41
42class SlavePool
43{
44 public:
45 SlavePool() : ref( 0 ) {}
46 int ref;
47 QHash<int, KIO::Slave*> slaves;
48
49 void removeSlave( KIO::Slave *slave, bool disconnect = false )
50 {
51 kDebug() << "Removing slave" << slave << "from pool";
52 const int slaveKey = slaves.key( slave );
53 if ( slaveKey > 0 ) {
54 slaves.remove( slaveKey );
55 if ( disconnect ) {
56 KIO::Scheduler::disconnectSlave( slave );
57 }
58 }
59 }
60};
61
62K_GLOBAL_STATIC( SlavePool, s_slavePool )
63
64
68class SmtpJobPrivate
69{
70 public:
71 SmtpJobPrivate( SmtpJob *parent ) : q( parent ) {}
72
73 void smtpSessionResult( SmtpSession *session )
74 {
75#ifndef MAILTRANSPORT_INPROCESS_SMTP
76 Q_UNUSED( session );
77#else
78 if ( !session->errorMessage().isEmpty() ) {
79 q->setError( KJob::UserDefinedError );
80 q->setErrorText( session->errorMessage() );
81 }
82 q->emitResult();
83#endif
84 }
85
86 SmtpJob *q;
87 KIO::Slave *slave;
88 enum State {
89 Idle, Precommand, Smtp
90 } currentState;
91 bool finished;
92};
93
94SmtpJob::SmtpJob( Transport *transport, QObject *parent )
95 : TransportJob( transport, parent ), d( new SmtpJobPrivate( this ) )
96{
97 d->currentState = SmtpJobPrivate::Idle;
98 d->slave = 0;
99 d->finished = false;
100 if ( !s_slavePool.isDestroyed() ) {
101 s_slavePool->ref++;
102 }
103 KIO::Scheduler::connect( SIGNAL(slaveError(KIO::Slave*,int,QString)),
104 this, SLOT(slaveError(KIO::Slave*,int,QString)) );
105}
106
107SmtpJob::~SmtpJob()
108{
109 if ( !s_slavePool.isDestroyed() ) {
110 s_slavePool->ref--;
111 if ( s_slavePool->ref == 0 ) {
112 kDebug() << "clearing SMTP slave pool" << s_slavePool->slaves.count();
113 foreach ( KIO::Slave *slave, s_slavePool->slaves ) {
114 if ( slave ) {
115 KIO::Scheduler::disconnectSlave( slave );
116 }
117 }
118 s_slavePool->slaves.clear();
119 }
120 }
121 delete d;
122}
123
124void SmtpJob::doStart()
125{
126 if ( s_slavePool.isDestroyed() ) {
127 return;
128 }
129
130 if ( ( !s_slavePool->slaves.isEmpty() &&
131 s_slavePool->slaves.contains( transport()->id() ) ) ||
132 transport()->precommand().isEmpty() ) {
133 d->currentState = SmtpJobPrivate::Smtp;
134 startSmtpJob();
135 } else {
136 d->currentState = SmtpJobPrivate::Precommand;
137 PrecommandJob *job = new PrecommandJob( transport()->precommand(), this );
138 addSubjob( job );
139 job->start();
140 }
141}
142
143void SmtpJob::startSmtpJob()
144{
145 if ( s_slavePool.isDestroyed() ) {
146 return;
147 }
148
149 KUrl destination;
150 destination.setProtocol( ( transport()->encryption() == Transport::EnumEncryption::SSL ) ?
151 SMTPS_PROTOCOL : SMTP_PROTOCOL );
152 destination.setHost( transport()->host().trimmed() );
153 destination.setPort( transport()->port() );
154
155 destination.addQueryItem( QLatin1String( "headers" ), QLatin1String( "0" ) );
156 destination.addQueryItem( QLatin1String( "from" ), sender() );
157
158 foreach ( const QString &str, to() ) {
159 destination.addQueryItem( QLatin1String( "to" ), str );
160 }
161 foreach ( const QString &str, cc() ) {
162 destination.addQueryItem( QLatin1String( "cc" ), str );
163 }
164 foreach ( const QString &str, bcc() ) {
165 destination.addQueryItem( QLatin1String( "bcc" ), str );
166 }
167
168 if ( transport()->specifyHostname() ) {
169 destination.addQueryItem( QLatin1String( "hostname" ), transport()->localHostname() );
170 }
171
172 if ( transport()->requiresAuthentication() ) {
173 QString user = transport()->userName();
174 QString passwd = transport()->password();
175 if ( ( user.isEmpty() || passwd.isEmpty() ) &&
176 transport()->authenticationType() != Transport::EnumAuthenticationType::GSSAPI ) {
177
178 QPointer<KPasswordDialog> dlg =
179 new KPasswordDialog(
180 0,
181 KPasswordDialog::ShowUsernameLine |
182 KPasswordDialog::ShowKeepPassword );
183 dlg->setPrompt( i18n( "You need to supply a username and a password "
184 "to use this SMTP server." ) );
185 dlg->setKeepPassword( transport()->storePassword() );
186 dlg->addCommentLine( QString(), transport()->name() );
187 dlg->setUsername( user );
188 dlg->setPassword( passwd );
189
190 bool gotIt = false;
191 if ( dlg->exec() ) {
192 transport()->setUserName( dlg->username() );
193 transport()->setPassword( dlg->password() );
194 transport()->setStorePassword( dlg->keepPassword() );
195 transport()->writeConfig();
196 gotIt = true;
197 }
198 delete dlg;
199
200 if ( !gotIt ) {
201 setError( KilledJobError );
202 emitResult();
203 return;
204 }
205 }
206 destination.setUser( transport()->userName() );
207 destination.setPass( transport()->password() );
208 }
209
210 // dotstuffing is now done by the slave (see setting of metadata)
211 if ( !data().isEmpty() ) {
212 // allow +5% for subsequent LF->CRLF and dotstuffing (an average
213 // over 2G-lines gives an average line length of 42-43):
214 destination.addQueryItem( QLatin1String( "size" ),
215 QString::number( qRound( data().length() * 1.05 ) ) );
216 }
217
218 destination.setPath( QLatin1String( "/send" ) );
219
220#ifndef MAILTRANSPORT_INPROCESS_SMTP
221 d->slave = s_slavePool->slaves.value( transport()->id() );
222 if ( !d->slave ) {
223 KIO::MetaData slaveConfig;
224 slaveConfig.insert( QLatin1String( "tls" ),
225 ( transport()->encryption() == Transport::EnumEncryption::TLS ) ?
226 QLatin1String( "on" ) : QLatin1String( "off" ) );
227 if ( transport()->requiresAuthentication() ) {
228 slaveConfig.insert( QLatin1String( "sasl" ), transport()->authenticationTypeString() );
229 }
230 d->slave = KIO::Scheduler::getConnectedSlave( destination, slaveConfig );
231 kDebug() << "Created new SMTP slave" << d->slave;
232 s_slavePool->slaves.insert( transport()->id(), d->slave );
233 } else {
234 kDebug() << "Re-using existing slave" << d->slave;
235 }
236
237 KIO::TransferJob *job = KIO::put( destination, -1, KIO::HideProgressInfo );
238 if ( !d->slave || !job ) {
239 setError( UserDefinedError );
240 setErrorText( i18n( "Unable to create SMTP job." ) );
241 emitResult();
242 return;
243 }
244
245 job->addMetaData( QLatin1String( "lf2crlf+dotstuff" ), QLatin1String( "slave" ) );
246 connect( job, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
247 SLOT(dataRequest(KIO::Job*,QByteArray&)) );
248
249 addSubjob( job );
250 KIO::Scheduler::assignJobToSlave( d->slave, job );
251#else
252 SmtpSession *session = new SmtpSession( this );
253 connect( session, SIGNAL(result(MailTransport::SmtpSession*)),
254 SLOT(smtpSessionResult(MailTransport::SmtpSession*)) );
255 session->setUseTLS( transport()->encryption() == Transport::EnumEncryption::TLS );
256 if ( transport()->requiresAuthentication() ) {
257 session->setSaslMethod( transport()->authenticationTypeString() );
258 }
259 session->sendMessage( destination, buffer() );
260#endif
261
262 setTotalAmount( KJob::Bytes, data().length() );
263}
264
265bool SmtpJob::doKill()
266{
267 if ( s_slavePool.isDestroyed() ) {
268 return false;
269 }
270
271 if ( !hasSubjobs() ) {
272 return true;
273 }
274 if ( d->currentState == SmtpJobPrivate::Precommand ) {
275 return subjobs().first()->kill();
276 } else if ( d->currentState == SmtpJobPrivate::Smtp ) {
277 KIO::SimpleJob *job = static_cast<KIO::SimpleJob*>( subjobs().first() );
278 clearSubjobs();
279 KIO::Scheduler::cancelJob( job );
280 s_slavePool->removeSlave( d->slave );
281 return true;
282 }
283 return false;
284}
285
286void SmtpJob::slotResult( KJob *job )
287{
288 if ( s_slavePool.isDestroyed() ) {
289 return;
290 }
291
292 // The job has finished, so we don't care about any further errors. Set
293 // d->finished to true, so slaveError() knows about this and doesn't call
294 // emitResult() anymore.
295 // Sometimes, the SMTP slave emits more than one error
296 //
297 // The first error causes slotResult() to be called, but not slaveError(), since
298 // the scheduler doesn't emit errors for connected slaves.
299 //
300 // The second error then causes slaveError() to be called (as the slave is no
301 // longer connected), which does emitResult() a second time, which is invalid
302 // (and triggers an assert in KMail).
303 d->finished = true;
304
305 // Normally, calling TransportJob::slotResult() whould set the proper error code
306 // for error() via KComposite::slotResult(). However, we can't call that here,
307 // since that also emits the result signal.
308 // In KMail, when there are multiple mails in the outbox, KMail tries to send
309 // the next mail when it gets the result signal, which then would reuse the
310 // old broken slave from the slave pool if there was an error.
311 // To prevent that, we call TransportJob::slotResult() only after removing the
312 // slave from the pool and calculate the error code ourselves.
313 int errorCode = error();
314 if ( !errorCode ) {
315 errorCode = job->error();
316 }
317
318 if ( errorCode && d->currentState == SmtpJobPrivate::Smtp ) {
319 s_slavePool->removeSlave( d->slave, errorCode != KIO::ERR_SLAVE_DIED );
320 TransportJob::slotResult( job );
321 return;
322 }
323
324 TransportJob::slotResult( job );
325 if ( !error() && d->currentState == SmtpJobPrivate::Precommand ) {
326 d->currentState = SmtpJobPrivate::Smtp;
327 startSmtpJob();
328 return;
329 }
330 if ( !error() ) {
331 emitResult();
332 }
333}
334
335void SmtpJob::dataRequest( KIO::Job *job, QByteArray &data )
336{
337 if ( s_slavePool.isDestroyed() ) {
338 return;
339 }
340
341 Q_UNUSED( job );
342 Q_ASSERT( job );
343 if ( buffer()->atEnd() ) {
344 data.clear();
345 } else {
346 Q_ASSERT( buffer()->isOpen() );
347 data = buffer()->read( 32 * 1024 );
348 }
349 setProcessedAmount( KJob::Bytes, buffer()->pos() );
350}
351
352void SmtpJob::slaveError( KIO::Slave *slave, int errorCode, const QString &errorMsg )
353{
354 if ( s_slavePool.isDestroyed() ) {
355 return;
356 }
357
358 s_slavePool->removeSlave( slave, errorCode != KIO::ERR_SLAVE_DIED );
359 if ( d->slave == slave && !d->finished ) {
360 setError( errorCode );
361 setErrorText( KIO::buildErrorString( errorCode, errorMsg ) );
362 emitResult();
363 }
364}
365
366#include "moc_smtpjob.cpp"
MailTransport::PrecommandJob
Job to execute a command.
Definition: precommandjob.h:44
MailTransport::PrecommandJob::start
virtual void start()
Executes the precommand.
Definition: precommandjob.cpp:73
MailTransport::SmtpJob
Mail transport job for SMTP.
Definition: smtpjob.h:52
MailTransport::SmtpJob::~SmtpJob
virtual ~SmtpJob()
Deletes this job.
Definition: smtpjob.cpp:107
MailTransport::SmtpJob::SmtpJob
SmtpJob(Transport *transport, QObject *parent=0)
Creates a SmtpJob.
Definition: smtpjob.cpp:94
MailTransport::SmtpJob::doStart
virtual void doStart()
Do the actual work, implement in your subclass.
Definition: smtpjob.cpp:124
MailTransport::SmtpSession
Connection to an SMTP server.
Definition: smtpsession.h:34
MailTransport::SmtpSession::setUseTLS
void setUseTLS(bool useTLS)
Enable TLS encryption.
Definition: smtpsession.cpp:574
MailTransport::SmtpSession::setSaslMethod
void setSaslMethod(const QString &method)
Sets the SASL method used for authentication.
Definition: smtpsession.cpp:569
MailTransport::SmtpSession::sendMessage
void sendMessage(const KUrl &destination, QIODevice *data)
Send a message.
Definition: smtpsession.cpp:602
MailTransport::SmtpSession::errorMessage
QString errorMessage() const
Returns the error nmeesage, if any.
Definition: smtpsession.cpp:625
MailTransport::TransportJob
Abstract base class for all mail transport jobs.
Definition: transportjob.h:42
MailTransport::TransportJob::transport
Transport * transport() const
Returns the Transport object containing the mail transport settings.
Definition: transportjob.cpp:79
MailTransport::TransportJob::buffer
QBuffer * buffer()
Returns a QBuffer opened on the message data.
Definition: transportjob.cpp:109
MailTransport::TransportJob::data
QByteArray data() const
Returns the data of the mail.
Definition: transportjob.cpp:104
MailTransport::TransportJob::sender
QString sender() const
Returns the sender of the mail.
Definition: transportjob.cpp:84
MailTransport::TransportJob::bcc
QStringList bcc() const
Returns the "Bcc" receiver(s) of the mail.
Definition: transportjob.cpp:99
MailTransport::TransportJob::to
QStringList to() const
Returns the "To" receiver(s) of the mail.
Definition: transportjob.cpp:89
MailTransport::TransportJob::cc
QStringList cc() const
Returns the "Cc" receiver(s) of the mail.
Definition: transportjob.cpp:94
MailTransport::Transport
Represents the settings of a specific mail transport.
Definition: transport.h:51
MailTransport::Transport::setPassword
void setPassword(const QString &passwd)
Sets the password of this transport.
Definition: transport.cpp:73
MailTransport::Transport::password
QString password()
Returns the password of this transport.
Definition: transport.cpp:64
mailtransport_defs.h
Internal file containing constant definitions etc.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu Jul 21 2022 00:00:00 by doxygen 1.9.5 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

mailtransport

Skip menu "mailtransport"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Related Pages

kdepimlibs-4.14.10 API Reference

Skip menu "kdepimlibs-4.14.10 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal