• Skip to content
  • Skip to link menu
KDE 4.4 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 #ifdef __GNUC__
00153 #warning Argh!
00154 #endif
00155 //   if ( !kmkernel->msgSender()->sendQuotedPrintable() )
00156 //     query += "&body=8bit";
00157 
00158   if ( transport()->requiresAuthentication() ) {
00159     if( ( transport()->userName().isEmpty() || transport()->password().isEmpty() ) &&
00160         transport()->authenticationType() != Transport::EnumAuthenticationType::GSSAPI ) {
00161       QString user = transport()->userName();
00162       QString passwd = transport()->password();
00163       int result;
00164 
00165 #ifdef __GNUC__
00166 #warning yet another KMail specific thing
00167 #endif
00168 //       KCursorSaver idle( KBusyPtr::idle() );
00169       bool keep = transport()->storePassword();
00170       result = KIO::PasswordDialog::getNameAndPassword(
00171         user, passwd, &keep,
00172         i18n( "You need to supply a username and a password to use this SMTP server." ),
00173         false, QString(), transport()->name(), QString() );
00174 
00175       if ( result != QDialog::Accepted ) {
00176         setError( KilledJobError );
00177         emitResult();
00178         return;
00179       }
00180       transport()->setUserName( user );
00181       transport()->setPassword( passwd );
00182       transport()->setStorePassword( keep );
00183       transport()->writeConfig();
00184     }
00185     destination.setUser( transport()->userName() );
00186     destination.setPass( transport()->password() );
00187   }
00188 
00189   // dotstuffing is now done by the slave (see setting of metadata)
00190   if ( !data().isEmpty() ) {
00191     // allow +5% for subsequent LF->CRLF and dotstuffing (an average
00192     // over 2G-lines gives an average line length of 42-43):
00193     destination.addQueryItem( QLatin1String( "size" ),
00194                               QString::number( qRound( data().length() * 1.05 ) ) );
00195   }
00196 
00197   destination.setPath( QLatin1String( "/send" ) );
00198 
00199   d->slave = s_slavePool->slaves.value( transport()->id() );
00200   if ( !d->slave ) {
00201     KIO::MetaData slaveConfig;
00202     slaveConfig.insert( QLatin1String( "tls" ),
00203                         ( transport()->encryption() == Transport::EnumEncryption::TLS ) ?
00204                         QLatin1String( "on" ) : QLatin1String( "off" ) );
00205     if ( transport()->requiresAuthentication() ) {
00206       slaveConfig.insert( QLatin1String( "sasl" ), transport()->authenticationTypeString() );
00207     }
00208     d->slave = KIO::Scheduler::getConnectedSlave( destination, slaveConfig );
00209     kDebug() << "Created new SMTP slave" << d->slave;
00210     s_slavePool->slaves.insert( transport()->id(), d->slave );
00211   } else {
00212     kDebug() << "Re-using existing slave" << d->slave;
00213   }
00214 
00215   KIO::TransferJob *job = KIO::put( destination, -1, KIO::HideProgressInfo );
00216   if ( !d->slave || !job ) {
00217     setError( UserDefinedError );
00218     setErrorText( i18n( "Unable to create SMTP job." ) );
00219     emitResult();
00220     return;
00221   }
00222 
00223   job->addMetaData( QLatin1String( "lf2crlf+dotstuff" ), QLatin1String( "slave" ) );
00224   connect( job, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
00225            SLOT(dataRequest(KIO::Job*,QByteArray&)) );
00226 
00227   addSubjob( job );
00228   KIO::Scheduler::assignJobToSlave( d->slave, job );
00229 
00230   setTotalAmount( KJob::Bytes, data().length() );
00231 }
00232 
00233 bool SmtpJob::doKill()
00234 {
00235   if ( s_slavePool.isDestroyed() ) {
00236     return false;
00237   }
00238 
00239   if ( !hasSubjobs() ) {
00240     return true;
00241   }
00242   if ( d->currentState == SmtpJobPrivate::Precommand ) {
00243     return subjobs().first()->kill();
00244   } else if ( d->currentState == SmtpJobPrivate::Smtp ) {
00245     KIO::SimpleJob *job = static_cast<KIO::SimpleJob*>( subjobs().first() );
00246     clearSubjobs();
00247     KIO::Scheduler::cancelJob( job );
00248     s_slavePool->removeSlave( d->slave );
00249     return true;
00250   }
00251   return false;
00252 }
00253 
00254 void SmtpJob::slotResult( KJob *job )
00255 {
00256   if ( s_slavePool.isDestroyed() ) {
00257     return;
00258   }
00259 
00260   // The job has finished, so we don't care about any further errors. Set
00261   // d->finished to true, so slaveError() knows about this and doesn't call
00262   // emitResult() anymore.
00263   // Sometimes, the SMTP slave emits more than one error
00264   //
00265   // The first error causes slotResult() to be called, but not slaveError(), since
00266   // the scheduler doesn't emit errors for connected slaves.
00267   //
00268   // The second error then causes slaveError() to be called (as the slave is no
00269   // longer connected), which does emitResult() a second time, which is invalid
00270   // (and triggers an assert in KMail).
00271   d->finished = true;
00272 
00273   // Normally, calling TransportJob::slotResult() whould set the proper error code
00274   // for error() via KComposite::slotResult(). However, we can't call that here,
00275   // since that also emits the result signal.
00276   // In KMail, when there are multiple mails in the outbox, KMail tries to send
00277   // the next mail when it gets the result signal, which then would reuse the
00278   // old broken slave from the slave pool if there was an error.
00279   // To prevent that, we call TransportJob::slotResult() only after removing the
00280   // slave from the pool and calculate the error code ourselves.
00281   int errorCode = error();
00282   if ( !errorCode ) {
00283     errorCode = job->error();
00284   }
00285 
00286   if ( errorCode && d->currentState == SmtpJobPrivate::Smtp ) {
00287     s_slavePool->removeSlave( d->slave, errorCode != KIO::ERR_SLAVE_DIED );
00288     TransportJob::slotResult( job );
00289     return;
00290   }
00291 
00292   TransportJob::slotResult( job );
00293   if ( !error() && d->currentState == SmtpJobPrivate::Precommand ) {
00294     d->currentState = SmtpJobPrivate::Smtp;
00295     startSmtpJob();
00296     return;
00297   }
00298   if ( !error() ) {
00299     emitResult();
00300   }
00301 }
00302 
00303 void SmtpJob::dataRequest( KIO::Job *job, QByteArray &data )
00304 {
00305   if ( s_slavePool.isDestroyed() ) {
00306     return;
00307   }
00308 
00309   Q_ASSERT( job );
00310   if ( buffer()->atEnd() ) {
00311     data.clear();
00312   } else {
00313     Q_ASSERT( buffer()->isOpen() );
00314     data = buffer()->read( 32 * 1024 );
00315   }
00316   setProcessedAmount( KJob::Bytes, buffer()->pos() );
00317 }
00318 
00319 void SmtpJob::slaveError( KIO::Slave *slave, int errorCode, const QString &errorMsg )
00320 {
00321   if ( s_slavePool.isDestroyed() ) {
00322     return;
00323   }
00324 
00325   s_slavePool->removeSlave( slave, errorCode != KIO::ERR_SLAVE_DIED );
00326   if ( d->slave == slave && !d->finished ) {
00327     setError( errorCode );
00328     setErrorText( KIO::buildErrorString( errorCode, errorMsg ) );
00329     emitResult();
00330   }
00331 }
00332 
00333 #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.6.2-20100208
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