kmail

kmcommands.cpp

Go to the documentation of this file.
00001 /* -*- mode: C++; c-file-style: "gnu" -*-
00002     This file is part of KMail, the KDE mail client.
00003     Copyright (c) 2002 Don Sanders <sanders@kde.org>
00004 
00005     KMail is free software; you can redistribute it and/or modify it
00006     under the terms of the GNU General Public License, version 2, as
00007     published by the Free Software Foundation.
00008 
00009     KMail is distributed in the hope that it will be useful, but
00010     WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     General Public License for more details.
00013 
00014     You should have received a copy of the GNU General Public License
00015     along with this program; if not, write to the Free Software
00016     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00017 */
00018 
00019 //
00020 // This file implements various "command" classes. These command classes
00021 // are based on the command design pattern.
00022 //
00023 // Historically various operations were implemented as slots of KMMainWin.
00024 // This proved inadequate as KMail has multiple top level windows
00025 // (KMMainWin, KMReaderMainWin, SearchWindow, KMComposeWin) that may
00026 // benefit from using these operations. It is desirable that these
00027 // classes can operate without depending on or altering the state of
00028 // a KMMainWin, in fact it is possible no KMMainWin object even exists.
00029 //
00030 // Now these operations have been rewritten as KMCommand based classes,
00031 // making them independent of KMMainWin.
00032 //
00033 // The base command class KMCommand is async, which is a difference
00034 // from the conventional command pattern. As normal derived classes implement
00035 // the execute method, but client classes call start() instead of
00036 // calling execute() directly. start() initiates async operations,
00037 // and on completion of these operations calls execute() and then deletes
00038 // the command. (So the client must not construct commands on the stack).
00039 //
00040 // The type of async operation supported by KMCommand is retrieval
00041 // of messages from an IMAP server.
00042 
00043 #include "kmcommands.h"
00044 
00045 #ifdef HAVE_CONFIG_H
00046 #include <config.h>
00047 #endif
00048 
00049 #include <errno.h>
00050 #include <mimelib/enum.h>
00051 #include <mimelib/field.h>
00052 #include <mimelib/mimepp.h>
00053 #include <mimelib/string.h>
00054 #include <kapplication.h>
00055 #include <dcopclient.h>
00056 
00057 #include <qtextcodec.h>
00058 #include <qpopupmenu.h>
00059 #include <qeventloop.h>
00060 
00061 #include <libemailfunctions/email.h>
00062 #include <kdcopservicestarter.h>
00063 #include <kdebug.h>
00064 #include <kfiledialog.h>
00065 #include <kabc/stdaddressbook.h>
00066 #include <kabc/addresseelist.h>
00067 #include <kdirselectdialog.h>
00068 #include <klocale.h>
00069 #include <kmessagebox.h>
00070 #include <kparts/browserextension.h>
00071 #include <kprogress.h>
00072 #include <krun.h>
00073 #include <kbookmarkmanager.h>
00074 #include <kstandarddirs.h>
00075 #include <ktempfile.h>
00076 #include <kimproxy.h>
00077 #include <kuserprofile.h>
00078 // KIO headers
00079 #include <kio/job.h>
00080 #include <kio/netaccess.h>
00081 
00082 #include <libkpimidentities/identitymanager.h>
00083 
00084 #include "actionscheduler.h"
00085 using KMail::ActionScheduler;
00086 #include "mailinglist-magic.h"
00087 #include "kmaddrbook.h"
00088 #include <kaddrbook.h>
00089 #include "composer.h"
00090 #include "kmfiltermgr.h"
00091 #include "kmfoldermbox.h"
00092 #include "kmfolderimap.h"
00093 #include "kmfoldermgr.h"
00094 #include "kmheaders.h"
00095 #include "headeritem.h"
00096 #include "kmmainwidget.h"
00097 #include "kmmsgdict.h"
00098 #include "messagesender.h"
00099 #include "kmmsgpartdlg.h"
00100 #include "undostack.h"
00101 #include "kcursorsaver.h"
00102 #include "partNode.h"
00103 #include "objecttreeparser.h"
00104 using KMail::ObjectTreeParser;
00105 using KMail::FolderJob;
00106 #include "chiasmuskeyselector.h"
00107 #include "mailsourceviewer.h"
00108 using KMail::MailSourceViewer;
00109 #include "kmreadermainwin.h"
00110 #include "secondarywindow.h"
00111 using KMail::SecondaryWindow;
00112 #include "redirectdialog.h"
00113 using KMail::RedirectDialog;
00114 #include "util.h"
00115 #include "templateparser.h"
00116 #include "editorwatcher.h"
00117 #include "korghelper.h"
00118 
00119 #include "broadcaststatus.h"
00120 #include "globalsettings.h"
00121 
00122 #include <libkdepim/kfileio.h>
00123 #include "kcalendariface_stub.h"
00124 
00125 #include "progressmanager.h"
00126 using KPIM::ProgressManager;
00127 using KPIM::ProgressItem;
00128 #include <kmime_mdn.h>
00129 using namespace KMime;
00130 
00131 #include <kleo/specialjob.h>
00132 #include <kleo/cryptobackend.h>
00133 #include <kleo/cryptobackendfactory.h>
00134 
00135 #include <qclipboard.h>
00136 
00137 #include <memory>
00138 
00139 class LaterDeleterWithCommandCompletion : public KMail::Util::LaterDeleter
00140 {
00141 public:
00142   LaterDeleterWithCommandCompletion( KMCommand* command )
00143     :LaterDeleter( command ), m_result( KMCommand::Failed )
00144   {
00145   }
00146   ~LaterDeleterWithCommandCompletion()
00147   {
00148     setResult( m_result );
00149     KMCommand *command = static_cast<KMCommand*>( m_object );
00150     emit command->completed( command );
00151   }
00152   void setResult( KMCommand::Result v ) { m_result = v; }
00153 private:
00154   KMCommand::Result m_result;
00155 };
00156 
00157 
00158 KMCommand::KMCommand( QWidget *parent )
00159   : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ),
00160     mEmitsCompletedItself( false ), mParent( parent )
00161 {
00162 }
00163 
00164 KMCommand::KMCommand( QWidget *parent, const QPtrList<KMMsgBase> &msgList )
00165   : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ),
00166     mEmitsCompletedItself( false ), mParent( parent ), mMsgList( msgList )
00167 {
00168 }
00169 
00170 KMCommand::KMCommand( QWidget *parent, KMMsgBase *msgBase )
00171   : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ),
00172     mEmitsCompletedItself( false ), mParent( parent )
00173 {
00174   mMsgList.append( msgBase );
00175 }
00176 
00177 KMCommand::KMCommand( QWidget *parent, KMMessage *msg )
00178   : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ),
00179     mEmitsCompletedItself( false ), mParent( parent )
00180 {
00181   if (msg)
00182     mMsgList.append( &msg->toMsgBase() );
00183 }
00184 
00185 KMCommand::~KMCommand()
00186 {
00187   QValueListIterator<QGuardedPtr<KMFolder> > fit;
00188   for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) {
00189     if (!(*fit))
00190       continue;
00191     (*fit)->close("kmcommand");
00192   }
00193 }
00194 
00195 KMCommand::Result KMCommand::result()
00196 {
00197   if ( mResult == Undefined )
00198     kdDebug(5006) << k_funcinfo << "mResult is Undefined" << endl;
00199   return mResult;
00200 }
00201 
00202 void KMCommand::start()
00203 {
00204   QTimer::singleShot( 0, this, SLOT( slotStart() ) );
00205 }
00206 
00207 
00208 const QPtrList<KMMessage> KMCommand::retrievedMsgs() const
00209 {
00210   return mRetrievedMsgs;
00211 }
00212 
00213 KMMessage *KMCommand::retrievedMessage() const
00214 {
00215   return mRetrievedMsgs.getFirst();
00216 }
00217 
00218 QWidget *KMCommand::parentWidget() const
00219 {
00220   return mParent;
00221 }
00222 
00223 int KMCommand::mCountJobs = 0;
00224 
00225 void KMCommand::slotStart()
00226 {
00227   connect( this, SIGNAL( messagesTransfered( KMCommand::Result ) ),
00228            this, SLOT( slotPostTransfer( KMCommand::Result ) ) );
00229   kmkernel->filterMgr()->ref();
00230 
00231   if (mMsgList.find(0) != -1) {
00232       emit messagesTransfered( Failed );
00233       return;
00234   }
00235 
00236   if ((mMsgList.count() == 1) &&
00237       (mMsgList.getFirst()->isMessage()) &&
00238       (mMsgList.getFirst()->parent() == 0))
00239   {
00240     // Special case of operating on message that isn't in a folder
00241     mRetrievedMsgs.append((KMMessage*)mMsgList.getFirst());
00242     emit messagesTransfered( OK );
00243     return;
00244   }
00245 
00246   for (KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next())
00247     if (!mb->parent()) {
00248       emit messagesTransfered( Failed );
00249       return;
00250     } else {
00251       keepFolderOpen( mb->parent() );
00252     }
00253 
00254   // transfer the selected messages first
00255   transferSelectedMsgs();
00256 }
00257 
00258 void KMCommand::slotPostTransfer( KMCommand::Result result )
00259 {
00260   disconnect( this, SIGNAL( messagesTransfered( KMCommand::Result ) ),
00261               this, SLOT( slotPostTransfer( KMCommand::Result ) ) );
00262   if ( result == OK )
00263     result = execute();
00264   mResult = result;
00265   QPtrListIterator<KMMessage> it( mRetrievedMsgs );
00266   KMMessage* msg;
00267   while ( (msg = it.current()) != 0 )
00268   {
00269     ++it;
00270     if (msg->parent())
00271       msg->setTransferInProgress(false);
00272   }
00273   kmkernel->filterMgr()->deref();
00274   if ( !emitsCompletedItself() )
00275     emit completed( this );
00276   if ( !deletesItself() )
00277     deleteLater();
00278 }
00279 
00280 void KMCommand::transferSelectedMsgs()
00281 {
00282   // make sure no other transfer is active
00283   if (KMCommand::mCountJobs > 0) {
00284     emit messagesTransfered( Failed );
00285     return;
00286   }
00287 
00288   bool complete = true;
00289   KMCommand::mCountJobs = 0;
00290   mCountMsgs = 0;
00291   mRetrievedMsgs.clear();
00292   mCountMsgs = mMsgList.count();
00293   uint totalSize = 0;
00294   // the KProgressDialog for the user-feedback. Only enable it if it's needed.
00295   // For some commands like KMSetStatusCommand it's not needed. Note, that
00296   // for some reason the KProgressDialog eats the MouseReleaseEvent (if a
00297   // command is executed after the MousePressEvent), cf. bug #71761.
00298   if ( mCountMsgs > 0 ) {
00299     mProgressDialog = new KProgressDialog(mParent, "transferProgress",
00300       i18n("Please wait"),
00301       i18n("Please wait while the message is transferred",
00302         "Please wait while the %n messages are transferred", mMsgList.count()),
00303       true);
00304     mProgressDialog->setMinimumDuration(1000);
00305   }
00306   for (KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next())
00307   {
00308     // check if all messages are complete
00309     KMMessage *thisMsg = 0;
00310     if ( mb->isMessage() )
00311       thisMsg = static_cast<KMMessage*>(mb);
00312     else
00313     {
00314       KMFolder *folder = mb->parent();
00315       int idx = folder->find(mb);
00316       if (idx < 0) continue;
00317       thisMsg = folder->getMsg(idx);
00318     }
00319     if (!thisMsg) continue;
00320     if ( thisMsg->transferInProgress() &&
00321          thisMsg->parent()->folderType() == KMFolderTypeImap )
00322     {
00323       thisMsg->setTransferInProgress( false, true );
00324       thisMsg->parent()->ignoreJobsForMessage( thisMsg );
00325     }
00326 
00327     if ( thisMsg->parent() && !thisMsg->isComplete() &&
00328          ( !mProgressDialog || !mProgressDialog->wasCancelled() ) )
00329     {
00330       kdDebug(5006)<<"### INCOMPLETE\n";
00331       // the message needs to be transferred first
00332       complete = false;
00333       KMCommand::mCountJobs++;
00334       FolderJob *job = thisMsg->parent()->createJob(thisMsg);
00335       job->setCancellable( false );
00336       totalSize += thisMsg->msgSizeServer();
00337       // emitted when the message was transferred successfully
00338       connect(job, SIGNAL(messageRetrieved(KMMessage*)),
00339               this, SLOT(slotMsgTransfered(KMMessage*)));
00340       // emitted when the job is destroyed
00341       connect(job, SIGNAL(finished()),
00342               this, SLOT(slotJobFinished()));
00343       connect(job, SIGNAL(progress(unsigned long, unsigned long)),
00344               this, SLOT(slotProgress(unsigned long, unsigned long)));
00345       // msg musn't be deleted
00346       thisMsg->setTransferInProgress(true);
00347       job->start();
00348     } else {
00349       thisMsg->setTransferInProgress(true);
00350       mRetrievedMsgs.append(thisMsg);
00351     }
00352   }
00353 
00354   if (complete)
00355   {
00356     delete mProgressDialog;
00357     mProgressDialog = 0;
00358     emit messagesTransfered( OK );
00359   } else {
00360     // wait for the transfer and tell the progressBar the necessary steps
00361     if ( mProgressDialog ) {
00362       connect(mProgressDialog, SIGNAL(cancelClicked()),
00363               this, SLOT(slotTransferCancelled()));
00364       mProgressDialog->progressBar()->setTotalSteps(totalSize);
00365     }
00366   }
00367 }
00368 
00369 void KMCommand::slotMsgTransfered(KMMessage* msg)
00370 {
00371   if ( mProgressDialog && mProgressDialog->wasCancelled() ) {
00372     emit messagesTransfered( Canceled );
00373     return;
00374   }
00375 
00376   // save the complete messages
00377   mRetrievedMsgs.append(msg);
00378 }
00379 
00380 void KMCommand::slotProgress( unsigned long done, unsigned long /*total*/ )
00381 {
00382   mProgressDialog->progressBar()->setProgress( done );
00383 }
00384 
00385 void KMCommand::slotJobFinished()
00386 {
00387   // the job is finished (with / without error)
00388   KMCommand::mCountJobs--;
00389 
00390   if ( mProgressDialog && mProgressDialog->wasCancelled() ) return;
00391 
00392   if ( (mCountMsgs - static_cast<int>(mRetrievedMsgs.count())) > KMCommand::mCountJobs )
00393   {
00394     // the message wasn't retrieved before => error
00395     if ( mProgressDialog )
00396       mProgressDialog->hide();
00397     slotTransferCancelled();
00398     return;
00399   }
00400   // update the progressbar
00401   if ( mProgressDialog ) {
00402     mProgressDialog->setLabel(i18n("Please wait while the message is transferred",
00403           "Please wait while the %n messages are transferred", KMCommand::mCountJobs));
00404   }
00405   if (KMCommand::mCountJobs == 0)
00406   {
00407     // all done
00408     delete mProgressDialog;
00409     mProgressDialog = 0;
00410     emit messagesTransfered( OK );
00411   }
00412 }
00413 
00414 void KMCommand::slotTransferCancelled()
00415 {
00416   // kill the pending jobs
00417   QValueListIterator<QGuardedPtr<KMFolder> > fit;
00418   for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) {
00419     if (!(*fit))
00420       continue;
00421     KMFolder *folder = *fit;
00422     KMFolderImap *imapFolder = dynamic_cast<KMFolderImap*>(folder);
00423     if (imapFolder && imapFolder->account()) {
00424       imapFolder->account()->killAllJobs();
00425     }
00426   }
00427 
00428   KMCommand::mCountJobs = 0;
00429   mCountMsgs = 0;
00430   // unget the transfered messages
00431   QPtrListIterator<KMMessage> it( mRetrievedMsgs );
00432   KMMessage* msg;
00433   while ( (msg = it.current()) != 0 )
00434   {
00435     KMFolder *folder = msg->parent();
00436     ++it;
00437     if (!folder)
00438       continue;
00439     msg->setTransferInProgress(false);
00440     int idx = folder->find(msg);
00441     if (idx > 0) folder->unGetMsg(idx);
00442   }
00443   mRetrievedMsgs.clear();
00444   emit messagesTransfered( Canceled );
00445 }
00446 
00447 void KMCommand::keepFolderOpen( KMFolder *folder )
00448 {
00449   folder->open("kmcommand");
00450   mFolders.append( folder );
00451 }
00452 
00453 KMMailtoComposeCommand::KMMailtoComposeCommand( const KURL &url,
00454                                                 KMMessage *msg )
00455   :mUrl( url ), mMessage( msg )
00456 {
00457 }
00458 
00459 KMCommand::Result KMMailtoComposeCommand::execute()
00460 {
00461   KMMessage *msg = new KMMessage;
00462   uint id = 0;
00463 
00464   if ( mMessage && mMessage->parent() )
00465     id = mMessage->parent()->identity();
00466 
00467   msg->initHeader(id);
00468   msg->setCharset("utf-8");
00469   msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
00470 
00471   KMail::Composer * win = KMail::makeComposer( msg, id );
00472   win->setCharset("", true);
00473   win->setFocusToSubject();
00474   win->show();
00475 
00476   return OK;
00477 }
00478 
00479 
00480 KMMailtoReplyCommand::KMMailtoReplyCommand( QWidget *parent,
00481    const KURL &url, KMMessage *msg, const QString &selection )
00482   :KMCommand( parent, msg ), mUrl( url ), mSelection( selection  )
00483 {
00484 }
00485 
00486 KMCommand::Result KMMailtoReplyCommand::execute()
00487 {
00488   //TODO : consider factoring createReply into this method.
00489   KMMessage *msg = retrievedMessage();
00490   if ( !msg || !msg->codec() ) {
00491     return Failed;
00492   }
00493   KMMessage *rmsg = msg->createReply( KMail::ReplyNone, mSelection );
00494   rmsg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
00495 
00496   KMail::Composer * win = KMail::makeComposer( rmsg, 0 );
00497   win->setCharset(msg->codec()->mimeName(), true);
00498   win->setReplyFocus();
00499   win->show();
00500 
00501   return OK;
00502 }
00503 
00504 
00505 KMMailtoForwardCommand::KMMailtoForwardCommand( QWidget *parent,
00506    const KURL &url, KMMessage *msg )
00507   :KMCommand( parent, msg ), mUrl( url )
00508 {
00509 }
00510 
00511 KMCommand::Result KMMailtoForwardCommand::execute()
00512 {
00513   //TODO : consider factoring createForward into this method.
00514   KMMessage *msg = retrievedMessage();
00515   if ( !msg || !msg->codec() ) {
00516     return Failed;
00517   }
00518   KMMessage *fmsg = msg->createForward();
00519   fmsg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
00520 
00521   KMail::Composer * win = KMail::makeComposer( fmsg );
00522   win->setCharset(msg->codec()->mimeName(), true);
00523   win->show();
00524 
00525   return OK;
00526 }
00527 
00528 
00529 KMAddBookmarksCommand::KMAddBookmarksCommand( const KURL &url, QWidget *parent )
00530   : KMCommand( parent ), mUrl( url )
00531 {
00532 }
00533 
00534 KMCommand::Result KMAddBookmarksCommand::execute()
00535 {
00536   QString filename = locateLocal( "data", QString::fromLatin1("konqueror/bookmarks.xml") );
00537   KBookmarkManager *bookManager = KBookmarkManager::managerForFile( filename,
00538                                                                     false );
00539   KBookmarkGroup group = bookManager->root();
00540   group.addBookmark( bookManager, mUrl.path(), KURL( mUrl ) );
00541   if( bookManager->save() ) {
00542     bookManager->emitChanged( group );
00543   }
00544 
00545   return OK;
00546 }
00547 
00548 KMMailtoAddAddrBookCommand::KMMailtoAddAddrBookCommand( const KURL &url,
00549    QWidget *parent )
00550   : KMCommand( parent ), mUrl( url )
00551 {
00552 }
00553 
00554 KMCommand::Result KMMailtoAddAddrBookCommand::execute()
00555 {
00556   KAddrBookExternal::addEmail( KMMessage::decodeMailtoUrl( mUrl.path() ),
00557                                parentWidget() );
00558 
00559   return OK;
00560 }
00561 
00562 
00563 KMMailtoOpenAddrBookCommand::KMMailtoOpenAddrBookCommand( const KURL &url,
00564    QWidget *parent )
00565   : KMCommand( parent ), mUrl( url )
00566 {
00567 }
00568 
00569 KMCommand::Result KMMailtoOpenAddrBookCommand::execute()
00570 {
00571   KAddrBookExternal::openEmail( KMMessage::decodeMailtoUrl( mUrl.path() ),
00572                                 parentWidget() );
00573 
00574   return OK;
00575 }
00576 
00577 
00578 KMUrlCopyCommand::KMUrlCopyCommand( const KURL &url, KMMainWidget *mainWidget )
00579   :mUrl( url ), mMainWidget( mainWidget )
00580 {
00581 }
00582 
00583 KMCommand::Result KMUrlCopyCommand::execute()
00584 {
00585   QClipboard* clip = QApplication::clipboard();
00586 
00587   if (mUrl.protocol() == "mailto") {
00588     // put the url into the mouse selection and the clipboard
00589     QString address = KMMessage::decodeMailtoUrl( mUrl.path() );
00590     clip->setSelectionMode( true );
00591     clip->setText( address );
00592     clip->setSelectionMode( false );
00593     clip->setText( address );
00594     KPIM::BroadcastStatus::instance()->setStatusMsg( i18n( "Address copied to clipboard." ));
00595   } else {
00596     // put the url into the mouse selection and the clipboard
00597     clip->setSelectionMode( true );
00598     clip->setText( mUrl.url() );
00599     clip->setSelectionMode( false );
00600     clip->setText( mUrl.url() );
00601     KPIM::BroadcastStatus::instance()->setStatusMsg( i18n( "URL copied to clipboard." ));
00602   }
00603 
00604   return OK;
00605 }
00606 
00607 
00608 KMUrlOpenCommand::KMUrlOpenCommand( const KURL &url, KMReaderWin *readerWin )
00609   :mUrl( url ), mReaderWin( readerWin )
00610 {
00611 }
00612 
00613 KMCommand::Result KMUrlOpenCommand::execute()
00614 {
00615   if ( !mUrl.isEmpty() )
00616     mReaderWin->slotUrlOpen( mUrl, KParts::URLArgs() );
00617 
00618   return OK;
00619 }
00620 
00621 
00622 KMUrlSaveCommand::KMUrlSaveCommand( const KURL &url, QWidget *parent )
00623   : KMCommand( parent ), mUrl( url )
00624 {
00625 }
00626 
00627 KMCommand::Result KMUrlSaveCommand::execute()
00628 {
00629   if ( mUrl.isEmpty() )
00630     return OK;
00631   KURL saveUrl = KFileDialog::getSaveURL(mUrl.fileName(), QString::null,
00632                                          parentWidget() );
00633   if ( saveUrl.isEmpty() )
00634     return Canceled;
00635   if ( KIO::NetAccess::exists( saveUrl, false, parentWidget() ) )
00636   {
00637     if (KMessageBox::warningContinueCancel(0,
00638         i18n("<qt>File <b>%1</b> exists.<br>Do you want to replace it?</qt>")
00639         .arg(saveUrl.prettyURL()), i18n("Save to File"), i18n("&Replace"))
00640         != KMessageBox::Continue)
00641       return Canceled;
00642   }
00643   KIO::Job *job = KIO::file_copy(mUrl, saveUrl, -1, true);
00644   connect(job, SIGNAL(result(KIO::Job*)), SLOT(slotUrlSaveResult(KIO::Job*)));
00645   setEmitsCompletedItself( true );
00646   return OK;
00647 }
00648 
00649 void KMUrlSaveCommand::slotUrlSaveResult( KIO::Job *job )
00650 {
00651   if ( job->error() ) {
00652     job->showErrorDialog();
00653     setResult( Failed );
00654     emit completed( this );
00655   }
00656   else {
00657     setResult( OK );
00658     emit completed( this );
00659   }
00660 }
00661 
00662 
00663 KMEditMsgCommand::KMEditMsgCommand( QWidget *parent, KMMessage *msg )
00664   :KMCommand( parent, msg )
00665 {
00666 }
00667 
00668 KMCommand::Result KMEditMsgCommand::execute()
00669 {
00670   KMMessage *msg = retrievedMessage();
00671   if ( !msg || !msg->parent() ||
00672        ( !kmkernel->folderIsDraftOrOutbox( msg->parent() ) &&
00673          !kmkernel->folderIsTemplates( msg->parent() ) ) )
00674     return Failed;
00675 
00676   // Remember the old parent, we need it a bit further down to be able
00677   // to put the unchanged messsage back in the original folder if the nth
00678   // edit is discarded, for n > 1.
00679   KMFolder *parent = msg->parent();
00680   if ( parent )
00681     parent->take( parent->find( msg ) );
00682 
00683   KMail::Composer * win = KMail::makeComposer();
00684   msg->setTransferInProgress(false); // From here on on, the composer owns the message.
00685   win->setMsg(msg, false, true);
00686   win->setFolder( parent );
00687   win->show();
00688 
00689   return OK;
00690 }
00691 
00692 KMUseTemplateCommand::KMUseTemplateCommand( QWidget *parent, KMMessage *msg )
00693   :KMCommand( parent, msg )
00694 {
00695 }
00696 
00697 KMCommand::Result KMUseTemplateCommand::execute()
00698 {
00699   KMMessage *msg = retrievedMessage();
00700   if ( !msg || !msg->parent() ||
00701        !kmkernel->folderIsTemplates( msg->parent() ) )
00702     return Failed;
00703 
00704   // Take a copy of the original message, which remains unchanged.
00705   KMMessage *newMsg = new KMMessage( new DwMessage( *msg->asDwMessage() ) );
00706   newMsg->setComplete( msg->isComplete() );
00707 
00708   // these fields need to be regenerated for the new message
00709   newMsg->removeHeaderField("Date");
00710   newMsg->removeHeaderField("Message-ID");
00711 
00712   KMail::Composer *win = KMail::makeComposer();
00713   newMsg->setTransferInProgress( false ); // From here on on, the composer owns the message.
00714   win->setMsg( newMsg, false, true );
00715   win->show();
00716 
00717   return OK;
00718 }
00719 
00720 KMShowMsgSrcCommand::KMShowMsgSrcCommand( QWidget *parent,
00721   KMMessage *msg, bool fixedFont )
00722   :KMCommand( parent, msg ), mFixedFont( fixedFont )
00723 {
00724   // remember complete state
00725   mMsgWasComplete = msg->isComplete();
00726 }
00727 
00728 KMCommand::Result KMShowMsgSrcCommand::execute()
00729 {
00730   KMMessage *msg = retrievedMessage();
00731   if ( !msg || !msg->codec() ) {
00732     return Failed;
00733   }
00734   if ( msg->isComplete() && !mMsgWasComplete )
00735     msg->notify(); // notify observers as msg was transfered
00736   QString str = msg->codec()->toUnicode( msg->asString() );
00737 
00738   MailSourceViewer *viewer = new MailSourceViewer(); // deletes itself upon close
00739   viewer->setCaption( i18n("Message as Plain Text") );
00740   viewer->setText(str);
00741   if( mFixedFont )
00742     viewer->setFont(KGlobalSettings::fixedFont());
00743 
00744   // Well, there is no widget to be seen here, so we have to use QCursor::pos()
00745   // Update: (GS) I'm not going to make this code behave according to Xinerama
00746   //         configuration because this is quite the hack.
00747   if (QApplication::desktop()->isVirtualDesktop()) {
00748     int scnum = QApplication::desktop()->screenNumber(QCursor::pos());
00749     viewer->resize(QApplication::desktop()->screenGeometry(scnum).width()/2,
00750                   2*QApplication::desktop()->screenGeometry(scnum).height()/3);
00751   } else {
00752     viewer->resize(QApplication::desktop()->geometry().width()/2,
00753                   2*QApplication::desktop()->geometry().height()/3);
00754   }
00755   viewer->show();
00756 
00757   return OK;
00758 }
00759 
00760 static KURL subjectToUrl( const QString & subject ) {
00761     return KFileDialog::getSaveURL( subject.stripWhiteSpace()
00762                                            .replace( QDir::separator(), '_' ),
00763                                     "*.mbox" );
00764 }
00765 
00766 KMSaveMsgCommand::KMSaveMsgCommand( QWidget *parent, KMMessage * msg )
00767   : KMCommand( parent ),
00768     mMsgListIndex( 0 ),
00769     mStandAloneMessage( 0 ),
00770     mOffset( 0 ),
00771     mTotalSize( msg ? msg->msgSize() : 0 )
00772 {
00773   if ( !msg ) return;
00774   setDeletesItself( true );
00775   // If the mail has a serial number, operate on sernums, if it does not
00776   // we need to work with the pointer, but can be reasonably sure it won't
00777   // go away, since it'll be an encapsulated message or one that was opened
00778   // from an .eml file.
00779   if ( msg->getMsgSerNum() != 0 ) {
00780     mMsgList.append( msg->getMsgSerNum() );
00781     if ( msg->parent() ) {
00782       msg->parent()->open( "kmsavemsgcommand" );
00783     }
00784   } else {
00785     mStandAloneMessage = msg;
00786   }
00787   mUrl = subjectToUrl( msg->cleanSubject() );
00788 }
00789 
00790 KMSaveMsgCommand::KMSaveMsgCommand( QWidget *parent,
00791                                     const QPtrList<KMMsgBase> &msgList )
00792   : KMCommand( parent ),
00793     mMsgListIndex( 0 ),
00794     mStandAloneMessage( 0 ),
00795     mOffset( 0 ),
00796     mTotalSize( 0 )
00797 {
00798   if (!msgList.getFirst())
00799     return;
00800   setDeletesItself( true );
00801   KMMsgBase *msgBase = msgList.getFirst();
00802 
00803   // We operate on serNums and not the KMMsgBase pointers, as those can
00804   // change, or become invalid when changing the current message, switching
00805   // folders, etc.
00806   QPtrListIterator<KMMsgBase> it(msgList);
00807   while ( it.current() ) {
00808     mMsgList.append( (*it)->getMsgSerNum() );
00809     mTotalSize += (*it)->msgSize();
00810     if ((*it)->parent() != 0)
00811       (*it)->parent()->open("kmcommand");
00812     ++it;
00813   }
00814   mMsgListIndex = 0;
00815   mUrl = subjectToUrl( msgBase->cleanSubject() );
00816 }
00817 
00818 KURL KMSaveMsgCommand::url()
00819 {
00820   return mUrl;
00821 }
00822 
00823 KMCommand::Result KMSaveMsgCommand::execute()
00824 {
00825   mJob = KIO::put( mUrl, S_IRUSR|S_IWUSR, false, false );
00826   mJob->slotTotalSize( mTotalSize );
00827   mJob->setAsyncDataEnabled( true );
00828   mJob->setReportDataSent( true );
00829   connect(mJob, SIGNAL(dataReq(KIO::Job*, QByteArray &)),
00830     SLOT(slotSaveDataReq()));
00831   connect(mJob, SIGNAL(result(KIO::Job*)),
00832     SLOT(slotSaveResult(KIO::Job*)));
00833   setEmitsCompletedItself( true );
00834   return OK;
00835 }
00836 
00837 void KMSaveMsgCommand::slotSaveDataReq()
00838 {
00839   int remainingBytes = mData.size() - mOffset;
00840   if ( remainingBytes > 0 ) {
00841     // eat leftovers first
00842     if ( remainingBytes > MAX_CHUNK_SIZE )
00843       remainingBytes = MAX_CHUNK_SIZE;
00844 
00845     QByteArray data;
00846     data.duplicate( mData.data() + mOffset, remainingBytes );
00847     mJob->sendAsyncData( data );
00848     mOffset += remainingBytes;
00849     return;
00850   }
00851   // No leftovers, process next message.
00852   if ( mMsgListIndex < mMsgList.size() ) {
00853     KMMessage *msg = 0;
00854     int idx = -1;
00855     KMFolder * p = 0;
00856     KMMsgDict::instance()->getLocation( mMsgList[mMsgListIndex], &p, &idx );
00857     assert( p );
00858     assert( idx >= 0 );
00859     //kdDebug() << "SERNUM: " << mMsgList[mMsgListIndex] << " idx: " << idx << " folder: " << p->prettyURL() << endl;
00860     msg = p->getMsg(idx);
00861 
00862     if ( msg ) {
00863       if ( msg->transferInProgress() ) {
00864         QByteArray data = QByteArray();
00865         mJob->sendAsyncData( data );
00866       }
00867       msg->setTransferInProgress( true );
00868       if (msg->isComplete() ) {
00869       slotMessageRetrievedForSaving( msg );
00870       } else {
00871         // retrieve Message first
00872         if ( msg->parent()  && !msg->isComplete() ) {
00873           FolderJob *job = msg->parent()->createJob( msg );
00874           job->setCancellable( false );
00875           connect(job, SIGNAL( messageRetrieved( KMMessage* ) ),
00876                   this, SLOT( slotMessageRetrievedForSaving( KMMessage* ) ) );
00877           job->start();
00878         }
00879       }
00880     } else {
00881       mJob->slotError( KIO::ERR_ABORTED,
00882                        i18n("The message was removed while saving it. "
00883                             "It has not been saved.") );
00884     }
00885   } else {
00886     if ( mStandAloneMessage ) {
00887       // do the special case of a standalone message
00888       slotMessageRetrievedForSaving( mStandAloneMessage );
00889       mStandAloneMessage = 0;
00890     } else {
00891       // No more messages. Tell the putjob we are done.
00892       QByteArray data = QByteArray();
00893       mJob->sendAsyncData( data );
00894     }
00895   }
00896 }
00897 
00898 void KMSaveMsgCommand::slotMessageRetrievedForSaving(KMMessage *msg)
00899 {
00900   if ( msg ) {
00901     mData = KMFolderMbox::escapeFrom( msg->asDwString() );
00902     KMail::Util::insert( mData, 0, msg->mboxMessageSeparator() );
00903     KMail::Util::append( mData, "\n" );
00904     msg->setTransferInProgress(false);
00905 
00906     mOffset = 0;
00907     QByteArray data;
00908     int size;
00909     // Unless it is great than 64 k send the whole message. kio buffers for us.
00910     if( mData.size() > (unsigned int) MAX_CHUNK_SIZE )
00911       size = MAX_CHUNK_SIZE;
00912     else
00913       size = mData.size();
00914 
00915     data.duplicate( mData, size );
00916     mJob->sendAsyncData( data );
00917     mOffset += size;
00918   }
00919   ++mMsgListIndex;
00920   // Get rid of the message.
00921   if ( msg && msg->parent() && msg->getMsgSerNum() ) {
00922     int idx = -1;
00923     KMFolder * p = 0;
00924     KMMsgDict::instance()->getLocation( msg, &p, &idx );
00925     assert( p == msg->parent() ); assert( idx >= 0 );
00926     p->unGetMsg( idx );
00927     p->close("kmcommand");
00928   }
00929 }
00930 
00931 void KMSaveMsgCommand::slotSaveResult(KIO::Job *job)
00932 {
00933   if (job->error())
00934   {
00935     if (job->error() == KIO::ERR_FILE_ALREADY_EXIST)
00936     {
00937       if (KMessageBox::warningContinueCancel(0,
00938         i18n("File %1 exists.\nDo you want to replace it?")
00939         .arg(mUrl.prettyURL()), i18n("Save to File"), i18n("&Replace"))
00940         == KMessageBox::Continue) {
00941         mOffset = 0;
00942 
00943         mJob = KIO::put( mUrl, S_IRUSR|S_IWUSR, true, false );
00944         mJob->slotTotalSize( mTotalSize );
00945         mJob->setAsyncDataEnabled( true );
00946         mJob->setReportDataSent( true );
00947         connect(mJob, SIGNAL(dataReq(KIO::Job*, QByteArray &)),
00948             SLOT(slotSaveDataReq()));
00949         connect(mJob, SIGNAL(result(KIO::Job*)),
00950             SLOT(slotSaveResult(KIO::Job*)));
00951       }
00952     }
00953     else
00954     {
00955       job->showErrorDialog();
00956       setResult( Failed );
00957       emit completed( this );
00958       deleteLater();
00959     }
00960   } else {
00961     setResult( OK );
00962     emit completed( this );
00963     deleteLater();
00964   }
00965 }
00966 
00967 //-----------------------------------------------------------------------------
00968 
00969 KMOpenMsgCommand::KMOpenMsgCommand( QWidget *parent, const KURL & url,
00970                                     const QString & encoding )
00971   : KMCommand( parent ),
00972     mUrl( url ),
00973     mEncoding( encoding )
00974 {
00975   setDeletesItself( true );
00976 }
00977 
00978 KMCommand::Result KMOpenMsgCommand::execute()
00979 {
00980   if ( mUrl.isEmpty() ) {
00981     mUrl = KFileDialog::getOpenURL( ":OpenMessage", "message/rfc822 application/mbox",
00982                                     parentWidget(), i18n("Open Message") );
00983   }
00984   if ( mUrl.isEmpty() ) {
00985     setDeletesItself( false );
00986     return Canceled;
00987   }
00988   mJob = KIO::get( mUrl, false, false );
00989   mJob->setReportDataSent( true );
00990   connect( mJob, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
00991            this, SLOT( slotDataArrived( KIO::Job*, const QByteArray & ) ) );
00992   connect( mJob, SIGNAL( result( KIO::Job * ) ),
00993            SLOT( slotResult( KIO::Job * ) ) );
00994   setEmitsCompletedItself( true );
00995   return OK;
00996 }
00997 
00998 void KMOpenMsgCommand::slotDataArrived( KIO::Job *, const QByteArray & data )
00999 {
01000   if ( data.isEmpty() )
01001     return;
01002 
01003   mMsgString.append( data.data(), data.size() );
01004 }
01005 
01006 void KMOpenMsgCommand::slotResult( KIO::Job *job )
01007 {
01008   if ( job->error() ) {
01009     // handle errors
01010     job->showErrorDialog();
01011     setResult( Failed );
01012     emit completed( this );
01013   }
01014   else {
01015     int startOfMessage = 0;
01016     if ( mMsgString.compare( 0, 5, "From ", 5 ) == 0 ) {
01017       startOfMessage = mMsgString.find( '\n' );
01018       if ( startOfMessage == -1 ) {
01019         KMessageBox::sorry( parentWidget(),
01020                             i18n( "The file does not contain a message." ) );
01021         setResult( Failed );
01022         emit completed( this );
01023         // Emulate closing of a secondary window so that KMail exits in case it
01024         // was started with the --view command line option. Otherwise an
01025         // invisible KMail would keep running.
01026         SecondaryWindow *win = new SecondaryWindow();
01027         win->close();
01028         win->deleteLater();
01029         deleteLater();
01030         return;
01031       }
01032       startOfMessage += 1; // the message starts after the '\n'
01033     }
01034     // check for multiple messages in the file
01035     bool multipleMessages = true;
01036     int endOfMessage = mMsgString.find( "\nFrom " );
01037     if ( endOfMessage == -1 ) {
01038       endOfMessage = mMsgString.length();
01039       multipleMessages = false;
01040     }
01041     DwMessage *dwMsg = new DwMessage;
01042     dwMsg->FromString( mMsgString.substr( startOfMessage,
01043                                           endOfMessage - startOfMessage ) );
01044     dwMsg->Parse();
01045     // check whether we have a message ( no headers => this isn't a message )
01046     if ( dwMsg->Headers().NumFields() == 0 ) {
01047       KMessageBox::sorry( parentWidget(),
01048                           i18n( "The file does not contain a message." ) );
01049       delete dwMsg; dwMsg = 0;
01050       setResult( Failed );
01051       emit completed( this );
01052       // Emulate closing of a secondary window (see above).
01053       SecondaryWindow *win = new SecondaryWindow();
01054       win->close();
01055       win->deleteLater();
01056       deleteLater();
01057       return;
01058     }
01059     KMMessage *msg = new KMMessage( dwMsg );
01060     msg->setReadyToShow( true );
01061     KMReaderMainWin *win = new KMReaderMainWin();
01062     win->showMsg( mEncoding, msg );
01063     win->show();
01064     if ( multipleMessages )
01065       KMessageBox::information( win,
01066                                 i18n( "The file contains multiple messages. "
01067                                       "Only the first message is shown." ) );
01068     setResult( OK );
01069     emit completed( this );
01070   }
01071   deleteLater();
01072 }
01073 
01074 //-----------------------------------------------------------------------------
01075 
01076 //TODO: ReplyTo, NoQuoteReplyTo, ReplyList, ReplyToAll, ReplyAuthor
01077 //      are all similar and should be factored
01078 KMReplyToCommand::KMReplyToCommand( QWidget *parent, KMMessage *msg,
01079                                     const QString &selection )
01080   : KMCommand( parent, msg ), mSelection( selection )
01081 {
01082 }
01083 
01084 KMCommand::Result KMReplyToCommand::execute()
01085 {
01086   KCursorSaver busy(KBusyPtr::busy());
01087   KMMessage *msg = retrievedMessage();
01088   if ( !msg || !msg->codec() ) {
01089     return Failed;
01090   }
01091   KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection );
01092   KMail::Composer * win = KMail::makeComposer( reply );
01093   win->setCharset( msg->codec()->mimeName(), true );
01094   win->setReplyFocus();
01095   win->show();
01096 
01097   return OK;
01098 }
01099 
01100 
01101 KMNoQuoteReplyToCommand::KMNoQuoteReplyToCommand( QWidget *parent,
01102                                                   KMMessage *msg )
01103   : KMCommand( parent, msg )
01104 {
01105 }
01106 
01107 KMCommand::Result KMNoQuoteReplyToCommand::execute()
01108 {
01109   KCursorSaver busy(KBusyPtr::busy());
01110   KMMessage *msg = retrievedMessage();
01111   if ( !msg || !msg->codec() ) {
01112     return Failed;
01113   }
01114   KMMessage *reply = msg->createReply( KMail::ReplySmart, "", true);
01115   KMail::Composer * win = KMail::makeComposer( reply );
01116   win->setCharset(msg->codec()->mimeName(), true);
01117   win->setReplyFocus(false);
01118   win->show();
01119 
01120   return OK;
01121 }
01122 
01123 
01124 KMReplyListCommand::KMReplyListCommand( QWidget *parent,
01125   KMMessage *msg, const QString &selection )
01126  : KMCommand( parent, msg ), mSelection( selection )
01127 {
01128 }
01129 
01130 KMCommand::Result KMReplyListCommand::execute()
01131 {
01132   KCursorSaver busy(KBusyPtr::busy());
01133   KMMessage *msg = retrievedMessage();
01134   if ( !msg || !msg->codec() ) {
01135     return Failed;
01136   }
01137   KMMessage *reply = msg->createReply( KMail::ReplyList, mSelection);
01138   KMail::Composer * win = KMail::makeComposer( reply );
01139   win->setCharset(msg->codec()->mimeName(), true);
01140   win->setReplyFocus(false);
01141   win->show();
01142 
01143   return OK;
01144 }
01145 
01146 
01147 KMReplyToAllCommand::KMReplyToAllCommand( QWidget *parent,
01148   KMMessage *msg, const QString &selection )
01149   :KMCommand( parent, msg ), mSelection( selection )
01150 {
01151 }
01152 
01153 KMCommand::Result KMReplyToAllCommand::execute()
01154 {
01155   KCursorSaver busy(KBusyPtr::busy());
01156   KMMessage *msg = retrievedMessage();
01157   if ( !msg || !msg->codec() ) {
01158     return Failed;
01159   }
01160   KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection );
01161   KMail::Composer * win = KMail::makeComposer( reply );
01162   win->setCharset( msg->codec()->mimeName(), true );
01163   win->setReplyFocus();
01164   win->show();
01165 
01166   return OK;
01167 }
01168 
01169 
01170 KMReplyAuthorCommand::KMReplyAuthorCommand( QWidget *parent, KMMessage *msg,
01171                                             const QString &selection )
01172   : KMCommand( parent, msg ), mSelection( selection )
01173 {
01174 }
01175 
01176 KMCommand::Result KMReplyAuthorCommand::execute()
01177 {
01178   KCursorSaver busy(KBusyPtr::busy());
01179   KMMessage *msg = retrievedMessage();
01180   if ( !msg || !msg->codec() ) {
01181     return Failed;
01182   }
01183   KMMessage *reply = msg->createReply( KMail::ReplyAuthor, mSelection );
01184   KMail::Composer * win = KMail::makeComposer( reply );
01185   win->setCharset( msg->codec()->mimeName(), true );
01186   win->setReplyFocus();
01187   win->show();
01188 
01189   return OK;
01190 }
01191 
01192 
01193 KMForwardInlineCommand::KMForwardInlineCommand( QWidget *parent,
01194   const QPtrList<KMMsgBase> &msgList, uint identity )
01195   : KMCommand( parent, msgList ),
01196     mIdentity( identity )
01197 {
01198 }
01199 
01200 KMForwardInlineCommand::KMForwardInlineCommand( QWidget *parent,
01201   KMMessage *msg, uint identity )
01202   : KMCommand( parent, msg ),
01203     mIdentity( identity )
01204 {
01205 }
01206 
01207 KMCommand::Result KMForwardInlineCommand::execute()
01208 {
01209   QPtrList<KMMessage> msgList = retrievedMsgs();
01210 
01211   if (msgList.count() >= 2) { // Multiple forward
01212 
01213     uint id = 0;
01214     QPtrList<KMMessage> linklist;
01215     for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
01216       // set the identity
01217       if (id == 0)
01218         id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
01219 
01220       // msgText += msg->createForwardBody();
01221       linklist.append( msg );
01222     }
01223     if ( id == 0 )
01224       id = mIdentity; // use folder identity if no message had an id set
01225     KMMessage *fwdMsg = new KMMessage;
01226     fwdMsg->initHeader( id );
01227     fwdMsg->setAutomaticFields( true );
01228     fwdMsg->setCharset( "utf-8" );
01229     // fwdMsg->setBody( msgText );
01230 
01231     for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) {
01232       TemplateParser parser( fwdMsg, TemplateParser::Forward,
01233         msg->body(), false, false, false, false);
01234         parser.process( msg, 0, true );
01235 
01236       fwdMsg->link( msg, KMMsgStatusForwarded );
01237     }
01238 
01239     KCursorSaver busy( KBusyPtr::busy() );
01240     KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
01241     win->setCharset("");
01242     win->show();
01243 
01244   } else { // forward a single message at most
01245 
01246     KMMessage *msg = msgList.getFirst();
01247     if ( !msg || !msg->codec() )
01248       return Failed;
01249 
01250     KCursorSaver busy( KBusyPtr::busy() );
01251     KMMessage *fwdMsg = msg->createForward();
01252 
01253     uint id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
01254     if ( id == 0 )
01255       id = mIdentity;
01256     {
01257       KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
01258       win->setCharset( fwdMsg->codec()->mimeName(), true );
01259       win->setBody( fwdMsg->bodyToUnicode() );
01260       win->show();
01261     }
01262   }
01263   return OK;
01264 }
01265 
01266 
01267 KMForwardAttachedCommand::KMForwardAttachedCommand( QWidget *parent,
01268            const QPtrList<KMMsgBase> &msgList, uint identity, KMail::Composer *win )
01269   : KMCommand( parent, msgList ), mIdentity( identity ),
01270     mWin( QGuardedPtr<KMail::Composer>( win ))
01271 {
01272 }
01273 
01274 KMForwardAttachedCommand::KMForwardAttachedCommand( QWidget *parent,
01275            KMMessage * msg, uint identity, KMail::Composer *win )
01276   : KMCommand( parent, msg ), mIdentity( identity ),
01277     mWin( QGuardedPtr< KMail::Composer >( win ))
01278 {
01279 }
01280 
01281 KMCommand::Result KMForwardAttachedCommand::execute()
01282 {
01283   QPtrList<KMMessage> msgList = retrievedMsgs();
01284   KMMessage *fwdMsg = new KMMessage;
01285 
01286   if (msgList.count() >= 2) {
01287     // don't respect X-KMail-Identity headers because they might differ for
01288     // the selected mails
01289     fwdMsg->initHeader(mIdentity);
01290   }
01291   else if (msgList.count() == 1) {
01292     KMMessage *msg = msgList.getFirst();
01293     fwdMsg->initFromMessage(msg);
01294     fwdMsg->setSubject( msg->forwardSubject() );
01295   }
01296 
01297   fwdMsg->setAutomaticFields(true);
01298 
01299   KCursorSaver busy(KBusyPtr::busy());
01300   if (!mWin)
01301     mWin = KMail::makeComposer(fwdMsg, mIdentity);
01302 
01303   // iterate through all the messages to be forwarded
01304   for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) {
01305     // remove headers that shouldn't be forwarded
01306     msg->removePrivateHeaderFields();
01307     msg->removeHeaderField("BCC");
01308     // set the part
01309     KMMessagePart *msgPart = new KMMessagePart;
01310     msgPart->setTypeStr("message");
01311     msgPart->setSubtypeStr("rfc822");
01312     msgPart->setCharset(msg->charset());
01313     msgPart->setName("forwarded message");
01314     msgPart->setContentDescription(msg->from()+": "+msg->subject());
01315     msgPart->setContentDisposition( "inline" );
01316     // THIS HAS TO BE AFTER setCte()!!!!
01317     msgPart->setMessageBody( KMail::Util::ByteArray( msg->asDwString() ) );
01318     msgPart->setCharset("");
01319 
01320     fwdMsg->link(msg, KMMsgStatusForwarded);
01321     mWin->addAttach(msgPart);
01322   }
01323 
01324   mWin->show();
01325 
01326   return OK;
01327 }
01328 
01329 
01330 KMForwardDigestCommand::KMForwardDigestCommand( QWidget *parent,
01331            const QPtrList<KMMsgBase> &msgList, uint identity, KMail::Composer *win )
01332   : KMCommand( parent, msgList ), mIdentity( identity ),
01333     mWin( QGuardedPtr<KMail::Composer>( win ))
01334 {
01335 }
01336 
01337 KMForwardDigestCommand::KMForwardDigestCommand( QWidget *parent,
01338            KMMessage * msg, uint identity, KMail::Composer *win )
01339   : KMCommand( parent, msg ), mIdentity( identity ),
01340     mWin( QGuardedPtr< KMail::Composer >( win ))
01341 {
01342 }
01343 
01344 KMCommand::Result KMForwardDigestCommand::execute()
01345 {
01346   QPtrList<KMMessage> msgList = retrievedMsgs();
01347 
01348   if ( msgList.count() < 2 )
01349     return Undefined; // must have more than 1 for a digest
01350 
01351   uint id = 0;
01352   KMMessage *fwdMsg = new KMMessage;
01353   KMMessagePart *msgPart = new KMMessagePart;
01354   QString msgPartText;
01355   int msgCnt = 0; // incase there are some we can't forward for some reason
01356 
01357   // dummy header initialization; initialization with the correct identity
01358   // is done below
01359   fwdMsg->initHeader( id );
01360   fwdMsg->setAutomaticFields( true );
01361   fwdMsg->mMsg->Headers().ContentType().CreateBoundary( 1 );
01362   QCString boundary( fwdMsg->mMsg->Headers().ContentType().Boundary().c_str() );
01363   msgPartText = i18n("\nThis is a MIME digest forward. The content of the"
01364                      " message is contained in the attachment(s).\n\n\n");
01365   // iterate through all the messages to be forwarded
01366   for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
01367     // set the identity
01368     if ( id == 0 )
01369       id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
01370     // set the part header
01371     msgPartText += "--";
01372     msgPartText += QString::fromLatin1( boundary );
01373     msgPartText += "\nContent-Type: MESSAGE/RFC822";
01374     msgPartText += QString( "; CHARSET=%1" ).arg( msg->charset() );
01375     msgPartText += '\n';
01376     DwHeaders dwh;
01377     dwh.MessageId().CreateDefault();
01378     msgPartText += QString( "Content-ID: %1\n" ).arg( dwh.MessageId().AsString().c_str() );
01379     msgPartText += QString( "Content-Description: %1" ).arg( msg->subject() );
01380     if ( !msg->subject().contains( "(fwd)" ) )
01381       msgPartText += " (fwd)";
01382     msgPartText += "\n\n";
01383     // remove headers that shouldn't be forwarded
01384     msg->removePrivateHeaderFields();
01385     msg->removeHeaderField( "BCC" );
01386     // set the part
01387     msgPartText += msg->headerAsString();
01388     msgPartText += '\n';
01389     msgPartText += msg->body();
01390     msgPartText += '\n';     // eot
01391     msgCnt++;
01392     fwdMsg->link( msg, KMMsgStatusForwarded );
01393   }
01394 
01395   if ( id == 0 )
01396     id = mIdentity; // use folder identity if no message had an id set
01397   fwdMsg->initHeader( id );
01398   msgPartText += "--";
01399   msgPartText += QString::fromLatin1( boundary );
01400   msgPartText += "--\n";
01401   QCString tmp;
01402   msgPart->setTypeStr( "MULTIPART" );
01403   tmp.sprintf( "Digest; boundary=\"%s\"", boundary.data() );
01404   msgPart->setSubtypeStr( tmp );
01405   msgPart->setName( "unnamed" );
01406   msgPart->setCte( DwMime::kCte7bit );   // does it have to be 7bit?
01407   msgPart->setContentDescription( QString( "Digest of %1 messages." ).arg( msgCnt ) );
01408   // THIS HAS TO BE AFTER setCte()!!!!
01409   msgPart->setBodyEncoded( QCString( msgPartText.ascii() ) );
01410   KCursorSaver busy( KBusyPtr::busy() );
01411   KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
01412   win->addAttach( msgPart );
01413   win->show();
01414   return OK;
01415 }
01416 
01417 KMRedirectCommand::KMRedirectCommand( QWidget *parent,
01418                                       KMMessage *msg )
01419   : KMCommand( parent, msg )
01420 {
01421 }
01422 
01423 KMCommand::Result KMRedirectCommand::execute()
01424 {
01425   KMMessage *msg = retrievedMessage();
01426   if ( !msg || !msg->codec() )
01427     return Failed;
01428 
01429   RedirectDialog dlg( parentWidget(), "redirect", true,
01430                       kmkernel->msgSender()->sendImmediate() );
01431   if (dlg.exec()==QDialog::Rejected) return Failed;
01432 
01433   KMMessage *newMsg = msg->createRedirect( dlg.to() );
01434   KMFilterAction::sendMDN( msg, KMime::MDN::Dispatched );
01435 
01436   const KMail::MessageSender::SendMethod method = dlg.sendImmediate()
01437     ? KMail::MessageSender::SendImmediate
01438     : KMail::MessageSender::SendLater;
01439   if ( !kmkernel->msgSender()->send( newMsg, method ) ) {
01440     kdDebug(5006) << "KMRedirectCommand: could not redirect message (sending failed)" << endl;
01441     return Failed; // error: couldn't send
01442   }
01443   return OK;
01444 }
01445 
01446 
01447 KMCustomReplyToCommand::KMCustomReplyToCommand( QWidget *parent, KMMessage *msg,
01448                                                 const QString &selection,
01449                                                 const QString &tmpl )
01450   : KMCommand( parent, msg ), mSelection( selection ), mTemplate( tmpl )
01451 {
01452 }
01453 
01454 KMCommand::Result KMCustomReplyToCommand::execute()
01455 {
01456   KCursorSaver busy(KBusyPtr::busy());
01457   KMMessage *msg = retrievedMessage();
01458   if ( !msg || !msg->codec() ) {
01459     return Failed;
01460   }
01461   KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection,
01462                                        false, true, false, mTemplate );
01463   KMail::Composer * win = KMail::makeComposer( reply );
01464   win->setCharset( msg->codec()->mimeName(), true );
01465   win->setReplyFocus();
01466   win->show();
01467 
01468   return OK;
01469 }
01470 
01471 
01472 KMCustomReplyAllToCommand::KMCustomReplyAllToCommand( QWidget *parent, KMMessage *msg,
01473                                                       const QString &selection,
01474                                                       const QString &tmpl )
01475   : KMCommand( parent, msg ), mSelection( selection ), mTemplate( tmpl )
01476 {
01477 }
01478 
01479 KMCommand::Result KMCustomReplyAllToCommand::execute()
01480 {
01481   KCursorSaver busy(KBusyPtr::busy());
01482   KMMessage *msg = retrievedMessage();
01483   if ( !msg || !msg->codec() ) {
01484     return Failed;
01485   }
01486   KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection,
01487                                        false, true, false, mTemplate );
01488   KMail::Composer * win = KMail::makeComposer( reply );
01489   win->setCharset( msg->codec()->mimeName(), true );
01490   win->setReplyFocus();
01491   win->show();
01492 
01493   return OK;
01494 }
01495 
01496 
01497 KMCustomForwardCommand::KMCustomForwardCommand( QWidget *parent,
01498   const QPtrList<KMMsgBase> &msgList, uint identity, const QString &tmpl )
01499   : KMCommand( parent, msgList ),
01500     mIdentity( identity ), mTemplate( tmpl )
01501 {
01502 }
01503 
01504 KMCustomForwardCommand::KMCustomForwardCommand( QWidget *parent,
01505   KMMessage *msg, uint identity, const QString &tmpl )
01506   : KMCommand( parent, msg ),
01507     mIdentity( identity ), mTemplate( tmpl )
01508 {
01509 }
01510 
01511 KMCommand::Result KMCustomForwardCommand::execute()
01512 {
01513   QPtrList<KMMessage> msgList = retrievedMsgs();
01514 
01515   if (msgList.count() >= 2) { // Multiple forward
01516 
01517     uint id = 0;
01518     QPtrList<KMMessage> linklist;
01519     for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
01520       // set the identity
01521       if (id == 0)
01522         id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
01523 
01524       // msgText += msg->createForwardBody();
01525       linklist.append( msg );
01526     }
01527     if ( id == 0 )
01528       id = mIdentity; // use folder identity if no message had an id set
01529     KMMessage *fwdMsg = new KMMessage;
01530     fwdMsg->initHeader( id );
01531     fwdMsg->setAutomaticFields( true );
01532     fwdMsg->setCharset( "utf-8" );
01533     // fwdMsg->setBody( msgText );
01534 
01535     for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) {
01536       TemplateParser parser( fwdMsg, TemplateParser::Forward,
01537         msg->body(), false, false, false, false);
01538         parser.process( msg, 0, true );
01539 
01540       fwdMsg->link( msg, KMMsgStatusForwarded );
01541     }
01542 
01543     KCursorSaver busy( KBusyPtr::busy() );
01544     KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
01545     win->setCharset("");
01546     win->show();
01547 
01548   } else { // forward a single message at most
01549 
01550     KMMessage *msg = msgList.getFirst();
01551     if ( !msg || !msg->codec() )
01552       return Failed;
01553 
01554     KCursorSaver busy( KBusyPtr::busy() );
01555     KMMessage *fwdMsg = msg->createForward( mTemplate );
01556 
01557     uint id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
01558     if ( id == 0 )
01559       id = mIdentity;
01560     {
01561       KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
01562       win->setCharset( fwdMsg->codec()->mimeName(), true );
01563       win->show();
01564     }
01565   }
01566   return OK;
01567 }
01568 
01569 
01570 KMPrintCommand::KMPrintCommand( QWidget *parent,
01571   KMMessage *msg, bool htmlOverride, bool htmlLoadExtOverride,
01572   bool useFixedFont, const QString & encoding )
01573   : KMCommand( parent, msg ), mHtmlOverride( htmlOverride ),
01574     mHtmlLoadExtOverride( htmlLoadExtOverride ),
01575     mUseFixedFont( useFixedFont ), mEncoding( encoding )
01576 {
01577   mOverrideFont = KGlobalSettings::generalFont();
01578 }
01579 
01580 
01581 void KMPrintCommand::setOverrideFont( const QFont& font )
01582 {
01583   mOverrideFont = font;
01584 }
01585 
01586 KMCommand::Result KMPrintCommand::execute()
01587 {
01588   KMReaderWin printWin( 0, 0, 0 );
01589   printWin.setPrinting( true );
01590   printWin.readConfig();
01591   printWin.setHtmlOverride( mHtmlOverride );
01592   printWin.setHtmlLoadExtOverride( mHtmlLoadExtOverride );
01593   printWin.setUseFixedFont( mUseFixedFont );
01594   printWin.setOverrideEncoding( mEncoding );
01595   printWin.setPrintFont( mOverrideFont );
01596   printWin.setDecryptMessageOverwrite( true );
01597   printWin.setMsg( retrievedMessage(), true );
01598   printWin.printMsg();
01599 
01600   return OK;
01601 }
01602 
01603 
01604 KMSetStatusCommand::KMSetStatusCommand( KMMsgStatus status,
01605   const QValueList<Q_UINT32> &serNums, bool toggle )
01606   : mStatus( status ), mSerNums( serNums ), mToggle( toggle )
01607 {
01608 }
01609 
01610 KMCommand::Result KMSetStatusCommand::execute()
01611 {
01612   QValueListIterator<Q_UINT32> it;
01613   int idx = -1;
01614   KMFolder *folder = 0;
01615   bool parentStatus = false;
01616 
01617   // Toggle actions on threads toggle the whole thread
01618   // depending on the state of the parent.
01619   if (mToggle) {
01620     KMMsgBase *msg;
01621     KMMsgDict::instance()->getLocation( *mSerNums.begin(), &folder, &idx );
01622     if (folder) {
01623       msg = folder->getMsgBase(idx);
01624       if (msg && (msg->status()&mStatus))
01625         parentStatus = true;
01626       else
01627         parentStatus = false;
01628     }
01629   }
01630   QMap< KMFolder*, QValueList<int> > folderMap;
01631   for ( it = mSerNums.begin(); it != mSerNums.end(); ++it ) {
01632     KMMsgDict::instance()->getLocation( *it, &folder, &idx );
01633     if (folder) {
01634       if (mToggle) {
01635         KMMsgBase *msg = folder->getMsgBase(idx);
01636         // check if we are already at the target toggle state
01637         if (msg) {
01638           bool myStatus;
01639           if (msg->status()&mStatus)
01640             myStatus = true;
01641           else
01642             myStatus = false;
01643           if (myStatus != parentStatus)
01644             continue;
01645         }
01646       }
01647       /* Collect the ids for each folder in a separate list and
01648          send them off in one go at the end. */
01649       folderMap[folder].append(idx);
01650     }
01651   }
01652   QMapIterator< KMFolder*, QValueList<int> > it2 = folderMap.begin();
01653   while ( it2 != folderMap.end() ) {
01654      KMFolder *f = it2.key();
01655      f->setStatus( (*it2), mStatus, mToggle );
01656      ++it2;
01657   }
01658   //kapp->dcopClient()->emitDCOPSignal( "unreadCountChanged()", QByteArray() );
01659 
01660   return OK;
01661 }
01662 
01663 
01664 KMFilterCommand::KMFilterCommand( const QCString &field, const QString &value )
01665   : mField( field ), mValue( value )
01666 {
01667 }
01668 
01669 KMCommand::Result KMFilterCommand::execute()
01670 {
01671   kmkernel->filterMgr()->createFilter( mField, mValue );
01672 
01673   return OK;
01674 }
01675 
01676 
01677 KMFilterActionCommand::KMFilterActionCommand( QWidget *parent,
01678                                               const QPtrList<KMMsgBase> &msgList,
01679                                               KMFilter *filter )
01680   : KMCommand( parent, msgList ), mFilter( filter  )
01681 {
01682   QPtrListIterator<KMMsgBase> it(msgList);
01683   while ( it.current() ) {
01684     serNumList.append( (*it)->getMsgSerNum() );
01685     ++it;
01686   }
01687 }
01688 
01689 KMCommand::Result KMFilterActionCommand::execute()
01690 {
01691   KCursorSaver busy( KBusyPtr::busy() );
01692 
01693   int msgCount = 0;
01694   int msgCountToFilter = serNumList.count();
01695   ProgressItem* progressItem =
01696     ProgressManager::createProgressItem ( "filter"+ProgressManager::getUniqueID(),
01697                                           i18n( "Filtering messages" ) );
01698   progressItem->setTotalItems( msgCountToFilter );
01699   QValueList<Q_UINT32>::const_iterator it;
01700   for ( it = serNumList.begin(); it != serNumList.end(); it++ ) {
01701     Q_UINT32 serNum = *it;
01702     int diff = msgCountToFilter - ++msgCount;
01703     if ( diff < 10 || !( msgCount % 20 ) || msgCount <= 10 ) {
01704       progressItem->updateProgress();
01705       QString statusMsg = i18n("Filtering message %1 of %2");
01706       statusMsg = statusMsg.arg( msgCount ).arg( msgCountToFilter );
01707       KPIM::BroadcastStatus::instance()->setStatusMsg( statusMsg );
01708       KApplication::kApplication()->eventLoop()->processEvents( QEventLoop::ExcludeUserInput, 50 );
01709     }
01710 
01711     int filterResult = kmkernel->filterMgr()->process( serNum, mFilter );
01712     if (filterResult == 2) {
01713       // something went horribly wrong (out of space?)
01714       perror("Critical error");
01715       kmkernel->emergencyExit( i18n("Not enough free disk space?" ));
01716     }
01717     progressItem->incCompletedItems();
01718   }
01719 
01720   progressItem->setComplete();
01721   progressItem = 0;
01722   return OK;
01723 }
01724 
01725 
01726 KMMetaFilterActionCommand::KMMetaFilterActionCommand( KMFilter *filter,
01727                                                       KMHeaders *headers,
01728                                                       KMMainWidget *main )
01729     : QObject( main ),
01730       mFilter( filter ), mHeaders( headers ), mMainWidget( main )
01731 {
01732 }
01733 
01734 void KMMetaFilterActionCommand::start()
01735 {
01736   if (ActionScheduler::isEnabled() ) {
01737     // use action scheduler
01738     KMFilterMgr::FilterSet set = KMFilterMgr::All;
01739     QValueList<KMFilter*> filters;
01740     filters.append( mFilter );
01741     ActionScheduler *scheduler = new ActionScheduler( set, filters, mHeaders );
01742     scheduler->setAlwaysMatch( true );
01743     scheduler->setAutoDestruct( true );
01744 
01745     int contentX, contentY;
01746     HeaderItem *nextItem = mHeaders->prepareMove( &contentX, &contentY );
01747     QPtrList<KMMsgBase> msgList = *mHeaders->selectedMsgs(true);
01748     mHeaders->finalizeMove( nextItem, contentX, contentY );
01749 
01750     for (KMMsgBase *msg = msgList.first(); msg; msg = msgList.next())
01751       scheduler->execFilters( msg );
01752   } else {
01753     KMCommand *filterCommand =
01754       new KMFilterActionCommand( mMainWidget,
01755                                  *mHeaders->selectedMsgs(), mFilter );
01756     filterCommand->start();
01757     int contentX, contentY;
01758     HeaderItem *item = mHeaders->prepareMove( &contentX, &contentY );
01759     mHeaders->finalizeMove( item, contentX, contentY );
01760   }
01761 }
01762 
01763 FolderShortcutCommand::FolderShortcutCommand( KMMainWidget *mainwidget,
01764                                               KMFolder *folder )
01765     : mMainWidget( mainwidget ), mFolder( folder ), mAction( 0 )
01766 {
01767 }
01768 
01769 
01770 FolderShortcutCommand::~FolderShortcutCommand()
01771 {
01772   if ( mAction ) mAction->unplugAll();
01773   delete mAction;
01774 }
01775 
01776 void FolderShortcutCommand::start()
01777 {
01778   mMainWidget->slotSelectFolder( mFolder );
01779 }
01780 
01781 void FolderShortcutCommand::setAction( KAction* action )
01782 {
01783   mAction = action;
01784 }
01785 
01786 KMMailingListFilterCommand::KMMailingListFilterCommand( QWidget *parent,
01787                                                         KMMessage *msg )
01788   : KMCommand( parent, msg )
01789 {
01790 }
01791 
01792 KMCommand::Result KMMailingListFilterCommand::execute()
01793 {
01794   QCString name;
01795   QString value;
01796   KMMessage *msg = retrievedMessage();
01797   if (!msg)
01798     return Failed;
01799 
01800   if ( !MailingList::name( msg, name, value ).isEmpty() ) {
01801     kmkernel->filterMgr()->createFilter( name, value );
01802     return OK;
01803   }
01804   else
01805     return Failed;
01806 }
01807 
01808 
01809 void KMMenuCommand::folderToPopupMenu(bool move,
01810   QObject *receiver, KMMenuToFolder *aMenuToFolder, QPopupMenu *menu )
01811 {
01812   while ( menu->count() )
01813   {
01814     QPopupMenu *popup = menu->findItem( menu->idAt( 0 ) )->popup();
01815     if (popup)
01816       delete popup;
01817     else
01818       menu->removeItemAt( 0 );
01819   }
01820 
01821   if (!kmkernel->imapFolderMgr()->dir().first() &&
01822       !kmkernel->dimapFolderMgr()->dir().first())
01823   { // only local folders
01824     makeFolderMenu( &kmkernel->folderMgr()->dir(), move,
01825                     receiver, aMenuToFolder, menu );
01826   } else {
01827     // operate on top-level items
01828     QPopupMenu* subMenu = new QPopupMenu(menu);
01829     makeFolderMenu( &kmkernel->folderMgr()->dir(),
01830                     move, receiver, aMenuToFolder, subMenu );
01831     menu->insertItem( i18n( "Local Folders" ), subMenu );
01832     KMFolderDir* fdir = &kmkernel->imapFolderMgr()->dir();
01833     for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) {
01834       if (node->isDir())
01835         continue;
01836       subMenu = new QPopupMenu(menu);
01837       makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu );
01838       menu->insertItem( node->label(), subMenu );
01839     }
01840     fdir = &kmkernel->dimapFolderMgr()->dir();
01841     for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) {
01842       if (node->isDir())
01843         continue;
01844       subMenu = new QPopupMenu(menu);
01845       makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu );
01846       menu->insertItem( node->label(), subMenu );
01847     }
01848   }
01849 }
01850 
01851 void KMMenuCommand::makeFolderMenu(KMFolderNode* node, bool move,
01852   QObject *receiver, KMMenuToFolder *aMenuToFolder, QPopupMenu *menu )
01853 {
01854   // connect the signals
01855   if (move)
01856   {
01857     disconnect(menu, SIGNAL(activated(int)), receiver,
01858            SLOT(moveSelectedToFolder(int)));
01859     connect(menu, SIGNAL(activated(int)), receiver,
01860              SLOT(moveSelectedToFolder(int)));
01861   } else {
01862     disconnect(menu, SIGNAL(activated(int)), receiver,
01863            SLOT(copySelectedToFolder(int)));
01864     connect(menu, SIGNAL(activated(int)), receiver,
01865              SLOT(copySelectedToFolder(int)));
01866   }
01867 
01868   KMFolder *folder = 0;
01869   KMFolderDir *folderDir = 0;
01870   if (node->isDir()) {
01871     folderDir = static_cast<KMFolderDir*>(node);
01872   } else {
01873     folder = static_cast<KMFolder*>(node);
01874     folderDir = folder->child();
01875   }
01876 
01877   if (folder && !folder->noContent())
01878   {
01879     int menuId;
01880     if (move)
01881       menuId = menu->insertItem(i18n("Move to This Folder"));
01882     else
01883       menuId = menu->insertItem(i18n("Copy to This Folder"));
01884     aMenuToFolder->insert( menuId, folder );
01885     menu->setItemEnabled( menuId, !folder->isReadOnly() );
01886     menu->insertSeparator();
01887   }
01888 
01889   if (!folderDir)
01890     return;
01891 
01892   for (KMFolderNode *it = folderDir->first(); it; it = folderDir->next() ) {
01893     if (it->isDir())
01894       continue;
01895     KMFolder *child = static_cast<KMFolder*>(it);
01896     QString label = child->label();
01897     label.replace("&","&&");
01898     if (child->child() && child->child()->first()) {
01899       // descend
01900       QPopupMenu *subMenu = new QPopupMenu(menu, "subMenu");
01901       makeFolderMenu( child, move, receiver,
01902                       aMenuToFolder, subMenu );
01903       menu->insertItem( label, subMenu );
01904     } else {
01905       // insert an item
01906       int menuId = menu->insertItem( label );
01907       aMenuToFolder->insert( menuId, child );
01908       menu->setItemEnabled( menuId, !child->isReadOnly() );
01909     }
01910   }
01911   return;
01912 }
01913 
01914 
01915 KMCopyCommand::KMCopyCommand( KMFolder* destFolder,
01916                               const QPtrList<KMMsgBase> &msgList )
01917 :mDestFolder( destFolder ), mMsgList( msgList )
01918 {
01919   setDeletesItself( true );
01920 }
01921 
01922 KMCopyCommand::KMCopyCommand( KMFolder* destFolder, KMMessage * msg )
01923   :mDestFolder( destFolder )
01924 {
01925   setDeletesItself( true );
01926   mMsgList.append( &msg->toMsgBase() );
01927 }
01928 
01929 KMCommand::Result KMCopyCommand::execute()
01930 {
01931   KMMsgBase *msgBase;
01932   KMMessage *msg, *newMsg;
01933   int idx = -1;
01934   bool isMessage;
01935   QPtrList<KMMessage> list;
01936   QPtrList<KMMessage> localList;
01937 
01938   if (mDestFolder && mDestFolder->open("kmcommand") != 0)
01939   {
01940     deleteLater();
01941     return Failed;
01942   }
01943 
01944   setEmitsCompletedItself( true );
01945   KCursorSaver busy(KBusyPtr::busy());
01946 
01947   for (msgBase = mMsgList.first(); msgBase; msgBase = mMsgList.next() )
01948   {
01949     KMFolder *srcFolder = msgBase->parent();
01950     if (( isMessage = msgBase->isMessage() ))
01951     {
01952       msg = static_cast<KMMessage*>(msgBase);
01953     } else {
01954       idx = srcFolder->find(msgBase);
01955       assert(idx != -1);
01956       msg = srcFolder->getMsg(idx);
01957       // corrupt IMAP cache, see FolderStorage::getMsg()
01958       if ( msg == 0 ) {
01959         KMessageBox::error( parentWidget(), i18n("Corrupt IMAP cache detected in folder %1. "
01960             "Copying of messages aborted.").arg( srcFolder->prettyURL() ) );
01961         deleteLater();
01962         return Failed;
01963       }
01964     }
01965 
01966     if (srcFolder && mDestFolder &&
01967         (srcFolder->folderType()== KMFolderTypeImap) &&
01968         (mDestFolder->folderType() == KMFolderTypeImap) &&
01969         (static_cast<KMFolderImap*>(srcFolder->storage())->account() ==
01970          static_cast<KMFolderImap*>(mDestFolder->storage())->account()))
01971     {
01972       // imap => imap with same account
01973       list.append(msg);
01974     } else {
01975       newMsg = new KMMessage( new DwMessage( *msg->asDwMessage() ) );
01976       newMsg->setComplete(msg->isComplete());
01977       // make sure the attachment state is only calculated when it's complete
01978       if (!newMsg->isComplete())
01979         newMsg->setReadyToShow(false);
01980       newMsg->setStatus(msg->status());
01981 
01982       if (srcFolder && !newMsg->isComplete())
01983       {
01984         // imap => others
01985         newMsg->setParent(msg->parent());
01986         FolderJob *job = srcFolder->createJob(newMsg);
01987         job->setCancellable( false );
01988         mPendingJobs << job;
01989         connect(job, SIGNAL(messageRetrieved(KMMessage*)),
01990                 mDestFolder, SLOT(reallyAddCopyOfMsg(KMMessage*)));
01991         connect( job, SIGNAL(result(KMail::FolderJob*)),
01992                  this, SLOT(slotJobFinished(KMail::FolderJob*)) );
01993         job->start();
01994       } else {
01995         // local => others
01996         localList.append(newMsg);
01997       }
01998     }
01999 
02000     if (srcFolder && !isMessage && list.isEmpty())
02001     {
02002       assert(idx != -1);
02003       srcFolder->unGetMsg( idx );
02004     }
02005 
02006   } // end for
02007 
02008   bool deleteNow = false;
02009   if (!localList.isEmpty())
02010   {
02011     QValueList<int> index;
02012     mDestFolder->addMsg( localList, index );
02013     for ( QValueListIterator<int> it = index.begin(); it != index.end(); ++it ) {
02014       mDestFolder->unGetMsg( *it );
02015     }
02016     if ( mDestFolder->folderType() == KMFolderTypeImap ) {
02017       if ( mPendingJobs.isEmpty() ) {
02018         // wait for the end of the copy before closing the folder
02019         KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
02020         connect( imapDestFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
02021             this, SLOT( slotFolderComplete( KMFolderImap*, bool ) ) );
02022       }
02023     } else {
02024       deleteNow = list.isEmpty() && mPendingJobs.isEmpty(); // we're done if there are no other mails we need to fetch
02025     }
02026   }
02027 
02028 //TODO: Get rid of the other cases just use this one for all types of folder
02029 //TODO: requires adding copyMsg and getFolder methods to KMFolder.h
02030   if (!list.isEmpty())
02031   {
02032     // copy the message(s); note: the list is empty afterwards!
02033     KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
02034     connect( imapDestFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
02035         this, SLOT( slotFolderComplete( KMFolderImap*, bool ) ) );
02036     imapDestFolder->copyMsg(list);
02037     imapDestFolder->getFolder();
02038   }
02039 
02040   // only close the folder and delete the job if we're done
02041   // otherwise this is done in slotMsgAdded or slotFolderComplete
02042   if ( deleteNow )
02043   {
02044     mDestFolder->close("kmcommand");
02045     setResult( OK );
02046     emit completed( this );
02047     deleteLater();
02048   }
02049 
02050   return OK;
02051 }
02052 
02053 void KMCopyCommand::slotJobFinished(KMail::FolderJob * job)
02054 {
02055   mPendingJobs.remove( job );
02056   if ( job->error() ) {
02057     kdDebug(5006) << k_funcinfo << "folder job failed: " << job->error() << endl;
02058     // kill all pending jobs
02059     for ( QValueList<KMail::FolderJob*>::Iterator it = mPendingJobs.begin(); it != mPendingJobs.end(); ++it ) {
02060       disconnect( (*it), SIGNAL(result(KMail::FolderJob*)),
02061                   this, SLOT(slotJobFinished(KMail::FolderJob*)) );
02062       (*it)->kill();
02063     }
02064     mPendingJobs.clear();
02065     setResult( Failed );
02066   }
02067 
02068   if ( mPendingJobs.isEmpty() )
02069   {
02070     mDestFolder->close("kmcommand");
02071     emit completed( this );
02072     deleteLater();
02073   }
02074 }
02075 
02076 void KMCopyCommand::slotFolderComplete( KMFolderImap*, bool success )
02077 {
02078   kdDebug(5006) << k_funcinfo << success << endl;
02079   if ( !success )
02080     setResult( Failed );
02081   mDestFolder->close( "kmcommand" );
02082   emit completed( this );
02083   deleteLater();
02084 }
02085 
02086 
02087 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
02088                               const QPtrList<KMMsgBase> &msgList)
02089   : mDestFolder( destFolder ), mProgressItem( 0 )
02090 {
02091   QPtrList<KMMsgBase> tmp = msgList;
02092   for ( KMMsgBase *msgBase = tmp.first(); msgBase; msgBase = tmp.next() )
02093     mSerNumList.append( msgBase->getMsgSerNum() );
02094 }
02095 
02096 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
02097                               KMMessage *msg )
02098   : mDestFolder( destFolder ), mProgressItem( 0 )
02099 {
02100   mSerNumList.append( msg->getMsgSerNum() );
02101 }
02102 
02103 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
02104                               KMMsgBase *msgBase )
02105   : mDestFolder( destFolder ), mProgressItem( 0 )
02106 {
02107   mSerNumList.append( msgBase->getMsgSerNum() );
02108 }
02109 
02110 KMMoveCommand::KMMoveCommand( Q_UINT32 )
02111   : mProgressItem( 0 )
02112 {
02113 }
02114 
02115 KMCommand::Result KMMoveCommand::execute()
02116 {
02117   setEmitsCompletedItself( true );
02118   setDeletesItself( true );
02119   typedef QMap< KMFolder*, QPtrList<KMMessage>* > FolderToMessageListMap;
02120   FolderToMessageListMap folderDeleteList;
02121 
02122   if (mDestFolder && mDestFolder->open("kmcommand") != 0) {
02123     completeMove( Failed );
02124     return Failed;
02125   }
02126   KCursorSaver busy(KBusyPtr::busy());
02127 
02128   // TODO set SSL state according to source and destfolder connection?
02129   Q_ASSERT( !mProgressItem );
02130   mProgressItem =
02131      ProgressManager::createProgressItem (
02132          "move"+ProgressManager::getUniqueID(),
02133          mDestFolder ? i18n( "Moving messages" ) : i18n( "Deleting messages" ) );
02134   connect( mProgressItem, SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
02135            this, SLOT( slotMoveCanceled() ) );
02136 
02137   KMMessage *msg;
02138   int rc = 0;
02139   int index;
02140   QPtrList<KMMessage> list;
02141   int undoId = -1;
02142   mCompleteWithAddedMsg = false;
02143 
02144   if (mDestFolder) {
02145     connect (mDestFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
02146              this, SLOT(slotMsgAddedToDestFolder(KMFolder*, Q_UINT32)));
02147     mLostBoys = mSerNumList;
02148   }
02149   mProgressItem->setTotalItems( mSerNumList.count() );
02150 
02151   for ( QValueList<Q_UINT32>::ConstIterator it = mSerNumList.constBegin(); it != mSerNumList.constEnd(); ++it ) {
02152     KMFolder *srcFolder;
02153     int idx = -1;
02154     KMMsgDict::instance()->getLocation( *it, &srcFolder, &idx );
02155     if (srcFolder == mDestFolder)
02156       continue;
02157     assert(idx != -1);
02158     srcFolder->open( "kmmovecommand" );
02159     mOpenedFolders.append( srcFolder );
02160     msg = srcFolder->getMsg(idx);
02161     if ( !msg ) {
02162       kdDebug(5006) << k_funcinfo << "No message found for serial number " << *it << endl;
02163       continue;
02164     }
02165     bool undo = msg->enableUndo();
02166 
02167     if ( msg && msg->transferInProgress() &&
02168          srcFolder->folderType() == KMFolderTypeImap )
02169     {
02170       // cancel the download
02171       msg->setTransferInProgress( false, true );
02172       static_cast<KMFolderImap*>(srcFolder->storage())->ignoreJobsForMessage( msg );
02173     }
02174 
02175     if (mDestFolder) {
02176       if (mDestFolder->folderType() == KMFolderTypeImap) {
02177         /* If we are moving to an imap folder, connect to it's completed
02178          * signal so we notice when all the mails should have showed up in it
02179          * but haven't for some reason. */
02180         KMFolderImap *imapFolder = static_cast<KMFolderImap*> ( mDestFolder->storage() );
02181         disconnect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )),
02182                  this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
02183 
02184         connect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )),
02185                  this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
02186         list.append(msg);
02187       } else {
02188         // We are moving to a local folder.
02189         if ( srcFolder->folderType() == KMFolderTypeImap )
02190         {
02191           // do not complete here but wait until all messages are transferred
02192           mCompleteWithAddedMsg = true;
02193         }
02194         rc = mDestFolder->moveMsg(msg, &index);
02195         if (rc == 0 && index != -1) {
02196           KMMsgBase *mb = mDestFolder->unGetMsg( mDestFolder->count() - 1 );
02197           if (undo && mb)
02198           {
02199             if ( undoId == -1 )
02200               undoId = kmkernel->undoStack()->newUndoAction( srcFolder, mDestFolder );
02201             kmkernel->undoStack()->addMsgToAction( undoId, mb->getMsgSerNum() );
02202           }
02203         } else if (rc != 0) {
02204           // Something  went wrong. Stop processing here, it is likely that the
02205           // other moves would fail as well.
02206           completeMove( Failed );
02207           return Failed;
02208         }
02209       }
02210     } else {
02211       // really delete messages that are already in the trash folder or if
02212       // we are really, really deleting, not just moving to trash
02213       if (srcFolder->folderType() == KMFolderTypeImap) {
02214         if (!folderDeleteList[srcFolder])
02215           folderDeleteList[srcFolder] = new QPtrList<KMMessage>;
02216         folderDeleteList[srcFolder]->append( msg );
02217       } else {
02218         srcFolder->removeMsg(idx);
02219         delete msg;
02220       }
02221     }
02222   }
02223   if (!list.isEmpty() && mDestFolder) {
02224     // will be completed with folderComplete signal
02225     mDestFolder->moveMsg(list, &index);
02226   } else {
02227     FolderToMessageListMap::Iterator it;
02228     for ( it = folderDeleteList.begin(); it != folderDeleteList.end(); ++it ) {
02229       it.key()->removeMsg(*it.data());
02230       delete it.data();
02231     }
02232     if ( !mCompleteWithAddedMsg ) {
02233       // imap folders will be completed in slotMsgAddedToDestFolder
02234       completeMove( OK );
02235     }
02236   }
02237 
02238   return OK;
02239 }
02240 
02241 void KMMoveCommand::slotImapFolderCompleted(KMFolderImap* imapFolder, bool success)
02242 {
02243   disconnect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )),
02244       this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
02245   if ( success ) {
02246     // the folder was checked successfully but we were still called, so check
02247     // if we are still waiting for messages to show up. If so, uidValidity
02248     // changed, or something else went wrong. Clean up.
02249 
02250     /* Unfortunately older UW imap servers change uid validity for each put job.
02251      * Yes, it is really that broken. *sigh* So we cannot report error here, I guess. */
02252     if ( !mLostBoys.isEmpty() ) {
02253       kdDebug(5006) <<  "### Not all moved messages reported back that they were " << endl
02254                     <<  "### added to the target folder. Did uidValidity change? " << endl;
02255     }
02256     completeMove( OK );
02257   } else {
02258     // Should we inform the user here or leave that to the caller?
02259     completeMove( Failed );
02260   }
02261 }
02262 
02263 void KMMoveCommand::slotMsgAddedToDestFolder(KMFolder *folder, Q_UINT32 serNum)
02264 {
02265   if ( folder != mDestFolder || mLostBoys.find( serNum ) == mLostBoys.end() ) {
02266     //kdDebug(5006) << "KMMoveCommand::msgAddedToDestFolder different "
02267     //                 "folder or invalid serial number." << endl;
02268     return;
02269   }
02270   mLostBoys.remove(serNum);
02271   if ( mLostBoys.isEmpty() ) {
02272     // we are done. All messages transferred to the host succesfully
02273     disconnect (mDestFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
02274              this, SLOT(slotMsgAddedToDestFolder(KMFolder*, Q_UINT32)));
02275     if (mDestFolder && mDestFolder->folderType() != KMFolderTypeImap) {
02276       mDestFolder->sync();
02277     }
02278     if ( mCompleteWithAddedMsg ) {
02279       completeMove( OK );
02280     }
02281   } else {
02282     if ( mProgressItem ) {
02283       mProgressItem->incCompletedItems();
02284       mProgressItem->updateProgress();
02285     }
02286   }
02287 }
02288 
02289 void KMMoveCommand::completeMove( Result result )
02290 {
02291   if ( mDestFolder )
02292     mDestFolder->close("kmcommand");
02293   while ( !mOpenedFolders.empty() ) {
02294     KMFolder *folder = mOpenedFolders.back();
02295     mOpenedFolders.pop_back();
02296     folder->close("kmcommand");
02297   }
02298   if ( mProgressItem ) {
02299     mProgressItem->setComplete();
02300     mProgressItem = 0;
02301   }
02302   setResult( result );
02303   emit completed( this );
02304   deleteLater();
02305 }
02306 
02307 void KMMoveCommand::slotMoveCanceled()
02308 {
02309   completeMove( Canceled );
02310 }
02311 
02312 // srcFolder doesn't make much sense for searchFolders
02313 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder,
02314   const QPtrList<KMMsgBase> &msgList )
02315 :KMMoveCommand( findTrashFolder( srcFolder ), msgList)
02316 {
02317   srcFolder->open("kmcommand");
02318   mOpenedFolders.push_back( srcFolder );
02319 }
02320 
02321 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder, KMMessage * msg )
02322 :KMMoveCommand( findTrashFolder( srcFolder ), msg)
02323 {
02324   srcFolder->open("kmcommand");
02325   mOpenedFolders.push_back( srcFolder );
02326 }
02327 
02328 KMDeleteMsgCommand::KMDeleteMsgCommand( Q_UINT32 sernum )
02329 :KMMoveCommand( sernum )
02330 {
02331   KMFolder *srcFolder = 0;
02332   int idx;
02333   KMMsgDict::instance()->getLocation( sernum, &srcFolder, &idx );
02334   if ( srcFolder ) {
02335     KMMsgBase *msg = srcFolder->getMsgBase( idx );
02336     srcFolder->open("kmcommand");
02337     mOpenedFolders.push_back( srcFolder );
02338     addMsg( msg );
02339   }
02340   setDestFolder( findTrashFolder( srcFolder ) );
02341 }
02342 
02343 KMFolder * KMDeleteMsgCommand::findTrashFolder( KMFolder * folder )
02344 {
02345   KMFolder* trash = folder->trashFolder();
02346   if( !trash )
02347     trash = kmkernel->trashFolder();
02348   if( trash != folder )
02349     return trash;
02350   return 0;
02351 }
02352 
02353 
02354 KMUrlClickedCommand::KMUrlClickedCommand( const KURL &url, uint identity,
02355   KMReaderWin *readerWin, bool htmlPref, KMMainWidget *mainWidget )
02356   :mUrl( url ), mIdentity( identity ), mReaderWin( readerWin ),
02357    mHtmlPref( htmlPref ), mMainWidget( mainWidget )
02358 {
02359 }
02360 
02361 KMCommand::Result KMUrlClickedCommand::execute()
02362 {
02363   KMMessage* msg;
02364 
02365   if (mUrl.protocol() == "mailto")
02366   {
02367     msg = new KMMessage;
02368     msg->initHeader(mIdentity);
02369     msg->setCharset("utf-8");
02370     msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
02371     QString query=mUrl.query();
02372     while (!query.isEmpty()) {
02373       QString queryPart;
02374       int secondQuery = query.find('?',1);
02375       if (secondQuery != -1)
02376         queryPart = query.left(secondQuery);
02377       else
02378         queryPart = query;
02379       query = query.mid(queryPart.length());
02380 
02381       if (queryPart.left(9) == "?subject=")
02382         msg->setSubject( KURL::decode_string(queryPart.mid(9)) );
02383       else if (queryPart.left(6) == "?body=")
02384         // It is correct to convert to latin1() as URL should not contain
02385         // anything except ascii.
02386         msg->setBody( KURL::decode_string(queryPart.mid(6)).latin1() );
02387       else if (queryPart.left(4) == "?cc=")
02388         msg->setCc( KURL::decode_string(queryPart.mid(4)) );
02389     }
02390 
02391     KMail::Composer * win = KMail::makeComposer( msg, mIdentity );
02392     win->setCharset("", true);
02393     win->show();
02394   }
02395   else if ( mUrl.protocol() == "im" )
02396   {
02397     kmkernel->imProxy()->chatWithContact( mUrl.path() );
02398   }
02399   else if ((mUrl.protocol() == "http") || (mUrl.protocol() == "https") ||
02400            (mUrl.protocol() == "ftp")  || (mUrl.protocol() == "file")  ||
02401            (mUrl.protocol() == "ftps") || (mUrl.protocol() == "sftp" ) ||
02402            (mUrl.protocol() == "help") || (mUrl.protocol() == "vnc")   ||
02403            (mUrl.protocol() == "smb")  || (mUrl.protocol() == "fish")  ||
02404            (mUrl.protocol() == "news"))
02405   {
02406     KPIM::BroadcastStatus::instance()->setStatusMsg( i18n("Opening URL..."));
02407     KMimeType::Ptr mime = KMimeType::findByURL( mUrl );
02408     if (mime->name() == "application/x-desktop" ||
02409         mime->name() == "application/x-executable" ||
02410         mime->name() == "application/x-msdos-program" ||
02411         mime->name() == "application/x-shellscript" )
02412     {
02413       if (KMessageBox::warningYesNo( 0, i18n( "<qt>Do you really want to execute <b>%1</b>?</qt>" )
02414         .arg( mUrl.prettyURL() ), QString::null, i18n("Execute"), KStdGuiItem::cancel() ) != KMessageBox::Yes)
02415         return Canceled;
02416     }
02417     KRun * runner = new KRun( mUrl );
02418     runner->setRunExecutables( false );
02419   }
02420   else
02421     return Failed;
02422 
02423   return OK;
02424 }
02425 
02426 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, KMMessage *msg )
02427   : KMCommand( parent, msg ), mImplicitAttachments( true ), mEncoded( false )
02428 {
02429 }
02430 
02431 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, const QPtrList<KMMsgBase>& msgs )
02432   : KMCommand( parent, msgs ), mImplicitAttachments( true ), mEncoded( false )
02433 {
02434 }
02435 
02436 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, QPtrList<partNode>& attachments,
02437                                                     KMMessage *msg, bool encoded )
02438   : KMCommand( parent ), mImplicitAttachments( false ), mEncoded( encoded )
02439 {
02440   for ( QPtrListIterator<partNode> it( attachments ); it.current(); ++it ) {
02441     mAttachmentMap.insert( it.current(), msg );
02442   }
02443 }
02444 
02445 KMCommand::Result KMSaveAttachmentsCommand::execute()
02446 {
02447   setEmitsCompletedItself( true );
02448   if ( mImplicitAttachments ) {
02449     QPtrList<KMMessage> msgList = retrievedMsgs();
02450     KMMessage *msg;
02451     for ( QPtrListIterator<KMMessage> itr( msgList );
02452           ( msg = itr.current() );
02453           ++itr ) {
02454       partNode *rootNode = partNode::fromMessage( msg );
02455       for ( partNode *child = rootNode; child;
02456             child = child->firstChild() ) {
02457         for ( partNode *node = child; node; node = node->nextSibling() ) {
02458           if ( node->type() != DwMime::kTypeMultipart )
02459             mAttachmentMap.insert( node, msg );
02460         }
02461       }
02462     }
02463   }
02464   setDeletesItself( true );
02465   // load all parts
02466   KMLoadPartsCommand *command = new KMLoadPartsCommand( mAttachmentMap );
02467   connect( command, SIGNAL( partsRetrieved() ),
02468            this, SLOT( slotSaveAll() ) );
02469   command->start();
02470 
02471   return OK;
02472 }
02473 
02474 void KMSaveAttachmentsCommand::slotSaveAll()
02475 {
02476   // now that all message parts have been retrieved, remove all parts which
02477   // don't represent an attachment if they were not explicitely passed in the
02478   // c'tor
02479   if ( mImplicitAttachments ) {
02480     for ( PartNodeMessageMap::iterator it = mAttachmentMap.begin();
02481           it != mAttachmentMap.end(); ) {
02482       // only body parts which have a filename or a name parameter (except for
02483       // the root node for which name is set to the message's subject) are
02484       // considered attachments
02485       if ( it.key()->msgPart().fileName().stripWhiteSpace().isEmpty() &&
02486            ( it.key()->msgPart().name().stripWhiteSpace().isEmpty() ||
02487              !it.key()->parentNode() ) ) {
02488         PartNodeMessageMap::iterator delIt = it;
02489         ++it;
02490         mAttachmentMap.remove( delIt );
02491       }
02492       else
02493         ++it;
02494     }
02495     if ( mAttachmentMap.isEmpty() ) {
02496       KMessageBox::information( 0, i18n("Found no attachments to save.") );
02497       setResult( OK ); // The user has already been informed.
02498       emit completed( this );
02499       deleteLater();
02500       return;
02501     }
02502   }
02503 
02504   KURL url, dirUrl;
02505   if ( mAttachmentMap.count() > 1 ) {
02506     // get the dir
02507     dirUrl = KDirSelectDialog::selectDirectory( QString::null, false,
02508                                                 parentWidget(),
02509                                                 i18n("Save Attachments To") );
02510     if ( !dirUrl.isValid() ) {
02511       setResult( Canceled );
02512       emit completed( this );
02513       deleteLater();
02514       return;
02515     }
02516 
02517     // we may not get a slash-terminated url out of KDirSelectDialog
02518     dirUrl.adjustPath( 1 );
02519   }
02520   else {
02521     // only one item, get the desired filename
02522     partNode *node = mAttachmentMap.begin().key();
02523     // replace all ':' with '_' because ':' isn't allowed on FAT volumes
02524     QString s =
02525       node->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
02526     if ( s.isEmpty() )
02527       s = node->msgPart().name().stripWhiteSpace().replace( ':', '_' );
02528     if ( s.isEmpty() )
02529       s = i18n("filename for an unnamed attachment", "attachment.1");
02530     url = KFileDialog::getSaveURL( s, QString::null, parentWidget(),
02531                                    QString::null );
02532     if ( url.isEmpty() ) {
02533       setResult( Canceled );
02534       emit completed( this );
02535       deleteLater();
02536       return;
02537     }
02538   }
02539 
02540   QMap< QString, int > renameNumbering;
02541 
02542   Result globalResult = OK;
02543   int unnamedAtmCount = 0;
02544   for ( PartNodeMessageMap::const_iterator it = mAttachmentMap.begin();
02545         it != mAttachmentMap.end();
02546         ++it ) {
02547     KURL curUrl;
02548     if ( !dirUrl.isEmpty() ) {
02549       curUrl = dirUrl;
02550       QString s =
02551         it.key()->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
02552       if ( s.isEmpty() )
02553         s = it.key()->msgPart().name().stripWhiteSpace().replace( ':', '_' );
02554       if ( s.isEmpty() ) {
02555         ++unnamedAtmCount;
02556         s = i18n("filename for the %1-th unnamed attachment",
02557                  "attachment.%1")
02558             .arg( unnamedAtmCount );
02559       }
02560       curUrl.setFileName( s );
02561     } else {
02562       curUrl = url;
02563     }
02564 
02565     if ( !curUrl.isEmpty() ) {
02566 
02567      // Rename the file if we have already saved one with the same name:
02568      // try appending a number before extension (e.g. "pic.jpg" => "pic_2.jpg")
02569      QString origFile = curUrl.fileName();
02570      QString file = origFile;
02571 
02572      while ( renameNumbering.contains(file) ) {
02573        file = origFile;
02574        int num = renameNumbering[file] + 1;
02575        int dotIdx = file.findRev('.');
02576        file = file.insert( (dotIdx>=0) ? dotIdx : file.length(), QString("_") + QString::number(num) );
02577      }
02578      curUrl.setFileName(file);
02579 
02580      // Increment the counter for both the old and the new filename
02581      if ( !renameNumbering.contains(origFile))
02582          renameNumbering[origFile] = 1;
02583      else
02584          renameNumbering[origFile]++;
02585 
02586      if ( file != origFile ) {
02587         if ( !renameNumbering.contains(file))
02588             renameNumbering[file] = 1;
02589         else
02590             renameNumbering[file]++;
02591      }
02592 
02593 
02594       if ( KIO::NetAccess::exists( curUrl, false, parentWidget() ) ) {
02595         if ( KMessageBox::warningContinueCancel( parentWidget(),
02596               i18n( "A file named %1 already exists. Do you want to overwrite it?" )
02597               .arg( curUrl.fileName() ),
02598               i18n( "File Already Exists" ), i18n("&Overwrite") ) == KMessageBox::Cancel) {
02599           continue;
02600         }
02601       }
02602       // save
02603       const Result result = saveItem( it.key(), curUrl );
02604       if ( result != OK )
02605         globalResult = result;
02606     }
02607   }
02608   setResult( globalResult );
02609   emit completed( this );
02610   deleteLater();
02611 }
02612 
02613 KMCommand::Result KMSaveAttachmentsCommand::saveItem( partNode *node,
02614                                                       const KURL& url )
02615 {
02616   bool bSaveEncrypted = false;
02617   bool bEncryptedParts = node->encryptionState() != KMMsgNotEncrypted;
02618   if( bEncryptedParts )
02619     if( KMessageBox::questionYesNo( parentWidget(),
02620           i18n( "The part %1 of the message is encrypted. Do you want to keep the encryption when saving?" ).
02621           arg( url.fileName() ),
02622           i18n( "KMail Question" ), i18n("Keep Encryption"), i18n("Do Not Keep") ) ==
02623         KMessageBox::Yes )
02624       bSaveEncrypted = true;
02625 
02626   bool bSaveWithSig = true;
02627   if( node->signatureState() != KMMsgNotSigned )
02628     if( KMessageBox::questionYesNo( parentWidget(),
02629           i18n( "The part %1 of the message is signed. Do you want to keep the signature when saving?" ).
02630           arg( url.fileName() ),
02631           i18n( "KMail Question" ), i18n("Keep Signature"), i18n("Do Not Keep") ) !=
02632         KMessageBox::Yes )
02633       bSaveWithSig = false;
02634 
02635   QByteArray data;
02636   if ( mEncoded )
02637   {
02638     // This does not decode the Message Content-Transfer-Encoding
02639     // but saves the _original_ content of the message part
02640     data = KMail::Util::ByteArray( node->msgPart().dwBody() );
02641   }
02642   else
02643   {
02644     if( bSaveEncrypted || !bEncryptedParts) {
02645       partNode *dataNode = node;
02646       QCString rawReplyString;
02647       bool gotRawReplyString = false;
02648       if( !bSaveWithSig ) {
02649         if( DwMime::kTypeMultipart == node->type() &&
02650             DwMime::kSubtypeSigned == node->subType() ){
02651           // carefully look for the part that is *not* the signature part:
02652           if( node->findType( DwMime::kTypeApplication,
02653                 DwMime::kSubtypePgpSignature,
02654                 true, false ) ){
02655             dataNode = node->findTypeNot( DwMime::kTypeApplication,
02656                 DwMime::kSubtypePgpSignature,
02657                 true, false );
02658           }else if( node->findType( DwMime::kTypeApplication,
02659                 DwMime::kSubtypePkcs7Mime,
02660                 true, false ) ){
02661             dataNode = node->findTypeNot( DwMime::kTypeApplication,
02662                 DwMime::kSubtypePkcs7Mime,
02663                 true, false );
02664           }else{
02665             dataNode = node->findTypeNot( DwMime::kTypeMultipart,
02666                 DwMime::kSubtypeUnknown,
02667                 true, false );
02668           }
02669     }else{
02670       ObjectTreeParser otp( 0, 0, false, false, false );
02671 
02672       // process this node and all it's siblings and descendants
02673       dataNode->setProcessed( false, true );
02674       otp.parseObjectTree( dataNode );
02675 
02676       rawReplyString = otp.rawReplyString();
02677       gotRawReplyString = true;
02678         }
02679       }
02680       QByteArray cstr = gotRawReplyString
02681                          ? rawReplyString
02682                          : dataNode->msgPart().bodyDecodedBinary();
02683       data = cstr;
02684       size_t size = cstr.size();
02685       if ( dataNode->msgPart().type() == DwMime::kTypeText ) {
02686         // convert CRLF to LF before writing text attachments to disk
02687         size = KMail::Util::crlf2lf( cstr.data(), size );
02688       }
02689       data.resize( size );
02690     }
02691   }
02692   QDataStream ds;
02693   QFile file;
02694   KTempFile tf;
02695   tf.setAutoDelete( true );
02696   if ( url.isLocalFile() )
02697   {
02698     // save directly
02699     file.setName( url.path() );
02700     if ( !file.open( IO_WriteOnly ) )
02701     {
02702       KMessageBox::error( parentWidget(),
02703           i18n( "%2 is detailed error description",
02704             "Could not write the file %1:\n%2" )
02705           .arg( file.name() )
02706           .arg( QString::fromLocal8Bit( strerror( errno ) ) ),
02707           i18n( "KMail Error" ) );
02708       return Failed;
02709     }
02710 
02711     // #79685 by default use the umask the user defined, but let it be configurable
02712     if ( GlobalSettings::self()->disregardUmask() )
02713       fchmod( file.handle(), S_IRUSR | S_IWUSR );
02714 
02715     ds.setDevice( &file );
02716   } else
02717   {
02718     // tmp file for upload
02719     ds.setDevice( tf.file() );
02720   }
02721 
02722   ds.writeRawBytes( data.data(), data.size() );
02723   if ( !url.isLocalFile() )
02724   {
02725     tf.close();
02726     if ( !KIO::NetAccess::upload( tf.name(), url, parentWidget() ) )
02727     {
02728       KMessageBox::error( parentWidget(),
02729           i18n( "Could not write the file %1." )
02730           .arg( url.path() ),
02731           i18n( "KMail Error" ) );
02732       return Failed;
02733     }
02734   } else
02735     file.close();
02736   return OK;
02737 }
02738 
02739 KMLoadPartsCommand::KMLoadPartsCommand( QPtrList<partNode>& parts, KMMessage *msg )
02740   : mNeedsRetrieval( 0 )
02741 {
02742   for ( QPtrListIterator<partNode> it( parts ); it.current(); ++it ) {
02743     mPartMap.insert( it.current(), msg );
02744   }
02745 }
02746 
02747 KMLoadPartsCommand::KMLoadPartsCommand( partNode *node, KMMessage *msg )
02748   : mNeedsRetrieval( 0 )
02749 {
02750   mPartMap.insert( node, msg );
02751 }
02752 
02753 KMLoadPartsCommand::KMLoadPartsCommand( PartNodeMessageMap& partMap )
02754   : mNeedsRetrieval( 0 ), mPartMap( partMap )
02755 {
02756 }
02757 
02758 void KMLoadPartsCommand::slotStart()
02759 {
02760   for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
02761         it != mPartMap.end();
02762         ++it ) {
02763     if ( !it.key()->msgPart().isComplete() &&
02764          !it.key()->msgPart().partSpecifier().isEmpty() ) {
02765       // incomplete part, so retrieve it first
02766       ++mNeedsRetrieval;
02767       KMFolder* curFolder = it.data()->parent();
02768       if ( curFolder ) {
02769         FolderJob *job =
02770           curFolder->createJob( it.data(), FolderJob::tGetMessage,
02771                                 0, it.key()->msgPart().partSpecifier() );
02772         job->setCancellable( false );
02773         connect( job, SIGNAL(messageUpdated(KMMessage*, QString)),
02774                  this, SLOT(slotPartRetrieved(KMMessage*, QString)) );
02775         job->start();
02776       } else
02777         kdWarning(5006) << "KMLoadPartsCommand - msg has no parent" << endl;
02778     }
02779   }
02780   if ( mNeedsRetrieval == 0 )
02781     execute();
02782 }
02783 
02784 void KMLoadPartsCommand::slotPartRetrieved( KMMessage *msg,
02785                                             QString partSpecifier )
02786 {
02787   DwBodyPart *part =
02788     msg->findDwBodyPart( msg->getFirstDwBodyPart(), partSpecifier );
02789   if ( part ) {
02790     // update the DwBodyPart in the partNode
02791     for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
02792           it != mPartMap.end();
02793           ++it ) {
02794       if ( it.key()->dwPart()->partId() == part->partId() )
02795         it.key()->setDwPart( part );
02796     }
02797   } else
02798     kdWarning(5006) << "KMLoadPartsCommand::slotPartRetrieved - could not find bodypart!" << endl;
02799   --mNeedsRetrieval;
02800   if ( mNeedsRetrieval == 0 )
02801     execute();
02802 }
02803 
02804 KMCommand::Result KMLoadPartsCommand::execute()
02805 {
02806   emit partsRetrieved();
02807   setResult( OK );
02808   emit completed( this );
02809   deleteLater();
02810   return OK;
02811 }
02812 
02813 KMResendMessageCommand::KMResendMessageCommand( QWidget *parent,
02814    KMMessage *msg )
02815   :KMCommand( parent, msg )
02816 {
02817 }
02818 
02819 KMCommand::Result KMResendMessageCommand::execute()
02820 {
02821    KMMessage *msg = retrievedMessage();
02822    if ( !msg || !msg->codec() ) {
02823      return Failed;
02824    }
02825    KMMessage *newMsg = new KMMessage(*msg);
02826 
02827    QStringList whiteList;
02828    whiteList << "To" << "Cc" << "Bcc" << "Subject";
02829    newMsg->sanitizeHeaders( whiteList );
02830 
02831    newMsg->setCharset(msg->codec()->mimeName());
02832    newMsg->setParent( 0 );
02833 
02834    // make sure we have an identity set, default, if necessary
02835    newMsg->setHeaderField("X-KMail-Identity", QString::number( newMsg->identityUoid() ));
02836    newMsg->applyIdentity( newMsg->identityUoid() );
02837 
02838    KMail::Composer * win = KMail::makeComposer();
02839    win->setMsg(newMsg, false, true);
02840    win->show();
02841 
02842    return OK;
02843 }
02844 
02845 KMMailingListCommand::KMMailingListCommand( QWidget *parent, KMFolder *folder )
02846   : KMCommand( parent ), mFolder( folder )
02847 {
02848 }
02849 
02850 KMCommand::Result KMMailingListCommand::execute()
02851 {
02852   KURL::List lst = urls();
02853   QString handler = ( mFolder->mailingList().handler() == MailingList::KMail )
02854     ? "mailto" : "https";
02855 
02856   KMCommand *command = 0;
02857   for ( KURL::List::Iterator itr = lst.begin(); itr != lst.end(); ++itr ) {
02858     if ( handler == (*itr).protocol() ) {
02859       command = new KMUrlClickedCommand( *itr, mFolder->identity(), 0, false );
02860     }
02861   }
02862   if ( !command && !lst.empty() ) {
02863     command =
02864       new KMUrlClickedCommand( lst.first(), mFolder->identity(), 0, false );
02865   }
02866   if ( command ) {
02867     connect( command, SIGNAL( completed( KMCommand * ) ),
02868              this, SLOT( commandCompleted( KMCommand * ) ) );
02869     setDeletesItself( true );
02870     setEmitsCompletedItself( true );
02871     command->start();
02872     return OK;
02873   }
02874   return Failed;
02875 }
02876 
02877 void KMMailingListCommand::commandCompleted( KMCommand *command )
02878 {
02879   setResult( command->result() );
02880   emit completed( this );
02881   deleteLater();
02882 }
02883 
02884 KMMailingListPostCommand::KMMailingListPostCommand( QWidget *parent, KMFolder *folder )
02885   : KMMailingListCommand( parent, folder )
02886 {
02887 }
02888 KURL::List KMMailingListPostCommand::urls() const
02889 {
02890   return mFolder->mailingList().postURLS();
02891 }
02892 
02893 KMMailingListSubscribeCommand::KMMailingListSubscribeCommand( QWidget *parent, KMFolder *folder )
02894   : KMMailingListCommand( parent, folder )
02895 {
02896 }
02897 KURL::List KMMailingListSubscribeCommand::urls() const
02898 {
02899   return mFolder->mailingList().subscribeURLS();
02900 }
02901 
02902 KMMailingListUnsubscribeCommand::KMMailingListUnsubscribeCommand( QWidget *parent, KMFolder *folder )
02903   : KMMailingListCommand( parent, folder )
02904 {
02905 }
02906 KURL::List KMMailingListUnsubscribeCommand::urls() const
02907 {
02908   return mFolder->mailingList().unsubscribeURLS();
02909 }
02910 
02911 KMMailingListArchivesCommand::KMMailingListArchivesCommand( QWidget *parent, KMFolder *folder )
02912   : KMMailingListCommand( parent, folder )
02913 {
02914 }
02915 KURL::List KMMailingListArchivesCommand::urls() const
02916 {
02917   return mFolder->mailingList().archiveURLS();
02918 }
02919 
02920 KMMailingListHelpCommand::KMMailingListHelpCommand( QWidget *parent, KMFolder *folder )
02921   : KMMailingListCommand( parent, folder )
02922 {
02923 }
02924 KURL::List KMMailingListHelpCommand::urls() const
02925 {
02926   return mFolder->mailingList().helpURLS();
02927 }
02928 
02929 KMIMChatCommand::KMIMChatCommand( const KURL &url, KMMessage *msg )
02930   :mUrl( url ), mMessage( msg )
02931 {
02932 }
02933 
02934 KMCommand::Result KMIMChatCommand::execute()
02935 {
02936   kdDebug( 5006 ) << k_funcinfo << " URL is: " << mUrl << endl;
02937   QString addr = KMMessage::decodeMailtoUrl( mUrl.path() );
02938   // find UID for mail address
02939   KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
02940   KABC::AddresseeList addressees = addressBook->findByEmail( KPIM::getEmailAddress( addr ) ) ;
02941 
02942   // start chat
02943   if( addressees.count() == 1 ) {
02944     kmkernel->imProxy()->chatWithContact( addressees[0].uid() );
02945     return OK;
02946   }
02947   else
02948   {
02949     kdDebug( 5006 ) << "Didn't find exactly one addressee, couldn't tell who to chat to for that email address.  Count = " << addressees.count() << endl;
02950 
02951     QString apology;
02952     if ( addressees.isEmpty() )
02953       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." );
02954     else
02955     {
02956       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." );
02957       QStringList nameList;
02958       KABC::AddresseeList::const_iterator it = addressees.begin();
02959       KABC::AddresseeList::const_iterator end = addressees.end();
02960       for ( ; it != end; ++it )
02961       {
02962           nameList.append( (*it).realName() );
02963       }
02964       QString names = nameList.join( QString::fromLatin1( ",\n" ) );
02965       apology = apology.arg( names );
02966     }
02967 
02968     KMessageBox::sorry( parentWidget(), apology );
02969     return Failed;
02970   }
02971 }
02972 
02973 KMHandleAttachmentCommand::KMHandleAttachmentCommand( partNode* node,
02974      KMMessage* msg, int atmId, const QString& atmName,
02975      AttachmentAction action, KService::Ptr offer, QWidget* parent )
02976 : KMCommand( parent ), mNode( node ), mMsg( msg ), mAtmId( atmId ), mAtmName( atmName ),
02977   mAction( action ), mOffer( offer ), mJob( 0 )
02978 {
02979 }
02980 
02981 void KMHandleAttachmentCommand::slotStart()
02982 {
02983   if ( !mNode->msgPart().isComplete() )
02984   {
02985     // load the part
02986     kdDebug(5006) << "load part" << endl;
02987     KMLoadPartsCommand *command = new KMLoadPartsCommand( mNode, mMsg );
02988     connect( command, SIGNAL( partsRetrieved() ),
02989         this, SLOT( slotPartComplete() ) );
02990     command->start();
02991   } else
02992   {
02993     execute();
02994   }
02995 }
02996 
02997 void KMHandleAttachmentCommand::slotPartComplete()
02998 {
02999   execute();
03000 }
03001 
03002 KMCommand::Result KMHandleAttachmentCommand::execute()
03003 {
03004   switch( mAction )
03005   {
03006     case Open:
03007       atmOpen();
03008       break;
03009     case OpenWith:
03010       atmOpenWith();
03011       break;
03012     case View:
03013       atmView();
03014       break;
03015     case Save:
03016       atmSave();
03017       break;
03018     case Properties:
03019       atmProperties();
03020       break;
03021     case ChiasmusEncrypt:
03022       atmEncryptWithChiasmus();
03023       return Undefined;
03024       break;
03025     default:
03026       kdDebug(5006) << "unknown action " << mAction << endl;
03027       break;
03028   }
03029   setResult( OK );
03030   emit completed( this );
03031   deleteLater();
03032   return OK;
03033 }
03034 
03035 QString KMHandleAttachmentCommand::createAtmFileLink() const
03036 {
03037   QFileInfo atmFileInfo( mAtmName );
03038 
03039   if ( atmFileInfo.size() == 0 )
03040   {
03041     kdDebug(5006) << k_funcinfo << "rewriting attachment" << endl;
03042     // there is something wrong so write the file again
03043     QByteArray data = mNode->msgPart().bodyDecodedBinary();
03044     size_t size = data.size();
03045     if ( mNode->msgPart().type() == DwMime::kTypeText && size) {
03046       // convert CRLF to LF before writing text attachments to disk
03047       size = KMail::Util::crlf2lf( data.data(), size );
03048     }
03049     KPIM::kBytesToFile( data.data(), size, mAtmName, false, false, false );
03050   }
03051 
03052   KTempFile *linkFile = new KTempFile( locateLocal("tmp", atmFileInfo.fileName() +"_["),
03053                           "]."+ atmFileInfo.extension() );
03054 
03055   linkFile->setAutoDelete(true);
03056   QString linkName = linkFile->name();
03057   delete linkFile;
03058 
03059   if ( ::link(QFile::encodeName( mAtmName ), QFile::encodeName( linkName )) == 0 ) {
03060     return linkName; // success
03061   }
03062   return QString::null;
03063 }
03064 
03065 KService::Ptr KMHandleAttachmentCommand::getServiceOffer()
03066 {
03067   KMMessagePart& msgPart = mNode->msgPart();
03068   const QString contentTypeStr =
03069     ( msgPart.typeStr() + '/' + msgPart.subtypeStr() ).lower();
03070 
03071   if ( contentTypeStr == "text/x-vcard" ) {
03072     atmView();
03073     return 0;
03074   }
03075   // determine the MIME type of the attachment
03076   KMimeType::Ptr mimetype;
03077   // prefer the value of the Content-Type header
03078   mimetype = KMimeType::mimeType( contentTypeStr );
03079   if ( mimetype->name() == "application/octet-stream" ) {
03080     // consider the filename if Content-Type is application/octet-stream
03081     mimetype = KMimeType::findByPath( mAtmName, 0, true /* no disk access */ );
03082   }
03083   if ( ( mimetype->name() == "application/octet-stream" )
03084        && msgPart.isComplete() ) {
03085     // consider the attachment's contents if neither the Content-Type header
03086     // nor the filename give us a clue
03087     mimetype = KMimeType::findByFileContent( mAtmName );
03088   }
03089   return KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
03090 }
03091 
03092 void KMHandleAttachmentCommand::atmOpen()
03093 {
03094   if ( !mOffer )
03095     mOffer = getServiceOffer();
03096   if ( !mOffer ) {
03097     kdDebug(5006) << k_funcinfo << "got no offer" << endl;
03098     return;
03099   }
03100 
03101   KURL::List lst;
03102   KURL url;
03103   bool autoDelete = true;
03104   QString fname = createAtmFileLink();
03105 
03106   if ( fname.isNull() ) {
03107     autoDelete = false;
03108     fname = mAtmName;
03109   }
03110 
03111   url.setPath( fname );
03112   lst.append( url );
03113   if ( (KRun::run( *mOffer, lst, autoDelete ) <= 0) && autoDelete ) {
03114       QFile::remove(url.path());
03115   }
03116 }
03117 
03118 void KMHandleAttachmentCommand::atmOpenWith()
03119 {
03120   KURL::List lst;
03121   KURL url;
03122   bool autoDelete = true;
03123   QString fname = createAtmFileLink();
03124 
03125   if ( fname.isNull() ) {
03126     autoDelete = false;
03127     fname = mAtmName;
03128   }
03129 
03130   url.setPath( fname );
03131   lst.append( url );
03132   if ( (! KRun::displayOpenWithDialog(lst, autoDelete)) && autoDelete ) {
03133     QFile::remove( url.path() );
03134   }
03135 }
03136 
03137 void KMHandleAttachmentCommand::atmView()
03138 {
03139   // we do not handle this ourself
03140   emit showAttachment( mAtmId, mAtmName );
03141 }
03142 
03143 void KMHandleAttachmentCommand::atmSave()
03144 {
03145   QPtrList<partNode> parts;
03146   parts.append( mNode );
03147   // save, do not leave encoded
03148   KMSaveAttachmentsCommand *command =
03149     new KMSaveAttachmentsCommand( 0, parts, mMsg, false );
03150   command->start();
03151 }
03152 
03153 void KMHandleAttachmentCommand::atmProperties()
03154 {
03155   KMMsgPartDialogCompat dlg( parentWidget() , 0, true );
03156   KMMessagePart& msgPart = mNode->msgPart();
03157   dlg.setMsgPart( &msgPart );
03158   dlg.exec();
03159 }
03160 
03161 void KMHandleAttachmentCommand::atmEncryptWithChiasmus()
03162 {
03163   const partNode * node = mNode;
03164   Q_ASSERT( node );
03165   if ( !node )
03166     return;
03167 
03168   // FIXME: better detection of mimetype??
03169   if ( !mAtmName.endsWith( ".xia", false ) )
03170     return;
03171 
03172   const Kleo::CryptoBackend::Protocol * chiasmus =
03173     Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
03174   Q_ASSERT( chiasmus );
03175   if ( !chiasmus )
03176     return;
03177 
03178   const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
03179   if ( !listjob.get() ) {
03180     const QString msg = i18n( "Chiasmus backend does not offer the "
03181                               "\"x-obtain-keys\" function. Please report this bug." );
03182     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03183     return;
03184   }
03185 
03186   if ( listjob->exec() ) {
03187     listjob->showErrorDialog( parentWidget(), i18n( "Chiasmus Backend Error" ) );
03188     return;
03189   }
03190 
03191   const QVariant result = listjob->property( "result" );
03192   if ( result.type() != QVariant::StringList ) {
03193     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
03194                               "The \"x-obtain-keys\" function did not return a "
03195                               "string list. Please report this bug." );
03196     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03197     return;
03198   }
03199 
03200   const QStringList keys = result.toStringList();
03201   if ( keys.empty() ) {
03202     const QString msg = i18n( "No keys have been found. Please check that a "
03203                               "valid key path has been set in the Chiasmus "
03204                               "configuration." );
03205     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03206     return;
03207   }
03208 
03209   ChiasmusKeySelector selectorDlg( parentWidget(), i18n( "Chiasmus Decryption Key Selection" ),
03210                                    keys, GlobalSettings::chiasmusDecryptionKey(),
03211                                    GlobalSettings::chiasmusDecryptionOptions() );
03212   if ( selectorDlg.exec() != QDialog::Accepted )
03213     return;
03214 
03215   GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
03216   GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
03217   assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
03218 
03219   Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", QMap<QString,QVariant>() );
03220   if ( !job ) {
03221     const QString msg = i18n( "Chiasmus backend does not offer the "
03222                               "\"x-decrypt\" function. Please report this bug." );
03223     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03224     return;
03225   }
03226 
03227   const QByteArray input = node->msgPart().bodyDecodedBinary();
03228 
03229   if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
03230        !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
03231        !job->setProperty( "input", input ) ) {
03232     const QString msg = i18n( "The \"x-decrypt\" function does not accept "
03233                               "the expected parameters. Please report this bug." );
03234     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03235     return;
03236   }
03237 
03238   setDeletesItself( true ); // the job below is async, we have to cleanup ourselves
03239   if ( job->start() ) {
03240     job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
03241     return;
03242   }
03243 
03244   mJob = job;
03245   connect( job, SIGNAL(result(const GpgME::Error&,const QVariant&)),
03246            this, SLOT(slotAtmDecryptWithChiasmusResult(const GpgME::Error&,const QVariant&)) );
03247 }
03248 
03249 static const QString chomp( const QString & base, const QString & suffix, bool cs ) {
03250   return base.endsWith( suffix, cs ) ? base.left( base.length() - suffix.length() ) : base ;
03251 }
03252 
03253 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusResult( const GpgME::Error & err, const QVariant & result )
03254 {
03255   LaterDeleterWithCommandCompletion d( this );
03256   if ( !mJob )
03257     return;
03258   Q_ASSERT( mJob == sender() );
03259   if ( mJob != sender() )
03260     return;
03261   Kleo::Job * job = mJob;
03262   mJob = 0;
03263   if ( err.isCanceled() )
03264     return;
03265   if ( err ) {
03266     job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
03267     return;
03268   }
03269 
03270   if ( result.type() != QVariant::ByteArray ) {
03271     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
03272                               "The \"x-decrypt\" function did not return a "
03273                               "byte array. Please report this bug." );
03274     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03275     return;
03276   }
03277 
03278   const KURL url = KFileDialog::getSaveURL( chomp( mAtmName, ".xia", false ), QString::null, parentWidget() );
03279   if ( url.isEmpty() )
03280     return;
03281 
03282   bool overwrite = KMail::Util::checkOverwrite( url, parentWidget() );
03283   if ( !overwrite )
03284     return;
03285 
03286   d.setDisabled( true ); // we got this far, don't delete yet
03287   KIO::Job * uploadJob = KIO::storedPut( result.toByteArray(), url, -1, overwrite, false /*resume*/ );
03288   uploadJob->setWindow( parentWidget() );
03289   connect( uploadJob, SIGNAL(result(KIO::Job*)),
03290            this, SLOT(slotAtmDecryptWithChiasmusUploadResult(KIO::Job*)) );
03291 }
03292 
03293 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusUploadResult( KIO::Job * job )
03294 {
03295   if ( job->error() )
03296     job->showErrorDialog();
03297   LaterDeleterWithCommandCompletion d( this );
03298   d.setResult( OK );
03299 }
03300 
03301 
03302 AttachmentModifyCommand::AttachmentModifyCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03303     KMCommand( parent, msg ),
03304     mPartIndex( node->nodeId() ),
03305     mSernum( 0 )
03306 {
03307 }
03308 
03309 AttachmentModifyCommand::~ AttachmentModifyCommand()
03310 {
03311 }
03312 
03313 KMCommand::Result AttachmentModifyCommand::execute()
03314 {
03315   KMMessage *msg = retrievedMessage();
03316   if ( !msg )
03317     return Failed;
03318   mSernum = msg->getMsgSerNum();
03319 
03320   mFolder = msg->parent();
03321   if ( !mFolder || !mFolder->storage() )
03322     return Failed;
03323 
03324   Result res = doAttachmentModify();
03325   if ( res != OK )
03326     return res;
03327 
03328   setEmitsCompletedItself( true );
03329   setDeletesItself( true );
03330   return OK;
03331 }
03332 
03333 void AttachmentModifyCommand::storeChangedMessage(KMMessage * msg)
03334 {
03335   if ( !mFolder || !mFolder->storage() ) {
03336     kdWarning(5006) << k_funcinfo << "We lost the folder!" << endl;
03337     setResult( Failed );
03338     emit completed( this );
03339     deleteLater();
03340   }
03341   int res = mFolder->addMsg( msg ) != 0;
03342   if ( mFolder->folderType() == KMFolderTypeImap ) {
03343     KMFolderImap *f = static_cast<KMFolderImap*>( mFolder->storage() );
03344     connect( f, SIGNAL(folderComplete(KMFolderImap*,bool)),
03345              SLOT(messageStoreResult(KMFolderImap*,bool)) );
03346   } else {
03347     messageStoreResult( 0, res == 0 );
03348   }
03349 }
03350 
03351 void AttachmentModifyCommand::messageStoreResult(KMFolderImap* folder, bool success )
03352 {
03353   Q_UNUSED( folder );
03354   if ( success ) {
03355     KMCommand *delCmd = new KMDeleteMsgCommand( mSernum );
03356     connect( delCmd, SIGNAL(completed(KMCommand*)), SLOT(messageDeleteResult(KMCommand*)) );
03357     delCmd->start();
03358     return;
03359   }
03360   kdWarning(5006) << k_funcinfo << "Adding modified message failed." << endl;
03361   setResult( Failed );
03362   emit completed( this );
03363   deleteLater();
03364 }
03365 
03366 void AttachmentModifyCommand::messageDeleteResult(KMCommand * cmd)
03367 {
03368   setResult( cmd->result() );
03369   emit completed( this );
03370   deleteLater();
03371 }
03372 
03373 DwBodyPart * AttachmentModifyCommand::findPart(KMMessage* msg, int index)
03374 {
03375   int accu = 0;
03376   return findPartInternal( msg->getTopLevelPart(), index, accu );
03377 }
03378 
03379 DwBodyPart * AttachmentModifyCommand::findPartInternal(DwEntity * root, int index, int & accu)
03380 {
03381   accu++;
03382   if ( index < accu ) // should not happen
03383     return 0;
03384   DwBodyPart *current = dynamic_cast<DwBodyPart*>( root );
03385   if ( index == accu )
03386     return current;
03387   DwBodyPart *rv = 0;
03388   if ( root->Body().FirstBodyPart() )
03389     rv = findPartInternal( root->Body().FirstBodyPart(), index, accu );
03390   if ( !rv && current && current->Next() )
03391     rv = findPartInternal( current->Next(), index, accu );
03392   return rv;
03393 }
03394 
03395 
03396 KMDeleteAttachmentCommand::KMDeleteAttachmentCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03397     AttachmentModifyCommand( node, msg, parent )
03398 {
03399   kdDebug(5006) << k_funcinfo << endl;
03400 }
03401 
03402 KMDeleteAttachmentCommand::~KMDeleteAttachmentCommand()
03403 {
03404   kdDebug(5006) << k_funcinfo << endl;
03405 }
03406 
03407 KMCommand::Result KMDeleteAttachmentCommand::doAttachmentModify()
03408 {
03409   KMMessage *msg = retrievedMessage();
03410   KMMessagePart part;
03411   DwBodyPart *dwpart = findPart( msg, mPartIndex );
03412   if ( !dwpart )
03413     return Failed;
03414   KMMessage::bodyPart( dwpart, &part, true );
03415   if ( !part.isComplete() )
03416      return Failed;
03417 
03418   DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
03419   if ( !parentNode )
03420     return Failed;
03421   parentNode->RemoveBodyPart( dwpart );
03422 
03423   // add dummy part to show that a attachment has been deleted
03424   KMMessagePart dummyPart;
03425   dummyPart.duplicate( part );
03426   QString comment = i18n("This attachment has been deleted.");
03427   if ( !part.fileName().isEmpty() )
03428     comment = i18n( "The attachment '%1' has been deleted." ).arg( part.fileName() );
03429   dummyPart.setContentDescription( comment );
03430   dummyPart.setBodyEncodedBinary( QByteArray() );
03431   QCString cd = dummyPart.contentDisposition();
03432   if ( cd.find( "inline", 0, false ) == 0 ) {
03433     cd.replace( 0, 10, "attachment" );
03434     dummyPart.setContentDisposition( cd );
03435   } else if ( cd.isEmpty() ) {
03436     dummyPart.setContentDisposition( "attachment" );
03437   }
03438   DwBodyPart* newDwPart = msg->createDWBodyPart( &dummyPart );
03439   parentNode->AddBodyPart( newDwPart );
03440   msg->getTopLevelPart()->Assemble();
03441 
03442   KMMessage *newMsg = new KMMessage();
03443   newMsg->fromDwString( msg->asDwString() );
03444   newMsg->setStatus( msg->status() );
03445 
03446   storeChangedMessage( newMsg );
03447   return OK;
03448 }
03449 
03450 
03451 KMEditAttachmentCommand::KMEditAttachmentCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03452     AttachmentModifyCommand( node, msg, parent )
03453 {
03454   kdDebug(5006) << k_funcinfo << endl;
03455   mTempFile.setAutoDelete( true );
03456 }
03457 
03458 KMEditAttachmentCommand::~ KMEditAttachmentCommand()
03459 {
03460 }
03461 
03462 KMCommand::Result KMEditAttachmentCommand::doAttachmentModify()
03463 {
03464   KMMessage *msg = retrievedMessage();
03465   KMMessagePart part;
03466   DwBodyPart *dwpart = findPart( msg, mPartIndex );
03467   if ( !dwpart )
03468     return Failed;
03469   KMMessage::bodyPart( dwpart, &part, true );
03470   if ( !part.isComplete() )
03471      return Failed;
03472 
03473   if( !dynamic_cast<DwBody*>( dwpart->Parent() ) )
03474     return Failed;
03475 
03476   mTempFile.file()->writeBlock( part.bodyDecodedBinary() );
03477   mTempFile.file()->flush();
03478 
03479   KMail::EditorWatcher *watcher = new KMail::EditorWatcher( KURL(mTempFile.file()->name()), part.typeStr() + "/" + part.subtypeStr(), false, this );
03480   connect( watcher, SIGNAL(editDone(KMail::EditorWatcher*)), SLOT(editDone(KMail::EditorWatcher*)) );
03481   if ( !watcher->start() )
03482     return Failed;
03483   setEmitsCompletedItself( true );
03484   setDeletesItself( true );
03485   return OK;
03486 }
03487 
03488 void KMEditAttachmentCommand::editDone(KMail::EditorWatcher * watcher)
03489 {
03490   kdDebug(5006) << k_funcinfo << endl;
03491   // anything changed?
03492   if ( !watcher->fileChanged() ) {
03493     kdDebug(5006) << k_funcinfo << "File has not been changed" << endl;
03494     setResult( Canceled );
03495     emit completed( this );
03496     deleteLater();
03497   }
03498 
03499   mTempFile.file()->reset();
03500   QByteArray data = mTempFile.file()->readAll();
03501 
03502   // build the new message
03503   KMMessage *msg = retrievedMessage();
03504   KMMessagePart part;
03505   DwBodyPart *dwpart = findPart( msg, mPartIndex );
03506   KMMessage::bodyPart( dwpart, &part, true );
03507 
03508   DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
03509   assert( parentNode );
03510   parentNode->RemoveBodyPart( dwpart );
03511 
03512   KMMessagePart att;
03513   att.duplicate( part );
03514   att.setBodyEncodedBinary( data );
03515 
03516   DwBodyPart* newDwPart = msg->createDWBodyPart( &att );
03517   parentNode->AddBodyPart( newDwPart );
03518   msg->getTopLevelPart()->Assemble();
03519 
03520   KMMessage *newMsg = new KMMessage();
03521   newMsg->fromDwString( msg->asDwString() );
03522   newMsg->setStatus( msg->status() );
03523 
03524   storeChangedMessage( newMsg );
03525 }
03526 
03527 
03528 CreateTodoCommand::CreateTodoCommand(QWidget * parent, KMMessage * msg)
03529   : KMCommand( parent, msg )
03530 {
03531 }
03532 
03533 KMCommand::Result CreateTodoCommand::execute()
03534 {
03535   KMMessage *msg = retrievedMessage();
03536   if ( !msg || !msg->codec() ) {
03537     return Failed;
03538   }
03539 
03540   KMail::KorgHelper::ensureRunning();
03541 
03542   QString txt = i18n("From: %1\nTo: %2\nSubject: %3").arg( msg->from() )
03543                 .arg( msg->to() ).arg( msg->subject() );
03544 
03545   KTempFile tf;
03546   tf.setAutoDelete( true );
03547   QString uri = "kmail:" + QString::number( msg->getMsgSerNum() ) + "/" + msg->msgId();
03548   tf.file()->writeBlock( msg->asDwString().c_str(), msg->asDwString().length() );
03549   tf.close();
03550 
03551   KCalendarIface_stub *iface = new KCalendarIface_stub( kapp->dcopClient(), "korganizer", "CalendarIface" );
03552   iface->openTodoEditor( i18n("Mail: %1").arg( msg->subject() ), txt,
03553                          uri, tf.name(), QStringList(), "message/rfc822" );
03554   delete iface;
03555 
03556   return OK;
03557 }
03558 
03559 #include "kmcommands.moc"