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 
00285     const KPIM::Identity & id = kmkernel->identityManager()
00286       ->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
00287     if ( !mCurrentMsg->fcc().isEmpty() )
00288     {
00289       sentFolder = kmkernel->folderMgr()->findIdString( mCurrentMsg->fcc() );
00290       if ( sentFolder == 0 )
00291       // This is *NOT* supposed to be imapSentFolder!
00292         sentFolder =
00293           kmkernel->dimapFolderMgr()->findIdString( mCurrentMsg->fcc() );
00294       if ( sentFolder == 0 )
00295         imapSentFolder =
00296           kmkernel->imapFolderMgr()->findIdString( mCurrentMsg->fcc() );
00297     }
00298     else if ( !id.fcc().isEmpty() )
00299     {
00300       sentFolder = kmkernel->folderMgr()->findIdString( id.fcc() );
00301       if ( sentFolder == 0 )
00302         // This is *NOT* supposed to be imapSentFolder!
00303         sentFolder = kmkernel->dimapFolderMgr()->findIdString( id.fcc() );
00304       if ( sentFolder == 0 )
00305         imapSentFolder = kmkernel->imapFolderMgr()->findIdString( id.fcc() );
00306     }
00307     if (imapSentFolder && imapSentFolder->noContent()) imapSentFolder = 0;
00308 
00309     if ( sentFolder == 0 )
00310       sentFolder = kmkernel->sentFolder();
00311 
00312     if ( sentFolder ) {
00313       rc = sentFolder->open();
00314       if (rc != 0) {
00315         cleanup();
00316         return;
00317       }
00318     }
00319 
00320     // Disable the emitting of msgAdded signal, because the message is taken out of the
00321     // current folder (outbox) and re-added, to make filter actions changing the message
00322     // work. We don't want that to screw up message counts.
00323     if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( true );
00324     int processResult = kmkernel->filterMgr()->process(mCurrentMsg,KMFilterMgr::Outbound);
00325     if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( false );
00326 
00327     // 0==processed ok, 1==no filter matched, 2==critical error, abort!
00328     switch (processResult) {
00329     case 2:
00330       perror("Critical error: Unable to process sent mail (out of space?)");
00331       KMessageBox::information(0, i18n("Critical error: "
00332                    "Unable to process sent mail (out of space?)"
00333                    "Moving failing message to \"sent-mail\" folder."));
00334       sentFolder->moveMsg(mCurrentMsg);
00335       sentFolder->close();
00336       cleanup();
00337       return;
00338     case 1:
00339       if (sentFolder->moveMsg(mCurrentMsg) != 0)
00340       {
00341         KMessageBox::error(0, i18n("Moving the sent message \"%1\" from the "
00342           "\"outbox\" to the \"sent-mail\" folder failed.\n"
00343           "Possible reasons are lack of disk space or write permission. "
00344           "Please try to fix the problem and move the message manually.")
00345           .arg(mCurrentMsg->subject()));
00346         cleanup();
00347         return;
00348       }
00349       if (imapSentFolder) {
00350         // Does proper folder refcounting and message locking
00351         KMCommand *command = new KMMoveCommand( imapSentFolder, mCurrentMsg );
00352         command->keepFolderOpen( sentFolder ); // will open it, and close it once done
00353         command->start();
00354       }
00355     default:
00356       break;
00357     }
00358     setStatusByLink( mCurrentMsg );
00359     if (mCurrentMsg->parent() && !imapSentFolder) {
00360       // for speed optimization, this code assumes that mCurrentMsg is the
00361       // last one in it's parent folder; make sure that's really the case:
00362       assert( mCurrentMsg->parent()->find( mCurrentMsg )
00363               == mCurrentMsg->parent()->count() - 1 );
00364        // unGet this message:
00365       mCurrentMsg->parent()->unGetMsg( mCurrentMsg->parent()->count() -1 );
00366     }
00367 
00368     mCurrentMsg = 0;
00369   }
00370 
00371   // See if there is another queued message
00372   mCurrentMsg = mOutboxFolder->getMsg(mFailedMessages);
00373   if ( mCurrentMsg && !mCurrentMsg->transferInProgress() &&
00374        mCurrentMsg->sender().isEmpty() ) {
00375     // if we do not have a sender address then use the email address of the
00376     // message's identity or of the default identity unless those two are also
00377     // empty
00378     const KPIM::Identity & id = kmkernel->identityManager()
00379       ->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
00380     if ( !id.emailAddr().isEmpty() ) {
00381       mCurrentMsg->setFrom( id.fullEmailAddr() );
00382     }
00383     else if ( !kmkernel->identityManager()->defaultIdentity().emailAddr().isEmpty() ) {
00384       mCurrentMsg->setFrom( kmkernel->identityManager()->defaultIdentity().fullEmailAddr() );
00385     }
00386     else {
00387       KMessageBox::sorry( 0, i18n( "It's not possible to send messages "
00388                                    "without specifying a sender address.\n"
00389                                    "Please set the email address of "
00390                                    "identity '%1' in the Identities "
00391                                    "section of the configuration dialog "
00392                                    "and then try again." )
00393                              .arg( id.identityName() ) );
00394       mOutboxFolder->unGetMsg( mFailedMessages );
00395       mCurrentMsg = 0;
00396     }
00397   }
00398   if (!mCurrentMsg || mCurrentMsg->transferInProgress())
00399   {
00400     // a message is locked finish the send
00401     if (mCurrentMsg && mCurrentMsg->transferInProgress())
00402         mCurrentMsg = 0;
00403     // no more message: cleanup and done
00404     if ( sentFolder != 0 )
00405         sentFolder->close();
00406     if ( someSent ) {
00407       if ( mSentMessages == mTotalMessages ) {
00408         setStatusMsg(i18n("%n queued message successfully sent.",
00409                           "%n queued messages successfully sent.",
00410                      mSentMessages));
00411       } else {
00412         setStatusMsg(i18n("%1 of %2 queued messages successfully sent.")
00413             .arg(mSentMessages).arg( mTotalMessages ));
00414       }
00415     }
00416     cleanup();
00417     return;
00418   }
00419   mCurrentMsg->setTransferInProgress( TRUE );
00420 
00421   // start the sender process or initialize communication
00422   if (!mSendInProgress)
00423   {
00424     Q_ASSERT( !mProgressItem );
00425     mProgressItem = KPIM::ProgressManager::createProgressItem(
00426       "Sender",
00427       i18n( "Sending messages" ),
00428       i18n("Initiating sender process..."),
00429       true );
00430     connect( mProgressItem, SIGNAL( progressItemCanceled( ProgressItem* ) ),
00431              this, SLOT( slotAbortSend() ) );
00432     kapp->ref();
00433     mSendInProgress = TRUE;
00434   }
00435 
00436   QString msgTransport = mCurrentMsg->headerField("X-KMail-Transport");
00437   if (msgTransport.isEmpty())
00438   {
00439     QStringList sl = KMTransportInfo::availableTransports();
00440     if (!sl.isEmpty()) msgTransport = sl[0];
00441   }
00442   if (!mSendProc || msgTransport != mMethodStr) {
00443     if (mSendProcStarted && mSendProc) {
00444       mSendProc->finish(true);
00445       mSendProcStarted = FALSE;
00446     }
00447 
00448     mSendProc = createSendProcFromString(msgTransport);
00449     mMethodStr = msgTransport;
00450 
00451     if( mTransportInfo->encryption == "TLS" || mTransportInfo->encryption == "SSL" )
00452       mProgressItem->setUsesCrypto( true );
00453 
00454     if (!mSendProc)
00455       sendProcStarted(false);
00456     else {
00457       connect(mSendProc, SIGNAL(idle()), SLOT(slotIdle()));
00458       connect(mSendProc, SIGNAL(started(bool)), SLOT(sendProcStarted(bool)));
00459 
00460       // Run the precommand if there is one
00461       if (!mTransportInfo->precommand.isEmpty())
00462       {
00463         setStatusMsg(i18n("Executing precommand %1")
00464           .arg(mTransportInfo->precommand));
00465         mPrecommand = new KMPrecommand(mTransportInfo->precommand);
00466         connect(mPrecommand, SIGNAL(finished(bool)),
00467           SLOT(slotPrecommandFinished(bool)));
00468         if (!mPrecommand->start())
00469         {
00470           delete mPrecommand;
00471           mPrecommand = 0;
00472         }
00473         return;
00474       }
00475 
00476       mSendProc->start();
00477     }
00478   }
00479   else if (!mSendProcStarted)
00480     mSendProc->start();
00481   else
00482     doSendMsgAux();
00483 }
00484 
00485 
00486 //-----------------------------------------------------------------------------
00487 void KMSender::sendProcStarted(bool success)
00488 {
00489   if (!success) {
00490     if (mSendProc)
00491        mSendProc->finish(true);
00492     else
00493       setStatusMsg(i18n("Unrecognized transport protocol. Unable to send message."));
00494     mSendProc = 0;
00495     mSendProcStarted = false;
00496     cleanup();
00497     return;
00498   }
00499   doSendMsgAux();
00500 }
00501 
00502 
00503 //-----------------------------------------------------------------------------
00504 void KMSender::doSendMsgAux()
00505 {
00506   mSendProcStarted = TRUE;
00507 
00508   // start sending the current message
00509 
00510   mSendProc->preSendInit();
00511   setStatusMsg(i18n("%3: subject of message","Sending message %1 of %2: %3")
00512            .arg(mSentMessages+mFailedMessages+1).arg(mTotalMessages)
00513            .arg(mCurrentMsg->subject()));
00514   if (!mSendProc->send(mCurrentMsg))
00515   {
00516     cleanup();
00517     setStatusMsg(i18n("Failed to send (some) queued messages."));
00518     return;
00519   }
00520   // Do *not* add code here, after send(). It can happen that this method
00521   // is called recursively if send() emits the idle signal directly.
00522 }
00523 
00524 
00525 //-----------------------------------------------------------------------------
00526 void KMSender::cleanup(void)
00527 {
00528   kdDebug(5006) << k_funcinfo << endl;
00529   if (mSendProc && mSendProcStarted) mSendProc->finish(true);
00530   mSendProc = 0;
00531   mSendProcStarted = FALSE;
00532   if (mSendInProgress) kapp->deref();
00533   mSendInProgress = FALSE;
00534   if (mCurrentMsg)
00535   {
00536     mCurrentMsg->setTransferInProgress( FALSE );
00537     mCurrentMsg = 0;
00538   }
00539   if ( mSentFolder ) {
00540     mSentFolder->close();
00541     mSentFolder = 0;
00542   }
00543   if ( mOutboxFolder ) {
00544     disconnect( mOutboxFolder, SIGNAL(msgAdded(int)),
00545                 this, SLOT(outboxMsgAdded(int)) );
00546     mOutboxFolder->close();
00547     if ( mOutboxFolder->count( true ) == 0 ) {
00548       mOutboxFolder->expunge();
00549     }
00550     else if ( mOutboxFolder->needsCompacting() ) {
00551       mOutboxFolder->compact( KMFolder::CompactSilentlyNow );
00552     }
00553     mOutboxFolder = 0;
00554   }
00555 
00556   mSendAborted = false;
00557   mSentMessages = 0;
00558   mFailedMessages = 0;
00559   mSentBytes = 0;
00560   if ( mProgressItem )
00561     mProgressItem->setComplete();
00562   mProgressItem = 0;
00563   kmkernel->filterMgr()->deref();
00564 }
00565 
00566 
00567 //-----------------------------------------------------------------------------
00568 void KMSender::slotAbortSend()
00569 {
00570   mSendAborted = true;
00571   delete mPrecommand;
00572   mPrecommand = 0;
00573   if (mSendProc) mSendProc->abort();
00574 }
00575 
00576 //-----------------------------------------------------------------------------
00577 void KMSender::slotIdle()
00578 {
00579   assert(mSendProc != 0);
00580 
00581   QString msg;
00582   QString errString;
00583   if (mSendProc)
00584       errString = mSendProc->message();
00585 
00586   if (mSendAborted) {
00587     // sending of message aborted
00588     msg = i18n("Sending aborted:\n%1\n"
00589         "The message will stay in the 'outbox' folder until you either "
00590         "fix the problem (e.g. a broken address) or remove the message "
00591         "from the 'outbox' folder.\n"
00592         "The following transport protocol was used:\n  %2")
00593       .arg(errString)
00594       .arg(mMethodStr);
00595     if (!errString.isEmpty()) KMessageBox::error(0,msg);
00596     setStatusMsg( i18n( "Sending aborted." ) );
00597   } else {
00598     if (!mSendProc->sendOk()) {
00599       mCurrentMsg->setTransferInProgress( false );
00600       mCurrentMsg = 0;
00601       mFailedMessages++;
00602       // Sending of message failed.
00603       if (!errString.isEmpty()) {
00604         int res = KMessageBox::Yes;
00605         if (mSentMessages+mFailedMessages != mTotalMessages) {
00606           msg = i18n("<p>Sending failed:</p>"
00607             "<p>%1</p>"
00608             "<p>The message will stay in the 'outbox' folder until you either "
00609             "fix the problem (e.g. a broken address) or remove the message "
00610             "from the 'outbox' folder.</p>"
00611             "<p>The following transport protocol was used:  %2</p>"
00612             "<p>Do you want me to continue sending the remaining messages?</p>")
00613             .arg(errString)
00614             .arg(mMethodStr);
00615           res = KMessageBox::warningYesNo( 0 , msg ,
00616                   i18n( "Continue Sending" ), i18n( "&Continue Sending" ),
00617                   i18n("&Abort Sending") );
00618         } else {
00619           msg = i18n("Sending failed:\n%1\n"
00620             "The message will stay in the 'outbox' folder until you either "
00621             "fix the problem (e.g. a broken address) or remove the message "
00622             "from the 'outbox' folder.\n"
00623             "The following transport protocol was used:\n %2")
00624             .arg(errString)
00625             .arg(mMethodStr);
00626           KMessageBox::error(0,msg);
00627         }
00628         if (res == KMessageBox::Yes) {
00629           // Try the next one.
00630           doSendMsg();
00631           return;
00632         } else {
00633           setStatusMsg( i18n( "Sending aborted." ) );
00634         }
00635       }
00636     } else {
00637       // Sending suceeded.
00638       doSendMsg();
00639       return;
00640     }
00641   }
00642   mSendProc->finish(true);
00643   mSendProc = 0;
00644   mSendProcStarted = false;
00645 
00646   cleanup();
00647 }
00648 
00649 
00650 //-----------------------------------------------------------------------------
00651 void KMSender::slotPrecommandFinished(bool normalExit)
00652 {
00653   delete mPrecommand;
00654   mPrecommand = 0;
00655   if (normalExit) mSendProc->start();
00656   else slotIdle();
00657 }
00658 
00659 
00660 //-----------------------------------------------------------------------------
00661 void KMSender::setSendImmediate(bool aSendImmediate)
00662 {
00663   mSendImmediate = aSendImmediate;
00664 }
00665 
00666 
00667 //-----------------------------------------------------------------------------
00668 void KMSender::setSendQuotedPrintable(bool aSendQuotedPrintable)
00669 {
00670   mSendQuotedPrintable = aSendQuotedPrintable;
00671 }
00672 
00673 
00674 //-----------------------------------------------------------------------------
00675 KMSendProc* KMSender::createSendProcFromString(QString transport)
00676 {
00677   mTransportInfo->type = QString::null;
00678   int nr = KMTransportInfo::findTransport(transport);
00679   if (nr)
00680   {
00681     mTransportInfo->readConfig(nr);
00682   } else {
00683     if (transport.startsWith("smtp://")) // should probably use KURL and SMTP_PROTOCOL
00684     {
00685       mTransportInfo->type = "smtp";
00686       mTransportInfo->auth = FALSE;
00687       mTransportInfo->encryption = "NONE";
00688       QString serverport = transport.mid(7);
00689       int colon = serverport.find(':');
00690       if (colon != -1) {
00691         mTransportInfo->host = serverport.left(colon);
00692         mTransportInfo->port = serverport.mid(colon + 1);
00693       } else {
00694         mTransportInfo->host = serverport;
00695         mTransportInfo->port = "25";
00696       }
00697     } else
00698     if (transport.startsWith("smtps://"))  // should probably use KURL and SMTPS_PROTOCOL
00699     {
00700       mTransportInfo->type = "smtps";
00701       mTransportInfo->auth = FALSE;
00702       mTransportInfo->encryption = "ssl";
00703       QString serverport = transport.mid(7);
00704       int colon = serverport.find(':');
00705       if (colon != -1) {
00706         mTransportInfo->host = serverport.left(colon);
00707         mTransportInfo->port = serverport.mid(colon + 1);
00708       } else {
00709         mTransportInfo->host = serverport;
00710         mTransportInfo->port = "465";
00711       }
00712     }
00713     else if (transport.startsWith("file://"))
00714     {
00715       mTransportInfo->type = "sendmail";
00716       mTransportInfo->host = transport.mid(7);
00717     }
00718   }
00719   // strip off a trailing "/"
00720   while (mTransportInfo->host.endsWith("/")) {
00721     mTransportInfo->host.truncate(mTransportInfo->host.length()-1);
00722   }
00723 
00724 
00725   if (mTransportInfo->type == "sendmail")
00726     return new KMSendSendmail(this);
00727   if (mTransportInfo->type == "smtp" || mTransportInfo->type == "smtps")
00728     return new KMSendSMTP(this);
00729 
00730   return 0L;
00731 }
00732 
00733 //-----------------------------------------------------------------------------
00734 void KMSender::setStatusByLink(const KMMessage *aMsg)
00735 {
00736   int n = 0;
00737   while (1) {
00738     ulong msn;
00739     KMMsgStatus status;
00740     aMsg->getLink(n, &msn, &status);
00741     if (!msn || !status)
00742       break;
00743     n++;
00744 
00745     KMFolder *folder = 0;
00746     int index = -1;
00747     kmkernel->msgDict()->getLocation(msn, &folder, &index);
00748     if (folder && index != -1) {
00749       folder->open();
00750       if ( status == KMMsgStatusDeleted ) {
00751         // Move the message to the trash folder
00752         KMDeleteMsgCommand *cmd = 
00753           new KMDeleteMsgCommand( folder, folder->getMsg( index ) ); 
00754         cmd->start();
00755       } else {
00756         folder->setStatus(index, status);
00757       }
00758       folder->close();
00759     } else {
00760       kdWarning(5006) << k_funcinfo << "Cannot update linked message, it could not be found!" << endl;
00761     }
00762   }
00763 }
00764 
00765 //=============================================================================
00766 //=============================================================================
00767 KMSendProc::KMSendProc(KMSender* aSender): QObject()
00768 {
00769   mSender = aSender;
00770   preSendInit();
00771 }
00772 
00773 //-----------------------------------------------------------------------------
00774 void KMSendProc::preSendInit(void)
00775 {
00776   mSending = FALSE;
00777   mSendOk = FALSE;
00778   mMsg = QString::null;
00779 }
00780 
00781 //-----------------------------------------------------------------------------
00782 void KMSendProc::failed(const QString &aMsg)
00783 {
00784   mSending = FALSE;
00785   mSendOk = FALSE;
00786   mMsg = aMsg;
00787 }
00788 
00789 //-----------------------------------------------------------------------------
00790 void KMSendProc::start(void)
00791 {
00792   emit started(true);
00793 }
00794 
00795 //-----------------------------------------------------------------------------
00796 bool KMSendProc::finish(bool destructive)
00797 {
00798   if (destructive) deleteLater();
00799   return TRUE;
00800 }
00801 
00802 //-----------------------------------------------------------------------------
00803 void KMSendProc::statusMsg(const QString& aMsg)
00804 {
00805   if (mSender) mSender->setStatusMsg(aMsg);
00806 }
00807 
00808 //-----------------------------------------------------------------------------
00809 bool KMSendProc::addRecipients( const AddrSpecList & al )
00810 {
00811   for ( AddrSpecList::const_iterator it = al.begin() ; it != al.end() ; ++it )
00812     if ( !addOneRecipient( (*it).asString() ) )
00813       return false;
00814   return true;
00815 }
00816 
00817 
00818 //=============================================================================
00819 //=============================================================================
00820 KMSendSendmail::KMSendSendmail(KMSender* aSender):
00821   KMSendProc(aSender)
00822 {
00823   mMailerProc = 0;
00824 }
00825 
00826 //-----------------------------------------------------------------------------
00827 KMSendSendmail::~KMSendSendmail()
00828 {
00829   delete mMailerProc;
00830 }
00831 
00832 //-----------------------------------------------------------------------------
00833 void KMSendSendmail::start(void)
00834 {
00835   if (mSender->transportInfo()->host.isEmpty())
00836   {
00837     QString str = i18n("Please specify a mailer program in the settings.");
00838     QString msg;
00839     msg = i18n("Sending failed:\n%1\n"
00840     "The message will stay in the 'outbox' folder and will be resent.\n"
00841         "Please remove it from there if you do not want the message to "
00842         "be resent.\n"
00843     "The following transport protocol was used:\n  %2")
00844     .arg(str + "\n")
00845     .arg("sendmail://");
00846     KMessageBox::information(0,msg);
00847     emit started(false);
00848     return;
00849   }
00850 
00851   if (!mMailerProc)
00852   {
00853     mMailerProc = new KProcess;
00854     assert(mMailerProc != 0);
00855     connect(mMailerProc,SIGNAL(processExited(KProcess*)),
00856         this, SLOT(sendmailExited(KProcess*)));
00857     connect(mMailerProc,SIGNAL(wroteStdin(KProcess*)),
00858         this, SLOT(wroteStdin(KProcess*)));
00859     connect(mMailerProc,SIGNAL(receivedStderr(KProcess*,char*,int)),
00860         this, SLOT(receivedStderr(KProcess*, char*, int)));
00861   }
00862   emit started(true);
00863 }
00864 
00865 //-----------------------------------------------------------------------------
00866 bool KMSendSendmail::finish(bool destructive)
00867 {
00868   delete mMailerProc;
00869   mMailerProc = 0;
00870   if (destructive)
00871         deleteLater();
00872   return TRUE;
00873 }
00874 
00875 //-----------------------------------------------------------------------------
00876 void KMSendSendmail::abort()
00877 {
00878   delete mMailerProc;
00879   mMailerProc = 0;
00880   mSendOk = false;
00881   mMsgStr = 0;
00882   idle();
00883 }
00884 
00885 
00886 //-----------------------------------------------------------------------------
00887 bool KMSendSendmail::send(KMMessage* aMsg)
00888 {
00889   QString bccStr;
00890 
00891   mMailerProc->clearArguments();
00892   *mMailerProc << mSender->transportInfo()->host;
00893   *mMailerProc << "-i";
00894   *mMailerProc << "-f";
00895   *mMailerProc << aMsg->sender().latin1();
00896 
00897   if( !aMsg->headerField("X-KMail-Recipients").isEmpty() ) {
00898     // extended BCC handling to prevent TOs and CCs from seeing
00899     // BBC information by looking at source of an OpenPGP encrypted mail
00900     addRecipients(aMsg->extractAddrSpecs("X-KMail-Recipients"));
00901     aMsg->removeHeaderField( "X-KMail-Recipients" );
00902   } else {
00903     addRecipients(aMsg->extractAddrSpecs("To"));
00904     addRecipients(aMsg->extractAddrSpecs("Cc"));
00905     addRecipients(aMsg->extractAddrSpecs("Bcc"));
00906   }
00907 
00908   mMsgStr = aMsg->asSendableString();
00909 
00910   if (!mMailerProc->start(KProcess::NotifyOnExit,KProcess::All))
00911   {
00912     KMessageBox::information(0,i18n("Failed to execute mailer program %1")
00913                  .arg(mSender->transportInfo()->host));
00914     return FALSE;
00915   }
00916   mMsgPos  = mMsgStr.data();
00917   mMsgRest = mMsgStr.length();
00918   wroteStdin(mMailerProc);
00919 
00920   return TRUE;
00921 }
00922 
00923 
00924 //-----------------------------------------------------------------------------
00925 void KMSendSendmail::wroteStdin(KProcess *proc)
00926 {
00927   char* str;
00928   int len;
00929 
00930   assert(proc!=0);
00931   Q_UNUSED( proc );
00932 
00933   str = mMsgPos;
00934   len = (mMsgRest>1024 ? 1024 : mMsgRest);
00935 
00936   if (len <= 0)
00937   {
00938     mMailerProc->closeStdin();
00939   }
00940   else
00941   {
00942     mMsgRest -= len;
00943     mMsgPos  += len;
00944     mMailerProc->writeStdin(str,len);
00945     // if code is added after writeStdin() KProcess probably initiates
00946     // a race condition.
00947   }
00948 }
00949 
00950 
00951 //-----------------------------------------------------------------------------
00952 void KMSendSendmail::receivedStderr(KProcess *proc, char *buffer, int buflen)
00953 {
00954   assert(proc!=0);
00955   Q_UNUSED( proc );
00956   mMsg.replace(mMsg.length(), buflen, buffer);
00957 }
00958 
00959 
00960 //-----------------------------------------------------------------------------
00961 void KMSendSendmail::sendmailExited(KProcess *proc)
00962 {
00963   assert(proc!=0);
00964   mSendOk = (proc->normalExit() && proc->exitStatus()==0);
00965   if (!mSendOk) failed(i18n("Sendmail exited abnormally."));
00966   mMsgStr = 0;
00967   emit idle();
00968 }
00969 
00970 
00971 //-----------------------------------------------------------------------------
00972 bool KMSendSendmail::addOneRecipient(const QString& aRcpt)
00973 {
00974   assert(mMailerProc!=0);
00975   if (!aRcpt.isEmpty()) *mMailerProc << aRcpt;
00976   return TRUE;
00977 }
00978 
00979 
00980 
00981 //-----------------------------------------------------------------------------
00982 //=============================================================================
00983 //=============================================================================
00984 KMSendSMTP::KMSendSMTP(KMSender *sender)
00985   : KMSendProc(sender),
00986     mInProcess(false),
00987     mJob(0),
00988     mSlave(0)
00989 {
00990   KIO::Scheduler::connect(SIGNAL(slaveError(KIO::Slave *, int,
00991     const QString &)), this, SLOT(slaveError(KIO::Slave *, int,
00992     const QString &)));
00993 }
00994 
00995 KMSendSMTP::~KMSendSMTP()
00996 {
00997   if (mJob) mJob->kill();
00998 }
00999 
01000 bool KMSendSMTP::send(KMMessage *aMsg)
01001 {
01002   KMTransportInfo *ti = mSender->transportInfo();
01003   assert(aMsg != 0);
01004 
01005   const QString sender = aMsg->sender();
01006   if ( sender.isEmpty() )
01007     return false;
01008 
01009   // email this is from
01010   mQuery = "headers=0&from=";
01011   mQuery += KURL::encode_string( sender );
01012 
01013   // recipients
01014   if( !aMsg->headerField("X-KMail-Recipients").isEmpty() ) {
01015     // extended BCC handling to prevent TOs and CCs from seeing
01016     // BBC information by looking at source of an OpenPGP encrypted mail
01017     mQueryField = "&to=";
01018     if( !addRecipients( aMsg->extractAddrSpecs("X-KMail-Recipients")) ) {
01019       return FALSE;
01020     }
01021     aMsg->removeHeaderField( "X-KMail-Recipients" );
01022   } else {
01023     mQueryField = "&to=";
01024     if(!addRecipients(aMsg->extractAddrSpecs("To")))
01025     {
01026       return FALSE;
01027     }
01028 
01029     if(!aMsg->cc().isEmpty())
01030     {
01031       mQueryField = "&cc=";
01032       if(!addRecipients(aMsg->extractAddrSpecs("Cc"))) return FALSE;
01033     }
01034 
01035     QString bccStr = aMsg->bcc();
01036     if(!bccStr.isEmpty())
01037     {
01038       mQueryField = "&bcc=";
01039       if (!addRecipients(aMsg->extractAddrSpecs("Bcc"))) return FALSE;
01040     }
01041   }
01042 
01043   if (ti->specifyHostname)
01044     mQuery += "&hostname=" + KURL::encode_string(ti->localHostname);
01045 
01046   if ( !kmkernel->msgSender()->sendQuotedPrintable() )
01047     mQuery += "&body=8bit";
01048 
01049   KURL destination;
01050 
01051   destination.setProtocol((ti->encryption == "SSL") ? SMTPS_PROTOCOL : SMTP_PROTOCOL);
01052   destination.setHost(ti->host);
01053   destination.setPort(ti->port.toUShort());
01054 
01055   if (ti->auth)
01056   {
01057     if(ti->user.isEmpty() || ti->pass.isEmpty())
01058     {
01059       bool b = FALSE;
01060       int result;
01061 
01062       KCursorSaver idle(KBusyPtr::idle());
01063       result = KIO::PasswordDialog::getNameAndPassword(ti->user, ti->pass,
01064     &b, i18n("You need to supply a username and a password to use this "
01065          "SMTP server."), FALSE, QString::null, ti->name, QString::null);
01066 
01067       if ( result != QDialog::Accepted )
01068       {
01069         abort();
01070         return FALSE;
01071       }
01072       if (int id = KMTransportInfo::findTransport(ti->name))
01073         ti->writeConfig(id);
01074     }
01075     destination.setUser(ti->user);
01076     destination.setPass(ti->pass);
01077   }
01078 
01079   if (!mSlave || !mInProcess)
01080   {
01081     KIO::MetaData slaveConfig;
01082     slaveConfig.insert("tls", (ti->encryption == "TLS") ? "on" : "off");
01083     if (ti->auth) slaveConfig.insert("sasl", ti->authType);
01084     mSlave = KIO::Scheduler::getConnectedSlave(destination, slaveConfig);
01085   }
01086 
01087   if (!mSlave)
01088   {
01089     abort();
01090     return false;
01091   }
01092 
01093   // dotstuffing is now done by the slave (see setting of metadata)
01094   mMessage = aMsg->asSendableString();
01095   mMessageLength = mMessage.length();
01096   mMessageOffset = 0;
01097 
01098   if ( mMessageLength )
01099     // allow +5% for subsequent LF->CRLF and dotstuffing (an average
01100     // over 2G-lines gives an average line length of 42-43):
01101     mQuery += "&size=" + QString::number( qRound( mMessageLength * 1.05 ) );
01102 
01103   destination.setPath("/send");
01104   destination.setQuery(mQuery);
01105   mQuery = QString::null;
01106 
01107   if ((mJob = KIO::put(destination, -1, false, false, false)))
01108   {
01109     mJob->addMetaData( "lf2crlf+dotstuff", "slave" );
01110     KIO::Scheduler::assignJobToSlave(mSlave, mJob);
01111     connect(mJob, SIGNAL(result(KIO::Job *)), this, SLOT(result(KIO::Job *)));
01112     connect(mJob, SIGNAL(dataReq(KIO::Job *, QByteArray &)),
01113         this, SLOT(dataReq(KIO::Job *, QByteArray &)));
01114     mSendOk = true;
01115     mInProcess = true;
01116     return mSendOk;
01117   }
01118   else
01119   {
01120     abort();
01121     return false;
01122   }
01123 }
01124 
01125 void KMSendSMTP::abort()
01126 {
01127   finish(false);
01128   emit idle();
01129 }
01130 
01131 bool KMSendSMTP::finish(bool b)
01132 {
01133   if(mJob)
01134   {
01135     mJob->kill(TRUE);
01136     mJob = 0;
01137     mSlave = 0;
01138   }
01139 
01140   if (mSlave)
01141   {
01142     KIO::Scheduler::disconnectSlave(mSlave);
01143     mSlave = 0;
01144   }
01145 
01146   mInProcess = false;
01147   return KMSendProc::finish(b);
01148 }
01149 
01150 bool KMSendSMTP::addOneRecipient(const QString& _addr)
01151 {
01152   if(!_addr.isEmpty())
01153     mQuery += mQueryField + KURL::encode_string(_addr);
01154 
01155   return true;
01156 }
01157 
01158 void KMSendSMTP::dataReq(KIO::Job *, QByteArray &array)
01159 {
01160   // Send it by 32K chuncks
01161   int chunkSize = QMIN( mMessageLength - mMessageOffset, 0x8000 );
01162   if ( chunkSize > 0 ) {
01163     array.duplicate(mMessage.data() + mMessageOffset, chunkSize);
01164     mMessageOffset += chunkSize;
01165   } else
01166   {
01167     array.resize(0);
01168     mMessage.resize(0);
01169   }
01170   mSender->emitProgressInfo( mMessageOffset );
01171 }
01172 
01173 void KMSendSMTP::result(KIO::Job *_job)
01174 {
01175   if (!mJob) return;
01176   mJob = 0;
01177 
01178   if(_job->error())
01179   {
01180     mSendOk = false;
01181     if (_job->error() == KIO::ERR_SLAVE_DIED) mSlave = 0;
01182     failed(_job->errorString());
01183     abort();
01184   } else {
01185     emit idle();
01186   }
01187 }
01188 
01189 void KMSendSMTP::slaveError(KIO::Slave *aSlave, int error, const QString &errorMsg)
01190 {
01191   if (aSlave == mSlave)
01192   {
01193     if (error == KIO::ERR_SLAVE_DIED) mSlave = 0;
01194     mSendOk = false;
01195     mJob = 0;
01196     failed(KIO::buildErrorString(error, errorMsg));
01197     abort();
01198   }
01199 }
01200 
01201 #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 Mon Apr 4 06:44:24 2005 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003