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