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