00001
00002
00003 #include <config.h>
00004
00005 #define REALLY_WANT_KMSENDER
00006 #include "kmsender.h"
00007 #include "kmsender_p.h"
00008 #undef REALLY_WANT_KMSENDER
00009
00010 #include <kmime_header_parsing.h>
00011 using namespace KMime::Types;
00012
00013 #include <kio/passdlg.h>
00014 #include <kio/scheduler.h>
00015 #include <kapplication.h>
00016 #include <kmessagebox.h>
00017 #include <kdeversion.h>
00018 #include <klocale.h>
00019 #include <kdebug.h>
00020 #include <kconfig.h>
00021
00022 #include <assert.h>
00023 #include <stdio.h>
00024 #include <unistd.h>
00025 #include <sys/types.h>
00026 #include <sys/stat.h>
00027 #include <sys/wait.h>
00028 #include "globalsettings.h"
00029 #include "kmfiltermgr.h"
00030
00031 #include "kcursorsaver.h"
00032 #include <libkpimidentities/identity.h>
00033 #include <libkpimidentities/identitymanager.h>
00034 #include "progressmanager.h"
00035 #include "kmaccount.h"
00036 #include "kmtransport.h"
00037 #include "kmfolderindex.h"
00038 #include "kmfoldermgr.h"
00039 #include "kmmsgdict.h"
00040 #include "kmmsgpart.h"
00041 #include "protocols.h"
00042 #include "kmcommands.h"
00043 #include <mimelib/mediatyp.h>
00044 #include <mimelib/enum.h>
00045 #include <mimelib/param.h>
00046
00047 #define SENDER_GROUP "sending mail"
00048
00049
00050 KMSender::KMSender()
00051 : mOutboxFolder( 0 ), mSentFolder( 0 )
00052 {
00053 mPrecommand = 0;
00054 mSendProc = 0;
00055 mSendProcStarted = false;
00056 mSendInProgress = false;
00057 mCurrentMsg = 0;
00058 mTransportInfo = new KMTransportInfo();
00059 readConfig();
00060 mSendAborted = false;
00061 mSentMessages = 0;
00062 mTotalMessages = 0;
00063 mFailedMessages = 0;
00064 mSentBytes = 0;
00065 mTotalBytes = 0;
00066 mProgressItem = 0;
00067 }
00068
00069
00070
00071 KMSender::~KMSender()
00072 {
00073 writeConfig(false);
00074 delete mSendProc;
00075 delete mPrecommand;
00076 delete mTransportInfo;
00077 }
00078
00079
00080 void KMSender::setStatusMsg(const QString &msg)
00081 {
00082 if ( mProgressItem )
00083 mProgressItem->setStatus(msg);
00084 }
00085
00086
00087 void KMSender::readConfig(void)
00088 {
00089 QString str;
00090 KConfigGroup config(KMKernel::config(), SENDER_GROUP);
00091
00092 mSendImmediate = config.readBoolEntry("Immediate", true);
00093 mSendQuotedPrintable = config.readBoolEntry("Quoted-Printable", true);
00094 }
00095
00096
00097
00098 void KMSender::writeConfig(bool aWithSync)
00099 {
00100 KConfigGroup config(KMKernel::config(), SENDER_GROUP);
00101
00102 config.writeEntry("Immediate", mSendImmediate);
00103 config.writeEntry("Quoted-Printable", mSendQuotedPrintable);
00104
00105 if (aWithSync) config.sync();
00106 }
00107
00108
00109
00110 bool KMSender::settingsOk() const
00111 {
00112 if (KMTransportInfo::availableTransports().isEmpty())
00113 {
00114 KMessageBox::information(0,i18n("Please create an account for sending and try again."));
00115 return false;
00116 }
00117 return true;
00118 }
00119
00120 static void handleRedirections( KMMessage * m ) {
00121 const QString from = m->headerField("X-KMail-Redirect-From");
00122 const QString msgId = m->msgId();
00123 if( from.isEmpty() || msgId.isEmpty() )
00124 m->setMsgId( KMMessage::generateMessageId( m->sender() ) );
00125 }
00126
00127
00128 bool KMSender::doSend(KMMessage* aMsg, short sendNow)
00129 {
00130 if(!aMsg)
00131 return false;
00132
00133 if (!settingsOk()) return false;
00134
00135 if (aMsg->to().isEmpty())
00136 {
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157 aMsg->setTo("Undisclosed.Recipients: ;");
00158 }
00159
00160 handleRedirections( aMsg );
00161
00162 if (sendNow==-1) sendNow = mSendImmediate;
00163
00164 KMFolder * const outbox = kmkernel->outboxFolder();
00165 const KMFolderOpener openOutbox( outbox, "outbox" );
00166
00167 aMsg->setStatus(KMMsgStatusQueued);
00168
00169 if ( const int err = outbox->addMsg(aMsg) ) {
00170 Q_UNUSED( err );
00171 KMessageBox::information(0,i18n("Cannot add message to outbox folder"));
00172 return false;
00173 }
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186 const int idx = outbox->count() - 1;
00187 KMMessage * const unencryptedMsg = aMsg->unencryptedMsg();
00188 outbox->unGetMsg( idx );
00189 KMMessage * const tempMsg = outbox->getMsg( idx );
00190 tempMsg->setUnencryptedMsg( unencryptedMsg );
00191
00192 if ( !sendNow || mSendInProgress )
00193 return true;
00194
00195 return sendQueued();
00196 }
00197
00198
00199
00200 void KMSender::outboxMsgAdded(int idx)
00201 {
00202 ++mTotalMessages;
00203 KMMsgBase* msg = kmkernel->outboxFolder()->getMsgBase(idx);
00204 Q_ASSERT(msg);
00205 if ( msg )
00206 mTotalBytes += msg->msgSize();
00207 }
00208
00209
00210
00211 bool KMSender::doSendQueued( const QString &customTransport )
00212 {
00213 if (!settingsOk()) return false;
00214
00215 if (mSendInProgress)
00216 {
00217 return false;
00218 }
00219
00220
00221 mOutboxFolder = kmkernel->outboxFolder();
00222 mOutboxFolder->open("dosendoutbox");
00223 mTotalMessages = mOutboxFolder->count();
00224 if (mTotalMessages == 0) {
00225
00226 mOutboxFolder->close("dosendoutbox");
00227 mOutboxFolder = 0;
00228 return true;
00229 }
00230 mTotalBytes = 0;
00231 for( int i = 0 ; i<mTotalMessages ; ++i )
00232 mTotalBytes += mOutboxFolder->getMsgBase(i)->msgSize();
00233
00234 connect( mOutboxFolder, SIGNAL(msgAdded(int)),
00235 this, SLOT(outboxMsgAdded(int)) );
00236 mCurrentMsg = 0;
00237
00238 mSentFolder = kmkernel->sentFolder();
00239 mSentFolder->open("dosendsent");
00240 kmkernel->filterMgr()->ref();
00241
00242
00243 mCustomTransport = customTransport;
00244 doSendMsg();
00245 return true;
00246 }
00247
00248
00249 void KMSender::emitProgressInfo( int currentFileProgress )
00250 {
00251 int percent = (mTotalBytes) ? ( 100 * (mSentBytes+currentFileProgress) / mTotalBytes ) : 0;
00252 if (percent > 100) percent = 100;
00253 mProgressItem->setProgress(percent);
00254 }
00255
00256 static bool messageIsDispositionNotificationReport( KMMessage *msg )
00257 {
00258 if ( msg->type() == DwMime::kTypeMessage &&
00259 msg->subtype() == DwMime::kSubtypeDispositionNotification )
00260 return true;
00261
00262 if ( msg->type() != DwMime::kTypeMultipart ||
00263 msg->subtype() != DwMime::kSubtypeReport )
00264 return false;
00265
00266 DwMediaType& ct = msg->dwContentType();
00267 DwParameter *param = ct.FirstParameter();
00268 while( param ) {
00269 if ( !qstricmp( param->Attribute().c_str(), "report-type")
00270 && !qstricmp( param->Value().c_str(), "disposition-notification" ) )
00271 return true;
00272 else
00273 param = param->Next();
00274 }
00275 return false;
00276 }
00277
00278
00279 void KMSender::doSendMsg()
00280 {
00281 if (!kmkernel)
00282 return;
00283
00284 const bool someSent = mCurrentMsg;
00285 if (someSent) {
00286 mSentMessages++;
00287 mSentBytes += mCurrentMsg->msgSize();
00288 }
00289
00290
00291 KMFolder *sentFolder = 0, *imapSentFolder = 0;
00292 if (mCurrentMsg && kmkernel->filterMgr())
00293 {
00294 mCurrentMsg->setTransferInProgress( false );
00295 if( mCurrentMsg->hasUnencryptedMsg() ) {
00296 kdDebug(5006) << "KMSender::doSendMsg() post-processing: replace mCurrentMsg body by unencryptedMsg data" << endl;
00297
00298 mCurrentMsg->deleteBodyParts();
00299
00300 KMMessage & newMsg( *mCurrentMsg->unencryptedMsg() );
00301 mCurrentMsg->dwContentType() = newMsg.dwContentType();
00302 mCurrentMsg->setContentTransferEncodingStr( newMsg.contentTransferEncodingStr() );
00303 QCString newDispo = newMsg.headerField("Content-Disposition").latin1();
00304 if( newDispo.isEmpty() )
00305 mCurrentMsg->removeHeaderField( "Content-Disposition" );
00306 else
00307 mCurrentMsg->setHeaderField( "Content-Disposition", newDispo );
00308
00309 mCurrentMsg->setBody( newMsg.body() );
00310
00311 KMMessagePart msgPart;
00312 for( int i = 0; i < newMsg.numBodyParts(); ++i ) {
00313 newMsg.bodyPart( i, &msgPart );
00314 mCurrentMsg->addBodyPart( &msgPart );
00315 }
00316 }
00317 mCurrentMsg->setStatus(KMMsgStatusSent);
00318 mCurrentMsg->setStatus(KMMsgStatusRead);
00319 mCurrentMsg->updateAttachmentState();
00320 mCurrentMsg->updateInvitationState();
00321
00322 const KPIM::Identity & id = kmkernel->identityManager()
00323 ->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
00324 if ( !mCurrentMsg->fcc().isEmpty() )
00325 {
00326 sentFolder = kmkernel->folderMgr()->findIdString( mCurrentMsg->fcc() );
00327 if ( sentFolder == 0 )
00328
00329 sentFolder =
00330 kmkernel->dimapFolderMgr()->findIdString( mCurrentMsg->fcc() );
00331 if ( sentFolder == 0 )
00332 imapSentFolder =
00333 kmkernel->imapFolderMgr()->findIdString( mCurrentMsg->fcc() );
00334 }
00335
00336
00337 if ( ( sentFolder == 0 || sentFolder->isReadOnly() )
00338 && ( imapSentFolder == 0 || imapSentFolder->isReadOnly() )
00339 && !id.fcc().isEmpty() )
00340 {
00341 sentFolder = kmkernel->folderMgr()->findIdString( id.fcc() );
00342 if ( sentFolder == 0 )
00343
00344 sentFolder = kmkernel->dimapFolderMgr()->findIdString( id.fcc() );
00345 if ( sentFolder == 0 )
00346 imapSentFolder = kmkernel->imapFolderMgr()->findIdString( id.fcc() );
00347 }
00348 if (imapSentFolder
00349 && ( imapSentFolder->noContent() || imapSentFolder->isReadOnly() ) )
00350 imapSentFolder = 0;
00351
00352 if ( sentFolder == 0 || sentFolder->isReadOnly() )
00353 sentFolder = kmkernel->sentFolder();
00354
00355 if ( sentFolder ) {
00356 if ( const int err = sentFolder->open("sentFolder") ) {
00357 Q_UNUSED( err );
00358 cleanup();
00359 return;
00360 }
00361 }
00362
00363
00364
00365
00366 if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( true );
00367 const int processResult = kmkernel->filterMgr()->process(mCurrentMsg,KMFilterMgr::Outbound);
00368 if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( false );
00369
00370
00371 switch (processResult) {
00372 case 2:
00373 perror("Critical error: Unable to process sent mail (out of space?)");
00374 KMessageBox::information(0, i18n("Critical error: "
00375 "Unable to process sent mail (out of space?)"
00376 "Moving failing message to \"sent-mail\" folder."));
00377 if ( sentFolder ) {
00378 sentFolder->moveMsg(mCurrentMsg);
00379 sentFolder->close("sentFolder");
00380 }
00381 cleanup();
00382 return;
00383 case 1:
00384 if ( sentFolder && sentFolder->moveMsg(mCurrentMsg) != 0 )
00385 {
00386 KMessageBox::error(0, i18n("Moving the sent message \"%1\" from the "
00387 "\"outbox\" to the \"sent-mail\" folder failed.\n"
00388 "Possible reasons are lack of disk space or write permission. "
00389 "Please try to fix the problem and move the message manually.")
00390 .arg(mCurrentMsg->subject()));
00391 cleanup();
00392 return;
00393 }
00394 if (imapSentFolder) {
00395
00396 KMCommand *command = new KMMoveCommand( imapSentFolder, mCurrentMsg );
00397 command->keepFolderOpen( sentFolder );
00398 command->start();
00399 }
00400 default:
00401 break;
00402 }
00403 setStatusByLink( mCurrentMsg );
00404 if (mCurrentMsg->parent() && !imapSentFolder) {
00405
00406
00407 assert( mCurrentMsg->parent()->find( mCurrentMsg )
00408 == mCurrentMsg->parent()->count() - 1 );
00409
00410 mCurrentMsg->parent()->unGetMsg( mCurrentMsg->parent()->count() -1 );
00411 }
00412
00413 mCurrentMsg = 0;
00414 }
00415
00416
00417 mCurrentMsg = mOutboxFolder->getMsg(mFailedMessages);
00418 if ( mCurrentMsg && !mCurrentMsg->transferInProgress() &&
00419 mCurrentMsg->sender().isEmpty() ) {
00420
00421
00422
00423 const KPIM::Identity & id = kmkernel->identityManager()
00424 ->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
00425 if ( !id.emailAddr().isEmpty() ) {
00426 mCurrentMsg->setFrom( id.fullEmailAddr() );
00427 }
00428 else if ( !kmkernel->identityManager()->defaultIdentity().emailAddr().isEmpty() ) {
00429 mCurrentMsg->setFrom( kmkernel->identityManager()->defaultIdentity().fullEmailAddr() );
00430 }
00431 else {
00432 KMessageBox::sorry( 0, i18n( "It's not possible to send messages "
00433 "without specifying a sender address.\n"
00434 "Please set the email address of "
00435 "identity '%1' in the Identities "
00436 "section of the configuration dialog "
00437 "and then try again." )
00438 .arg( id.identityName() ) );
00439 mOutboxFolder->unGetMsg( mFailedMessages );
00440 mCurrentMsg = 0;
00441 }
00442 }
00443 if (!mCurrentMsg || mCurrentMsg->transferInProgress())
00444 {
00445
00446 if (mCurrentMsg && mCurrentMsg->transferInProgress())
00447 mCurrentMsg = 0;
00448
00449 if ( sentFolder != 0 )
00450 sentFolder->close("sentFolder");
00451 if ( someSent ) {
00452 if ( mSentMessages == mTotalMessages ) {
00453 setStatusMsg(i18n("%n queued message successfully sent.",
00454 "%n queued messages successfully sent.",
00455 mSentMessages));
00456 } else {
00457 setStatusMsg(i18n("%1 of %2 queued messages successfully sent.")
00458 .arg(mSentMessages).arg( mTotalMessages ));
00459 }
00460 }
00461 cleanup();
00462 return;
00463 }
00464 mCurrentMsg->setTransferInProgress( true );
00465
00466
00467 if (!mSendInProgress)
00468 {
00469 Q_ASSERT( !mProgressItem );
00470 mProgressItem = KPIM::ProgressManager::createProgressItem(
00471 "Sender",
00472 i18n( "Sending messages" ),
00473 i18n("Initiating sender process..."),
00474 true );
00475 connect( mProgressItem, SIGNAL(progressItemCanceled(KPIM::ProgressItem*)),
00476 this, SLOT( slotAbortSend() ) );
00477 kapp->ref();
00478 mSendInProgress = true;
00479 }
00480
00481 QString msgTransport = mCustomTransport;
00482 if ( msgTransport.isEmpty() ) {
00483 msgTransport = mCurrentMsg->headerField("X-KMail-Transport");
00484 }
00485 if ( msgTransport.isEmpty() ) {
00486 const QStringList sl = KMTransportInfo::availableTransports();
00487 if (!sl.empty()) msgTransport = sl.front();
00488 }
00489
00490 if (!mSendProc || msgTransport != mMethodStr) {
00491 if (mSendProcStarted && mSendProc) {
00492 mSendProc->finish();
00493 mSendProcStarted = false;
00494 }
00495
00496 mSendProc = createSendProcFromString(msgTransport);
00497 mMethodStr = msgTransport;
00498
00499 if( mTransportInfo->encryption == "TLS" || mTransportInfo->encryption == "SSL" ) {
00500 mProgressItem->setUsesCrypto( true );
00501 } else if ( !mCustomTransport.isEmpty() ) {
00502 int result = KMessageBox::warningContinueCancel( 0,
00503 i18n( "You have chosen to send all queued email using an unencrypted transport, do you want to continue? "),
00504 i18n( "Security Warning" ),
00505 i18n( "Send Unencrypted" ),
00506 "useCustomTransportWithoutAsking", false);
00507
00508 if( result == KMessageBox::Cancel ) {
00509 mProgressItem->cancel();
00510 mProgressItem->setComplete();
00511 slotAbortSend();
00512 cleanup();
00513 return;
00514 }
00515 }
00516
00517 if (!mSendProc)
00518 sendProcStarted(false);
00519 else {
00520 connect(mSendProc, SIGNAL(idle()), SLOT(slotIdle()));
00521 connect(mSendProc, SIGNAL(started(bool)), SLOT(sendProcStarted(bool)));
00522
00523
00524 if ( !mTransportInfo->precommand.isEmpty() ) {
00525 runPrecommand( mTransportInfo->precommand );
00526 return;
00527 }
00528
00529 mSendProc->start();
00530 }
00531 }
00532 else if (!mSendProcStarted)
00533 mSendProc->start();
00534 else
00535 doSendMsgAux();
00536 }
00537
00538 bool KMSender::runPrecommand( const QString & cmd ) {
00539 setStatusMsg( i18n("Executing precommand %1").arg( cmd ) );
00540 mPrecommand = new KMPrecommand( cmd );
00541 connect( mPrecommand, SIGNAL(finished(bool)),
00542 SLOT(slotPrecommandFinished(bool)) );
00543 if ( !mPrecommand->start() ) {
00544 delete mPrecommand; mPrecommand = 0;
00545 return false;
00546 }
00547 return true;
00548 }
00549
00550
00551 void KMSender::sendProcStarted(bool success)
00552 {
00553 if (!success) {
00554 if (mSendProc)
00555 mSendProc->finish();
00556 else
00557 setStatusMsg(i18n("Unrecognized transport protocol. Unable to send message."));
00558 mSendProc = 0;
00559 mSendProcStarted = false;
00560 cleanup();
00561 return;
00562 }
00563 doSendMsgAux();
00564 }
00565
00566
00567 static QStringList addrSpecListToStringList( const AddrSpecList & l, bool allowEmpty=false ) {
00568 QStringList result;
00569 for ( AddrSpecList::const_iterator it = l.begin(), end = l.end() ; it != end ; ++it ) {
00570 const QString s = (*it).asString();
00571 if ( allowEmpty || !s.isEmpty() )
00572 result.push_back( s );
00573 }
00574 return result;
00575 }
00576
00577 static void extractSenderToCCAndBcc( KMMessage * aMsg, QString * sender, QStringList * to, QStringList * cc, QStringList * bcc ) {
00578 if ( sender ) *sender = aMsg->sender();
00579 if( !aMsg->headerField("X-KMail-Recipients").isEmpty() ) {
00580
00581
00582 if ( to ) *to = addrSpecListToStringList( aMsg->extractAddrSpecs( "X-KMail-Recipients" ) );
00583 aMsg->removeHeaderField( "X-KMail-Recipients" );
00584 } else {
00585 if ( to ) *to = addrSpecListToStringList( aMsg->extractAddrSpecs( "To" ) );
00586 if ( cc ) *cc = addrSpecListToStringList( aMsg->extractAddrSpecs( "Cc" ) );
00587 if ( bcc ) *bcc = addrSpecListToStringList( aMsg->extractAddrSpecs( "Bcc" ) );
00588 }
00589 }
00590
00591
00592 void KMSender::doSendMsgAux()
00593 {
00594 mSendProcStarted = true;
00595
00596
00597
00598 setStatusMsg(i18n("%3: subject of message","Sending message %1 of %2: %3")
00599 .arg(mSentMessages+mFailedMessages+1).arg(mTotalMessages)
00600 .arg(mCurrentMsg->subject()));
00601 QStringList to, cc, bcc;
00602 QString sender;
00603 extractSenderToCCAndBcc( mCurrentMsg, &sender, &to, &cc, &bcc );
00604
00605
00606 if ( messageIsDispositionNotificationReport( mCurrentMsg ) && GlobalSettings::self()->sendMDNsWithEmptySender() )
00607 sender = "<>";
00608
00609 const QByteArray message = mCurrentMsg->asSendableString();
00610 if ( sender.isEmpty() || !mSendProc->send( sender, to, cc, bcc, message ) ) {
00611 if ( mCurrentMsg )
00612 mCurrentMsg->setTransferInProgress( false );
00613 if ( mOutboxFolder )
00614 mOutboxFolder->unGetMsg( mFailedMessages );
00615 mCurrentMsg = 0;
00616 cleanup();
00617 setStatusMsg(i18n("Failed to send (some) queued messages."));
00618 return;
00619 }
00620
00621
00622 }
00623
00624
00625
00626 void KMSender::cleanup(void)
00627 {
00628 kdDebug(5006) << k_funcinfo << endl;
00629 if (mSendProc && mSendProcStarted) mSendProc->finish();
00630 mSendProc = 0;
00631 mSendProcStarted = false;
00632 if (mSendInProgress) kapp->deref();
00633 mSendInProgress = false;
00634 if (mCurrentMsg)
00635 {
00636 mCurrentMsg->setTransferInProgress( false );
00637 mCurrentMsg = 0;
00638 }
00639 if ( mSentFolder ) {
00640 mSentFolder->close("dosendsent");
00641 mSentFolder = 0;
00642 }
00643 if ( mOutboxFolder ) {
00644 disconnect( mOutboxFolder, SIGNAL(msgAdded(int)),
00645 this, SLOT(outboxMsgAdded(int)) );
00646 mOutboxFolder->close("dosendoutbox");
00647 if ( mOutboxFolder->count( true ) == 0 ) {
00648 mOutboxFolder->expunge();
00649 }
00650 else if ( mOutboxFolder->needsCompacting() ) {
00651 mOutboxFolder->compact( KMFolder::CompactSilentlyNow );
00652 }
00653 mOutboxFolder = 0;
00654 }
00655
00656 mSendAborted = false;
00657 mSentMessages = 0;
00658 mFailedMessages = 0;
00659 mSentBytes = 0;
00660 if ( mProgressItem )
00661 mProgressItem->setComplete();
00662 mProgressItem = 0;
00663 kmkernel->filterMgr()->deref();
00664 }
00665
00666
00667
00668 void KMSender::slotAbortSend()
00669 {
00670 mSendAborted = true;
00671 delete mPrecommand;
00672 mPrecommand = 0;
00673 if (mSendProc) mSendProc->abort();
00674 }
00675
00676
00677 void KMSender::slotIdle()
00678 {
00679 assert(mSendProc != 0);
00680
00681 QString msg;
00682 QString errString;
00683 if (mSendProc)
00684 errString = mSendProc->lastErrorMessage();
00685
00686 if (mSendAborted) {
00687
00688 if ( mCurrentMsg ) {
00689 mCurrentMsg->setTransferInProgress( false );
00690 if ( mOutboxFolder )
00691 mOutboxFolder->unGetMsg( mFailedMessages );
00692 mCurrentMsg = 0;
00693 }
00694 msg = i18n("Sending aborted:\n%1\n"
00695 "The message will stay in the 'outbox' folder until you either "
00696 "fix the problem (e.g. a broken address) or remove the message "
00697 "from the 'outbox' folder.\n"
00698 "The following transport protocol was used:\n %2")
00699 .arg(errString)
00700 .arg(mMethodStr);
00701 if (!errString.isEmpty()) KMessageBox::error(0,msg);
00702 setStatusMsg( i18n( "Sending aborted." ) );
00703 } else {
00704 if (!mSendProc->sendOk()) {
00705 if ( mCurrentMsg )
00706 mCurrentMsg->setTransferInProgress( false );
00707 if ( mOutboxFolder )
00708 mOutboxFolder->unGetMsg( mFailedMessages );
00709 mCurrentMsg = 0;
00710 mFailedMessages++;
00711
00712 QMapIterator <QString,QString> pc;
00713 if ( (pc = mPasswdCache.find( mMethodStr )) != mPasswdCache.end() ) {
00714 mPasswdCache.erase(pc);
00715 }
00716
00717 if (!errString.isEmpty()) {
00718 int res = KMessageBox::Yes;
00719 if (mSentMessages+mFailedMessages != mTotalMessages) {
00720 msg = i18n("<p>Sending failed:</p>"
00721 "<p>%1</p>"
00722 "<p>The message will stay in the 'outbox' folder until you either "
00723 "fix the problem (e.g. a broken address) or remove the message "
00724 "from the 'outbox' folder.</p>"
00725 "<p>The following transport protocol was used: %2</p>"
00726 "<p>Do you want me to continue sending the remaining messages?</p>")
00727 .arg(errString)
00728 .arg(mMethodStr);
00729 res = KMessageBox::warningYesNo( 0 , msg ,
00730 i18n( "Continue Sending" ), i18n( "&Continue Sending" ),
00731 i18n("&Abort Sending") );
00732 } else {
00733 msg = i18n("Sending failed:\n%1\n"
00734 "The message will stay in the 'outbox' folder until you either "
00735 "fix the problem (e.g. a broken address) or remove the message "
00736 "from the 'outbox' folder.\n"
00737 "The following transport protocol was used:\n %2")
00738 .arg(errString)
00739 .arg(mMethodStr);
00740 KMessageBox::error(0,msg);
00741 }
00742 if (res == KMessageBox::Yes) {
00743
00744 doSendMsg();
00745 return;
00746 } else {
00747 setStatusMsg( i18n( "Sending aborted." ) );
00748 }
00749 }
00750 } else {
00751
00752 doSendMsg();
00753 return;
00754 }
00755 }
00756 mSendProc->finish();
00757 mSendProc = 0;
00758 mSendProcStarted = false;
00759
00760 cleanup();
00761 }
00762
00763
00764
00765 void KMSender::slotPrecommandFinished(bool normalExit)
00766 {
00767 delete mPrecommand;
00768 mPrecommand = 0;
00769 if (normalExit) mSendProc->start();
00770 else slotIdle();
00771 }
00772
00773
00774
00775 void KMSender::setSendImmediate(bool aSendImmediate)
00776 {
00777 mSendImmediate = aSendImmediate;
00778 }
00779
00780
00781
00782 void KMSender::setSendQuotedPrintable(bool aSendQuotedPrintable)
00783 {
00784 mSendQuotedPrintable = aSendQuotedPrintable;
00785 }
00786
00787
00788
00789 KMSendProc* KMSender::createSendProcFromString( const QString & transport )
00790 {
00791 mTransportInfo->type = QString::null;
00792 int nr = KMTransportInfo::findTransport(transport);
00793 if (nr)
00794 {
00795 mTransportInfo->readConfig(nr);
00796 } else {
00797 if (transport.startsWith("smtp://"))
00798 {
00799 mTransportInfo->type = "smtp";
00800 mTransportInfo->auth = false;
00801 mTransportInfo->encryption = "NONE";
00802 QString serverport = transport.mid(7);
00803 int colon = serverport.find(':');
00804 if (colon != -1) {
00805 mTransportInfo->host = serverport.left(colon);
00806 mTransportInfo->port = serverport.mid(colon + 1);
00807 } else {
00808 mTransportInfo->host = serverport;
00809 mTransportInfo->port = "25";
00810 }
00811 } else
00812 if (transport.startsWith("smtps://"))
00813 {
00814 mTransportInfo->type = "smtps";
00815 mTransportInfo->auth = false;
00816 mTransportInfo->encryption = "ssl";
00817 QString serverport = transport.mid(7);
00818 int colon = serverport.find(':');
00819 if (colon != -1) {
00820 mTransportInfo->host = serverport.left(colon);
00821 mTransportInfo->port = serverport.mid(colon + 1);
00822 } else {
00823 mTransportInfo->host = serverport;
00824 mTransportInfo->port = "465";
00825 }
00826 }
00827 else if (transport.startsWith("file://"))
00828 {
00829 mTransportInfo->type = "sendmail";
00830 mTransportInfo->host = transport.mid(7);
00831 }
00832 }
00833
00834 while (mTransportInfo->host.endsWith("/")) {
00835 mTransportInfo->host.truncate(mTransportInfo->host.length()-1);
00836 }
00837
00838
00839 if (mTransportInfo->type == "sendmail")
00840 return new KMSendSendmail(this);
00841 if (mTransportInfo->type == "smtp" || mTransportInfo->type == "smtps")
00842 return new KMSendSMTP(this);
00843
00844 return 0L;
00845 }
00846
00847
00848 void KMSender::setStatusByLink(const KMMessage *aMsg)
00849 {
00850 int n = 0;
00851 while (1) {
00852 ulong msn;
00853 KMMsgStatus status;
00854 aMsg->getLink(n, &msn, &status);
00855 if (!msn || !status)
00856 break;
00857 n++;
00858
00859 KMFolder *folder = 0;
00860 int index = -1;
00861 KMMsgDict::instance()->getLocation(msn, &folder, &index);
00862 if (folder && index != -1) {
00863 KMFolderOpener openFolder(folder, "setstatus");
00864 if ( status == KMMsgStatusDeleted ) {
00865
00866 KMDeleteMsgCommand *cmd =
00867 new KMDeleteMsgCommand( folder, folder->getMsg( index ) );
00868 cmd->start();
00869 } else {
00870 folder->setStatus(index, status);
00871 }
00872 } else {
00873 kdWarning(5006) << k_funcinfo << "Cannot update linked message, it could not be found!" << endl;
00874 }
00875 }
00876 }
00877
00878
00879
00880 KMSendProc::KMSendProc( KMSender * sender )
00881 : QObject( 0 ),
00882 mSender( sender ),
00883 mLastErrorMessage(),
00884 mSendOk( false ),
00885 mSending( false )
00886 {
00887 }
00888
00889
00890 void KMSendProc::reset()
00891 {
00892 mSending = false;
00893 mSendOk = false;
00894 mLastErrorMessage = QString::null;
00895 }
00896
00897
00898 void KMSendProc::failed(const QString &aMsg)
00899 {
00900 mSending = false;
00901 mSendOk = false;
00902 mLastErrorMessage = aMsg;
00903 }
00904
00905
00906 void KMSendProc::statusMsg(const QString& aMsg)
00907 {
00908 if (mSender) mSender->setStatusMsg(aMsg);
00909 }
00910
00911
00912
00913 KMSendSendmail::KMSendSendmail( KMSender * sender )
00914 : KMSendProc( sender ),
00915 mMsgStr(),
00916 mMsgPos( 0 ),
00917 mMsgRest( 0 ),
00918 mMailerProc( 0 )
00919 {
00920
00921 }
00922
00923 KMSendSendmail::~KMSendSendmail() {
00924 delete mMailerProc; mMailerProc = 0;
00925 }
00926
00927 bool KMSendSendmail::doStart() {
00928
00929 if (mSender->transportInfo()->host.isEmpty())
00930 {
00931 const QString str = i18n("Please specify a mailer program in the settings.");
00932 const QString msg = i18n("Sending failed:\n%1\n"
00933 "The message will stay in the 'outbox' folder and will be resent.\n"
00934 "Please remove it from there if you do not want the message to "
00935 "be resent.\n"
00936 "The following transport protocol was used:\n %2")
00937 .arg(str + "\n")
00938 .arg("sendmail://");
00939 KMessageBox::information(0,msg);
00940 return false;
00941 }
00942
00943 if (!mMailerProc)
00944 {
00945 mMailerProc = new KProcess;
00946 assert(mMailerProc != 0);
00947 connect(mMailerProc,SIGNAL(processExited(KProcess*)),
00948 this, SLOT(sendmailExited(KProcess*)));
00949 connect(mMailerProc,SIGNAL(wroteStdin(KProcess*)),
00950 this, SLOT(wroteStdin(KProcess*)));
00951 connect(mMailerProc,SIGNAL(receivedStderr(KProcess*,char*,int)),
00952 this, SLOT(receivedStderr(KProcess*, char*, int)));
00953 }
00954 return true;
00955 }
00956
00957 void KMSendSendmail::doFinish() {
00958 delete mMailerProc;
00959 mMailerProc = 0;
00960 }
00961
00962 void KMSendSendmail::abort()
00963 {
00964 delete mMailerProc;
00965 mMailerProc = 0;
00966 mSendOk = false;
00967 mMsgStr = 0;
00968 idle();
00969 }
00970
00971 bool KMSendSendmail::doSend( const QString & sender, const QStringList & to, const QStringList & cc, const QStringList & bcc, const QByteArray & message ) {
00972 mMailerProc->clearArguments();
00973 *mMailerProc << mSender->transportInfo()->host
00974 << "-i" << "-f" << sender
00975 << to << cc << bcc ;
00976
00977 mMsgStr = message;
00978
00979 if ( !mMailerProc->start( KProcess::NotifyOnExit, KProcess::All ) ) {
00980 KMessageBox::information( 0, i18n("Failed to execute mailer program %1")
00981 .arg( mSender->transportInfo()->host ) );
00982 return false;
00983 }
00984 mMsgPos = mMsgStr.data();
00985 mMsgRest = mMsgStr.size();
00986 wroteStdin( mMailerProc );
00987
00988 return true;
00989 }
00990
00991
00992 void KMSendSendmail::wroteStdin(KProcess *proc)
00993 {
00994 char* str;
00995 int len;
00996
00997 assert(proc!=0);
00998 Q_UNUSED( proc );
00999
01000 str = mMsgPos;
01001 len = (mMsgRest>1024 ? 1024 : mMsgRest);
01002
01003 if (len <= 0)
01004 {
01005 mMailerProc->closeStdin();
01006 }
01007 else
01008 {
01009 mMsgRest -= len;
01010 mMsgPos += len;
01011 mMailerProc->writeStdin(str,len);
01012
01013
01014 }
01015 }
01016
01017
01018 void KMSendSendmail::receivedStderr(KProcess *proc, char *buffer, int buflen)
01019 {
01020 assert(proc!=0);
01021 Q_UNUSED( proc );
01022 mLastErrorMessage.replace(mLastErrorMessage.length(), buflen, buffer);
01023 }
01024
01025
01026 void KMSendSendmail::sendmailExited(KProcess *proc)
01027 {
01028 assert(proc!=0);
01029 mSendOk = (proc->normalExit() && proc->exitStatus()==0);
01030 if (!mSendOk) failed(i18n("Sendmail exited abnormally."));
01031 mMsgStr = 0;
01032 emit idle();
01033 }
01034
01035
01036
01037
01038
01039
01040 KMSendSMTP::KMSendSMTP(KMSender *sender)
01041 : KMSendProc(sender),
01042 mInProcess(false),
01043 mJob(0),
01044 mSlave(0)
01045 {
01046 KIO::Scheduler::connect(SIGNAL(slaveError(KIO::Slave *, int,
01047 const QString &)), this, SLOT(slaveError(KIO::Slave *, int,
01048 const QString &)));
01049 }
01050
01051 KMSendSMTP::~KMSendSMTP()
01052 {
01053 if (mJob) mJob->kill();
01054 }
01055
01056 bool KMSendSMTP::doSend( const QString & sender, const QStringList & to, const QStringList & cc, const QStringList & bcc, const QByteArray & message ) {
01057 QString query = "headers=0&from=";
01058 query += KURL::encode_string( sender );
01059
01060 QStringList::ConstIterator it;
01061
01062 for ( it = to.begin(); it != to.end(); ++it )
01063 query += "&to=" + KURL::encode_string(*it);
01064 for ( it = cc.begin(); it != cc.end(); ++it )
01065 query += "&cc=" + KURL::encode_string(*it);
01066 for ( it = bcc.begin(); it != bcc.end(); ++it )
01067 query += "&bcc=" + KURL::encode_string(*it);
01068
01069 KMTransportInfo * ti = mSender->transportInfo();
01070
01071 if ( ti->specifyHostname )
01072 query += "&hostname=" + KURL::encode_string( ti->localHostname );
01073
01074 if ( !kmkernel->msgSender()->sendQuotedPrintable() )
01075 query += "&body=8bit";
01076
01077 KURL destination;
01078
01079 destination.setProtocol((ti->encryption == "SSL") ? SMTPS_PROTOCOL : SMTP_PROTOCOL);
01080 destination.setHost(ti->host);
01081 destination.setPort(ti->port.toUShort());
01082
01083 if (ti->auth)
01084 {
01085 QMapIterator<QString,QString> tpc = mSender->mPasswdCache.find( ti->name );
01086 QString tpwd = ( tpc != mSender->mPasswdCache.end() )?(*tpc):QString::null;
01087
01088 if ( ti->passwd().isEmpty() )
01089 ti->setPasswd( tpwd );
01090
01091 if( (ti->user.isEmpty() || ti->passwd().isEmpty()) &&
01092 ti->authType != "GSSAPI" )
01093 {
01094 bool b = false;
01095 int result;
01096
01097 KCursorSaver idle(KBusyPtr::idle());
01098 QString passwd = ti->passwd();
01099 result = KIO::PasswordDialog::getNameAndPassword(ti->user, passwd,
01100 &b, i18n("You need to supply a username and a password to use this "
01101 "SMTP server."), false, QString::null, ti->name, QString::null);
01102
01103 if ( result != QDialog::Accepted )
01104 {
01105 abort();
01106 return false;
01107 }
01108 if (int id = KMTransportInfo::findTransport(ti->name)) {
01109 ti->setPasswd( passwd );
01110 ti->writeConfig(id);
01111
01112
01113 mSender->mPasswdCache[ti->name] = passwd;
01114 }
01115 }
01116 destination.setUser(ti->user);
01117 destination.setPass(ti->passwd());
01118 }
01119
01120 if (!mSlave || !mInProcess)
01121 {
01122 KIO::MetaData slaveConfig;
01123 slaveConfig.insert("tls", (ti->encryption == "TLS") ? "on" : "off");
01124 if (ti->auth) slaveConfig.insert("sasl", ti->authType);
01125 mSlave = KIO::Scheduler::getConnectedSlave(destination, slaveConfig);
01126 }
01127
01128 if (!mSlave)
01129 {
01130 abort();
01131 return false;
01132 }
01133
01134
01135 mMessage = message;
01136 mMessageLength = mMessage.size();
01137 mMessageOffset = 0;
01138
01139 if ( mMessageLength )
01140
01141
01142 query += "&size=" + QString::number( qRound( mMessageLength * 1.05 ) );
01143
01144 destination.setPath("/send");
01145 destination.setQuery( query );
01146
01147 mJob = KIO::put( destination, -1, false, false, false );
01148 if ( !mJob ) {
01149 abort();
01150 return false;
01151 }
01152 mJob->addMetaData( "lf2crlf+dotstuff", "slave" );
01153 KIO::Scheduler::assignJobToSlave(mSlave, mJob);
01154 connect(mJob, SIGNAL(result(KIO::Job *)), this, SLOT(result(KIO::Job *)));
01155 connect(mJob, SIGNAL(dataReq(KIO::Job *, QByteArray &)),
01156 this, SLOT(dataReq(KIO::Job *, QByteArray &)));
01157 mSendOk = true;
01158 mInProcess = true;
01159 return true;
01160 }
01161
01162 void KMSendSMTP::cleanup() {
01163 if(mJob)
01164 {
01165 mJob->kill(true);
01166 mJob = 0;
01167 mSlave = 0;
01168 }
01169
01170 if (mSlave)
01171 {
01172 KIO::Scheduler::disconnectSlave(mSlave);
01173 mSlave = 0;
01174 }
01175
01176 mInProcess = false;
01177 }
01178
01179 void KMSendSMTP::abort() {
01180 cleanup();
01181 emit idle();
01182 }
01183
01184 void KMSendSMTP::doFinish() {
01185 cleanup();
01186 }
01187
01188 void KMSendSMTP::dataReq(KIO::Job *, QByteArray &array)
01189 {
01190
01191 const int chunkSize = QMIN( mMessageLength - mMessageOffset, 32*1024 );
01192 if ( chunkSize > 0 ) {
01193 array.duplicate(mMessage.data() + mMessageOffset, chunkSize);
01194 mMessageOffset += chunkSize;
01195 } else
01196 {
01197 array.resize(0);
01198 mMessage.resize(0);
01199 }
01200 mSender->emitProgressInfo( mMessageOffset );
01201 }
01202
01203 void KMSendSMTP::result(KIO::Job *_job)
01204 {
01205 if (!mJob) return;
01206 mJob = 0;
01207
01208 if(_job->error())
01209 {
01210 mSendOk = false;
01211 if (_job->error() == KIO::ERR_SLAVE_DIED) mSlave = 0;
01212 failed(_job->errorString());
01213 abort();
01214 } else {
01215 emit idle();
01216 }
01217 }
01218
01219 void KMSendSMTP::slaveError(KIO::Slave *aSlave, int error, const QString &errorMsg)
01220 {
01221 if (aSlave == mSlave)
01222 {
01223 if (error == KIO::ERR_SLAVE_DIED) mSlave = 0;
01224 mSendOk = false;
01225 mJob = 0;
01226 failed(KIO::buildErrorString(error, errorMsg));
01227 abort();
01228 }
01229 }
01230
01231 #include "kmsender.moc"
01232 #include "kmsender_p.moc"