kmail

kmcommands.cpp
1 /* -*- mode: C++; c-file-style: "gnu" -*-
2  This file is part of KMail, the KDE mail client.
3  Copyright (c) 2002 Don Sanders <sanders@kde.org>
4 
5  KMail is free software; you can redistribute it and/or modify it
6  under the terms of the GNU General Public License, version 2, as
7  published by the Free Software Foundation.
8 
9  KMail is distributed in the hope that it will be useful, but
10  WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18 
19 //
20 // This file implements various "command" classes. These command classes
21 // are based on the command design pattern.
22 //
23 // Historically various operations were implemented as slots of KMMainWin.
24 // This proved inadequate as KMail has multiple top level windows
25 // (KMMainWin, KMReaderMainWin, SearchWindow, KMComposeWin) that may
26 // benefit from using these operations. It is desirable that these
27 // classes can operate without depending on or altering the state of
28 // a KMMainWin, in fact it is possible no KMMainWin object even exists.
29 //
30 // Now these operations have been rewritten as KMCommand based classes,
31 // making them independent of KMMainWin.
32 //
33 // The base command class KMCommand is async, which is a difference
34 // from the conventional command pattern. As normal derived classes implement
35 // the execute method, but client classes call start() instead of
36 // calling execute() directly. start() initiates async operations,
37 // and on completion of these operations calls execute() and then deletes
38 // the command. (So the client must not construct commands on the stack).
39 //
40 // The type of async operation supported by KMCommand is retrieval
41 // of messages from an IMAP server.
42 
43 #include "kmcommands.h"
44 
45 #ifdef HAVE_CONFIG_H
46 #include <config.h>
47 #endif
48 
49 #include <errno.h>
50 #include <mimelib/enum.h>
51 #include <mimelib/field.h>
52 #include <mimelib/mimepp.h>
53 #include <mimelib/string.h>
54 #include <kapplication.h>
55 #include <dcopclient.h>
56 
57 #include <tqtextcodec.h>
58 #include <tqpopupmenu.h>
59 #include <tqeventloop.h>
60 
61 #include <libemailfunctions/email.h>
62 #include <kdcopservicestarter.h>
63 #include <kdebug.h>
64 #include <kfiledialog.h>
65 #include <kabc/stdaddressbook.h>
66 #include <kabc/addresseelist.h>
67 #include <kdirselectdialog.h>
68 #include <klocale.h>
69 #include <kmessagebox.h>
70 #include <kparts/browserextension.h>
71 #include <kprogress.h>
72 #include <krun.h>
73 #include <kbookmarkmanager.h>
74 #include <kstandarddirs.h>
75 #include <ktempfile.h>
76 #include <kimproxy.h>
77 #include <kuserprofile.h>
78 // KIO headers
79 #include <kio/job.h>
80 #include <kio/netaccess.h>
81 
82 #include <libkpimidentities/identitymanager.h>
83 
84 #include "actionscheduler.h"
85 using KMail::ActionScheduler;
86 #include "mailinglist-magic.h"
87 #include "kmaddrbook.h"
88 #include <kaddrbook.h>
89 #include "composer.h"
90 #include "kmfiltermgr.h"
91 #include "kmfoldermbox.h"
92 #include "kmfolderimap.h"
93 #include "kmfoldermgr.h"
94 #include "kmheaders.h"
95 #include "headeritem.h"
96 #include "kmmainwidget.h"
97 #include "kmmsgdict.h"
98 #include "messagesender.h"
99 #include "kmmsgpartdlg.h"
100 #include "undostack.h"
101 #include "kcursorsaver.h"
102 #include "partNode.h"
103 #include "objecttreeparser.h"
104 #include "csshelper.h"
105 using KMail::ObjectTreeParser;
106 using KMail::FolderJob;
107 #include "chiasmuskeyselector.h"
108 #include "mailsourceviewer.h"
109 using KMail::MailSourceViewer;
110 #include "kmreadermainwin.h"
111 #include "secondarywindow.h"
113 #include "redirectdialog.h"
115 #include "util.h"
116 #include "templateparser.h"
117 #include "editorwatcher.h"
118 #include "korghelper.h"
119 
120 #include "broadcaststatus.h"
121 #include "globalsettings.h"
122 
123 #include <libkdepim/kfileio.h>
124 #include "kcalendariface_stub.h"
125 
126 #include "progressmanager.h"
127 using KPIM::ProgressManager;
128 using KPIM::ProgressItem;
129 #include <kmime_mdn.h>
130 using namespace KMime;
131 
132 #include <kleo/specialjob.h>
133 #include <kleo/cryptobackend.h>
134 #include <kleo/cryptobackendfactory.h>
135 
136 #include <tqclipboard.h>
137 
138 #include <memory>
139 
140 class LaterDeleterWithCommandCompletion : public KMail::Util::LaterDeleter
141 {
142 public:
143  LaterDeleterWithCommandCompletion( KMCommand* command )
144  :LaterDeleter( command ), m_result( KMCommand::Failed )
145  {
146  }
147  ~LaterDeleterWithCommandCompletion()
148  {
149  setResult( m_result );
150  KMCommand *command = static_cast<KMCommand*>( m_object );
151  emit command->completed( command );
152  }
153  void setResult( KMCommand::Result v ) { m_result = v; }
154 private:
155  KMCommand::Result m_result;
156 };
157 
158 
159 KMCommand::KMCommand( TQWidget *parent )
160  : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ),
161  mEmitsCompletedItself( false ), mParent( parent )
162 {
163 }
164 
165 KMCommand::KMCommand( TQWidget *parent, const TQPtrList<KMMsgBase> &msgList )
166  : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ),
167  mEmitsCompletedItself( false ), mParent( parent ), mMsgList( msgList )
168 {
169 }
170 
171 KMCommand::KMCommand( TQWidget *parent, KMMsgBase *msgBase )
172  : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ),
173  mEmitsCompletedItself( false ), mParent( parent )
174 {
175  mMsgList.append( msgBase );
176 }
177 
178 KMCommand::KMCommand( TQWidget *parent, KMMessage *msg )
179  : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ),
180  mEmitsCompletedItself( false ), mParent( parent )
181 {
182  if (msg)
183  mMsgList.append( &msg->toMsgBase() );
184 }
185 
186 KMCommand::~KMCommand()
187 {
188  TQValueListIterator<TQGuardedPtr<KMFolder> > fit;
189  for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) {
190  if (!(*fit))
191  continue;
192  (*fit)->close("kmcommand");
193  }
194 }
195 
196 KMCommand::Result KMCommand::result()
197 {
198  if ( mResult == Undefined )
199  kdDebug(5006) << k_funcinfo << "mResult is Undefined" << endl;
200  return mResult;
201 }
202 
203 void KMCommand::start()
204 {
205  TQTimer::singleShot( 0, this, TQT_SLOT( slotStart() ) );
206 }
207 
208 
209 const TQPtrList<KMMessage> KMCommand::retrievedMsgs() const
210 {
211  return mRetrievedMsgs;
212 }
213 
214 KMMessage *KMCommand::retrievedMessage() const
215 {
216  return mRetrievedMsgs.getFirst();
217 }
218 
219 TQWidget *KMCommand::parentWidget() const
220 {
221  return mParent;
222 }
223 
224 int KMCommand::mCountJobs = 0;
225 
226 void KMCommand::slotStart()
227 {
228  connect( this, TQT_SIGNAL( messagesTransfered( KMCommand::Result ) ),
229  this, TQT_SLOT( slotPostTransfer( KMCommand::Result ) ) );
230  kmkernel->filterMgr()->ref();
231 
232  if (mMsgList.find(0) != -1) {
233  emit messagesTransfered( Failed );
234  return;
235  }
236 
237  if ((mMsgList.count() == 1) &&
238  (mMsgList.getFirst()->isMessage()) &&
239  (mMsgList.getFirst()->parent() == 0))
240  {
241  // Special case of operating on message that isn't in a folder
242  mRetrievedMsgs.append((KMMessage*)mMsgList.getFirst());
243  emit messagesTransfered( OK );
244  return;
245  }
246 
247  for ( KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next() ) {
248  if ( mb ) {
249  if ( !mb->parent() ) {
250  emit messagesTransfered( Failed );
251  return;
252  } else {
253  keepFolderOpen( mb->parent() );
254  }
255  }
256  }
257 
258  // transfer the selected messages first
259  transferSelectedMsgs();
260 }
261 
262 void KMCommand::slotPostTransfer( KMCommand::Result result )
263 {
264  disconnect( this, TQT_SIGNAL( messagesTransfered( KMCommand::Result ) ),
265  this, TQT_SLOT( slotPostTransfer( KMCommand::Result ) ) );
266  if ( result == OK )
267  result = execute();
268  mResult = result;
269  TQPtrListIterator<KMMessage> it( mRetrievedMsgs );
270  KMMessage* msg;
271  while ( (msg = it.current()) != 0 )
272  {
273  ++it;
274  if (msg->parent())
275  msg->setTransferInProgress(false);
276  }
277  kmkernel->filterMgr()->deref();
278  if ( !emitsCompletedItself() )
279  emit completed( this );
280  if ( !deletesItself() )
281  deleteLater();
282 }
283 
284 void KMCommand::transferSelectedMsgs()
285 {
286  // make sure no other transfer is active
287  if (KMCommand::mCountJobs > 0) {
288  emit messagesTransfered( Failed );
289  return;
290  }
291 
292  bool complete = true;
293  KMCommand::mCountJobs = 0;
294  mCountMsgs = 0;
295  mRetrievedMsgs.clear();
296  mCountMsgs = mMsgList.count();
297  uint totalSize = 0;
298  // the KProgressDialog for the user-feedback. Only enable it if it's needed.
299  // For some commands like KMSeStatusCommand it's not needed. Note, that
300  // for some reason the KProgressDialog eats the MouseReleaseEvent (if a
301  // command is executed after the MousePressEvent), cf. bug #71761.
302  if ( mCountMsgs > 0 ) {
303  mProgressDialog = new KProgressDialog(mParent, "transferProgress",
304  i18n("Please wait"),
305  i18n("Please wait while the message is transferred",
306  "Please wait while the %n messages are transferred", mMsgList.count()),
307  true);
308  mProgressDialog->setMinimumDuration(1000);
309  }
310  for (KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next())
311  {
312  // check if all messages are complete
313  KMMessage *thisMsg = 0;
314  if ( mb->isMessage() )
315  thisMsg = static_cast<KMMessage*>(mb);
316  else
317  {
318  KMFolder *folder = mb->parent();
319  int idx = folder->find(mb);
320  if (idx < 0) continue;
321  thisMsg = folder->getMsg(idx);
322  }
323  if (!thisMsg) continue;
324  if ( thisMsg->transferInProgress() &&
325  thisMsg->parent()->folderType() == KMFolderTypeImap )
326  {
327  thisMsg->setTransferInProgress( false, true );
328  thisMsg->parent()->ignoreJobsForMessage( thisMsg );
329  }
330 
331  if ( thisMsg->parent() && !thisMsg->isComplete() &&
332  ( !mProgressDialog || !mProgressDialog->wasCancelled() ) )
333  {
334  kdDebug(5006)<<"### INCOMPLETE\n";
335  // the message needs to be transferred first
336  complete = false;
337  KMCommand::mCountJobs++;
338  FolderJob *job = thisMsg->parent()->createJob(thisMsg);
339  job->setCancellable( false );
340  totalSize += thisMsg->msgSizeServer();
341  // emitted when the message was transferred successfully
342  connect(job, TQT_SIGNAL(messageRetrieved(KMMessage*)),
343  this, TQT_SLOT(slotMsgTransfered(KMMessage*)));
344  // emitted when the job is destroyed
345  connect(job, TQT_SIGNAL(finished()),
346  this, TQT_SLOT(slotJobFinished()));
347  connect(job, TQT_SIGNAL(progress(unsigned long, unsigned long)),
348  this, TQT_SLOT(slotProgress(unsigned long, unsigned long)));
349  // msg musn't be deleted
350  thisMsg->setTransferInProgress(true);
351  job->start();
352  } else {
353  thisMsg->setTransferInProgress(true);
354  mRetrievedMsgs.append(thisMsg);
355  }
356  }
357 
358  if (complete)
359  {
360  delete mProgressDialog;
361  mProgressDialog = 0;
362  emit messagesTransfered( OK );
363  } else {
364  // wait for the transfer and tell the progressBar the necessary steps
365  if ( mProgressDialog ) {
366  connect(mProgressDialog, TQT_SIGNAL(cancelClicked()),
367  this, TQT_SLOT(slotTransferCancelled()));
368  mProgressDialog->progressBar()->setTotalSteps(totalSize);
369  }
370  }
371 }
372 
373 void KMCommand::slotMsgTransfered(KMMessage* msg)
374 {
375  if ( mProgressDialog && mProgressDialog->wasCancelled() ) {
376  emit messagesTransfered( Canceled );
377  return;
378  }
379 
380  // save the complete messages
381  mRetrievedMsgs.append(msg);
382 }
383 
384 void KMCommand::slotProgress( unsigned long done, unsigned long /*total*/ )
385 {
386  mProgressDialog->progressBar()->setProgress( done );
387 }
388 
389 void KMCommand::slotJobFinished()
390 {
391  // the job is finished (with / without error)
392  KMCommand::mCountJobs--;
393 
394  if ( mProgressDialog && mProgressDialog->wasCancelled() ) return;
395 
396  if ( (mCountMsgs - static_cast<int>(mRetrievedMsgs.count())) > KMCommand::mCountJobs )
397  {
398  // the message wasn't retrieved before => error
399  if ( mProgressDialog )
400  mProgressDialog->hide();
401  slotTransferCancelled();
402  return;
403  }
404  // update the progressbar
405  if ( mProgressDialog ) {
406  mProgressDialog->setLabel(i18n("Please wait while the message is transferred",
407  "Please wait while the %n messages are transferred", KMCommand::mCountJobs));
408  }
409  if (KMCommand::mCountJobs == 0)
410  {
411  // all done
412  delete mProgressDialog;
413  mProgressDialog = 0;
414  emit messagesTransfered( OK );
415  }
416 }
417 
418 void KMCommand::slotTransferCancelled()
419 {
420  // kill the pending jobs
421  TQValueListIterator<TQGuardedPtr<KMFolder> > fit;
422  for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) {
423  if (!(*fit))
424  continue;
425  KMFolder *folder = *fit;
426  KMFolderImap *imapFolder = dynamic_cast<KMFolderImap*>(folder);
427  if (imapFolder && imapFolder->account()) {
428  imapFolder->account()->killAllJobs();
429  }
430  }
431 
432  KMCommand::mCountJobs = 0;
433  mCountMsgs = 0;
434  // unget the transfered messages
435  TQPtrListIterator<KMMessage> it( mRetrievedMsgs );
436  KMMessage* msg;
437  while ( (msg = it.current()) != 0 )
438  {
439  KMFolder *folder = msg->parent();
440  ++it;
441  if (!folder)
442  continue;
443  msg->setTransferInProgress(false);
444  int idx = folder->find(msg);
445  if (idx > 0) folder->unGetMsg(idx);
446  }
447  mRetrievedMsgs.clear();
448  emit messagesTransfered( Canceled );
449 }
450 
451 void KMCommand::keepFolderOpen( KMFolder *folder )
452 {
453  folder->open( "kmcommand" );
454  mFolders.append( folder );
455 }
456 
457 KMMailtoComposeCommand::KMMailtoComposeCommand( const KURL &url,
458  KMMessage *msg )
459  :mUrl( url ), mMessage( msg )
460 {
461 }
462 
463 KMCommand::Result KMMailtoComposeCommand::execute()
464 {
465  KMMessage *msg = new KMMessage;
466  uint id = 0;
467 
468  if ( mMessage && mMessage->parent() )
469  id = mMessage->parent()->identity();
470 
471  msg->initHeader(id);
472  msg->setCharset("utf-8");
473  msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
474 
475  KMail::Composer * win = KMail::makeComposer( msg, id );
476  win->setCharset("", true);
477  win->setFocusToSubject();
478  win->show();
479 
480  return OK;
481 }
482 
483 
484 KMMailtoReplyCommand::KMMailtoReplyCommand( TQWidget *parent,
485  const KURL &url, KMMessage *msg, const TQString &selection )
486  :KMCommand( parent, msg ), mUrl( url ), mSelection( selection )
487 {
488 }
489 
490 KMCommand::Result KMMailtoReplyCommand::execute()
491 {
492  //TODO : consider factoring createReply into this method.
493  KMMessage *msg = retrievedMessage();
494  if ( !msg || !msg->codec() ) {
495  return Failed;
496  }
497  KMMessage *rmsg = msg->createReply( KMail::ReplyNone, mSelection );
498  rmsg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
499 
500  KMail::Composer * win = KMail::makeComposer( rmsg, 0 );
501  win->setCharset(msg->codec()->mimeName(), true);
502  win->setReplyFocus();
503  win->show();
504 
505  return OK;
506 }
507 
508 
509 KMMailtoForwardCommand::KMMailtoForwardCommand( TQWidget *parent,
510  const KURL &url, KMMessage *msg )
511  :KMCommand( parent, msg ), mUrl( url )
512 {
513 }
514 
515 KMCommand::Result KMMailtoForwardCommand::execute()
516 {
517  //TODO : consider factoring createForward into this method.
518  KMMessage *msg = retrievedMessage();
519  if ( !msg || !msg->codec() ) {
520  return Failed;
521  }
522  KMMessage *fmsg = msg->createForward();
523  fmsg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
524 
525  KMail::Composer * win = KMail::makeComposer( fmsg );
526  win->setCharset(msg->codec()->mimeName(), true);
527  win->show();
528 
529  return OK;
530 }
531 
532 
533 KMAddBookmarksCommand::KMAddBookmarksCommand( const KURL &url, TQWidget *parent )
534  : KMCommand( parent ), mUrl( url )
535 {
536 }
537 
538 KMCommand::Result KMAddBookmarksCommand::execute()
539 {
540  TQString filename = locateLocal( "data", TQString::fromLatin1("konqueror/bookmarks.xml") );
541  KBookmarkManager *bookManager = KBookmarkManager::managerForFile( filename,
542  false );
543  KBookmarkGroup group = bookManager->root();
544  group.addBookmark( bookManager, mUrl.path(), KURL( mUrl ) );
545  if( bookManager->save() ) {
546  bookManager->emitChanged( group );
547  }
548 
549  return OK;
550 }
551 
552 KMMailtoAddAddrBookCommand::KMMailtoAddAddrBookCommand( const KURL &url,
553  TQWidget *parent )
554  : KMCommand( parent ), mUrl( url )
555 {
556 }
557 
558 KMCommand::Result KMMailtoAddAddrBookCommand::execute()
559 {
560  KAddrBookExternal::addEmail( KMMessage::decodeMailtoUrl( mUrl.path() ),
561  parentWidget() );
562 
563  return OK;
564 }
565 
566 
567 KMMailtoOpenAddrBookCommand::KMMailtoOpenAddrBookCommand( const KURL &url,
568  TQWidget *parent )
569  : KMCommand( parent ), mUrl( url )
570 {
571 }
572 
573 KMCommand::Result KMMailtoOpenAddrBookCommand::execute()
574 {
575  KAddrBookExternal::openEmail( KMMessage::decodeMailtoUrl( mUrl.path() ),
576  parentWidget() );
577 
578  return OK;
579 }
580 
581 
582 KMUrlCopyCommand::KMUrlCopyCommand( const KURL &url, KMMainWidget *mainWidget )
583  :mUrl( url ), mMainWidget( mainWidget )
584 {
585 }
586 
587 KMCommand::Result KMUrlCopyCommand::execute()
588 {
589  TQClipboard* clip = TQApplication::clipboard();
590 
591  if (mUrl.protocol() == "mailto") {
592  // put the url into the mouse selection and the clipboard
593  TQString address = KMMessage::decodeMailtoUrl( mUrl.path() );
594  clip->setSelectionMode( true );
595  clip->setText( address );
596  clip->setSelectionMode( false );
597  clip->setText( address );
598  KPIM::BroadcastStatus::instance()->setStatusMsg( i18n( "Address copied to clipboard." ));
599  } else {
600  // put the url into the mouse selection and the clipboard
601  clip->setSelectionMode( true );
602  clip->setText( mUrl.url() );
603  clip->setSelectionMode( false );
604  clip->setText( mUrl.url() );
605  KPIM::BroadcastStatus::instance()->setStatusMsg( i18n( "URL copied to clipboard." ));
606  }
607 
608  return OK;
609 }
610 
611 
612 KMUrlOpenCommand::KMUrlOpenCommand( const KURL &url, KMReaderWin *readerWin )
613  :mUrl( url ), mReaderWin( readerWin )
614 {
615 }
616 
617 KMCommand::Result KMUrlOpenCommand::execute()
618 {
619  if ( !mUrl.isEmpty() )
620  mReaderWin->slotUrlOpen( mUrl, KParts::URLArgs() );
621 
622  return OK;
623 }
624 
625 
626 KMUrlSaveCommand::KMUrlSaveCommand( const KURL &url, TQWidget *parent )
627  : KMCommand( parent ), mUrl( url )
628 {
629 }
630 
631 KMCommand::Result KMUrlSaveCommand::execute()
632 {
633  if ( mUrl.isEmpty() )
634  return OK;
635  KURL saveUrl = KFileDialog::getSaveURL(mUrl.fileName(), TQString(),
636  parentWidget() );
637  if ( saveUrl.isEmpty() )
638  return Canceled;
639  if ( KIO::NetAccess::exists( saveUrl, false, parentWidget() ) )
640  {
641  if (KMessageBox::warningContinueCancel(0,
642  i18n("<qt>File <b>%1</b> exists.<br>Do you want to replace it?</qt>")
643  .arg(saveUrl.prettyURL()), i18n("Save to File"), i18n("&Replace"))
644  != KMessageBox::Continue)
645  return Canceled;
646  }
647  KIO::Job *job = KIO::file_copy(mUrl, saveUrl, -1, true);
648  connect(job, TQT_SIGNAL(result(KIO::Job*)), TQT_SLOT(slotUrlSaveResult(KIO::Job*)));
649  setEmitsCompletedItself( true );
650  return OK;
651 }
652 
653 void KMUrlSaveCommand::slotUrlSaveResult( KIO::Job *job )
654 {
655  if ( job->error() ) {
656  job->showErrorDialog();
657  setResult( Failed );
658  emit completed( this );
659  }
660  else {
661  setResult( OK );
662  emit completed( this );
663  }
664 }
665 
666 
667 KMEditMsgCommand::KMEditMsgCommand( TQWidget *parent, KMMessage *msg )
668  :KMCommand( parent, msg )
669 {
670 }
671 
672 KMCommand::Result KMEditMsgCommand::execute()
673 {
674  KMMessage *msg = retrievedMessage();
675  if ( !msg || !msg->parent() ||
676  ( !kmkernel->folderIsDraftOrOutbox( msg->parent() ) &&
677  !kmkernel->folderIsTemplates( msg->parent() ) ) )
678  return Failed;
679 
680  // Remember the old parent, we need it a bit further down to be able
681  // to put the unchanged messsage back in the original folder if the nth
682  // edit is discarded, for n > 1.
683  KMFolder *parent = msg->parent();
684  if ( parent )
685  parent->take( parent->find( msg ) );
686 
687  KMail::Composer * win = KMail::makeComposer();
688  msg->setTransferInProgress(false); // From here on on, the composer owns the message.
689  win->setMsg(msg, false, true);
690  win->setFolder( parent );
691  win->show();
692 
693  return OK;
694 }
695 
696 KMUseTemplateCommand::KMUseTemplateCommand( TQWidget *parent, KMMessage *msg )
697  :KMCommand( parent, msg )
698 {
699 }
700 
701 KMCommand::Result KMUseTemplateCommand::execute()
702 {
703  KMMessage *msg = retrievedMessage();
704  if ( !msg || !msg->parent() ||
705  !kmkernel->folderIsTemplates( msg->parent() ) )
706  return Failed;
707 
708  // Take a copy of the original message, which remains unchanged.
709  KMMessage *newMsg = new KMMessage( new DwMessage( *msg->asDwMessage() ) );
710  newMsg->setComplete( msg->isComplete() );
711 
712  // these fields need to be regenerated for the new message
713  newMsg->removeHeaderField("Date");
714  newMsg->removeHeaderField("Message-ID");
715 
716  KMail::Composer *win = KMail::makeComposer();
717  newMsg->setTransferInProgress( false ); // From here on on, the composer owns the message.
718  win->setMsg( newMsg, false, true );
719  win->show();
720 
721  return OK;
722 }
723 
724 KMShowMsgSrcCommand::KMShowMsgSrcCommand( TQWidget *parent,
725  KMMessage *msg, bool fixedFont )
726  :KMCommand( parent, msg ), mFixedFont( fixedFont )
727 {
728  // remember complete state
729  mMsgWasComplete = msg->isComplete();
730 }
731 
732 KMCommand::Result KMShowMsgSrcCommand::execute()
733 {
734  KMMessage *msg = retrievedMessage();
735  if ( !msg || !msg->codec() ) {
736  return Failed;
737  }
738  if ( msg->isComplete() && !mMsgWasComplete )
739  msg->notify(); // notify observers as msg was transfered
740  TQString str = msg->codec()->toUnicode( msg->asString() );
741 
742  MailSourceViewer *viewer = new MailSourceViewer(); // deletes itself upon close
743  viewer->setCaption( i18n("Message as Plain Text") );
744  viewer->setText(str);
745  if( mFixedFont )
746  viewer->setFont(KGlobalSettings::fixedFont());
747 
748  // Well, there is no widget to be seen here, so we have to use TQCursor::pos()
749  // Update: (GS) I'm not going to make this code behave according to Xinerama
750  // configuration because this is quite the hack.
751  if (TQApplication::desktop()->isVirtualDesktop()) {
752  int scnum = TQApplication::desktop()->screenNumber(TQCursor::pos());
753  viewer->resize(TQApplication::desktop()->screenGeometry(scnum).width()/2,
754  2*TQApplication::desktop()->screenGeometry(scnum).height()/3);
755  } else {
756  viewer->resize(TQApplication::desktop()->geometry().width()/2,
757  2*TQApplication::desktop()->geometry().height()/3);
758  }
759  viewer->show();
760 
761  return OK;
762 }
763 
764 static KURL subjectToUrl( const TQString & subject )
765 {
766  // We need to replace colons with underscores since those cause problems with KFileDialog (bug
767  // in KFileDialog though) and also on Windows filesystems.
768  // We also look at the special case of ": ", since converting that to "_ " would look strange,
769  // simply "_" looks better.
770  // We also don't allow filenames starting with a dot, since then the file is hidden and the poor
771  // user can't find it anymore.
772  // Don't allow filenames starting with a tilde either, since that will cause the file dialog to
773  // discard the filename entirely.
774  // https://issues.kolab.org/issue3805
775  const TQString filter = i18n( "*.mbox|email messages (*.mbox)\n*|all files (*)" );
776  TQString cleanSubject = subject.stripWhiteSpace()
777  .replace( TQDir::separator(), '_' )
778  .replace( ": ", "_" )
779  .replace( ':', '_' )
780  .replace( '.', '_' )
781  .replace( '~', '_' );
782  return KFileDialog::getSaveURL( cleanSubject, filter );
783 }
784 
785 KMSaveMsgCommand::KMSaveMsgCommand( TQWidget *parent, KMMessage * msg )
786  : KMCommand( parent ),
787  mMsgListIndex( 0 ),
788  mStandAloneMessage( 0 ),
789  mOffset( 0 ),
790  mTotalSize( msg ? msg->msgSize() : 0 )
791 {
792  if ( !msg ) return;
793  setDeletesItself( true );
794  // If the mail has a serial number, operate on sernums, if it does not
795  // we need to work with the pointer, but can be reasonably sure it won't
796  // go away, since it'll be an encapsulated message or one that was opened
797  // from an .eml file.
798  if ( msg->getMsgSerNum() != 0 ) {
799  mMsgList.append( msg->getMsgSerNum() );
800  if ( msg->parent() ) {
801  msg->parent()->open( "kmsavemsgcommand" );
802  }
803  } else {
804  mStandAloneMessage = msg;
805  }
806  mUrl = subjectToUrl( msg->cleanSubject() );
807 }
808 
809 KMSaveMsgCommand::KMSaveMsgCommand( TQWidget *parent,
810  const TQPtrList<KMMsgBase> &msgList )
811  : KMCommand( parent ),
812  mMsgListIndex( 0 ),
813  mStandAloneMessage( 0 ),
814  mOffset( 0 ),
815  mTotalSize( 0 )
816 {
817  if (!msgList.getFirst())
818  return;
819  setDeletesItself( true );
820  KMMsgBase *msgBase = msgList.getFirst();
821 
822  // We operate on serNums and not the KMMsgBase pointers, as those can
823  // change, or become invalid when changing the current message, switching
824  // folders, etc.
825  TQPtrListIterator<KMMsgBase> it(msgList);
826  while ( it.current() ) {
827  mMsgList.append( (*it)->getMsgSerNum() );
828  mTotalSize += (*it)->msgSize();
829  if ((*it)->parent() != 0)
830  (*it)->parent()->open("kmcommand");
831  ++it;
832  }
833  mMsgListIndex = 0;
834  mUrl = subjectToUrl( msgBase->cleanSubject() );
835 }
836 
837 KURL KMSaveMsgCommand::url()
838 {
839  return mUrl;
840 }
841 
842 KMCommand::Result KMSaveMsgCommand::execute()
843 {
844  mJob = KIO::put( mUrl, S_IRUSR|S_IWUSR, false, false );
845  mJob->slotTotalSize( mTotalSize );
846  mJob->setAsyncDataEnabled( true );
847  mJob->setReportDataSent( true );
848  connect(mJob, TQT_SIGNAL(dataReq(KIO::Job*, TQByteArray &)),
849  TQT_SLOT(slotSaveDataReq()));
850  connect(mJob, TQT_SIGNAL(result(KIO::Job*)),
851  TQT_SLOT(slotSaveResult(KIO::Job*)));
852  setEmitsCompletedItself( true );
853  return OK;
854 }
855 
856 void KMSaveMsgCommand::slotSaveDataReq()
857 {
858  int remainingBytes = mData.size() - mOffset;
859  if ( remainingBytes > 0 ) {
860  // eat leftovers first
861  if ( remainingBytes > MAX_CHUNK_SIZE )
862  remainingBytes = MAX_CHUNK_SIZE;
863 
864  TQByteArray data;
865  data.duplicate( mData.data() + mOffset, remainingBytes );
866  mJob->sendAsyncData( data );
867  mOffset += remainingBytes;
868  return;
869  }
870  // No leftovers, process next message.
871  if ( mMsgListIndex < mMsgList.size() ) {
872  KMMessage *msg = 0;
873  int idx = -1;
874  KMFolder * p = 0;
875  KMMsgDict::instance()->getLocation( mMsgList[mMsgListIndex], &p, &idx );
876  assert( p );
877  assert( idx >= 0 );
878  //kdDebug() << "SERNUM: " << mMsgList[mMsgListIndex] << " idx: " << idx << " folder: " << p->prettyURL() << endl;
879 
880  const bool alreadyGot = p->isMessage( idx );
881 
882  msg = p->getMsg(idx);
883 
884  if ( msg ) {
885  // Only unGet the message if it isn't already got.
886  if ( !alreadyGot ) {
887  mUngetMsgs.append( msg );
888  }
889  if ( msg->transferInProgress() ) {
890  TQByteArray data = TQByteArray();
891  mJob->sendAsyncData( data );
892  }
893  msg->setTransferInProgress( true );
894  if ( msg->isComplete() ) {
895  slotMessageRetrievedForSaving( msg );
896  } else {
897  // retrieve Message first
898  if ( msg->parent() && !msg->isComplete() ) {
899  FolderJob *job = msg->parent()->createJob( msg );
900  job->setCancellable( false );
901  connect(job, TQT_SIGNAL( messageRetrieved( KMMessage* ) ),
902  this, TQT_SLOT( slotMessageRetrievedForSaving( KMMessage* ) ) );
903  job->start();
904  }
905  }
906  } else {
907  mJob->slotError( KIO::ERR_ABORTED,
908  i18n("The message was removed while saving it. "
909  "It has not been saved.") );
910  }
911  } else {
912  if ( mStandAloneMessage ) {
913  // do the special case of a standalone message
914  slotMessageRetrievedForSaving( mStandAloneMessage );
915  mStandAloneMessage = 0;
916  } else {
917  // No more messages. Tell the putjob we are done.
918  TQByteArray data = TQByteArray();
919  mJob->sendAsyncData( data );
920  }
921  }
922 }
923 
924 void KMSaveMsgCommand::slotMessageRetrievedForSaving(KMMessage *msg)
925 {
926  if ( msg ) {
927  mData = KMFolderMbox::escapeFrom( msg->asDwString() );
928  KMail::Util::insert( mData, 0, msg->mboxMessageSeparator() );
929  KMail::Util::append( mData, "\n" );
930  msg->setTransferInProgress(false);
931 
932  mOffset = 0;
933  TQByteArray data;
934  int size;
935  // Unless it is great than 64 k send the whole message. kio buffers for us.
936  if( mData.size() > (unsigned int) MAX_CHUNK_SIZE )
937  size = MAX_CHUNK_SIZE;
938  else
939  size = mData.size();
940 
941  data.duplicate( mData, size );
942  mJob->sendAsyncData( data );
943  mOffset += size;
944  }
945  ++mMsgListIndex;
946  // Get rid of the message.
947  if ( msg && msg->parent() && msg->getMsgSerNum() &&
948  mUngetMsgs.contains( msg ) ) {
949  int idx = -1;
950  KMFolder * p = 0;
951  KMMsgDict::instance()->getLocation( msg, &p, &idx );
952  assert( p == msg->parent() ); assert( idx >= 0 );
953  p->unGetMsg( idx );
954  p->close("kmcommand");
955  }
956 }
957 
958 void KMSaveMsgCommand::slotSaveResult(KIO::Job *job)
959 {
960  if (job->error())
961  {
962  if (job->error() == KIO::ERR_FILE_ALREADY_EXIST)
963  {
964  if (KMessageBox::warningContinueCancel(0,
965  i18n("File %1 exists.\nDo you want to replace it?")
966  .arg(mUrl.prettyURL()), i18n("Save to File"), i18n("&Replace"))
967  == KMessageBox::Continue) {
968  mOffset = 0;
969 
970  mJob = KIO::put( mUrl, S_IRUSR|S_IWUSR, true, false );
971  mJob->slotTotalSize( mTotalSize );
972  mJob->setAsyncDataEnabled( true );
973  mJob->setReportDataSent( true );
974  connect(mJob, TQT_SIGNAL(dataReq(KIO::Job*, TQByteArray &)),
975  TQT_SLOT(slotSaveDataReq()));
976  connect(mJob, TQT_SIGNAL(result(KIO::Job*)),
977  TQT_SLOT(slotSaveResult(KIO::Job*)));
978  }
979  }
980  else
981  {
982  job->showErrorDialog();
983  setResult( Failed );
984  emit completed( this );
985  deleteLater();
986  }
987  } else {
988  setResult( OK );
989  emit completed( this );
990  deleteLater();
991  }
992 }
993 
994 //-----------------------------------------------------------------------------
995 
996 KMOpenMsgCommand::KMOpenMsgCommand( TQWidget *parent, const KURL & url,
997  const TQString & encoding )
998  : KMCommand( parent ),
999  mUrl( url ),
1000  mEncoding( encoding )
1001 {
1002  setDeletesItself( true );
1003 }
1004 
1005 KMCommand::Result KMOpenMsgCommand::execute()
1006 {
1007  if ( mUrl.isEmpty() ) {
1008  mUrl = KFileDialog::getOpenURL( ":OpenMessage", "message/rfc822 application/mbox",
1009  parentWidget(), i18n("Open Message") );
1010  }
1011  if ( mUrl.isEmpty() ) {
1012  setDeletesItself( false );
1013  return Canceled;
1014  }
1015  mJob = KIO::get( mUrl, false, false );
1016  mJob->setReportDataSent( true );
1017  connect( mJob, TQT_SIGNAL( data( KIO::Job *, const TQByteArray & ) ),
1018  this, TQT_SLOT( slotDataArrived( KIO::Job*, const TQByteArray & ) ) );
1019  connect( mJob, TQT_SIGNAL( result( KIO::Job * ) ),
1020  TQT_SLOT( slotResult( KIO::Job * ) ) );
1021  setEmitsCompletedItself( true );
1022  return OK;
1023 }
1024 
1025 void KMOpenMsgCommand::slotDataArrived( KIO::Job *, const TQByteArray & data )
1026 {
1027  if ( data.isEmpty() )
1028  return;
1029 
1030  mMsgString.append( data.data(), data.size() );
1031 }
1032 
1033 void KMOpenMsgCommand::slotResult( KIO::Job *job )
1034 {
1035  if ( job->error() ) {
1036  // handle errors
1037  job->showErrorDialog();
1038  setResult( Failed );
1039  emit completed( this );
1040  }
1041  else {
1042  int startOfMessage = 0;
1043  if ( mMsgString.compare( 0, 5, "From ", 5 ) == 0 ) {
1044  startOfMessage = mMsgString.find( '\n' );
1045  if ( startOfMessage == -1 ) {
1046  KMessageBox::sorry( parentWidget(),
1047  i18n( "The file does not contain a message." ) );
1048  setResult( Failed );
1049  emit completed( this );
1050  // Emulate closing of a secondary window so that KMail exits in case it
1051  // was started with the --view command line option. Otherwise an
1052  // invisible KMail would keep running.
1053  SecondaryWindow *win = new SecondaryWindow();
1054  win->close();
1055  win->deleteLater();
1056  deleteLater();
1057  return;
1058  }
1059  startOfMessage += 1; // the message starts after the '\n'
1060  }
1061  // check for multiple messages in the file
1062  bool multipleMessages = true;
1063  int endOfMessage = mMsgString.find( "\nFrom " );
1064  if ( endOfMessage == -1 ) {
1065  endOfMessage = mMsgString.length();
1066  multipleMessages = false;
1067  }
1068  DwMessage *dwMsg = new DwMessage;
1069  dwMsg->FromString( mMsgString.substr( startOfMessage,
1070  endOfMessage - startOfMessage ) );
1071  dwMsg->Parse();
1072  // check whether we have a message ( no headers => this isn't a message )
1073  if ( dwMsg->Headers().NumFields() == 0 ) {
1074  KMessageBox::sorry( parentWidget(),
1075  i18n( "The file does not contain a message." ) );
1076  delete dwMsg; dwMsg = 0;
1077  setResult( Failed );
1078  emit completed( this );
1079  // Emulate closing of a secondary window (see above).
1080  SecondaryWindow *win = new SecondaryWindow();
1081  win->close();
1082  win->deleteLater();
1083  deleteLater();
1084  return;
1085  }
1086  KMMessage *msg = new KMMessage( dwMsg );
1087  msg->setReadyToShow( true );
1088  KMReaderMainWin *win = new KMReaderMainWin();
1089  win->showMsg( mEncoding, msg );
1090  win->show();
1091  if ( multipleMessages )
1092  KMessageBox::information( win,
1093  i18n( "The file contains multiple messages. "
1094  "Only the first message is shown." ) );
1095  setResult( OK );
1096  emit completed( this );
1097  }
1098  deleteLater();
1099 }
1100 
1101 //-----------------------------------------------------------------------------
1102 
1103 //TODO: ReplyTo, NoQuoteReplyTo, ReplyList, ReplyToAll, ReplyAuthor
1104 // are all similar and should be factored
1105 KMReplyToCommand::KMReplyToCommand( TQWidget *parent, KMMessage *msg,
1106  const TQString &selection )
1107  : KMCommand( parent, msg ), mSelection( selection )
1108 {
1109 }
1110 
1111 KMCommand::Result KMReplyToCommand::execute()
1112 {
1113  KCursorSaver busy(KBusyPtr::busy());
1114  KMMessage *msg = retrievedMessage();
1115  if ( !msg || !msg->codec() ) {
1116  return Failed;
1117  }
1118  KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection );
1119  KMail::Composer * win = KMail::makeComposer( reply );
1120  win->setCharset( msg->codec()->mimeName(), true );
1121  win->setReplyFocus();
1122  win->show();
1123 
1124  return OK;
1125 }
1126 
1127 
1128 KMNoQuoteReplyToCommand::KMNoQuoteReplyToCommand( TQWidget *parent,
1129  KMMessage *msg )
1130  : KMCommand( parent, msg )
1131 {
1132 }
1133 
1134 KMCommand::Result KMNoQuoteReplyToCommand::execute()
1135 {
1136  KCursorSaver busy(KBusyPtr::busy());
1137  KMMessage *msg = retrievedMessage();
1138  if ( !msg || !msg->codec() ) {
1139  return Failed;
1140  }
1141  KMMessage *reply = msg->createReply( KMail::ReplySmart, "", true);
1142  KMail::Composer * win = KMail::makeComposer( reply );
1143  win->setCharset(msg->codec()->mimeName(), true);
1144  win->setReplyFocus(false);
1145  win->show();
1146 
1147  return OK;
1148 }
1149 
1150 
1151 KMReplyListCommand::KMReplyListCommand( TQWidget *parent,
1152  KMMessage *msg, const TQString &selection )
1153  : KMCommand( parent, msg ), mSelection( selection )
1154 {
1155 }
1156 
1157 KMCommand::Result KMReplyListCommand::execute()
1158 {
1159  KCursorSaver busy(KBusyPtr::busy());
1160  KMMessage *msg = retrievedMessage();
1161  if ( !msg || !msg->codec() ) {
1162  return Failed;
1163  }
1164  KMMessage *reply = msg->createReply( KMail::ReplyList, mSelection);
1165  KMail::Composer * win = KMail::makeComposer( reply );
1166  win->setCharset(msg->codec()->mimeName(), true);
1167  win->setReplyFocus(false);
1168  win->show();
1169 
1170  return OK;
1171 }
1172 
1173 
1174 KMReplyToAllCommand::KMReplyToAllCommand( TQWidget *parent,
1175  KMMessage *msg, const TQString &selection )
1176  :KMCommand( parent, msg ), mSelection( selection )
1177 {
1178 }
1179 
1180 KMCommand::Result KMReplyToAllCommand::execute()
1181 {
1182  KCursorSaver busy(KBusyPtr::busy());
1183  KMMessage *msg = retrievedMessage();
1184  if ( !msg || !msg->codec() ) {
1185  return Failed;
1186  }
1187  KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection );
1188  KMail::Composer * win = KMail::makeComposer( reply );
1189  win->setCharset( msg->codec()->mimeName(), true );
1190  win->setReplyFocus();
1191  win->show();
1192 
1193  return OK;
1194 }
1195 
1196 
1197 KMReplyAuthorCommand::KMReplyAuthorCommand( TQWidget *parent, KMMessage *msg,
1198  const TQString &selection )
1199  : KMCommand( parent, msg ), mSelection( selection )
1200 {
1201 }
1202 
1203 KMCommand::Result KMReplyAuthorCommand::execute()
1204 {
1205  KCursorSaver busy(KBusyPtr::busy());
1206  KMMessage *msg = retrievedMessage();
1207  if ( !msg || !msg->codec() ) {
1208  return Failed;
1209  }
1210  KMMessage *reply = msg->createReply( KMail::ReplyAuthor, mSelection );
1211  KMail::Composer * win = KMail::makeComposer( reply );
1212  win->setCharset( msg->codec()->mimeName(), true );
1213  win->setReplyFocus();
1214  win->show();
1215 
1216  return OK;
1217 }
1218 
1219 
1220 KMForwardInlineCommand::KMForwardInlineCommand( TQWidget *parent,
1221  const TQPtrList<KMMsgBase> &msgList, uint identity )
1222  : KMCommand( parent, msgList ),
1223  mIdentity( identity )
1224 {
1225 }
1226 
1227 KMForwardInlineCommand::KMForwardInlineCommand( TQWidget *parent,
1228  KMMessage *msg, uint identity )
1229  : KMCommand( parent, msg ),
1230  mIdentity( identity )
1231 {
1232 }
1233 
1234 KMCommand::Result KMForwardInlineCommand::execute()
1235 {
1236  TQPtrList<KMMessage> msgList = retrievedMsgs();
1237 
1238  if (msgList.count() >= 2) { // Multiple forward
1239 
1240  uint id = 0;
1241  TQPtrList<KMMessage> linklist;
1242  for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
1243  // set the identity
1244  if (id == 0)
1245  id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
1246 
1247  // msgText += msg->createForwardBody();
1248  linklist.append( msg );
1249  }
1250  if ( id == 0 )
1251  id = mIdentity; // use folder identity if no message had an id set
1252  KMMessage *fwdMsg = new KMMessage;
1253  fwdMsg->initHeader( id );
1254  fwdMsg->setAutomaticFields( true );
1255  fwdMsg->setCharset( "utf-8" );
1256  // fwdMsg->setBody( msgText );
1257 
1258  for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) {
1259  TemplateParser parser( fwdMsg, TemplateParser::Forward );
1260  parser.setSelection( msg->body() ); // FIXME: Why is this needed?
1261  parser.process( msg, 0, true );
1262 
1263  fwdMsg->link( msg, KMMsgStatusForwarded );
1264  }
1265 
1266  KCursorSaver busy( KBusyPtr::busy() );
1267  KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
1268  win->setCharset("");
1269  win->show();
1270 
1271  } else { // forward a single message at most
1272 
1273  KMMessage *msg = msgList.getFirst();
1274  if ( !msg || !msg->codec() )
1275  return Failed;
1276 
1277  KCursorSaver busy( KBusyPtr::busy() );
1278  KMMessage *fwdMsg = msg->createForward();
1279 
1280  uint id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
1281  if ( id == 0 )
1282  id = mIdentity;
1283  {
1284  KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
1285  win->setCharset( fwdMsg->codec()->mimeName(), true );
1286  win->show();
1287  }
1288  }
1289  return OK;
1290 }
1291 
1292 
1293 KMForwardAttachedCommand::KMForwardAttachedCommand( TQWidget *parent,
1294  const TQPtrList<KMMsgBase> &msgList, uint identity, KMail::Composer *win )
1295  : KMCommand( parent, msgList ), mIdentity( identity ),
1296  mWin( TQGuardedPtr<KMail::Composer>( win ))
1297 {
1298 }
1299 
1300 KMForwardAttachedCommand::KMForwardAttachedCommand( TQWidget *parent,
1301  KMMessage * msg, uint identity, KMail::Composer *win )
1302  : KMCommand( parent, msg ), mIdentity( identity ),
1303  mWin( TQGuardedPtr< KMail::Composer >( win ))
1304 {
1305 }
1306 
1307 KMCommand::Result KMForwardAttachedCommand::execute()
1308 {
1309  TQPtrList<KMMessage> msgList = retrievedMsgs();
1310  KMMessage *fwdMsg = new KMMessage;
1311 
1312  if (msgList.count() >= 2) {
1313  // don't respect X-KMail-Identity headers because they might differ for
1314  // the selected mails
1315  fwdMsg->initHeader(mIdentity);
1316  }
1317  else if (msgList.count() == 1) {
1318  KMMessage *msg = msgList.getFirst();
1319  fwdMsg->initFromMessage(msg);
1320  fwdMsg->setSubject( msg->forwardSubject() );
1321  }
1322 
1323  fwdMsg->setAutomaticFields(true);
1324 
1325  KCursorSaver busy(KBusyPtr::busy());
1326  if (!mWin)
1327  mWin = KMail::makeComposer(fwdMsg, mIdentity);
1328 
1329  // iterate through all the messages to be forwarded
1330  for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) {
1331  // remove headers that shouldn't be forwarded
1333  msg->removeHeaderField("BCC");
1334  // set the part
1335  KMMessagePart *msgPart = new KMMessagePart;
1336  msgPart->setTypeStr("message");
1337  msgPart->setSubtypeStr("rfc822");
1338  msgPart->setCharset(msg->charset());
1339  msgPart->setName("forwarded message");
1340  msgPart->setContentDescription(msg->from()+": "+msg->subject());
1341  msgPart->setContentDisposition( "inline" );
1342  // THIS HAS TO BE AFTER setCte()!!!!
1343  msgPart->setMessageBody( KMail::Util::ByteArray( msg->asDwString() ) );
1344  msgPart->setCharset("");
1345 
1346  fwdMsg->link(msg, KMMsgStatusForwarded);
1347  mWin->addAttach(msgPart);
1348  }
1349 
1350  mWin->show();
1351 
1352  return OK;
1353 }
1354 
1355 
1356 KMForwardDigestCommand::KMForwardDigestCommand( TQWidget *parent,
1357  const TQPtrList<KMMsgBase> &msgList, uint identity, KMail::Composer *win )
1358  : KMCommand( parent, msgList ), mIdentity( identity ),
1359  mWin( TQGuardedPtr<KMail::Composer>( win ))
1360 {
1361 }
1362 
1363 KMForwardDigestCommand::KMForwardDigestCommand( TQWidget *parent,
1364  KMMessage * msg, uint identity, KMail::Composer *win )
1365  : KMCommand( parent, msg ), mIdentity( identity ),
1366  mWin( TQGuardedPtr< KMail::Composer >( win ))
1367 {
1368 }
1369 
1370 KMCommand::Result KMForwardDigestCommand::execute()
1371 {
1372  TQPtrList<KMMessage> msgList = retrievedMsgs();
1373 
1374  if ( msgList.count() < 2 )
1375  return Undefined; // must have more than 1 for a digest
1376 
1377  uint id = 0;
1378  KMMessage *fwdMsg = new KMMessage;
1379  KMMessagePart *msgPart = new KMMessagePart;
1380  TQString msgPartText;
1381  int msgCnt = 0; // incase there are some we can't forward for some reason
1382 
1383  // dummy header initialization; initialization with the correct identity
1384  // is done below
1385  fwdMsg->initHeader( id );
1386  fwdMsg->setAutomaticFields( true );
1387  fwdMsg->mMsg->Headers().ContentType().CreateBoundary( 1 );
1388  TQCString boundary( fwdMsg->mMsg->Headers().ContentType().Boundary().c_str() );
1389  msgPartText = i18n("\nThis is a MIME digest forward. The content of the"
1390  " message is contained in the attachment(s).\n\n\n");
1391  // iterate through all the messages to be forwarded
1392  for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
1393  // set the identity
1394  if ( id == 0 )
1395  id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
1396  // set the part header
1397  msgPartText += "--";
1398  msgPartText += TQString::fromLatin1( boundary );
1399  msgPartText += "\nContent-Type: MESSAGE/RFC822";
1400  msgPartText += TQString( "; CHARSET=%1" ).arg( TQString(msg->charset()) );
1401  msgPartText += '\n';
1402  DwHeaders dwh;
1403  dwh.MessageId().CreateDefault();
1404  msgPartText += TQString( "Content-ID: %1\n" ).arg( dwh.MessageId().AsString().c_str() );
1405  msgPartText += TQString( "Content-Description: %1" ).arg( msg->subject() );
1406  if ( !msg->subject().contains( "(fwd)" ) )
1407  msgPartText += " (fwd)";
1408  msgPartText += "\n\n";
1409  // remove headers that shouldn't be forwarded
1411  msg->removeHeaderField( "BCC" );
1412  // set the part
1413  msgPartText += msg->headerAsString();
1414  msgPartText += '\n';
1415  msgPartText += msg->body();
1416  msgPartText += '\n'; // eot
1417  msgCnt++;
1418  fwdMsg->link( msg, KMMsgStatusForwarded );
1419  }
1420 
1421  if ( id == 0 )
1422  id = mIdentity; // use folder identity if no message had an id set
1423  fwdMsg->initHeader( id );
1424  msgPartText += "--";
1425  msgPartText += TQString::fromLatin1( boundary );
1426  msgPartText += "--\n";
1427  TQCString tmp;
1428  msgPart->setTypeStr( "MULTIPART" );
1429  tmp.sprintf( "Digest; boundary=\"%s\"", boundary.data() );
1430  msgPart->setSubtypeStr( tmp );
1431  msgPart->setName( "unnamed" );
1432  msgPart->setCte( DwMime::kCte7bit ); // does it have to be 7bit?
1433  msgPart->setContentDescription( TQString( "Digest of %1 messages." ).arg( msgCnt ) );
1434  // THIS HAS TO BE AFTER setCte()!!!!
1435  msgPart->setBodyEncoded( TQCString( msgPartText.ascii() ) );
1436  KCursorSaver busy( KBusyPtr::busy() );
1437  KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
1438  win->addAttach( msgPart );
1439  win->show();
1440  return OK;
1441 }
1442 
1443 KMRedirectCommand::KMRedirectCommand( TQWidget *parent,
1444  KMMessage *msg )
1445  : KMCommand( parent, msg )
1446 {
1447 }
1448 
1449 KMCommand::Result KMRedirectCommand::execute()
1450 {
1451  KMMessage *msg = retrievedMessage();
1452  if ( !msg || !msg->codec() )
1453  return Failed;
1454 
1455  RedirectDialog dlg( parentWidget(), "redirect", true,
1456  kmkernel->msgSender()->sendImmediate() );
1457  if (dlg.exec()==TQDialog::Rejected) return Failed;
1458 
1459  KMMessage *newMsg = msg->createRedirect( dlg.to() );
1460  KMFilterAction::sendMDN( msg, KMime::MDN::Dispatched );
1461 
1462  const KMail::MessageSender::SendMethod method = dlg.sendImmediate()
1463  ? KMail::MessageSender::SendImmediate
1464  : KMail::MessageSender::SendLater;
1465  if ( !kmkernel->msgSender()->send( newMsg, method ) ) {
1466  kdDebug(5006) << "KMRedirectCommand: could not redirect message (sending failed)" << endl;
1467  return Failed; // error: couldn't send
1468  }
1469  return OK;
1470 }
1471 
1472 
1473 KMCustomReplyToCommand::KMCustomReplyToCommand( TQWidget *parent, KMMessage *msg,
1474  const TQString &selection,
1475  const TQString &tmpl )
1476  : KMCommand( parent, msg ), mSelection( selection ), mTemplate( tmpl )
1477 {
1478 }
1479 
1480 KMCommand::Result KMCustomReplyToCommand::execute()
1481 {
1482  KCursorSaver busy(KBusyPtr::busy());
1483  KMMessage *msg = retrievedMessage();
1484  if ( !msg || !msg->codec() ) {
1485  return Failed;
1486  }
1487  KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection,
1488  false, true, mTemplate );
1489  KMail::Composer * win = KMail::makeComposer( reply );
1490  win->setCharset( msg->codec()->mimeName(), true );
1491  win->setReplyFocus();
1492  win->show();
1493 
1494  return OK;
1495 }
1496 
1497 
1498 KMCustomReplyAllToCommand::KMCustomReplyAllToCommand( TQWidget *parent, KMMessage *msg,
1499  const TQString &selection,
1500  const TQString &tmpl )
1501  : KMCommand( parent, msg ), mSelection( selection ), mTemplate( tmpl )
1502 {
1503 }
1504 
1505 KMCommand::Result KMCustomReplyAllToCommand::execute()
1506 {
1507  KCursorSaver busy(KBusyPtr::busy());
1508  KMMessage *msg = retrievedMessage();
1509  if ( !msg || !msg->codec() ) {
1510  return Failed;
1511  }
1512  KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection,
1513  false, true, mTemplate );
1514  KMail::Composer * win = KMail::makeComposer( reply );
1515  win->setCharset( msg->codec()->mimeName(), true );
1516  win->setReplyFocus();
1517  win->show();
1518 
1519  return OK;
1520 }
1521 
1522 
1523 KMCustomForwardCommand::KMCustomForwardCommand( TQWidget *parent,
1524  const TQPtrList<KMMsgBase> &msgList, uint identity, const TQString &tmpl )
1525  : KMCommand( parent, msgList ),
1526  mIdentity( identity ), mTemplate( tmpl )
1527 {
1528 }
1529 
1530 KMCustomForwardCommand::KMCustomForwardCommand( TQWidget *parent,
1531  KMMessage *msg, uint identity, const TQString &tmpl )
1532  : KMCommand( parent, msg ),
1533  mIdentity( identity ), mTemplate( tmpl )
1534 {
1535 }
1536 
1537 KMCommand::Result KMCustomForwardCommand::execute()
1538 {
1539  TQPtrList<KMMessage> msgList = retrievedMsgs();
1540 
1541  if (msgList.count() >= 2) { // Multiple forward
1542 
1543  uint id = 0;
1544  TQPtrList<KMMessage> linklist;
1545  for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
1546  // set the identity
1547  if (id == 0)
1548  id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
1549 
1550  // msgText += msg->createForwardBody();
1551  linklist.append( msg );
1552  }
1553  if ( id == 0 )
1554  id = mIdentity; // use folder identity if no message had an id set
1555  KMMessage *fwdMsg = new KMMessage;
1556  fwdMsg->initHeader( id );
1557  fwdMsg->setAutomaticFields( true );
1558  fwdMsg->setCharset( "utf-8" );
1559  // fwdMsg->setBody( msgText );
1560 
1561  for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) {
1562  TemplateParser parser( fwdMsg, TemplateParser::Forward );
1563  parser.setSelection( msg->body() ); // FIXME: Why is this needed?
1564  parser.process( msg, 0, true );
1565 
1566  fwdMsg->link( msg, KMMsgStatusForwarded );
1567  }
1568 
1569  KCursorSaver busy( KBusyPtr::busy() );
1570  KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
1571  win->setCharset("");
1572  win->show();
1573 
1574  } else { // forward a single message at most
1575 
1576  KMMessage *msg = msgList.getFirst();
1577  if ( !msg || !msg->codec() )
1578  return Failed;
1579 
1580  KCursorSaver busy( KBusyPtr::busy() );
1581  KMMessage *fwdMsg = msg->createForward( mTemplate );
1582 
1583  uint id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
1584  if ( id == 0 )
1585  id = mIdentity;
1586  {
1587  KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
1588  win->setCharset( fwdMsg->codec()->mimeName(), true );
1589  win->show();
1590  }
1591  }
1592  return OK;
1593 }
1594 
1595 
1596 KMPrintCommand::KMPrintCommand( TQWidget *parent, KMMessage *msg,
1597  const KMail::HeaderStyle *headerStyle,
1598  const KMail::HeaderStrategy *headerStrategy,
1599  bool htmlOverride, bool htmlLoadExtOverride,
1600  bool useFixedFont, const TQString & encoding )
1601  : KMCommand( parent, msg ),
1602  mHeaderStyle( headerStyle ), mHeaderStrategy( headerStrategy ),
1603  mHtmlOverride( htmlOverride ),
1604  mHtmlLoadExtOverride( htmlLoadExtOverride ),
1605  mUseFixedFont( useFixedFont ), mEncoding( encoding )
1606 {
1607  if ( GlobalSettings::useDefaultFonts() )
1608  mOverrideFont = KGlobalSettings::generalFont();
1609  else {
1610  KConfigGroup fonts( KMKernel::config(), "Fonts" );
1611  TQString tmp = fonts.readEntry( "print-font", KGlobalSettings::generalFont().toString() );
1612  mOverrideFont.fromString( tmp );
1613  }
1614 }
1615 
1616 
1617 void KMPrintCommand::setOverrideFont( const TQFont& font )
1618 {
1619  mOverrideFont = font;
1620 }
1621 
1622 KMCommand::Result KMPrintCommand::execute()
1623 {
1624  KMReaderWin printWin( 0, 0, 0 );
1625  printWin.setPrinting( true );
1626  printWin.readConfig();
1627  if ( mHeaderStyle != 0 && mHeaderStrategy != 0 )
1628  printWin.setHeaderStyleAndStrategy( mHeaderStyle, mHeaderStrategy );
1629  printWin.setHtmlOverride( mHtmlOverride );
1630  printWin.setHtmlLoadExtOverride( mHtmlLoadExtOverride );
1631  printWin.setUseFixedFont( mUseFixedFont );
1632  printWin.setOverrideEncoding( mEncoding );
1633  printWin.cssHelper()->setPrintFont( mOverrideFont );
1634  printWin.setDecryptMessageOverwrite( true );
1635  printWin.setMsg( retrievedMessage(), true );
1636  printWin.printMsg();
1637 
1638  return OK;
1639 }
1640 
1641 
1642 KMSeStatusCommand::KMSeStatusCommand( KMMsgStatus status,
1643  const TQValueList<TQ_UINT32> &serNums, bool toggle )
1644  : mStatus( status ), mSerNums( serNums ), mToggle( toggle )
1645 {
1646 }
1647 
1648 KMCommand::Result KMSeStatusCommand::execute()
1649 {
1650  TQValueListIterator<TQ_UINT32> it;
1651  int idx = -1;
1652  KMFolder *folder = 0;
1653  bool parenStatus = false;
1654 
1655  // Toggle actions on threads toggle the whole thread
1656  // depending on the state of the parent.
1657  if (mToggle) {
1658  KMMsgBase *msg;
1659  KMMsgDict::instance()->getLocation( *mSerNums.begin(), &folder, &idx );
1660  if (folder) {
1661  msg = folder->getMsgBase(idx);
1662  if (msg && (msg->status()&mStatus))
1663  parenStatus = true;
1664  else
1665  parenStatus = false;
1666  }
1667  }
1668  TQMap< KMFolder*, TQValueList<int> > folderMap;
1669  for ( it = mSerNums.begin(); it != mSerNums.end(); ++it ) {
1670  KMMsgDict::instance()->getLocation( *it, &folder, &idx );
1671  if (folder) {
1672  if (mToggle) {
1673  KMMsgBase *msg = folder->getMsgBase(idx);
1674  // check if we are already at the target toggle state
1675  if (msg) {
1676  bool myStatus;
1677  if (msg->status()&mStatus)
1678  myStatus = true;
1679  else
1680  myStatus = false;
1681  if (myStatus != parenStatus)
1682  continue;
1683  }
1684  }
1685  /* Collect the ids for each folder in a separate list and
1686  send them off in one go at the end. */
1687  folderMap[folder].append(idx);
1688  }
1689  }
1690  TQMapIterator< KMFolder*, TQValueList<int> > it2 = folderMap.begin();
1691  while ( it2 != folderMap.end() ) {
1692  KMFolder *f = it2.key();
1693  f->setStatus( (*it2), mStatus, mToggle );
1694  ++it2;
1695  }
1696  //kapp->dcopClient()->emitDCOPSignal( "unreadCountChanged()", TQByteArray() );
1697 
1698  return OK;
1699 }
1700 
1701 
1702 KMFilterCommand::KMFilterCommand( const TQCString &field, const TQString &value )
1703  : mField( field ), mValue( value )
1704 {
1705 }
1706 
1707 KMCommand::Result KMFilterCommand::execute()
1708 {
1709  kmkernel->filterMgr()->createFilter( mField, mValue );
1710 
1711  return OK;
1712 }
1713 
1714 
1715 KMFilterActionCommand::KMFilterActionCommand( TQWidget *parent,
1716  const TQPtrList<KMMsgBase> &msgList,
1717  KMFilter *filter )
1718  : KMCommand( parent, msgList ), mFilter( filter )
1719 {
1720  TQPtrListIterator<KMMsgBase> it(msgList);
1721  while ( it.current() ) {
1722  serNumList.append( (*it)->getMsgSerNum() );
1723  ++it;
1724  }
1725 }
1726 
1727 KMCommand::Result KMFilterActionCommand::execute()
1728 {
1729  KCursorSaver busy( KBusyPtr::busy() );
1730 
1731  int msgCount = 0;
1732  int msgCountToFilter = serNumList.count();
1733  ProgressItem* progressItem =
1734  ProgressManager::createProgressItem ( "filter"+ProgressManager::getUniqueID(),
1735  i18n( "Filtering messages" ) );
1736  progressItem->setTotalItems( msgCountToFilter );
1737  TQValueList<TQ_UINT32>::const_iterator it;
1738  for ( it = serNumList.begin(); it != serNumList.end(); it++ ) {
1739  TQ_UINT32 serNum = *it;
1740  int diff = msgCountToFilter - ++msgCount;
1741  if ( diff < 10 || !( msgCount % 20 ) || msgCount <= 10 ) {
1742  progressItem->updateProgress();
1743  TQString statusMsg = i18n("Filtering message %1 of %2");
1744  statusMsg = statusMsg.arg( msgCount ).arg( msgCountToFilter );
1745  KPIM::BroadcastStatus::instance()->setStatusMsg( statusMsg );
1746  KApplication::kApplication()->eventLoop()->processEvents( TQEventLoop::ExcludeUserInput, 50 );
1747  }
1748 
1749  int filterResult = kmkernel->filterMgr()->process( serNum, mFilter );
1750  if (filterResult == 2) {
1751  // something went horribly wrong (out of space?)
1752  perror("Critical error");
1753  kmkernel->emergencyExit( i18n("Not enough free disk space?" ));
1754  }
1755  progressItem->incCompletedItems();
1756  }
1757 
1758  progressItem->setComplete();
1759  progressItem = 0;
1760  return OK;
1761 }
1762 
1763 
1764 KMMetaFilterActionCommand::KMMetaFilterActionCommand( KMFilter *filter,
1765  KMHeaders *headers,
1766  KMMainWidget *main )
1767  : TQObject( main ),
1768  mFilter( filter ), mHeaders( headers ), mMainWidget( main )
1769 {
1770 }
1771 
1772 void KMMetaFilterActionCommand::start()
1773 {
1774  if (ActionScheduler::isEnabled() ) {
1775  // use action scheduler
1776  KMFilterMgr::FilterSet set = KMFilterMgr::All;
1777  TQValueList<KMFilter*> filters;
1778  filters.append( mFilter );
1779  ActionScheduler *scheduler = new ActionScheduler( set, filters, mHeaders );
1780  scheduler->setAlwaysMatch( true );
1781  scheduler->setAutoDestruct( true );
1782 
1783  int contentX, contentY;
1784  HeaderItem *nextItem = mHeaders->prepareMove( &contentX, &contentY );
1785  TQPtrList<KMMsgBase> msgList = *mHeaders->selectedMsgs(true);
1786  mHeaders->finalizeMove( nextItem, contentX, contentY );
1787 
1788  for (KMMsgBase *msg = msgList.first(); msg; msg = msgList.next())
1789  scheduler->execFilters( msg );
1790  } else {
1791  KMCommand *filterCommand =
1792  new KMFilterActionCommand( mMainWidget,
1793  *mHeaders->selectedMsgs(), mFilter );
1794  filterCommand->start();
1795  int contentX, contentY;
1796  HeaderItem *item = mHeaders->prepareMove( &contentX, &contentY );
1797  mHeaders->finalizeMove( item, contentX, contentY );
1798  }
1799 }
1800 
1801 FolderShortcutCommand::FolderShortcutCommand( KMMainWidget *mainwidget,
1802  KMFolder *folder )
1803  : mMainWidget( mainwidget ), mFolder( folder ), mAction( 0 )
1804 {
1805 }
1806 
1807 
1808 FolderShortcutCommand::~FolderShortcutCommand()
1809 {
1810  if ( mAction ) mAction->unplugAll();
1811  delete mAction;
1812 }
1813 
1814 void FolderShortcutCommand::start()
1815 {
1816  mMainWidget->slotSelectFolder( mFolder );
1817 }
1818 
1819 void FolderShortcutCommand::setAction( KAction* action )
1820 {
1821  mAction = action;
1822 }
1823 
1824 KMMailingListFilterCommand::KMMailingListFilterCommand( TQWidget *parent,
1825  KMMessage *msg )
1826  : KMCommand( parent, msg )
1827 {
1828 }
1829 
1830 KMCommand::Result KMMailingListFilterCommand::execute()
1831 {
1832  TQCString name;
1833  TQString value;
1834  KMMessage *msg = retrievedMessage();
1835  if (!msg)
1836  return Failed;
1837 
1838  if ( !MailingList::name( msg, name, value ).isEmpty() ) {
1839  kmkernel->filterMgr()->createFilter( name, value );
1840  return OK;
1841  }
1842  else
1843  return Failed;
1844 }
1845 
1846 
1847 void KMMenuCommand::folderToPopupMenu(bool move,
1848  TQObject *receiver, KMMenuToFolder *aMenuToFolder, TQPopupMenu *menu )
1849 {
1850  while ( menu->count() )
1851  {
1852  TQPopupMenu *popup = menu->findItem( menu->idAt( 0 ) )->popup();
1853  if (popup)
1854  delete popup;
1855  else
1856  menu->removeItemAt( 0 );
1857  }
1858 
1859  if (!kmkernel->imapFolderMgr()->dir().first() &&
1860  !kmkernel->dimapFolderMgr()->dir().first())
1861  { // only local folders
1862  makeFolderMenu( &kmkernel->folderMgr()->dir(), move,
1863  receiver, aMenuToFolder, menu );
1864  } else {
1865  // operate on top-level items
1866  TQPopupMenu* subMenu = new TQPopupMenu(menu);
1867  makeFolderMenu( &kmkernel->folderMgr()->dir(),
1868  move, receiver, aMenuToFolder, subMenu );
1869  menu->insertItem( i18n( "Local Folders" ), subMenu );
1870  KMFolderDir* fdir = &kmkernel->imapFolderMgr()->dir();
1871  for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) {
1872  if (node->isDir())
1873  continue;
1874  subMenu = new TQPopupMenu(menu);
1875  makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu );
1876  menu->insertItem( node->label(), subMenu );
1877  }
1878  fdir = &kmkernel->dimapFolderMgr()->dir();
1879  for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) {
1880  if (node->isDir())
1881  continue;
1882  subMenu = new TQPopupMenu(menu);
1883  makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu );
1884  menu->insertItem( node->label(), subMenu );
1885  }
1886  }
1887 }
1888 
1889 void KMMenuCommand::makeFolderMenu(KMFolderNode* node, bool move,
1890  TQObject *receiver, KMMenuToFolder *aMenuToFolder, TQPopupMenu *menu )
1891 {
1892  // connect the signals
1893  if (move)
1894  {
1895  disconnect(menu, TQT_SIGNAL(activated(int)), receiver,
1896  TQT_SLOT(moveSelectedToFolder(int)));
1897  connect(menu, TQT_SIGNAL(activated(int)), receiver,
1898  TQT_SLOT(moveSelectedToFolder(int)));
1899  } else {
1900  disconnect(menu, TQT_SIGNAL(activated(int)), receiver,
1901  TQT_SLOT(copySelectedToFolder(int)));
1902  connect(menu, TQT_SIGNAL(activated(int)), receiver,
1903  TQT_SLOT(copySelectedToFolder(int)));
1904  }
1905 
1906  KMFolder *folder = 0;
1907  KMFolderDir *folderDir = 0;
1908  if (node->isDir()) {
1909  folderDir = static_cast<KMFolderDir*>(node);
1910  } else {
1911  folder = static_cast<KMFolder*>(node);
1912  folderDir = folder->child();
1913  }
1914 
1915  if (folder && !folder->noContent())
1916  {
1917  int menuId;
1918  if (move)
1919  menuId = menu->insertItem(i18n("Move to This Folder"));
1920  else
1921  menuId = menu->insertItem(i18n("Copy to This Folder"));
1922  aMenuToFolder->insert( menuId, folder );
1923  menu->setItemEnabled( menuId, !folder->isReadOnly() );
1924  menu->insertSeparator();
1925  }
1926 
1927  if (!folderDir)
1928  return;
1929 
1930  for (KMFolderNode *it = folderDir->first(); it; it = folderDir->next() ) {
1931  if (it->isDir())
1932  continue;
1933  KMFolder *child = static_cast<KMFolder*>(it);
1934  TQString label = child->label();
1935  label.replace("&","&&");
1936  if (child->child() && child->child()->first()) {
1937  // descend
1938  TQPopupMenu *subMenu = new TQPopupMenu(menu, "subMenu");
1939  makeFolderMenu( child, move, receiver,
1940  aMenuToFolder, subMenu );
1941  menu->insertItem( label, subMenu );
1942  } else {
1943  // insert an item
1944  int menuId = menu->insertItem( label );
1945  aMenuToFolder->insert( menuId, child );
1946  menu->setItemEnabled( menuId, !child->isReadOnly() );
1947  }
1948  }
1949  return;
1950 }
1951 
1952 
1953 KMCopyCommand::KMCopyCommand( KMFolder* destFolder,
1954  const TQPtrList<KMMsgBase> &msgList )
1955 :mDestFolder( destFolder ), mMsgList( msgList )
1956 {
1957  setDeletesItself( true );
1958 }
1959 
1960 KMCopyCommand::KMCopyCommand( KMFolder* destFolder, KMMessage * msg )
1961  :mDestFolder( destFolder )
1962 {
1963  setDeletesItself( true );
1964  mMsgList.append( &msg->toMsgBase() );
1965 }
1966 
1967 KMCommand::Result KMCopyCommand::execute()
1968 {
1969  KMMsgBase *msgBase;
1970  KMMessage *msg, *newMsg;
1971  int idx = -1;
1972  bool isMessage;
1973  TQPtrList<KMMessage> list;
1974  TQPtrList<KMMessage> localList;
1975 
1976  if (mDestFolder && mDestFolder->open("kmcommand") != 0)
1977  {
1978  deleteLater();
1979  return Failed;
1980  }
1981 
1982  setEmitsCompletedItself( true );
1983  KCursorSaver busy(KBusyPtr::busy());
1984 
1985  for (msgBase = mMsgList.first(); msgBase; msgBase = mMsgList.next() )
1986  {
1987  KMFolder *srcFolder = msgBase->parent();
1988  if (( isMessage = msgBase->isMessage() ))
1989  {
1990  msg = static_cast<KMMessage*>(msgBase);
1991  } else {
1992  idx = srcFolder->find(msgBase);
1993  assert(idx != -1);
1994  msg = srcFolder->getMsg(idx);
1995  // corrupt IMAP cache, see FolderStorage::getMsg()
1996  if ( msg == 0 ) {
1997  KMessageBox::error( parentWidget(), i18n("Corrupt IMAP cache detected in folder %1. "
1998  "Copying of messages aborted.").arg( srcFolder->prettyURL() ) );
1999  deleteLater();
2000  return Failed;
2001  }
2002  }
2003 
2004  if (srcFolder && mDestFolder &&
2005  (srcFolder->folderType()== KMFolderTypeImap) &&
2006  (mDestFolder->folderType() == KMFolderTypeImap) &&
2007  (static_cast<KMFolderImap*>(srcFolder->storage())->account() ==
2008  static_cast<KMFolderImap*>(mDestFolder->storage())->account()))
2009  {
2010  // imap => imap with same account
2011  list.append(msg);
2012  } else {
2013  newMsg = new KMMessage( new DwMessage( *msg->asDwMessage() ) );
2014  newMsg->setComplete(msg->isComplete());
2015  // make sure the attachment state is only calculated when it's complete
2016  if (!newMsg->isComplete())
2017  newMsg->setReadyToShow(false);
2018  newMsg->setStatus(msg->status());
2019 
2020  if (srcFolder && !newMsg->isComplete())
2021  {
2022  // imap => others
2023  newMsg->setParent(msg->parent());
2024  FolderJob *job = srcFolder->createJob(newMsg);
2025  job->setCancellable( false );
2026  mPendingJobs << job;
2027  connect(job, TQT_SIGNAL(messageRetrieved(KMMessage*)),
2028  mDestFolder, TQT_SLOT(reallyAddCopyOfMsg(KMMessage*)));
2029  connect( job, TQT_SIGNAL(result(KMail::FolderJob*)),
2030  this, TQT_SLOT(slotJobFinished(KMail::FolderJob*)) );
2031  job->start();
2032  } else {
2033  // local => others
2034  localList.append(newMsg);
2035  }
2036  }
2037 
2038  if (srcFolder && !isMessage && list.isEmpty())
2039  {
2040  assert(idx != -1);
2041  srcFolder->unGetMsg( idx );
2042  }
2043 
2044  } // end for
2045 
2046  bool deleteNow = false;
2047  if (!localList.isEmpty())
2048  {
2049  TQValueList<int> index;
2050  mDestFolder->addMsg( localList, index );
2051  for ( TQValueListIterator<int> it = index.begin(); it != index.end(); ++it ) {
2052  mDestFolder->unGetMsg( *it );
2053  }
2054  if ( mDestFolder->folderType() == KMFolderTypeImap ) {
2055  if ( mPendingJobs.isEmpty() ) {
2056  // wait for the end of the copy before closing the folder
2057  KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
2058  connect( imapDestFolder, TQT_SIGNAL( folderComplete( KMFolderImap*, bool ) ),
2059  this, TQT_SLOT( slotFolderComplete( KMFolderImap*, bool ) ) );
2060  }
2061  } else {
2062  deleteNow = list.isEmpty() && mPendingJobs.isEmpty(); // we're done if there are no other mails we need to fetch
2063  }
2064  }
2065 
2066 //TODO: Get rid of the other cases just use this one for all types of folder
2067 //TODO: requires adding copyMsg and getFolder methods to KMFolder.h
2068  if (!list.isEmpty())
2069  {
2070  // copy the message(s); note: the list is empty afterwards!
2071  KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
2072  connect( imapDestFolder, TQT_SIGNAL( folderComplete( KMFolderImap*, bool ) ),
2073  this, TQT_SLOT( slotFolderComplete( KMFolderImap*, bool ) ) );
2074  imapDestFolder->copyMsg(list);
2075  imapDestFolder->getFolder();
2076  }
2077 
2078  // only close the folder and delete the job if we're done
2079  // otherwise this is done in slotMsgAdded or slotFolderComplete
2080  if ( deleteNow )
2081  {
2082  mDestFolder->close("kmcommand");
2083  setResult( OK );
2084  emit completed( this );
2085  deleteLater();
2086  }
2087 
2088  return OK;
2089 }
2090 
2091 void KMCopyCommand::slotJobFinished(KMail::FolderJob * job)
2092 {
2093  mPendingJobs.remove( job );
2094  if ( job->error() ) {
2095  kdDebug(5006) << k_funcinfo << "folder job failed: " << job->error() << endl;
2096  // kill all pending jobs
2097  for ( TQValueList<KMail::FolderJob*>::Iterator it = mPendingJobs.begin(); it != mPendingJobs.end(); ++it ) {
2098  disconnect( (*it), TQT_SIGNAL(result(KMail::FolderJob*)),
2099  this, TQT_SLOT(slotJobFinished(KMail::FolderJob*)) );
2100  (*it)->kill();
2101  }
2102  mPendingJobs.clear();
2103  setResult( Failed );
2104  }
2105 
2106  if ( mPendingJobs.isEmpty() )
2107  {
2108  mDestFolder->close("kmcommand");
2109  emit completed( this );
2110  deleteLater();
2111  }
2112 }
2113 
2114 void KMCopyCommand::slotFolderComplete( KMFolderImap*, bool success )
2115 {
2116  kdDebug(5006) << k_funcinfo << success << endl;
2117  if ( !success )
2118  setResult( Failed );
2119  mDestFolder->close( "kmcommand" );
2120  emit completed( this );
2121  deleteLater();
2122 }
2123 
2124 
2125 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
2126  const TQPtrList<KMMsgBase> &msgList)
2127  : mDestFolder( destFolder ), mProgressItem( 0 )
2128 {
2129  TQPtrList<KMMsgBase> tmp = msgList;
2130  for ( KMMsgBase *msgBase = tmp.first(); msgBase; msgBase = tmp.next() )
2131  mSerNumList.append( msgBase->getMsgSerNum() );
2132 }
2133 
2134 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
2135  KMMessage *msg )
2136  : mDestFolder( destFolder ), mProgressItem( 0 )
2137 {
2138  mSerNumList.append( msg->getMsgSerNum() );
2139 }
2140 
2141 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
2142  KMMsgBase *msgBase )
2143  : mDestFolder( destFolder ), mProgressItem( 0 )
2144 {
2145  mSerNumList.append( msgBase->getMsgSerNum() );
2146 }
2147 
2148 KMMoveCommand::KMMoveCommand( TQ_UINT32 )
2149  : mProgressItem( 0 )
2150 {
2151 }
2152 
2153 KMCommand::Result KMMoveCommand::execute()
2154 {
2155  setEmitsCompletedItself( true );
2156  setDeletesItself( true );
2157  typedef TQMap< KMFolder*, TQPtrList<KMMessage>* > FolderToMessageListMap;
2158  FolderToMessageListMap folderDeleteList;
2159 
2160  if (mDestFolder && mDestFolder->open("kmcommand") != 0) {
2161  completeMove( Failed );
2162  return Failed;
2163  }
2164  KCursorSaver busy(KBusyPtr::busy());
2165 
2166  // TODO set SSL state according to source and destfolder connection?
2167  Q_ASSERT( !mProgressItem );
2168  mProgressItem =
2169  ProgressManager::createProgressItem (
2170  "move"+ProgressManager::getUniqueID(),
2171  mDestFolder ? i18n( "Moving messages" ) : i18n( "Deleting messages" ) );
2172  connect( mProgressItem, TQT_SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
2173  this, TQT_SLOT( slotMoveCanceled() ) );
2174 
2175  KMMessage *msg;
2176  int rc = 0;
2177  int index;
2178  TQPtrList<KMMessage> list;
2179  int undoId = -1;
2180  mCompleteWithAddedMsg = false;
2181 
2182  if (mDestFolder) {
2183  connect (mDestFolder, TQT_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)),
2184  this, TQT_SLOT(slotMsgAddedToDestFolder(KMFolder*, TQ_UINT32)));
2185  mLostBoys = mSerNumList;
2186  }
2187  mProgressItem->setTotalItems( mSerNumList.count() );
2188 
2189  for ( TQValueList<TQ_UINT32>::ConstIterator it = mSerNumList.constBegin(); it != mSerNumList.constEnd(); ++it ) {
2190  if ( *it == 0 ) {
2191  kdDebug(5006) << k_funcinfo << "serial number == 0!" << endl;
2192  continue; // invalid message
2193  }
2194  KMFolder *srcFolder = 0;
2195  int idx = -1;
2196  KMMsgDict::instance()->getLocation( *it, &srcFolder, &idx );
2197  if (srcFolder == mDestFolder)
2198  continue;
2199  assert(srcFolder);
2200  assert(idx != -1);
2201  if ( !srcFolder->isOpened() ) {
2202  srcFolder->open( "kmmovecommand" );
2203  mOpenedFolders.append( srcFolder );
2204  }
2205  msg = srcFolder->getMsg(idx);
2206  if ( !msg ) {
2207  kdDebug(5006) << k_funcinfo << "No message found for serial number " << *it << endl;
2208  continue;
2209  }
2210  bool undo = msg->enableUndo();
2211 
2212  if ( msg && msg->transferInProgress() &&
2213  srcFolder->folderType() == KMFolderTypeImap )
2214  {
2215  // cancel the download
2216  msg->setTransferInProgress( false, true );
2217  static_cast<KMFolderImap*>(srcFolder->storage())->ignoreJobsForMessage( msg );
2218  }
2219 
2220  if (mDestFolder) {
2221  if (mDestFolder->folderType() == KMFolderTypeImap) {
2222  /* If we are moving to an imap folder, connect to it's completed
2223  * signal so we notice when all the mails should have showed up in it
2224  * but haven't for some reason. */
2225  KMFolderImap *imapFolder = static_cast<KMFolderImap*> ( mDestFolder->storage() );
2226  disconnect (imapFolder, TQT_SIGNAL(folderComplete( KMFolderImap*, bool )),
2227  this, TQT_SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
2228 
2229  connect (imapFolder, TQT_SIGNAL(folderComplete( KMFolderImap*, bool )),
2230  this, TQT_SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
2231  list.append(msg);
2232  } else {
2233  // We are moving to a local folder.
2234  if ( srcFolder->folderType() == KMFolderTypeImap )
2235  {
2236  // do not complete here but wait until all messages are transferred
2237  mCompleteWithAddedMsg = true;
2238  }
2239  rc = mDestFolder->moveMsg(msg, &index);
2240  if (rc == 0 && index != -1) {
2241  KMMsgBase *mb = mDestFolder->unGetMsg( mDestFolder->count() - 1 );
2242  if (undo && mb)
2243  {
2244  if ( undoId == -1 )
2245  undoId = kmkernel->undoStack()->newUndoAction( srcFolder, mDestFolder );
2246  kmkernel->undoStack()->addMsgToAction( undoId, mb->getMsgSerNum() );
2247  }
2248  } else if (rc != 0) {
2249  // Something went wrong. Stop processing here, it is likely that the
2250  // other moves would fail as well.
2251  completeMove( Failed );
2252  return Failed;
2253  }
2254  }
2255  } else {
2256  // really delete messages that are already in the trash folder or if
2257  // we are really, really deleting, not just moving to trash
2258  if (srcFolder->folderType() == KMFolderTypeImap) {
2259  if (!folderDeleteList[srcFolder])
2260  folderDeleteList[srcFolder] = new TQPtrList<KMMessage>;
2261  folderDeleteList[srcFolder]->append( msg );
2262  } else {
2263  srcFolder->removeMsg(idx);
2264  delete msg;
2265  }
2266  }
2267  }
2268  if (!list.isEmpty() && mDestFolder) {
2269  // will be completed with folderComplete signal
2270  mDestFolder->moveMsg(list, &index);
2271  } else {
2272  FolderToMessageListMap::Iterator it;
2273  for ( it = folderDeleteList.begin(); it != folderDeleteList.end(); ++it ) {
2274  it.key()->removeMsg(*it.data());
2275  delete it.data();
2276  }
2277  if ( !mCompleteWithAddedMsg ) {
2278  // imap folders will be completed in slotMsgAddedToDestFolder
2279  completeMove( OK );
2280  }
2281  }
2282 
2283  return OK;
2284 }
2285 
2286 void KMMoveCommand::slotImapFolderCompleted(KMFolderImap* imapFolder, bool success)
2287 {
2288  disconnect (imapFolder, TQT_SIGNAL(folderComplete( KMFolderImap*, bool )),
2289  this, TQT_SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
2290  if ( success ) {
2291  // the folder was checked successfully but we were still called, so check
2292  // if we are still waiting for messages to show up. If so, uidValidity
2293  // changed, or something else went wrong. Clean up.
2294 
2295  /* Unfortunately older UW imap servers change uid validity for each put job.
2296  * Yes, it is really that broken. *sigh* So we cannot report error here, I guess. */
2297  if ( !mLostBoys.isEmpty() ) {
2298  kdDebug(5006) << "### Not all moved messages reported back that they were " << endl
2299  << "### added to the target folder. Did uidValidity change? " << endl;
2300  }
2301  completeMove( OK );
2302  } else {
2303  // Should we inform the user here or leave that to the caller?
2304  completeMove( Failed );
2305  }
2306 }
2307 
2308 void KMMoveCommand::slotMsgAddedToDestFolder(KMFolder *folder, TQ_UINT32 serNum)
2309 {
2310  if ( folder != mDestFolder || mLostBoys.find( serNum ) == mLostBoys.end() ) {
2311  //kdDebug(5006) << "KMMoveCommand::msgAddedToDestFolder different "
2312  // "folder or invalid serial number." << endl;
2313  return;
2314  }
2315  mLostBoys.remove(serNum);
2316  if ( mLostBoys.isEmpty() ) {
2317  // we are done. All messages transferred to the host succesfully
2318  disconnect (mDestFolder, TQT_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)),
2319  this, TQT_SLOT(slotMsgAddedToDestFolder(KMFolder*, TQ_UINT32)));
2320  if (mDestFolder && mDestFolder->folderType() != KMFolderTypeImap) {
2321  mDestFolder->sync();
2322  }
2323  if ( mCompleteWithAddedMsg ) {
2324  completeMove( OK );
2325  }
2326  } else {
2327  if ( mProgressItem ) {
2328  mProgressItem->incCompletedItems();
2329  mProgressItem->updateProgress();
2330  }
2331  }
2332 }
2333 
2334 void KMMoveCommand::completeMove( Result result )
2335 {
2336  if ( mDestFolder )
2337  mDestFolder->close("kmcommand");
2338  while ( !mOpenedFolders.empty() ) {
2339  KMFolder *folder = mOpenedFolders.back();
2340  mOpenedFolders.pop_back();
2341  folder->close("kmcommand");
2342  }
2343  if ( mProgressItem ) {
2344  mProgressItem->setComplete();
2345  mProgressItem = 0;
2346  }
2347  setResult( result );
2348  emit completed( this );
2349  deleteLater();
2350 }
2351 
2352 void KMMoveCommand::slotMoveCanceled()
2353 {
2354  completeMove( Canceled );
2355 }
2356 
2357 // srcFolder doesn't make much sense for searchFolders
2358 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder,
2359  const TQPtrList<KMMsgBase> &msgList )
2360 :KMMoveCommand( findTrashFolder( srcFolder ), msgList)
2361 {
2362  srcFolder->open("kmcommand");
2363  mOpenedFolders.push_back( srcFolder );
2364 }
2365 
2366 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder, KMMessage * msg )
2367 :KMMoveCommand( findTrashFolder( srcFolder ), msg)
2368 {
2369  srcFolder->open("kmcommand");
2370  mOpenedFolders.push_back( srcFolder );
2371 }
2372 
2373 KMDeleteMsgCommand::KMDeleteMsgCommand( TQ_UINT32 sernum )
2374 :KMMoveCommand( sernum )
2375 {
2376  if ( !sernum ) {
2377  setDestFolder( 0 );
2378  return;
2379  }
2380 
2381  KMFolder *srcFolder = 0;
2382  int idx;
2383  KMMsgDict::instance()->getLocation( sernum, &srcFolder, &idx );
2384  if ( srcFolder ) {
2385  KMMsgBase *msg = srcFolder->getMsgBase( idx );
2386  srcFolder->open("kmcommand");
2387  mOpenedFolders.push_back( srcFolder );
2388  addMsg( msg );
2389  }
2390  setDestFolder( findTrashFolder( srcFolder ) );
2391 }
2392 
2393 KMFolder * KMDeleteMsgCommand::findTrashFolder( KMFolder * folder )
2394 {
2395  KMFolder* trash = folder->trashFolder();
2396  if( !trash )
2397  trash = kmkernel->trashFolder();
2398  if( trash != folder )
2399  return trash;
2400  return 0;
2401 }
2402 
2403 
2404 KMUrlClickedCommand::KMUrlClickedCommand( const KURL &url, uint identity,
2405  KMReaderWin *readerWin, bool htmlPref, KMMainWidget *mainWidget )
2406  :mUrl( url ), mIdentity( identity ), mReaderWin( readerWin ),
2407  mHtmlPref( htmlPref ), mMainWidget( mainWidget )
2408 {
2409 }
2410 
2411 KMCommand::Result KMUrlClickedCommand::execute()
2412 {
2413  KMMessage* msg;
2414 
2415  if (mUrl.protocol() == "mailto")
2416  {
2417  msg = new KMMessage;
2418  msg->initHeader(mIdentity);
2419  msg->setCharset("utf-8");
2420  msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
2421  TQString query=mUrl.query();
2422  while (!query.isEmpty()) {
2423  TQString queryPart;
2424  int secondQuery = query.find('?',1);
2425  if (secondQuery != -1)
2426  queryPart = query.left(secondQuery);
2427  else
2428  queryPart = query;
2429  query = query.mid(queryPart.length());
2430 
2431  if (queryPart.left(9) == "?subject=")
2432  msg->setSubject( KURL::decode_string(queryPart.mid(9)) );
2433  else if (queryPart.left(6) == "?body=")
2434  // It is correct to convert to latin1() as URL should not contain
2435  // anything except ascii.
2436  msg->setBody( KURL::decode_string(queryPart.mid(6)).latin1() );
2437  else if (queryPart.left(4) == "?cc=")
2438  msg->setCc( KURL::decode_string(queryPart.mid(4)) );
2439  }
2440 
2441  KMail::Composer * win = KMail::makeComposer( msg, mIdentity );
2442  win->setCharset("", true);
2443  win->show();
2444  }
2445  else if ( mUrl.protocol() == "im" )
2446  {
2447  kmkernel->imProxy()->chatWithContact( mUrl.path() );
2448  }
2449  else if ((mUrl.protocol() == "http") || (mUrl.protocol() == "https") ||
2450  (mUrl.protocol() == "ftp") || (mUrl.protocol() == "file") ||
2451  (mUrl.protocol() == "ftps") || (mUrl.protocol() == "sftp" ) ||
2452  (mUrl.protocol() == "help") || (mUrl.protocol() == "vnc") ||
2453  (mUrl.protocol() == "smb") || (mUrl.protocol() == "fish") ||
2454  (mUrl.protocol() == "news"))
2455  {
2456  KPIM::BroadcastStatus::instance()->setStatusMsg( i18n("Opening URL..."));
2457  KMimeType::Ptr mime = KMimeType::findByURL( mUrl );
2458  if (mime->name() == "application/x-desktop" ||
2459  mime->name() == "application/x-executable" ||
2460  mime->name() == "application/x-msdos-program" ||
2461  mime->name() == "application/x-shellscript" )
2462  {
2463  if (KMessageBox::warningYesNo( 0, i18n( "<qt>Do you really want to execute <b>%1</b>?</qt>" )
2464  .arg( mUrl.prettyURL() ), TQString(), i18n("Execute"), KStdGuiItem::cancel() ) != KMessageBox::Yes)
2465  return Canceled;
2466  }
2467  KRun * runner = new KRun( mUrl );
2468  runner->setRunExecutables( false );
2469  }
2470  else
2471  return Failed;
2472 
2473  return OK;
2474 }
2475 
2476 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( TQWidget *parent, KMMessage *msg )
2477  : KMCommand( parent, msg ), mImplicitAttachments( true ), mEncoded( false )
2478 {
2479 }
2480 
2481 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( TQWidget *parent, const TQPtrList<KMMsgBase>& msgs )
2482  : KMCommand( parent, msgs ), mImplicitAttachments( true ), mEncoded( false )
2483 {
2484 }
2485 
2486 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( TQWidget *parent, TQPtrList<partNode>& attachments,
2487  KMMessage *msg, bool encoded )
2488  : KMCommand( parent ), mImplicitAttachments( false ), mEncoded( encoded )
2489 {
2490  for ( TQPtrListIterator<partNode> it( attachments ); it.current(); ++it ) {
2491  mAttachmentMap.insert( it.current(), msg );
2492  }
2493 }
2494 
2495 KMCommand::Result KMSaveAttachmentsCommand::execute()
2496 {
2497  setEmitsCompletedItself( true );
2498  if ( mImplicitAttachments ) {
2499  TQPtrList<KMMessage> msgList = retrievedMsgs();
2500  KMMessage *msg;
2501  for ( TQPtrListIterator<KMMessage> itr( msgList );
2502  ( msg = itr.current() );
2503  ++itr ) {
2504  partNode *rootNode = partNode::fromMessage( msg );
2505  for ( partNode *child = rootNode; child;
2506  child = child->firstChild() ) {
2507  for ( partNode *node = child; node; node = node->nextSibling() ) {
2508  if ( node->type() != DwMime::kTypeMultipart )
2509  mAttachmentMap.insert( node, msg );
2510  }
2511  }
2512  }
2513  }
2514  setDeletesItself( true );
2515  // load all parts
2516  KMLoadPartsCommand *command = new KMLoadPartsCommand( mAttachmentMap );
2517  connect( command, TQT_SIGNAL( partsRetrieved() ),
2518  this, TQT_SLOT( slotSaveAll() ) );
2519  command->start();
2520 
2521  return OK;
2522 }
2523 
2524 void KMSaveAttachmentsCommand::slotSaveAll()
2525 {
2526  // now that all message parts have been retrieved, remove all parts which
2527  // don't represent an attachment if they were not explicitely passed in the
2528  // c'tor
2529  if ( mImplicitAttachments ) {
2530  for ( PartNodeMessageMap::iterator it = mAttachmentMap.begin();
2531  it != mAttachmentMap.end(); ) {
2532  // only body parts which have a filename or a name parameter (except for
2533  // the root node for which name is set to the message's subject) are
2534  // considered attachments
2535  if ( it.key()->msgPart().fileName().stripWhiteSpace().isEmpty() &&
2536  ( it.key()->msgPart().name().stripWhiteSpace().isEmpty() ||
2537  !it.key()->parentNode() ) ) {
2538  PartNodeMessageMap::iterator delIt = it;
2539  ++it;
2540  mAttachmentMap.remove( delIt );
2541  }
2542  else
2543  ++it;
2544  }
2545  if ( mAttachmentMap.isEmpty() ) {
2546  KMessageBox::information( 0, i18n("Found no attachments to save.") );
2547  setResult( OK ); // The user has already been informed.
2548  emit completed( this );
2549  deleteLater();
2550  return;
2551  }
2552  }
2553 
2554  KURL url, dirUrl;
2555  if ( mAttachmentMap.count() > 1 ) {
2556  // get the dir
2557  dirUrl = KDirSelectDialog::selectDirectory( TQString(), false,
2558  parentWidget(),
2559  i18n("Save Attachments To") );
2560  if ( !dirUrl.isValid() ) {
2561  setResult( Canceled );
2562  emit completed( this );
2563  deleteLater();
2564  return;
2565  }
2566 
2567  // we may not get a slash-terminated url out of KDirSelectDialog
2568  dirUrl.adjustPath( 1 );
2569  }
2570  else {
2571  // only one item, get the desired filename
2572  partNode *node = mAttachmentMap.begin().key();
2573  // replace all ':' with '_' because ':' isn't allowed on FAT volumes
2574  TQString s =
2575  node->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
2576  if ( s.isEmpty() )
2577  s = node->msgPart().name().stripWhiteSpace().replace( ':', '_' );
2578  if ( s.isEmpty() )
2579  s = i18n("filename for an unnamed attachment", "attachment.1");
2580  url = KFileDialog::getSaveURL( s, TQString(), parentWidget(),
2581  TQString() );
2582  if ( url.isEmpty() ) {
2583  setResult( Canceled );
2584  emit completed( this );
2585  deleteLater();
2586  return;
2587  }
2588  }
2589 
2590  TQMap< TQString, int > renameNumbering;
2591 
2592  Result globalResult = OK;
2593  int unnamedAtmCount = 0;
2594  for ( PartNodeMessageMap::const_iterator it = mAttachmentMap.begin();
2595  it != mAttachmentMap.end();
2596  ++it ) {
2597  KURL curUrl;
2598  if ( !dirUrl.isEmpty() ) {
2599  curUrl = dirUrl;
2600  TQString s =
2601  it.key()->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
2602  if ( s.isEmpty() )
2603  s = it.key()->msgPart().name().stripWhiteSpace().replace( ':', '_' );
2604  if ( s.isEmpty() ) {
2605  ++unnamedAtmCount;
2606  s = i18n("filename for the %1-th unnamed attachment",
2607  "attachment.%1")
2608  .arg( unnamedAtmCount );
2609  }
2610  curUrl.setFileName( s );
2611  } else {
2612  curUrl = url;
2613  }
2614 
2615  if ( !curUrl.isEmpty() ) {
2616 
2617  // Rename the file if we have already saved one with the same name:
2618  // try appending a number before extension (e.g. "pic.jpg" => "pic_2.jpg")
2619  TQString origFile = curUrl.fileName();
2620  TQString file = origFile;
2621 
2622  while ( renameNumbering.contains(file) ) {
2623  file = origFile;
2624  int num = renameNumbering[file] + 1;
2625  int dotIdx = file.findRev('.');
2626  file = file.insert( (dotIdx>=0) ? dotIdx : file.length(), TQString("_") + TQString::number(num) );
2627  }
2628  curUrl.setFileName(file);
2629 
2630  // Increment the counter for both the old and the new filename
2631  if ( !renameNumbering.contains(origFile))
2632  renameNumbering[origFile] = 1;
2633  else
2634  renameNumbering[origFile]++;
2635 
2636  if ( file != origFile ) {
2637  if ( !renameNumbering.contains(file))
2638  renameNumbering[file] = 1;
2639  else
2640  renameNumbering[file]++;
2641  }
2642 
2643 
2644  if ( KIO::NetAccess::exists( curUrl, false, parentWidget() ) ) {
2645  if ( KMessageBox::warningContinueCancel( parentWidget(),
2646  i18n( "A file named %1 already exists. Do you want to overwrite it?" )
2647  .arg( curUrl.fileName() ),
2648  i18n( "File Already Exists" ), i18n("&Overwrite") ) == KMessageBox::Cancel) {
2649  continue;
2650  }
2651  }
2652  // save
2653  const Result result = saveItem( it.key(), curUrl );
2654  if ( result != OK )
2655  globalResult = result;
2656  }
2657  }
2658  setResult( globalResult );
2659  emit completed( this );
2660  deleteLater();
2661 }
2662 
2663 KMCommand::Result KMSaveAttachmentsCommand::saveItem( partNode *node,
2664  const KURL& url )
2665 {
2666  bool bSaveEncrypted = false;
2667  bool bEncryptedParts = node->encryptionState() != KMMsgNotEncrypted;
2668  if( bEncryptedParts )
2669  if( KMessageBox::questionYesNo( parentWidget(),
2670  i18n( "The part %1 of the message is encrypted. Do you want to keep the encryption when saving?" ).
2671  arg( url.fileName() ),
2672  i18n( "KMail Question" ), i18n("Keep Encryption"), i18n("Do Not Keep") ) ==
2673  KMessageBox::Yes )
2674  bSaveEncrypted = true;
2675 
2676  bool bSaveWithSig = true;
2677  if( node->signatureState() != KMMsgNotSigned )
2678  if( KMessageBox::questionYesNo( parentWidget(),
2679  i18n( "The part %1 of the message is signed. Do you want to keep the signature when saving?" ).
2680  arg( url.fileName() ),
2681  i18n( "KMail Question" ), i18n("Keep Signature"), i18n("Do Not Keep") ) !=
2682  KMessageBox::Yes )
2683  bSaveWithSig = false;
2684 
2685  TQByteArray data;
2686  if ( mEncoded )
2687  {
2688  // This does not decode the Message Content-Transfer-Encoding
2689  // but saves the _original_ content of the message part
2690  data = KMail::Util::ByteArray( node->msgPart().dwBody() );
2691  }
2692  else
2693  {
2694  if( bSaveEncrypted || !bEncryptedParts) {
2695  partNode *dataNode = node;
2696  TQCString rawReplyString;
2697  bool gotRawReplyString = false;
2698  if( !bSaveWithSig ) {
2699  if( DwMime::kTypeMultipart == node->type() &&
2700  DwMime::kSubtypeSigned == node->subType() ){
2701  // carefully look for the part that is *not* the signature part:
2702  if( node->findType( DwMime::kTypeApplication,
2703  DwMime::kSubtypePgpSignature,
2704  true, false ) ){
2705  dataNode = node->findTypeNot( DwMime::kTypeApplication,
2706  DwMime::kSubtypePgpSignature,
2707  true, false );
2708  }else if( node->findType( DwMime::kTypeApplication,
2709  DwMime::kSubtypePkcs7Mime,
2710  true, false ) ){
2711  dataNode = node->findTypeNot( DwMime::kTypeApplication,
2712  DwMime::kSubtypePkcs7Mime,
2713  true, false );
2714  }else{
2715  dataNode = node->findTypeNot( DwMime::kTypeMultipart,
2716  DwMime::kSubtypeUnknown,
2717  true, false );
2718  }
2719  }else{
2720  ObjectTreeParser otp( 0, 0, false, false, false );
2721 
2722  // process this node and all it's siblings and descendants
2723  dataNode->setProcessed( false, true );
2724  otp.parseObjectTree( dataNode );
2725 
2726  rawReplyString = otp.rawReplyString();
2727  gotRawReplyString = true;
2728  }
2729  }
2730  TQByteArray cstr = gotRawReplyString
2731  ? rawReplyString
2732  : dataNode->msgPart().bodyDecodedBinary();
2733  data = cstr;
2734  size_t size = cstr.size();
2735  if ( dataNode->msgPart().type() == DwMime::kTypeText ) {
2736  // convert CRLF to LF before writing text attachments to disk
2737  size = KMail::Util::crlf2lf( cstr.data(), size );
2738  }
2739  data.resize( size );
2740  }
2741  }
2742  TQDataStream ds;
2743  TQFile file;
2744  KTempFile tf;
2745  tf.setAutoDelete( true );
2746  if ( url.isLocalFile() )
2747  {
2748  // save directly
2749  file.setName( url.path() );
2750  if ( !file.open( IO_WriteOnly ) )
2751  {
2752  KMessageBox::error( parentWidget(),
2753  i18n( "%2 is detailed error description",
2754  "Could not write the file %1:\n%2" )
2755  .arg( file.name() )
2756  .arg( TQString::fromLocal8Bit( strerror( errno ) ) ),
2757  i18n( "KMail Error" ) );
2758  return Failed;
2759  }
2760 
2761  // #79685 by default use the umask the user defined, but let it be configurable
2762  if ( GlobalSettings::self()->disregardUmask() )
2763  fchmod( file.handle(), S_IRUSR | S_IWUSR );
2764 
2765  ds.setDevice( &file );
2766  } else
2767  {
2768  // tmp file for upload
2769  ds.setDevice( tf.file() );
2770  }
2771 
2772  ds.writeRawBytes( data.data(), data.size() );
2773  if ( !url.isLocalFile() )
2774  {
2775  tf.close();
2776  if ( !KIO::NetAccess::upload( tf.name(), url, parentWidget() ) )
2777  {
2778  KMessageBox::error( parentWidget(),
2779  i18n( "Could not write the file %1." )
2780  .arg( url.path() ),
2781  i18n( "KMail Error" ) );
2782  return Failed;
2783  }
2784  } else
2785  file.close();
2786  return OK;
2787 }
2788 
2789 KMLoadPartsCommand::KMLoadPartsCommand( TQPtrList<partNode>& parts, KMMessage *msg )
2790  : mNeedsRetrieval( 0 )
2791 {
2792  for ( TQPtrListIterator<partNode> it( parts ); it.current(); ++it ) {
2793  mPartMap.insert( it.current(), msg );
2794  }
2795 }
2796 
2797 KMLoadPartsCommand::KMLoadPartsCommand( partNode *node, KMMessage *msg )
2798  : mNeedsRetrieval( 0 )
2799 {
2800  mPartMap.insert( node, msg );
2801 }
2802 
2803 KMLoadPartsCommand::KMLoadPartsCommand( PartNodeMessageMap& partMap )
2804  : mNeedsRetrieval( 0 ), mPartMap( partMap )
2805 {
2806 }
2807 
2808 void KMLoadPartsCommand::slotStart()
2809 {
2810  for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
2811  it != mPartMap.end();
2812  ++it ) {
2813  if ( !it.key()->msgPart().isComplete() &&
2814  !it.key()->msgPart().partSpecifier().isEmpty() ) {
2815  // incomplete part, so retrieve it first
2816  ++mNeedsRetrieval;
2817  KMFolder* curFolder = it.data()->parent();
2818  if ( curFolder ) {
2819  FolderJob *job =
2820  curFolder->createJob( it.data(), FolderJob::tGetMessage,
2821  0, it.key()->msgPart().partSpecifier() );
2822  job->setCancellable( false );
2823  connect( job, TQT_SIGNAL(messageUpdated(KMMessage*, TQString)),
2824  this, TQT_SLOT(slotPartRetrieved(KMMessage*, TQString)) );
2825  job->start();
2826  } else
2827  kdWarning(5006) << "KMLoadPartsCommand - msg has no parent" << endl;
2828  }
2829  }
2830  if ( mNeedsRetrieval == 0 )
2831  execute();
2832 }
2833 
2834 void KMLoadPartsCommand::slotPartRetrieved( KMMessage *msg,
2835  TQString partSpecifier )
2836 {
2837  DwBodyPart *part =
2838  msg->findDwBodyPart( msg->getFirstDwBodyPart(), partSpecifier );
2839  if ( part ) {
2840  // update the DwBodyPart in the partNode
2841  for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
2842  it != mPartMap.end();
2843  ++it ) {
2844  if ( it.key()->dwPart()->partId() == part->partId() )
2845  it.key()->setDwPart( part );
2846  }
2847  } else
2848  kdWarning(5006) << "KMLoadPartsCommand::slotPartRetrieved - could not find bodypart!" << endl;
2849  --mNeedsRetrieval;
2850  if ( mNeedsRetrieval == 0 )
2851  execute();
2852 }
2853 
2854 KMCommand::Result KMLoadPartsCommand::execute()
2855 {
2856  emit partsRetrieved();
2857  setResult( OK );
2858  emit completed( this );
2859  deleteLater();
2860  return OK;
2861 }
2862 
2863 KMResendMessageCommand::KMResendMessageCommand( TQWidget *parent,
2864  KMMessage *msg )
2865  :KMCommand( parent, msg )
2866 {
2867 }
2868 
2869 KMCommand::Result KMResendMessageCommand::execute()
2870 {
2871  KMMessage *msg = retrievedMessage();
2872  if ( !msg || !msg->codec() ) {
2873  return Failed;
2874  }
2875  KMMessage *newMsg = new KMMessage(*msg);
2876 
2877  TQStringList whiteList;
2878  whiteList << "To" << "Cc" << "Bcc" << "Subject";
2879  newMsg->sanitizeHeaders( whiteList );
2880 
2881  newMsg->setCharset(msg->codec()->mimeName());
2882  newMsg->setParent( 0 );
2883 
2884  // make sure we have an identity set, default, if necessary
2885  newMsg->setHeaderField("X-KMail-Identity", TQString::number( newMsg->identityUoid() ));
2886  newMsg->applyIdentity( newMsg->identityUoid() );
2887 
2888  KMail::Composer * win = KMail::makeComposer();
2889  win->setMsg(newMsg, false, true);
2890  win->show();
2891 
2892  return OK;
2893 }
2894 
2895 KMMailingListCommand::KMMailingListCommand( TQWidget *parent, KMFolder *folder )
2896  : KMCommand( parent ), mFolder( folder )
2897 {
2898 }
2899 
2900 KMCommand::Result KMMailingListCommand::execute()
2901 {
2902  KURL::List lst = urls();
2903  TQString handler = ( mFolder->mailingList().handler() == MailingList::KMail )
2904  ? "mailto" : "https";
2905 
2906  KMCommand *command = 0;
2907  for ( KURL::List::Iterator itr = lst.begin(); itr != lst.end(); ++itr ) {
2908  if ( handler == (*itr).protocol() ) {
2909  command = new KMUrlClickedCommand( *itr, mFolder->identity(), 0, false );
2910  }
2911  }
2912  if ( !command && !lst.empty() ) {
2913  command =
2914  new KMUrlClickedCommand( lst.first(), mFolder->identity(), 0, false );
2915  }
2916  if ( command ) {
2917  connect( command, TQT_SIGNAL( completed( KMCommand * ) ),
2918  this, TQT_SLOT( commandCompleted( KMCommand * ) ) );
2919  setDeletesItself( true );
2920  setEmitsCompletedItself( true );
2921  command->start();
2922  return OK;
2923  }
2924  return Failed;
2925 }
2926 
2927 void KMMailingListCommand::commandCompleted( KMCommand *command )
2928 {
2929  setResult( command->result() );
2930  emit completed( this );
2931  deleteLater();
2932 }
2933 
2934 KMMailingListPostCommand::KMMailingListPostCommand( TQWidget *parent, KMFolder *folder )
2935  : KMMailingListCommand( parent, folder )
2936 {
2937 }
2938 KURL::List KMMailingListPostCommand::urls() const
2939 {
2940  return mFolder->mailingList().postURLS();
2941 }
2942 
2943 KMMailingListSubscribeCommand::KMMailingListSubscribeCommand( TQWidget *parent, KMFolder *folder )
2944  : KMMailingListCommand( parent, folder )
2945 {
2946 }
2947 KURL::List KMMailingListSubscribeCommand::urls() const
2948 {
2949  return mFolder->mailingList().subscribeURLS();
2950 }
2951 
2952 KMMailingListUnsubscribeCommand::KMMailingListUnsubscribeCommand( TQWidget *parent, KMFolder *folder )
2953  : KMMailingListCommand( parent, folder )
2954 {
2955 }
2956 KURL::List KMMailingListUnsubscribeCommand::urls() const
2957 {
2958  return mFolder->mailingList().unsubscribeURLS();
2959 }
2960 
2961 KMMailingListArchivesCommand::KMMailingListArchivesCommand( TQWidget *parent, KMFolder *folder )
2962  : KMMailingListCommand( parent, folder )
2963 {
2964 }
2965 KURL::List KMMailingListArchivesCommand::urls() const
2966 {
2967  return mFolder->mailingList().archiveURLS();
2968 }
2969 
2970 KMMailingListHelpCommand::KMMailingListHelpCommand( TQWidget *parent, KMFolder *folder )
2971  : KMMailingListCommand( parent, folder )
2972 {
2973 }
2974 KURL::List KMMailingListHelpCommand::urls() const
2975 {
2976  return mFolder->mailingList().helpURLS();
2977 }
2978 
2979 KMIMChatCommand::KMIMChatCommand( const KURL &url, KMMessage *msg )
2980  :mUrl( url ), mMessage( msg )
2981 {
2982 }
2983 
2984 KMCommand::Result KMIMChatCommand::execute()
2985 {
2986  kdDebug( 5006 ) << k_funcinfo << " URL is: " << mUrl << endl;
2987  TQString addr = KMMessage::decodeMailtoUrl( mUrl.path() );
2988  // find UID for mail address
2989  KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
2990  KABC::AddresseeList addressees = addressBook->findByEmail( KPIM::getEmailAddress( addr ) ) ;
2991 
2992  // start chat
2993  if( addressees.count() == 1 ) {
2994  kmkernel->imProxy()->chatWithContact( addressees[0].uid() );
2995  return OK;
2996  }
2997  else
2998  {
2999  kdDebug( 5006 ) << "Didn't find exactly one addressee, couldn't tell who to chat to for that email address. Count = " << addressees.count() << endl;
3000 
3001  TQString apology;
3002  if ( addressees.isEmpty() )
3003  apology = i18n( "There is no Address Book entry for this email address. Add them to the Address Book and then add instant messaging addresses using your preferred messaging client." );
3004  else
3005  {
3006  apology = i18n( "More than one Address Book entry uses this email address:\n %1\n it is not possible to determine who to chat with." );
3007  TQStringList nameList;
3008  KABC::AddresseeList::const_iterator it = addressees.begin();
3009  KABC::AddresseeList::const_iterator end = addressees.end();
3010  for ( ; it != end; ++it )
3011  {
3012  nameList.append( (*it).realName() );
3013  }
3014  TQString names = nameList.join( TQString::fromLatin1( ",\n" ) );
3015  apology = apology.arg( names );
3016  }
3017 
3018  KMessageBox::sorry( parentWidget(), apology );
3019  return Failed;
3020  }
3021 }
3022 
3023 KMHandleAttachmentCommand::KMHandleAttachmentCommand( partNode* node,
3024  KMMessage* msg, int atmId, const TQString& atmName,
3025  AttachmentAction action, KService::Ptr offer, TQWidget* parent )
3026 : KMCommand( parent ), mNode( node ), mMsg( msg ), mAtmId( atmId ), mAtmName( atmName ),
3027  mAction( action ), mOffer( offer ), mJob( 0 )
3028 {
3029 }
3030 
3031 void KMHandleAttachmentCommand::slotStart()
3032 {
3033  if ( !mNode->msgPart().isComplete() )
3034  {
3035  // load the part
3036  kdDebug(5006) << "load part" << endl;
3037  KMLoadPartsCommand *command = new KMLoadPartsCommand( mNode, mMsg );
3038  connect( command, TQT_SIGNAL( partsRetrieved() ),
3039  this, TQT_SLOT( slotPartComplete() ) );
3040  command->start();
3041  } else
3042  {
3043  execute();
3044  }
3045 }
3046 
3047 void KMHandleAttachmentCommand::slotPartComplete()
3048 {
3049  execute();
3050 }
3051 
3052 KMCommand::Result KMHandleAttachmentCommand::execute()
3053 {
3054  switch( mAction )
3055  {
3056  case Open:
3057  atmOpen();
3058  break;
3059  case OpenWith:
3060  atmOpenWith();
3061  break;
3062  case View:
3063  atmView();
3064  break;
3065  case Save:
3066  atmSave();
3067  break;
3068  case Properties:
3069  atmProperties();
3070  break;
3071  case ChiasmusEncrypt:
3072  atmEncryptWithChiasmus();
3073  return Undefined;
3074  break;
3075  default:
3076  kdDebug(5006) << "unknown action " << mAction << endl;
3077  break;
3078  }
3079  setResult( OK );
3080  emit completed( this );
3081  deleteLater();
3082  return OK;
3083 }
3084 
3085 TQString KMHandleAttachmentCommand::createAtmFileLink() const
3086 {
3087  TQFileInfo atmFileInfo( mAtmName );
3088 
3089  if ( atmFileInfo.size() == 0 )
3090  {
3091  kdDebug(5006) << k_funcinfo << "rewriting attachment" << endl;
3092  // there is something wrong so write the file again
3093  TQByteArray data = mNode->msgPart().bodyDecodedBinary();
3094  size_t size = data.size();
3095  if ( mNode->msgPart().type() == DwMime::kTypeText && size) {
3096  // convert CRLF to LF before writing text attachments to disk
3097  size = KMail::Util::crlf2lf( data.data(), size );
3098  }
3099  KPIM::kBytesToFile( data.data(), size, mAtmName, false, false, false );
3100  }
3101 
3102  KTempFile *linkFile = new KTempFile( locateLocal("tmp", atmFileInfo.fileName() +"_["),
3103  "]."+ atmFileInfo.extension() );
3104 
3105  linkFile->setAutoDelete(true);
3106  TQString linkName = linkFile->name();
3107  delete linkFile;
3108 
3109  if ( ::link(TQFile::encodeName( mAtmName ), TQFile::encodeName( linkName )) == 0 ) {
3110  return linkName; // success
3111  }
3112  return TQString();
3113 }
3114 
3115 KService::Ptr KMHandleAttachmentCommand::getServiceOffer()
3116 {
3117  KMMessagePart& msgPart = mNode->msgPart();
3118  const TQString contentTypeStr =
3119  ( msgPart.typeStr() + '/' + msgPart.subtypeStr() ).lower();
3120 
3121  if ( contentTypeStr == "text/x-vcard" ) {
3122  atmView();
3123  return 0;
3124  }
3125  // determine the MIME type of the attachment
3126  KMimeType::Ptr mimetype;
3127  // prefer the value of the Content-Type header
3128  mimetype = KMimeType::mimeType( contentTypeStr );
3129  if ( mimetype->name() == "application/octet-stream" ) {
3130  // consider the filename if Content-Type is application/octet-stream
3131  mimetype = KMimeType::findByPath( mAtmName, 0, true /* no disk access */ );
3132  }
3133  if ( ( mimetype->name() == "application/octet-stream" )
3134  && msgPart.isComplete() ) {
3135  // consider the attachment's contents if neither the Content-Type header
3136  // nor the filename give us a clue
3137  mimetype = KMimeType::findByFileContent( mAtmName );
3138  }
3139  return KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
3140 }
3141 
3142 void KMHandleAttachmentCommand::atmOpen()
3143 {
3144  if ( !mOffer )
3145  mOffer = getServiceOffer();
3146  if ( !mOffer ) {
3147  kdDebug(5006) << k_funcinfo << "got no offer" << endl;
3148  return;
3149  }
3150 
3151  KURL::List lst;
3152  KURL url;
3153  bool autoDelete = true;
3154  TQString fname = createAtmFileLink();
3155 
3156  if ( fname.isNull() ) {
3157  autoDelete = false;
3158  fname = mAtmName;
3159  }
3160 
3161  url.setPath( fname );
3162  lst.append( url );
3163  if ( (KRun::run( *mOffer, lst, autoDelete ) <= 0) && autoDelete ) {
3164  TQFile::remove(url.path());
3165  }
3166 }
3167 
3168 void KMHandleAttachmentCommand::atmOpenWith()
3169 {
3170  KURL::List lst;
3171  KURL url;
3172  bool autoDelete = true;
3173  TQString fname = createAtmFileLink();
3174 
3175  if ( fname.isNull() ) {
3176  autoDelete = false;
3177  fname = mAtmName;
3178  }
3179 
3180  url.setPath( fname );
3181  lst.append( url );
3182  if ( (! KRun::displayOpenWithDialog(lst, autoDelete)) && autoDelete ) {
3183  TQFile::remove( url.path() );
3184  }
3185 }
3186 
3187 void KMHandleAttachmentCommand::atmView()
3188 {
3189  // we do not handle this ourself
3190  emit showAttachment( mAtmId, mAtmName );
3191 }
3192 
3193 void KMHandleAttachmentCommand::atmSave()
3194 {
3195  TQPtrList<partNode> parts;
3196  parts.append( mNode );
3197  // save, do not leave encoded
3198  KMSaveAttachmentsCommand *command =
3199  new KMSaveAttachmentsCommand( parentWidget(), parts, mMsg, false );
3200  command->start();
3201 }
3202 
3203 void KMHandleAttachmentCommand::atmProperties()
3204 {
3205  KMMsgPartDialogCompat dlg( parentWidget() , 0, true );
3206  KMMessagePart& msgPart = mNode->msgPart();
3207  dlg.setMsgPart( &msgPart );
3208  dlg.exec();
3209 }
3210 
3211 void KMHandleAttachmentCommand::atmEncryptWithChiasmus()
3212 {
3213  const partNode * node = mNode;
3214  Q_ASSERT( node );
3215  if ( !node )
3216  return;
3217 
3218  // FIXME: better detection of mimetype??
3219  if ( !mAtmName.endsWith( ".xia", false ) )
3220  return;
3221 
3222  const Kleo::CryptoBackend::Protocol * chiasmus =
3223  Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
3224  Q_ASSERT( chiasmus );
3225  if ( !chiasmus )
3226  return;
3227 
3228  const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", TQMap<TQString,TQVariant>() ) );
3229  if ( !listjob.get() ) {
3230  const TQString msg = i18n( "Chiasmus backend does not offer the "
3231  "\"x-obtain-keys\" function. Please report this bug." );
3232  KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
3233  return;
3234  }
3235 
3236  if ( listjob->exec() ) {
3237  listjob->showErrorDialog( parentWidget(), i18n( "Chiasmus Backend Error" ) );
3238  return;
3239  }
3240 
3241  const TQVariant result = listjob->property( "result" );
3242  if ( result.type() != TQVariant::StringList ) {
3243  const TQString msg = i18n( "Unexpected return value from Chiasmus backend: "
3244  "The \"x-obtain-keys\" function did not return a "
3245  "string list. Please report this bug." );
3246  KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
3247  return;
3248  }
3249 
3250  const TQStringList keys = result.toStringList();
3251  if ( keys.empty() ) {
3252  const TQString msg = i18n( "No keys have been found. Please check that a "
3253  "valid key path has been set in the Chiasmus "
3254  "configuration." );
3255  KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
3256  return;
3257  }
3258 
3259  ChiasmusKeySelector selectorDlg( parentWidget(), i18n( "Chiasmus Decryption Key Selection" ),
3260  keys, GlobalSettings::chiasmusDecryptionKey(),
3261  GlobalSettings::chiasmusDecryptionOptions() );
3262  if ( selectorDlg.exec() != TQDialog::Accepted )
3263  return;
3264 
3265  GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
3266  GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
3267  assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
3268 
3269  Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", TQMap<TQString,TQVariant>() );
3270  if ( !job ) {
3271  const TQString msg = i18n( "Chiasmus backend does not offer the "
3272  "\"x-decrypt\" function. Please report this bug." );
3273  KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
3274  return;
3275  }
3276 
3277  const TQByteArray input = node->msgPart().bodyDecodedBinary();
3278 
3279  if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
3280  !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
3281  !job->setProperty( "input", input ) ) {
3282  const TQString msg = i18n( "The \"x-decrypt\" function does not accept "
3283  "the expected parameters. Please report this bug." );
3284  KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
3285  return;
3286  }
3287 
3288  setDeletesItself( true ); // the job below is async, we have to cleanup ourselves
3289  if ( job->start() ) {
3290  job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
3291  return;
3292  }
3293 
3294  mJob = job;
3295  connect( job, TQT_SIGNAL(result(const GpgME::Error&,const TQVariant&)),
3296  this, TQT_SLOT(slotAtmDecryptWithChiasmusResult(const GpgME::Error&,const TQVariant&)) );
3297 }
3298 
3299 static const TQString chomp( const TQString & base, const TQString & suffix, bool cs ) {
3300  return base.endsWith( suffix, cs ) ? base.left( base.length() - suffix.length() ) : base ;
3301 }
3302 
3303 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusResult( const GpgME::Error & err, const TQVariant & result )
3304 {
3305  LaterDeleterWithCommandCompletion d( this );
3306  if ( !mJob )
3307  return;
3308  Q_ASSERT( mJob == sender() );
3309  if ( mJob != sender() )
3310  return;
3311  Kleo::Job * job = mJob;
3312  mJob = 0;
3313  if ( err.isCanceled() )
3314  return;
3315  if ( err ) {
3316  job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
3317  return;
3318  }
3319 
3320  if ( result.type() != TQVariant::ByteArray ) {
3321  const TQString msg = i18n( "Unexpected return value from Chiasmus backend: "
3322  "The \"x-decrypt\" function did not return a "
3323  "byte array. Please report this bug." );
3324  KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
3325  return;
3326  }
3327 
3328  const KURL url = KFileDialog::getSaveURL( chomp( mAtmName, ".xia", false ), TQString(), parentWidget() );
3329  if ( url.isEmpty() )
3330  return;
3331 
3332  bool overwrite = KMail::Util::checkOverwrite( url, parentWidget() );
3333  if ( !overwrite )
3334  return;
3335 
3336  d.setDisabled( true ); // we got this far, don't delete yet
3337  KIO::Job * uploadJob = KIO::storedPut( result.toByteArray(), url, -1, overwrite, false /*resume*/ );
3338  uploadJob->setWindow( parentWidget() );
3339  connect( uploadJob, TQT_SIGNAL(result(KIO::Job*)),
3340  this, TQT_SLOT(slotAtmDecryptWithChiasmusUploadResult(KIO::Job*)) );
3341 }
3342 
3343 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusUploadResult( KIO::Job * job )
3344 {
3345  if ( job->error() )
3346  job->showErrorDialog();
3347  LaterDeleterWithCommandCompletion d( this );
3348  d.setResult( OK );
3349 }
3350 
3351 
3352 AttachmentModifyCommand::AttachmentModifyCommand(partNode * node, KMMessage * msg, TQWidget * parent) :
3353  KMCommand( parent, msg ),
3354  mPartIndex( node->nodeId() ),
3355  mSernum( 0 )
3356 {
3357 }
3358 
3359 AttachmentModifyCommand::AttachmentModifyCommand( int nodeId, KMMessage *msg, TQWidget *parent )
3360  : KMCommand( parent, msg ),
3361  mPartIndex( nodeId ),
3362  mSernum( 0 )
3363 {
3364 }
3365 
3366 AttachmentModifyCommand::~ AttachmentModifyCommand()
3367 {
3368 }
3369 
3370 KMCommand::Result AttachmentModifyCommand::execute()
3371 {
3372  KMMessage *msg = retrievedMessage();
3373  if ( !msg )
3374  return Failed;
3375  mSernum = msg->getMsgSerNum();
3376 
3377  mFolder = msg->parent();
3378  if ( !mFolder || !mFolder->storage() )
3379  return Failed;
3380 
3381  Result res = doAttachmentModify();
3382  if ( res != OK )
3383  return res;
3384 
3385  setEmitsCompletedItself( true );
3386  setDeletesItself( true );
3387  return OK;
3388 }
3389 
3390 void AttachmentModifyCommand::storeChangedMessage(KMMessage * msg)
3391 {
3392  if ( !mFolder || !mFolder->storage() ) {
3393  kdWarning(5006) << k_funcinfo << "We lost the folder!" << endl;
3394  setResult( Failed );
3395  emit completed( this );
3396  deleteLater();
3397  }
3398  int res = mFolder->addMsg( msg ) != 0;
3399  if ( mFolder->folderType() == KMFolderTypeImap ) {
3400  KMFolderImap *f = static_cast<KMFolderImap*>( mFolder->storage() );
3401  connect( f, TQT_SIGNAL(folderComplete(KMFolderImap*,bool)),
3402  TQT_SLOT(messageStoreResult(KMFolderImap*,bool)) );
3403  } else {
3404  messageStoreResult( 0, res == 0 );
3405  }
3406 }
3407 
3408 void AttachmentModifyCommand::messageStoreResult(KMFolderImap* folder, bool success )
3409 {
3410  Q_UNUSED( folder );
3411  if ( success ) {
3412  KMCommand *delCmd = new KMDeleteMsgCommand( mSernum );
3413  connect( delCmd, TQT_SIGNAL(completed(KMCommand*)), TQT_SLOT(messageDeleteResult(KMCommand*)) );
3414  delCmd->start();
3415  return;
3416  }
3417  kdWarning(5006) << k_funcinfo << "Adding modified message failed." << endl;
3418  setResult( Failed );
3419  emit completed( this );
3420  deleteLater();
3421 }
3422 
3423 void AttachmentModifyCommand::messageDeleteResult(KMCommand * cmd)
3424 {
3425  setResult( cmd->result() );
3426  emit completed( this );
3427  deleteLater();
3428 }
3429 
3430 KMDeleteAttachmentCommand::KMDeleteAttachmentCommand(partNode * node, KMMessage * msg, TQWidget * parent) :
3431  AttachmentModifyCommand( node, msg, parent )
3432 {
3433  kdDebug(5006) << k_funcinfo << endl;
3434 }
3435 
3436 KMDeleteAttachmentCommand::KMDeleteAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent )
3437  : AttachmentModifyCommand( nodeId, msg, parent )
3438 {
3439  kdDebug(5006) << k_funcinfo << endl;
3440 }
3441 
3442 KMDeleteAttachmentCommand::~KMDeleteAttachmentCommand()
3443 {
3444  kdDebug(5006) << k_funcinfo << endl;
3445 }
3446 
3447 KMCommand::Result KMDeleteAttachmentCommand::doAttachmentModify()
3448 {
3449  KMMessage *msg = retrievedMessage();
3450  if ( !msg || !msg->deleteBodyPart( mPartIndex ) )
3451  return Failed;
3452 
3453  KMMessage *newMsg = new KMMessage();
3454  newMsg->fromDwString( msg->asDwString() );
3455  newMsg->setStatus( msg->status() );
3456 
3457  storeChangedMessage( newMsg );
3458  return OK;
3459 }
3460 
3461 
3462 KMEditAttachmentCommand::KMEditAttachmentCommand(partNode * node, KMMessage * msg, TQWidget * parent) :
3463  AttachmentModifyCommand( node, msg, parent )
3464 {
3465  kdDebug(5006) << k_funcinfo << endl;
3466  mTempFile.setAutoDelete( true );
3467 }
3468 
3469 KMEditAttachmentCommand::KMEditAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent )
3470  : AttachmentModifyCommand( nodeId, msg, parent )
3471 {
3472  kdDebug(5006) << k_funcinfo << endl;
3473  mTempFile.setAutoDelete( true );
3474 }
3475 
3476 KMEditAttachmentCommand::~ KMEditAttachmentCommand()
3477 {
3478 }
3479 
3480 KMCommand::Result KMEditAttachmentCommand::doAttachmentModify()
3481 {
3482  KMMessage *msg = retrievedMessage();
3483  if ( !msg )
3484  return Failed;
3485 
3486  KMMessagePart part;
3487  DwBodyPart *dwpart = msg->findPart( mPartIndex );
3488  if ( !dwpart )
3489  return Failed;
3490  KMMessage::bodyPart( dwpart, &part, true );
3491  if ( !part.isComplete() )
3492  return Failed;
3493 
3494  if( !dynamic_cast<DwBody*>( dwpart->Parent() ) )
3495  return Failed;
3496 
3497  mTempFile.file()->writeBlock( part.bodyDecodedBinary() );
3498  mTempFile.file()->flush();
3499 
3500  KMail::EditorWatcher *watcher =
3501  new KMail::EditorWatcher( KURL( mTempFile.file()->name() ),
3502  part.typeStr() + "/" + part.subtypeStr(),
3503  false, this, parentWidget() );
3504  connect( watcher, TQT_SIGNAL(editDone(KMail::EditorWatcher*)), TQT_SLOT(editDone(KMail::EditorWatcher*)) );
3505  if ( !watcher->start() )
3506  return Failed;
3507  setEmitsCompletedItself( true );
3508  setDeletesItself( true );
3509  return OK;
3510 }
3511 
3512 void KMEditAttachmentCommand::editDone(KMail::EditorWatcher * watcher)
3513 {
3514  kdDebug(5006) << k_funcinfo << endl;
3515  // anything changed?
3516  if ( !watcher->fileChanged() ) {
3517  kdDebug(5006) << k_funcinfo << "File has not been changed" << endl;
3518  setResult( Canceled );
3519  emit completed( this );
3520  deleteLater();
3521  }
3522 
3523  mTempFile.file()->reset();
3524  TQByteArray data = mTempFile.file()->readAll();
3525 
3526  // build the new message
3527  KMMessage *msg = retrievedMessage();
3528  KMMessagePart part;
3529  DwBodyPart *dwpart = msg->findPart( mPartIndex );
3530  KMMessage::bodyPart( dwpart, &part, true );
3531 
3532  DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
3533  assert( parentNode );
3534  parentNode->RemoveBodyPart( dwpart );
3535 
3536  KMMessagePart att;
3537  att.duplicate( part );
3538  att.setBodyEncodedBinary( data );
3539 
3540  DwBodyPart* newDwPart = msg->createDWBodyPart( &att );
3541  parentNode->AddBodyPart( newDwPart );
3542  msg->getTopLevelPart()->Assemble();
3543 
3544  KMMessage *newMsg = new KMMessage();
3545  newMsg->fromDwString( msg->asDwString() );
3546  newMsg->setStatus( msg->status() );
3547 
3548  storeChangedMessage( newMsg );
3549 }
3550 
3551 
3552 CreateTodoCommand::CreateTodoCommand(TQWidget * parent, KMMessage * msg)
3553  : KMCommand( parent, msg )
3554 {
3555 }
3556 
3557 KMCommand::Result CreateTodoCommand::execute()
3558 {
3559  KMMessage *msg = retrievedMessage();
3560  if ( !msg || !msg->codec() ) {
3561  return Failed;
3562  }
3563 
3564  KMail::KorgHelper::ensureRunning();
3565 
3566  TQString txt = i18n("From: %1\nTo: %2\nSubject: %3").arg( msg->from() )
3567  .arg( msg->to() ).arg( msg->subject() );
3568 
3569  KTempFile tf;
3570  tf.setAutoDelete( true );
3571  TQString uri = "kmail:" + TQString::number( msg->getMsgSerNum() ) + "/" + msg->msgId();
3572  tf.file()->writeBlock( msg->asDwString().c_str(), msg->asDwString().length() );
3573  tf.close();
3574 
3575  KCalendarIface_stub *iface = new KCalendarIface_stub( kapp->dcopClient(), "korganizer", "CalendarIface" );
3576  iface->openTodoEditor( i18n("Mail: %1").arg( msg->subject() ), txt, uri,
3577  tf.name(), TQStringList(), "message/rfc822", true );
3578  delete iface;
3579 
3580  return OK;
3581 }
3582 
3583 #include "kmcommands.moc"