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