kmail Library API Documentation

kmsender.cpp

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