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 kmkernel->outboxFolder()->open("outbox");
00165 const KMFolderCloser openOutbox( "outbox", kmkernel->outboxFolder() );
00166
00167 aMsg->setStatus(KMMsgStatusQueued);
00168
00169 if ( const int err = openOutbox.folder()->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 = openOutbox.folder()->count() - 1;
00187 KMMessage * const unencryptedMsg = aMsg->unencryptedMsg();
00188 openOutbox.folder()->unGetMsg( idx );
00189 KMMessage * const tempMsg = openOutbox.folder()->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 folder->open("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 folder->close("setstatus");
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"