kmail Library API Documentation

kmsender.cpp

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