kmail

kmsender.cpp

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