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
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
00156
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
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
00190 if ( !data().isEmpty() ) {
00191
00192
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
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271 d->finished = true;
00272
00273
00274
00275
00276
00277
00278
00279
00280
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"