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();
00165   const KMFolderCloser openOutbox( 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();
00223   mTotalMessages = mOutboxFolder->count();
00224   if (mTotalMessages == 0) {
00225     // Nothing in the outbox. We are done.
00226     mOutboxFolder->close();
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();
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() ) {
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();
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();
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 QCString 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();
00640     mSentFolder = 0;
00641   }
00642   if ( mOutboxFolder ) {
00643     disconnect( mOutboxFolder, SIGNAL(msgAdded(int)),
00644                 this, SLOT(outboxMsgAdded(int)) );
00645     mOutboxFolder->close();
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       // Sending of message failed.
00711       if (!errString.isEmpty()) {
00712         int res = KMessageBox::Yes;
00713         if (mSentMessages+mFailedMessages != mTotalMessages) {
00714           msg = i18n("<p>Sending failed:</p>"
00715             "<p>%1</p>"
00716             "<p>The message will stay in the 'outbox' folder until you either "
00717             "fix the problem (e.g. a broken address) or remove the message "
00718             "from the 'outbox' folder.</p>"
00719             "<p>The following transport protocol was used:  %2</p>"
00720             "<p>Do you want me to continue sending the remaining messages?</p>")
00721             .arg(errString)
00722             .arg(mMethodStr);
00723           res = KMessageBox::warningYesNo( 0 , msg ,
00724                   i18n( "Continue Sending" ), i18n( "&Continue Sending" ),
00725                   i18n("&Abort Sending") );
00726         } else {
00727           msg = i18n("Sending failed:\n%1\n"
00728             "The message will stay in the 'outbox' folder until you either "
00729             "fix the problem (e.g. a broken address) or remove the message "
00730             "from the 'outbox' folder.\n"
00731             "The following transport protocol was used:\n %2")
00732             .arg(errString)
00733             .arg(mMethodStr);
00734           KMessageBox::error(0,msg);
00735         }
00736         if (res == KMessageBox::Yes) {
00737           // Try the next one.
00738           doSendMsg();
00739           return;
00740         } else {
00741           setStatusMsg( i18n( "Sending aborted." ) );
00742         }
00743       }
00744     } else {
00745       // Sending suceeded.
00746       doSendMsg();
00747       return;
00748     }
00749   }
00750   mSendProc->finish();
00751   mSendProc = 0;
00752   mSendProcStarted = false;
00753 
00754   cleanup();
00755 }
00756 
00757 
00758 //-----------------------------------------------------------------------------
00759 void KMSender::slotPrecommandFinished(bool normalExit)
00760 {
00761   delete mPrecommand;
00762   mPrecommand = 0;
00763   if (normalExit) mSendProc->start();
00764   else slotIdle();
00765 }
00766 
00767 
00768 //-----------------------------------------------------------------------------
00769 void KMSender::setSendImmediate(bool aSendImmediate)
00770 {
00771   mSendImmediate = aSendImmediate;
00772 }
00773 
00774 
00775 //-----------------------------------------------------------------------------
00776 void KMSender::setSendQuotedPrintable(bool aSendQuotedPrintable)
00777 {
00778   mSendQuotedPrintable = aSendQuotedPrintable;
00779 }
00780 
00781 
00782 //-----------------------------------------------------------------------------
00783 KMSendProc* KMSender::createSendProcFromString( const QString & transport )
00784 {
00785   mTransportInfo->type = QString::null;
00786   int nr = KMTransportInfo::findTransport(transport);
00787   if (nr)
00788   {
00789     mTransportInfo->readConfig(nr);
00790   } else {
00791     if (transport.startsWith("smtp://")) // should probably use KURL and SMTP_PROTOCOL
00792     {
00793       mTransportInfo->type = "smtp";
00794       mTransportInfo->auth = FALSE;
00795       mTransportInfo->encryption = "NONE";
00796       QString serverport = transport.mid(7);
00797       int colon = serverport.find(':');
00798       if (colon != -1) {
00799         mTransportInfo->host = serverport.left(colon);
00800         mTransportInfo->port = serverport.mid(colon + 1);
00801       } else {
00802         mTransportInfo->host = serverport;
00803         mTransportInfo->port = "25";
00804       }
00805     } else
00806     if (transport.startsWith("smtps://"))  // should probably use KURL and SMTPS_PROTOCOL
00807     {
00808       mTransportInfo->type = "smtps";
00809       mTransportInfo->auth = FALSE;
00810       mTransportInfo->encryption = "ssl";
00811       QString serverport = transport.mid(7);
00812       int colon = serverport.find(':');
00813       if (colon != -1) {
00814         mTransportInfo->host = serverport.left(colon);
00815         mTransportInfo->port = serverport.mid(colon + 1);
00816       } else {
00817         mTransportInfo->host = serverport;
00818         mTransportInfo->port = "465";
00819       }
00820     }
00821     else if (transport.startsWith("file://"))
00822     {
00823       mTransportInfo->type = "sendmail";
00824       mTransportInfo->host = transport.mid(7);
00825     }
00826   }
00827   // strip off a trailing "/"
00828   while (mTransportInfo->host.endsWith("/")) {
00829     mTransportInfo->host.truncate(mTransportInfo->host.length()-1);
00830   }
00831 
00832 
00833   if (mTransportInfo->type == "sendmail")
00834     return new KMSendSendmail(this);
00835   if (mTransportInfo->type == "smtp" || mTransportInfo->type == "smtps")
00836     return new KMSendSMTP(this);
00837 
00838   return 0L;
00839 }
00840 
00841 //-----------------------------------------------------------------------------
00842 void KMSender::setStatusByLink(const KMMessage *aMsg)
00843 {
00844   int n = 0;
00845   while (1) {
00846     ulong msn;
00847     KMMsgStatus status;
00848     aMsg->getLink(n, &msn, &status);
00849     if (!msn || !status)
00850       break;
00851     n++;
00852 
00853     KMFolder *folder = 0;
00854     int index = -1;
00855     KMMsgDict::instance()->getLocation(msn, &folder, &index);
00856     if (folder && index != -1) {
00857       folder->open();
00858       if ( status == KMMsgStatusDeleted ) {
00859         // Move the message to the trash folder
00860         KMDeleteMsgCommand *cmd =
00861           new KMDeleteMsgCommand( folder, folder->getMsg( index ) );
00862         cmd->start();
00863       } else {
00864         folder->setStatus(index, status);
00865       }
00866       folder->close();
00867     } else {
00868       kdWarning(5006) << k_funcinfo << "Cannot update linked message, it could not be found!" << endl;
00869     }
00870   }
00871 }
00872 
00873 //=============================================================================
00874 //=============================================================================
00875 KMSendProc::KMSendProc( KMSender * sender )
00876   : QObject( 0 ),
00877     mSender( sender ),
00878     mLastErrorMessage(),
00879     mSendOk( false ),
00880     mSending( false )
00881 {
00882 }
00883 
00884 //-----------------------------------------------------------------------------
00885 void KMSendProc::reset()
00886 {
00887   mSending = FALSE;
00888   mSendOk = FALSE;
00889   mLastErrorMessage = QString::null;
00890 }
00891 
00892 //-----------------------------------------------------------------------------
00893 void KMSendProc::failed(const QString &aMsg)
00894 {
00895   mSending = FALSE;
00896   mSendOk = FALSE;
00897   mLastErrorMessage = aMsg;
00898 }
00899 
00900 //-----------------------------------------------------------------------------
00901 void KMSendProc::statusMsg(const QString& aMsg)
00902 {
00903   if (mSender) mSender->setStatusMsg(aMsg);
00904 }
00905 
00906 //=============================================================================
00907 //=============================================================================
00908 KMSendSendmail::KMSendSendmail( KMSender * sender )
00909   : KMSendProc( sender ),
00910     mMsgStr(),
00911     mMsgPos( 0 ),
00912     mMsgRest( 0 ),
00913     mMailerProc( 0 )
00914 {
00915 
00916 }
00917 
00918 KMSendSendmail::~KMSendSendmail() {
00919   delete mMailerProc; mMailerProc = 0;
00920 }
00921 
00922 bool KMSendSendmail::doStart() {
00923 
00924   if (mSender->transportInfo()->host.isEmpty())
00925   {
00926     const QString str = i18n("Please specify a mailer program in the settings.");
00927     const QString msg = i18n("Sending failed:\n%1\n"
00928                              "The message will stay in the 'outbox' folder and will be resent.\n"
00929                              "Please remove it from there if you do not want the message to "
00930                              "be resent.\n"
00931                              "The following transport protocol was used:\n  %2")
00932                         .arg(str + "\n")
00933                         .arg("sendmail://");
00934     KMessageBox::information(0,msg);
00935     return false;
00936   }
00937 
00938   if (!mMailerProc)
00939   {
00940     mMailerProc = new KProcess;
00941     assert(mMailerProc != 0);
00942     connect(mMailerProc,SIGNAL(processExited(KProcess*)),
00943         this, SLOT(sendmailExited(KProcess*)));
00944     connect(mMailerProc,SIGNAL(wroteStdin(KProcess*)),
00945         this, SLOT(wroteStdin(KProcess*)));
00946     connect(mMailerProc,SIGNAL(receivedStderr(KProcess*,char*,int)),
00947         this, SLOT(receivedStderr(KProcess*, char*, int)));
00948   }
00949   return true;
00950 }
00951 
00952 void KMSendSendmail::doFinish() {
00953   delete mMailerProc;
00954   mMailerProc = 0;
00955 }
00956 
00957 void KMSendSendmail::abort()
00958 {
00959   delete mMailerProc;
00960   mMailerProc = 0;
00961   mSendOk = false;
00962   mMsgStr = 0;
00963   idle();
00964 }
00965 
00966 bool KMSendSendmail::doSend( const QString & sender, const QStringList & to, const QStringList & cc, const QStringList & bcc, const QCString & message ) {
00967   mMailerProc->clearArguments();
00968   *mMailerProc << mSender->transportInfo()->host
00969                << "-i" << "-f" << sender
00970                << to << cc << bcc ;
00971 
00972   mMsgStr = message;
00973 
00974   if ( !mMailerProc->start( KProcess::NotifyOnExit, KProcess::All ) ) {
00975     KMessageBox::information( 0, i18n("Failed to execute mailer program %1")
00976                               .arg( mSender->transportInfo()->host ) );
00977     return false;
00978   }
00979   mMsgPos  = mMsgStr.data();
00980   mMsgRest = mMsgStr.length();
00981   wroteStdin( mMailerProc );
00982 
00983   return true;
00984 }
00985 
00986 
00987 void KMSendSendmail::wroteStdin(KProcess *proc)
00988 {
00989   char* str;
00990   int len;
00991 
00992   assert(proc!=0);
00993   Q_UNUSED( proc );
00994 
00995   str = mMsgPos;
00996   len = (mMsgRest>1024 ? 1024 : mMsgRest);
00997 
00998   if (len <= 0)
00999   {
01000     mMailerProc->closeStdin();
01001   }
01002   else
01003   {
01004     mMsgRest -= len;
01005     mMsgPos  += len;
01006     mMailerProc->writeStdin(str,len);
01007     // if code is added after writeStdin() KProcess probably initiates
01008     // a race condition.
01009   }
01010 }
01011 
01012 
01013 void KMSendSendmail::receivedStderr(KProcess *proc, char *buffer, int buflen)
01014 {
01015   assert(proc!=0);
01016   Q_UNUSED( proc );
01017   mLastErrorMessage.replace(mLastErrorMessage.length(), buflen, buffer);
01018 }
01019 
01020 
01021 void KMSendSendmail::sendmailExited(KProcess *proc)
01022 {
01023   assert(proc!=0);
01024   mSendOk = (proc->normalExit() && proc->exitStatus()==0);
01025   if (!mSendOk) failed(i18n("Sendmail exited abnormally."));
01026   mMsgStr = 0;
01027   emit idle();
01028 }
01029 
01030 
01031 
01032 //-----------------------------------------------------------------------------
01033 //=============================================================================
01034 //=============================================================================
01035 KMSendSMTP::KMSendSMTP(KMSender *sender)
01036   : KMSendProc(sender),
01037     mInProcess(false),
01038     mJob(0),
01039     mSlave(0)
01040 {
01041   KIO::Scheduler::connect(SIGNAL(slaveError(KIO::Slave *, int,
01042     const QString &)), this, SLOT(slaveError(KIO::Slave *, int,
01043     const QString &)));
01044 }
01045 
01046 KMSendSMTP::~KMSendSMTP()
01047 {
01048   if (mJob) mJob->kill();
01049 }
01050 
01051 bool KMSendSMTP::doSend( const QString & sender, const QStringList & to, const QStringList & cc, const QStringList & bcc, const QCString & message ) {
01052   QString query = "headers=0&from=";
01053   query += KURL::encode_string( sender );
01054 
01055   QStringList::ConstIterator it;
01056 
01057   for ( it = to.begin(); it != to.end(); ++it )
01058     query += "&to=" + KURL::encode_string(*it);
01059   for ( it = cc.begin(); it != cc.end(); ++it )
01060     query += "&cc=" + KURL::encode_string(*it);
01061   for ( it = bcc.begin(); it != bcc.end(); ++it )
01062     query += "&bcc=" + KURL::encode_string(*it);
01063   KMTransportInfo * ti = mSender->transportInfo();
01064 
01065   if ( ti->specifyHostname )
01066     query += "&hostname=" + KURL::encode_string( ti->localHostname );
01067 
01068   if ( !kmkernel->msgSender()->sendQuotedPrintable() )
01069     query += "&body=8bit";
01070 
01071   KURL destination;
01072 
01073   destination.setProtocol((ti->encryption == "SSL") ? SMTPS_PROTOCOL : SMTP_PROTOCOL);
01074   destination.setHost(ti->host);
01075   destination.setPort(ti->port.toUShort());
01076 
01077   if (ti->auth)
01078   {
01079     if( (ti->user.isEmpty() || ti->passwd().isEmpty()) &&
01080       ti->authType != "GSSAPI" )
01081     {
01082       bool b = FALSE;
01083       int result;
01084 
01085       KCursorSaver idle(KBusyPtr::idle());
01086       QString passwd = ti->passwd();
01087       result = KIO::PasswordDialog::getNameAndPassword(ti->user, passwd,
01088     &b, i18n("You need to supply a username and a password to use this "
01089          "SMTP server."), FALSE, QString::null, ti->name, QString::null);
01090 
01091       if ( result != QDialog::Accepted )
01092       {
01093         abort();
01094         return FALSE;
01095       }
01096       if (int id = KMTransportInfo::findTransport(ti->name)) {
01097         ti->setPasswd( passwd );
01098         ti->writeConfig(id);
01099       }
01100     }
01101     destination.setUser(ti->user);
01102     destination.setPass(ti->passwd());
01103   }
01104 
01105   if (!mSlave || !mInProcess)
01106   {
01107     KIO::MetaData slaveConfig;
01108     slaveConfig.insert("tls", (ti->encryption == "TLS") ? "on" : "off");
01109     if (ti->auth) slaveConfig.insert("sasl", ti->authType);
01110     mSlave = KIO::Scheduler::getConnectedSlave(destination, slaveConfig);
01111   }
01112 
01113   if (!mSlave)
01114   {
01115     abort();
01116     return false;
01117   }
01118 
01119   // dotstuffing is now done by the slave (see setting of metadata)
01120   mMessage = message;
01121   mMessageLength = mMessage.length();
01122   mMessageOffset = 0;
01123 
01124   if ( mMessageLength )
01125     // allow +5% for subsequent LF->CRLF and dotstuffing (an average
01126     // over 2G-lines gives an average line length of 42-43):
01127     query += "&size=" + QString::number( qRound( mMessageLength * 1.05 ) );
01128 
01129   destination.setPath("/send");
01130   destination.setQuery( query );
01131 
01132   mJob = KIO::put( destination, -1, false, false, false );
01133   if ( !mJob ) {
01134     abort();
01135     return false;
01136   }
01137   mJob->addMetaData( "lf2crlf+dotstuff", "slave" );
01138   KIO::Scheduler::assignJobToSlave(mSlave, mJob);
01139   connect(mJob, SIGNAL(result(KIO::Job *)), this, SLOT(result(KIO::Job *)));
01140   connect(mJob, SIGNAL(dataReq(KIO::Job *, QByteArray &)),
01141           this, SLOT(dataReq(KIO::Job *, QByteArray &)));
01142   mSendOk = true;
01143   mInProcess = true;
01144   return true;
01145 }
01146 
01147 void KMSendSMTP::cleanup() {
01148   if(mJob)
01149   {
01150     mJob->kill(TRUE);
01151     mJob = 0;
01152     mSlave = 0;
01153   }
01154 
01155   if (mSlave)
01156   {
01157     KIO::Scheduler::disconnectSlave(mSlave);
01158     mSlave = 0;
01159   }
01160 
01161   mInProcess = false;
01162 }
01163 
01164 void KMSendSMTP::abort() {
01165   cleanup();
01166   emit idle();
01167 }
01168 
01169 void KMSendSMTP::doFinish() {
01170   cleanup();
01171 }
01172 
01173 void KMSendSMTP::dataReq(KIO::Job *, QByteArray &array)
01174 {
01175   // Send it by 32K chuncks
01176   const int chunkSize = QMIN( mMessageLength - mMessageOffset, 32*1024 );
01177   if ( chunkSize > 0 ) {
01178     array.duplicate(mMessage.data() + mMessageOffset, chunkSize);
01179     mMessageOffset += chunkSize;
01180   } else
01181   {
01182     array.resize(0);
01183     mMessage.resize(0);
01184   }
01185   mSender->emitProgressInfo( mMessageOffset );
01186 }
01187 
01188 void KMSendSMTP::result(KIO::Job *_job)
01189 {
01190   if (!mJob) return;
01191   mJob = 0;
01192 
01193   if(_job->error())
01194   {
01195     mSendOk = false;
01196     if (_job->error() == KIO::ERR_SLAVE_DIED) mSlave = 0;
01197     failed(_job->errorString());
01198     abort();
01199   } else {
01200     emit idle();
01201   }
01202 }
01203 
01204 void KMSendSMTP::slaveError(KIO::Slave *aSlave, int error, const QString &errorMsg)
01205 {
01206   if (aSlave == mSlave)
01207   {
01208     if (error == KIO::ERR_SLAVE_DIED) mSlave = 0;
01209     mSendOk = false;
01210     mJob = 0;
01211     failed(KIO::buildErrorString(error, errorMsg));
01212     abort();
01213   }
01214 }
01215 
01216 #include "kmsender.moc"
01217 #include "kmsender_p.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys