• Skip to content
  • Skip to link menu
KDE 4.5 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • 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 
00028 #include <QBuffer>
00029 #include <QHash>
00030 
00031 #include <KLocalizedString>
00032 #include <KUrl>
00033 #include <KIO/Job>
00034 #include <KIO/Scheduler>
00035 #include <KIO/PasswordDialog>
00036 
00037 using namespace MailTransport;
00038 
00039 class SlavePool
00040 {
00041   public:
00042     SlavePool() : ref( 0 ) {}
00043     int ref;
00044     QHash<int,KIO::Slave*> slaves;
00045 
00046     void removeSlave( KIO::Slave *slave, bool disconnect = false )
00047     {
00048       kDebug() << "Removing slave" << slave << "from pool";
00049       const int slaveKey = slaves.key( slave );
00050       if ( slaveKey > 0 ) {
00051         slaves.remove( slaveKey );
00052         if ( disconnect ) {
00053           KIO::Scheduler::disconnectSlave( slave );
00054         }
00055       }
00056     }
00057 };
00058 
00059 K_GLOBAL_STATIC( SlavePool, s_slavePool )
00060 
00061 
00065 class SmtpJobPrivate
00066 {
00067   public:
00068     KIO::Slave *slave;
00069     enum State {
00070       Idle, Precommand, Smtp
00071     } currentState;
00072     bool finished;
00073 };
00074 
00075 SmtpJob::SmtpJob( Transport *transport, QObject *parent )
00076   : TransportJob( transport, parent ), d( new SmtpJobPrivate )
00077 {
00078   d->currentState = SmtpJobPrivate::Idle;
00079   d->slave = 0;
00080   d->finished = false;
00081   if ( !s_slavePool.isDestroyed() ) {
00082     s_slavePool->ref++;
00083   }
00084   KIO::Scheduler::connect( SIGNAL(slaveError(KIO::Slave*,int,QString)),
00085                            this, SLOT(slaveError(KIO::Slave*,int,QString)) );
00086 }
00087 
00088 SmtpJob::~SmtpJob()
00089 {
00090   if ( !s_slavePool.isDestroyed() ) {
00091     s_slavePool->ref--;
00092     if ( s_slavePool->ref == 0 ) {
00093       kDebug() << "clearing SMTP slave pool" << s_slavePool->slaves.count();
00094       foreach ( KIO::Slave *slave, s_slavePool->slaves ) {
00095         if ( slave ) {
00096           KIO::Scheduler::disconnectSlave( slave );
00097         }
00098       }
00099       s_slavePool->slaves.clear();
00100     }
00101   }
00102   delete d;
00103 }
00104 
00105 void SmtpJob::doStart()
00106 {
00107   if ( s_slavePool.isDestroyed() ) {
00108     return;
00109   }
00110 
00111   if ( s_slavePool->slaves.contains( transport()->id() ) ||
00112        transport()->precommand().isEmpty() ) {
00113     d->currentState = SmtpJobPrivate::Smtp;
00114     startSmtpJob();
00115   } else {
00116     d->currentState = SmtpJobPrivate::Precommand;
00117     PrecommandJob *job = new PrecommandJob( transport()->precommand(), this );
00118     addSubjob( job );
00119     job->start();
00120   }
00121 }
00122 
00123 void SmtpJob::startSmtpJob()
00124 {
00125   if ( s_slavePool.isDestroyed() ) {
00126     return;
00127   }
00128 
00129   KUrl destination;
00130   destination.setProtocol( ( transport()->encryption() == Transport::EnumEncryption::SSL ) ?
00131                            SMTPS_PROTOCOL : SMTP_PROTOCOL );
00132   destination.setHost( transport()->host().trimmed() );
00133   destination.setPort( transport()->port() );
00134 
00135   destination.addQueryItem( QLatin1String( "headers" ), QLatin1String( "0" ) );
00136   destination.addQueryItem( QLatin1String( "from" ), sender() );
00137 
00138   foreach ( const QString &str, to() ) {
00139     destination.addQueryItem( QLatin1String( "to" ), str );
00140   }
00141   foreach ( const QString &str, cc() ) {
00142     destination.addQueryItem( QLatin1String( "cc" ), str );
00143   }
00144   foreach ( const QString &str, bcc() ) {
00145     destination.addQueryItem( QLatin1String( "bcc" ), str );
00146   }
00147 
00148   if ( transport()->specifyHostname() ) {
00149     destination.addQueryItem( QLatin1String( "hostname" ), transport()->localHostname() );
00150   }
00151 
00152   if ( transport()->requiresAuthentication() ) {
00153     if( ( transport()->userName().isEmpty() || transport()->password().isEmpty() ) &&
00154         transport()->authenticationType() != Transport::EnumAuthenticationType::GSSAPI ) {
00155       QString user = transport()->userName();
00156       QString passwd = transport()->password();
00157       int result;
00158 
00159       bool keep = transport()->storePassword();
00160       result = KIO::PasswordDialog::getNameAndPassword(
00161         user, passwd, &keep,
00162         i18n( "You need to supply a username and a password to use this SMTP server." ),
00163         false, QString(), transport()->name(), QString() );
00164 
00165       if ( result != QDialog::Accepted ) {
00166         setError( KilledJobError );
00167         emitResult();
00168         return;
00169       }
00170       transport()->setUserName( user );
00171       transport()->setPassword( passwd );
00172       transport()->setStorePassword( keep );
00173       transport()->writeConfig();
00174     }
00175     destination.setUser( transport()->userName() );
00176     destination.setPass( transport()->password() );
00177   }
00178 
00179   // dotstuffing is now done by the slave (see setting of metadata)
00180   if ( !data().isEmpty() ) {
00181     // allow +5% for subsequent LF->CRLF and dotstuffing (an average
00182     // over 2G-lines gives an average line length of 42-43):
00183     destination.addQueryItem( QLatin1String( "size" ),
00184                               QString::number( qRound( data().length() * 1.05 ) ) );
00185   }
00186 
00187   destination.setPath( QLatin1String( "/send" ) );
00188 
00189   d->slave = s_slavePool->slaves.value( transport()->id() );
00190   if ( !d->slave ) {
00191     KIO::MetaData slaveConfig;
00192     slaveConfig.insert( QLatin1String( "tls" ),
00193                         ( transport()->encryption() == Transport::EnumEncryption::TLS ) ?
00194                         QLatin1String( "on" ) : QLatin1String( "off" ) );
00195     if ( transport()->requiresAuthentication() ) {
00196       slaveConfig.insert( QLatin1String( "sasl" ), transport()->authenticationTypeString() );
00197     }
00198     d->slave = KIO::Scheduler::getConnectedSlave( destination, slaveConfig );
00199     kDebug() << "Created new SMTP slave" << d->slave;
00200     s_slavePool->slaves.insert( transport()->id(), d->slave );
00201   } else {
00202     kDebug() << "Re-using existing slave" << d->slave;
00203   }
00204 
00205   KIO::TransferJob *job = KIO::put( destination, -1, KIO::HideProgressInfo );
00206   if ( !d->slave || !job ) {
00207     setError( UserDefinedError );
00208     setErrorText( i18n( "Unable to create SMTP job." ) );
00209     emitResult();
00210     return;
00211   }
00212 
00213   job->addMetaData( QLatin1String( "lf2crlf+dotstuff" ), QLatin1String( "slave" ) );
00214   connect( job, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
00215            SLOT(dataRequest(KIO::Job*,QByteArray&)) );
00216 
00217   addSubjob( job );
00218   KIO::Scheduler::assignJobToSlave( d->slave, job );
00219 
00220   setTotalAmount( KJob::Bytes, data().length() );
00221 }
00222 
00223 bool SmtpJob::doKill()
00224 {
00225   if ( s_slavePool.isDestroyed() ) {
00226     return false;
00227   }
00228 
00229   if ( !hasSubjobs() ) {
00230     return true;
00231   }
00232   if ( d->currentState == SmtpJobPrivate::Precommand ) {
00233     return subjobs().first()->kill();
00234   } else if ( d->currentState == SmtpJobPrivate::Smtp ) {
00235     KIO::SimpleJob *job = static_cast<KIO::SimpleJob*>( subjobs().first() );
00236     clearSubjobs();
00237     KIO::Scheduler::cancelJob( job );
00238     s_slavePool->removeSlave( d->slave );
00239     return true;
00240   }
00241   return false;
00242 }
00243 
00244 void SmtpJob::slotResult( KJob *job )
00245 {
00246   if ( s_slavePool.isDestroyed() ) {
00247     return;
00248   }
00249 
00250   // The job has finished, so we don't care about any further errors. Set
00251   // d->finished to true, so slaveError() knows about this and doesn't call
00252   // emitResult() anymore.
00253   // Sometimes, the SMTP slave emits more than one error
00254   //
00255   // The first error causes slotResult() to be called, but not slaveError(), since
00256   // the scheduler doesn't emit errors for connected slaves.
00257   //
00258   // The second error then causes slaveError() to be called (as the slave is no
00259   // longer connected), which does emitResult() a second time, which is invalid
00260   // (and triggers an assert in KMail).
00261   d->finished = true;
00262 
00263   // Normally, calling TransportJob::slotResult() whould set the proper error code
00264   // for error() via KComposite::slotResult(). However, we can't call that here,
00265   // since that also emits the result signal.
00266   // In KMail, when there are multiple mails in the outbox, KMail tries to send
00267   // the next mail when it gets the result signal, which then would reuse the
00268   // old broken slave from the slave pool if there was an error.
00269   // To prevent that, we call TransportJob::slotResult() only after removing the
00270   // slave from the pool and calculate the error code ourselves.
00271   int errorCode = error();
00272   if ( !errorCode ) {
00273     errorCode = job->error();
00274   }
00275 
00276   if ( errorCode && d->currentState == SmtpJobPrivate::Smtp ) {
00277     s_slavePool->removeSlave( d->slave, errorCode != KIO::ERR_SLAVE_DIED );
00278     TransportJob::slotResult( job );
00279     return;
00280   }
00281 
00282   TransportJob::slotResult( job );
00283   if ( !error() && d->currentState == SmtpJobPrivate::Precommand ) {
00284     d->currentState = SmtpJobPrivate::Smtp;
00285     startSmtpJob();
00286     return;
00287   }
00288   if ( !error() ) {
00289     emitResult();
00290   }
00291 }
00292 
00293 void SmtpJob::dataRequest( KIO::Job *job, QByteArray &data )
00294 {
00295   if ( s_slavePool.isDestroyed() ) {
00296     return;
00297   }
00298 
00299   Q_UNUSED( job );
00300   Q_ASSERT( job );
00301   if ( buffer()->atEnd() ) {
00302     data.clear();
00303   } else {
00304     Q_ASSERT( buffer()->isOpen() );
00305     data = buffer()->read( 32 * 1024 );
00306   }
00307   setProcessedAmount( KJob::Bytes, buffer()->pos() );
00308 }
00309 
00310 void SmtpJob::slaveError( KIO::Slave *slave, int errorCode, const QString &errorMsg )
00311 {
00312   if ( s_slavePool.isDestroyed() ) {
00313     return;
00314   }
00315 
00316   s_slavePool->removeSlave( slave, errorCode != KIO::ERR_SLAVE_DIED );
00317   if ( d->slave == slave && !d->finished ) {
00318     setError( errorCode );
00319     setErrorText( KIO::buildErrorString( errorCode, errorMsg ) );
00320     emitResult();
00321   }
00322 }
00323 
00324 #include "smtpjob.moc"

mailtransport

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.7.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal