• Skip to content
  • Skip to link menu
KDE 4.1 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       const int slaveKey = slaves.key( slave );
00049       if ( slaveKey > 0 ) {
00050         slaves.remove( slaveKey );
00051         if ( disconnect ) {
00052           KIO::Scheduler::disconnectSlave( slave );
00053         }
00054       }
00055     }
00056 };
00057 
00058 K_GLOBAL_STATIC( SlavePool, s_slavePool )
00059 
00060 
00064 class SmtpJobPrivate
00065 {
00066   public:
00067     KIO::Slave *slave;
00068     enum State {
00069       Idle, Precommand, Smtp
00070     } currentState;
00071     bool finished;
00072 };
00073 
00074 SmtpJob::SmtpJob( Transport *transport, QObject *parent )
00075   : TransportJob( transport, parent ), d( new SmtpJobPrivate )
00076 {
00077   d->currentState = SmtpJobPrivate::Idle;
00078   d->slave = 0;
00079   d->finished = false;
00080   if ( !s_slavePool.isDestroyed() ) {
00081     s_slavePool->ref++;
00082   }
00083   KIO::Scheduler::connect( SIGNAL(slaveError(KIO::Slave*,int,QString)),
00084                            this, SLOT(slaveError(KIO::Slave*,int,QString)) );
00085 }
00086 
00087 SmtpJob::~SmtpJob()
00088 {
00089   if ( !s_slavePool.isDestroyed() ) {
00090     s_slavePool->ref--;
00091     if ( s_slavePool->ref == 0 ) {
00092       kDebug() << "clearing SMTP slave pool" << s_slavePool->slaves.count();
00093       foreach ( KIO::Slave *slave, s_slavePool->slaves.values() ) {
00094         KIO::Scheduler::disconnectSlave( slave );
00095       }
00096       s_slavePool->slaves.clear();
00097     }
00098   }
00099   delete d;
00100 }
00101 
00102 void SmtpJob::doStart()
00103 {
00104   if ( s_slavePool.isDestroyed() ) {
00105     return;
00106   }
00107 
00108   if ( s_slavePool->slaves.contains( transport()->id() ) ||
00109        transport()->precommand().isEmpty() ) {
00110     d->currentState = SmtpJobPrivate::Smtp;
00111     startSmtpJob();
00112   } else {
00113     d->currentState = SmtpJobPrivate::Precommand;
00114     PrecommandJob *job = new PrecommandJob( transport()->precommand(), this );
00115     addSubjob( job );
00116     job->start();
00117   }
00118 }
00119 
00120 void SmtpJob::startSmtpJob()
00121 {
00122   if ( s_slavePool.isDestroyed() ) {
00123     return;
00124   }
00125 
00126   KUrl destination;
00127   destination.setProtocol( ( transport()->encryption() == Transport::EnumEncryption::SSL ) ?
00128                            SMTPS_PROTOCOL : SMTP_PROTOCOL );
00129   destination.setHost( transport()->host() );
00130   destination.setPort( transport()->port() );
00131 
00132   destination.addQueryItem( QLatin1String( "headers" ), QLatin1String( "0" ) );
00133   destination.addQueryItem( QLatin1String( "from" ), sender() );
00134 
00135   foreach ( const QString& str, to() ) {
00136     destination.addQueryItem( QLatin1String( "to" ), str );
00137   }
00138   foreach ( const QString& str, cc() ) {
00139     destination.addQueryItem( QLatin1String( "cc" ), str );
00140   }
00141   foreach ( const QString& str, bcc() ) {
00142     destination.addQueryItem( QLatin1String( "bcc" ), str );
00143   }
00144 
00145   if ( transport()->specifyHostname() ) {
00146     destination.addQueryItem( QLatin1String( "hostname" ), transport()->localHostname() );
00147   }
00148 
00149 #ifdef __GNUC__
00150 #warning Argh!
00151 #endif
00152 //   if ( !kmkernel->msgSender()->sendQuotedPrintable() )
00153 //     query += "&body=8bit";
00154 
00155   if ( transport()->requiresAuthentication() ) {
00156     if( ( transport()->userName().isEmpty() || transport()->password().isEmpty() ) &&
00157         transport()->authenticationType() != Transport::EnumAuthenticationType::GSSAPI ) {
00158       QString user = transport()->userName();
00159       QString passwd = transport()->password();
00160       int result;
00161 
00162 #ifdef __GNUC__
00163 #warning yet another KMail specific thing
00164 #endif
00165 //       KCursorSaver idle( KBusyPtr::idle() );
00166       bool keep = true;
00167       result = KIO::PasswordDialog::getNameAndPassword(
00168         user, passwd, &keep,
00169         i18n( "You need to supply a username and a password to use this SMTP server." ),
00170         false, QString(), transport()->name(), QString() );
00171 
00172       if ( result != QDialog::Accepted ) {
00173         setError( KilledJobError );
00174         emitResult();
00175         return;
00176       }
00177       transport()->setUserName( user );
00178       transport()->setPassword( passwd );
00179       transport()->setStorePassword( keep );
00180       transport()->writeConfig();
00181     }
00182     destination.setUser( transport()->userName() );
00183     destination.setPass( transport()->password() );
00184   }
00185 
00186   // dotstuffing is now done by the slave (see setting of metadata)
00187   if ( !data().isEmpty() ) {
00188     // allow +5% for subsequent LF->CRLF and dotstuffing (an average
00189     // over 2G-lines gives an average line length of 42-43):
00190     destination.addQueryItem( QLatin1String( "size" ),
00191                               QString::number( qRound( data().length() * 1.05 ) ) );
00192   }
00193 
00194   destination.setPath( QLatin1String( "/send" ) );
00195 
00196   d->slave = s_slavePool->slaves.value( transport()->id() );
00197   if ( !d->slave ) {
00198     kDebug() << "creating new SMTP 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     s_slavePool->slaves.insert( transport()->id(), d->slave );
00208   } else {
00209     kDebug() << "re-using existing slave";
00210   }
00211 
00212   KIO::TransferJob *job = KIO::put( destination, -1, KIO::HideProgressInfo );
00213   if ( !d->slave || !job ) {
00214     setError( UserDefinedError );
00215     setErrorText( i18n( "Unable to create SMTP job." ) );
00216     emitResult();
00217     return;
00218   }
00219 
00220   job->addMetaData( QLatin1String( "lf2crlf+dotstuff" ), QLatin1String( "slave" ) );
00221   connect( job, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
00222            SLOT(dataRequest(KIO::Job*,QByteArray&)) );
00223 
00224   addSubjob( job );
00225   KIO::Scheduler::assignJobToSlave( d->slave, job );
00226 
00227   setTotalAmount( KJob::Bytes, data().length() );
00228 }
00229 
00230 bool SmtpJob::doKill()
00231 {
00232   if ( s_slavePool.isDestroyed() ) {
00233     return false;
00234   }
00235 
00236   if ( !hasSubjobs() ) {
00237     return true;
00238   }
00239   if ( d->currentState == SmtpJobPrivate::Precommand ) {
00240     return subjobs().first()->kill();
00241   } else if ( d->currentState == SmtpJobPrivate::Smtp ) {
00242     KIO::SimpleJob *job = static_cast<KIO::SimpleJob*>( subjobs().first() );
00243     clearSubjobs();
00244     KIO::Scheduler::cancelJob( job );
00245     s_slavePool->removeSlave( d->slave );
00246     return true;
00247   }
00248   return false;
00249 }
00250 
00251 void SmtpJob::slotResult( KJob *job )
00252 {
00253   if ( s_slavePool.isDestroyed() ) {
00254     return;
00255   }
00256 
00257   // The job has finished, so we don't care about any further errors. Set
00258   // d->finished to true, so slaveError() knows about this and doesn't call
00259   // emitResult() anymore.
00260   // Sometimes, the SMTP slave emits more than one error
00261   //
00262   // The first error causes slotResult() to be called, but not slaveError(), since
00263   // the scheduler doesn't emit errors for connected slaves.
00264   //
00265   // The second error then causes slaveError() to be called (as the slave is no
00266   // longer connected), which does emitResult() a second time, which is invalid
00267   // (and triggers an assert in KMail).
00268   d->finished = true;
00269 
00270   TransportJob::slotResult( job );
00271   if ( error() && d->currentState == SmtpJobPrivate::Smtp ) {
00272     s_slavePool->removeSlave( d->slave, error() != KIO::ERR_SLAVE_DIED );
00273     return;
00274   }
00275   if ( !error() && d->currentState == SmtpJobPrivate::Precommand ) {
00276     d->currentState = SmtpJobPrivate::Smtp;
00277     startSmtpJob();
00278     return;
00279   }
00280   if ( !error() ) {
00281     emitResult();
00282   }
00283 }
00284 
00285 void SmtpJob::dataRequest( KIO::Job *job, QByteArray &data )
00286 {
00287   if ( s_slavePool.isDestroyed() ) {
00288     return;
00289   }
00290 
00291   Q_ASSERT( job );
00292   if ( buffer()->atEnd() ) {
00293     data.clear();
00294   } else {
00295     Q_ASSERT( buffer()->isOpen() );
00296     data = buffer()->read( 32 * 1024 );
00297   }
00298   setProcessedAmount( KJob::Bytes, buffer()->pos() );
00299 }
00300 
00301 void SmtpJob::slaveError( KIO::Slave *slave, int errorCode, const QString &errorMsg )
00302 {
00303   if ( s_slavePool.isDestroyed() ) {
00304     return;
00305   }
00306 
00307   s_slavePool->removeSlave( slave, errorCode != KIO::ERR_SLAVE_DIED );
00308   if ( d->slave == slave && !d->finished ) {
00309     setError( errorCode );
00310     setErrorText( KIO::buildErrorString( errorCode, errorMsg ) );
00311     emitResult();
00312   }
00313 }
00314 
00315 #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
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.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