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 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();
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 QByteArray & 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.size();
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 QByteArray & 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 
01064   KMTransportInfo * ti = mSender->transportInfo();
01065 
01066   if ( ti->specifyHostname )
01067     query += "&hostname=" + KURL::encode_string( ti->localHostname );
01068 
01069   if ( !kmkernel->msgSender()->sendQuotedPrintable() )
01070     query += "&body=8bit";
01071 
01072   KURL destination;
01073 
01074   destination.setProtocol((ti->encryption == "SSL") ? SMTPS_PROTOCOL : SMTP_PROTOCOL);
01075   destination.setHost(ti->host);
01076   destination.setPort(ti->port.toUShort());
01077 
01078   if (ti->auth)
01079   {
01080     if( (ti->user.isEmpty() || ti->passwd().isEmpty()) &&
01081       ti->authType != "GSSAPI" )
01082     {
01083       bool b = FALSE;
01084       int result;
01085 
01086       KCursorSaver idle(KBusyPtr::idle());
01087       QString passwd = ti->passwd();
01088       result = KIO::PasswordDialog::getNameAndPassword(ti->user, passwd,
01089     &b, i18n("You need to supply a username and a password to use this "
01090          "SMTP server."), FALSE, QString::null, ti->name, QString::null);
01091 
01092       if ( result != QDialog::Accepted )
01093       {
01094         abort();
01095         return FALSE;
01096       }
01097       if (int id = KMTransportInfo::findTransport(ti->name)) {
01098         ti->setPasswd( passwd );
01099         ti->writeConfig(id);
01100       }
01101     }
01102     destination.setUser(ti->user);
01103     destination.setPass(ti->passwd());
01104   }
01105 
01106   if (!mSlave || !mInProcess)
01107   {
01108     KIO::MetaData slaveConfig;
01109     slaveConfig.insert("tls", (ti->encryption == "TLS") ? "on" : "off");
01110     if (ti->auth) slaveConfig.insert("sasl", ti->authType);
01111     mSlave = KIO::Scheduler::getConnectedSlave(destination, slaveConfig);
01112   }
01113 
01114   if (!mSlave)
01115   {
01116     abort();
01117     return false;
01118   }
01119 
01120   // dotstuffing is now done by the slave (see setting of metadata)
01121   mMessage = message;
01122   mMessageLength = mMessage.size();
01123   mMessageOffset = 0;
01124 
01125   if ( mMessageLength )
01126     // allow +5% for subsequent LF->CRLF and dotstuffing (an average
01127     // over 2G-lines gives an average line length of 42-43):
01128     query += "&size=" + QString::number( qRound( mMessageLength * 1.05 ) );
01129 
01130   destination.setPath("/send");
01131   destination.setQuery( query );
01132 
01133   mJob = KIO::put( destination, -1, false, false, false );
01134   if ( !mJob ) {
01135     abort();
01136     return false;
01137   }
01138   mJob->addMetaData( "lf2crlf+dotstuff", "slave" );
01139   KIO::Scheduler::assignJobToSlave(mSlave, mJob);
01140   connect(mJob, SIGNAL(result(KIO::Job *)), this, SLOT(result(KIO::Job *)));
01141   connect(mJob, SIGNAL(dataReq(KIO::Job *, QByteArray &)),
01142           this, SLOT(dataReq(KIO::Job *, QByteArray &)));
01143   mSendOk = true;
01144   mInProcess = true;
01145   return true;
01146 }
01147 
01148 void KMSendSMTP::cleanup() {
01149   if(mJob)
01150   {
01151     mJob->kill(TRUE);
01152     mJob = 0;
01153     mSlave = 0;
01154   }
01155 
01156   if (mSlave)
01157   {
01158     KIO::Scheduler::disconnectSlave(mSlave);
01159     mSlave = 0;
01160   }
01161 
01162   mInProcess = false;
01163 }
01164 
01165 void KMSendSMTP::abort() {
01166   cleanup();
01167   emit idle();
01168 }
01169 
01170 void KMSendSMTP::doFinish() {
01171   cleanup();
01172 }
01173 
01174 void KMSendSMTP::dataReq(KIO::Job *, QByteArray &array)
01175 {
01176   // Send it by 32K chuncks
01177   const int chunkSize = QMIN( mMessageLength - mMessageOffset, 32*1024 );
01178   if ( chunkSize > 0 ) {
01179     array.duplicate(mMessage.data() + mMessageOffset, chunkSize);
01180     mMessageOffset += chunkSize;
01181   } else
01182   {
01183     array.resize(0);
01184     mMessage.resize(0);
01185   }
01186   mSender->emitProgressInfo( mMessageOffset );
01187 }
01188 
01189 void KMSendSMTP::result(KIO::Job *_job)
01190 {
01191   if (!mJob) return;
01192   mJob = 0;
01193 
01194   if(_job->error())
01195   {
01196     mSendOk = false;
01197     if (_job->error() == KIO::ERR_SLAVE_DIED) mSlave = 0;
01198     failed(_job->errorString());
01199     abort();
01200   } else {
01201     emit idle();
01202   }
01203 }
01204 
01205 void KMSendSMTP::slaveError(KIO::Slave *aSlave, int error, const QString &errorMsg)
01206 {
01207   if (aSlave == mSlave)
01208   {
01209     if (error == KIO::ERR_SLAVE_DIED) mSlave = 0;
01210     mSendOk = false;
01211     mJob = 0;
01212     failed(KIO::buildErrorString(error, errorMsg));
01213     abort();
01214   }
01215 }
01216 
01217 #include "kmsender.moc"
01218 #include "kmsender_p.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys