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();
00165 const KMFolderCloser openOutbox( 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();
00223 mTotalMessages = mOutboxFolder->count();
00224 if (mTotalMessages == 0) {
00225
00226 mOutboxFolder->close();
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();
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() ) {
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();
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();
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();
00640 mSentFolder = 0;
00641 }
00642 if ( mOutboxFolder ) {
00643 disconnect( mOutboxFolder, SIGNAL(msgAdded(int)),
00644 this, SLOT(outboxMsgAdded(int)) );
00645 mOutboxFolder->close();
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 if (!errString.isEmpty()) {
00712 int res = KMessageBox::Yes;
00713 if (mSentMessages+mFailedMessages != mTotalMessages) {
00714 msg = i18n("<p>Sending failed:</p>"
00715 "<p>%1</p>"
00716 "<p>The message will stay in the 'outbox' folder until you either "
00717 "fix the problem (e.g. a broken address) or remove the message "
00718 "from the 'outbox' folder.</p>"
00719 "<p>The following transport protocol was used: %2</p>"
00720 "<p>Do you want me to continue sending the remaining messages?</p>")
00721 .arg(errString)
00722 .arg(mMethodStr);
00723 res = KMessageBox::warningYesNo( 0 , msg ,
00724 i18n( "Continue Sending" ), i18n( "&Continue Sending" ),
00725 i18n("&Abort Sending") );
00726 } else {
00727 msg = i18n("Sending failed:\n%1\n"
00728 "The message will stay in the 'outbox' folder until you either "
00729 "fix the problem (e.g. a broken address) or remove the message "
00730 "from the 'outbox' folder.\n"
00731 "The following transport protocol was used:\n %2")
00732 .arg(errString)
00733 .arg(mMethodStr);
00734 KMessageBox::error(0,msg);
00735 }
00736 if (res == KMessageBox::Yes) {
00737
00738 doSendMsg();
00739 return;
00740 } else {
00741 setStatusMsg( i18n( "Sending aborted." ) );
00742 }
00743 }
00744 } else {
00745
00746 doSendMsg();
00747 return;
00748 }
00749 }
00750 mSendProc->finish();
00751 mSendProc = 0;
00752 mSendProcStarted = false;
00753
00754 cleanup();
00755 }
00756
00757
00758
00759 void KMSender::slotPrecommandFinished(bool normalExit)
00760 {
00761 delete mPrecommand;
00762 mPrecommand = 0;
00763 if (normalExit) mSendProc->start();
00764 else slotIdle();
00765 }
00766
00767
00768
00769 void KMSender::setSendImmediate(bool aSendImmediate)
00770 {
00771 mSendImmediate = aSendImmediate;
00772 }
00773
00774
00775
00776 void KMSender::setSendQuotedPrintable(bool aSendQuotedPrintable)
00777 {
00778 mSendQuotedPrintable = aSendQuotedPrintable;
00779 }
00780
00781
00782
00783 KMSendProc* KMSender::createSendProcFromString( const QString & transport )
00784 {
00785 mTransportInfo->type = QString::null;
00786 int nr = KMTransportInfo::findTransport(transport);
00787 if (nr)
00788 {
00789 mTransportInfo->readConfig(nr);
00790 } else {
00791 if (transport.startsWith("smtp://"))
00792 {
00793 mTransportInfo->type = "smtp";
00794 mTransportInfo->auth = FALSE;
00795 mTransportInfo->encryption = "NONE";
00796 QString serverport = transport.mid(7);
00797 int colon = serverport.find(':');
00798 if (colon != -1) {
00799 mTransportInfo->host = serverport.left(colon);
00800 mTransportInfo->port = serverport.mid(colon + 1);
00801 } else {
00802 mTransportInfo->host = serverport;
00803 mTransportInfo->port = "25";
00804 }
00805 } else
00806 if (transport.startsWith("smtps://"))
00807 {
00808 mTransportInfo->type = "smtps";
00809 mTransportInfo->auth = FALSE;
00810 mTransportInfo->encryption = "ssl";
00811 QString serverport = transport.mid(7);
00812 int colon = serverport.find(':');
00813 if (colon != -1) {
00814 mTransportInfo->host = serverport.left(colon);
00815 mTransportInfo->port = serverport.mid(colon + 1);
00816 } else {
00817 mTransportInfo->host = serverport;
00818 mTransportInfo->port = "465";
00819 }
00820 }
00821 else if (transport.startsWith("file://"))
00822 {
00823 mTransportInfo->type = "sendmail";
00824 mTransportInfo->host = transport.mid(7);
00825 }
00826 }
00827
00828 while (mTransportInfo->host.endsWith("/")) {
00829 mTransportInfo->host.truncate(mTransportInfo->host.length()-1);
00830 }
00831
00832
00833 if (mTransportInfo->type == "sendmail")
00834 return new KMSendSendmail(this);
00835 if (mTransportInfo->type == "smtp" || mTransportInfo->type == "smtps")
00836 return new KMSendSMTP(this);
00837
00838 return 0L;
00839 }
00840
00841
00842 void KMSender::setStatusByLink(const KMMessage *aMsg)
00843 {
00844 int n = 0;
00845 while (1) {
00846 ulong msn;
00847 KMMsgStatus status;
00848 aMsg->getLink(n, &msn, &status);
00849 if (!msn || !status)
00850 break;
00851 n++;
00852
00853 KMFolder *folder = 0;
00854 int index = -1;
00855 KMMsgDict::instance()->getLocation(msn, &folder, &index);
00856 if (folder && index != -1) {
00857 folder->open();
00858 if ( status == KMMsgStatusDeleted ) {
00859
00860 KMDeleteMsgCommand *cmd =
00861 new KMDeleteMsgCommand( folder, folder->getMsg( index ) );
00862 cmd->start();
00863 } else {
00864 folder->setStatus(index, status);
00865 }
00866 folder->close();
00867 } else {
00868 kdWarning(5006) << k_funcinfo << "Cannot update linked message, it could not be found!" << endl;
00869 }
00870 }
00871 }
00872
00873
00874
00875 KMSendProc::KMSendProc( KMSender * sender )
00876 : QObject( 0 ),
00877 mSender( sender ),
00878 mLastErrorMessage(),
00879 mSendOk( false ),
00880 mSending( false )
00881 {
00882 }
00883
00884
00885 void KMSendProc::reset()
00886 {
00887 mSending = FALSE;
00888 mSendOk = FALSE;
00889 mLastErrorMessage = QString::null;
00890 }
00891
00892
00893 void KMSendProc::failed(const QString &aMsg)
00894 {
00895 mSending = FALSE;
00896 mSendOk = FALSE;
00897 mLastErrorMessage = aMsg;
00898 }
00899
00900
00901 void KMSendProc::statusMsg(const QString& aMsg)
00902 {
00903 if (mSender) mSender->setStatusMsg(aMsg);
00904 }
00905
00906
00907
00908 KMSendSendmail::KMSendSendmail( KMSender * sender )
00909 : KMSendProc( sender ),
00910 mMsgStr(),
00911 mMsgPos( 0 ),
00912 mMsgRest( 0 ),
00913 mMailerProc( 0 )
00914 {
00915
00916 }
00917
00918 KMSendSendmail::~KMSendSendmail() {
00919 delete mMailerProc; mMailerProc = 0;
00920 }
00921
00922 bool KMSendSendmail::doStart() {
00923
00924 if (mSender->transportInfo()->host.isEmpty())
00925 {
00926 const QString str = i18n("Please specify a mailer program in the settings.");
00927 const QString msg = i18n("Sending failed:\n%1\n"
00928 "The message will stay in the 'outbox' folder and will be resent.\n"
00929 "Please remove it from there if you do not want the message to "
00930 "be resent.\n"
00931 "The following transport protocol was used:\n %2")
00932 .arg(str + "\n")
00933 .arg("sendmail://");
00934 KMessageBox::information(0,msg);
00935 return false;
00936 }
00937
00938 if (!mMailerProc)
00939 {
00940 mMailerProc = new KProcess;
00941 assert(mMailerProc != 0);
00942 connect(mMailerProc,SIGNAL(processExited(KProcess*)),
00943 this, SLOT(sendmailExited(KProcess*)));
00944 connect(mMailerProc,SIGNAL(wroteStdin(KProcess*)),
00945 this, SLOT(wroteStdin(KProcess*)));
00946 connect(mMailerProc,SIGNAL(receivedStderr(KProcess*,char*,int)),
00947 this, SLOT(receivedStderr(KProcess*, char*, int)));
00948 }
00949 return true;
00950 }
00951
00952 void KMSendSendmail::doFinish() {
00953 delete mMailerProc;
00954 mMailerProc = 0;
00955 }
00956
00957 void KMSendSendmail::abort()
00958 {
00959 delete mMailerProc;
00960 mMailerProc = 0;
00961 mSendOk = false;
00962 mMsgStr = 0;
00963 idle();
00964 }
00965
00966 bool KMSendSendmail::doSend( const QString & sender, const QStringList & to, const QStringList & cc, const QStringList & bcc, const QByteArray & message ) {
00967 mMailerProc->clearArguments();
00968 *mMailerProc << mSender->transportInfo()->host
00969 << "-i" << "-f" << sender
00970 << to << cc << bcc ;
00971
00972 mMsgStr = message;
00973
00974 if ( !mMailerProc->start( KProcess::NotifyOnExit, KProcess::All ) ) {
00975 KMessageBox::information( 0, i18n("Failed to execute mailer program %1")
00976 .arg( mSender->transportInfo()->host ) );
00977 return false;
00978 }
00979 mMsgPos = mMsgStr.data();
00980 mMsgRest = mMsgStr.size();
00981 wroteStdin( mMailerProc );
00982
00983 return true;
00984 }
00985
00986
00987 void KMSendSendmail::wroteStdin(KProcess *proc)
00988 {
00989 char* str;
00990 int len;
00991
00992 assert(proc!=0);
00993 Q_UNUSED( proc );
00994
00995 str = mMsgPos;
00996 len = (mMsgRest>1024 ? 1024 : mMsgRest);
00997
00998 if (len <= 0)
00999 {
01000 mMailerProc->closeStdin();
01001 }
01002 else
01003 {
01004 mMsgRest -= len;
01005 mMsgPos += len;
01006 mMailerProc->writeStdin(str,len);
01007
01008
01009 }
01010 }
01011
01012
01013 void KMSendSendmail::receivedStderr(KProcess *proc, char *buffer, int buflen)
01014 {
01015 assert(proc!=0);
01016 Q_UNUSED( proc );
01017 mLastErrorMessage.replace(mLastErrorMessage.length(), buflen, buffer);
01018 }
01019
01020
01021 void KMSendSendmail::sendmailExited(KProcess *proc)
01022 {
01023 assert(proc!=0);
01024 mSendOk = (proc->normalExit() && proc->exitStatus()==0);
01025 if (!mSendOk) failed(i18n("Sendmail exited abnormally."));
01026 mMsgStr = 0;
01027 emit idle();
01028 }
01029
01030
01031
01032
01033
01034
01035 KMSendSMTP::KMSendSMTP(KMSender *sender)
01036 : KMSendProc(sender),
01037 mInProcess(false),
01038 mJob(0),
01039 mSlave(0)
01040 {
01041 KIO::Scheduler::connect(SIGNAL(slaveError(KIO::Slave *, int,
01042 const QString &)), this, SLOT(slaveError(KIO::Slave *, int,
01043 const QString &)));
01044 }
01045
01046 KMSendSMTP::~KMSendSMTP()
01047 {
01048 if (mJob) mJob->kill();
01049 }
01050
01051 bool KMSendSMTP::doSend( const QString & sender, const QStringList & to, const QStringList & cc, const QStringList & bcc, const QByteArray & message ) {
01052 QString query = "headers=0&from=";
01053 query += KURL::encode_string( sender );
01054
01055 QStringList::ConstIterator it;
01056
01057 for ( it = to.begin(); it != to.end(); ++it )
01058 query += "&to=" + KURL::encode_string(*it);
01059 for ( it = cc.begin(); it != cc.end(); ++it )
01060 query += "&cc=" + KURL::encode_string(*it);
01061 for ( it = bcc.begin(); it != bcc.end(); ++it )
01062 query += "&bcc=" + KURL::encode_string(*it);
01063
01064 KMTransportInfo * ti = mSender->transportInfo();
01065
01066 if ( ti->specifyHostname )
01067 query += "&hostname=" + KURL::encode_string( ti->localHostname );
01068
01069 if ( !kmkernel->msgSender()->sendQuotedPrintable() )
01070 query += "&body=8bit";
01071
01072 KURL destination;
01073
01074 destination.setProtocol((ti->encryption == "SSL") ? SMTPS_PROTOCOL : SMTP_PROTOCOL);
01075 destination.setHost(ti->host);
01076 destination.setPort(ti->port.toUShort());
01077
01078 if (ti->auth)
01079 {
01080 if( (ti->user.isEmpty() || ti->passwd().isEmpty()) &&
01081 ti->authType != "GSSAPI" )
01082 {
01083 bool b = FALSE;
01084 int result;
01085
01086 KCursorSaver idle(KBusyPtr::idle());
01087 QString passwd = ti->passwd();
01088 result = KIO::PasswordDialog::getNameAndPassword(ti->user, passwd,
01089 &b, i18n("You need to supply a username and a password to use this "
01090 "SMTP server."), FALSE, QString::null, ti->name, QString::null);
01091
01092 if ( result != QDialog::Accepted )
01093 {
01094 abort();
01095 return FALSE;
01096 }
01097 if (int id = KMTransportInfo::findTransport(ti->name)) {
01098 ti->setPasswd( passwd );
01099 ti->writeConfig(id);
01100 }
01101 }
01102 destination.setUser(ti->user);
01103 destination.setPass(ti->passwd());
01104 }
01105
01106 if (!mSlave || !mInProcess)
01107 {
01108 KIO::MetaData slaveConfig;
01109 slaveConfig.insert("tls", (ti->encryption == "TLS") ? "on" : "off");
01110 if (ti->auth) slaveConfig.insert("sasl", ti->authType);
01111 mSlave = KIO::Scheduler::getConnectedSlave(destination, slaveConfig);
01112 }
01113
01114 if (!mSlave)
01115 {
01116 abort();
01117 return false;
01118 }
01119
01120
01121 mMessage = message;
01122 mMessageLength = mMessage.size();
01123 mMessageOffset = 0;
01124
01125 if ( mMessageLength )
01126
01127
01128 query += "&size=" + QString::number( qRound( mMessageLength * 1.05 ) );
01129
01130 destination.setPath("/send");
01131 destination.setQuery( query );
01132
01133 mJob = KIO::put( destination, -1, false, false, false );
01134 if ( !mJob ) {
01135 abort();
01136 return false;
01137 }
01138 mJob->addMetaData( "lf2crlf+dotstuff", "slave" );
01139 KIO::Scheduler::assignJobToSlave(mSlave, mJob);
01140 connect(mJob, SIGNAL(result(KIO::Job *)), this, SLOT(result(KIO::Job *)));
01141 connect(mJob, SIGNAL(dataReq(KIO::Job *, QByteArray &)),
01142 this, SLOT(dataReq(KIO::Job *, QByteArray &)));
01143 mSendOk = true;
01144 mInProcess = true;
01145 return true;
01146 }
01147
01148 void KMSendSMTP::cleanup() {
01149 if(mJob)
01150 {
01151 mJob->kill(TRUE);
01152 mJob = 0;
01153 mSlave = 0;
01154 }
01155
01156 if (mSlave)
01157 {
01158 KIO::Scheduler::disconnectSlave(mSlave);
01159 mSlave = 0;
01160 }
01161
01162 mInProcess = false;
01163 }
01164
01165 void KMSendSMTP::abort() {
01166 cleanup();
01167 emit idle();
01168 }
01169
01170 void KMSendSMTP::doFinish() {
01171 cleanup();
01172 }
01173
01174 void KMSendSMTP::dataReq(KIO::Job *, QByteArray &array)
01175 {
01176
01177 const int chunkSize = QMIN( mMessageLength - mMessageOffset, 32*1024 );
01178 if ( chunkSize > 0 ) {
01179 array.duplicate(mMessage.data() + mMessageOffset, chunkSize);
01180 mMessageOffset += chunkSize;
01181 } else
01182 {
01183 array.resize(0);
01184 mMessage.resize(0);
01185 }
01186 mSender->emitProgressInfo( mMessageOffset );
01187 }
01188
01189 void KMSendSMTP::result(KIO::Job *_job)
01190 {
01191 if (!mJob) return;
01192 mJob = 0;
01193
01194 if(_job->error())
01195 {
01196 mSendOk = false;
01197 if (_job->error() == KIO::ERR_SLAVE_DIED) mSlave = 0;
01198 failed(_job->errorString());
01199 abort();
01200 } else {
01201 emit idle();
01202 }
01203 }
01204
01205 void KMSendSMTP::slaveError(KIO::Slave *aSlave, int error, const QString &errorMsg)
01206 {
01207 if (aSlave == mSlave)
01208 {
01209 if (error == KIO::ERR_SLAVE_DIED) mSlave = 0;
01210 mSendOk = false;
01211 mJob = 0;
01212 failed(KIO::buildErrorString(error, errorMsg));
01213 abort();
01214 }
01215 }
01216
01217 #include "kmsender.moc"
01218 #include "kmsender_p.moc"