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