kmail

kmsender.cpp

00001 // kmsender.cpp
00002 
00003 #include <config.h>
00004 
00005 #define REALLY_WANT_KMSENDER
00006 #include "kmsender.h"
00007 #include "kmsender_p.h"
00008 #undef REALLY_WANT_KMSENDER
00009 
00010 #include <kmime_header_parsing.h>
00011 using namespace KMime::Types;
00012 
00013 #include <kio/passdlg.h>
00014 #include <kio/scheduler.h>
00015 #include <kapplication.h>
00016 #include <kmessagebox.h>
00017 #include <kdeversion.h>
00018 #include <klocale.h>
00019 #include <kdebug.h>
00020 #include <kconfig.h>
00021 
00022 #include <assert.h>
00023 #include <stdio.h>
00024 #include <unistd.h>
00025 #include <sys/types.h>
00026 #include <sys/stat.h>
00027 #include <sys/wait.h>
00028 #include "globalsettings.h"
00029 #include "kmfiltermgr.h"
00030 
00031 #include "kcursorsaver.h"
00032 #include <libkpimidentities/identity.h>
00033 #include <libkpimidentities/identitymanager.h>
00034 #include "progressmanager.h"
00035 #include "kmaccount.h"
00036 #include "kmtransport.h"
00037 #include "kmfolderindex.h"
00038 #include "kmfoldermgr.h"
00039 #include "kmmsgdict.h"
00040 #include "kmmsgpart.h"
00041 #include "protocols.h"
00042 #include "kmcommands.h"
00043 #include <mimelib/mediatyp.h>
00044 #include <mimelib/enum.h>
00045 #include <mimelib/param.h>
00046 
00047 #define SENDER_GROUP "sending mail"
00048 
00049 //-----------------------------------------------------------------------------
00050 KMSender::KMSender()
00051   : mOutboxFolder( 0 ), mSentFolder( 0 )
00052 {
00053   mPrecommand = 0;
00054   mSendProc = 0;
00055   mSendProcStarted = FALSE;
00056   mSendInProgress = FALSE;
00057   mCurrentMsg = 0;
00058   mTransportInfo = new KMTransportInfo();
00059   readConfig();
00060   mSendAborted = false;
00061   mSentMessages = 0;
00062   mTotalMessages = 0;
00063   mFailedMessages = 0;
00064   mSentBytes = 0;
00065   mTotalBytes = 0;
00066   mProgressItem = 0;
00067 }
00068 
00069 
00070 //-----------------------------------------------------------------------------
00071 KMSender::~KMSender()
00072 {
00073   writeConfig(FALSE);
00074   delete mSendProc;
00075   delete mPrecommand;
00076   delete mTransportInfo;
00077 }
00078 
00079 //-----------------------------------------------------------------------------
00080 void KMSender::setStatusMsg(const QString &msg)
00081 {
00082   if ( mProgressItem )
00083     mProgressItem->setStatus(msg);
00084 }
00085 
00086 //-----------------------------------------------------------------------------
00087 void KMSender::readConfig(void)
00088 {
00089   QString str;
00090   KConfigGroup config(KMKernel::config(), SENDER_GROUP);
00091 
00092   mSendImmediate = config.readBoolEntry("Immediate", TRUE);
00093   mSendQuotedPrintable = config.readBoolEntry("Quoted-Printable", TRUE);
00094 }
00095 
00096 
00097 //-----------------------------------------------------------------------------
00098 void KMSender::writeConfig(bool aWithSync)
00099 {
00100   KConfigGroup config(KMKernel::config(), SENDER_GROUP);
00101 
00102   config.writeEntry("Immediate", mSendImmediate);
00103   config.writeEntry("Quoted-Printable", mSendQuotedPrintable);
00104 
00105   if (aWithSync) config.sync();
00106 }
00107 
00108 
00109 //-----------------------------------------------------------------------------
00110 bool KMSender::settingsOk() const
00111 {
00112   if (KMTransportInfo::availableTransports().isEmpty())
00113   {
00114     KMessageBox::information(0,i18n("Please create an account for sending and try again."));
00115     return false;
00116   }
00117   return true;
00118 }
00119 
00120 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   kmkernel->outboxFolder()->open("outbox");
00165   const KMFolderCloser openOutbox( "outbox", kmkernel->outboxFolder() );
00166 
00167   aMsg->setStatus(KMMsgStatusQueued);
00168 
00169   if ( const int err = openOutbox.folder()->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 = openOutbox.folder()->count() - 1;
00187   KMMessage * const unencryptedMsg = aMsg->unencryptedMsg();
00188   openOutbox.folder()->unGetMsg( idx );
00189   KMMessage * const tempMsg = openOutbox.folder()->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       folder->open("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       folder->close("setstatus");
00872     } else {
00873       kdWarning(5006) << k_funcinfo << "Cannot update linked message, it could not be found!" << endl;
00874     }
00875   }
00876 }
00877 
00878 //=============================================================================
00879 //=============================================================================
00880 KMSendProc::KMSendProc( KMSender * sender )
00881   : QObject( 0 ),
00882     mSender( sender ),
00883     mLastErrorMessage(),
00884     mSendOk( false ),
00885     mSending( false )
00886 {
00887 }
00888 
00889 //-----------------------------------------------------------------------------
00890 void KMSendProc::reset()
00891 {
00892   mSending = FALSE;
00893   mSendOk = FALSE;
00894   mLastErrorMessage = QString::null;
00895 }
00896 
00897 //-----------------------------------------------------------------------------
00898 void KMSendProc::failed(const QString &aMsg)
00899 {
00900   mSending = FALSE;
00901   mSendOk = FALSE;
00902   mLastErrorMessage = aMsg;
00903 }
00904 
00905 //-----------------------------------------------------------------------------
00906 void KMSendProc::statusMsg(const QString& aMsg)
00907 {
00908   if (mSender) mSender->setStatusMsg(aMsg);
00909 }
00910 
00911 //=============================================================================
00912 //=============================================================================
00913 KMSendSendmail::KMSendSendmail( KMSender * sender )
00914   : KMSendProc( sender ),
00915     mMsgStr(),
00916     mMsgPos( 0 ),
00917     mMsgRest( 0 ),
00918     mMailerProc( 0 )
00919 {
00920 
00921 }
00922 
00923 KMSendSendmail::~KMSendSendmail() {
00924   delete mMailerProc; mMailerProc = 0;
00925 }
00926 
00927 bool KMSendSendmail::doStart() {
00928 
00929   if (mSender->transportInfo()->host.isEmpty())
00930   {
00931     const QString str = i18n("Please specify a mailer program in the settings.");
00932     const QString msg = i18n("Sending failed:\n%1\n"
00933                              "The message will stay in the 'outbox' folder and will be resent.\n"
00934                              "Please remove it from there if you do not want the message to "
00935                              "be resent.\n"
00936                              "The following transport protocol was used:\n  %2")
00937                         .arg(str + "\n")
00938                         .arg("sendmail://");
00939     KMessageBox::information(0,msg);
00940     return false;
00941   }
00942 
00943   if (!mMailerProc)
00944   {
00945     mMailerProc = new KProcess;
00946     assert(mMailerProc != 0);
00947     connect(mMailerProc,SIGNAL(processExited(KProcess*)),
00948         this, SLOT(sendmailExited(KProcess*)));
00949     connect(mMailerProc,SIGNAL(wroteStdin(KProcess*)),
00950         this, SLOT(wroteStdin(KProcess*)));
00951     connect(mMailerProc,SIGNAL(receivedStderr(KProcess*,char*,int)),
00952         this, SLOT(receivedStderr(KProcess*, char*, int)));
00953   }
00954   return true;
00955 }
00956 
00957 void KMSendSendmail::doFinish() {
00958   delete mMailerProc;
00959   mMailerProc = 0;
00960 }
00961 
00962 void KMSendSendmail::abort()
00963 {
00964   delete mMailerProc;
00965   mMailerProc = 0;
00966   mSendOk = false;
00967   mMsgStr = 0;
00968   idle();
00969 }
00970 
00971 bool KMSendSendmail::doSend( const QString & sender, const QStringList & to, const QStringList & cc, const QStringList & bcc, const QByteArray & message ) {
00972   mMailerProc->clearArguments();
00973   *mMailerProc << mSender->transportInfo()->host
00974                << "-i" << "-f" << sender
00975                << to << cc << bcc ;
00976 
00977   mMsgStr = message;
00978 
00979   if ( !mMailerProc->start( KProcess::NotifyOnExit, KProcess::All ) ) {
00980     KMessageBox::information( 0, i18n("Failed to execute mailer program %1")
00981                               .arg( mSender->transportInfo()->host ) );
00982     return false;
00983   }
00984   mMsgPos  = mMsgStr.data();
00985   mMsgRest = mMsgStr.size();
00986   wroteStdin( mMailerProc );
00987 
00988   return true;
00989 }
00990 
00991 
00992 void KMSendSendmail::wroteStdin(KProcess *proc)
00993 {
00994   char* str;
00995   int len;
00996 
00997   assert(proc!=0);
00998   Q_UNUSED( proc );
00999 
01000   str = mMsgPos;
01001   len = (mMsgRest>1024 ? 1024 : mMsgRest);
01002 
01003   if (len <= 0)
01004   {
01005     mMailerProc->closeStdin();
01006   }
01007   else
01008   {
01009     mMsgRest -= len;
01010     mMsgPos  += len;
01011     mMailerProc->writeStdin(str,len);
01012     // if code is added after writeStdin() KProcess probably initiates
01013     // a race condition.
01014   }
01015 }
01016 
01017 
01018 void KMSendSendmail::receivedStderr(KProcess *proc, char *buffer, int buflen)
01019 {
01020   assert(proc!=0);
01021   Q_UNUSED( proc );
01022   mLastErrorMessage.replace(mLastErrorMessage.length(), buflen, buffer);
01023 }
01024 
01025 
01026 void KMSendSendmail::sendmailExited(KProcess *proc)
01027 {
01028   assert(proc!=0);
01029   mSendOk = (proc->normalExit() && proc->exitStatus()==0);
01030   if (!mSendOk) failed(i18n("Sendmail exited abnormally."));
01031   mMsgStr = 0;
01032   emit idle();
01033 }
01034 
01035 
01036 
01037 //-----------------------------------------------------------------------------
01038 //=============================================================================
01039 //=============================================================================
01040 KMSendSMTP::KMSendSMTP(KMSender *sender)
01041   : KMSendProc(sender),
01042     mInProcess(false),
01043     mJob(0),
01044     mSlave(0)
01045 {
01046   KIO::Scheduler::connect(SIGNAL(slaveError(KIO::Slave *, int,
01047     const QString &)), this, SLOT(slaveError(KIO::Slave *, int,
01048     const QString &)));
01049 }
01050 
01051 KMSendSMTP::~KMSendSMTP()
01052 {
01053   if (mJob) mJob->kill();
01054 }
01055 
01056 bool KMSendSMTP::doSend( const QString & sender, const QStringList & to, const QStringList & cc, const QStringList & bcc, const QByteArray & message ) {
01057   QString query = "headers=0&from=";
01058   query += KURL::encode_string( sender );
01059 
01060   QStringList::ConstIterator it;
01061 
01062   for ( it = to.begin(); it != to.end(); ++it )
01063     query += "&to=" + KURL::encode_string(*it);
01064   for ( it = cc.begin(); it != cc.end(); ++it )
01065     query += "&cc=" + KURL::encode_string(*it);
01066   for ( it = bcc.begin(); it != bcc.end(); ++it )
01067     query += "&bcc=" + KURL::encode_string(*it);
01068 
01069   KMTransportInfo * ti = mSender->transportInfo();
01070 
01071   if ( ti->specifyHostname )
01072     query += "&hostname=" + KURL::encode_string( ti->localHostname );
01073 
01074   if ( !kmkernel->msgSender()->sendQuotedPrintable() )
01075     query += "&body=8bit";
01076 
01077   KURL destination;
01078 
01079   destination.setProtocol((ti->encryption == "SSL") ? SMTPS_PROTOCOL : SMTP_PROTOCOL);
01080   destination.setHost(ti->host);
01081   destination.setPort(ti->port.toUShort());
01082 
01083   if (ti->auth)
01084   {
01085     QMapIterator<QString,QString> tpc = mSender->mPasswdCache.find( ti->name );
01086     QString tpwd = ( tpc != mSender->mPasswdCache.end() )?(*tpc):QString::null;
01087 
01088     if ( ti->passwd().isEmpty() )
01089       ti->setPasswd( tpwd );
01090 
01091     if( (ti->user.isEmpty() || ti->passwd().isEmpty()) &&
01092       ti->authType != "GSSAPI" )
01093     {
01094       bool b = FALSE;
01095       int result;
01096 
01097       KCursorSaver idle(KBusyPtr::idle());
01098       QString passwd = ti->passwd();
01099       result = KIO::PasswordDialog::getNameAndPassword(ti->user, passwd,
01100     &b, i18n("You need to supply a username and a password to use this "
01101          "SMTP server."), FALSE, QString::null, ti->name, QString::null);
01102 
01103       if ( result != QDialog::Accepted )
01104       {
01105         abort();
01106         return FALSE;
01107       }
01108       if (int id = KMTransportInfo::findTransport(ti->name)) {
01109         ti->setPasswd( passwd );
01110         ti->writeConfig(id);
01111 
01112         // save the password into the cache
01113         mSender->mPasswdCache[ti->name] = passwd;
01114       }
01115     }
01116     destination.setUser(ti->user);
01117     destination.setPass(ti->passwd());
01118   }
01119 
01120   if (!mSlave || !mInProcess)
01121   {
01122     KIO::MetaData slaveConfig;
01123     slaveConfig.insert("tls", (ti->encryption == "TLS") ? "on" : "off");
01124     if (ti->auth) slaveConfig.insert("sasl", ti->authType);
01125     mSlave = KIO::Scheduler::getConnectedSlave(destination, slaveConfig);
01126   }
01127 
01128   if (!mSlave)
01129   {
01130     abort();
01131     return false;
01132   }
01133 
01134   // dotstuffing is now done by the slave (see setting of metadata)
01135   mMessage = message;
01136   mMessageLength = mMessage.size();
01137   mMessageOffset = 0;
01138 
01139   if ( mMessageLength )
01140     // allow +5% for subsequent LF->CRLF and dotstuffing (an average
01141     // over 2G-lines gives an average line length of 42-43):
01142     query += "&size=" + QString::number( qRound( mMessageLength * 1.05 ) );
01143 
01144   destination.setPath("/send");
01145   destination.setQuery( query );
01146 
01147   mJob = KIO::put( destination, -1, false, false, false );
01148   if ( !mJob ) {
01149     abort();
01150     return false;
01151   }
01152   mJob->addMetaData( "lf2crlf+dotstuff", "slave" );
01153   KIO::Scheduler::assignJobToSlave(mSlave, mJob);
01154   connect(mJob, SIGNAL(result(KIO::Job *)), this, SLOT(result(KIO::Job *)));
01155   connect(mJob, SIGNAL(dataReq(KIO::Job *, QByteArray &)),
01156           this, SLOT(dataReq(KIO::Job *, QByteArray &)));
01157   mSendOk = true;
01158   mInProcess = true;
01159   return true;
01160 }
01161 
01162 void KMSendSMTP::cleanup() {
01163   if(mJob)
01164   {
01165     mJob->kill(TRUE);
01166     mJob = 0;
01167     mSlave = 0;
01168   }
01169 
01170   if (mSlave)
01171   {
01172     KIO::Scheduler::disconnectSlave(mSlave);
01173     mSlave = 0;
01174   }
01175 
01176   mInProcess = false;
01177 }
01178 
01179 void KMSendSMTP::abort() {
01180   cleanup();
01181   emit idle();
01182 }
01183 
01184 void KMSendSMTP::doFinish() {
01185   cleanup();
01186 }
01187 
01188 void KMSendSMTP::dataReq(KIO::Job *, QByteArray &array)
01189 {
01190   // Send it by 32K chuncks
01191   const int chunkSize = QMIN( mMessageLength - mMessageOffset, 32*1024 );
01192   if ( chunkSize > 0 ) {
01193     array.duplicate(mMessage.data() + mMessageOffset, chunkSize);
01194     mMessageOffset += chunkSize;
01195   } else
01196   {
01197     array.resize(0);
01198     mMessage.resize(0);
01199   }
01200   mSender->emitProgressInfo( mMessageOffset );
01201 }
01202 
01203 void KMSendSMTP::result(KIO::Job *_job)
01204 {
01205   if (!mJob) return;
01206   mJob = 0;
01207 
01208   if(_job->error())
01209   {
01210     mSendOk = false;
01211     if (_job->error() == KIO::ERR_SLAVE_DIED) mSlave = 0;
01212     failed(_job->errorString());
01213     abort();
01214   } else {
01215     emit idle();
01216   }
01217 }
01218 
01219 void KMSendSMTP::slaveError(KIO::Slave *aSlave, int error, const QString &errorMsg)
01220 {
01221   if (aSlave == mSlave)
01222   {
01223     if (error == KIO::ERR_SLAVE_DIED) mSlave = 0;
01224     mSendOk = false;
01225     mJob = 0;
01226     failed(KIO::buildErrorString(error, errorMsg));
01227     abort();
01228   }
01229 }
01230 
01231 #include "kmsender.moc"
01232 #include "kmsender_p.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys