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   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     mCurrentMsg->updateInvitationState();
00321 
00322     const KPIM::Identity & id = kmkernel->identityManager()
00323       ->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
00324     if ( !mCurrentMsg->fcc().isEmpty() )
00325     {
00326       sentFolder = kmkernel->folderMgr()->findIdString( mCurrentMsg->fcc() );
00327       if ( sentFolder == 0 )
00328       // This is *NOT* supposed to be imapSentFolder!
00329         sentFolder =
00330           kmkernel->dimapFolderMgr()->findIdString( mCurrentMsg->fcc() );
00331       if ( sentFolder == 0 )
00332         imapSentFolder =
00333           kmkernel->imapFolderMgr()->findIdString( mCurrentMsg->fcc() );
00334     }
00335     // No, or no usable sentFolder, and no, or no usable imapSentFolder,
00336     // let's try the on in the identity
00337     if ( ( sentFolder == 0 || sentFolder->isReadOnly() )
00338       && ( imapSentFolder == 0 || imapSentFolder->isReadOnly() )
00339       && !id.fcc().isEmpty() )
00340     {
00341       sentFolder = kmkernel->folderMgr()->findIdString( id.fcc() );
00342       if ( sentFolder == 0 )
00343         // This is *NOT* supposed to be imapSentFolder!
00344         sentFolder = kmkernel->dimapFolderMgr()->findIdString( id.fcc() );
00345       if ( sentFolder == 0 )
00346         imapSentFolder = kmkernel->imapFolderMgr()->findIdString( id.fcc() );
00347     }
00348     if (imapSentFolder
00349         && ( imapSentFolder->noContent() || imapSentFolder->isReadOnly() ) )
00350         imapSentFolder = 0;
00351 
00352     if ( sentFolder == 0 || sentFolder->isReadOnly() )
00353       sentFolder = kmkernel->sentFolder();
00354 
00355     if ( sentFolder ) {
00356       if ( const int err = sentFolder->open("sentFolder") ) {
00357         Q_UNUSED( err );
00358         cleanup();
00359         return;
00360       }
00361     }
00362 
00363     // Disable the emitting of msgAdded signal, because the message is taken out of the
00364     // current folder (outbox) and re-added, to make filter actions changing the message
00365     // work. We don't want that to screw up message counts.
00366     if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( true );
00367     const int processResult = kmkernel->filterMgr()->process(mCurrentMsg,KMFilterMgr::Outbound);
00368     if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( false );
00369 
00370     // 0==processed ok, 1==no filter matched, 2==critical error, abort!
00371     switch (processResult) {
00372     case 2:
00373       perror("Critical error: Unable to process sent mail (out of space?)");
00374       KMessageBox::information(0, i18n("Critical error: "
00375                    "Unable to process sent mail (out of space?)"
00376                    "Moving failing message to \"sent-mail\" folder."));
00377       if ( sentFolder ) {
00378         sentFolder->moveMsg(mCurrentMsg);
00379         sentFolder->close("sentFolder");
00380       }
00381       cleanup();
00382       return;
00383     case 1:
00384       if ( sentFolder && sentFolder->moveMsg(mCurrentMsg) != 0 )
00385       {
00386         KMessageBox::error(0, i18n("Moving the sent message \"%1\" from the "
00387           "\"outbox\" to the \"sent-mail\" folder failed.\n"
00388           "Possible reasons are lack of disk space or write permission. "
00389           "Please try to fix the problem and move the message manually.")
00390           .arg(mCurrentMsg->subject()));
00391         cleanup();
00392         return;
00393       }
00394       if (imapSentFolder) {
00395         // Does proper folder refcounting and message locking
00396         KMCommand *command = new KMMoveCommand( imapSentFolder, mCurrentMsg );
00397         command->keepFolderOpen( sentFolder ); // will open it, and close it once done
00398         command->start();
00399       }
00400     default:
00401       break;
00402     }
00403     setStatusByLink( mCurrentMsg );
00404     if (mCurrentMsg->parent() && !imapSentFolder) {
00405       // for speed optimization, this code assumes that mCurrentMsg is the
00406       // last one in it's parent folder; make sure that's really the case:
00407       assert( mCurrentMsg->parent()->find( mCurrentMsg )
00408               == mCurrentMsg->parent()->count() - 1 );
00409        // unGet this message:
00410       mCurrentMsg->parent()->unGetMsg( mCurrentMsg->parent()->count() -1 );
00411     }
00412 
00413     mCurrentMsg = 0;
00414   }
00415 
00416   // See if there is another queued message
00417   mCurrentMsg = mOutboxFolder->getMsg(mFailedMessages);
00418   if ( mCurrentMsg && !mCurrentMsg->transferInProgress() &&
00419        mCurrentMsg->sender().isEmpty() ) {
00420     // if we do not have a sender address then use the email address of the
00421     // message's identity or of the default identity unless those two are also
00422     // empty
00423     const KPIM::Identity & id = kmkernel->identityManager()
00424       ->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
00425     if ( !id.emailAddr().isEmpty() ) {
00426       mCurrentMsg->setFrom( id.fullEmailAddr() );
00427     }
00428     else if ( !kmkernel->identityManager()->defaultIdentity().emailAddr().isEmpty() ) {
00429       mCurrentMsg->setFrom( kmkernel->identityManager()->defaultIdentity().fullEmailAddr() );
00430     }
00431     else {
00432       KMessageBox::sorry( 0, i18n( "It's not possible to send messages "
00433                                    "without specifying a sender address.\n"
00434                                    "Please set the email address of "
00435                                    "identity '%1' in the Identities "
00436                                    "section of the configuration dialog "
00437                                    "and then try again." )
00438                              .arg( id.identityName() ) );
00439       mOutboxFolder->unGetMsg( mFailedMessages );
00440       mCurrentMsg = 0;
00441     }
00442   }
00443   if (!mCurrentMsg || mCurrentMsg->transferInProgress())
00444   {
00445     // a message is locked finish the send
00446     if (mCurrentMsg && mCurrentMsg->transferInProgress())
00447         mCurrentMsg = 0;
00448     // no more message: cleanup and done
00449     if ( sentFolder != 0 )
00450         sentFolder->close("sentFolder");
00451     if ( someSent ) {
00452       if ( mSentMessages == mTotalMessages ) {
00453         setStatusMsg(i18n("%n queued message successfully sent.",
00454                           "%n queued messages successfully sent.",
00455                           mSentMessages));
00456       } else {
00457         setStatusMsg(i18n("%1 of %2 queued messages successfully sent.")
00458             .arg(mSentMessages).arg( mTotalMessages ));
00459       }
00460     }
00461     cleanup();
00462     return;
00463   }
00464   mCurrentMsg->setTransferInProgress( true );
00465 
00466   // start the sender process or initialize communication
00467   if (!mSendInProgress)
00468   {
00469     Q_ASSERT( !mProgressItem );
00470     mProgressItem = KPIM::ProgressManager::createProgressItem(
00471       "Sender",
00472       i18n( "Sending messages" ),
00473       i18n("Initiating sender process..."),
00474       true );
00475     connect( mProgressItem, SIGNAL(progressItemCanceled(KPIM::ProgressItem*)),
00476              this, SLOT( slotAbortSend() ) );
00477     kapp->ref();
00478     mSendInProgress = true;
00479   }
00480 
00481   QString msgTransport = mCustomTransport;
00482   if ( msgTransport.isEmpty() ) {
00483     msgTransport = mCurrentMsg->headerField("X-KMail-Transport");
00484   }
00485   if ( msgTransport.isEmpty() ) {
00486     const QStringList sl = KMTransportInfo::availableTransports();
00487     if (!sl.empty()) msgTransport = sl.front();
00488   }
00489 
00490   if (!mSendProc || msgTransport != mMethodStr) {
00491     if (mSendProcStarted && mSendProc) {
00492       mSendProc->finish();
00493       mSendProcStarted = false;
00494     }
00495 
00496     mSendProc = createSendProcFromString(msgTransport);
00497     mMethodStr = msgTransport;
00498 
00499     if( mTransportInfo->encryption == "TLS" || mTransportInfo->encryption == "SSL" ) {
00500       mProgressItem->setUsesCrypto( true );
00501     } else if ( !mCustomTransport.isEmpty() ) {
00502         int result = KMessageBox::warningContinueCancel( 0,
00503         i18n( "You have chosen to send all queued email using an unencrypted transport, do you want to continue? "),
00504         i18n( "Security Warning" ),
00505         i18n( "Send Unencrypted" ),
00506         "useCustomTransportWithoutAsking", false);
00507 
00508       if( result == KMessageBox::Cancel ) {
00509         mProgressItem->cancel();
00510         mProgressItem->setComplete();
00511         slotAbortSend();
00512         cleanup();
00513         return;
00514       }
00515     }
00516 
00517     if (!mSendProc)
00518       sendProcStarted(false);
00519     else {
00520       connect(mSendProc, SIGNAL(idle()), SLOT(slotIdle()));
00521       connect(mSendProc, SIGNAL(started(bool)), SLOT(sendProcStarted(bool)));
00522 
00523       // Run the precommand if there is one
00524       if ( !mTransportInfo->precommand.isEmpty() ) {
00525         runPrecommand( mTransportInfo->precommand );
00526         return;
00527       }
00528 
00529       mSendProc->start();
00530     }
00531   }
00532   else if (!mSendProcStarted)
00533     mSendProc->start();
00534   else
00535     doSendMsgAux();
00536 }
00537 
00538 bool KMSender::runPrecommand( const QString & cmd ) {
00539   setStatusMsg( i18n("Executing precommand %1").arg( cmd ) );
00540   mPrecommand = new KMPrecommand( cmd );
00541   connect( mPrecommand, SIGNAL(finished(bool)),
00542            SLOT(slotPrecommandFinished(bool)) );
00543   if ( !mPrecommand->start() ) {
00544     delete mPrecommand; mPrecommand = 0;
00545     return false;
00546   }
00547   return true;
00548 }
00549 
00550 //-----------------------------------------------------------------------------
00551 void KMSender::sendProcStarted(bool success)
00552 {
00553   if (!success) {
00554     if (mSendProc)
00555        mSendProc->finish();
00556     else
00557       setStatusMsg(i18n("Unrecognized transport protocol. Unable to send message."));
00558     mSendProc = 0;
00559     mSendProcStarted = false;
00560     cleanup();
00561     return;
00562   }
00563   doSendMsgAux();
00564 }
00565 
00566 
00567 static QStringList addrSpecListToStringList( const AddrSpecList & l, bool allowEmpty=false ) {
00568   QStringList result;
00569   for ( AddrSpecList::const_iterator it = l.begin(), end = l.end() ; it != end ; ++it ) {
00570     const QString s = (*it).asString();
00571     if ( allowEmpty || !s.isEmpty() )
00572       result.push_back( s );
00573   }
00574   return result;
00575 }
00576 
00577 static void extractSenderToCCAndBcc( KMMessage * aMsg, QString * sender, QStringList * to, QStringList * cc, QStringList * bcc ) {
00578   if ( sender ) *sender = aMsg->sender();
00579   if( !aMsg->headerField("X-KMail-Recipients").isEmpty() ) {
00580     // extended BCC handling to prevent TOs and CCs from seeing
00581     // BBC information by looking at source of an OpenPGP encrypted mail
00582     if ( to ) *to = addrSpecListToStringList( aMsg->extractAddrSpecs( "X-KMail-Recipients" ) );
00583     aMsg->removeHeaderField( "X-KMail-Recipients" );
00584   } else {
00585     if ( to ) *to = addrSpecListToStringList( aMsg->extractAddrSpecs( "To" ) );
00586     if ( cc ) *cc = addrSpecListToStringList( aMsg->extractAddrSpecs( "Cc" ) );
00587     if ( bcc ) *bcc = addrSpecListToStringList( aMsg->extractAddrSpecs( "Bcc" ) );
00588   }
00589 }
00590 
00591 //-----------------------------------------------------------------------------
00592 void KMSender::doSendMsgAux()
00593 {
00594   mSendProcStarted = true;
00595 
00596   // start sending the current message
00597 
00598   setStatusMsg(i18n("%3: subject of message","Sending message %1 of %2: %3")
00599            .arg(mSentMessages+mFailedMessages+1).arg(mTotalMessages)
00600            .arg(mCurrentMsg->subject()));
00601   QStringList to, cc, bcc;
00602   QString sender;
00603   extractSenderToCCAndBcc( mCurrentMsg, &sender, &to, &cc, &bcc );
00604 
00605   // MDNs are required to have an empty envelope from as per RFC2298.
00606   if ( messageIsDispositionNotificationReport( mCurrentMsg ) && GlobalSettings::self()->sendMDNsWithEmptySender() )
00607     sender = "<>";
00608 
00609   const QByteArray message = mCurrentMsg->asSendableString();
00610   if ( sender.isEmpty() || !mSendProc->send( sender, to, cc, bcc, message ) ) {
00611     if ( mCurrentMsg )
00612       mCurrentMsg->setTransferInProgress( false );
00613     if ( mOutboxFolder )
00614       mOutboxFolder->unGetMsg( mFailedMessages );
00615     mCurrentMsg = 0;
00616     cleanup();
00617     setStatusMsg(i18n("Failed to send (some) queued messages."));
00618     return;
00619   }
00620   // Do *not* add code here, after send(). It can happen that this method
00621   // is called recursively if send() emits the idle signal directly.
00622 }
00623 
00624 
00625 //-----------------------------------------------------------------------------
00626 void KMSender::cleanup(void)
00627 {
00628   kdDebug(5006) << k_funcinfo << endl;
00629   if (mSendProc && mSendProcStarted) mSendProc->finish();
00630   mSendProc = 0;
00631   mSendProcStarted = false;
00632   if (mSendInProgress) kapp->deref();
00633   mSendInProgress = false;
00634   if (mCurrentMsg)
00635   {
00636     mCurrentMsg->setTransferInProgress( false );
00637     mCurrentMsg = 0;
00638   }
00639   if ( mSentFolder ) {
00640     mSentFolder->close("dosendsent");
00641     mSentFolder = 0;
00642   }
00643   if ( mOutboxFolder ) {
00644     disconnect( mOutboxFolder, SIGNAL(msgAdded(int)),
00645                 this, SLOT(outboxMsgAdded(int)) );
00646     mOutboxFolder->close("dosendoutbox");
00647     if ( mOutboxFolder->count( true ) == 0 ) {
00648       mOutboxFolder->expunge();
00649     }
00650     else if ( mOutboxFolder->needsCompacting() ) {
00651       mOutboxFolder->compact( KMFolder::CompactSilentlyNow );
00652     }
00653     mOutboxFolder = 0;
00654   }
00655 
00656   mSendAborted = false;
00657   mSentMessages = 0;
00658   mFailedMessages = 0;
00659   mSentBytes = 0;
00660   if ( mProgressItem )
00661     mProgressItem->setComplete();
00662   mProgressItem = 0;
00663   kmkernel->filterMgr()->deref();
00664 }
00665 
00666 
00667 //-----------------------------------------------------------------------------
00668 void KMSender::slotAbortSend()
00669 {
00670   mSendAborted = true;
00671   delete mPrecommand;
00672   mPrecommand = 0;
00673   if (mSendProc) mSendProc->abort();
00674 }
00675 
00676 //-----------------------------------------------------------------------------
00677 void KMSender::slotIdle()
00678 {
00679   assert(mSendProc != 0);
00680 
00681   QString msg;
00682   QString errString;
00683   if (mSendProc)
00684       errString = mSendProc->lastErrorMessage();
00685 
00686   if (mSendAborted) {
00687     // sending of message aborted
00688     if ( mCurrentMsg ) {
00689       mCurrentMsg->setTransferInProgress( false );
00690       if ( mOutboxFolder )
00691         mOutboxFolder->unGetMsg( mFailedMessages );
00692       mCurrentMsg = 0;
00693     }
00694     msg = i18n("Sending aborted:\n%1\n"
00695         "The message will stay in the 'outbox' folder until you either "
00696         "fix the problem (e.g. a broken address) or remove the message "
00697         "from the 'outbox' folder.\n"
00698         "The following transport protocol was used:\n  %2")
00699       .arg(errString)
00700       .arg(mMethodStr);
00701     if (!errString.isEmpty()) KMessageBox::error(0,msg);
00702     setStatusMsg( i18n( "Sending aborted." ) );
00703   } else {
00704     if (!mSendProc->sendOk()) {
00705       if ( mCurrentMsg )
00706         mCurrentMsg->setTransferInProgress( false );
00707       if ( mOutboxFolder )
00708         mOutboxFolder->unGetMsg( mFailedMessages );
00709       mCurrentMsg = 0;
00710       mFailedMessages++;
00711       // reset cached password
00712       QMapIterator <QString,QString> pc;
00713       if ( (pc = mPasswdCache.find( mMethodStr )) != mPasswdCache.end() ) {
00714         mPasswdCache.erase(pc);
00715       }
00716       // Sending of message failed.
00717       if (!errString.isEmpty()) {
00718         int res = KMessageBox::Yes;
00719         if (mSentMessages+mFailedMessages != mTotalMessages) {
00720           msg = i18n("<p>Sending failed:</p>"
00721             "<p>%1</p>"
00722             "<p>The message will stay in the 'outbox' folder until you either "
00723             "fix the problem (e.g. a broken address) or remove the message "
00724             "from the 'outbox' folder.</p>"
00725             "<p>The following transport protocol was used:  %2</p>"
00726             "<p>Do you want me to continue sending the remaining messages?</p>")
00727             .arg(errString)
00728             .arg(mMethodStr);
00729           res = KMessageBox::warningYesNo( 0 , msg ,
00730                   i18n( "Continue Sending" ), i18n( "&Continue Sending" ),
00731                   i18n("&Abort Sending") );
00732         } else {
00733           msg = i18n("Sending failed:\n%1\n"
00734             "The message will stay in the 'outbox' folder until you either "
00735             "fix the problem (e.g. a broken address) or remove the message "
00736             "from the 'outbox' folder.\n"
00737             "The following transport protocol was used:\n %2")
00738             .arg(errString)
00739             .arg(mMethodStr);
00740           KMessageBox::error(0,msg);
00741         }
00742         if (res == KMessageBox::Yes) {
00743           // Try the next one.
00744           doSendMsg();
00745           return;
00746         } else {
00747           setStatusMsg( i18n( "Sending aborted." ) );
00748         }
00749       }
00750     } else {
00751       // Sending suceeded.
00752       doSendMsg();
00753       return;
00754     }
00755   }
00756   mSendProc->finish();
00757   mSendProc = 0;
00758   mSendProcStarted = false;
00759 
00760   cleanup();
00761 }
00762 
00763 
00764 //-----------------------------------------------------------------------------
00765 void KMSender::slotPrecommandFinished(bool normalExit)
00766 {
00767   delete mPrecommand;
00768   mPrecommand = 0;
00769   if (normalExit) mSendProc->start();
00770   else slotIdle();
00771 }
00772 
00773 
00774 //-----------------------------------------------------------------------------
00775 void KMSender::setSendImmediate(bool aSendImmediate)
00776 {
00777   mSendImmediate = aSendImmediate;
00778 }
00779 
00780 
00781 //-----------------------------------------------------------------------------
00782 void KMSender::setSendQuotedPrintable(bool aSendQuotedPrintable)
00783 {
00784   mSendQuotedPrintable = aSendQuotedPrintable;
00785 }
00786 
00787 
00788 //-----------------------------------------------------------------------------
00789 KMSendProc* KMSender::createSendProcFromString( const QString & transport )
00790 {
00791   mTransportInfo->type = QString::null;
00792   int nr = KMTransportInfo::findTransport(transport);
00793   if (nr)
00794   {
00795     mTransportInfo->readConfig(nr);
00796   } else {
00797     if (transport.startsWith("smtp://")) // should probably use KURL and SMTP_PROTOCOL
00798     {
00799       mTransportInfo->type = "smtp";
00800       mTransportInfo->auth = false;
00801       mTransportInfo->encryption = "NONE";
00802       QString serverport = transport.mid(7);
00803       int colon = serverport.find(':');
00804       if (colon != -1) {
00805         mTransportInfo->host = serverport.left(colon);
00806         mTransportInfo->port = serverport.mid(colon + 1);
00807       } else {
00808         mTransportInfo->host = serverport;
00809         mTransportInfo->port = "25";
00810       }
00811     } else
00812     if (transport.startsWith("smtps://"))  // should probably use KURL and SMTPS_PROTOCOL
00813     {
00814       mTransportInfo->type = "smtps";
00815       mTransportInfo->auth = false;
00816       mTransportInfo->encryption = "ssl";
00817       QString serverport = transport.mid(7);
00818       int colon = serverport.find(':');
00819       if (colon != -1) {
00820         mTransportInfo->host = serverport.left(colon);
00821         mTransportInfo->port = serverport.mid(colon + 1);
00822       } else {
00823         mTransportInfo->host = serverport;
00824         mTransportInfo->port = "465";
00825       }
00826     }
00827     else if (transport.startsWith("file://"))
00828     {
00829       mTransportInfo->type = "sendmail";
00830       mTransportInfo->host = transport.mid(7);
00831     }
00832   }
00833   // strip off a trailing "/"
00834   while (mTransportInfo->host.endsWith("/")) {
00835     mTransportInfo->host.truncate(mTransportInfo->host.length()-1);
00836   }
00837 
00838 
00839   if (mTransportInfo->type == "sendmail")
00840     return new KMSendSendmail(this);
00841   if (mTransportInfo->type == "smtp" || mTransportInfo->type == "smtps")
00842     return new KMSendSMTP(this);
00843 
00844   return 0L;
00845 }
00846 
00847 //-----------------------------------------------------------------------------
00848 void KMSender::setStatusByLink(const KMMessage *aMsg)
00849 {
00850   int n = 0;
00851   while (1) {
00852     ulong msn;
00853     KMMsgStatus status;
00854     aMsg->getLink(n, &msn, &status);
00855     if (!msn || !status)
00856       break;
00857     n++;
00858 
00859     KMFolder *folder = 0;
00860     int index = -1;
00861     KMMsgDict::instance()->getLocation(msn, &folder, &index);
00862     if (folder && index != -1) {
00863       KMFolderOpener openFolder(folder, "setstatus");
00864       if ( status == KMMsgStatusDeleted ) {
00865         // Move the message to the trash folder
00866         KMDeleteMsgCommand *cmd =
00867           new KMDeleteMsgCommand( folder, folder->getMsg( index ) );
00868         cmd->start();
00869       } else {
00870         folder->setStatus(index, status);
00871       }
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