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 <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
00153
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
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
00187 if ( !data().isEmpty() ) {
00188
00189
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
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
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"