• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.10.5 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 <KIO/Job>
36 #include <KIO/Scheduler>
37 #include <KPasswordDialog>
38 
39 using namespace MailTransport;
40 
41 class SlavePool
42 {
43  public:
44  SlavePool() : ref( 0 ) {}
45  int ref;
46  QHash<int, KIO::Slave*> slaves;
47 
48  void removeSlave( KIO::Slave *slave, bool disconnect = false )
49  {
50  kDebug() << "Removing slave" << slave << "from pool";
51  const int slaveKey = slaves.key( slave );
52  if ( slaveKey > 0 ) {
53  slaves.remove( slaveKey );
54  if ( disconnect ) {
55  KIO::Scheduler::disconnectSlave( slave );
56  }
57  }
58  }
59 };
60 
61 K_GLOBAL_STATIC( SlavePool, s_slavePool )
62 
63 
67 class SmtpJobPrivate
68 {
69  public:
70  SmtpJobPrivate( SmtpJob *parent ) : q( parent ) {}
71 
72  void smtpSessionResult( SmtpSession *session )
73  {
74 #ifndef MAILTRANSPORT_INPROCESS_SMTP
75  Q_UNUSED( session );
76 #else
77  if ( !session->errorMessage().isEmpty() ) {
78  q->setError( KJob::UserDefinedError );
79  q->setErrorText( session->errorMessage() );
80  }
81  q->emitResult();
82 #endif
83  }
84 
85  SmtpJob *q;
86  KIO::Slave *slave;
87  enum State {
88  Idle, Precommand, Smtp
89  } currentState;
90  bool finished;
91 };
92 
93 SmtpJob::SmtpJob( Transport *transport, QObject *parent )
94  : TransportJob( transport, parent ), d( new SmtpJobPrivate( this ) )
95 {
96  d->currentState = SmtpJobPrivate::Idle;
97  d->slave = 0;
98  d->finished = false;
99  if ( !s_slavePool.isDestroyed() ) {
100  s_slavePool->ref++;
101  }
102  KIO::Scheduler::connect( SIGNAL(slaveError(KIO::Slave*,int,QString)),
103  this, SLOT(slaveError(KIO::Slave*,int,QString)) );
104 }
105 
106 SmtpJob::~SmtpJob()
107 {
108  if ( !s_slavePool.isDestroyed() ) {
109  s_slavePool->ref--;
110  if ( s_slavePool->ref == 0 ) {
111  kDebug() << "clearing SMTP slave pool" << s_slavePool->slaves.count();
112  foreach ( KIO::Slave *slave, s_slavePool->slaves ) {
113  if ( slave ) {
114  KIO::Scheduler::disconnectSlave( slave );
115  }
116  }
117  s_slavePool->slaves.clear();
118  }
119  }
120  delete d;
121 }
122 
123 void SmtpJob::doStart()
124 {
125  if ( s_slavePool.isDestroyed() ) {
126  return;
127  }
128 
129  if ( ( !s_slavePool->slaves.isEmpty() &&
130  s_slavePool->slaves.contains( transport()->id() ) ) ||
131  transport()->precommand().isEmpty() ) {
132  d->currentState = SmtpJobPrivate::Smtp;
133  startSmtpJob();
134  } else {
135  d->currentState = SmtpJobPrivate::Precommand;
136  PrecommandJob *job = new PrecommandJob( transport()->precommand(), this );
137  addSubjob( job );
138  job->start();
139  }
140 }
141 
142 void SmtpJob::startSmtpJob()
143 {
144  if ( s_slavePool.isDestroyed() ) {
145  return;
146  }
147 
148  KUrl destination;
149  destination.setProtocol( ( transport()->encryption() == Transport::EnumEncryption::SSL ) ?
150  SMTPS_PROTOCOL : SMTP_PROTOCOL );
151  destination.setHost( transport()->host().trimmed() );
152  destination.setPort( transport()->port() );
153 
154  destination.addQueryItem( QLatin1String( "headers" ), QLatin1String( "0" ) );
155  destination.addQueryItem( QLatin1String( "from" ), sender() );
156 
157  foreach ( const QString &str, to() ) {
158  destination.addQueryItem( QLatin1String( "to" ), str );
159  }
160  foreach ( const QString &str, cc() ) {
161  destination.addQueryItem( QLatin1String( "cc" ), str );
162  }
163  foreach ( const QString &str, bcc() ) {
164  destination.addQueryItem( QLatin1String( "bcc" ), str );
165  }
166 
167  if ( transport()->specifyHostname() ) {
168  destination.addQueryItem( QLatin1String( "hostname" ), transport()->localHostname() );
169  }
170 
171  if ( transport()->requiresAuthentication() ) {
172  QString user = transport()->userName();
173  QString passwd = transport()->password();
174  if ( ( user.isEmpty() || passwd.isEmpty() ) &&
175  transport()->authenticationType() != Transport::EnumAuthenticationType::GSSAPI ) {
176 
177  QPointer<KPasswordDialog> dlg =
178  new KPasswordDialog(
179  0,
180  KPasswordDialog::ShowUsernameLine |
181  KPasswordDialog::ShowKeepPassword );
182  dlg->setPrompt( i18n( "You need to supply a username and a password "
183  "to use this SMTP server." ) );
184  dlg->setKeepPassword( transport()->storePassword() );
185  dlg->addCommentLine( QString(), transport()->name() );
186  dlg->setUsername( user );
187  dlg->setPassword( passwd );
188 
189  bool gotIt = false;
190  if ( dlg->exec() ) {
191  transport()->setUserName( dlg->username() );
192  transport()->setPassword( dlg->password() );
193  transport()->setStorePassword( dlg->keepPassword() );
194  transport()->writeConfig();
195  gotIt = true;
196  }
197  delete dlg;
198 
199  if ( !gotIt ) {
200  setError( KilledJobError );
201  emitResult();
202  return;
203  }
204  }
205  destination.setUser( transport()->userName() );
206  destination.setPass( transport()->password() );
207  }
208 
209  // dotstuffing is now done by the slave (see setting of metadata)
210  if ( !data().isEmpty() ) {
211  // allow +5% for subsequent LF->CRLF and dotstuffing (an average
212  // over 2G-lines gives an average line length of 42-43):
213  destination.addQueryItem( QLatin1String( "size" ),
214  QString::number( qRound( data().length() * 1.05 ) ) );
215  }
216 
217  destination.setPath( QLatin1String( "/send" ) );
218 
219 #ifndef MAILTRANSPORT_INPROCESS_SMTP
220  d->slave = s_slavePool->slaves.value( transport()->id() );
221  if ( !d->slave ) {
222  KIO::MetaData slaveConfig;
223  slaveConfig.insert( QLatin1String( "tls" ),
224  ( transport()->encryption() == Transport::EnumEncryption::TLS ) ?
225  QLatin1String( "on" ) : QLatin1String( "off" ) );
226  if ( transport()->requiresAuthentication() ) {
227  slaveConfig.insert( QLatin1String( "sasl" ), transport()->authenticationTypeString() );
228  }
229  d->slave = KIO::Scheduler::getConnectedSlave( destination, slaveConfig );
230  kDebug() << "Created new SMTP slave" << d->slave;
231  s_slavePool->slaves.insert( transport()->id(), d->slave );
232  } else {
233  kDebug() << "Re-using existing slave" << d->slave;
234  }
235 
236  KIO::TransferJob *job = KIO::put( destination, -1, KIO::HideProgressInfo );
237  if ( !d->slave || !job ) {
238  setError( UserDefinedError );
239  setErrorText( i18n( "Unable to create SMTP job." ) );
240  emitResult();
241  return;
242  }
243 
244  job->addMetaData( QLatin1String( "lf2crlf+dotstuff" ), QLatin1String( "slave" ) );
245  connect( job, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
246  SLOT(dataRequest(KIO::Job*,QByteArray&)) );
247 
248  addSubjob( job );
249  KIO::Scheduler::assignJobToSlave( d->slave, job );
250 #else
251  SmtpSession *session = new SmtpSession( this );
252  connect( session, SIGNAL(result(MailTransport::SmtpSession*)),
253  SLOT(smtpSessionResult(MailTransport::SmtpSession*)) );
254  session->setUseTLS( transport()->encryption() == Transport::EnumEncryption::TLS );
255  if ( transport()->requiresAuthentication() ) {
256  session->setSaslMethod( transport()->authenticationTypeString() );
257  }
258  session->sendMessage( destination, buffer() );
259 #endif
260 
261  setTotalAmount( KJob::Bytes, data().length() );
262 }
263 
264 bool SmtpJob::doKill()
265 {
266  if ( s_slavePool.isDestroyed() ) {
267  return false;
268  }
269 
270  if ( !hasSubjobs() ) {
271  return true;
272  }
273  if ( d->currentState == SmtpJobPrivate::Precommand ) {
274  return subjobs().first()->kill();
275  } else if ( d->currentState == SmtpJobPrivate::Smtp ) {
276  KIO::SimpleJob *job = static_cast<KIO::SimpleJob*>( subjobs().first() );
277  clearSubjobs();
278  KIO::Scheduler::cancelJob( job );
279  s_slavePool->removeSlave( d->slave );
280  return true;
281  }
282  return false;
283 }
284 
285 void SmtpJob::slotResult( KJob *job )
286 {
287  if ( s_slavePool.isDestroyed() ) {
288  return;
289  }
290 
291  // The job has finished, so we don't care about any further errors. Set
292  // d->finished to true, so slaveError() knows about this and doesn't call
293  // emitResult() anymore.
294  // Sometimes, the SMTP slave emits more than one error
295  //
296  // The first error causes slotResult() to be called, but not slaveError(), since
297  // the scheduler doesn't emit errors for connected slaves.
298  //
299  // The second error then causes slaveError() to be called (as the slave is no
300  // longer connected), which does emitResult() a second time, which is invalid
301  // (and triggers an assert in KMail).
302  d->finished = true;
303 
304  // Normally, calling TransportJob::slotResult() whould set the proper error code
305  // for error() via KComposite::slotResult(). However, we can't call that here,
306  // since that also emits the result signal.
307  // In KMail, when there are multiple mails in the outbox, KMail tries to send
308  // the next mail when it gets the result signal, which then would reuse the
309  // old broken slave from the slave pool if there was an error.
310  // To prevent that, we call TransportJob::slotResult() only after removing the
311  // slave from the pool and calculate the error code ourselves.
312  int errorCode = error();
313  if ( !errorCode ) {
314  errorCode = job->error();
315  }
316 
317  if ( errorCode && d->currentState == SmtpJobPrivate::Smtp ) {
318  s_slavePool->removeSlave( d->slave, errorCode != KIO::ERR_SLAVE_DIED );
319  TransportJob::slotResult( job );
320  return;
321  }
322 
323  TransportJob::slotResult( job );
324  if ( !error() && d->currentState == SmtpJobPrivate::Precommand ) {
325  d->currentState = SmtpJobPrivate::Smtp;
326  startSmtpJob();
327  return;
328  }
329  if ( !error() ) {
330  emitResult();
331  }
332 }
333 
334 void SmtpJob::dataRequest( KIO::Job *job, QByteArray &data )
335 {
336  if ( s_slavePool.isDestroyed() ) {
337  return;
338  }
339 
340  Q_UNUSED( job );
341  Q_ASSERT( job );
342  if ( buffer()->atEnd() ) {
343  data.clear();
344  } else {
345  Q_ASSERT( buffer()->isOpen() );
346  data = buffer()->read( 32 * 1024 );
347  }
348  setProcessedAmount( KJob::Bytes, buffer()->pos() );
349 }
350 
351 void SmtpJob::slaveError( KIO::Slave *slave, int errorCode, const QString &errorMsg )
352 {
353  if ( s_slavePool.isDestroyed() ) {
354  return;
355  }
356 
357  s_slavePool->removeSlave( slave, errorCode != KIO::ERR_SLAVE_DIED );
358  if ( d->slave == slave && !d->finished ) {
359  setError( errorCode );
360  setErrorText( KIO::buildErrorString( errorCode, errorMsg ) );
361  emitResult();
362  }
363 }
364 
365 #include "moc_smtpjob.cpp"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jul 13 2013 01:26:52 by doxygen 1.8.3.1 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.10.5 API Reference

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