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