kmail Library API Documentation

kmsender.cpp

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