00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
00200 if ( !data().isEmpty() ) {
00201
00202
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
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290 d->finished = true;
00291
00292
00293
00294
00295
00296
00297
00298
00299
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"