kmail Library API Documentation

kmsender.cpp

00001 // kmsender.cpp
00002 
00003 #include <config.h>
00004 
00005 #include <kmime_header_parsing.h>
00006 using namespace KMime::Types;
00007 
00008 #include <kio/passdlg.h>
00009 #include <kio/scheduler.h>
00010 #include <kapplication.h>
00011 #include <kmessagebox.h>
00012 #include <kdeversion.h>
00013 #include <klocale.h>
00014 #include <kdebug.h>
00015 #include <kconfig.h>
00016 
00017 #include <assert.h>
00018 #include <stdio.h>
00019 #include <unistd.h>
00020 #include <sys/types.h>
00021 #include <sys/stat.h>
00022 #include <sys/wait.h>
00023 #include "kmfiltermgr.h"
00024 
00025 #include "kcursorsaver.h"
00026 #include "kmsender.h"
00027 #include "kmidentity.h"
00028 #include "identitymanager.h"
00029 #include "kmbroadcaststatus.h"
00030 #include "kmaccount.h"
00031 #include "kmtransport.h"
00032 #include "kmfolderindex.h"
00033 #include "kmfoldermgr.h"
00034 #include "kmmsgdict.h"
00035 #include "kmmsgpart.h"
00036 #include <mimelib/mediatyp.h>
00037 
00038 #define SENDER_GROUP "sending mail"
00039 
00040 //-----------------------------------------------------------------------------
00041 KMSender::KMSender()
00042 {
00043   mPrecommand = 0;
00044   mSendProc = 0;
00045   mSendProcStarted = FALSE;
00046   mSendInProgress = FALSE;
00047   mCurrentMsg = 0;
00048   mTransportInfo = new KMTransportInfo();
00049   readConfig();
00050   mSendAborted = false;
00051   mSentMessages = 0;
00052   mTotalMessages = 0;
00053   mFailedMessages = 0;
00054   mSentBytes = 0;
00055   mTotalBytes = 0;
00056 }
00057 
00058 
00059 //-----------------------------------------------------------------------------
00060 KMSender::~KMSender()
00061 {
00062   writeConfig(FALSE);
00063   delete mSendProc;
00064   delete mPrecommand;
00065   delete mTransportInfo;
00066 }
00067 
00068 //-----------------------------------------------------------------------------
00069 void KMSender::setStatusMsg(const QString &msg)
00070 {
00071   KMBroadcastStatus::instance()->setStatusMsg(msg);
00072 }
00073 
00074 //-----------------------------------------------------------------------------
00075 void KMSender::readConfig(void)
00076 {
00077   QString str;
00078   KConfigGroup config(KMKernel::config(), SENDER_GROUP);
00079 
00080   mSendImmediate = config.readBoolEntry("Immediate", TRUE);
00081   mSendQuotedPrintable = config.readBoolEntry("Quoted-Printable", TRUE);
00082 }
00083 
00084 
00085 //-----------------------------------------------------------------------------
00086 void KMSender::writeConfig(bool aWithSync)
00087 {
00088   KConfigGroup config(KMKernel::config(), SENDER_GROUP);
00089 
00090   config.writeEntry("Immediate", mSendImmediate);
00091   config.writeEntry("Quoted-Printable", mSendQuotedPrintable);
00092 
00093   if (aWithSync) config.sync();
00094 }
00095 
00096 
00097 //-----------------------------------------------------------------------------
00098 bool KMSender::settingsOk() const
00099 {
00100   if (KMTransportInfo::availableTransports().isEmpty())
00101   {
00102     KMessageBox::information(0,i18n("Please create an account for sending and try again."));
00103     return false;
00104   }
00105   return true;
00106 }
00107 
00108 
00109 //-----------------------------------------------------------------------------
00110 bool KMSender::send(KMMessage* aMsg, short sendNow)
00111 {
00112   int rc;
00113 
00114   //assert(aMsg != 0);
00115   if(!aMsg)
00116     {
00117       return false;
00118     }
00119   if (!settingsOk()) return FALSE;
00120 
00121   if (aMsg->to().isEmpty())
00122   {
00123     // RFC822 says:
00124     // Note that the "Bcc" field may be empty, while the "To" field is required to
00125     // have at least one address.
00126     return FALSE;
00127   }
00128 
00129   QString msgId = KMMessage::generateMessageId( aMsg->sender() );
00130     //kdDebug(5006) << "Setting Message-Id to '" << msgId << "'\n";
00131     aMsg->setMsgId( msgId );
00132 
00133   if (sendNow==-1) sendNow = mSendImmediate;
00134 
00135   kmkernel->outboxFolder()->open();
00136   aMsg->setStatus(KMMsgStatusQueued);
00137 
00138   // Handle redirections
00139   QString f = aMsg->headerField("X-KMail-Redirect-From");
00140   if(!f.isEmpty()) {
00141     uint id = aMsg->headerField("X-KMail-Identity").stripWhiteSpace().toUInt();
00142     const KMIdentity & ident =
00143       kmkernel->identityManager()->identityForUoidOrDefault( id );
00144     aMsg->setFrom(f + QString(" (by way of %1 <%2>)")
00145       .arg(ident.fullName()).arg(ident.emailAddr()));
00146   }
00147 
00148   rc = kmkernel->outboxFolder()->addMsg(aMsg);
00149   if (rc)
00150   {
00151     KMessageBox::information(0,i18n("Cannot add message to outbox folder"));
00152     return FALSE;
00153   }
00154 
00155   //Ensure the message is correctly and fully parsed
00156   kmkernel->outboxFolder()->unGetMsg( kmkernel->outboxFolder()->count() - 1 );
00157 
00158   if (sendNow && !mSendInProgress) rc = sendQueued();
00159   else rc = TRUE;
00160   kmkernel->outboxFolder()->close();
00161 
00162   return rc;
00163 }
00164 
00165 
00166 //-----------------------------------------------------------------------------
00167 void KMSender::outboxMsgAdded(int idx)
00168 {
00169     ++mTotalMessages;
00170     KMMsgBase* msg = kmkernel->outboxFolder()->getMsgBase(idx);
00171     Q_ASSERT(msg);
00172     if ( msg )
00173         mTotalBytes += msg->msgSize();
00174 }
00175 
00176 
00177 //-----------------------------------------------------------------------------
00178 bool KMSender::sendQueued(void)
00179 {
00180   if (!settingsOk()) return FALSE;
00181 
00182   if (mSendInProgress)
00183   {
00184     return FALSE;
00185   }
00186 
00187   // open necessary folders
00188   KMFolder* outbox = kmkernel->outboxFolder();
00189   outbox->open();
00190   mTotalMessages = outbox->count();
00191   if (mTotalMessages == 0) {
00192     // Nothing in the outbox. We are done.
00193     outbox->close();
00194     return TRUE;
00195   }
00196   mTotalBytes = 0;
00197   for( int i = 0 ; i<mTotalMessages ; ++i )
00198       mTotalBytes += outbox->getMsgBase(i)->msgSize();
00199 
00200   connect(outbox, SIGNAL(msgAdded(int)),
00201           this, SLOT(outboxMsgAdded(int)));
00202   mCurrentMsg = 0;
00203 
00204   kmkernel->sentFolder()->open();
00205   kmkernel->filterMgr()->ref();
00206 
00207   // start sending the messages
00208   doSendMsg();
00209   return TRUE;
00210 }
00211 
00212 //-----------------------------------------------------------------------------
00213 void KMSender::emitProgressInfo( int currentFileProgress )
00214 {
00215   int percent = (mTotalBytes) ? ( 100 * (mSentBytes+currentFileProgress) / mTotalBytes ) : 0;
00216   if (percent > 100) percent = 100;
00217   KMBroadcastStatus::instance()->setStatusProgressPercent("Sender", percent);
00218 }
00219 
00220 //-----------------------------------------------------------------------------
00221 void KMSender::doSendMsg()
00222 {
00223   if (!kmkernel)  //To handle message sending in progress when kaplan is exited
00224     return; //TODO: handle this case better
00225 
00226   KMFolder *sentFolder = 0, *imapSentFolder = 0;
00227   bool someSent = mCurrentMsg;
00228   int rc;
00229   if (someSent) {
00230       mSentMessages++;
00231       mSentBytes += mCurrentMsg->msgSize();
00232   }
00233   emitProgressInfo( 0 );
00234 
00235   // Post-process sent message (filtering)
00236   if (mCurrentMsg  && kmkernel->filterMgr())
00237   {
00238     mCurrentMsg->setTransferInProgress( FALSE );
00239     if( mCurrentMsg->hasUnencryptedMsg() ) {
00240 kdDebug(5006) << "KMSender::doSendMsg() post-processing: replace mCurrentMsg body by unencryptedMsg data" << endl;
00241       // delete all current body parts
00242       mCurrentMsg->deleteBodyParts();
00243       // copy Content-[..] headers from unencrypted message to current one
00244       KMMessage & newMsg( *mCurrentMsg->unencryptedMsg() );
00245       mCurrentMsg->dwContentType() = newMsg.dwContentType();
00246       mCurrentMsg->setContentTransferEncodingStr( newMsg.contentTransferEncodingStr() );
00247       QCString newDispo = newMsg.headerField("Content-Disposition").latin1();
00248       if( newDispo.isEmpty() )
00249         mCurrentMsg->removeHeaderField( "Content-Disposition" );
00250       else
00251         mCurrentMsg->setHeaderField( "Content-Disposition", newDispo );
00252       // copy the body
00253       mCurrentMsg->setBody( newMsg.body() );
00254       // copy all the body parts
00255       KMMessagePart msgPart;
00256       for( int i = 0; i < newMsg.numBodyParts(); ++i ) {
00257         newMsg.bodyPart( i, &msgPart );
00258         mCurrentMsg->addBodyPart( &msgPart );
00259       }
00260     }
00261     mCurrentMsg->setStatus(KMMsgStatusSent);
00262     mCurrentMsg->setStatus(KMMsgStatusRead); // otherwise it defaults to new on imap
00263 
00264     const KMIdentity & id = kmkernel->identityManager()
00265       ->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
00266     bool folderGone = false;
00267     if ( !mCurrentMsg->fcc().isEmpty() )
00268     {
00269       sentFolder = kmkernel->folderMgr()->findIdString( mCurrentMsg->fcc() );
00270       if ( sentFolder == 0 )
00271       // This is *NOT* supposed to be imapSentFolder!
00272         sentFolder = 
00273           kmkernel->dimapFolderMgr()->findIdString( mCurrentMsg->fcc() );
00274       if ( sentFolder == 0 )
00275         imapSentFolder =
00276           kmkernel->imapFolderMgr()->findIdString( mCurrentMsg->fcc() );
00277       if ( !sentFolder && !imapSentFolder )
00278         folderGone = true;
00279     }
00280     else if ( !id.fcc().isEmpty() )
00281     {
00282       sentFolder = kmkernel->folderMgr()->findIdString( id.fcc() );
00283       if ( sentFolder == 0 )
00284         // This is *NOT* supposed to be imapSentFolder!
00285         sentFolder = kmkernel->dimapFolderMgr()->findIdString( id.fcc() );
00286       if ( sentFolder == 0 )
00287         imapSentFolder = kmkernel->imapFolderMgr()->findIdString( id.fcc() );
00288       if ( !sentFolder && !imapSentFolder )
00289         folderGone = true;
00290     }
00291     if (imapSentFolder && imapSentFolder->noContent()) imapSentFolder = 0;
00292     if (folderGone)
00293       KMessageBox::information(0, i18n("The custom sent-mail folder for identity "
00294             "\"%1\" doesn't exist (anymore). "
00295             "Therefore the default sent-mail folder "
00296             "will be used.").arg( id.identityName() ) );
00297 
00298     if ( sentFolder == 0 ) 
00299       sentFolder = kmkernel->sentFolder();
00300 
00301     if ( sentFolder ) {
00302       rc = sentFolder->open();
00303       if (rc != 0) {
00304         cleanup();
00305         return;
00306       }
00307     }
00308 
00309     // Disable the emitting of msgAdded signal, because the message is taken out of the 
00310     // current folder (outbox) and re-added, to make filter actions changing the message
00311     // work. We don't want that to screw up message counts.
00312     if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( true );
00313     int processResult = kmkernel->filterMgr()->process(mCurrentMsg,KMFilterMgr::Outbound);
00314     if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( false );
00315     
00316     // 0==processed ok, 1==no filter matched, 2==critical error, abort!
00317     switch (processResult) {
00318     case 2:
00319       perror("Critical error: Unable to process sent mail (out of space?)");
00320       KMessageBox::information(0, i18n("Critical error: "
00321                    "Unable to process sent mail (out of space?)"
00322                    "Moving failing message to \"sent-mail\" folder."));
00323       sentFolder->moveMsg(mCurrentMsg);
00324       sentFolder->close();
00325       cleanup();
00326       return;
00327     case 1:
00328       if (sentFolder->moveMsg(mCurrentMsg) != 0)
00329       {
00330         KMessageBox::error(0, i18n("Moving the sent message \"%1\" from the "
00331           "\"outbox\" to the \"sent-mail\" folder failed.\n"
00332           "Possible reasons are lack of disk space or write permission. "
00333           "Please try to fix the problem and move the message manually.")
00334           .arg(mCurrentMsg->subject()));
00335         cleanup();
00336         return;
00337       }
00338       if (imapSentFolder) imapSentFolder->moveMsg(mCurrentMsg);
00339     default:
00340       break;
00341     }
00342     setStatusByLink( mCurrentMsg );
00343     if (mCurrentMsg->parent() && !imapSentFolder) {
00344       // for speed optimization, this code assumes that mCurrentMsg is the
00345       // last one in it's parent folder; make sure that's really the case:
00346       assert( mCurrentMsg->parent()->find( mCurrentMsg )
00347               == mCurrentMsg->parent()->count() - 1 );
00348        // unGet this message:
00349       mCurrentMsg->parent()->unGetMsg( mCurrentMsg->parent()->count() -1 );
00350     }
00351 
00352     mCurrentMsg = 0;
00353   }
00354 
00355   // See if there is another queued message
00356   mCurrentMsg = kmkernel->outboxFolder()->getMsg(mFailedMessages);
00357   if (!mCurrentMsg || mCurrentMsg->transferInProgress())
00358   {
00359     // a message is locked finish the send
00360     if (mCurrentMsg && mCurrentMsg->transferInProgress())
00361         mCurrentMsg = 0;
00362     // no more message: cleanup and done
00363     if ( sentFolder != 0 )
00364         sentFolder->close();
00365     if ( someSent ) {
00366       if ( mSentMessages == mTotalMessages ) {
00367         setStatusMsg(i18n("%n queued message successfully sent.",
00368                           "%n queued messages successfully sent.",
00369                      mSentMessages));
00370       } else {
00371         setStatusMsg(i18n("%1 of %2 queued messages successfully sent.")
00372             .arg(mSentMessages).arg( mTotalMessages ));
00373       }
00374     }
00375     cleanup();
00376     return;
00377   }
00378   mCurrentMsg->setTransferInProgress( TRUE );
00379 
00380   // start the sender process or initialize communication
00381   if (!mSendInProgress)
00382   {
00383     KMBroadcastStatus::instance()->reset();
00384     KMBroadcastStatus::instance()->setStatusProgressEnable( "Sender", true );
00385     connect(KMBroadcastStatus::instance(), SIGNAL(signalAbortRequested()),
00386       SLOT(slotAbortSend()));
00387     kapp->ref();
00388 
00389     mSendInProgress = TRUE;
00390     setStatusMsg(i18n("Initiating sender process..."));
00391   }
00392 
00393   QString msgTransport = mCurrentMsg->headerField("X-KMail-Transport");
00394   if (msgTransport.isEmpty())
00395   {
00396     QStringList sl = KMTransportInfo::availableTransports();
00397     if (!sl.isEmpty()) msgTransport = sl[0];
00398   }
00399   if (!mSendProc || msgTransport != mMethodStr) {
00400     if (mSendProcStarted && mSendProc) {
00401       mSendProc->finish(true);
00402       mSendProcStarted = FALSE;
00403     }
00404 
00405     mSendProc = createSendProcFromString(msgTransport);
00406     mMethodStr = msgTransport;
00407 
00408     if (!mSendProc)
00409       sendProcStarted(false);
00410     else {
00411       connect(mSendProc, SIGNAL(idle()), SLOT(slotIdle()));
00412       connect(mSendProc, SIGNAL(started(bool)), SLOT(sendProcStarted(bool)));
00413 
00414       // Run the precommand if there is one
00415       if (!mTransportInfo->precommand.isEmpty())
00416       {
00417         setStatusMsg(i18n("Executing precommand %1")
00418           .arg(mTransportInfo->precommand));
00419         mPrecommand = new KMPrecommand(mTransportInfo->precommand);
00420         connect(mPrecommand, SIGNAL(finished(bool)),
00421           SLOT(slotPrecommandFinished(bool)));
00422         if (!mPrecommand->start())
00423         {
00424           delete mPrecommand;
00425           mPrecommand = 0;
00426         }
00427         return;
00428       }
00429 
00430       mSendProc->start();
00431     }
00432   }
00433   else if (!mSendProcStarted)
00434     mSendProc->start();
00435   else
00436     doSendMsgAux();
00437 }
00438 
00439 
00440 //-----------------------------------------------------------------------------
00441 void KMSender::sendProcStarted(bool success)
00442 {
00443   if (!success) {
00444     if (mSendProc)
00445        mSendProc->finish(true);
00446     else
00447       setStatusMsg(i18n("Unrecognized transport protocol. Unable to send message."));
00448     mSendProc = 0;
00449     mSendProcStarted = false;
00450     cleanup();
00451     return;
00452   }
00453   doSendMsgAux();
00454 }
00455 
00456 
00457 //-----------------------------------------------------------------------------
00458 void KMSender::doSendMsgAux()
00459 {
00460   mSendProcStarted = TRUE;
00461 
00462   // start sending the current message
00463 
00464   mSendProc->preSendInit();
00465   setStatusMsg(i18n("%3: subject of message","Sending message %1 of %2: %3")
00466            .arg(mSentMessages+mFailedMessages+1).arg(mTotalMessages)
00467            .arg(mCurrentMsg->subject()));
00468   if (!mSendProc->send(mCurrentMsg))
00469   {
00470     cleanup();
00471     setStatusMsg(i18n("Failed to send (some) queued messages."));
00472     return;
00473   }
00474   // Do *not* add code here, after send(). It can happen that this method
00475   // is called recursively if send() emits the idle signal directly.
00476 }
00477 
00478 
00479 //-----------------------------------------------------------------------------
00480 void KMSender::cleanup(void)
00481 {
00482   if (mSendProc && mSendProcStarted) mSendProc->finish(true);
00483   mSendProc = 0;
00484   mSendProcStarted = FALSE;
00485   if (mSendInProgress) kapp->deref();
00486   mSendInProgress = FALSE;
00487   if (mCurrentMsg)
00488   {
00489     mCurrentMsg->setTransferInProgress( FALSE );
00490     mCurrentMsg = 0;
00491   }
00492   disconnect(kmkernel->outboxFolder(), SIGNAL(msgAdded(int)),
00493              this, SLOT(outboxMsgAdded(int)));
00494   kmkernel->sentFolder()->close();
00495   kmkernel->outboxFolder()->close();
00496   if (kmkernel->outboxFolder()->count()<0)
00497     kmkernel->outboxFolder()->expunge();
00498   else kmkernel->outboxFolder()->compact();
00499 
00500   mSendAborted = false;
00501   mSentMessages = 0;
00502   mFailedMessages = 0;
00503   mSentBytes = 0;
00504   disconnect(KMBroadcastStatus::instance(), SIGNAL(signalAbortRequested()),
00505     this, SLOT(slotAbortSend()));
00506   KMBroadcastStatus::instance()->setStatusProgressEnable( "Sender", false );
00507   KMBroadcastStatus::instance()->reset();
00508   kmkernel->filterMgr()->deref();
00509 }
00510 
00511 
00512 //-----------------------------------------------------------------------------
00513 void KMSender::slotAbortSend()
00514 {
00515   mSendAborted = true;
00516   delete mPrecommand;
00517   mPrecommand = 0;
00518   if (mSendProc) mSendProc->abort();
00519 }
00520 
00521 //-----------------------------------------------------------------------------
00522 void KMSender::slotIdle()
00523 {
00524   assert(mSendProc != 0);
00525 
00526   QString msg;
00527   QString errString;
00528   if (mSendProc)
00529       errString = mSendProc->message();
00530 
00531   if (mSendAborted) {
00532     // sending of message aborted
00533     msg = i18n("Sending aborted:\n%1\n"
00534         "The message will stay in the 'outbox' folder until you either "
00535         "fix the problem (e.g. a broken address) or remove the message "
00536         "from the 'outbox' folder.\n"
00537         "The following transport protocol was used:\n  %2")
00538       .arg(errString)
00539       .arg(mMethodStr);
00540     if (!errString.isEmpty()) KMessageBox::error(0,msg);
00541     setStatusMsg( i18n( "Sending aborted." ) );
00542   } else {
00543     if (!mSendProc->sendOk()) {
00544       mCurrentMsg->setTransferInProgress( false );
00545       mCurrentMsg = 0;
00546       mFailedMessages++;
00547       // Sending of message failed.
00548       if (!errString.isEmpty()) {
00549         int res = KMessageBox::Yes;
00550         if (mSentMessages+mFailedMessages != mTotalMessages) {
00551           msg = i18n("<p>Sending failed:</p>"
00552             "<p>%1</p>"
00553             "<p>The message will stay in the 'outbox' folder until you either "
00554             "fix the problem (e.g. a broken address) or remove the message "
00555             "from the 'outbox' folder.</p>"
00556             "<p>The following transport protocol was used:  %2</p>"
00557             "<p>Do you want me to continue sending the remaining messages?</p>")
00558             .arg(errString)
00559             .arg(mMethodStr);
00560           res = KMessageBox::warningYesNo( 0 , msg ,
00561                   i18n( "Continue sending" ), i18n( "&Continue sending" ),
00562                   i18n("&Abort sending") );
00563         } else {
00564           msg = i18n("Sending failed:\n%1\n"
00565             "The message will stay in the 'outbox' folder until you either "
00566             "fix the problem (e.g. a broken address) or remove the message "
00567             "from the 'outbox' folder.\n"
00568             "The following transport protocol was used:\n %2")
00569             .arg(errString)
00570             .arg(mMethodStr);
00571           KMessageBox::error(0,msg);
00572         }
00573         if (res == KMessageBox::Yes) {
00574           // Try the next one.
00575           doSendMsg();
00576           return;
00577         } else {
00578           setStatusMsg( i18n( "Sending aborted." ) );
00579         }
00580       }
00581     } else {
00582       // Sending suceeded.
00583       doSendMsg();
00584       return;
00585     }
00586   }
00587   mSendProc->finish(true);
00588   mSendProc = 0;
00589   mSendProcStarted = false;
00590 
00591   cleanup();
00592 }
00593 
00594 
00595 //-----------------------------------------------------------------------------
00596 void KMSender::slotPrecommandFinished(bool normalExit)
00597 {
00598   delete mPrecommand;
00599   mPrecommand = 0;
00600   if (normalExit) mSendProc->start();
00601   else slotIdle();
00602 }
00603 
00604 
00605 //-----------------------------------------------------------------------------
00606 void KMSender::setSendImmediate(bool aSendImmediate)
00607 {
00608   mSendImmediate = aSendImmediate;
00609 }
00610 
00611 
00612 //-----------------------------------------------------------------------------
00613 void KMSender::setSendQuotedPrintable(bool aSendQuotedPrintable)
00614 {
00615   mSendQuotedPrintable = aSendQuotedPrintable;
00616 }
00617 
00618 
00619 //-----------------------------------------------------------------------------
00620 KMSendProc* KMSender::createSendProcFromString(QString transport)
00621 {
00622   mTransportInfo->type = QString::null;
00623   int nr = KMTransportInfo::findTransport(transport);
00624   if (nr)
00625   {
00626     mTransportInfo->readConfig(nr);
00627   } else {
00628     if (transport.startsWith("smtp://"))
00629     {
00630       mTransportInfo->type = "smtp";
00631       mTransportInfo->auth = FALSE;
00632       mTransportInfo->encryption = "NONE";
00633       QString serverport = transport.mid(7);
00634       int colon = serverport.find(':');
00635       if (colon != -1) {
00636         mTransportInfo->host = serverport.left(colon);
00637         mTransportInfo->port = serverport.mid(colon + 1);
00638       } else {
00639         mTransportInfo->host = serverport;
00640         mTransportInfo->port = "25";
00641       }
00642     } else
00643     if (transport.startsWith("smtps://"))
00644     {
00645       mTransportInfo->type = "smtps";
00646       mTransportInfo->auth = FALSE;
00647       mTransportInfo->encryption = "ssl";
00648       QString serverport = transport.mid(7);
00649       int colon = serverport.find(':');
00650       if (colon != -1) {
00651         mTransportInfo->host = serverport.left(colon);
00652         mTransportInfo->port = serverport.mid(colon + 1);
00653       } else {
00654         mTransportInfo->host = serverport;
00655         mTransportInfo->port = "465";
00656       }
00657     }
00658     else if (transport.startsWith("file://"))
00659     {
00660       mTransportInfo->type = "sendmail";
00661       mTransportInfo->host = transport.mid(7);
00662     }
00663   }
00664   // strip off a trailing "/"
00665   while (mTransportInfo->host.endsWith("/")) {
00666     mTransportInfo->host.truncate(mTransportInfo->host.length()-1);
00667   }
00668 
00669 
00670   if (mTransportInfo->type == "sendmail")
00671     return new KMSendSendmail(this);
00672   if (mTransportInfo->type == "smtp" || mTransportInfo->type == "smtps")
00673     return new KMSendSMTP(this);
00674 
00675   return 0L;
00676 }
00677 
00678 //-----------------------------------------------------------------------------
00679 void KMSender::setStatusByLink(const KMMessage *aMsg)
00680 {
00681   int n = 0;
00682   while (1) {
00683     ulong msn;
00684     KMMsgStatus status;
00685     aMsg->getLink(n, &msn, &status);
00686     if (!msn || !status)
00687       break;
00688     n++;
00689 
00690     KMFolder *folder;
00691     int index;
00692     kmkernel->msgDict()->getLocation(msn, &folder, &index);
00693 
00694     if (folder) {
00695       folder->open();
00696       folder->setStatus(index, status);
00697       folder->close();
00698     }
00699   }
00700 }
00701 
00702 //=============================================================================
00703 //=============================================================================
00704 KMSendProc::KMSendProc(KMSender* aSender): QObject()
00705 {
00706   mSender = aSender;
00707   preSendInit();
00708 }
00709 
00710 //-----------------------------------------------------------------------------
00711 void KMSendProc::preSendInit(void)
00712 {
00713   mSending = FALSE;
00714   mSendOk = FALSE;
00715   mMsg = QString::null;
00716 }
00717 
00718 //-----------------------------------------------------------------------------
00719 void KMSendProc::failed(const QString &aMsg)
00720 {
00721   mSending = FALSE;
00722   mSendOk = FALSE;
00723   mMsg = aMsg;
00724 }
00725 
00726 //-----------------------------------------------------------------------------
00727 void KMSendProc::start(void)
00728 {
00729   emit started(true);
00730 }
00731 
00732 //-----------------------------------------------------------------------------
00733 bool KMSendProc::finish(bool destructive)
00734 {
00735   if (destructive) deleteLater();
00736   return TRUE;
00737 }
00738 
00739 #if !KDE_IS_VERSION( 3, 1, 90 ) // dotstuffing and LF->CRLF is not
00740                 // done by the SMTP kioslave
00741 QCString KMSendProc::prepareStr(const QCString &aStr, bool toCRLF,
00742  bool noSingleDot)
00743 {
00744   int tlen;
00745   const int len = aStr.length();
00746 
00747   if (aStr.isEmpty()) return QCString();
00748 
00749   QCString target( "" );
00750 
00751   if ( toCRLF ) {
00752     // (mmutz) headroom so we actually don't need to resize the target
00753     // array. Five percent should suffice. I measured a mean line
00754     // length of 42 (no joke) over the my last month's worth of mails.
00755     tlen = int(len * 1.05);
00756     target.resize( tlen );
00757 
00758     QCString::Iterator t = target.begin();
00759     QCString::Iterator te = target.end();
00760     te -= 5; // 4 is the max. #(chars) appended in one round, plus one for the \0.
00761     QCString::ConstIterator s = aStr.begin();
00762     while( (*s) ) {
00763 
00764       char c = *s++;
00765 
00766       if ( c == '\n' ) {
00767     *t++ = '\r';
00768     *t++ = c;
00769 
00770     if ( noSingleDot && (*s) == '.' ) {
00771       s++;
00772       *t++ = '.';
00773       *t++ = '.';
00774     }
00775       } else
00776     *t++ = c;
00777 
00778       if ( t >= te ) { // nearing the end of the target buffer.
00779     int tskip = t - target.begin();
00780     tlen += QMAX( len/128, 128 );
00781     if ( !target.resize( tlen ) )
00782       // OOM, what else can we do?
00783       return aStr;
00784     t = target.begin() + tskip;
00785       }
00786     }
00787     *t = '\0';
00788   } else {
00789     if ( !noSingleDot ) return aStr;
00790 
00791     tlen = 0;
00792 
00793     QCString::Iterator t = target.begin();
00794     QCString::ConstIterator olds = aStr.begin();
00795     QCString::ConstIterator s = aStr.begin();
00796 
00797     while ( (*s) ) {
00798       if ( *s++ == '\n' && *s == '.' ) {
00799 
00800     int skip = s - olds + 1;
00801 
00802     if ( tlen ) {
00803       if ( tlen + skip >= (int)target.size() ) {
00804         // resize to 128 + <currently used> + <yet to be copied>
00805         target.resize( 128 + tlen + len - ( olds - aStr.begin() ) );
00806         t = target.begin() + tlen;
00807       }
00808     } else {
00809       target.resize( int( len * 1.02 ) );
00810       t = target.begin();
00811     }
00812 
00813     memcpy( t, olds, skip );
00814     tlen += skip; // incl. '.'
00815     t += skip;
00816     olds = s; // *olds == '.', thus we double the dot in the next round
00817       }
00818     }
00819     // *s == \0 here.
00820 
00821     if ( !tlen ) return aStr; // didn't change anything
00822 
00823     // copy last chunk.
00824     if ( tlen + s - olds + 1 /* incl. \0 */ >= (int)target.size() ) {
00825       target.resize( tlen + s - olds + 1 );
00826       t = target.begin() + tlen;
00827     }
00828     memcpy( t, olds, s - olds + 1 );
00829   }
00830 
00831   return target;
00832 }
00833 #endif
00834 
00835 //-----------------------------------------------------------------------------
00836 void KMSendProc::statusMsg(const QString& aMsg)
00837 {
00838   if (mSender) mSender->setStatusMsg(aMsg);
00839 }
00840 
00841 //-----------------------------------------------------------------------------
00842 bool KMSendProc::addRecipients( const AddrSpecList & al )
00843 {
00844   for ( AddrSpecList::const_iterator it = al.begin() ; it != al.end() ; ++it )
00845     if ( !addOneRecipient( (*it).asString() ) )
00846       return false;
00847   return true;
00848 }
00849 
00850 
00851 //=============================================================================
00852 //=============================================================================
00853 KMSendSendmail::KMSendSendmail(KMSender* aSender):
00854   KMSendProc(aSender)
00855 {
00856   mMailerProc = 0;
00857 }
00858 
00859 //-----------------------------------------------------------------------------
00860 KMSendSendmail::~KMSendSendmail()
00861 {
00862   delete mMailerProc;
00863 }
00864 
00865 //-----------------------------------------------------------------------------
00866 void KMSendSendmail::start(void)
00867 {
00868   if (mSender->transportInfo()->host.isEmpty())
00869   {
00870     QString str = i18n("Please specify a mailer program in the settings.");
00871     QString msg;
00872     msg = i18n("Sending failed:\n%1\n"
00873     "The message will stay in the 'outbox' folder and will be resent.\n"
00874         "Please remove it from there if you do not want the message to "
00875         "be resent.\n"
00876     "The following transport protocol was used:\n  %2")
00877     .arg(str + "\n")
00878     .arg("sendmail://");
00879     KMessageBox::information(0,msg);
00880     emit started(false);
00881     return;
00882   }
00883 
00884   if (!mMailerProc)
00885   {
00886     mMailerProc = new KProcess;
00887     assert(mMailerProc != 0);
00888     connect(mMailerProc,SIGNAL(processExited(KProcess*)),
00889         this, SLOT(sendmailExited(KProcess*)));
00890     connect(mMailerProc,SIGNAL(wroteStdin(KProcess*)),
00891         this, SLOT(wroteStdin(KProcess*)));
00892     connect(mMailerProc,SIGNAL(receivedStderr(KProcess*,char*,int)),
00893         this, SLOT(receivedStderr(KProcess*, char*, int)));
00894   }
00895   emit started(true);
00896 }
00897 
00898 //-----------------------------------------------------------------------------
00899 bool KMSendSendmail::finish(bool destructive)
00900 {
00901   delete mMailerProc;
00902   mMailerProc = 0;
00903   if (destructive)
00904         deleteLater();
00905   return TRUE;
00906 }
00907 
00908 //-----------------------------------------------------------------------------
00909 void KMSendSendmail::abort()
00910 {
00911   delete mMailerProc;
00912   mMailerProc = 0;
00913   mSendOk = false;
00914   mMsgStr = 0;
00915   idle();
00916 }
00917 
00918 
00919 //-----------------------------------------------------------------------------
00920 bool KMSendSendmail::send(KMMessage* aMsg)
00921 {
00922   QString bccStr;
00923 
00924   mMailerProc->clearArguments();
00925   *mMailerProc << mSender->transportInfo()->host;
00926   *mMailerProc << "-i";
00927 
00928   if( !aMsg->headerField("X-KMail-Recipients").isEmpty() ) {
00929     // extended BCC handling to prevent TOs and CCs from seeing
00930     // BBC information by looking at source of an OpenPGP encrypted mail
00931     addRecipients(aMsg->extractAddrSpecs("X-KMail-Recipients"));
00932     aMsg->removeHeaderField( "X-KMail-Recipients" );
00933   } else {
00934     addRecipients(aMsg->extractAddrSpecs("To"));
00935     addRecipients(aMsg->extractAddrSpecs("Cc"));
00936     addRecipients(aMsg->extractAddrSpecs("Bcc"));
00937   }
00938 
00939   mMsgStr = aMsg->asSendableString();
00940 
00941   if (!mMailerProc->start(KProcess::NotifyOnExit,KProcess::All))
00942   {
00943     KMessageBox::information(0,i18n("Failed to execute mailer program %1")
00944                  .arg(mSender->transportInfo()->host));
00945     return FALSE;
00946   }
00947   mMsgPos  = mMsgStr.data();
00948   mMsgRest = mMsgStr.length();
00949   wroteStdin(mMailerProc);
00950 
00951   return TRUE;
00952 }
00953 
00954 
00955 //-----------------------------------------------------------------------------
00956 void KMSendSendmail::wroteStdin(KProcess *proc)
00957 {
00958   char* str;
00959   int len;
00960 
00961   assert(proc!=0);
00962   Q_UNUSED( proc );
00963 
00964   str = mMsgPos;
00965   len = (mMsgRest>1024 ? 1024 : mMsgRest);
00966 
00967   if (len <= 0)
00968   {
00969     mMailerProc->closeStdin();
00970   }
00971   else
00972   {
00973     mMsgRest -= len;
00974     mMsgPos  += len;
00975     mMailerProc->writeStdin(str,len);
00976     // if code is added after writeStdin() KProcess probably initiates
00977     // a race condition.
00978   }
00979 }
00980 
00981 
00982 //-----------------------------------------------------------------------------
00983 void KMSendSendmail::receivedStderr(KProcess *proc, char *buffer, int buflen)
00984 {
00985   assert(proc!=0);
00986   Q_UNUSED( proc );
00987   mMsg.replace(mMsg.length(), buflen, buffer);
00988 }
00989 
00990 
00991 //-----------------------------------------------------------------------------
00992 void KMSendSendmail::sendmailExited(KProcess *proc)
00993 {
00994   assert(proc!=0);
00995   mSendOk = (proc->normalExit() && proc->exitStatus()==0);
00996   if (!mSendOk) failed(i18n("Sendmail exited abnormally."));
00997   mMsgStr = 0;
00998   emit idle();
00999 }
01000 
01001 
01002 //-----------------------------------------------------------------------------
01003 bool KMSendSendmail::addOneRecipient(const QString& aRcpt)
01004 {
01005   assert(mMailerProc!=0);
01006   if (!aRcpt.isEmpty()) *mMailerProc << aRcpt;
01007   return TRUE;
01008 }
01009 
01010 
01011 
01012 //-----------------------------------------------------------------------------
01013 //=============================================================================
01014 //=============================================================================
01015 KMSendSMTP::KMSendSMTP(KMSender *sender)
01016   : KMSendProc(sender),
01017     mInProcess(false),
01018     mJob(0),
01019     mSlave(0)
01020 {
01021   KIO::Scheduler::connect(SIGNAL(slaveError(KIO::Slave *, int,
01022     const QString &)), this, SLOT(slaveError(KIO::Slave *, int,
01023     const QString &)));
01024 }
01025 
01026 KMSendSMTP::~KMSendSMTP()
01027 {
01028   if (mJob) mJob->kill();
01029 }
01030 
01031 bool KMSendSMTP::send(KMMessage *aMsg)
01032 {
01033   KMTransportInfo *ti = mSender->transportInfo();
01034   assert(aMsg != 0);
01035 
01036   const QString sender = aMsg->sender();
01037   if ( sender.isEmpty() )
01038     return false;
01039 
01040   // email this is from
01041   mQuery = "headers=0&from=";
01042   mQuery += KURL::encode_string( sender );
01043 
01044   // recipients
01045   if( !aMsg->headerField("X-KMail-Recipients").isEmpty() ) {
01046     // extended BCC handling to prevent TOs and CCs from seeing
01047     // BBC information by looking at source of an OpenPGP encrypted mail
01048     mQueryField = "&to=";
01049     if( !addRecipients( aMsg->extractAddrSpecs("X-KMail-Recipients")) ) {
01050       return FALSE;
01051     }
01052     aMsg->removeHeaderField( "X-KMail-Recipients" );
01053   } else {
01054     mQueryField = "&to=";
01055     if(!addRecipients(aMsg->extractAddrSpecs("To")))
01056     {
01057       return FALSE;
01058     }
01059 
01060     if(!aMsg->cc().isEmpty())
01061     {
01062       mQueryField = "&cc=";
01063       if(!addRecipients(aMsg->extractAddrSpecs("Cc"))) return FALSE;
01064     }
01065 
01066     QString bccStr = aMsg->bcc();
01067     if(!bccStr.isEmpty())
01068     {
01069       mQueryField = "&bcc=";
01070       if (!addRecipients(aMsg->extractAddrSpecs("Bcc"))) return FALSE;
01071     }
01072   }
01073 
01074   if (ti->specifyHostname)
01075     mQuery += "&hostname=" + KURL::encode_string(ti->localHostname);
01076 
01077   if ( !kmkernel->msgSender()->sendQuotedPrintable() )
01078     mQuery += "&body=8bit";
01079 
01080   KURL destination;
01081 
01082   destination.setProtocol((ti->encryption == "SSL") ? "smtps" : "smtp");
01083   destination.setHost(ti->host);
01084   destination.setPort(ti->port.toUShort());
01085 
01086   if (ti->auth)
01087   {
01088     if(ti->user.isEmpty() || ti->pass.isEmpty())
01089     {
01090       bool b = FALSE;
01091       int result;
01092 
01093       KCursorSaver idle(KBusyPtr::idle());
01094       result = KIO::PasswordDialog::getNameAndPassword(ti->user, ti->pass,
01095     &b, i18n("You need to supply a username and a password to use this "
01096          "SMTP server."), FALSE, QString::null, ti->name, QString::null);
01097 
01098       if ( result != QDialog::Accepted )
01099       {
01100         abort();
01101         return FALSE;
01102       }
01103       if (int id = KMTransportInfo::findTransport(ti->name))
01104         ti->writeConfig(id);
01105     }
01106     destination.setUser(ti->user);
01107     destination.setPass(ti->pass);
01108   }
01109 
01110   if (!mSlave || !mInProcess)
01111   {
01112     KIO::MetaData slaveConfig;
01113     slaveConfig.insert("tls", (ti->encryption == "TLS") ? "on" : "off");
01114     if (ti->auth) slaveConfig.insert("sasl", ti->authType);
01115     mSlave = KIO::Scheduler::getConnectedSlave(destination, slaveConfig);
01116   }
01117 
01118   if (!mSlave)
01119   {
01120     abort();
01121     return false;
01122   }
01123 
01124 #if KDE_IS_VERSION( 3, 1, 90 )
01125   // dotstuffing is now done by the slave (see setting of metadata)
01126   mMessage = aMsg->asSendableString();
01127 #else
01128   mMessage = prepareStr(aMsg->asSendableString(), TRUE);
01129 #endif
01130   mMessageLength = mMessage.length();
01131   mMessageOffset = 0;
01132 
01133   if ( mMessageLength )
01134     // allow +5% for subsequent LF->CRLF and dotstuffing (an average
01135     // over 2G-lines gives an average line length of 42-43):
01136     mQuery += "&size=" + QString::number( qRound( mMessageLength * 1.05 ) );
01137 
01138   destination.setPath("/send");
01139   destination.setQuery(mQuery);
01140   mQuery = QString::null;
01141 
01142   if ((mJob = KIO::put(destination, -1, false, false, false)))
01143   {
01144 #if KDE_IS_VERSION( 3, 1, 90 )
01145     mJob->addMetaData( "lf2crlf+dotstuff", "slave" );
01146 #endif
01147     KIO::Scheduler::assignJobToSlave(mSlave, mJob);
01148     connect(mJob, SIGNAL(result(KIO::Job *)), this, SLOT(result(KIO::Job *)));
01149     connect(mJob, SIGNAL(dataReq(KIO::Job *, QByteArray &)),
01150         this, SLOT(dataReq(KIO::Job *, QByteArray &)));
01151     mSendOk = true;
01152     mInProcess = true;
01153     return mSendOk;
01154   }
01155   else
01156   {
01157     abort();
01158     return false;
01159   }
01160 }
01161 
01162 void KMSendSMTP::abort()
01163 {
01164   finish(false);
01165   emit idle();
01166 }
01167 
01168 bool KMSendSMTP::finish(bool b)
01169 {
01170   if(mJob)
01171   {
01172     mJob->kill(TRUE);
01173     mJob = 0;
01174     mSlave = 0;
01175   }
01176 
01177   if (mSlave)
01178   {
01179     KIO::Scheduler::disconnectSlave(mSlave);
01180     mSlave = 0;
01181   }
01182 
01183   mInProcess = false;
01184   return KMSendProc::finish(b);
01185 }
01186 
01187 bool KMSendSMTP::addOneRecipient(const QString& _addr)
01188 {
01189   if(!_addr.isEmpty())
01190     mQuery += mQueryField + KURL::encode_string(_addr);
01191 
01192   return true;
01193 }
01194 
01195 void KMSendSMTP::dataReq(KIO::Job *, QByteArray &array)
01196 {
01197   // Send it by 32K chuncks
01198   int chunkSize = QMIN( mMessageLength - mMessageOffset, 0x8000 );
01199   if ( chunkSize > 0 ) {
01200     array.duplicate(mMessage.data() + mMessageOffset, chunkSize);
01201     mMessageOffset += chunkSize;
01202   } else
01203   {
01204     array.resize(0);
01205     mMessage.resize(0);
01206   }
01207   mSender->emitProgressInfo( mMessageOffset );
01208 }
01209 
01210 void KMSendSMTP::result(KIO::Job *_job)
01211 {
01212   if (!mJob) return;
01213   mJob = 0;
01214 
01215   if(_job->error())
01216   {
01217     mSendOk = false;
01218     if (_job->error() == KIO::ERR_SLAVE_DIED) mSlave = 0;
01219     failed(_job->errorString());
01220     abort();
01221   } else {
01222     emit idle();
01223   }
01224 }
01225 
01226 void KMSendSMTP::slaveError(KIO::Slave *aSlave, int error, const QString &errorMsg)
01227 {
01228   if (aSlave == mSlave)
01229   {
01230     if (error == KIO::ERR_SLAVE_DIED) mSlave = 0;
01231     mSendOk = false;
01232     mJob = 0;
01233     failed(KIO::buildErrorString(error, errorMsg));
01234     abort();
01235   }
01236 }
01237 
01238 #include "kmsender.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:37:34 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003