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

mailtransport

  • sources
  • kde-4.14
  • kdepimlibs
  • 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 
40 using namespace MailTransport;
41 
42 class 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 
62 K_GLOBAL_STATIC( SlavePool, s_slavePool )
63 
64 
68 class 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 
94 SmtpJob::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 
107 SmtpJob::~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 
124 void 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 
143 void 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 
265 bool 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 
286 void 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 
335 void 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 
352 void 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::TransportJob::bcc
QStringList bcc() const
Returns the "Bcc" receiver(s) of the mail.
Definition: transportjob.cpp:99
QByteArray::clear
void clear()
QHash::key
const Key key(const T &value) const
QByteArray
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::SmtpSession
Connection to an SMTP server.
Definition: smtpsession.h:33
QPointer
MailTransport::SmtpJob
Mail transport job for SMTP.
Definition: smtpjob.h:51
MailTransport::PrecommandJob::start
virtual void start()
Executes the precommand.
Definition: precommandjob.cpp:73
MailTransport::SmtpSession::setSaslMethod
void setSaslMethod(const QString &method)
Sets the SASL method used for authentication.
Definition: smtpsession.cpp:569
MailTransport::PrecommandJob
Job to execute a command.
Definition: precommandjob.h:43
QString::number
QString number(int n, int base)
QHash< int, KIO::Slave * >
MailTransport::SmtpSession::setUseTLS
void setUseTLS(bool useTLS)
Enable TLS encryption.
Definition: smtpsession.cpp:574
MailTransport::TransportJob::cc
QStringList cc() const
Returns the "Cc" receiver(s) of the mail.
Definition: transportjob.cpp:94
QObject
MailTransport::SmtpJob::SmtpJob
SmtpJob(Transport *transport, QObject *parent=0)
Creates a SmtpJob.
Definition: smtpjob.cpp:94
MailTransport::TransportJob::sender
QString sender() const
Returns the sender of the mail.
Definition: transportjob.cpp:84
QString::isEmpty
bool isEmpty() const
QIODevice::read
qint64 read(char *data, qint64 maxSize)
QString
MailTransport::TransportJob
Abstract base class for all mail transport jobs.
Definition: transportjob.h:41
MailTransport::TransportJob::data
QByteArray data() const
Returns the data of the mail.
Definition: transportjob.cpp:104
MailTransport::SmtpJob::doStart
virtual void doStart()
Do the actual work, implement in your subclass.
Definition: smtpjob.cpp:124
QLatin1String
MailTransport::SmtpSession::errorMessage
QString errorMessage() const
Returns the error nmeesage, if any.
Definition: smtpsession.cpp:625
mailtransport_defs.h
Internal file containing constant definitions etc.
MailTransport::Transport
Represents the settings of a specific mail transport.
Definition: transport.h:50
MailTransport::TransportJob::to
QStringList to() const
Returns the "To" receiver(s) of the mail.
Definition: transportjob.cpp:89
MailTransport::SmtpSession::sendMessage
void sendMessage(const KUrl &destination, QIODevice *data)
Send a message.
Definition: smtpsession.cpp:602
MailTransport::SmtpJob::~SmtpJob
virtual ~SmtpJob()
Deletes this job.
Definition: smtpjob.cpp:107
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
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:37:48 by doxygen 1.8.7 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 API Reference

Skip menu "kdepimlibs 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

Search



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