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