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
00321 const KPIM::Identity & id = kmkernel->identityManager()
00322 ->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
00323 if ( !mCurrentMsg->fcc().isEmpty() )
00324 {
00325 sentFolder = kmkernel->folderMgr()->findIdString( mCurrentMsg->fcc() );
00326 if ( sentFolder == 0 )
00327
00328 sentFolder =
00329 kmkernel->dimapFolderMgr()->findIdString( mCurrentMsg->fcc() );
00330 if ( sentFolder == 0 )
00331 imapSentFolder =
00332 kmkernel->imapFolderMgr()->findIdString( mCurrentMsg->fcc() );
00333 }
00334
00335
00336 if ( ( sentFolder == 0 || sentFolder->isReadOnly() )
00337 && ( imapSentFolder == 0 || imapSentFolder->isReadOnly() )
00338 && !id.fcc().isEmpty() )
00339 {
00340 sentFolder = kmkernel->folderMgr()->findIdString( id.fcc() );
00341 if ( sentFolder == 0 )
00342
00343 sentFolder = kmkernel->dimapFolderMgr()->findIdString( id.fcc() );
00344 if ( sentFolder == 0 )
00345 imapSentFolder = kmkernel->imapFolderMgr()->findIdString( id.fcc() );
00346 }
00347 if (imapSentFolder
00348 && ( imapSentFolder->noContent() || imapSentFolder->isReadOnly() ) )
00349 imapSentFolder = 0;
00350
00351 if ( sentFolder == 0 || sentFolder->isReadOnly() )
00352 sentFolder = kmkernel->sentFolder();
00353
00354 if ( sentFolder ) {
00355 if ( const int err = sentFolder->open("sentFolder") ) {
00356 Q_UNUSED( err );
00357 cleanup();
00358 return;
00359 }
00360 }
00361
00362
00363
00364
00365 if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( true );
00366 const int processResult = kmkernel->filterMgr()->process(mCurrentMsg,KMFilterMgr::Outbound);
00367 if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( false );
00368
00369
00370 switch (processResult) {
00371 case 2:
00372 perror("Critical error: Unable to process sent mail (out of space?)");
00373 KMessageBox::information(0, i18n("Critical error: "
00374 "Unable to process sent mail (out of space?)"
00375 "Moving failing message to \"sent-mail\" folder."));
00376 if ( sentFolder ) {
00377 sentFolder->moveMsg(mCurrentMsg);
00378 sentFolder->close("sentFolder");
00379 }
00380 cleanup();
00381 return;
00382 case 1:
00383 if ( sentFolder && sentFolder->moveMsg(mCurrentMsg) != 0 )
00384 {
00385 KMessageBox::error(0, i18n("Moving the sent message \"%1\" from the "
00386 "\"outbox\" to the \"sent-mail\" folder failed.\n"
00387 "Possible reasons are lack of disk space or write permission. "
00388 "Please try to fix the problem and move the message manually.")
00389 .arg(mCurrentMsg->subject()));
00390 cleanup();
00391 return;
00392 }
00393 if (imapSentFolder) {
00394
00395 KMCommand *command = new KMMoveCommand( imapSentFolder, mCurrentMsg );
00396 command->keepFolderOpen( sentFolder );
00397 command->start();
00398 }
00399 default:
00400 break;
00401 }
00402 setStatusByLink( mCurrentMsg );
00403 if (mCurrentMsg->parent() && !imapSentFolder) {
00404
00405
00406 assert( mCurrentMsg->parent()->find( mCurrentMsg )
00407 == mCurrentMsg->parent()->count() - 1 );
00408
00409 mCurrentMsg->parent()->unGetMsg( mCurrentMsg->parent()->count() -1 );
00410 }
00411
00412 mCurrentMsg = 0;
00413 }
00414
00415
00416 mCurrentMsg = mOutboxFolder->getMsg(mFailedMessages);
00417 if ( mCurrentMsg && !mCurrentMsg->transferInProgress() &&
00418 mCurrentMsg->sender().isEmpty() ) {
00419
00420
00421
00422 const KPIM::Identity & id = kmkernel->identityManager()
00423 ->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
00424 if ( !id.emailAddr().isEmpty() ) {
00425 mCurrentMsg->setFrom( id.fullEmailAddr() );
00426 }
00427 else if ( !kmkernel->identityManager()->defaultIdentity().emailAddr().isEmpty() ) {
00428 mCurrentMsg->setFrom( kmkernel->identityManager()->defaultIdentity().fullEmailAddr() );
00429 }
00430 else {
00431 KMessageBox::sorry( 0, i18n( "It's not possible to send messages "
00432 "without specifying a sender address.\n"
00433 "Please set the email address of "
00434 "identity '%1' in the Identities "
00435 "section of the configuration dialog "
00436 "and then try again." )
00437 .arg( id.identityName() ) );
00438 mOutboxFolder->unGetMsg( mFailedMessages );
00439 mCurrentMsg = 0;
00440 }
00441 }
00442 if (!mCurrentMsg || mCurrentMsg->transferInProgress())
00443 {
00444
00445 if (mCurrentMsg && mCurrentMsg->transferInProgress())
00446 mCurrentMsg = 0;
00447
00448 if ( sentFolder != 0 )
00449 sentFolder->close("sentFolder");
00450 if ( someSent ) {
00451 if ( mSentMessages == mTotalMessages ) {
00452 setStatusMsg(i18n("%n queued message successfully sent.",
00453 "%n queued messages successfully sent.",
00454 mSentMessages));
00455 } else {
00456 setStatusMsg(i18n("%1 of %2 queued messages successfully sent.")
00457 .arg(mSentMessages).arg( mTotalMessages ));
00458 }
00459 }
00460 cleanup();
00461 return;
00462 }
00463 mCurrentMsg->setTransferInProgress( true );
00464
00465
00466 if (!mSendInProgress)
00467 {
00468 Q_ASSERT( !mProgressItem );
00469 mProgressItem = KPIM::ProgressManager::createProgressItem(
00470 "Sender",
00471 i18n( "Sending messages" ),
00472 i18n("Initiating sender process..."),
00473 true );
00474 connect( mProgressItem, SIGNAL(progressItemCanceled(KPIM::ProgressItem*)),
00475 this, SLOT( slotAbortSend() ) );
00476 kapp->ref();
00477 mSendInProgress = true;
00478 }
00479
00480 QString msgTransport = mCustomTransport;
00481 if ( msgTransport.isEmpty() ) {
00482 msgTransport = mCurrentMsg->headerField("X-KMail-Transport");
00483 }
00484 if ( msgTransport.isEmpty() ) {
00485 const QStringList sl = KMTransportInfo::availableTransports();
00486 if (!sl.empty()) msgTransport = sl.front();
00487 }
00488
00489 if (!mSendProc || msgTransport != mMethodStr) {
00490 if (mSendProcStarted && mSendProc) {
00491 mSendProc->finish();
00492 mSendProcStarted = false;
00493 }
00494
00495 mSendProc = createSendProcFromString(msgTransport);
00496 mMethodStr = msgTransport;
00497
00498 if( mTransportInfo->encryption == "TLS" || mTransportInfo->encryption == "SSL" ) {
00499 mProgressItem->setUsesCrypto( true );
00500 } else if ( !mCustomTransport.isEmpty() ) {
00501 int result = KMessageBox::warningContinueCancel( 0,
00502 i18n( "You have chosen to send all queued email using an unencrypted transport, do you want to continue? "),
00503 i18n( "Security Warning" ),
00504 i18n( "Send Unencrypted" ),
00505 "useCustomTransportWithoutAsking", false);
00506
00507 if( result == KMessageBox::Cancel ) {
00508 mProgressItem->cancel();
00509 mProgressItem->setComplete();
00510 slotAbortSend();
00511 cleanup();
00512 return;
00513 }
00514 }
00515
00516 if (!mSendProc)
00517 sendProcStarted(false);
00518 else {
00519 connect(mSendProc, SIGNAL(idle()), SLOT(slotIdle()));
00520 connect(mSendProc, SIGNAL(started(bool)), SLOT(sendProcStarted(bool)));
00521
00522
00523 if ( !mTransportInfo->precommand.isEmpty() ) {
00524 runPrecommand( mTransportInfo->precommand );
00525 return;
00526 }
00527
00528 mSendProc->start();
00529 }
00530 }
00531 else if (!mSendProcStarted)
00532 mSendProc->start();
00533 else
00534 doSendMsgAux();
00535 }
00536
00537 bool KMSender::runPrecommand( const QString & cmd ) {
00538 setStatusMsg( i18n("Executing precommand %1").arg( cmd ) );
00539 mPrecommand = new KMPrecommand( cmd );
00540 connect( mPrecommand, SIGNAL(finished(bool)),
00541 SLOT(slotPrecommandFinished(bool)) );
00542 if ( !mPrecommand->start() ) {
00543 delete mPrecommand; mPrecommand = 0;
00544 return false;
00545 }
00546 return true;
00547 }
00548
00549
00550 void KMSender::sendProcStarted(bool success)
00551 {
00552 if (!success) {
00553 if (mSendProc)
00554 mSendProc->finish();
00555 else
00556 setStatusMsg(i18n("Unrecognized transport protocol. Unable to send message."));
00557 mSendProc = 0;
00558 mSendProcStarted = false;
00559 cleanup();
00560 return;
00561 }
00562 doSendMsgAux();
00563 }
00564
00565
00566 static QStringList addrSpecListToStringList( const AddrSpecList & l, bool allowEmpty=false ) {
00567 QStringList result;
00568 for ( AddrSpecList::const_iterator it = l.begin(), end = l.end() ; it != end ; ++it ) {
00569 const QString s = (*it).asString();
00570 if ( allowEmpty || !s.isEmpty() )
00571 result.push_back( s );
00572 }
00573 return result;
00574 }
00575
00576 static void extractSenderToCCAndBcc( KMMessage * aMsg, QString * sender, QStringList * to, QStringList * cc, QStringList * bcc ) {
00577 if ( sender ) *sender = aMsg->sender();
00578 if( !aMsg->headerField("X-KMail-Recipients").isEmpty() ) {
00579
00580
00581 if ( to ) *to = addrSpecListToStringList( aMsg->extractAddrSpecs( "X-KMail-Recipients" ) );
00582 aMsg->removeHeaderField( "X-KMail-Recipients" );
00583 } else {
00584 if ( to ) *to = addrSpecListToStringList( aMsg->extractAddrSpecs( "To" ) );
00585 if ( cc ) *cc = addrSpecListToStringList( aMsg->extractAddrSpecs( "Cc" ) );
00586 if ( bcc ) *bcc = addrSpecListToStringList( aMsg->extractAddrSpecs( "Bcc" ) );
00587 }
00588 }
00589
00590
00591 void KMSender::doSendMsgAux()
00592 {
00593 mSendProcStarted = true;
00594
00595
00596
00597 setStatusMsg(i18n("%3: subject of message","Sending message %1 of %2: %3")
00598 .arg(mSentMessages+mFailedMessages+1).arg(mTotalMessages)
00599 .arg(mCurrentMsg->subject()));
00600 QStringList to, cc, bcc;
00601 QString sender;
00602 extractSenderToCCAndBcc( mCurrentMsg, &sender, &to, &cc, &bcc );
00603
00604
00605 if ( messageIsDispositionNotificationReport( mCurrentMsg ) && GlobalSettings::self()->sendMDNsWithEmptySender() )
00606 sender = "<>";
00607
00608 const QByteArray message = mCurrentMsg->asSendableString();
00609 if ( sender.isEmpty() || !mSendProc->send( sender, to, cc, bcc, message ) ) {
00610 if ( mCurrentMsg )
00611 mCurrentMsg->setTransferInProgress( false );
00612 if ( mOutboxFolder )
00613 mOutboxFolder->unGetMsg( mFailedMessages );
00614 mCurrentMsg = 0;
00615 cleanup();
00616 setStatusMsg(i18n("Failed to send (some) queued messages."));
00617 return;
00618 }
00619
00620
00621 }
00622
00623
00624
00625 void KMSender::cleanup(void)
00626 {
00627 kdDebug(5006) << k_funcinfo << endl;
00628 if (mSendProc && mSendProcStarted) mSendProc->finish();
00629 mSendProc = 0;
00630 mSendProcStarted = false;
00631 if (mSendInProgress) kapp->deref();
00632 mSendInProgress = false;
00633 if (mCurrentMsg)
00634 {
00635 mCurrentMsg->setTransferInProgress( false );
00636 mCurrentMsg = 0;
00637 }
00638 if ( mSentFolder ) {
00639 mSentFolder->close("dosendsent");
00640 mSentFolder = 0;
00641 }
00642 if ( mOutboxFolder ) {
00643 disconnect( mOutboxFolder, SIGNAL(msgAdded(int)),
00644 this, SLOT(outboxMsgAdded(int)) );
00645 mOutboxFolder->close("dosendoutbox");
00646 if ( mOutboxFolder->count( true ) == 0 ) {
00647 mOutboxFolder->expunge();
00648 }
00649 else if ( mOutboxFolder->needsCompacting() ) {
00650 mOutboxFolder->compact( KMFolder::CompactSilentlyNow );
00651 }
00652 mOutboxFolder = 0;
00653 }
00654
00655 mSendAborted = false;
00656 mSentMessages = 0;
00657 mFailedMessages = 0;
00658 mSentBytes = 0;
00659 if ( mProgressItem )
00660 mProgressItem->setComplete();
00661 mProgressItem = 0;
00662 kmkernel->filterMgr()->deref();
00663 }
00664
00665
00666
00667 void KMSender::slotAbortSend()
00668 {
00669 mSendAborted = true;
00670 delete mPrecommand;
00671 mPrecommand = 0;
00672 if (mSendProc) mSendProc->abort();
00673 }
00674
00675
00676 void KMSender::slotIdle()
00677 {
00678 assert(mSendProc != 0);
00679
00680 QString msg;
00681 QString errString;
00682 if (mSendProc)
00683 errString = mSendProc->lastErrorMessage();
00684
00685 if (mSendAborted) {
00686
00687 if ( mCurrentMsg ) {
00688 mCurrentMsg->setTransferInProgress( false );
00689 if ( mOutboxFolder )
00690 mOutboxFolder->unGetMsg( mFailedMessages );
00691 mCurrentMsg = 0;
00692 }
00693 msg = i18n("Sending aborted:\n%1\n"
00694 "The message will stay in the 'outbox' folder until you either "
00695 "fix the problem (e.g. a broken address) or remove the message "
00696 "from the 'outbox' folder.\n"
00697 "The following transport protocol was used:\n %2")
00698 .arg(errString)
00699 .arg(mMethodStr);
00700 if (!errString.isEmpty()) KMessageBox::error(0,msg);
00701 setStatusMsg( i18n( "Sending aborted." ) );
00702 } else {
00703 if (!mSendProc->sendOk()) {
00704 if ( mCurrentMsg )
00705 mCurrentMsg->setTransferInProgress( false );
00706 if ( mOutboxFolder )
00707 mOutboxFolder->unGetMsg( mFailedMessages );
00708 mCurrentMsg = 0;
00709 mFailedMessages++;
00710
00711 QMapIterator <QString,QString> pc;
00712 if ( (pc = mPasswdCache.find( mMethodStr )) != mPasswdCache.end() ) {
00713 mPasswdCache.erase(pc);
00714 }
00715
00716 if (!errString.isEmpty()) {
00717 int res = KMessageBox::Yes;
00718 if (mSentMessages+mFailedMessages != mTotalMessages) {
00719 msg = i18n("<p>Sending failed:</p>"
00720 "<p>%1</p>"
00721 "<p>The message will stay in the 'outbox' folder until you either "
00722 "fix the problem (e.g. a broken address) or remove the message "
00723 "from the 'outbox' folder.</p>"
00724 "<p>The following transport protocol was used: %2</p>"
00725 "<p>Do you want me to continue sending the remaining messages?</p>")
00726 .arg(errString)
00727 .arg(mMethodStr);
00728 res = KMessageBox::warningYesNo( 0 , msg ,
00729 i18n( "Continue Sending" ), i18n( "&Continue Sending" ),
00730 i18n("&Abort Sending") );
00731 } else {
00732 msg = i18n("Sending failed:\n%1\n"
00733 "The message will stay in the 'outbox' folder until you either "
00734 "fix the problem (e.g. a broken address) or remove the message "
00735 "from the 'outbox' folder.\n"
00736 "The following transport protocol was used:\n %2")
00737 .arg(errString)
00738 .arg(mMethodStr);
00739 KMessageBox::error(0,msg);
00740 }
00741 if (res == KMessageBox::Yes) {
00742
00743 doSendMsg();
00744 return;
00745 } else {
00746 setStatusMsg( i18n( "Sending aborted." ) );
00747 }
00748 }
00749 } else {
00750
00751 doSendMsg();
00752 return;
00753 }
00754 }
00755 mSendProc->finish();
00756 mSendProc = 0;
00757 mSendProcStarted = false;
00758
00759 cleanup();
00760 }
00761
00762
00763
00764 void KMSender::slotPrecommandFinished(bool normalExit)
00765 {
00766 delete mPrecommand;
00767 mPrecommand = 0;
00768 if (normalExit) mSendProc->start();
00769 else slotIdle();
00770 }
00771
00772
00773
00774 void KMSender::setSendImmediate(bool aSendImmediate)
00775 {
00776 mSendImmediate = aSendImmediate;
00777 }
00778
00779
00780
00781 void KMSender::setSendQuotedPrintable(bool aSendQuotedPrintable)
00782 {
00783 mSendQuotedPrintable = aSendQuotedPrintable;
00784 }
00785
00786
00787
00788 KMSendProc* KMSender::createSendProcFromString( const QString & transport )
00789 {
00790 mTransportInfo->type = QString::null;
00791 int nr = KMTransportInfo::findTransport(transport);
00792 if (nr)
00793 {
00794 mTransportInfo->readConfig(nr);
00795 } else {
00796 if (transport.startsWith("smtp://"))
00797 {
00798 mTransportInfo->type = "smtp";
00799 mTransportInfo->auth = false;
00800 mTransportInfo->encryption = "NONE";
00801 QString serverport = transport.mid(7);
00802 int colon = serverport.find(':');
00803 if (colon != -1) {
00804 mTransportInfo->host = serverport.left(colon);
00805 mTransportInfo->port = serverport.mid(colon + 1);
00806 } else {
00807 mTransportInfo->host = serverport;
00808 mTransportInfo->port = "25";
00809 }
00810 } else
00811 if (transport.startsWith("smtps://"))
00812 {
00813 mTransportInfo->type = "smtps";
00814 mTransportInfo->auth = false;
00815 mTransportInfo->encryption = "ssl";
00816 QString serverport = transport.mid(7);
00817 int colon = serverport.find(':');
00818 if (colon != -1) {
00819 mTransportInfo->host = serverport.left(colon);
00820 mTransportInfo->port = serverport.mid(colon + 1);
00821 } else {
00822 mTransportInfo->host = serverport;
00823 mTransportInfo->port = "465";
00824 }
00825 }
00826 else if (transport.startsWith("file://"))
00827 {
00828 mTransportInfo->type = "sendmail";
00829 mTransportInfo->host = transport.mid(7);
00830 }
00831 }
00832
00833 while (mTransportInfo->host.endsWith("/")) {
00834 mTransportInfo->host.truncate(mTransportInfo->host.length()-1);
00835 }
00836
00837
00838 if (mTransportInfo->type == "sendmail")
00839 return new KMSendSendmail(this);
00840 if (mTransportInfo->type == "smtp" || mTransportInfo->type == "smtps")
00841 return new KMSendSMTP(this);
00842
00843 return 0L;
00844 }
00845
00846
00847 void KMSender::setStatusByLink(const KMMessage *aMsg)
00848 {
00849 int n = 0;
00850 while (1) {
00851 ulong msn;
00852 KMMsgStatus status;
00853 aMsg->getLink(n, &msn, &status);
00854 if (!msn || !status)
00855 break;
00856 n++;
00857
00858 KMFolder *folder = 0;
00859 int index = -1;
00860 KMMsgDict::instance()->getLocation(msn, &folder, &index);
00861 if (folder && index != -1) {
00862 KMFolderOpener openFolder(folder, "setstatus");
00863 if ( status == KMMsgStatusDeleted ) {
00864
00865 KMDeleteMsgCommand *cmd =
00866 new KMDeleteMsgCommand( folder, folder->getMsg( index ) );
00867 cmd->start();
00868 } else {
00869 folder->setStatus(index, status);
00870 }
00871 } else {
00872 kdWarning(5006) << k_funcinfo << "Cannot update linked message, it could not be found!" << endl;
00873 }
00874 }
00875 }
00876
00877
00878
00879 KMSendProc::KMSendProc( KMSender * sender )
00880 : QObject( 0 ),
00881 mSender( sender ),
00882 mLastErrorMessage(),
00883 mSendOk( false ),
00884 mSending( false )
00885 {
00886 }
00887
00888
00889 void KMSendProc::reset()
00890 {
00891 mSending = false;
00892 mSendOk = false;
00893 mLastErrorMessage = QString::null;
00894 }
00895
00896
00897 void KMSendProc::failed(const QString &aMsg)
00898 {
00899 mSending = false;
00900 mSendOk = false;
00901 mLastErrorMessage = aMsg;
00902 }
00903
00904
00905 void KMSendProc::statusMsg(const QString& aMsg)
00906 {
00907 if (mSender) mSender->setStatusMsg(aMsg);
00908 }
00909
00910
00911
00912 KMSendSendmail::KMSendSendmail( KMSender * sender )
00913 : KMSendProc( sender ),
00914 mMsgStr(),
00915 mMsgPos( 0 ),
00916 mMsgRest( 0 ),
00917 mMailerProc( 0 )
00918 {
00919
00920 }
00921
00922 KMSendSendmail::~KMSendSendmail() {
00923 delete mMailerProc; mMailerProc = 0;
00924 }
00925
00926 bool KMSendSendmail::doStart() {
00927
00928 if (mSender->transportInfo()->host.isEmpty())
00929 {
00930 const QString str = i18n("Please specify a mailer program in the settings.");
00931 const QString msg = i18n("Sending failed:\n%1\n"
00932 "The message will stay in the 'outbox' folder and will be resent.\n"
00933 "Please remove it from there if you do not want the message to "
00934 "be resent.\n"
00935 "The following transport protocol was used:\n %2")
00936 .arg(str + "\n")
00937 .arg("sendmail://");
00938 KMessageBox::information(0,msg);
00939 return false;
00940 }
00941
00942 if (!mMailerProc)
00943 {
00944 mMailerProc = new KProcess;
00945 assert(mMailerProc != 0);
00946 connect(mMailerProc,SIGNAL(processExited(KProcess*)),
00947 this, SLOT(sendmailExited(KProcess*)));
00948 connect(mMailerProc,SIGNAL(wroteStdin(KProcess*)),
00949 this, SLOT(wroteStdin(KProcess*)));
00950 connect(mMailerProc,SIGNAL(receivedStderr(KProcess*,char*,int)),
00951 this, SLOT(receivedStderr(KProcess*, char*, int)));
00952 }
00953 return true;
00954 }
00955
00956 void KMSendSendmail::doFinish() {
00957 delete mMailerProc;
00958 mMailerProc = 0;
00959 }
00960
00961 void KMSendSendmail::abort()
00962 {
00963 delete mMailerProc;
00964 mMailerProc = 0;
00965 mSendOk = false;
00966 mMsgStr = 0;
00967 idle();
00968 }
00969
00970 bool KMSendSendmail::doSend( const QString & sender, const QStringList & to, const QStringList & cc, const QStringList & bcc, const QByteArray & message ) {
00971 mMailerProc->clearArguments();
00972 *mMailerProc << mSender->transportInfo()->host
00973 << "-i" << "-f" << sender
00974 << to << cc << bcc ;
00975
00976 mMsgStr = message;
00977
00978 if ( !mMailerProc->start( KProcess::NotifyOnExit, KProcess::All ) ) {
00979 KMessageBox::information( 0, i18n("Failed to execute mailer program %1")
00980 .arg( mSender->transportInfo()->host ) );
00981 return false;
00982 }
00983 mMsgPos = mMsgStr.data();
00984 mMsgRest = mMsgStr.size();
00985 wroteStdin( mMailerProc );
00986
00987 return true;
00988 }
00989
00990
00991 void KMSendSendmail::wroteStdin(KProcess *proc)
00992 {
00993 char* str;
00994 int len;
00995
00996 assert(proc!=0);
00997 Q_UNUSED( proc );
00998
00999 str = mMsgPos;
01000 len = (mMsgRest>1024 ? 1024 : mMsgRest);
01001
01002 if (len <= 0)
01003 {
01004 mMailerProc->closeStdin();
01005 }
01006 else
01007 {
01008 mMsgRest -= len;
01009 mMsgPos += len;
01010 mMailerProc->writeStdin(str,len);
01011
01012
01013 }
01014 }
01015
01016
01017 void KMSendSendmail::receivedStderr(KProcess *proc, char *buffer, int buflen)
01018 {
01019 assert(proc!=0);
01020 Q_UNUSED( proc );
01021 mLastErrorMessage.replace(mLastErrorMessage.length(), buflen, buffer);
01022 }
01023
01024
01025 void KMSendSendmail::sendmailExited(KProcess *proc)
01026 {
01027 assert(proc!=0);
01028 mSendOk = (proc->normalExit() && proc->exitStatus()==0);
01029 if (!mSendOk) failed(i18n("Sendmail exited abnormally."));
01030 mMsgStr = 0;
01031 emit idle();
01032 }
01033
01034
01035
01036
01037
01038
01039 KMSendSMTP::KMSendSMTP(KMSender *sender)
01040 : KMSendProc(sender),
01041 mInProcess(false),
01042 mJob(0),
01043 mSlave(0)
01044 {
01045 KIO::Scheduler::connect(SIGNAL(slaveError(KIO::Slave *, int,
01046 const QString &)), this, SLOT(slaveError(KIO::Slave *, int,
01047 const QString &)));
01048 }
01049
01050 KMSendSMTP::~KMSendSMTP()
01051 {
01052 if (mJob) mJob->kill();
01053 }
01054
01055 bool KMSendSMTP::doSend( const QString & sender, const QStringList & to, const QStringList & cc, const QStringList & bcc, const QByteArray & message ) {
01056 QString query = "headers=0&from=";
01057 query += KURL::encode_string( sender );
01058
01059 QStringList::ConstIterator it;
01060
01061 for ( it = to.begin(); it != to.end(); ++it )
01062 query += "&to=" + KURL::encode_string(*it);
01063 for ( it = cc.begin(); it != cc.end(); ++it )
01064 query += "&cc=" + KURL::encode_string(*it);
01065 for ( it = bcc.begin(); it != bcc.end(); ++it )
01066 query += "&bcc=" + KURL::encode_string(*it);
01067
01068 KMTransportInfo * ti = mSender->transportInfo();
01069
01070 if ( ti->specifyHostname )
01071 query += "&hostname=" + KURL::encode_string( ti->localHostname );
01072
01073 if ( !kmkernel->msgSender()->sendQuotedPrintable() )
01074 query += "&body=8bit";
01075
01076 KURL destination;
01077
01078 destination.setProtocol((ti->encryption == "SSL") ? SMTPS_PROTOCOL : SMTP_PROTOCOL);
01079 destination.setHost(ti->host);
01080 destination.setPort(ti->port.toUShort());
01081
01082 if (ti->auth)
01083 {
01084 QMapIterator<QString,QString> tpc = mSender->mPasswdCache.find( ti->name );
01085 QString tpwd = ( tpc != mSender->mPasswdCache.end() )?(*tpc):QString::null;
01086
01087 if ( ti->passwd().isEmpty() )
01088 ti->setPasswd( tpwd );
01089
01090 if( (ti->user.isEmpty() || ti->passwd().isEmpty()) &&
01091 ti->authType != "GSSAPI" )
01092 {
01093 bool b = false;
01094 int result;
01095
01096 KCursorSaver idle(KBusyPtr::idle());
01097 QString passwd = ti->passwd();
01098 result = KIO::PasswordDialog::getNameAndPassword(ti->user, passwd,
01099 &b, i18n("You need to supply a username and a password to use this "
01100 "SMTP server."), false, QString::null, ti->name, QString::null);
01101
01102 if ( result != QDialog::Accepted )
01103 {
01104 abort();
01105 return false;
01106 }
01107 if (int id = KMTransportInfo::findTransport(ti->name)) {
01108 ti->setPasswd( passwd );
01109 ti->writeConfig(id);
01110
01111
01112 mSender->mPasswdCache[ti->name] = passwd;
01113 }
01114 }
01115 destination.setUser(ti->user);
01116 destination.setPass(ti->passwd());
01117 }
01118
01119 if (!mSlave || !mInProcess)
01120 {
01121 KIO::MetaData slaveConfig;
01122 slaveConfig.insert("tls", (ti->encryption == "TLS") ? "on" : "off");
01123 if (ti->auth) slaveConfig.insert("sasl", ti->authType);
01124 mSlave = KIO::Scheduler::getConnectedSlave(destination, slaveConfig);
01125 }
01126
01127 if (!mSlave)
01128 {
01129 abort();
01130 return false;
01131 }
01132
01133
01134 mMessage = message;
01135 mMessageLength = mMessage.size();
01136 mMessageOffset = 0;
01137
01138 if ( mMessageLength )
01139
01140
01141 query += "&size=" + QString::number( qRound( mMessageLength * 1.05 ) );
01142
01143 destination.setPath("/send");
01144 destination.setQuery( query );
01145
01146 mJob = KIO::put( destination, -1, false, false, false );
01147 if ( !mJob ) {
01148 abort();
01149 return false;
01150 }
01151 mJob->addMetaData( "lf2crlf+dotstuff", "slave" );
01152 KIO::Scheduler::assignJobToSlave(mSlave, mJob);
01153 connect(mJob, SIGNAL(result(KIO::Job *)), this, SLOT(result(KIO::Job *)));
01154 connect(mJob, SIGNAL(dataReq(KIO::Job *, QByteArray &)),
01155 this, SLOT(dataReq(KIO::Job *, QByteArray &)));
01156 mSendOk = true;
01157 mInProcess = true;
01158 return true;
01159 }
01160
01161 void KMSendSMTP::cleanup() {
01162 if(mJob)
01163 {
01164 mJob->kill(true);
01165 mJob = 0;
01166 mSlave = 0;
01167 }
01168
01169 if (mSlave)
01170 {
01171 KIO::Scheduler::disconnectSlave(mSlave);
01172 mSlave = 0;
01173 }
01174
01175 mInProcess = false;
01176 }
01177
01178 void KMSendSMTP::abort() {
01179 cleanup();
01180 emit idle();
01181 }
01182
01183 void KMSendSMTP::doFinish() {
01184 cleanup();
01185 }
01186
01187 void KMSendSMTP::dataReq(KIO::Job *, QByteArray &array)
01188 {
01189
01190 const int chunkSize = QMIN( mMessageLength - mMessageOffset, 32*1024 );
01191 if ( chunkSize > 0 ) {
01192 array.duplicate(mMessage.data() + mMessageOffset, chunkSize);
01193 mMessageOffset += chunkSize;
01194 } else
01195 {
01196 array.resize(0);
01197 mMessage.resize(0);
01198 }
01199 mSender->emitProgressInfo( mMessageOffset );
01200 }
01201
01202 void KMSendSMTP::result(KIO::Job *_job)
01203 {
01204 if (!mJob) return;
01205 mJob = 0;
01206
01207 if(_job->error())
01208 {
01209 mSendOk = false;
01210 if (_job->error() == KIO::ERR_SLAVE_DIED) mSlave = 0;
01211 failed(_job->errorString());
01212 abort();
01213 } else {
01214 emit idle();
01215 }
01216 }
01217
01218 void KMSendSMTP::slaveError(KIO::Slave *aSlave, int error, const QString &errorMsg)
01219 {
01220 if (aSlave == mSlave)
01221 {
01222 if (error == KIO::ERR_SLAVE_DIED) mSlave = 0;
01223 mSendOk = false;
01224 mJob = 0;
01225 failed(KIO::buildErrorString(error, errorMsg));
01226 abort();
01227 }
01228 }
01229
01230 #include "kmsender.moc"
01231 #include "kmsender_p.moc"