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

mailtransport

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.7
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