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