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

mailtransport

smtpjob.cpp
00001 /*
00002   Copyright (c) 2007 Volker Krause <vkrause@kde.org>
00003 
00004   Based on KMail code by:
00005   Copyright (c) 1996-1998 Stefan Taferner <taferner@kde.org>
00006 
00007   This library is free software; you can redistribute it and/or modify it
00008   under the terms of the GNU Library General Public License as published by
00009   the Free Software Foundation; either version 2 of the License, or (at your
00010   option) any later version.
00011 
00012   This library is distributed in the hope that it will be useful, but WITHOUT
00013   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00015   License for more details.
00016 
00017   You should have received a copy of the GNU Library General Public License
00018   along with this library; see the file COPYING.LIB.  If not, write to the
00019   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00020   02110-1301, USA.
00021 */
00022 
00023 #include "smtpjob.h"
00024 #include "transport.h"
00025 #include "mailtransport_defs.h"
00026 #include "precommandjob.h"
00027 #include "smtp/smtpsession.h"
00028 
00029 #include <QBuffer>
00030 #include <QHash>
00031 #include <QPointer>
00032 
00033 #include <KLocalizedString>
00034 #include <KUrl>
00035 #include <KIO/Job>
00036 #include <KIO/Scheduler>
00037 #include <KPasswordDialog>
00038 
00039 using namespace MailTransport;
00040 
00041 class SlavePool
00042 {
00043   public:
00044     SlavePool() : ref( 0 ) {}
00045     int ref;
00046     QHash<int,KIO::Slave*> slaves;
00047 
00048     void removeSlave( KIO::Slave *slave, bool disconnect = false )
00049     {
00050       kDebug() << "Removing slave" << slave << "from pool";
00051       const int slaveKey = slaves.key( slave );
00052       if ( slaveKey > 0 ) {
00053         slaves.remove( slaveKey );
00054         if ( disconnect ) {
00055           KIO::Scheduler::disconnectSlave( slave );
00056         }
00057       }
00058     }
00059 };
00060 
00061 K_GLOBAL_STATIC( SlavePool, s_slavePool )
00062 
00063 
00067 class SmtpJobPrivate
00068 {
00069   public:
00070     SmtpJobPrivate( SmtpJob *parent ) : q( parent ) {}
00071 
00072     void smtpSessionResult( SmtpSession *session )
00073     {
00074 #ifndef MAILTRANSPORT_INPROCESS_SMTP
00075       Q_UNUSED( session );
00076 #else
00077       if ( !session->errorMessage().isEmpty() ) {
00078         q->setError( KJob::UserDefinedError );
00079         q->setErrorText( session->errorMessage() );
00080       }
00081       q->emitResult();
00082 #endif
00083     }
00084 
00085     SmtpJob *q;
00086     KIO::Slave *slave;
00087     enum State {
00088       Idle, Precommand, Smtp
00089     } currentState;
00090     bool finished;
00091 };
00092 
00093 SmtpJob::SmtpJob( Transport *transport, QObject *parent )
00094   : TransportJob( transport, parent ), d( new SmtpJobPrivate( this ) )
00095 {
00096   d->currentState = SmtpJobPrivate::Idle;
00097   d->slave = 0;
00098   d->finished = false;
00099   if ( !s_slavePool.isDestroyed() ) {
00100     s_slavePool->ref++;
00101   }
00102   KIO::Scheduler::connect( SIGNAL(slaveError(KIO::Slave*,int,QString)),
00103                            this, SLOT(slaveError(KIO::Slave*,int,QString)) );
00104 }
00105 
00106 SmtpJob::~SmtpJob()
00107 {
00108   if ( !s_slavePool.isDestroyed() ) {
00109     s_slavePool->ref--;
00110     if ( s_slavePool->ref == 0 ) {
00111       kDebug() << "clearing SMTP slave pool" << s_slavePool->slaves.count();
00112       foreach ( KIO::Slave *slave, s_slavePool->slaves ) {
00113         if ( slave ) {
00114           KIO::Scheduler::disconnectSlave( slave );
00115         }
00116       }
00117       s_slavePool->slaves.clear();
00118     }
00119   }
00120   delete d;
00121 }
00122 
00123 void SmtpJob::doStart()
00124 {
00125   if ( s_slavePool.isDestroyed() ) {
00126     return;
00127   }
00128 
00129   if ( ( !s_slavePool->slaves.isEmpty() &&
00130          s_slavePool->slaves.contains( transport()->id() ) ) ||
00131        transport()->precommand().isEmpty() ) {
00132     d->currentState = SmtpJobPrivate::Smtp;
00133     startSmtpJob();
00134   } else {
00135     d->currentState = SmtpJobPrivate::Precommand;
00136     PrecommandJob *job = new PrecommandJob( transport()->precommand(), this );
00137     addSubjob( job );
00138     job->start();
00139   }
00140 }
00141 
00142 void SmtpJob::startSmtpJob()
00143 {
00144   if ( s_slavePool.isDestroyed() ) {
00145     return;
00146   }
00147 
00148   KUrl destination;
00149   destination.setProtocol( ( transport()->encryption() == Transport::EnumEncryption::SSL ) ?
00150                            SMTPS_PROTOCOL : SMTP_PROTOCOL );
00151   destination.setHost( transport()->host().trimmed() );
00152   destination.setPort( transport()->port() );
00153 
00154   destination.addQueryItem( QLatin1String( "headers" ), QLatin1String( "0" ) );
00155   destination.addQueryItem( QLatin1String( "from" ), sender() );
00156 
00157   foreach ( const QString &str, to() ) {
00158     destination.addQueryItem( QLatin1String( "to" ), str );
00159   }
00160   foreach ( const QString &str, cc() ) {
00161     destination.addQueryItem( QLatin1String( "cc" ), str );
00162   }
00163   foreach ( const QString &str, bcc() ) {
00164     destination.addQueryItem( QLatin1String( "bcc" ), str );
00165   }
00166 
00167   if ( transport()->specifyHostname() ) {
00168     destination.addQueryItem( QLatin1String( "hostname" ), transport()->localHostname() );
00169   }
00170 
00171   if ( transport()->requiresAuthentication() ) {
00172     QString user = transport()->userName();
00173     QString passwd = transport()->password();
00174     if ( ( user.isEmpty() || passwd.isEmpty() ) &&
00175          transport()->authenticationType() != Transport::EnumAuthenticationType::GSSAPI ) {
00176 
00177       QPointer<KPasswordDialog> dlg =
00178         new KPasswordDialog( 0,
00179                              KPasswordDialog::ShowUsernameLine |
00180                              KPasswordDialog::ShowKeepPassword );
00181       dlg->setPrompt( i18n( "You need to supply a username and a password "
00182                             "to use this SMTP server." ) );
00183       dlg->setKeepPassword( transport()->storePassword() );
00184       dlg->addCommentLine( QString(), transport()->name() );
00185       dlg->setUsername( user );
00186       dlg->setPassword( passwd );
00187 
00188       if ( dlg->exec() != QDialog::Accepted ) {
00189         setError( KilledJobError );
00190         emitResult();
00191         delete dlg;
00192         return;
00193       }
00194       transport()->setUserName( dlg->username() );
00195       transport()->setPassword( dlg->password() );
00196       transport()->setStorePassword( dlg->keepPassword() );
00197       transport()->writeConfig();
00198       delete dlg;
00199     }
00200     destination.setUser( transport()->userName() );
00201     destination.setPass( transport()->password() );
00202   }
00203 
00204   // dotstuffing is now done by the slave (see setting of metadata)
00205   if ( !data().isEmpty() ) {
00206     // allow +5% for subsequent LF->CRLF and dotstuffing (an average
00207     // over 2G-lines gives an average line length of 42-43):
00208     destination.addQueryItem( QLatin1String( "size" ),
00209                               QString::number( qRound( data().length() * 1.05 ) ) );
00210   }
00211 
00212   destination.setPath( QLatin1String( "/send" ) );
00213 
00214 #ifndef MAILTRANSPORT_INPROCESS_SMTP
00215   d->slave = s_slavePool->slaves.value( transport()->id() );
00216   if ( !d->slave ) {
00217     KIO::MetaData slaveConfig;
00218     slaveConfig.insert( QLatin1String( "tls" ),
00219                         ( transport()->encryption() == Transport::EnumEncryption::TLS ) ?
00220                         QLatin1String( "on" ) : QLatin1String( "off" ) );
00221     if ( transport()->requiresAuthentication() ) {
00222       slaveConfig.insert( QLatin1String( "sasl" ), transport()->authenticationTypeString() );
00223     }
00224     d->slave = KIO::Scheduler::getConnectedSlave( destination, slaveConfig );
00225     kDebug() << "Created new SMTP slave" << d->slave;
00226     s_slavePool->slaves.insert( transport()->id(), d->slave );
00227   } else {
00228     kDebug() << "Re-using existing slave" << d->slave;
00229   }
00230 
00231   KIO::TransferJob *job = KIO::put( destination, -1, KIO::HideProgressInfo );
00232   if ( !d->slave || !job ) {
00233     setError( UserDefinedError );
00234     setErrorText( i18n( "Unable to create SMTP job." ) );
00235     emitResult();
00236     return;
00237   }
00238 
00239   job->addMetaData( QLatin1String( "lf2crlf+dotstuff" ), QLatin1String( "slave" ) );
00240   connect( job, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
00241            SLOT(dataRequest(KIO::Job*,QByteArray&)) );
00242 
00243   addSubjob( job );
00244   KIO::Scheduler::assignJobToSlave( d->slave, job );
00245 #else
00246   SmtpSession *session = new SmtpSession( this );
00247   connect( session, SIGNAL(result(MailTransport::SmtpSession*)),
00248            SLOT(smtpSessionResult(MailTransport::SmtpSession*)) );
00249   session->setUseTLS( transport()->encryption() == Transport::EnumEncryption::TLS );
00250   if ( transport()->requiresAuthentication() ) {
00251     session->setSaslMethod( transport()->authenticationTypeString() );
00252   }
00253   session->sendMessage( destination, buffer() );
00254 #endif
00255 
00256   setTotalAmount( KJob::Bytes, data().length() );
00257 }
00258 
00259 bool SmtpJob::doKill()
00260 {
00261   if ( s_slavePool.isDestroyed() ) {
00262     return false;
00263   }
00264 
00265   if ( !hasSubjobs() ) {
00266     return true;
00267   }
00268   if ( d->currentState == SmtpJobPrivate::Precommand ) {
00269     return subjobs().first()->kill();
00270   } else if ( d->currentState == SmtpJobPrivate::Smtp ) {
00271     KIO::SimpleJob *job = static_cast<KIO::SimpleJob*>( subjobs().first() );
00272     clearSubjobs();
00273     KIO::Scheduler::cancelJob( job );
00274     s_slavePool->removeSlave( d->slave );
00275     return true;
00276   }
00277   return false;
00278 }
00279 
00280 void SmtpJob::slotResult( KJob *job )
00281 {
00282   if ( s_slavePool.isDestroyed() ) {
00283     return;
00284   }
00285 
00286   // The job has finished, so we don't care about any further errors. Set
00287   // d->finished to true, so slaveError() knows about this and doesn't call
00288   // emitResult() anymore.
00289   // Sometimes, the SMTP slave emits more than one error
00290   //
00291   // The first error causes slotResult() to be called, but not slaveError(), since
00292   // the scheduler doesn't emit errors for connected slaves.
00293   //
00294   // The second error then causes slaveError() to be called (as the slave is no
00295   // longer connected), which does emitResult() a second time, which is invalid
00296   // (and triggers an assert in KMail).
00297   d->finished = true;
00298 
00299   // Normally, calling TransportJob::slotResult() whould set the proper error code
00300   // for error() via KComposite::slotResult(). However, we can't call that here,
00301   // since that also emits the result signal.
00302   // In KMail, when there are multiple mails in the outbox, KMail tries to send
00303   // the next mail when it gets the result signal, which then would reuse the
00304   // old broken slave from the slave pool if there was an error.
00305   // To prevent that, we call TransportJob::slotResult() only after removing the
00306   // slave from the pool and calculate the error code ourselves.
00307   int errorCode = error();
00308   if ( !errorCode ) {
00309     errorCode = job->error();
00310   }
00311 
00312   if ( errorCode && d->currentState == SmtpJobPrivate::Smtp ) {
00313     s_slavePool->removeSlave( d->slave, errorCode != KIO::ERR_SLAVE_DIED );
00314     TransportJob::slotResult( job );
00315     return;
00316   }
00317 
00318   TransportJob::slotResult( job );
00319   if ( !error() && d->currentState == SmtpJobPrivate::Precommand ) {
00320     d->currentState = SmtpJobPrivate::Smtp;
00321     startSmtpJob();
00322     return;
00323   }
00324   if ( !error() ) {
00325     emitResult();
00326   }
00327 }
00328 
00329 void SmtpJob::dataRequest( KIO::Job *job, QByteArray &data )
00330 {
00331   if ( s_slavePool.isDestroyed() ) {
00332     return;
00333   }
00334 
00335   Q_UNUSED( job );
00336   Q_ASSERT( job );
00337   if ( buffer()->atEnd() ) {
00338     data.clear();
00339   } else {
00340     Q_ASSERT( buffer()->isOpen() );
00341     data = buffer()->read( 32 * 1024 );
00342   }
00343   setProcessedAmount( KJob::Bytes, buffer()->pos() );
00344 }
00345 
00346 void SmtpJob::slaveError( KIO::Slave *slave, int errorCode, const QString &errorMsg )
00347 {
00348   if ( s_slavePool.isDestroyed() ) {
00349     return;
00350   }
00351 
00352   s_slavePool->removeSlave( slave, errorCode != KIO::ERR_SLAVE_DIED );
00353   if ( d->slave == slave && !d->finished ) {
00354     setError( errorCode );
00355     setErrorText( KIO::buildErrorString( errorCode, errorMsg ) );
00356     emitResult();
00357   }
00358 }
00359 
00360 #include "smtpjob.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu Aug 2 2012 15:25:02 by doxygen 1.7.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.8.5 API Reference

Skip menu "kdepimlibs-4.8.5 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • 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