kmail

kmcommands.cpp

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     (void) new KRun( mUrl );
02418   }
02419   else
02420     return Failed;
02421 
02422   return OK;
02423 }
02424 
02425 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, KMMessage *msg )
02426   : KMCommand( parent, msg ), mImplicitAttachments( true ), mEncoded( false )
02427 {
02428 }
02429 
02430 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, const QPtrList<KMMsgBase>& msgs )
02431   : KMCommand( parent, msgs ), mImplicitAttachments( true ), mEncoded( false )
02432 {
02433 }
02434 
02435 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, QPtrList<partNode>& attachments,
02436                                                     KMMessage *msg, bool encoded )
02437   : KMCommand( parent ), mImplicitAttachments( false ), mEncoded( encoded )
02438 {
02439   for ( QPtrListIterator<partNode> it( attachments ); it.current(); ++it ) {
02440     mAttachmentMap.insert( it.current(), msg );
02441   }
02442 }
02443 
02444 KMCommand::Result KMSaveAttachmentsCommand::execute()
02445 {
02446   setEmitsCompletedItself( true );
02447   if ( mImplicitAttachments ) {
02448     QPtrList<KMMessage> msgList = retrievedMsgs();
02449     KMMessage *msg;
02450     for ( QPtrListIterator<KMMessage> itr( msgList );
02451           ( msg = itr.current() );
02452           ++itr ) {
02453       partNode *rootNode = partNode::fromMessage( msg );
02454       for ( partNode *child = rootNode; child;
02455             child = child->firstChild() ) {
02456         for ( partNode *node = child; node; node = node->nextSibling() ) {
02457           if ( node->type() != DwMime::kTypeMultipart )
02458             mAttachmentMap.insert( node, msg );
02459         }
02460       }
02461     }
02462   }
02463   setDeletesItself( true );
02464   // load all parts
02465   KMLoadPartsCommand *command = new KMLoadPartsCommand( mAttachmentMap );
02466   connect( command, SIGNAL( partsRetrieved() ),
02467            this, SLOT( slotSaveAll() ) );
02468   command->start();
02469 
02470   return OK;
02471 }
02472 
02473 void KMSaveAttachmentsCommand::slotSaveAll()
02474 {
02475   // now that all message parts have been retrieved, remove all parts which
02476   // don't represent an attachment if they were not explicitely passed in the
02477   // c'tor
02478   if ( mImplicitAttachments ) {
02479     for ( PartNodeMessageMap::iterator it = mAttachmentMap.begin();
02480           it != mAttachmentMap.end(); ) {
02481       // only body parts which have a filename or a name parameter (except for
02482       // the root node for which name is set to the message's subject) are
02483       // considered attachments
02484       if ( it.key()->msgPart().fileName().stripWhiteSpace().isEmpty() &&
02485            ( it.key()->msgPart().name().stripWhiteSpace().isEmpty() ||
02486              !it.key()->parentNode() ) ) {
02487         PartNodeMessageMap::iterator delIt = it;
02488         ++it;
02489         mAttachmentMap.remove( delIt );
02490       }
02491       else
02492         ++it;
02493     }
02494     if ( mAttachmentMap.isEmpty() ) {
02495       KMessageBox::information( 0, i18n("Found no attachments to save.") );
02496       setResult( OK ); // The user has already been informed.
02497       emit completed( this );
02498       deleteLater();
02499       return;
02500     }
02501   }
02502 
02503   KURL url, dirUrl;
02504   if ( mAttachmentMap.count() > 1 ) {
02505     // get the dir
02506     dirUrl = KDirSelectDialog::selectDirectory( QString::null, false,
02507                                                 parentWidget(),
02508                                                 i18n("Save Attachments To") );
02509     if ( !dirUrl.isValid() ) {
02510       setResult( Canceled );
02511       emit completed( this );
02512       deleteLater();
02513       return;
02514     }
02515 
02516     // we may not get a slash-terminated url out of KDirSelectDialog
02517     dirUrl.adjustPath( 1 );
02518   }
02519   else {
02520     // only one item, get the desired filename
02521     partNode *node = mAttachmentMap.begin().key();
02522     // replace all ':' with '_' because ':' isn't allowed on FAT volumes
02523     QString s =
02524       node->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
02525     if ( s.isEmpty() )
02526       s = node->msgPart().name().stripWhiteSpace().replace( ':', '_' );
02527     if ( s.isEmpty() )
02528       s = i18n("filename for an unnamed attachment", "attachment.1");
02529     url = KFileDialog::getSaveURL( s, QString::null, parentWidget(),
02530                                    QString::null );
02531     if ( url.isEmpty() ) {
02532       setResult( Canceled );
02533       emit completed( this );
02534       deleteLater();
02535       return;
02536     }
02537   }
02538 
02539   QMap< QString, int > renameNumbering;
02540 
02541   Result globalResult = OK;
02542   int unnamedAtmCount = 0;
02543   for ( PartNodeMessageMap::const_iterator it = mAttachmentMap.begin();
02544         it != mAttachmentMap.end();
02545         ++it ) {
02546     KURL curUrl;
02547     if ( !dirUrl.isEmpty() ) {
02548       curUrl = dirUrl;
02549       QString s =
02550         it.key()->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
02551       if ( s.isEmpty() )
02552         s = it.key()->msgPart().name().stripWhiteSpace().replace( ':', '_' );
02553       if ( s.isEmpty() ) {
02554         ++unnamedAtmCount;
02555         s = i18n("filename for the %1-th unnamed attachment",
02556                  "attachment.%1")
02557             .arg( unnamedAtmCount );
02558       }
02559       curUrl.setFileName( s );
02560     } else {
02561       curUrl = url;
02562     }
02563 
02564     if ( !curUrl.isEmpty() ) {
02565 
02566      // Rename the file if we have already saved one with the same name:
02567      // try appending a number before extension (e.g. "pic.jpg" => "pic_2.jpg")
02568      QString origFile = curUrl.fileName();
02569      QString file = origFile;
02570 
02571      while ( renameNumbering.contains(file) ) {
02572        file = origFile;
02573        int num = renameNumbering[file] + 1;
02574        int dotIdx = file.findRev('.');
02575        file = file.insert( (dotIdx>=0) ? dotIdx : file.length(), QString("_") + QString::number(num) );
02576      }
02577      curUrl.setFileName(file);
02578 
02579      // Increment the counter for both the old and the new filename
02580      if ( !renameNumbering.contains(origFile))
02581          renameNumbering[origFile] = 1;
02582      else
02583          renameNumbering[origFile]++;
02584 
02585      if ( file != origFile ) {
02586         if ( !renameNumbering.contains(file))
02587             renameNumbering[file] = 1;
02588         else
02589             renameNumbering[file]++;
02590      }
02591 
02592 
02593       if ( KIO::NetAccess::exists( curUrl, false, parentWidget() ) ) {
02594         if ( KMessageBox::warningContinueCancel( parentWidget(),
02595               i18n( "A file named %1 already exists. Do you want to overwrite it?" )
02596               .arg( curUrl.fileName() ),
02597               i18n( "File Already Exists" ), i18n("&Overwrite") ) == KMessageBox::Cancel) {
02598           continue;
02599         }
02600       }
02601       // save
02602       const Result result = saveItem( it.key(), curUrl );
02603       if ( result != OK )
02604         globalResult = result;
02605     }
02606   }
02607   setResult( globalResult );
02608   emit completed( this );
02609   deleteLater();
02610 }
02611 
02612 KMCommand::Result KMSaveAttachmentsCommand::saveItem( partNode *node,
02613                                                       const KURL& url )
02614 {
02615   bool bSaveEncrypted = false;
02616   bool bEncryptedParts = node->encryptionState() != KMMsgNotEncrypted;
02617   if( bEncryptedParts )
02618     if( KMessageBox::questionYesNo( parentWidget(),
02619           i18n( "The part %1 of the message is encrypted. Do you want to keep the encryption when saving?" ).
02620           arg( url.fileName() ),
02621           i18n( "KMail Question" ), i18n("Keep Encryption"), i18n("Do Not Keep") ) ==
02622         KMessageBox::Yes )
02623       bSaveEncrypted = true;
02624 
02625   bool bSaveWithSig = true;
02626   if( node->signatureState() != KMMsgNotSigned )
02627     if( KMessageBox::questionYesNo( parentWidget(),
02628           i18n( "The part %1 of the message is signed. Do you want to keep the signature when saving?" ).
02629           arg( url.fileName() ),
02630           i18n( "KMail Question" ), i18n("Keep Signature"), i18n("Do Not Keep") ) !=
02631         KMessageBox::Yes )
02632       bSaveWithSig = false;
02633 
02634   QByteArray data;
02635   if ( mEncoded )
02636   {
02637     // This does not decode the Message Content-Transfer-Encoding
02638     // but saves the _original_ content of the message part
02639     data = KMail::Util::ByteArray( node->msgPart().dwBody() );
02640   }
02641   else
02642   {
02643     if( bSaveEncrypted || !bEncryptedParts) {
02644       partNode *dataNode = node;
02645       QCString rawReplyString;
02646       bool gotRawReplyString = false;
02647       if( !bSaveWithSig ) {
02648         if( DwMime::kTypeMultipart == node->type() &&
02649             DwMime::kSubtypeSigned == node->subType() ){
02650           // carefully look for the part that is *not* the signature part:
02651           if( node->findType( DwMime::kTypeApplication,
02652                 DwMime::kSubtypePgpSignature,
02653                 true, false ) ){
02654             dataNode = node->findTypeNot( DwMime::kTypeApplication,
02655                 DwMime::kSubtypePgpSignature,
02656                 true, false );
02657           }else if( node->findType( DwMime::kTypeApplication,
02658                 DwMime::kSubtypePkcs7Mime,
02659                 true, false ) ){
02660             dataNode = node->findTypeNot( DwMime::kTypeApplication,
02661                 DwMime::kSubtypePkcs7Mime,
02662                 true, false );
02663           }else{
02664             dataNode = node->findTypeNot( DwMime::kTypeMultipart,
02665                 DwMime::kSubtypeUnknown,
02666                 true, false );
02667           }
02668     }else{
02669       ObjectTreeParser otp( 0, 0, false, false, false );
02670 
02671       // process this node and all it's siblings and descendants
02672       dataNode->setProcessed( false, true );
02673       otp.parseObjectTree( dataNode );
02674 
02675       rawReplyString = otp.rawReplyString();
02676       gotRawReplyString = true;
02677         }
02678       }
02679       QByteArray cstr = gotRawReplyString
02680                          ? rawReplyString
02681                          : dataNode->msgPart().bodyDecodedBinary();
02682       data = cstr;
02683       size_t size = cstr.size();
02684       if ( dataNode->msgPart().type() == DwMime::kTypeText ) {
02685         // convert CRLF to LF before writing text attachments to disk
02686         size = KMail::Util::crlf2lf( cstr.data(), size );
02687       }
02688       data.resize( size );
02689     }
02690   }
02691   QDataStream ds;
02692   QFile file;
02693   KTempFile tf;
02694   tf.setAutoDelete( true );
02695   if ( url.isLocalFile() )
02696   {
02697     // save directly
02698     file.setName( url.path() );
02699     if ( !file.open( IO_WriteOnly ) )
02700     {
02701       KMessageBox::error( parentWidget(),
02702           i18n( "%2 is detailed error description",
02703             "Could not write the file %1:\n%2" )
02704           .arg( file.name() )
02705           .arg( QString::fromLocal8Bit( strerror( errno ) ) ),
02706           i18n( "KMail Error" ) );
02707       return Failed;
02708     }
02709 
02710     // #79685 by default use the umask the user defined, but let it be configurable
02711     if ( GlobalSettings::self()->disregardUmask() )
02712       fchmod( file.handle(), S_IRUSR | S_IWUSR );
02713 
02714     ds.setDevice( &file );
02715   } else
02716   {
02717     // tmp file for upload
02718     ds.setDevice( tf.file() );
02719   }
02720 
02721   ds.writeRawBytes( data.data(), data.size() );
02722   if ( !url.isLocalFile() )
02723   {
02724     tf.close();
02725     if ( !KIO::NetAccess::upload( tf.name(), url, parentWidget() ) )
02726     {
02727       KMessageBox::error( parentWidget(),
02728           i18n( "Could not write the file %1." )
02729           .arg( url.path() ),
02730           i18n( "KMail Error" ) );
02731       return Failed;
02732     }
02733   } else
02734     file.close();
02735   return OK;
02736 }
02737 
02738 KMLoadPartsCommand::KMLoadPartsCommand( QPtrList<partNode>& parts, KMMessage *msg )
02739   : mNeedsRetrieval( 0 )
02740 {
02741   for ( QPtrListIterator<partNode> it( parts ); it.current(); ++it ) {
02742     mPartMap.insert( it.current(), msg );
02743   }
02744 }
02745 
02746 KMLoadPartsCommand::KMLoadPartsCommand( partNode *node, KMMessage *msg )
02747   : mNeedsRetrieval( 0 )
02748 {
02749   mPartMap.insert( node, msg );
02750 }
02751 
02752 KMLoadPartsCommand::KMLoadPartsCommand( PartNodeMessageMap& partMap )
02753   : mNeedsRetrieval( 0 ), mPartMap( partMap )
02754 {
02755 }
02756 
02757 void KMLoadPartsCommand::slotStart()
02758 {
02759   for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
02760         it != mPartMap.end();
02761         ++it ) {
02762     if ( !it.key()->msgPart().isComplete() &&
02763          !it.key()->msgPart().partSpecifier().isEmpty() ) {
02764       // incomplete part, so retrieve it first
02765       ++mNeedsRetrieval;
02766       KMFolder* curFolder = it.data()->parent();
02767       if ( curFolder ) {
02768         FolderJob *job =
02769           curFolder->createJob( it.data(), FolderJob::tGetMessage,
02770                                 0, it.key()->msgPart().partSpecifier() );
02771         job->setCancellable( false );
02772         connect( job, SIGNAL(messageUpdated(KMMessage*, QString)),
02773                  this, SLOT(slotPartRetrieved(KMMessage*, QString)) );
02774         job->start();
02775       } else
02776         kdWarning(5006) << "KMLoadPartsCommand - msg has no parent" << endl;
02777     }
02778   }
02779   if ( mNeedsRetrieval == 0 )
02780     execute();
02781 }
02782 
02783 void KMLoadPartsCommand::slotPartRetrieved( KMMessage *msg,
02784                                             QString partSpecifier )
02785 {
02786   DwBodyPart *part =
02787     msg->findDwBodyPart( msg->getFirstDwBodyPart(), partSpecifier );
02788   if ( part ) {
02789     // update the DwBodyPart in the partNode
02790     for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
02791           it != mPartMap.end();
02792           ++it ) {
02793       if ( it.key()->dwPart()->partId() == part->partId() )
02794         it.key()->setDwPart( part );
02795     }
02796   } else
02797     kdWarning(5006) << "KMLoadPartsCommand::slotPartRetrieved - could not find bodypart!" << endl;
02798   --mNeedsRetrieval;
02799   if ( mNeedsRetrieval == 0 )
02800     execute();
02801 }
02802 
02803 KMCommand::Result KMLoadPartsCommand::execute()
02804 {
02805   emit partsRetrieved();
02806   setResult( OK );
02807   emit completed( this );
02808   deleteLater();
02809   return OK;
02810 }
02811 
02812 KMResendMessageCommand::KMResendMessageCommand( QWidget *parent,
02813    KMMessage *msg )
02814   :KMCommand( parent, msg )
02815 {
02816 }
02817 
02818 KMCommand::Result KMResendMessageCommand::execute()
02819 {
02820    KMMessage *msg = retrievedMessage();
02821    if ( !msg || !msg->codec() ) {
02822      return Failed;
02823    }
02824    KMMessage *newMsg = new KMMessage(*msg);
02825 
02826    QStringList whiteList;
02827    whiteList << "To" << "Cc" << "Bcc" << "Subject";
02828    newMsg->sanitizeHeaders( whiteList );
02829 
02830    newMsg->setCharset(msg->codec()->mimeName());
02831    newMsg->setParent( 0 );
02832 
02833    // make sure we have an identity set, default, if necessary
02834    newMsg->setHeaderField("X-KMail-Identity", QString::number( newMsg->identityUoid() ));
02835    newMsg->applyIdentity( newMsg->identityUoid() );
02836 
02837    KMail::Composer * win = KMail::makeComposer();
02838    win->setMsg(newMsg, false, true);
02839    win->show();
02840 
02841    return OK;
02842 }
02843 
02844 KMMailingListCommand::KMMailingListCommand( QWidget *parent, KMFolder *folder )
02845   : KMCommand( parent ), mFolder( folder )
02846 {
02847 }
02848 
02849 KMCommand::Result KMMailingListCommand::execute()
02850 {
02851   KURL::List lst = urls();
02852   QString handler = ( mFolder->mailingList().handler() == MailingList::KMail )
02853     ? "mailto" : "https";
02854 
02855   KMCommand *command = 0;
02856   for ( KURL::List::Iterator itr = lst.begin(); itr != lst.end(); ++itr ) {
02857     if ( handler == (*itr).protocol() ) {
02858       command = new KMUrlClickedCommand( *itr, mFolder->identity(), 0, false );
02859     }
02860   }
02861   if ( !command && !lst.empty() ) {
02862     command =
02863       new KMUrlClickedCommand( lst.first(), mFolder->identity(), 0, false );
02864   }
02865   if ( command ) {
02866     connect( command, SIGNAL( completed( KMCommand * ) ),
02867              this, SLOT( commandCompleted( KMCommand * ) ) );
02868     setDeletesItself( true );
02869     setEmitsCompletedItself( true );
02870     command->start();
02871     return OK;
02872   }
02873   return Failed;
02874 }
02875 
02876 void KMMailingListCommand::commandCompleted( KMCommand *command )
02877 {
02878   setResult( command->result() );
02879   emit completed( this );
02880   deleteLater();
02881 }
02882 
02883 KMMailingListPostCommand::KMMailingListPostCommand( QWidget *parent, KMFolder *folder )
02884   : KMMailingListCommand( parent, folder )
02885 {
02886 }
02887 KURL::List KMMailingListPostCommand::urls() const
02888 {
02889   return mFolder->mailingList().postURLS();
02890 }
02891 
02892 KMMailingListSubscribeCommand::KMMailingListSubscribeCommand( QWidget *parent, KMFolder *folder )
02893   : KMMailingListCommand( parent, folder )
02894 {
02895 }
02896 KURL::List KMMailingListSubscribeCommand::urls() const
02897 {
02898   return mFolder->mailingList().subscribeURLS();
02899 }
02900 
02901 KMMailingListUnsubscribeCommand::KMMailingListUnsubscribeCommand( QWidget *parent, KMFolder *folder )
02902   : KMMailingListCommand( parent, folder )
02903 {
02904 }
02905 KURL::List KMMailingListUnsubscribeCommand::urls() const
02906 {
02907   return mFolder->mailingList().unsubscribeURLS();
02908 }
02909 
02910 KMMailingListArchivesCommand::KMMailingListArchivesCommand( QWidget *parent, KMFolder *folder )
02911   : KMMailingListCommand( parent, folder )
02912 {
02913 }
02914 KURL::List KMMailingListArchivesCommand::urls() const
02915 {
02916   return mFolder->mailingList().archiveURLS();
02917 }
02918 
02919 KMMailingListHelpCommand::KMMailingListHelpCommand( QWidget *parent, KMFolder *folder )
02920   : KMMailingListCommand( parent, folder )
02921 {
02922 }
02923 KURL::List KMMailingListHelpCommand::urls() const
02924 {
02925   return mFolder->mailingList().helpURLS();
02926 }
02927 
02928 KMIMChatCommand::KMIMChatCommand( const KURL &url, KMMessage *msg )
02929   :mUrl( url ), mMessage( msg )
02930 {
02931 }
02932 
02933 KMCommand::Result KMIMChatCommand::execute()
02934 {
02935   kdDebug( 5006 ) << k_funcinfo << " URL is: " << mUrl << endl;
02936   QString addr = KMMessage::decodeMailtoUrl( mUrl.path() );
02937   // find UID for mail address
02938   KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
02939   KABC::AddresseeList addressees = addressBook->findByEmail( KPIM::getEmailAddress( addr ) ) ;
02940 
02941   // start chat
02942   if( addressees.count() == 1 ) {
02943     kmkernel->imProxy()->chatWithContact( addressees[0].uid() );
02944     return OK;
02945   }
02946   else
02947   {
02948     kdDebug( 5006 ) << "Didn't find exactly one addressee, couldn't tell who to chat to for that email address.  Count = " << addressees.count() << endl;
02949 
02950     QString apology;
02951     if ( addressees.isEmpty() )
02952       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." );
02953     else
02954     {
02955       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." );
02956       QStringList nameList;
02957       KABC::AddresseeList::const_iterator it = addressees.begin();
02958       KABC::AddresseeList::const_iterator end = addressees.end();
02959       for ( ; it != end; ++it )
02960       {
02961           nameList.append( (*it).realName() );
02962       }
02963       QString names = nameList.join( QString::fromLatin1( ",\n" ) );
02964       apology = apology.arg( names );
02965     }
02966 
02967     KMessageBox::sorry( parentWidget(), apology );
02968     return Failed;
02969   }
02970 }
02971 
02972 KMHandleAttachmentCommand::KMHandleAttachmentCommand( partNode* node,
02973      KMMessage* msg, int atmId, const QString& atmName,
02974      AttachmentAction action, KService::Ptr offer, QWidget* parent )
02975 : KMCommand( parent ), mNode( node ), mMsg( msg ), mAtmId( atmId ), mAtmName( atmName ),
02976   mAction( action ), mOffer( offer ), mJob( 0 )
02977 {
02978 }
02979 
02980 void KMHandleAttachmentCommand::slotStart()
02981 {
02982   if ( !mNode->msgPart().isComplete() )
02983   {
02984     // load the part
02985     kdDebug(5006) << "load part" << endl;
02986     KMLoadPartsCommand *command = new KMLoadPartsCommand( mNode, mMsg );
02987     connect( command, SIGNAL( partsRetrieved() ),
02988         this, SLOT( slotPartComplete() ) );
02989     command->start();
02990   } else
02991   {
02992     execute();
02993   }
02994 }
02995 
02996 void KMHandleAttachmentCommand::slotPartComplete()
02997 {
02998   execute();
02999 }
03000 
03001 KMCommand::Result KMHandleAttachmentCommand::execute()
03002 {
03003   switch( mAction )
03004   {
03005     case Open:
03006       atmOpen();
03007       break;
03008     case OpenWith:
03009       atmOpenWith();
03010       break;
03011     case View:
03012       atmView();
03013       break;
03014     case Save:
03015       atmSave();
03016       break;
03017     case Properties:
03018       atmProperties();
03019       break;
03020     case ChiasmusEncrypt:
03021       atmEncryptWithChiasmus();
03022       return Undefined;
03023       break;
03024     default:
03025       kdDebug(5006) << "unknown action " << mAction << endl;
03026       break;
03027   }
03028   setResult( OK );
03029   emit completed( this );
03030   deleteLater();
03031   return OK;
03032 }
03033 
03034 QString KMHandleAttachmentCommand::createAtmFileLink() const
03035 {
03036   QFileInfo atmFileInfo( mAtmName );
03037 
03038   if ( atmFileInfo.size() == 0 )
03039   {
03040     kdDebug(5006) << k_funcinfo << "rewriting attachment" << endl;
03041     // there is something wrong so write the file again
03042     QByteArray data = mNode->msgPart().bodyDecodedBinary();
03043     size_t size = data.size();
03044     if ( mNode->msgPart().type() == DwMime::kTypeText && size) {
03045       // convert CRLF to LF before writing text attachments to disk
03046       size = KMail::Util::crlf2lf( data.data(), size );
03047     }
03048     KPIM::kBytesToFile( data.data(), size, mAtmName, false, false, false );
03049   }
03050 
03051   KTempFile *linkFile = new KTempFile( locateLocal("tmp", atmFileInfo.fileName() +"_["),
03052                           "]."+ atmFileInfo.extension() );
03053 
03054   linkFile->setAutoDelete(true);
03055   QString linkName = linkFile->name();
03056   delete linkFile;
03057 
03058   if ( ::link(QFile::encodeName( mAtmName ), QFile::encodeName( linkName )) == 0 ) {
03059     return linkName; // success
03060   }
03061   return QString::null;
03062 }
03063 
03064 KService::Ptr KMHandleAttachmentCommand::getServiceOffer()
03065 {
03066   KMMessagePart& msgPart = mNode->msgPart();
03067   const QString contentTypeStr =
03068     ( msgPart.typeStr() + '/' + msgPart.subtypeStr() ).lower();
03069 
03070   if ( contentTypeStr == "text/x-vcard" ) {
03071     atmView();
03072     return 0;
03073   }
03074   // determine the MIME type of the attachment
03075   KMimeType::Ptr mimetype;
03076   // prefer the value of the Content-Type header
03077   mimetype = KMimeType::mimeType( contentTypeStr );
03078   if ( mimetype->name() == "application/octet-stream" ) {
03079     // consider the filename if Content-Type is application/octet-stream
03080     mimetype = KMimeType::findByPath( mAtmName, 0, true /* no disk access */ );
03081   }
03082   if ( ( mimetype->name() == "application/octet-stream" )
03083        && msgPart.isComplete() ) {
03084     // consider the attachment's contents if neither the Content-Type header
03085     // nor the filename give us a clue
03086     mimetype = KMimeType::findByFileContent( mAtmName );
03087   }
03088   return KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
03089 }
03090 
03091 void KMHandleAttachmentCommand::atmOpen()
03092 {
03093   if ( !mOffer )
03094     mOffer = getServiceOffer();
03095   if ( !mOffer ) {
03096     kdDebug(5006) << k_funcinfo << "got no offer" << endl;
03097     return;
03098   }
03099 
03100   KURL::List lst;
03101   KURL url;
03102   bool autoDelete = true;
03103   QString fname = createAtmFileLink();
03104 
03105   if ( fname.isNull() ) {
03106     autoDelete = false;
03107     fname = mAtmName;
03108   }
03109 
03110   url.setPath( fname );
03111   lst.append( url );
03112   if ( (KRun::run( *mOffer, lst, autoDelete ) <= 0) && autoDelete ) {
03113       QFile::remove(url.path());
03114   }
03115 }
03116 
03117 void KMHandleAttachmentCommand::atmOpenWith()
03118 {
03119   KURL::List lst;
03120   KURL url;
03121   bool autoDelete = true;
03122   QString fname = createAtmFileLink();
03123 
03124   if ( fname.isNull() ) {
03125     autoDelete = false;
03126     fname = mAtmName;
03127   }
03128 
03129   url.setPath( fname );
03130   lst.append( url );
03131   if ( (! KRun::displayOpenWithDialog(lst, autoDelete)) && autoDelete ) {
03132     QFile::remove( url.path() );
03133   }
03134 }
03135 
03136 void KMHandleAttachmentCommand::atmView()
03137 {
03138   // we do not handle this ourself
03139   emit showAttachment( mAtmId, mAtmName );
03140 }
03141 
03142 void KMHandleAttachmentCommand::atmSave()
03143 {
03144   QPtrList<partNode> parts;
03145   parts.append( mNode );
03146   // save, do not leave encoded
03147   KMSaveAttachmentsCommand *command =
03148     new KMSaveAttachmentsCommand( 0, parts, mMsg, false );
03149   command->start();
03150 }
03151 
03152 void KMHandleAttachmentCommand::atmProperties()
03153 {
03154   KMMsgPartDialogCompat dlg( parentWidget() , 0, true );
03155   KMMessagePart& msgPart = mNode->msgPart();
03156   dlg.setMsgPart( &msgPart );
03157   dlg.exec();
03158 }
03159 
03160 void KMHandleAttachmentCommand::atmEncryptWithChiasmus()
03161 {
03162   const partNode * node = mNode;
03163   Q_ASSERT( node );
03164   if ( !node )
03165     return;
03166 
03167   // FIXME: better detection of mimetype??
03168   if ( !mAtmName.endsWith( ".xia", false ) )
03169     return;
03170 
03171   const Kleo::CryptoBackend::Protocol * chiasmus =
03172     Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
03173   Q_ASSERT( chiasmus );
03174   if ( !chiasmus )
03175     return;
03176 
03177   const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
03178   if ( !listjob.get() ) {
03179     const QString msg = i18n( "Chiasmus backend does not offer the "
03180                               "\"x-obtain-keys\" function. Please report this bug." );
03181     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03182     return;
03183   }
03184 
03185   if ( listjob->exec() ) {
03186     listjob->showErrorDialog( parentWidget(), i18n( "Chiasmus Backend Error" ) );
03187     return;
03188   }
03189 
03190   const QVariant result = listjob->property( "result" );
03191   if ( result.type() != QVariant::StringList ) {
03192     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
03193                               "The \"x-obtain-keys\" function did not return a "
03194                               "string list. Please report this bug." );
03195     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03196     return;
03197   }
03198 
03199   const QStringList keys = result.toStringList();
03200   if ( keys.empty() ) {
03201     const QString msg = i18n( "No keys have been found. Please check that a "
03202                               "valid key path has been set in the Chiasmus "
03203                               "configuration." );
03204     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03205     return;
03206   }
03207 
03208   ChiasmusKeySelector selectorDlg( parentWidget(), i18n( "Chiasmus Decryption Key Selection" ),
03209                                    keys, GlobalSettings::chiasmusDecryptionKey(),
03210                                    GlobalSettings::chiasmusDecryptionOptions() );
03211   if ( selectorDlg.exec() != QDialog::Accepted )
03212     return;
03213 
03214   GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
03215   GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
03216   assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
03217 
03218   Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", QMap<QString,QVariant>() );
03219   if ( !job ) {
03220     const QString msg = i18n( "Chiasmus backend does not offer the "
03221                               "\"x-decrypt\" function. Please report this bug." );
03222     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03223     return;
03224   }
03225 
03226   const QByteArray input = node->msgPart().bodyDecodedBinary();
03227 
03228   if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
03229        !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
03230        !job->setProperty( "input", input ) ) {
03231     const QString msg = i18n( "The \"x-decrypt\" function does not accept "
03232                               "the expected parameters. Please report this bug." );
03233     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03234     return;
03235   }
03236 
03237   setDeletesItself( true ); // the job below is async, we have to cleanup ourselves
03238   if ( job->start() ) {
03239     job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
03240     return;
03241   }
03242 
03243   mJob = job;
03244   connect( job, SIGNAL(result(const GpgME::Error&,const QVariant&)),
03245            this, SLOT(slotAtmDecryptWithChiasmusResult(const GpgME::Error&,const QVariant&)) );
03246 }
03247 
03248 static const QString chomp( const QString & base, const QString & suffix, bool cs ) {
03249   return base.endsWith( suffix, cs ) ? base.left( base.length() - suffix.length() ) : base ;
03250 }
03251 
03252 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusResult( const GpgME::Error & err, const QVariant & result )
03253 {
03254   LaterDeleterWithCommandCompletion d( this );
03255   if ( !mJob )
03256     return;
03257   Q_ASSERT( mJob == sender() );
03258   if ( mJob != sender() )
03259     return;
03260   Kleo::Job * job = mJob;
03261   mJob = 0;
03262   if ( err.isCanceled() )
03263     return;
03264   if ( err ) {
03265     job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
03266     return;
03267   }
03268 
03269   if ( result.type() != QVariant::ByteArray ) {
03270     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
03271                               "The \"x-decrypt\" function did not return a "
03272                               "byte array. Please report this bug." );
03273     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03274     return;
03275   }
03276 
03277   const KURL url = KFileDialog::getSaveURL( chomp( mAtmName, ".xia", false ), QString::null, parentWidget() );
03278   if ( url.isEmpty() )
03279     return;
03280 
03281   bool overwrite = KMail::Util::checkOverwrite( url, parentWidget() );
03282   if ( !overwrite )
03283     return;
03284 
03285   d.setDisabled( true ); // we got this far, don't delete yet
03286   KIO::Job * uploadJob = KIO::storedPut( result.toByteArray(), url, -1, overwrite, false /*resume*/ );
03287   uploadJob->setWindow( parentWidget() );
03288   connect( uploadJob, SIGNAL(result(KIO::Job*)),
03289            this, SLOT(slotAtmDecryptWithChiasmusUploadResult(KIO::Job*)) );
03290 }
03291 
03292 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusUploadResult( KIO::Job * job )
03293 {
03294   if ( job->error() )
03295     job->showErrorDialog();
03296   LaterDeleterWithCommandCompletion d( this );
03297   d.setResult( OK );
03298 }
03299 
03300 
03301 AttachmentModifyCommand::AttachmentModifyCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03302     KMCommand( parent, msg ),
03303     mPartIndex( node->nodeId() ),
03304     mSernum( 0 )
03305 {
03306 }
03307 
03308 AttachmentModifyCommand::~ AttachmentModifyCommand()
03309 {
03310 }
03311 
03312 KMCommand::Result AttachmentModifyCommand::execute()
03313 {
03314   KMMessage *msg = retrievedMessage();
03315   if ( !msg )
03316     return Failed;
03317   mSernum = msg->getMsgSerNum();
03318 
03319   mFolder = msg->parent();
03320   if ( !mFolder || !mFolder->storage() )
03321     return Failed;
03322 
03323   Result res = doAttachmentModify();
03324   if ( res != OK )
03325     return res;
03326 
03327   setEmitsCompletedItself( true );
03328   setDeletesItself( true );
03329   return OK;
03330 }
03331 
03332 void AttachmentModifyCommand::storeChangedMessage(KMMessage * msg)
03333 {
03334   if ( !mFolder || !mFolder->storage() ) {
03335     kdWarning(5006) << k_funcinfo << "We lost the folder!" << endl;
03336     setResult( Failed );
03337     emit completed( this );
03338     deleteLater();
03339   }
03340   int res = mFolder->addMsg( msg ) != 0;
03341   if ( mFolder->folderType() == KMFolderTypeImap ) {
03342     KMFolderImap *f = static_cast<KMFolderImap*>( mFolder->storage() );
03343     connect( f, SIGNAL(folderComplete(KMFolderImap*,bool)),
03344              SLOT(messageStoreResult(KMFolderImap*,bool)) );
03345   } else {
03346     messageStoreResult( 0, res == 0 );
03347   }
03348 }
03349 
03350 void AttachmentModifyCommand::messageStoreResult(KMFolderImap* folder, bool success )
03351 {
03352   Q_UNUSED( folder );
03353   if ( success ) {
03354     KMCommand *delCmd = new KMDeleteMsgCommand( mSernum );
03355     connect( delCmd, SIGNAL(completed(KMCommand*)), SLOT(messageDeleteResult(KMCommand*)) );
03356     delCmd->start();
03357     return;
03358   }
03359   kdWarning(5006) << k_funcinfo << "Adding modified message failed." << endl;
03360   setResult( Failed );
03361   emit completed( this );
03362   deleteLater();
03363 }
03364 
03365 void AttachmentModifyCommand::messageDeleteResult(KMCommand * cmd)
03366 {
03367   setResult( cmd->result() );
03368   emit completed( this );
03369   deleteLater();
03370 }
03371 
03372 DwBodyPart * AttachmentModifyCommand::findPart(KMMessage* msg, int index)
03373 {
03374   int accu = 0;
03375   return findPartInternal( msg->getTopLevelPart(), index, accu );
03376 }
03377 
03378 DwBodyPart * AttachmentModifyCommand::findPartInternal(DwEntity * root, int index, int & accu)
03379 {
03380   accu++;
03381   if ( index < accu ) // should not happen
03382     return 0;
03383   DwBodyPart *current = dynamic_cast<DwBodyPart*>( root );
03384   if ( index == accu )
03385     return current;
03386   DwBodyPart *rv = 0;
03387   if ( root->Body().FirstBodyPart() )
03388     rv = findPartInternal( root->Body().FirstBodyPart(), index, accu );
03389   if ( !rv && current && current->Next() )
03390     rv = findPartInternal( current->Next(), index, accu );
03391   return rv;
03392 }
03393 
03394 
03395 KMDeleteAttachmentCommand::KMDeleteAttachmentCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03396     AttachmentModifyCommand( node, msg, parent )
03397 {
03398   kdDebug(5006) << k_funcinfo << endl;
03399 }
03400 
03401 KMDeleteAttachmentCommand::~KMDeleteAttachmentCommand()
03402 {
03403   kdDebug(5006) << k_funcinfo << endl;
03404 }
03405 
03406 KMCommand::Result KMDeleteAttachmentCommand::doAttachmentModify()
03407 {
03408   KMMessage *msg = retrievedMessage();
03409   KMMessagePart part;
03410   DwBodyPart *dwpart = findPart( msg, mPartIndex );
03411   if ( !dwpart )
03412     return Failed;
03413   KMMessage::bodyPart( dwpart, &part, true );
03414   if ( !part.isComplete() )
03415      return Failed;
03416 
03417   DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
03418   if ( !parentNode )
03419     return Failed;
03420   parentNode->RemoveBodyPart( dwpart );
03421 
03422   // add dummy part to show that a attachment has been deleted
03423   KMMessagePart dummyPart;
03424   dummyPart.duplicate( part );
03425   QString comment = i18n("This attachment has been deleted.");
03426   if ( !part.fileName().isEmpty() )
03427     comment = i18n( "The attachment '%1' has been deleted." ).arg( part.fileName() );
03428   dummyPart.setContentDescription( comment );
03429   dummyPart.setBodyEncodedBinary( QByteArray() );
03430   QCString cd = dummyPart.contentDisposition();
03431   if ( cd.find( "inline", 0, false ) == 0 ) {
03432     cd.replace( 0, 10, "attachment" );
03433     dummyPart.setContentDisposition( cd );
03434   } else if ( cd.isEmpty() ) {
03435     dummyPart.setContentDisposition( "attachment" );
03436   }
03437   DwBodyPart* newDwPart = msg->createDWBodyPart( &dummyPart );
03438   parentNode->AddBodyPart( newDwPart );
03439   msg->getTopLevelPart()->Assemble();
03440 
03441   KMMessage *newMsg = new KMMessage();
03442   newMsg->fromDwString( msg->asDwString() );
03443   newMsg->setStatus( msg->status() );
03444 
03445   storeChangedMessage( newMsg );
03446   return OK;
03447 }
03448 
03449 
03450 KMEditAttachmentCommand::KMEditAttachmentCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03451     AttachmentModifyCommand( node, msg, parent )
03452 {
03453   kdDebug(5006) << k_funcinfo << endl;
03454   mTempFile.setAutoDelete( true );
03455 }
03456 
03457 KMEditAttachmentCommand::~ KMEditAttachmentCommand()
03458 {
03459 }
03460 
03461 KMCommand::Result KMEditAttachmentCommand::doAttachmentModify()
03462 {
03463   KMMessage *msg = retrievedMessage();
03464   KMMessagePart part;
03465   DwBodyPart *dwpart = findPart( msg, mPartIndex );
03466   if ( !dwpart )
03467     return Failed;
03468   KMMessage::bodyPart( dwpart, &part, true );
03469   if ( !part.isComplete() )
03470      return Failed;
03471 
03472   if( !dynamic_cast<DwBody*>( dwpart->Parent() ) )
03473     return Failed;
03474 
03475   mTempFile.file()->writeBlock( part.bodyDecodedBinary() );
03476   mTempFile.file()->flush();
03477 
03478   KMail::EditorWatcher *watcher = new KMail::EditorWatcher( KURL(mTempFile.file()->name()), part.typeStr() + "/" + part.subtypeStr(), false, this );
03479   connect( watcher, SIGNAL(editDone(KMail::EditorWatcher*)), SLOT(editDone(KMail::EditorWatcher*)) );
03480   if ( !watcher->start() )
03481     return Failed;
03482   setEmitsCompletedItself( true );
03483   setDeletesItself( true );
03484   return OK;
03485 }
03486 
03487 void KMEditAttachmentCommand::editDone(KMail::EditorWatcher * watcher)
03488 {
03489   kdDebug(5006) << k_funcinfo << endl;
03490   // anything changed?
03491   if ( !watcher->fileChanged() ) {
03492     kdDebug(5006) << k_funcinfo << "File has not been changed" << endl;
03493     setResult( Canceled );
03494     emit completed( this );
03495     deleteLater();
03496   }
03497 
03498   mTempFile.file()->reset();
03499   QByteArray data = mTempFile.file()->readAll();
03500 
03501   // build the new message
03502   KMMessage *msg = retrievedMessage();
03503   KMMessagePart part;
03504   DwBodyPart *dwpart = findPart( msg, mPartIndex );
03505   KMMessage::bodyPart( dwpart, &part, true );
03506 
03507   DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
03508   assert( parentNode );
03509   parentNode->RemoveBodyPart( dwpart );
03510 
03511   KMMessagePart att;
03512   att.duplicate( part );
03513   att.setBodyEncodedBinary( data );
03514 
03515   DwBodyPart* newDwPart = msg->createDWBodyPart( &att );
03516   parentNode->AddBodyPart( newDwPart );
03517   msg->getTopLevelPart()->Assemble();
03518 
03519   KMMessage *newMsg = new KMMessage();
03520   newMsg->fromDwString( msg->asDwString() );
03521   newMsg->setStatus( msg->status() );
03522 
03523   storeChangedMessage( newMsg );
03524 }
03525 
03526 
03527 CreateTodoCommand::CreateTodoCommand(QWidget * parent, KMMessage * msg)
03528   : KMCommand( parent, msg )
03529 {
03530 }
03531 
03532 KMCommand::Result CreateTodoCommand::execute()
03533 {
03534   KMMessage *msg = retrievedMessage();
03535   if ( !msg || !msg->codec() ) {
03536     return Failed;
03537   }
03538 
03539   KMail::KorgHelper::ensureRunning();
03540 
03541   QString txt = i18n("From: %1\nTo: %2\nSubject: %3").arg( msg->from() )
03542                 .arg( msg->to() ).arg( msg->subject() );
03543 
03544   KTempFile tf;
03545   tf.setAutoDelete( true );
03546   QString uri = "kmail:" + QString::number( msg->getMsgSerNum() ) + "/" + msg->msgId();
03547   tf.file()->writeBlock( msg->asDwString().c_str(), msg->asDwString().length() );
03548   tf.close();
03549 
03550   KCalendarIface_stub *iface = new KCalendarIface_stub( kapp->dcopClient(), "korganizer", "CalendarIface" );
03551   iface->openTodoEditor( i18n("Mail: %1").arg( msg->subject() ), txt,
03552                          uri, tf.name(), QStringList(), "message/rfc822" );
03553   delete iface;
03554 
03555   return OK;
03556 }
03557 
03558 #include "kmcommands.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys