kmail

kmsender.cpp
1 // kmsender.cpp
2 
3 #include <config.h>
4 
5 #define REALLY_WANT_KMSENDER
6 #include "kmsender.h"
7 #include "kmsender_p.h"
8 #undef REALLY_WANT_KMSENDER
9 
10 #include <kmime_header_parsing.h>
11 using namespace KMime::Types;
12 
13 #include <tdeio/passdlg.h>
14 #include <tdeio/scheduler.h>
15 #include <tdeapplication.h>
16 #include <tdemessagebox.h>
17 #include <tdeversion.h>
18 #include <tdelocale.h>
19 #include <kdebug.h>
20 #include <tdeconfig.h>
21 
22 #include <assert.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/wait.h>
28 #include "globalsettings.h"
29 #include "kmfiltermgr.h"
30 
31 #include "kcursorsaver.h"
32 #include <libkpimidentities/identity.h>
33 #include <libkpimidentities/identitymanager.h>
34 #include "progressmanager.h"
35 #include "kmaccount.h"
36 #include "kmtransport.h"
37 #include "kmfolderindex.h"
38 #include "kmfoldermgr.h"
39 #include "kmmsgdict.h"
40 #include "kmmsgpart.h"
41 #include "protocols.h"
42 #include "kmcommands.h"
43 #include <mimelib/mediatyp.h>
44 #include <mimelib/enum.h>
45 #include <mimelib/param.h>
46 
47 #define SENDER_GROUP "sending mail"
48 
49 //-----------------------------------------------------------------------------
50 KMSender::KMSender()
51  : mOutboxFolder( 0 ), mSentFolder( 0 )
52 {
53  mPrecommand = 0;
54  mSendProc = 0;
55  mSendProcStarted = false;
56  mSendInProgress = false;
57  mCurrentMsg = 0;
58  mTransportInfo = new KMTransportInfo();
59  readConfig();
60  mSendAborted = false;
61  mSentMessages = 0;
62  mTotalMessages = 0;
63  mFailedMessages = 0;
64  mSentBytes = 0;
65  mTotalBytes = 0;
66  mProgressItem = 0;
67 }
68 
69 
70 //-----------------------------------------------------------------------------
71 KMSender::~KMSender()
72 {
73  writeConfig(false);
74  delete mSendProc;
75  delete mPrecommand;
76  delete mTransportInfo;
77 }
78 
79 //-----------------------------------------------------------------------------
80 void KMSender::setStatusMsg(const TQString &msg)
81 {
82  if ( mProgressItem )
83  mProgressItem->setStatus(msg);
84 }
85 
86 //-----------------------------------------------------------------------------
87 void KMSender::readConfig(void)
88 {
89  TQString str;
90  TDEConfigGroup config(KMKernel::config(), SENDER_GROUP);
91 
92  mSendImmediate = config.readBoolEntry("Immediate", true);
93  mSendQuotedPrintable = config.readBoolEntry("Quoted-Printable", true);
94 }
95 
96 
97 //-----------------------------------------------------------------------------
98 void KMSender::writeConfig(bool aWithSync)
99 {
100  TDEConfigGroup config(KMKernel::config(), SENDER_GROUP);
101 
102  config.writeEntry("Immediate", mSendImmediate);
103  config.writeEntry("Quoted-Printable", mSendQuotedPrintable);
104 
105  if (aWithSync) config.sync();
106 }
107 
108 
109 //-----------------------------------------------------------------------------
110 bool KMSender::settingsOk() const
111 {
112  if (KMTransportInfo::availableTransports().isEmpty())
113  {
114  KMessageBox::information(0,i18n("Please create an account for sending and try again."));
115  return false;
116  }
117  return true;
118 }
119 
120 static void handleRedirections( KMMessage * m ) {
121  const TQString from = m->headerField("X-KMail-Redirect-From");
122  const TQString msgId = m->msgId();
123  if( from.isEmpty() || msgId.isEmpty() )
124  m->setMsgId( KMMessage::generateMessageId( m->sender() ) );
125 }
126 
127 //-----------------------------------------------------------------------------
128 bool KMSender::doSend(KMMessage* aMsg, short sendNow)
129 {
130  if(!aMsg)
131  return false;
132 
133  if (!settingsOk()) return false;
134 
135  if (aMsg->to().isEmpty())
136  {
137  // RFC822 says:
138  // Note that the "Bcc" field may be empty, while the "To" field is required to
139  // have at least one address.
140  //
141  // however:
142  //
143  // The following string is accepted according to RFC 2822,
144  // section 3.4 "Address Specification" where they say:
145  //
146  // "An address may either be an individual mailbox,
147  // or a group of mailboxes."
148  // and:
149  // "group + display-name ":" [mailbox-list / CFWS] ";"
150  // [CFWS]"
151  //
152  // In this syntax our "undisclosed-recipients: ;"
153  // just specifies an empty group.
154  //
155  // In further explanations RFC 2822 states that it *is*
156  // allowed to have a ZERO number of mailboxes in the "mailbox-list".
157  aMsg->setTo("Undisclosed.Recipients: ;");
158  }
159 
160  handleRedirections( aMsg );
161 
162  if (sendNow==-1) sendNow = mSendImmediate;
163 
164  KMFolder * const outbox = kmkernel->outboxFolder();
165  const KMFolderOpener openOutbox( outbox, "outbox" );
166 
167  aMsg->setStatus(KMMsgStatusQueued);
168 
169  if ( const int err = outbox->addMsg(aMsg) ) {
170  Q_UNUSED( err );
171  KMessageBox::information(0,i18n("Cannot add message to outbox folder"));
172  return false;
173  }
174 
175  //Ensure the message is correctly and fully parsed
176 
177  /* The above was added by Marc and seems to be necessary to ensure
178  * the mail is in a sane state before sending. The unGet makes the
179  * attached unencrypted version of the mail (if there is one ) disappear.
180  * though, so we need to make sure to keep it around and restore it
181  * afterwards. The real fix would be to replace the unGet with
182  * whatever parsing is triggered by it, but I'm too chicken to do that,
183  * in this branch.
184  * Note that the unencrypted mail will be lost if the mail remains in
185  * the outbox across a restart anyhow, but that never worked, afaikt. */
186  const int idx = outbox->count() - 1;
187  KMMessage * const unencryptedMsg = aMsg->unencryptedMsg();
188  outbox->unGetMsg( idx );
189  KMMessage * const tempMsg = outbox->getMsg( idx );
190  tempMsg->setUnencryptedMsg( unencryptedMsg );
191 
192  if ( !sendNow || mSendInProgress )
193  return true;
194 
195  return sendQueued();
196 }
197 
198 
199 //-----------------------------------------------------------------------------
200 void KMSender::outboxMsgAdded(int idx)
201 {
202  ++mTotalMessages;
203  KMMsgBase* msg = kmkernel->outboxFolder()->getMsgBase(idx);
204  Q_ASSERT(msg);
205  if ( msg )
206  mTotalBytes += msg->msgSize();
207 }
208 
209 
210 //-----------------------------------------------------------------------------
211 bool KMSender::doSendQueued( const TQString &customTransport )
212 {
213  if (!settingsOk()) return false;
214 
215  if (mSendInProgress)
216  {
217  return false;
218  }
219 
220  // open necessary folders
221  mOutboxFolder = kmkernel->outboxFolder();
222  mOutboxFolder->open("dosendoutbox");
223  mTotalMessages = mOutboxFolder->count();
224  if (mTotalMessages == 0) {
225  // Nothing in the outbox. We are done.
226  mOutboxFolder->close("dosendoutbox");
227  mOutboxFolder = 0;
228  return true;
229  }
230  mTotalBytes = 0;
231  for( int i = 0 ; i<mTotalMessages ; ++i )
232  mTotalBytes += mOutboxFolder->getMsgBase(i)->msgSize();
233 
234  connect( mOutboxFolder, TQ_SIGNAL(msgAdded(int)),
235  this, TQ_SLOT(outboxMsgAdded(int)) );
236  mCurrentMsg = 0;
237 
238  mSentFolder = kmkernel->sentFolder();
239  mSentFolder->open("dosendsent");
240  kmkernel->filterMgr()->ref();
241 
242  // start sending the messages
243  mCustomTransport = customTransport;
244  doSendMsg();
245  return true;
246 }
247 
248 //-----------------------------------------------------------------------------
249 void KMSender::emitProgressInfo( int currentFileProgress )
250 {
251  int percent = (mTotalBytes) ? ( 100 * (mSentBytes+currentFileProgress) / mTotalBytes ) : 0;
252  if (percent > 100) percent = 100;
253  mProgressItem->setProgress(percent);
254 }
255 
256 static bool messageIsDispositionNotificationReport( KMMessage *msg )
257 {
258  if ( msg->type() == DwMime::kTypeMessage &&
259  msg->subtype() == DwMime::kSubtypeDispositionNotification )
260  return true;
261 
262  if ( msg->type() != DwMime::kTypeMultipart ||
263  msg->subtype() != DwMime::kSubtypeReport )
264  return false;
265 
266  DwMediaType& ct = msg->dwContentType();
267  DwParameter *param = ct.FirstParameter();
268  while( param ) {
269  if ( !tqstricmp( param->Attribute().c_str(), "report-type")
270  && !tqstricmp( param->Value().c_str(), "disposition-notification" ) )
271  return true;
272  else
273  param = param->Next();
274  }
275  return false;
276 }
277 
278 //-----------------------------------------------------------------------------
279 void KMSender::doSendMsg()
280 {
281  if (!kmkernel) //To handle message sending in progress when kaplan is exited
282  return; //TODO: handle this case better
283 
284  const bool someSent = mCurrentMsg;
285  if (someSent) {
286  mSentMessages++;
287  mSentBytes += mCurrentMsg->msgSize();
288  }
289 
290  // Post-process sent message (filtering)
291  KMFolder *sentFolder = 0, *imapSentFolder = 0;
292  if (mCurrentMsg && kmkernel->filterMgr())
293  {
294  mCurrentMsg->setTransferInProgress( false );
295  if( mCurrentMsg->hasUnencryptedMsg() ) {
296  kdDebug(5006) << "KMSender::doSendMsg() post-processing: replace mCurrentMsg body by unencryptedMsg data" << endl;
297  // delete all current body parts
298  mCurrentMsg->deleteBodyParts();
299  // copy Content-[..] headers from unencrypted message to current one
300  KMMessage & newMsg( *mCurrentMsg->unencryptedMsg() );
301  mCurrentMsg->dwContentType() = newMsg.dwContentType();
302  mCurrentMsg->setContentTransferEncodingStr( newMsg.contentTransferEncodingStr() );
303  TQCString newDispo = newMsg.headerField("Content-Disposition").latin1();
304  if( newDispo.isEmpty() )
305  mCurrentMsg->removeHeaderField( "Content-Disposition" );
306  else
307  mCurrentMsg->setHeaderField( "Content-Disposition", newDispo );
308  // copy the body
309  mCurrentMsg->setBody( newMsg.body() );
310  // copy all the body parts
311  KMMessagePart msgPart;
312  for( int i = 0; i < newMsg.numBodyParts(); ++i ) {
313  newMsg.bodyPart( i, &msgPart );
314  mCurrentMsg->addBodyPart( &msgPart );
315  }
316  }
317  mCurrentMsg->setStatus(KMMsgStatusSent);
318  mCurrentMsg->setStatus(KMMsgStatusRead); // otherwise it defaults to new on imap
319  mCurrentMsg->updateAttachmentState();
320  mCurrentMsg->updateInvitationState();
321 
322  const KPIM::Identity & id = kmkernel->identityManager()
323  ->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
324  if ( !mCurrentMsg->fcc().isEmpty() )
325  {
326  sentFolder = kmkernel->folderMgr()->findIdString( mCurrentMsg->fcc() );
327  if ( sentFolder == 0 )
328  // This is *NOT* supposed to be imapSentFolder!
329  sentFolder =
330  kmkernel->dimapFolderMgr()->findIdString( mCurrentMsg->fcc() );
331  if ( sentFolder == 0 )
332  imapSentFolder =
333  kmkernel->imapFolderMgr()->findIdString( mCurrentMsg->fcc() );
334  }
335  // No, or no usable sentFolder, and no, or no usable imapSentFolder,
336  // let's try the on in the identity
337  if ( ( sentFolder == 0 || sentFolder->isReadOnly() )
338  && ( imapSentFolder == 0 || imapSentFolder->isReadOnly() )
339  && !id.fcc().isEmpty() )
340  {
341  sentFolder = kmkernel->folderMgr()->findIdString( id.fcc() );
342  if ( sentFolder == 0 )
343  // This is *NOT* supposed to be imapSentFolder!
344  sentFolder = kmkernel->dimapFolderMgr()->findIdString( id.fcc() );
345  if ( sentFolder == 0 )
346  imapSentFolder = kmkernel->imapFolderMgr()->findIdString( id.fcc() );
347  }
348  if (imapSentFolder
349  && ( imapSentFolder->noContent() || imapSentFolder->isReadOnly() ) )
350  imapSentFolder = 0;
351 
352  if ( sentFolder == 0 || sentFolder->isReadOnly() )
353  sentFolder = kmkernel->sentFolder();
354 
355  if ( sentFolder ) {
356  if ( const int err = sentFolder->open("sentFolder") ) {
357  Q_UNUSED( err );
358  cleanup();
359  return;
360  }
361  }
362 
363  // Disable the emitting of msgAdded signal, because the message is taken out of the
364  // current folder (outbox) and re-added, to make filter actions changing the message
365  // work. We don't want that to screw up message counts.
366  if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( true );
367  const int processResult = kmkernel->filterMgr()->process(mCurrentMsg,KMFilterMgr::Outbound);
368  if ( mCurrentMsg->parent() ) mCurrentMsg->parent()->quiet( false );
369 
370  // 0==processed ok, 1==no filter matched, 2==critical error, abort!
371  switch (processResult) {
372  case 2:
373  perror("Critical error: Unable to process sent mail (out of space?)");
374  KMessageBox::information(0, i18n("Critical error: "
375  "Unable to process sent mail (out of space?)"
376  "Moving failing message to \"sent-mail\" folder."));
377  if ( sentFolder ) {
378  sentFolder->moveMsg(mCurrentMsg);
379  sentFolder->close("sentFolder");
380  }
381  cleanup();
382  return;
383  case 1:
384  if ( sentFolder && sentFolder->moveMsg(mCurrentMsg) != 0 )
385  {
386  KMessageBox::error(0, i18n("Moving the sent message \"%1\" from the "
387  "\"outbox\" to the \"sent-mail\" folder failed.\n"
388  "Possible reasons are lack of disk space or write permission. "
389  "Please try to fix the problem and move the message manually.")
390  .arg(mCurrentMsg->subject()));
391  cleanup();
392  return;
393  }
394  if (imapSentFolder) {
395  // Does proper folder refcounting and message locking
396  KMCommand *command = new KMMoveCommand( imapSentFolder, mCurrentMsg );
397  command->keepFolderOpen( sentFolder ); // will open it, and close it once done
398  command->start();
399  }
400  default:
401  break;
402  }
403  setStatusByLink( mCurrentMsg );
404  if (mCurrentMsg->parent() && !imapSentFolder) {
405  // for speed optimization, this code assumes that mCurrentMsg is the
406  // last one in it's parent folder; make sure that's really the case:
407  assert( mCurrentMsg->parent()->find( mCurrentMsg )
408  == mCurrentMsg->parent()->count() - 1 );
409  // unGet this message:
410  mCurrentMsg->parent()->unGetMsg( mCurrentMsg->parent()->count() -1 );
411  }
412 
413  mCurrentMsg = 0;
414  }
415 
416  // See if there is another queued message
417  mCurrentMsg = mOutboxFolder->getMsg(mFailedMessages);
418  if ( mCurrentMsg && !mCurrentMsg->transferInProgress() &&
419  mCurrentMsg->sender().isEmpty() ) {
420  // if we do not have a sender address then use the email address of the
421  // message's identity or of the default identity unless those two are also
422  // empty
423  const KPIM::Identity & id = kmkernel->identityManager()
424  ->identityForUoidOrDefault( mCurrentMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
425  if ( !id.primaryEmailAddress().isEmpty() ) {
426  mCurrentMsg->setFrom( id.fullEmailAddr() );
427  }
428  else if ( !kmkernel->identityManager()->defaultIdentity().primaryEmailAddress().isEmpty() ) {
429  mCurrentMsg->setFrom( kmkernel->identityManager()->defaultIdentity().fullEmailAddr() );
430  }
431  else {
432  KMessageBox::sorry( 0, i18n( "It's not possible to send messages "
433  "without specifying a sender address.\n"
434  "Please set the email address of "
435  "identity '%1' in the Identities "
436  "section of the configuration dialog "
437  "and then try again." )
438  .arg( id.identityName() ) );
439  mOutboxFolder->unGetMsg( mFailedMessages );
440  mCurrentMsg = 0;
441  }
442  }
443  if (!mCurrentMsg || mCurrentMsg->transferInProgress())
444  {
445  // a message is locked finish the send
446  if (mCurrentMsg && mCurrentMsg->transferInProgress())
447  mCurrentMsg = 0;
448  // no more message: cleanup and done
449  if ( sentFolder != 0 )
450  sentFolder->close("sentFolder");
451  if ( someSent ) {
452  if ( mSentMessages == mTotalMessages ) {
453  setStatusMsg(i18n("%n queued message successfully sent.",
454  "%n queued messages successfully sent.",
455  mSentMessages));
456  } else {
457  setStatusMsg(i18n("%1 of %2 queued messages successfully sent.")
458  .arg(mSentMessages).arg( mTotalMessages ));
459  }
460  }
461  cleanup();
462  return;
463  }
464  mCurrentMsg->setTransferInProgress( true );
465 
466  // start the sender process or initialize communication
467  if (!mSendInProgress)
468  {
469  Q_ASSERT( !mProgressItem );
470  mProgressItem = KPIM::ProgressManager::createProgressItem(
471  "Sender",
472  i18n( "Sending messages" ),
473  i18n("Initiating sender process..."),
474  true );
475  connect( mProgressItem, TQ_SIGNAL(progressItemCanceled(KPIM::ProgressItem*)),
476  this, TQ_SLOT( slotAbortSend() ) );
477  kapp->ref();
478  mSendInProgress = true;
479  }
480 
481  TQString msgTransport = mCustomTransport;
482  if ( msgTransport.isEmpty() ) {
483  msgTransport = mCurrentMsg->headerField("X-KMail-Transport");
484  }
485  if ( msgTransport.isEmpty() ) {
486  msgTransport = GlobalSettings::self()->defaultTransport();
487  }
488  if ( msgTransport.isEmpty() ) {
489  const TQStringList sl = KMTransportInfo::availableTransports();
490  if (!sl.empty()) msgTransport = sl.front();
491  }
492 
493  if (!mSendProc || msgTransport != mMethodStr) {
494  if (mSendProcStarted && mSendProc) {
495  mSendProc->finish();
496  mSendProcStarted = false;
497  }
498 
499  mSendProc = createSendProcFromString(msgTransport);
500  mMethodStr = msgTransport;
501 
502  if( mTransportInfo->encryption == "TLS" || mTransportInfo->encryption == "SSL" ) {
503  mProgressItem->setUsesCrypto( true );
504  } else if ( !mCustomTransport.isEmpty() ) {
505  int result = KMessageBox::warningContinueCancel( 0,
506  i18n( "You have chosen to send all queued email using an unencrypted transport, do you want to continue? "),
507  i18n( "Security Warning" ),
508  i18n( "Send Unencrypted" ),
509  "useCustomTransportWithoutAsking", false);
510 
511  if( result == KMessageBox::Cancel ) {
512  mProgressItem->cancel();
513  mProgressItem->setComplete();
514  slotAbortSend();
515  cleanup();
516  return;
517  }
518  }
519 
520  if (!mSendProc)
521  sendProcStarted(false);
522  else {
523  connect(mSendProc, TQ_SIGNAL(idle()), TQ_SLOT(slotIdle()));
524  connect(mSendProc, TQ_SIGNAL(started(bool)), TQ_SLOT(sendProcStarted(bool)));
525 
526  // Run the precommand if there is one
527  if ( !mTransportInfo->precommand.isEmpty() ) {
528  runPrecommand( mTransportInfo->precommand );
529  return;
530  }
531 
532  mSendProc->start();
533  }
534  }
535  else if (!mSendProcStarted)
536  mSendProc->start();
537  else
538  doSendMsgAux();
539 }
540 
541 bool KMSender::runPrecommand( const TQString & cmd ) {
542  setStatusMsg( i18n("Executing precommand %1").arg( cmd ) );
543  mPrecommand = new KMPrecommand( cmd );
544  connect( mPrecommand, TQ_SIGNAL(finished(bool)),
545  TQ_SLOT(slotPrecommandFinished(bool)) );
546  if ( !mPrecommand->start() ) {
547  delete mPrecommand; mPrecommand = 0;
548  return false;
549  }
550  return true;
551 }
552 
553 //-----------------------------------------------------------------------------
554 void KMSender::sendProcStarted(bool success)
555 {
556  if (!success) {
557  if (mSendProc)
558  mSendProc->finish();
559  else
560  setStatusMsg(i18n("Unrecognized transport protocol. Unable to send message."));
561  mSendProc = 0;
562  mSendProcStarted = false;
563  cleanup();
564  return;
565  }
566  doSendMsgAux();
567 }
568 
569 
570 static TQStringList addrSpecListToStringList( const AddrSpecList & l, bool allowEmpty=false ) {
571  TQStringList result;
572  for ( AddrSpecList::const_iterator it = l.begin(), end = l.end() ; it != end ; ++it ) {
573  const TQString s = (*it).asString();
574  if ( allowEmpty || !s.isEmpty() )
575  result.push_back( s );
576  }
577  return result;
578 }
579 
580 static void extractSenderToCCAndBcc( KMMessage * aMsg, TQString * sender, TQStringList * to, TQStringList * cc, TQStringList * bcc ) {
581  if ( sender ) *sender = aMsg->sender();
582  if( !aMsg->headerField("X-KMail-Recipients").isEmpty() ) {
583  // extended BCC handling to prevent TOs and CCs from seeing
584  // BBC information by looking at source of an OpenPGP encrypted mail
585  if ( to ) *to = addrSpecListToStringList( aMsg->extractAddrSpecs( "X-KMail-Recipients" ) );
586  aMsg->removeHeaderField( "X-KMail-Recipients" );
587  } else {
588  if ( to ) *to = addrSpecListToStringList( aMsg->extractAddrSpecs( "To" ) );
589  if ( cc ) *cc = addrSpecListToStringList( aMsg->extractAddrSpecs( "Cc" ) );
590  if ( bcc ) *bcc = addrSpecListToStringList( aMsg->extractAddrSpecs( "Bcc" ) );
591  }
592 }
593 
594 //-----------------------------------------------------------------------------
595 void KMSender::doSendMsgAux()
596 {
597  mSendProcStarted = true;
598 
599  // start sending the current message
600 
601  setStatusMsg(i18n("%3: subject of message","Sending message %1 of %2: %3")
602  .arg(mSentMessages+mFailedMessages+1).arg(mTotalMessages)
603  .arg(mCurrentMsg->subject()));
604  TQStringList to, cc, bcc;
605  TQString sender;
606  extractSenderToCCAndBcc( mCurrentMsg, &sender, &to, &cc, &bcc );
607 
608  // MDNs are required to have an empty envelope from as per RFC2298.
609  if ( messageIsDispositionNotificationReport( mCurrentMsg ) && GlobalSettings::self()->sendMDNsWithEmptySender() )
610  sender = "<>";
611 
612  const TQByteArray message = mCurrentMsg->asSendableString();
613  if ( sender.isEmpty() || !mSendProc->send( sender, to, cc, bcc, message ) ) {
614  if ( mCurrentMsg )
615  mCurrentMsg->setTransferInProgress( false );
616  if ( mOutboxFolder )
617  mOutboxFolder->unGetMsg( mFailedMessages );
618  mCurrentMsg = 0;
619  cleanup();
620  setStatusMsg(i18n("Failed to send (some) queued messages."));
621  return;
622  }
623  // Do *not* add code here, after send(). It can happen that this method
624  // is called recursively if send() emits the idle signal directly.
625 }
626 
627 
628 //-----------------------------------------------------------------------------
629 void KMSender::cleanup(void)
630 {
631  kdDebug(5006) << k_funcinfo << endl;
632  if (mSendProc && mSendProcStarted) mSendProc->finish();
633  mSendProc = 0;
634  mSendProcStarted = false;
635  if (mSendInProgress) kapp->deref();
636  mSendInProgress = false;
637  if (mCurrentMsg)
638  {
639  mCurrentMsg->setTransferInProgress( false );
640  mCurrentMsg = 0;
641  }
642  if ( mSentFolder ) {
643  mSentFolder->close("dosendsent");
644  mSentFolder = 0;
645  }
646  if ( mOutboxFolder ) {
647  disconnect( mOutboxFolder, TQ_SIGNAL(msgAdded(int)),
648  this, TQ_SLOT(outboxMsgAdded(int)) );
649  mOutboxFolder->close("dosendoutbox");
650  if ( mOutboxFolder->count( true ) == 0 ) {
651  mOutboxFolder->expunge();
652  }
653  else if ( mOutboxFolder->needsCompacting() ) {
654  mOutboxFolder->compact( KMFolder::CompactSilentlyNow );
655  }
656  mOutboxFolder = 0;
657  }
658 
659  mSendAborted = false;
660  mSentMessages = 0;
661  mFailedMessages = 0;
662  mSentBytes = 0;
663  if ( mProgressItem )
664  mProgressItem->setComplete();
665  mProgressItem = 0;
666  kmkernel->filterMgr()->deref();
667 }
668 
669 
670 //-----------------------------------------------------------------------------
671 void KMSender::slotAbortSend()
672 {
673  mSendAborted = true;
674  delete mPrecommand;
675  mPrecommand = 0;
676  if (mSendProc) mSendProc->abort();
677 }
678 
679 //-----------------------------------------------------------------------------
680 void KMSender::slotIdle()
681 {
682  assert(mSendProc != 0);
683 
684  TQString msg;
685  TQString errString;
686  if (mSendProc)
687  errString = mSendProc->lastErrorMessage();
688 
689  if (mSendAborted) {
690  // sending of message aborted
691  if ( mCurrentMsg ) {
692  mCurrentMsg->setTransferInProgress( false );
693  if ( mOutboxFolder )
694  mOutboxFolder->unGetMsg( mFailedMessages );
695  mCurrentMsg = 0;
696  }
697  msg = i18n("Sending aborted:\n%1\n"
698  "The message will stay in the 'outbox' folder until you either "
699  "fix the problem (e.g. a broken address) or remove the message "
700  "from the 'outbox' folder.\n"
701  "The following transport protocol was used:\n %2")
702  .arg(errString)
703  .arg(mMethodStr);
704  if (!errString.isEmpty()) KMessageBox::error(0,msg);
705  setStatusMsg( i18n( "Sending aborted." ) );
706  } else {
707  if (!mSendProc->sendOk()) {
708  if ( mCurrentMsg )
709  mCurrentMsg->setTransferInProgress( false );
710  if ( mOutboxFolder )
711  mOutboxFolder->unGetMsg( mFailedMessages );
712  mCurrentMsg = 0;
713  mFailedMessages++;
714  // reset cached password
715  TQMapIterator <TQString,TQString> pc;
716  if ( (pc = mPasswdCache.find( mMethodStr )) != mPasswdCache.end() ) {
717  mPasswdCache.erase(pc);
718  }
719  // Sending of message failed.
720  if (!errString.isEmpty()) {
721  int res = KMessageBox::Yes;
722  if (mSentMessages+mFailedMessages != mTotalMessages) {
723  msg = i18n("<p>Sending failed:</p>"
724  "<p>%1</p>"
725  "<p>The message will stay in the 'outbox' folder until you either "
726  "fix the problem (e.g. a broken address) or remove the message "
727  "from the 'outbox' folder.</p>"
728  "<p>The following transport protocol was used: %2</p>"
729  "<p>Do you want me to continue sending the remaining messages?</p>")
730  .arg(errString)
731  .arg(mMethodStr);
732  res = KMessageBox::warningYesNo( 0 , msg ,
733  i18n( "Continue Sending" ), i18n( "&Continue Sending" ),
734  i18n("&Abort Sending") );
735  } else {
736  msg = i18n("Sending failed:\n%1\n"
737  "The message will stay in the 'outbox' folder until you either "
738  "fix the problem (e.g. a broken address) or remove the message "
739  "from the 'outbox' folder.\n"
740  "The following transport protocol was used:\n %2")
741  .arg(errString)
742  .arg(mMethodStr);
743  KMessageBox::error(0,msg);
744  }
745  if (res == KMessageBox::Yes) {
746  // Try the next one.
747  doSendMsg();
748  return;
749  } else {
750  setStatusMsg( i18n( "Sending aborted." ) );
751  }
752  }
753  } else {
754  // Sending suceeded.
755  doSendMsg();
756  return;
757  }
758  }
759  mSendProc->finish();
760  mSendProc = 0;
761  mSendProcStarted = false;
762 
763  cleanup();
764 }
765 
766 
767 //-----------------------------------------------------------------------------
768 void KMSender::slotPrecommandFinished(bool normalExit)
769 {
770  delete mPrecommand;
771  mPrecommand = 0;
772  if (normalExit) mSendProc->start();
773  else slotIdle();
774 }
775 
776 
777 //-----------------------------------------------------------------------------
778 void KMSender::setSendImmediate(bool aSendImmediate)
779 {
780  mSendImmediate = aSendImmediate;
781 }
782 
783 
784 //-----------------------------------------------------------------------------
785 void KMSender::setSendQuotedPrintable(bool aSendQuotedPrintable)
786 {
787  mSendQuotedPrintable = aSendQuotedPrintable;
788 }
789 
790 
791 //-----------------------------------------------------------------------------
792 KMSendProc* KMSender::createSendProcFromString( const TQString & transport )
793 {
794  mTransportInfo->type = TQString();
795  int nr = KMTransportInfo::findTransport(transport);
796  if (nr)
797  {
798  mTransportInfo->readConfig(nr);
799  } else {
800  if (transport.startsWith("smtp://")) // should probably use KURL and SMTP_PROTOCOL
801  {
802  mTransportInfo->type = "smtp";
803  mTransportInfo->auth = false;
804  mTransportInfo->encryption = "NONE";
805  TQString serverport = transport.mid(7);
806  int colon = serverport.find(':');
807  if (colon != -1) {
808  mTransportInfo->host = serverport.left(colon);
809  mTransportInfo->port = serverport.mid(colon + 1);
810  } else {
811  mTransportInfo->host = serverport;
812  mTransportInfo->port = "25";
813  }
814  } else
815  if (transport.startsWith("smtps://")) // should probably use KURL and SMTPS_PROTOCOL
816  {
817  mTransportInfo->type = "smtps";
818  mTransportInfo->auth = false;
819  mTransportInfo->encryption = "ssl";
820  TQString serverport = transport.mid(7);
821  int colon = serverport.find(':');
822  if (colon != -1) {
823  mTransportInfo->host = serverport.left(colon);
824  mTransportInfo->port = serverport.mid(colon + 1);
825  } else {
826  mTransportInfo->host = serverport;
827  mTransportInfo->port = "465";
828  }
829  }
830  else if (transport.startsWith("file://"))
831  {
832  mTransportInfo->type = "sendmail";
833  mTransportInfo->host = transport.mid(7);
834  }
835  }
836  // strip off a trailing "/"
837  while (mTransportInfo->host.endsWith("/")) {
838  mTransportInfo->host.truncate(mTransportInfo->host.length()-1);
839  }
840 
841 
842  if (mTransportInfo->type == "sendmail")
843  return new KMSendSendmail(this);
844  if (mTransportInfo->type == "smtp" || mTransportInfo->type == "smtps")
845  return new KMSendSMTP(this);
846 
847  return 0L;
848 }
849 
850 //-----------------------------------------------------------------------------
851 void KMSender::setStatusByLink(const KMMessage *aMsg)
852 {
853  int n = 0;
854  while (1) {
855  ulong msn;
856  KMMsgStatus status;
857  aMsg->getLink(n, &msn, &status);
858  if (!msn || !status)
859  break;
860  n++;
861 
862  KMFolder *folder = 0;
863  int index = -1;
864  KMMsgDict::instance()->getLocation(msn, &folder, &index);
865  if (folder && index != -1) {
866  KMFolderOpener openFolder(folder, "setstatus");
867  if ( status == KMMsgStatusDeleted ) {
868  // Move the message to the trash folder
869  KMDeleteMsgCommand *cmd =
870  new KMDeleteMsgCommand( folder, folder->getMsg( index ) );
871  cmd->start();
872  } else {
873  folder->setStatus(index, status);
874  }
875  } else {
876  kdWarning(5006) << k_funcinfo << "Cannot update linked message, it could not be found!" << endl;
877  }
878  }
879 }
880 
881 //=============================================================================
882 //=============================================================================
883 KMSendProc::KMSendProc( KMSender * sender )
884  : TQObject( 0 ),
885  mSender( sender ),
886  mLastErrorMessage(),
887  mSendOk( false ),
888  mSending( false )
889 {
890 }
891 
892 //-----------------------------------------------------------------------------
893 void KMSendProc::reset()
894 {
895  mSending = false;
896  mSendOk = false;
897  mLastErrorMessage = TQString();
898 }
899 
900 //-----------------------------------------------------------------------------
901 void KMSendProc::failed(const TQString &aMsg)
902 {
903  mSending = false;
904  mSendOk = false;
905  mLastErrorMessage = aMsg;
906 }
907 
908 //-----------------------------------------------------------------------------
909 void KMSendProc::statusMsg(const TQString& aMsg)
910 {
911  if (mSender) mSender->setStatusMsg(aMsg);
912 }
913 
914 //=============================================================================
915 //=============================================================================
916 KMSendSendmail::KMSendSendmail( KMSender * sender )
917  : KMSendProc( sender ),
918  mMsgStr(),
919  mMsgPos( 0 ),
920  mMsgRest( 0 ),
921  mMailerProc( 0 )
922 {
923 
924 }
925 
926 KMSendSendmail::~KMSendSendmail() {
927  delete mMailerProc; mMailerProc = 0;
928 }
929 
930 bool KMSendSendmail::doStart() {
931 
932  if (mSender->transportInfo()->host.isEmpty())
933  {
934  const TQString str = i18n("Please specify a mailer program in the settings.");
935  const TQString msg = i18n("Sending failed:\n%1\n"
936  "The message will stay in the 'outbox' folder and will be resent.\n"
937  "Please remove it from there if you do not want the message to "
938  "be resent.\n"
939  "The following transport protocol was used:\n %2")
940  .arg(str + "\n")
941  .arg("sendmail://");
942  KMessageBox::information(0,msg);
943  return false;
944  }
945 
946  if (!mMailerProc)
947  {
948  mMailerProc = new TDEProcess;
949  assert(mMailerProc != 0);
950  connect(mMailerProc,TQ_SIGNAL(processExited(TDEProcess*)),
951  this, TQ_SLOT(sendmailExited(TDEProcess*)));
952  connect(mMailerProc,TQ_SIGNAL(wroteStdin(TDEProcess*)),
953  this, TQ_SLOT(wroteStdin(TDEProcess*)));
954  connect(mMailerProc,TQ_SIGNAL(receivedStderr(TDEProcess*,char*,int)),
955  this, TQ_SLOT(receivedStderr(TDEProcess*, char*, int)));
956  }
957  return true;
958 }
959 
960 void KMSendSendmail::doFinish() {
961  delete mMailerProc;
962  mMailerProc = 0;
963 }
964 
965 void KMSendSendmail::abort()
966 {
967  delete mMailerProc;
968  mMailerProc = 0;
969  mSendOk = false;
970  mMsgStr = 0;
971  idle();
972 }
973 
974 bool KMSendSendmail::doSend( const TQString & sender, const TQStringList & to, const TQStringList & cc, const TQStringList & bcc, const TQByteArray & message ) {
975  mMailerProc->clearArguments();
976  *mMailerProc << mSender->transportInfo()->host
977  << "-i" << "-f" << sender
978  << to << cc << bcc ;
979 
980  mMsgStr = message;
981 
982  if ( !mMailerProc->start( TDEProcess::NotifyOnExit, TDEProcess::All ) ) {
983  KMessageBox::information( 0, i18n("Failed to execute mailer program %1")
984  .arg( mSender->transportInfo()->host ) );
985  return false;
986  }
987  mMsgPos = mMsgStr.data();
988  mMsgRest = mMsgStr.size();
989  wroteStdin( mMailerProc );
990 
991  return true;
992 }
993 
994 
995 void KMSendSendmail::wroteStdin(TDEProcess *proc)
996 {
997  char* str;
998  int len;
999 
1000  assert(proc!=0);
1001  Q_UNUSED( proc );
1002 
1003  str = mMsgPos;
1004  len = (mMsgRest>1024 ? 1024 : mMsgRest);
1005 
1006  if (len <= 0)
1007  {
1008  mMailerProc->closeStdin();
1009  }
1010  else
1011  {
1012  mMsgRest -= len;
1013  mMsgPos += len;
1014  mMailerProc->writeStdin(str,len);
1015  // if code is added after writeStdin() TDEProcess probably initiates
1016  // a race condition.
1017  }
1018 }
1019 
1020 
1021 void KMSendSendmail::receivedStderr(TDEProcess *proc, char *buffer, int buflen)
1022 {
1023  assert(proc!=0);
1024  Q_UNUSED( proc );
1025  mLastErrorMessage.replace(mLastErrorMessage.length(), buflen, buffer);
1026 }
1027 
1028 
1029 void KMSendSendmail::sendmailExited(TDEProcess *proc)
1030 {
1031  assert(proc!=0);
1032  mSendOk = (proc->normalExit() && proc->exitStatus()==0);
1033  if (!mSendOk) failed(i18n("Sendmail exited abnormally."));
1034  mMsgStr = 0;
1035  emit idle();
1036 }
1037 
1038 
1039 
1040 //-----------------------------------------------------------------------------
1041 //=============================================================================
1042 //=============================================================================
1043 KMSendSMTP::KMSendSMTP(KMSender *sender)
1044  : KMSendProc(sender),
1045  mInProcess(false),
1046  mJob(0),
1047  mSlave(0)
1048 {
1049  TDEIO::Scheduler::connect(TQ_SIGNAL(slaveError(TDEIO::Slave *, int,
1050  const TQString &)), this, TQ_SLOT(slaveError(TDEIO::Slave *, int,
1051  const TQString &)));
1052 }
1053 
1054 KMSendSMTP::~KMSendSMTP()
1055 {
1056  if (mJob) mJob->kill();
1057 }
1058 
1059 bool KMSendSMTP::doSend( const TQString & sender, const TQStringList & to, const TQStringList & cc, const TQStringList & bcc, const TQByteArray & message ) {
1060  TQString query = "headers=0&from=";
1061  query += KURL::encode_string( sender );
1062 
1063  TQStringList::ConstIterator it;
1064 
1065  for ( it = to.begin(); it != to.end(); ++it )
1066  query += "&to=" + KURL::encode_string(*it);
1067  for ( it = cc.begin(); it != cc.end(); ++it )
1068  query += "&cc=" + KURL::encode_string(*it);
1069  for ( it = bcc.begin(); it != bcc.end(); ++it )
1070  query += "&bcc=" + KURL::encode_string(*it);
1071 
1072  KMTransportInfo * ti = mSender->transportInfo();
1073 
1074  if ( ti->specifyHostname )
1075  query += "&hostname=" + KURL::encode_string( ti->localHostname );
1076 
1077  if ( !kmkernel->msgSender()->sendQuotedPrintable() )
1078  query += "&body=8bit";
1079 
1080  KURL destination;
1081 
1082  destination.setProtocol((ti->encryption == "SSL") ? SMTPS_PROTOCOL : SMTP_PROTOCOL);
1083  destination.setHost(ti->host);
1084  destination.setPort(ti->port.toUShort());
1085 
1086  if (ti->auth)
1087  {
1088  TQMapIterator<TQString,TQString> tpc = mSender->mPasswdCache.find( ti->name );
1089  TQString tpwd = ( tpc != mSender->mPasswdCache.end() )?(*tpc):TQString();
1090 
1091  if ( ti->passwd().isEmpty() )
1092  ti->setPasswd( tpwd );
1093 
1094  if( (ti->user.isEmpty() || ti->passwd().isEmpty()) &&
1095  ti->authType != "GSSAPI" )
1096  {
1097  bool b = false;
1098  int result;
1099 
1100  KCursorSaver idle(KBusyPtr::idle());
1101  TQString passwd = ti->passwd();
1102  result = TDEIO::PasswordDialog::getNameAndPassword(ti->user, passwd,
1103  &b, i18n("You need to supply a username and a password to use this "
1104  "SMTP server."), false, TQString(), ti->name, TQString());
1105 
1106  if ( result != TQDialog::Accepted )
1107  {
1108  abort();
1109  return false;
1110  }
1111  if (int id = KMTransportInfo::findTransport(ti->name)) {
1112  ti->setPasswd( passwd );
1113  ti->writeConfig(id);
1114 
1115  // save the password into the cache
1116  mSender->mPasswdCache[ti->name] = passwd;
1117  }
1118  }
1119  destination.setUser(ti->user);
1120  destination.setPass(ti->passwd());
1121  }
1122 
1123  if (!mSlave || !mInProcess)
1124  {
1125  TDEIO::MetaData slaveConfig;
1126  slaveConfig.insert("tls", (ti->encryption == "TLS") ? "on" : "off");
1127  if (ti->auth) slaveConfig.insert("sasl", ti->authType);
1128  mSlave = TDEIO::Scheduler::getConnectedSlave(destination, slaveConfig);
1129  }
1130 
1131  if (!mSlave)
1132  {
1133  abort();
1134  return false;
1135  }
1136 
1137  // dotstuffing is now done by the slave (see setting of metadata)
1138  mMessage = message;
1139  mMessageLength = mMessage.size();
1140  mMessageOffset = 0;
1141 
1142  if ( mMessageLength )
1143  // allow +5% for subsequent LF->CRLF and dotstuffing (an average
1144  // over 2G-lines gives an average line length of 42-43):
1145  query += "&size=" + TQString::number( tqRound( mMessageLength * 1.05 ) );
1146 
1147  destination.setPath("/send");
1148  destination.setQuery( query );
1149 
1150  mJob = TDEIO::put( destination, -1, false, false, false );
1151  if ( !mJob ) {
1152  abort();
1153  return false;
1154  }
1155  mJob->addMetaData( "lf2crlf+dotstuff", "slave" );
1156  TDEIO::Scheduler::assignJobToSlave(mSlave, mJob);
1157  connect(mJob, TQ_SIGNAL(result(TDEIO::Job *)), this, TQ_SLOT(result(TDEIO::Job *)));
1158  connect(mJob, TQ_SIGNAL(dataReq(TDEIO::Job *, TQByteArray &)),
1159  this, TQ_SLOT(dataReq(TDEIO::Job *, TQByteArray &)));
1160  mSendOk = true;
1161  mInProcess = true;
1162  return true;
1163 }
1164 
1165 void KMSendSMTP::cleanup() {
1166  if(mJob)
1167  {
1168  mJob->kill(true);
1169  mJob = 0;
1170  mSlave = 0;
1171  }
1172 
1173  if (mSlave)
1174  {
1175  TDEIO::Scheduler::disconnectSlave(mSlave);
1176  mSlave = 0;
1177  }
1178 
1179  mInProcess = false;
1180 }
1181 
1182 void KMSendSMTP::abort() {
1183  cleanup();
1184  emit idle();
1185 }
1186 
1187 void KMSendSMTP::doFinish() {
1188  cleanup();
1189 }
1190 
1191 void KMSendSMTP::dataReq(TDEIO::Job *, TQByteArray &array)
1192 {
1193  // Send it by 32K chuncks
1194  const int chunkSize = TQMIN( mMessageLength - mMessageOffset, 32*1024 );
1195  if ( chunkSize > 0 ) {
1196  array.duplicate(mMessage.data() + mMessageOffset, chunkSize);
1197  mMessageOffset += chunkSize;
1198  } else
1199  {
1200  array.resize(0);
1201  mMessage.resize(0);
1202  }
1203  mSender->emitProgressInfo( mMessageOffset );
1204 }
1205 
1206 void KMSendSMTP::result(TDEIO::Job *_job)
1207 {
1208  if (!mJob) return;
1209  mJob = 0;
1210 
1211  if(_job->error())
1212  {
1213  mSendOk = false;
1214  if (_job->error() == TDEIO::ERR_SLAVE_DIED) mSlave = 0;
1215  failed(_job->errorString());
1216  abort();
1217  } else {
1218  emit idle();
1219  }
1220 }
1221 
1222 void KMSendSMTP::slaveError(TDEIO::Slave *aSlave, int error, const TQString &errorMsg)
1223 {
1224  if (aSlave == mSlave)
1225  {
1226  if (error == TDEIO::ERR_SLAVE_DIED) mSlave = 0;
1227  mSendOk = false;
1228  mJob = 0;
1229  failed(TDEIO::buildErrorString(error, errorMsg));
1230  abort();
1231  }
1232 }
1233 
1234 #include "kmsender.moc"
1235 #include "kmsender_p.moc"
sets a cursor and makes sure it's restored on destruction Create a KCursorSaver object when you want ...
Definition: kcursorsaver.h:14
RAII for KMFolder::open() / close().
Definition: kmfolder.h:688
Mail folder.
Definition: kmfolder.h:69
int moveMsg(KMMessage *msg, int *index_return=0)
Detaches the given message from it's current folder and adds it to this folder.
Definition: kmfolder.cpp:425
KMMsgInfo * unGetMsg(int idx)
Replace KMMessage with KMMsgInfo and delete KMMessage
Definition: kmfolder.cpp:326
int addMsg(KMMessage *msg, int *index_return=0)
Add the given message to the folder.
Definition: kmfolder.cpp:390
void close(const char *owner, bool force=false)
Close folder.
Definition: kmfolder.cpp:489
KMMessage * getMsg(int idx)
Read message at given index.
Definition: kmfolder.cpp:321
void setStatus(int idx, KMMsgStatus status, bool toggle=false)
Set the status of the message at index idx to status.
Definition: kmfolder.cpp:831
int count(bool cache=false) const
Number of messages in this folder.
Definition: kmfolder.cpp:445
int open(const char *owner)
Open folder for access.
Definition: kmfolder.cpp:479
bool isReadOnly() const
Is the folder read-only?
Definition: kmfolder.cpp:561
This is a Mime Message.
Definition: kmmessage.h:68
static TQString generateMessageId(const TQString &addr)
Generates the Message-Id.
Definition: kmmessage.cpp:3364
void setTransferInProgress(bool value, bool force=false)
Set that the message shall not be deleted because it is still required.
Definition: kmmessage.cpp:243
TQString msgId() const
Get or set the 'Message-Id' header field.
Definition: kmmessage.cpp:2182
KMMessage * unencryptedMsg() const
Returns an unencrypted copy of this message or 0 if none exists.
Definition: kmmessage.h:137
TQString sender() const
Definition: kmmessage.cpp:2039
void setStatus(const KMMsgStatus status, int idx=-1)
Set status and mark dirty.
Definition: kmmessage.cpp:4153
void setUnencryptedMsg(KMMessage *unencrypted)
Specifies an unencrypted copy of this message to be stored in a separate member variable to allow sav...
Definition: kmmessage.cpp:265
DwMediaType & dwContentType()
Return reference to Content-Type header for direct manipulation.
Definition: kmmessage.cpp:389
TQString to() const
Get or set the 'To' header field.
Definition: kmmessage.cpp:1894
void removeHeaderField(const TQCString &name)
Remove header field with given name.
Definition: kmmessage.cpp:2317
TQString headerField(const TQCString &name) const
Returns the value of a header field with the given name.
Definition: kmmessage.cpp:2289
void getLink(int n, ulong *retMsgSerNum, KMMsgStatus *reStatus) const
Returns the information for the Nth link into retMsg and reStatus.
Definition: kmmessage.cpp:4216
void getLocation(unsigned long key, KMFolder **retFolder, int *retIndex) const
Returns the folder the message represented by the serial number key is in and the index in that folde...
Definition: kmmsgdict.cpp:319
static const KMMsgDict * instance()
Access the globally unique MessageDict.
Definition: kmmsgdict.cpp:167