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 folder->setStatus(index, status); 00751 folder->close(); 00752 } 00753 } 00754 } 00755 00756 //============================================================================= 00757 //============================================================================= 00758 KMSendProc::KMSendProc(KMSender* aSender): QObject() 00759 { 00760 mSender = aSender; 00761 preSendInit(); 00762 } 00763 00764 //----------------------------------------------------------------------------- 00765 void KMSendProc::preSendInit(void) 00766 { 00767 mSending = FALSE; 00768 mSendOk = FALSE; 00769 mMsg = QString::null; 00770 } 00771 00772 //----------------------------------------------------------------------------- 00773 void KMSendProc::failed(const QString &aMsg) 00774 { 00775 mSending = FALSE; 00776 mSendOk = FALSE; 00777 mMsg = aMsg; 00778 } 00779 00780 //----------------------------------------------------------------------------- 00781 void KMSendProc::start(void) 00782 { 00783 emit started(true); 00784 } 00785 00786 //----------------------------------------------------------------------------- 00787 bool KMSendProc::finish(bool destructive) 00788 { 00789 if (destructive) deleteLater(); 00790 return TRUE; 00791 } 00792 00793 //----------------------------------------------------------------------------- 00794 void KMSendProc::statusMsg(const QString& aMsg) 00795 { 00796 if (mSender) mSender->setStatusMsg(aMsg); 00797 } 00798 00799 //----------------------------------------------------------------------------- 00800 bool KMSendProc::addRecipients( const AddrSpecList & al ) 00801 { 00802 for ( AddrSpecList::const_iterator it = al.begin() ; it != al.end() ; ++it ) 00803 if ( !addOneRecipient( (*it).asString() ) ) 00804 return false; 00805 return true; 00806 } 00807 00808 00809 //============================================================================= 00810 //============================================================================= 00811 KMSendSendmail::KMSendSendmail(KMSender* aSender): 00812 KMSendProc(aSender) 00813 { 00814 mMailerProc = 0; 00815 } 00816 00817 //----------------------------------------------------------------------------- 00818 KMSendSendmail::~KMSendSendmail() 00819 { 00820 delete mMailerProc; 00821 } 00822 00823 //----------------------------------------------------------------------------- 00824 void KMSendSendmail::start(void) 00825 { 00826 if (mSender->transportInfo()->host.isEmpty()) 00827 { 00828 QString str = i18n("Please specify a mailer program in the settings."); 00829 QString msg; 00830 msg = i18n("Sending failed:\n%1\n" 00831 "The message will stay in the 'outbox' folder and will be resent.\n" 00832 "Please remove it from there if you do not want the message to " 00833 "be resent.\n" 00834 "The following transport protocol was used:\n %2") 00835 .arg(str + "\n") 00836 .arg("sendmail://"); 00837 KMessageBox::information(0,msg); 00838 emit started(false); 00839 return; 00840 } 00841 00842 if (!mMailerProc) 00843 { 00844 mMailerProc = new KProcess; 00845 assert(mMailerProc != 0); 00846 connect(mMailerProc,SIGNAL(processExited(KProcess*)), 00847 this, SLOT(sendmailExited(KProcess*))); 00848 connect(mMailerProc,SIGNAL(wroteStdin(KProcess*)), 00849 this, SLOT(wroteStdin(KProcess*))); 00850 connect(mMailerProc,SIGNAL(receivedStderr(KProcess*,char*,int)), 00851 this, SLOT(receivedStderr(KProcess*, char*, int))); 00852 } 00853 emit started(true); 00854 } 00855 00856 //----------------------------------------------------------------------------- 00857 bool KMSendSendmail::finish(bool destructive) 00858 { 00859 delete mMailerProc; 00860 mMailerProc = 0; 00861 if (destructive) 00862 deleteLater(); 00863 return TRUE; 00864 } 00865 00866 //----------------------------------------------------------------------------- 00867 void KMSendSendmail::abort() 00868 { 00869 delete mMailerProc; 00870 mMailerProc = 0; 00871 mSendOk = false; 00872 mMsgStr = 0; 00873 idle(); 00874 } 00875 00876 00877 //----------------------------------------------------------------------------- 00878 bool KMSendSendmail::send(KMMessage* aMsg) 00879 { 00880 QString bccStr; 00881 00882 mMailerProc->clearArguments(); 00883 *mMailerProc << mSender->transportInfo()->host; 00884 *mMailerProc << "-i"; 00885 *mMailerProc << "-f"; 00886 *mMailerProc << aMsg->sender().latin1(); 00887 00888 if( !aMsg->headerField("X-KMail-Recipients").isEmpty() ) { 00889 // extended BCC handling to prevent TOs and CCs from seeing 00890 // BBC information by looking at source of an OpenPGP encrypted mail 00891 addRecipients(aMsg->extractAddrSpecs("X-KMail-Recipients")); 00892 aMsg->removeHeaderField( "X-KMail-Recipients" ); 00893 } else { 00894 addRecipients(aMsg->extractAddrSpecs("To")); 00895 addRecipients(aMsg->extractAddrSpecs("Cc")); 00896 addRecipients(aMsg->extractAddrSpecs("Bcc")); 00897 } 00898 00899 mMsgStr = aMsg->asSendableString(); 00900 00901 if (!mMailerProc->start(KProcess::NotifyOnExit,KProcess::All)) 00902 { 00903 KMessageBox::information(0,i18n("Failed to execute mailer program %1") 00904 .arg(mSender->transportInfo()->host)); 00905 return FALSE; 00906 } 00907 mMsgPos = mMsgStr.data(); 00908 mMsgRest = mMsgStr.length(); 00909 wroteStdin(mMailerProc); 00910 00911 return TRUE; 00912 } 00913 00914 00915 //----------------------------------------------------------------------------- 00916 void KMSendSendmail::wroteStdin(KProcess *proc) 00917 { 00918 char* str; 00919 int len; 00920 00921 assert(proc!=0); 00922 Q_UNUSED( proc ); 00923 00924 str = mMsgPos; 00925 len = (mMsgRest>1024 ? 1024 : mMsgRest); 00926 00927 if (len <= 0) 00928 { 00929 mMailerProc->closeStdin(); 00930 } 00931 else 00932 { 00933 mMsgRest -= len; 00934 mMsgPos += len; 00935 mMailerProc->writeStdin(str,len); 00936 // if code is added after writeStdin() KProcess probably initiates 00937 // a race condition. 00938 } 00939 } 00940 00941 00942 //----------------------------------------------------------------------------- 00943 void KMSendSendmail::receivedStderr(KProcess *proc, char *buffer, int buflen) 00944 { 00945 assert(proc!=0); 00946 Q_UNUSED( proc ); 00947 mMsg.replace(mMsg.length(), buflen, buffer); 00948 } 00949 00950 00951 //----------------------------------------------------------------------------- 00952 void KMSendSendmail::sendmailExited(KProcess *proc) 00953 { 00954 assert(proc!=0); 00955 mSendOk = (proc->normalExit() && proc->exitStatus()==0); 00956 if (!mSendOk) failed(i18n("Sendmail exited abnormally.")); 00957 mMsgStr = 0; 00958 emit idle(); 00959 } 00960 00961 00962 //----------------------------------------------------------------------------- 00963 bool KMSendSendmail::addOneRecipient(const QString& aRcpt) 00964 { 00965 assert(mMailerProc!=0); 00966 if (!aRcpt.isEmpty()) *mMailerProc << aRcpt; 00967 return TRUE; 00968 } 00969 00970 00971 00972 //----------------------------------------------------------------------------- 00973 //============================================================================= 00974 //============================================================================= 00975 KMSendSMTP::KMSendSMTP(KMSender *sender) 00976 : KMSendProc(sender), 00977 mInProcess(false), 00978 mJob(0), 00979 mSlave(0) 00980 { 00981 KIO::Scheduler::connect(SIGNAL(slaveError(KIO::Slave *, int, 00982 const QString &)), this, SLOT(slaveError(KIO::Slave *, int, 00983 const QString &))); 00984 } 00985 00986 KMSendSMTP::~KMSendSMTP() 00987 { 00988 if (mJob) mJob->kill(); 00989 } 00990 00991 bool KMSendSMTP::send(KMMessage *aMsg) 00992 { 00993 KMTransportInfo *ti = mSender->transportInfo(); 00994 assert(aMsg != 0); 00995 00996 const QString sender = aMsg->sender(); 00997 if ( sender.isEmpty() ) 00998 return false; 00999 01000 // email this is from 01001 mQuery = "headers=0&from="; 01002 mQuery += KURL::encode_string( sender ); 01003 01004 // recipients 01005 if( !aMsg->headerField("X-KMail-Recipients").isEmpty() ) { 01006 // extended BCC handling to prevent TOs and CCs from seeing 01007 // BBC information by looking at source of an OpenPGP encrypted mail 01008 mQueryField = "&to="; 01009 if( !addRecipients( aMsg->extractAddrSpecs("X-KMail-Recipients")) ) { 01010 return FALSE; 01011 } 01012 aMsg->removeHeaderField( "X-KMail-Recipients" ); 01013 } else { 01014 mQueryField = "&to="; 01015 if(!addRecipients(aMsg->extractAddrSpecs("To"))) 01016 { 01017 return FALSE; 01018 } 01019 01020 if(!aMsg->cc().isEmpty()) 01021 { 01022 mQueryField = "&cc="; 01023 if(!addRecipients(aMsg->extractAddrSpecs("Cc"))) return FALSE; 01024 } 01025 01026 QString bccStr = aMsg->bcc(); 01027 if(!bccStr.isEmpty()) 01028 { 01029 mQueryField = "&bcc="; 01030 if (!addRecipients(aMsg->extractAddrSpecs("Bcc"))) return FALSE; 01031 } 01032 } 01033 01034 if (ti->specifyHostname) 01035 mQuery += "&hostname=" + KURL::encode_string(ti->localHostname); 01036 01037 if ( !kmkernel->msgSender()->sendQuotedPrintable() ) 01038 mQuery += "&body=8bit"; 01039 01040 KURL destination; 01041 01042 destination.setProtocol((ti->encryption == "SSL") ? SMTPS_PROTOCOL : SMTP_PROTOCOL); 01043 destination.setHost(ti->host); 01044 destination.setPort(ti->port.toUShort()); 01045 01046 if (ti->auth) 01047 { 01048 if(ti->user.isEmpty() || ti->pass.isEmpty()) 01049 { 01050 bool b = FALSE; 01051 int result; 01052 01053 KCursorSaver idle(KBusyPtr::idle()); 01054 result = KIO::PasswordDialog::getNameAndPassword(ti->user, ti->pass, 01055 &b, i18n("You need to supply a username and a password to use this " 01056 "SMTP server."), FALSE, QString::null, ti->name, QString::null); 01057 01058 if ( result != QDialog::Accepted ) 01059 { 01060 abort(); 01061 return FALSE; 01062 } 01063 if (int id = KMTransportInfo::findTransport(ti->name)) 01064 ti->writeConfig(id); 01065 } 01066 destination.setUser(ti->user); 01067 destination.setPass(ti->pass); 01068 } 01069 01070 if (!mSlave || !mInProcess) 01071 { 01072 KIO::MetaData slaveConfig; 01073 slaveConfig.insert("tls", (ti->encryption == "TLS") ? "on" : "off"); 01074 if (ti->auth) slaveConfig.insert("sasl", ti->authType); 01075 mSlave = KIO::Scheduler::getConnectedSlave(destination, slaveConfig); 01076 } 01077 01078 if (!mSlave) 01079 { 01080 abort(); 01081 return false; 01082 } 01083 01084 // dotstuffing is now done by the slave (see setting of metadata) 01085 mMessage = aMsg->asSendableString(); 01086 mMessageLength = mMessage.length(); 01087 mMessageOffset = 0; 01088 01089 if ( mMessageLength ) 01090 // allow +5% for subsequent LF->CRLF and dotstuffing (an average 01091 // over 2G-lines gives an average line length of 42-43): 01092 mQuery += "&size=" + QString::number( qRound( mMessageLength * 1.05 ) ); 01093 01094 destination.setPath("/send"); 01095 destination.setQuery(mQuery); 01096 mQuery = QString::null; 01097 01098 if ((mJob = KIO::put(destination, -1, false, false, false))) 01099 { 01100 mJob->addMetaData( "lf2crlf+dotstuff", "slave" ); 01101 KIO::Scheduler::assignJobToSlave(mSlave, mJob); 01102 connect(mJob, SIGNAL(result(KIO::Job *)), this, SLOT(result(KIO::Job *))); 01103 connect(mJob, SIGNAL(dataReq(KIO::Job *, QByteArray &)), 01104 this, SLOT(dataReq(KIO::Job *, QByteArray &))); 01105 mSendOk = true; 01106 mInProcess = true; 01107 return mSendOk; 01108 } 01109 else 01110 { 01111 abort(); 01112 return false; 01113 } 01114 } 01115 01116 void KMSendSMTP::abort() 01117 { 01118 finish(false); 01119 emit idle(); 01120 } 01121 01122 bool KMSendSMTP::finish(bool b) 01123 { 01124 if(mJob) 01125 { 01126 mJob->kill(TRUE); 01127 mJob = 0; 01128 mSlave = 0; 01129 } 01130 01131 if (mSlave) 01132 { 01133 KIO::Scheduler::disconnectSlave(mSlave); 01134 mSlave = 0; 01135 } 01136 01137 mInProcess = false; 01138 return KMSendProc::finish(b); 01139 } 01140 01141 bool KMSendSMTP::addOneRecipient(const QString& _addr) 01142 { 01143 if(!_addr.isEmpty()) 01144 mQuery += mQueryField + KURL::encode_string(_addr); 01145 01146 return true; 01147 } 01148 01149 void KMSendSMTP::dataReq(KIO::Job *, QByteArray &array) 01150 { 01151 // Send it by 32K chuncks 01152 int chunkSize = QMIN( mMessageLength - mMessageOffset, 0x8000 ); 01153 if ( chunkSize > 0 ) { 01154 array.duplicate(mMessage.data() + mMessageOffset, chunkSize); 01155 mMessageOffset += chunkSize; 01156 } else 01157 { 01158 array.resize(0); 01159 mMessage.resize(0); 01160 } 01161 mSender->emitProgressInfo( mMessageOffset ); 01162 } 01163 01164 void KMSendSMTP::result(KIO::Job *_job) 01165 { 01166 if (!mJob) return; 01167 mJob = 0; 01168 01169 if(_job->error()) 01170 { 01171 mSendOk = false; 01172 if (_job->error() == KIO::ERR_SLAVE_DIED) mSlave = 0; 01173 failed(_job->errorString()); 01174 abort(); 01175 } else { 01176 emit idle(); 01177 } 01178 } 01179 01180 void KMSendSMTP::slaveError(KIO::Slave *aSlave, int error, const QString &errorMsg) 01181 { 01182 if (aSlave == mSlave) 01183 { 01184 if (error == KIO::ERR_SLAVE_DIED) mSlave = 0; 01185 mSendOk = false; 01186 mJob = 0; 01187 failed(KIO::buildErrorString(error, errorMsg)); 01188 abort(); 01189 } 01190 } 01191 01192 #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 Fri Aug 27 12:52:50 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003