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