kmail Library API Documentation

kmsender.cpp

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