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->show();
01260     }
01261   }
01262   return OK;
01263 }
01264 
01265 
01266 KMForwardAttachedCommand::KMForwardAttachedCommand( QWidget *parent,
01267            const QPtrList<KMMsgBase> &msgList, uint identity, KMail::Composer *win )
01268   : KMCommand( parent, msgList ), mIdentity( identity ),
01269     mWin( QGuardedPtr<KMail::Composer>( win ))
01270 {
01271 }
01272 
01273 KMForwardAttachedCommand::KMForwardAttachedCommand( QWidget *parent,
01274            KMMessage * msg, uint identity, KMail::Composer *win )
01275   : KMCommand( parent, msg ), mIdentity( identity ),
01276     mWin( QGuardedPtr< KMail::Composer >( win ))
01277 {
01278 }
01279 
01280 KMCommand::Result KMForwardAttachedCommand::execute()
01281 {
01282   QPtrList<KMMessage> msgList = retrievedMsgs();
01283   KMMessage *fwdMsg = new KMMessage;
01284 
01285   if (msgList.count() >= 2) {
01286     // don't respect X-KMail-Identity headers because they might differ for
01287     // the selected mails
01288     fwdMsg->initHeader(mIdentity);
01289   }
01290   else if (msgList.count() == 1) {
01291     KMMessage *msg = msgList.getFirst();
01292     fwdMsg->initFromMessage(msg);
01293     fwdMsg->setSubject( msg->forwardSubject() );
01294   }
01295 
01296   fwdMsg->setAutomaticFields(true);
01297 
01298   KCursorSaver busy(KBusyPtr::busy());
01299   if (!mWin)
01300     mWin = KMail::makeComposer(fwdMsg, mIdentity);
01301 
01302   // iterate through all the messages to be forwarded
01303   for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) {
01304     // remove headers that shouldn't be forwarded
01305     msg->removePrivateHeaderFields();
01306     msg->removeHeaderField("BCC");
01307     // set the part
01308     KMMessagePart *msgPart = new KMMessagePart;
01309     msgPart->setTypeStr("message");
01310     msgPart->setSubtypeStr("rfc822");
01311     msgPart->setCharset(msg->charset());
01312     msgPart->setName("forwarded message");
01313     msgPart->setContentDescription(msg->from()+": "+msg->subject());
01314     msgPart->setContentDisposition( "inline" );
01315     // THIS HAS TO BE AFTER setCte()!!!!
01316     msgPart->setMessageBody( KMail::Util::ByteArray( msg->asDwString() ) );
01317     msgPart->setCharset("");
01318 
01319     fwdMsg->link(msg, KMMsgStatusForwarded);
01320     mWin->addAttach(msgPart);
01321   }
01322 
01323   mWin->show();
01324 
01325   return OK;
01326 }
01327 
01328 
01329 KMForwardDigestCommand::KMForwardDigestCommand( QWidget *parent,
01330            const QPtrList<KMMsgBase> &msgList, uint identity, KMail::Composer *win )
01331   : KMCommand( parent, msgList ), mIdentity( identity ),
01332     mWin( QGuardedPtr<KMail::Composer>( win ))
01333 {
01334 }
01335 
01336 KMForwardDigestCommand::KMForwardDigestCommand( QWidget *parent,
01337            KMMessage * msg, uint identity, KMail::Composer *win )
01338   : KMCommand( parent, msg ), mIdentity( identity ),
01339     mWin( QGuardedPtr< KMail::Composer >( win ))
01340 {
01341 }
01342 
01343 KMCommand::Result KMForwardDigestCommand::execute()
01344 {
01345   QPtrList<KMMessage> msgList = retrievedMsgs();
01346 
01347   if ( msgList.count() < 2 )
01348     return Undefined; // must have more than 1 for a digest
01349 
01350   uint id = 0;
01351   KMMessage *fwdMsg = new KMMessage;
01352   KMMessagePart *msgPart = new KMMessagePart;
01353   QString msgPartText;
01354   int msgCnt = 0; // incase there are some we can't forward for some reason
01355 
01356   // dummy header initialization; initialization with the correct identity
01357   // is done below
01358   fwdMsg->initHeader( id );
01359   fwdMsg->setAutomaticFields( true );
01360   fwdMsg->mMsg->Headers().ContentType().CreateBoundary( 1 );
01361   QCString boundary( fwdMsg->mMsg->Headers().ContentType().Boundary().c_str() );
01362   msgPartText = i18n("\nThis is a MIME digest forward. The content of the"
01363                      " message is contained in the attachment(s).\n\n\n");
01364   // iterate through all the messages to be forwarded
01365   for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
01366     // set the identity
01367     if ( id == 0 )
01368       id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
01369     // set the part header
01370     msgPartText += "--";
01371     msgPartText += QString::fromLatin1( boundary );
01372     msgPartText += "\nContent-Type: MESSAGE/RFC822";
01373     msgPartText += QString( "; CHARSET=%1" ).arg( msg->charset() );
01374     msgPartText += '\n';
01375     DwHeaders dwh;
01376     dwh.MessageId().CreateDefault();
01377     msgPartText += QString( "Content-ID: %1\n" ).arg( dwh.MessageId().AsString().c_str() );
01378     msgPartText += QString( "Content-Description: %1" ).arg( msg->subject() );
01379     if ( !msg->subject().contains( "(fwd)" ) )
01380       msgPartText += " (fwd)";
01381     msgPartText += "\n\n";
01382     // remove headers that shouldn't be forwarded
01383     msg->removePrivateHeaderFields();
01384     msg->removeHeaderField( "BCC" );
01385     // set the part
01386     msgPartText += msg->headerAsString();
01387     msgPartText += '\n';
01388     msgPartText += msg->body();
01389     msgPartText += '\n';     // eot
01390     msgCnt++;
01391     fwdMsg->link( msg, KMMsgStatusForwarded );
01392   }
01393 
01394   if ( id == 0 )
01395     id = mIdentity; // use folder identity if no message had an id set
01396   fwdMsg->initHeader( id );
01397   msgPartText += "--";
01398   msgPartText += QString::fromLatin1( boundary );
01399   msgPartText += "--\n";
01400   QCString tmp;
01401   msgPart->setTypeStr( "MULTIPART" );
01402   tmp.sprintf( "Digest; boundary=\"%s\"", boundary.data() );
01403   msgPart->setSubtypeStr( tmp );
01404   msgPart->setName( "unnamed" );
01405   msgPart->setCte( DwMime::kCte7bit );   // does it have to be 7bit?
01406   msgPart->setContentDescription( QString( "Digest of %1 messages." ).arg( msgCnt ) );
01407   // THIS HAS TO BE AFTER setCte()!!!!
01408   msgPart->setBodyEncoded( QCString( msgPartText.ascii() ) );
01409   KCursorSaver busy( KBusyPtr::busy() );
01410   KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
01411   win->addAttach( msgPart );
01412   win->show();
01413   return OK;
01414 }
01415 
01416 KMRedirectCommand::KMRedirectCommand( QWidget *parent,
01417                                       KMMessage *msg )
01418   : KMCommand( parent, msg )
01419 {
01420 }
01421 
01422 KMCommand::Result KMRedirectCommand::execute()
01423 {
01424   KMMessage *msg = retrievedMessage();
01425   if ( !msg || !msg->codec() )
01426     return Failed;
01427 
01428   RedirectDialog dlg( parentWidget(), "redirect", true,
01429                       kmkernel->msgSender()->sendImmediate() );
01430   if (dlg.exec()==QDialog::Rejected) return Failed;
01431 
01432   KMMessage *newMsg = msg->createRedirect( dlg.to() );
01433   KMFilterAction::sendMDN( msg, KMime::MDN::Dispatched );
01434 
01435   const KMail::MessageSender::SendMethod method = dlg.sendImmediate()
01436     ? KMail::MessageSender::SendImmediate
01437     : KMail::MessageSender::SendLater;
01438   if ( !kmkernel->msgSender()->send( newMsg, method ) ) {
01439     kdDebug(5006) << "KMRedirectCommand: could not redirect message (sending failed)" << endl;
01440     return Failed; // error: couldn't send
01441   }
01442   return OK;
01443 }
01444 
01445 
01446 KMCustomReplyToCommand::KMCustomReplyToCommand( QWidget *parent, KMMessage *msg,
01447                                                 const QString &selection,
01448                                                 const QString &tmpl )
01449   : KMCommand( parent, msg ), mSelection( selection ), mTemplate( tmpl )
01450 {
01451 }
01452 
01453 KMCommand::Result KMCustomReplyToCommand::execute()
01454 {
01455   KCursorSaver busy(KBusyPtr::busy());
01456   KMMessage *msg = retrievedMessage();
01457   if ( !msg || !msg->codec() ) {
01458     return Failed;
01459   }
01460   KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection,
01461                                        false, true, false, mTemplate );
01462   KMail::Composer * win = KMail::makeComposer( reply );
01463   win->setCharset( msg->codec()->mimeName(), true );
01464   win->setReplyFocus();
01465   win->show();
01466 
01467   return OK;
01468 }
01469 
01470 
01471 KMCustomReplyAllToCommand::KMCustomReplyAllToCommand( QWidget *parent, KMMessage *msg,
01472                                                       const QString &selection,
01473                                                       const QString &tmpl )
01474   : KMCommand( parent, msg ), mSelection( selection ), mTemplate( tmpl )
01475 {
01476 }
01477 
01478 KMCommand::Result KMCustomReplyAllToCommand::execute()
01479 {
01480   KCursorSaver busy(KBusyPtr::busy());
01481   KMMessage *msg = retrievedMessage();
01482   if ( !msg || !msg->codec() ) {
01483     return Failed;
01484   }
01485   KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection,
01486                                        false, true, false, mTemplate );
01487   KMail::Composer * win = KMail::makeComposer( reply );
01488   win->setCharset( msg->codec()->mimeName(), true );
01489   win->setReplyFocus();
01490   win->show();
01491 
01492   return OK;
01493 }
01494 
01495 
01496 KMCustomForwardCommand::KMCustomForwardCommand( QWidget *parent,
01497   const QPtrList<KMMsgBase> &msgList, uint identity, const QString &tmpl )
01498   : KMCommand( parent, msgList ),
01499     mIdentity( identity ), mTemplate( tmpl )
01500 {
01501 }
01502 
01503 KMCustomForwardCommand::KMCustomForwardCommand( QWidget *parent,
01504   KMMessage *msg, uint identity, const QString &tmpl )
01505   : KMCommand( parent, msg ),
01506     mIdentity( identity ), mTemplate( tmpl )
01507 {
01508 }
01509 
01510 KMCommand::Result KMCustomForwardCommand::execute()
01511 {
01512   QPtrList<KMMessage> msgList = retrievedMsgs();
01513 
01514   if (msgList.count() >= 2) { // Multiple forward
01515 
01516     uint id = 0;
01517     QPtrList<KMMessage> linklist;
01518     for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
01519       // set the identity
01520       if (id == 0)
01521         id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
01522 
01523       // msgText += msg->createForwardBody();
01524       linklist.append( msg );
01525     }
01526     if ( id == 0 )
01527       id = mIdentity; // use folder identity if no message had an id set
01528     KMMessage *fwdMsg = new KMMessage;
01529     fwdMsg->initHeader( id );
01530     fwdMsg->setAutomaticFields( true );
01531     fwdMsg->setCharset( "utf-8" );
01532     // fwdMsg->setBody( msgText );
01533 
01534     for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) {
01535       TemplateParser parser( fwdMsg, TemplateParser::Forward,
01536         msg->body(), false, false, false, false);
01537         parser.process( msg, 0, true );
01538 
01539       fwdMsg->link( msg, KMMsgStatusForwarded );
01540     }
01541 
01542     KCursorSaver busy( KBusyPtr::busy() );
01543     KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
01544     win->setCharset("");
01545     win->show();
01546 
01547   } else { // forward a single message at most
01548 
01549     KMMessage *msg = msgList.getFirst();
01550     if ( !msg || !msg->codec() )
01551       return Failed;
01552 
01553     KCursorSaver busy( KBusyPtr::busy() );
01554     KMMessage *fwdMsg = msg->createForward( mTemplate );
01555 
01556     uint id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
01557     if ( id == 0 )
01558       id = mIdentity;
01559     {
01560       KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
01561       win->setCharset( fwdMsg->codec()->mimeName(), true );
01562       win->show();
01563     }
01564   }
01565   return OK;
01566 }
01567 
01568 
01569 KMPrintCommand::KMPrintCommand( QWidget *parent,
01570   KMMessage *msg, bool htmlOverride, bool htmlLoadExtOverride,
01571   bool useFixedFont, const QString & encoding )
01572   : KMCommand( parent, msg ), mHtmlOverride( htmlOverride ),
01573     mHtmlLoadExtOverride( htmlLoadExtOverride ),
01574     mUseFixedFont( useFixedFont ), mEncoding( encoding )
01575 {
01576   mOverrideFont = KGlobalSettings::generalFont();
01577 }
01578 
01579 
01580 void KMPrintCommand::setOverrideFont( const QFont& font )
01581 {
01582   mOverrideFont = font;
01583 }
01584 
01585 KMCommand::Result KMPrintCommand::execute()
01586 {
01587   KMReaderWin printWin( 0, 0, 0 );
01588   printWin.setPrinting( true );
01589   printWin.readConfig();
01590   printWin.setHtmlOverride( mHtmlOverride );
01591   printWin.setHtmlLoadExtOverride( mHtmlLoadExtOverride );
01592   printWin.setUseFixedFont( mUseFixedFont );
01593   printWin.setOverrideEncoding( mEncoding );
01594   printWin.setPrintFont( mOverrideFont );
01595   printWin.setDecryptMessageOverwrite( true );
01596   printWin.setMsg( retrievedMessage(), true );
01597   printWin.printMsg();
01598 
01599   return OK;
01600 }
01601 
01602 
01603 KMSetStatusCommand::KMSetStatusCommand( KMMsgStatus status,
01604   const QValueList<Q_UINT32> &serNums, bool toggle )
01605   : mStatus( status ), mSerNums( serNums ), mToggle( toggle )
01606 {
01607 }
01608 
01609 KMCommand::Result KMSetStatusCommand::execute()
01610 {
01611   QValueListIterator<Q_UINT32> it;
01612   int idx = -1;
01613   KMFolder *folder = 0;
01614   bool parentStatus = false;
01615 
01616   // Toggle actions on threads toggle the whole thread
01617   // depending on the state of the parent.
01618   if (mToggle) {
01619     KMMsgBase *msg;
01620     KMMsgDict::instance()->getLocation( *mSerNums.begin(), &folder, &idx );
01621     if (folder) {
01622       msg = folder->getMsgBase(idx);
01623       if (msg && (msg->status()&mStatus))
01624         parentStatus = true;
01625       else
01626         parentStatus = false;
01627     }
01628   }
01629   QMap< KMFolder*, QValueList<int> > folderMap;
01630   for ( it = mSerNums.begin(); it != mSerNums.end(); ++it ) {
01631     KMMsgDict::instance()->getLocation( *it, &folder, &idx );
01632     if (folder) {
01633       if (mToggle) {
01634         KMMsgBase *msg = folder->getMsgBase(idx);
01635         // check if we are already at the target toggle state
01636         if (msg) {
01637           bool myStatus;
01638           if (msg->status()&mStatus)
01639             myStatus = true;
01640           else
01641             myStatus = false;
01642           if (myStatus != parentStatus)
01643             continue;
01644         }
01645       }
01646       /* Collect the ids for each folder in a separate list and
01647          send them off in one go at the end. */
01648       folderMap[folder].append(idx);
01649     }
01650   }
01651   QMapIterator< KMFolder*, QValueList<int> > it2 = folderMap.begin();
01652   while ( it2 != folderMap.end() ) {
01653      KMFolder *f = it2.key();
01654      f->setStatus( (*it2), mStatus, mToggle );
01655      ++it2;
01656   }
01657   //kapp->dcopClient()->emitDCOPSignal( "unreadCountChanged()", QByteArray() );
01658 
01659   return OK;
01660 }
01661 
01662 
01663 KMFilterCommand::KMFilterCommand( const QCString &field, const QString &value )
01664   : mField( field ), mValue( value )
01665 {
01666 }
01667 
01668 KMCommand::Result KMFilterCommand::execute()
01669 {
01670   kmkernel->filterMgr()->createFilter( mField, mValue );
01671 
01672   return OK;
01673 }
01674 
01675 
01676 KMFilterActionCommand::KMFilterActionCommand( QWidget *parent,
01677                                               const QPtrList<KMMsgBase> &msgList,
01678                                               KMFilter *filter )
01679   : KMCommand( parent, msgList ), mFilter( filter  )
01680 {
01681   QPtrListIterator<KMMsgBase> it(msgList);
01682   while ( it.current() ) {
01683     serNumList.append( (*it)->getMsgSerNum() );
01684     ++it;
01685   }
01686 }
01687 
01688 KMCommand::Result KMFilterActionCommand::execute()
01689 {
01690   KCursorSaver busy( KBusyPtr::busy() );
01691 
01692   int msgCount = 0;
01693   int msgCountToFilter = serNumList.count();
01694   ProgressItem* progressItem =
01695     ProgressManager::createProgressItem ( "filter"+ProgressManager::getUniqueID(),
01696                                           i18n( "Filtering messages" ) );
01697   progressItem->setTotalItems( msgCountToFilter );
01698   QValueList<Q_UINT32>::const_iterator it;
01699   for ( it = serNumList.begin(); it != serNumList.end(); it++ ) {
01700     Q_UINT32 serNum = *it;
01701     int diff = msgCountToFilter - ++msgCount;
01702     if ( diff < 10 || !( msgCount % 20 ) || msgCount <= 10 ) {
01703       progressItem->updateProgress();
01704       QString statusMsg = i18n("Filtering message %1 of %2");
01705       statusMsg = statusMsg.arg( msgCount ).arg( msgCountToFilter );
01706       KPIM::BroadcastStatus::instance()->setStatusMsg( statusMsg );
01707       KApplication::kApplication()->eventLoop()->processEvents( QEventLoop::ExcludeUserInput, 50 );
01708     }
01709 
01710     int filterResult = kmkernel->filterMgr()->process( serNum, mFilter );
01711     if (filterResult == 2) {
01712       // something went horribly wrong (out of space?)
01713       perror("Critical error");
01714       kmkernel->emergencyExit( i18n("Not enough free disk space?" ));
01715     }
01716     progressItem->incCompletedItems();
01717   }
01718 
01719   progressItem->setComplete();
01720   progressItem = 0;
01721   return OK;
01722 }
01723 
01724 
01725 KMMetaFilterActionCommand::KMMetaFilterActionCommand( KMFilter *filter,
01726                                                       KMHeaders *headers,
01727                                                       KMMainWidget *main )
01728     : QObject( main ),
01729       mFilter( filter ), mHeaders( headers ), mMainWidget( main )
01730 {
01731 }
01732 
01733 void KMMetaFilterActionCommand::start()
01734 {
01735   if (ActionScheduler::isEnabled() ) {
01736     // use action scheduler
01737     KMFilterMgr::FilterSet set = KMFilterMgr::All;
01738     QValueList<KMFilter*> filters;
01739     filters.append( mFilter );
01740     ActionScheduler *scheduler = new ActionScheduler( set, filters, mHeaders );
01741     scheduler->setAlwaysMatch( true );
01742     scheduler->setAutoDestruct( true );
01743 
01744     int contentX, contentY;
01745     HeaderItem *nextItem = mHeaders->prepareMove( &contentX, &contentY );
01746     QPtrList<KMMsgBase> msgList = *mHeaders->selectedMsgs(true);
01747     mHeaders->finalizeMove( nextItem, contentX, contentY );
01748 
01749     for (KMMsgBase *msg = msgList.first(); msg; msg = msgList.next())
01750       scheduler->execFilters( msg );
01751   } else {
01752     KMCommand *filterCommand =
01753       new KMFilterActionCommand( mMainWidget,
01754                                  *mHeaders->selectedMsgs(), mFilter );
01755     filterCommand->start();
01756     int contentX, contentY;
01757     HeaderItem *item = mHeaders->prepareMove( &contentX, &contentY );
01758     mHeaders->finalizeMove( item, contentX, contentY );
01759   }
01760 }
01761 
01762 FolderShortcutCommand::FolderShortcutCommand( KMMainWidget *mainwidget,
01763                                               KMFolder *folder )
01764     : mMainWidget( mainwidget ), mFolder( folder ), mAction( 0 )
01765 {
01766 }
01767 
01768 
01769 FolderShortcutCommand::~FolderShortcutCommand()
01770 {
01771   if ( mAction ) mAction->unplugAll();
01772   delete mAction;
01773 }
01774 
01775 void FolderShortcutCommand::start()
01776 {
01777   mMainWidget->slotSelectFolder( mFolder );
01778 }
01779 
01780 void FolderShortcutCommand::setAction( KAction* action )
01781 {
01782   mAction = action;
01783 }
01784 
01785 KMMailingListFilterCommand::KMMailingListFilterCommand( QWidget *parent,
01786                                                         KMMessage *msg )
01787   : KMCommand( parent, msg )
01788 {
01789 }
01790 
01791 KMCommand::Result KMMailingListFilterCommand::execute()
01792 {
01793   QCString name;
01794   QString value;
01795   KMMessage *msg = retrievedMessage();
01796   if (!msg)
01797     return Failed;
01798 
01799   if ( !MailingList::name( msg, name, value ).isEmpty() ) {
01800     kmkernel->filterMgr()->createFilter( name, value );
01801     return OK;
01802   }
01803   else
01804     return Failed;
01805 }
01806 
01807 
01808 void KMMenuCommand::folderToPopupMenu(bool move,
01809   QObject *receiver, KMMenuToFolder *aMenuToFolder, QPopupMenu *menu )
01810 {
01811   while ( menu->count() )
01812   {
01813     QPopupMenu *popup = menu->findItem( menu->idAt( 0 ) )->popup();
01814     if (popup)
01815       delete popup;
01816     else
01817       menu->removeItemAt( 0 );
01818   }
01819 
01820   if (!kmkernel->imapFolderMgr()->dir().first() &&
01821       !kmkernel->dimapFolderMgr()->dir().first())
01822   { // only local folders
01823     makeFolderMenu( &kmkernel->folderMgr()->dir(), move,
01824                     receiver, aMenuToFolder, menu );
01825   } else {
01826     // operate on top-level items
01827     QPopupMenu* subMenu = new QPopupMenu(menu);
01828     makeFolderMenu( &kmkernel->folderMgr()->dir(),
01829                     move, receiver, aMenuToFolder, subMenu );
01830     menu->insertItem( i18n( "Local Folders" ), subMenu );
01831     KMFolderDir* fdir = &kmkernel->imapFolderMgr()->dir();
01832     for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) {
01833       if (node->isDir())
01834         continue;
01835       subMenu = new QPopupMenu(menu);
01836       makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu );
01837       menu->insertItem( node->label(), subMenu );
01838     }
01839     fdir = &kmkernel->dimapFolderMgr()->dir();
01840     for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) {
01841       if (node->isDir())
01842         continue;
01843       subMenu = new QPopupMenu(menu);
01844       makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu );
01845       menu->insertItem( node->label(), subMenu );
01846     }
01847   }
01848 }
01849 
01850 void KMMenuCommand::makeFolderMenu(KMFolderNode* node, bool move,
01851   QObject *receiver, KMMenuToFolder *aMenuToFolder, QPopupMenu *menu )
01852 {
01853   // connect the signals
01854   if (move)
01855   {
01856     disconnect(menu, SIGNAL(activated(int)), receiver,
01857            SLOT(moveSelectedToFolder(int)));
01858     connect(menu, SIGNAL(activated(int)), receiver,
01859              SLOT(moveSelectedToFolder(int)));
01860   } else {
01861     disconnect(menu, SIGNAL(activated(int)), receiver,
01862            SLOT(copySelectedToFolder(int)));
01863     connect(menu, SIGNAL(activated(int)), receiver,
01864              SLOT(copySelectedToFolder(int)));
01865   }
01866 
01867   KMFolder *folder = 0;
01868   KMFolderDir *folderDir = 0;
01869   if (node->isDir()) {
01870     folderDir = static_cast<KMFolderDir*>(node);
01871   } else {
01872     folder = static_cast<KMFolder*>(node);
01873     folderDir = folder->child();
01874   }
01875 
01876   if (folder && !folder->noContent())
01877   {
01878     int menuId;
01879     if (move)
01880       menuId = menu->insertItem(i18n("Move to This Folder"));
01881     else
01882       menuId = menu->insertItem(i18n("Copy to This Folder"));
01883     aMenuToFolder->insert( menuId, folder );
01884     menu->setItemEnabled( menuId, !folder->isReadOnly() );
01885     menu->insertSeparator();
01886   }
01887 
01888   if (!folderDir)
01889     return;
01890 
01891   for (KMFolderNode *it = folderDir->first(); it; it = folderDir->next() ) {
01892     if (it->isDir())
01893       continue;
01894     KMFolder *child = static_cast<KMFolder*>(it);
01895     QString label = child->label();
01896     label.replace("&","&&");
01897     if (child->child() && child->child()->first()) {
01898       // descend
01899       QPopupMenu *subMenu = new QPopupMenu(menu, "subMenu");
01900       makeFolderMenu( child, move, receiver,
01901                       aMenuToFolder, subMenu );
01902       menu->insertItem( label, subMenu );
01903     } else {
01904       // insert an item
01905       int menuId = menu->insertItem( label );
01906       aMenuToFolder->insert( menuId, child );
01907       menu->setItemEnabled( menuId, !child->isReadOnly() );
01908     }
01909   }
01910   return;
01911 }
01912 
01913 
01914 KMCopyCommand::KMCopyCommand( KMFolder* destFolder,
01915                               const QPtrList<KMMsgBase> &msgList )
01916 :mDestFolder( destFolder ), mMsgList( msgList )
01917 {
01918   setDeletesItself( true );
01919 }
01920 
01921 KMCopyCommand::KMCopyCommand( KMFolder* destFolder, KMMessage * msg )
01922   :mDestFolder( destFolder )
01923 {
01924   setDeletesItself( true );
01925   mMsgList.append( &msg->toMsgBase() );
01926 }
01927 
01928 KMCommand::Result KMCopyCommand::execute()
01929 {
01930   KMMsgBase *msgBase;
01931   KMMessage *msg, *newMsg;
01932   int idx = -1;
01933   bool isMessage;
01934   QPtrList<KMMessage> list;
01935   QPtrList<KMMessage> localList;
01936 
01937   if (mDestFolder && mDestFolder->open("kmcommand") != 0)
01938   {
01939     deleteLater();
01940     return Failed;
01941   }
01942 
01943   setEmitsCompletedItself( true );
01944   KCursorSaver busy(KBusyPtr::busy());
01945 
01946   for (msgBase = mMsgList.first(); msgBase; msgBase = mMsgList.next() )
01947   {
01948     KMFolder *srcFolder = msgBase->parent();
01949     if (( isMessage = msgBase->isMessage() ))
01950     {
01951       msg = static_cast<KMMessage*>(msgBase);
01952     } else {
01953       idx = srcFolder->find(msgBase);
01954       assert(idx != -1);
01955       msg = srcFolder->getMsg(idx);
01956       // corrupt IMAP cache, see FolderStorage::getMsg()
01957       if ( msg == 0 ) {
01958         KMessageBox::error( parentWidget(), i18n("Corrupt IMAP cache detected in folder %1. "
01959             "Copying of messages aborted.").arg( srcFolder->prettyURL() ) );
01960         deleteLater();
01961         return Failed;
01962       }
01963     }
01964 
01965     if (srcFolder && mDestFolder &&
01966         (srcFolder->folderType()== KMFolderTypeImap) &&
01967         (mDestFolder->folderType() == KMFolderTypeImap) &&
01968         (static_cast<KMFolderImap*>(srcFolder->storage())->account() ==
01969          static_cast<KMFolderImap*>(mDestFolder->storage())->account()))
01970     {
01971       // imap => imap with same account
01972       list.append(msg);
01973     } else {
01974       newMsg = new KMMessage( new DwMessage( *msg->asDwMessage() ) );
01975       newMsg->setComplete(msg->isComplete());
01976       // make sure the attachment state is only calculated when it's complete
01977       if (!newMsg->isComplete())
01978         newMsg->setReadyToShow(false);
01979       newMsg->setStatus(msg->status());
01980 
01981       if (srcFolder && !newMsg->isComplete())
01982       {
01983         // imap => others
01984         newMsg->setParent(msg->parent());
01985         FolderJob *job = srcFolder->createJob(newMsg);
01986         job->setCancellable( false );
01987         mPendingJobs << job;
01988         connect(job, SIGNAL(messageRetrieved(KMMessage*)),
01989                 mDestFolder, SLOT(reallyAddCopyOfMsg(KMMessage*)));
01990         connect( job, SIGNAL(result(KMail::FolderJob*)),
01991                  this, SLOT(slotJobFinished(KMail::FolderJob*)) );
01992         job->start();
01993       } else {
01994         // local => others
01995         localList.append(newMsg);
01996       }
01997     }
01998 
01999     if (srcFolder && !isMessage && list.isEmpty())
02000     {
02001       assert(idx != -1);
02002       srcFolder->unGetMsg( idx );
02003     }
02004 
02005   } // end for
02006 
02007   bool deleteNow = false;
02008   if (!localList.isEmpty())
02009   {
02010     QValueList<int> index;
02011     mDestFolder->addMsg( localList, index );
02012     for ( QValueListIterator<int> it = index.begin(); it != index.end(); ++it ) {
02013       mDestFolder->unGetMsg( *it );
02014     }
02015     if ( mDestFolder->folderType() == KMFolderTypeImap ) {
02016       if ( mPendingJobs.isEmpty() ) {
02017         // wait for the end of the copy before closing the folder
02018         KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
02019         connect( imapDestFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
02020             this, SLOT( slotFolderComplete( KMFolderImap*, bool ) ) );
02021       }
02022     } else {
02023       deleteNow = list.isEmpty() && mPendingJobs.isEmpty(); // we're done if there are no other mails we need to fetch
02024     }
02025   }
02026 
02027 //TODO: Get rid of the other cases just use this one for all types of folder
02028 //TODO: requires adding copyMsg and getFolder methods to KMFolder.h
02029   if (!list.isEmpty())
02030   {
02031     // copy the message(s); note: the list is empty afterwards!
02032     KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
02033     connect( imapDestFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
02034         this, SLOT( slotFolderComplete( KMFolderImap*, bool ) ) );
02035     imapDestFolder->copyMsg(list);
02036     imapDestFolder->getFolder();
02037   }
02038 
02039   // only close the folder and delete the job if we're done
02040   // otherwise this is done in slotMsgAdded or slotFolderComplete
02041   if ( deleteNow )
02042   {
02043     mDestFolder->close("kmcommand");
02044     setResult( OK );
02045     emit completed( this );
02046     deleteLater();
02047   }
02048 
02049   return OK;
02050 }
02051 
02052 void KMCopyCommand::slotJobFinished(KMail::FolderJob * job)
02053 {
02054   mPendingJobs.remove( job );
02055   if ( job->error() ) {
02056     kdDebug(5006) << k_funcinfo << "folder job failed: " << job->error() << endl;
02057     // kill all pending jobs
02058     for ( QValueList<KMail::FolderJob*>::Iterator it = mPendingJobs.begin(); it != mPendingJobs.end(); ++it ) {
02059       disconnect( (*it), SIGNAL(result(KMail::FolderJob*)),
02060                   this, SLOT(slotJobFinished(KMail::FolderJob*)) );
02061       (*it)->kill();
02062     }
02063     mPendingJobs.clear();
02064     setResult( Failed );
02065   }
02066 
02067   if ( mPendingJobs.isEmpty() )
02068   {
02069     mDestFolder->close("kmcommand");
02070     emit completed( this );
02071     deleteLater();
02072   }
02073 }
02074 
02075 void KMCopyCommand::slotFolderComplete( KMFolderImap*, bool success )
02076 {
02077   kdDebug(5006) << k_funcinfo << success << endl;
02078   if ( !success )
02079     setResult( Failed );
02080   mDestFolder->close( "kmcommand" );
02081   emit completed( this );
02082   deleteLater();
02083 }
02084 
02085 
02086 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
02087                               const QPtrList<KMMsgBase> &msgList)
02088   : mDestFolder( destFolder ), mProgressItem( 0 )
02089 {
02090   QPtrList<KMMsgBase> tmp = msgList;
02091   for ( KMMsgBase *msgBase = tmp.first(); msgBase; msgBase = tmp.next() )
02092     mSerNumList.append( msgBase->getMsgSerNum() );
02093 }
02094 
02095 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
02096                               KMMessage *msg )
02097   : mDestFolder( destFolder ), mProgressItem( 0 )
02098 {
02099   mSerNumList.append( msg->getMsgSerNum() );
02100 }
02101 
02102 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
02103                               KMMsgBase *msgBase )
02104   : mDestFolder( destFolder ), mProgressItem( 0 )
02105 {
02106   mSerNumList.append( msgBase->getMsgSerNum() );
02107 }
02108 
02109 KMMoveCommand::KMMoveCommand( Q_UINT32 )
02110   : mProgressItem( 0 )
02111 {
02112 }
02113 
02114 KMCommand::Result KMMoveCommand::execute()
02115 {
02116   setEmitsCompletedItself( true );
02117   setDeletesItself( true );
02118   typedef QMap< KMFolder*, QPtrList<KMMessage>* > FolderToMessageListMap;
02119   FolderToMessageListMap folderDeleteList;
02120 
02121   if (mDestFolder && mDestFolder->open("kmcommand") != 0) {
02122     completeMove( Failed );
02123     return Failed;
02124   }
02125   KCursorSaver busy(KBusyPtr::busy());
02126 
02127   // TODO set SSL state according to source and destfolder connection?
02128   Q_ASSERT( !mProgressItem );
02129   mProgressItem =
02130      ProgressManager::createProgressItem (
02131          "move"+ProgressManager::getUniqueID(),
02132          mDestFolder ? i18n( "Moving messages" ) : i18n( "Deleting messages" ) );
02133   connect( mProgressItem, SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
02134            this, SLOT( slotMoveCanceled() ) );
02135 
02136   KMMessage *msg;
02137   int rc = 0;
02138   int index;
02139   QPtrList<KMMessage> list;
02140   int undoId = -1;
02141   mCompleteWithAddedMsg = false;
02142 
02143   if (mDestFolder) {
02144     connect (mDestFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
02145              this, SLOT(slotMsgAddedToDestFolder(KMFolder*, Q_UINT32)));
02146     mLostBoys = mSerNumList;
02147   }
02148   mProgressItem->setTotalItems( mSerNumList.count() );
02149 
02150   for ( QValueList<Q_UINT32>::ConstIterator it = mSerNumList.constBegin(); it != mSerNumList.constEnd(); ++it ) {
02151     if ( *it == 0 ) {
02152       kdDebug(5006) << k_funcinfo << "serial number == 0!" << endl;
02153       continue; // invalid message
02154     }
02155     KMFolder *srcFolder = 0;
02156     int idx = -1;
02157     KMMsgDict::instance()->getLocation( *it, &srcFolder, &idx );
02158     if (srcFolder == mDestFolder)
02159       continue;
02160     assert(srcFolder);
02161     assert(idx != -1);
02162     if ( !srcFolder->isOpened() ) {
02163       srcFolder->open( "kmmovecommand" );
02164       mOpenedFolders.append( srcFolder );
02165     }
02166     msg = srcFolder->getMsg(idx);
02167     if ( !msg ) {
02168       kdDebug(5006) << k_funcinfo << "No message found for serial number " << *it << endl;
02169       continue;
02170     }
02171     bool undo = msg->enableUndo();
02172 
02173     if ( msg && msg->transferInProgress() &&
02174          srcFolder->folderType() == KMFolderTypeImap )
02175     {
02176       // cancel the download
02177       msg->setTransferInProgress( false, true );
02178       static_cast<KMFolderImap*>(srcFolder->storage())->ignoreJobsForMessage( msg );
02179     }
02180 
02181     if (mDestFolder) {
02182       if (mDestFolder->folderType() == KMFolderTypeImap) {
02183         /* If we are moving to an imap folder, connect to it's completed
02184          * signal so we notice when all the mails should have showed up in it
02185          * but haven't for some reason. */
02186         KMFolderImap *imapFolder = static_cast<KMFolderImap*> ( mDestFolder->storage() );
02187         disconnect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )),
02188                  this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
02189 
02190         connect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )),
02191                  this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
02192         list.append(msg);
02193       } else {
02194         // We are moving to a local folder.
02195         if ( srcFolder->folderType() == KMFolderTypeImap )
02196         {
02197           // do not complete here but wait until all messages are transferred
02198           mCompleteWithAddedMsg = true;
02199         }
02200         rc = mDestFolder->moveMsg(msg, &index);
02201         if (rc == 0 && index != -1) {
02202           KMMsgBase *mb = mDestFolder->unGetMsg( mDestFolder->count() - 1 );
02203           if (undo && mb)
02204           {
02205             if ( undoId == -1 )
02206               undoId = kmkernel->undoStack()->newUndoAction( srcFolder, mDestFolder );
02207             kmkernel->undoStack()->addMsgToAction( undoId, mb->getMsgSerNum() );
02208           }
02209         } else if (rc != 0) {
02210           // Something  went wrong. Stop processing here, it is likely that the
02211           // other moves would fail as well.
02212           completeMove( Failed );
02213           return Failed;
02214         }
02215       }
02216     } else {
02217       // really delete messages that are already in the trash folder or if
02218       // we are really, really deleting, not just moving to trash
02219       if (srcFolder->folderType() == KMFolderTypeImap) {
02220         if (!folderDeleteList[srcFolder])
02221           folderDeleteList[srcFolder] = new QPtrList<KMMessage>;
02222         folderDeleteList[srcFolder]->append( msg );
02223       } else {
02224         srcFolder->removeMsg(idx);
02225         delete msg;
02226       }
02227     }
02228   }
02229   if (!list.isEmpty() && mDestFolder) {
02230     // will be completed with folderComplete signal
02231     mDestFolder->moveMsg(list, &index);
02232   } else {
02233     FolderToMessageListMap::Iterator it;
02234     for ( it = folderDeleteList.begin(); it != folderDeleteList.end(); ++it ) {
02235       it.key()->removeMsg(*it.data());
02236       delete it.data();
02237     }
02238     if ( !mCompleteWithAddedMsg ) {
02239       // imap folders will be completed in slotMsgAddedToDestFolder
02240       completeMove( OK );
02241     }
02242   }
02243 
02244   return OK;
02245 }
02246 
02247 void KMMoveCommand::slotImapFolderCompleted(KMFolderImap* imapFolder, bool success)
02248 {
02249   disconnect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )),
02250       this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
02251   if ( success ) {
02252     // the folder was checked successfully but we were still called, so check
02253     // if we are still waiting for messages to show up. If so, uidValidity
02254     // changed, or something else went wrong. Clean up.
02255 
02256     /* Unfortunately older UW imap servers change uid validity for each put job.
02257      * Yes, it is really that broken. *sigh* So we cannot report error here, I guess. */
02258     if ( !mLostBoys.isEmpty() ) {
02259       kdDebug(5006) <<  "### Not all moved messages reported back that they were " << endl
02260                     <<  "### added to the target folder. Did uidValidity change? " << endl;
02261     }
02262     completeMove( OK );
02263   } else {
02264     // Should we inform the user here or leave that to the caller?
02265     completeMove( Failed );
02266   }
02267 }
02268 
02269 void KMMoveCommand::slotMsgAddedToDestFolder(KMFolder *folder, Q_UINT32 serNum)
02270 {
02271   if ( folder != mDestFolder || mLostBoys.find( serNum ) == mLostBoys.end() ) {
02272     //kdDebug(5006) << "KMMoveCommand::msgAddedToDestFolder different "
02273     //                 "folder or invalid serial number." << endl;
02274     return;
02275   }
02276   mLostBoys.remove(serNum);
02277   if ( mLostBoys.isEmpty() ) {
02278     // we are done. All messages transferred to the host succesfully
02279     disconnect (mDestFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
02280              this, SLOT(slotMsgAddedToDestFolder(KMFolder*, Q_UINT32)));
02281     if (mDestFolder && mDestFolder->folderType() != KMFolderTypeImap) {
02282       mDestFolder->sync();
02283     }
02284     if ( mCompleteWithAddedMsg ) {
02285       completeMove( OK );
02286     }
02287   } else {
02288     if ( mProgressItem ) {
02289       mProgressItem->incCompletedItems();
02290       mProgressItem->updateProgress();
02291     }
02292   }
02293 }
02294 
02295 void KMMoveCommand::completeMove( Result result )
02296 {
02297   if ( mDestFolder )
02298     mDestFolder->close("kmcommand");
02299   while ( !mOpenedFolders.empty() ) {
02300     KMFolder *folder = mOpenedFolders.back();
02301     mOpenedFolders.pop_back();
02302     folder->close("kmcommand");
02303   }
02304   if ( mProgressItem ) {
02305     mProgressItem->setComplete();
02306     mProgressItem = 0;
02307   }
02308   setResult( result );
02309   emit completed( this );
02310   deleteLater();
02311 }
02312 
02313 void KMMoveCommand::slotMoveCanceled()
02314 {
02315   completeMove( Canceled );
02316 }
02317 
02318 // srcFolder doesn't make much sense for searchFolders
02319 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder,
02320   const QPtrList<KMMsgBase> &msgList )
02321 :KMMoveCommand( findTrashFolder( srcFolder ), msgList)
02322 {
02323   srcFolder->open("kmcommand");
02324   mOpenedFolders.push_back( srcFolder );
02325 }
02326 
02327 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder, KMMessage * msg )
02328 :KMMoveCommand( findTrashFolder( srcFolder ), msg)
02329 {
02330   srcFolder->open("kmcommand");
02331   mOpenedFolders.push_back( srcFolder );
02332 }
02333 
02334 KMDeleteMsgCommand::KMDeleteMsgCommand( Q_UINT32 sernum )
02335 :KMMoveCommand( sernum )
02336 {
02337   if ( !sernum ) {
02338     setDestFolder( 0 );
02339     return;
02340   }
02341 
02342   KMFolder *srcFolder = 0;
02343   int idx;
02344   KMMsgDict::instance()->getLocation( sernum, &srcFolder, &idx );
02345   if ( srcFolder ) {
02346     KMMsgBase *msg = srcFolder->getMsgBase( idx );
02347     srcFolder->open("kmcommand");
02348     mOpenedFolders.push_back( srcFolder );
02349     addMsg( msg );
02350   }
02351   setDestFolder( findTrashFolder( srcFolder ) );
02352 }
02353 
02354 KMFolder * KMDeleteMsgCommand::findTrashFolder( KMFolder * folder )
02355 {
02356   KMFolder* trash = folder->trashFolder();
02357   if( !trash )
02358     trash = kmkernel->trashFolder();
02359   if( trash != folder )
02360     return trash;
02361   return 0;
02362 }
02363 
02364 
02365 KMUrlClickedCommand::KMUrlClickedCommand( const KURL &url, uint identity,
02366   KMReaderWin *readerWin, bool htmlPref, KMMainWidget *mainWidget )
02367   :mUrl( url ), mIdentity( identity ), mReaderWin( readerWin ),
02368    mHtmlPref( htmlPref ), mMainWidget( mainWidget )
02369 {
02370 }
02371 
02372 KMCommand::Result KMUrlClickedCommand::execute()
02373 {
02374   KMMessage* msg;
02375 
02376   if (mUrl.protocol() == "mailto")
02377   {
02378     msg = new KMMessage;
02379     msg->initHeader(mIdentity);
02380     msg->setCharset("utf-8");
02381     msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
02382     QString query=mUrl.query();
02383     while (!query.isEmpty()) {
02384       QString queryPart;
02385       int secondQuery = query.find('?',1);
02386       if (secondQuery != -1)
02387         queryPart = query.left(secondQuery);
02388       else
02389         queryPart = query;
02390       query = query.mid(queryPart.length());
02391 
02392       if (queryPart.left(9) == "?subject=")
02393         msg->setSubject( KURL::decode_string(queryPart.mid(9)) );
02394       else if (queryPart.left(6) == "?body=")
02395         // It is correct to convert to latin1() as URL should not contain
02396         // anything except ascii.
02397         msg->setBody( KURL::decode_string(queryPart.mid(6)).latin1() );
02398       else if (queryPart.left(4) == "?cc=")
02399         msg->setCc( KURL::decode_string(queryPart.mid(4)) );
02400     }
02401 
02402     KMail::Composer * win = KMail::makeComposer( msg, mIdentity );
02403     win->setCharset("", true);
02404     win->show();
02405   }
02406   else if ( mUrl.protocol() == "im" )
02407   {
02408     kmkernel->imProxy()->chatWithContact( mUrl.path() );
02409   }
02410   else if ((mUrl.protocol() == "http") || (mUrl.protocol() == "https") ||
02411            (mUrl.protocol() == "ftp")  || (mUrl.protocol() == "file")  ||
02412            (mUrl.protocol() == "ftps") || (mUrl.protocol() == "sftp" ) ||
02413            (mUrl.protocol() == "help") || (mUrl.protocol() == "vnc")   ||
02414            (mUrl.protocol() == "smb")  || (mUrl.protocol() == "fish")  ||
02415            (mUrl.protocol() == "news"))
02416   {
02417     KPIM::BroadcastStatus::instance()->setStatusMsg( i18n("Opening URL..."));
02418     KMimeType::Ptr mime = KMimeType::findByURL( mUrl );
02419     if (mime->name() == "application/x-desktop" ||
02420         mime->name() == "application/x-executable" ||
02421         mime->name() == "application/x-msdos-program" ||
02422         mime->name() == "application/x-shellscript" )
02423     {
02424       if (KMessageBox::warningYesNo( 0, i18n( "<qt>Do you really want to execute <b>%1</b>?</qt>" )
02425         .arg( mUrl.prettyURL() ), QString::null, i18n("Execute"), KStdGuiItem::cancel() ) != KMessageBox::Yes)
02426         return Canceled;
02427     }
02428     KRun * runner = new KRun( mUrl );
02429     runner->setRunExecutables( false );
02430   }
02431   else
02432     return Failed;
02433 
02434   return OK;
02435 }
02436 
02437 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, KMMessage *msg )
02438   : KMCommand( parent, msg ), mImplicitAttachments( true ), mEncoded( false )
02439 {
02440 }
02441 
02442 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, const QPtrList<KMMsgBase>& msgs )
02443   : KMCommand( parent, msgs ), mImplicitAttachments( true ), mEncoded( false )
02444 {
02445 }
02446 
02447 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, QPtrList<partNode>& attachments,
02448                                                     KMMessage *msg, bool encoded )
02449   : KMCommand( parent ), mImplicitAttachments( false ), mEncoded( encoded )
02450 {
02451   for ( QPtrListIterator<partNode> it( attachments ); it.current(); ++it ) {
02452     mAttachmentMap.insert( it.current(), msg );
02453   }
02454 }
02455 
02456 KMCommand::Result KMSaveAttachmentsCommand::execute()
02457 {
02458   setEmitsCompletedItself( true );
02459   if ( mImplicitAttachments ) {
02460     QPtrList<KMMessage> msgList = retrievedMsgs();
02461     KMMessage *msg;
02462     for ( QPtrListIterator<KMMessage> itr( msgList );
02463           ( msg = itr.current() );
02464           ++itr ) {
02465       partNode *rootNode = partNode::fromMessage( msg );
02466       for ( partNode *child = rootNode; child;
02467             child = child->firstChild() ) {
02468         for ( partNode *node = child; node; node = node->nextSibling() ) {
02469           if ( node->type() != DwMime::kTypeMultipart )
02470             mAttachmentMap.insert( node, msg );
02471         }
02472       }
02473     }
02474   }
02475   setDeletesItself( true );
02476   // load all parts
02477   KMLoadPartsCommand *command = new KMLoadPartsCommand( mAttachmentMap );
02478   connect( command, SIGNAL( partsRetrieved() ),
02479            this, SLOT( slotSaveAll() ) );
02480   command->start();
02481 
02482   return OK;
02483 }
02484 
02485 void KMSaveAttachmentsCommand::slotSaveAll()
02486 {
02487   // now that all message parts have been retrieved, remove all parts which
02488   // don't represent an attachment if they were not explicitely passed in the
02489   // c'tor
02490   if ( mImplicitAttachments ) {
02491     for ( PartNodeMessageMap::iterator it = mAttachmentMap.begin();
02492           it != mAttachmentMap.end(); ) {
02493       // only body parts which have a filename or a name parameter (except for
02494       // the root node for which name is set to the message's subject) are
02495       // considered attachments
02496       if ( it.key()->msgPart().fileName().stripWhiteSpace().isEmpty() &&
02497            ( it.key()->msgPart().name().stripWhiteSpace().isEmpty() ||
02498              !it.key()->parentNode() ) ) {
02499         PartNodeMessageMap::iterator delIt = it;
02500         ++it;
02501         mAttachmentMap.remove( delIt );
02502       }
02503       else
02504         ++it;
02505     }
02506     if ( mAttachmentMap.isEmpty() ) {
02507       KMessageBox::information( 0, i18n("Found no attachments to save.") );
02508       setResult( OK ); // The user has already been informed.
02509       emit completed( this );
02510       deleteLater();
02511       return;
02512     }
02513   }
02514 
02515   KURL url, dirUrl;
02516   if ( mAttachmentMap.count() > 1 ) {
02517     // get the dir
02518     dirUrl = KDirSelectDialog::selectDirectory( QString::null, false,
02519                                                 parentWidget(),
02520                                                 i18n("Save Attachments To") );
02521     if ( !dirUrl.isValid() ) {
02522       setResult( Canceled );
02523       emit completed( this );
02524       deleteLater();
02525       return;
02526     }
02527 
02528     // we may not get a slash-terminated url out of KDirSelectDialog
02529     dirUrl.adjustPath( 1 );
02530   }
02531   else {
02532     // only one item, get the desired filename
02533     partNode *node = mAttachmentMap.begin().key();
02534     // replace all ':' with '_' because ':' isn't allowed on FAT volumes
02535     QString s =
02536       node->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
02537     if ( s.isEmpty() )
02538       s = node->msgPart().name().stripWhiteSpace().replace( ':', '_' );
02539     if ( s.isEmpty() )
02540       s = i18n("filename for an unnamed attachment", "attachment.1");
02541     url = KFileDialog::getSaveURL( s, QString::null, parentWidget(),
02542                                    QString::null );
02543     if ( url.isEmpty() ) {
02544       setResult( Canceled );
02545       emit completed( this );
02546       deleteLater();
02547       return;
02548     }
02549   }
02550 
02551   QMap< QString, int > renameNumbering;
02552 
02553   Result globalResult = OK;
02554   int unnamedAtmCount = 0;
02555   for ( PartNodeMessageMap::const_iterator it = mAttachmentMap.begin();
02556         it != mAttachmentMap.end();
02557         ++it ) {
02558     KURL curUrl;
02559     if ( !dirUrl.isEmpty() ) {
02560       curUrl = dirUrl;
02561       QString s =
02562         it.key()->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
02563       if ( s.isEmpty() )
02564         s = it.key()->msgPart().name().stripWhiteSpace().replace( ':', '_' );
02565       if ( s.isEmpty() ) {
02566         ++unnamedAtmCount;
02567         s = i18n("filename for the %1-th unnamed attachment",
02568                  "attachment.%1")
02569             .arg( unnamedAtmCount );
02570       }
02571       curUrl.setFileName( s );
02572     } else {
02573       curUrl = url;
02574     }
02575 
02576     if ( !curUrl.isEmpty() ) {
02577 
02578      // Rename the file if we have already saved one with the same name:
02579      // try appending a number before extension (e.g. "pic.jpg" => "pic_2.jpg")
02580      QString origFile = curUrl.fileName();
02581      QString file = origFile;
02582 
02583      while ( renameNumbering.contains(file) ) {
02584        file = origFile;
02585        int num = renameNumbering[file] + 1;
02586        int dotIdx = file.findRev('.');
02587        file = file.insert( (dotIdx>=0) ? dotIdx : file.length(), QString("_") + QString::number(num) );
02588      }
02589      curUrl.setFileName(file);
02590 
02591      // Increment the counter for both the old and the new filename
02592      if ( !renameNumbering.contains(origFile))
02593          renameNumbering[origFile] = 1;
02594      else
02595          renameNumbering[origFile]++;
02596 
02597      if ( file != origFile ) {
02598         if ( !renameNumbering.contains(file))
02599             renameNumbering[file] = 1;
02600         else
02601             renameNumbering[file]++;
02602      }
02603 
02604 
02605       if ( KIO::NetAccess::exists( curUrl, false, parentWidget() ) ) {
02606         if ( KMessageBox::warningContinueCancel( parentWidget(),
02607               i18n( "A file named %1 already exists. Do you want to overwrite it?" )
02608               .arg( curUrl.fileName() ),
02609               i18n( "File Already Exists" ), i18n("&Overwrite") ) == KMessageBox::Cancel) {
02610           continue;
02611         }
02612       }
02613       // save
02614       const Result result = saveItem( it.key(), curUrl );
02615       if ( result != OK )
02616         globalResult = result;
02617     }
02618   }
02619   setResult( globalResult );
02620   emit completed( this );
02621   deleteLater();
02622 }
02623 
02624 KMCommand::Result KMSaveAttachmentsCommand::saveItem( partNode *node,
02625                                                       const KURL& url )
02626 {
02627   bool bSaveEncrypted = false;
02628   bool bEncryptedParts = node->encryptionState() != KMMsgNotEncrypted;
02629   if( bEncryptedParts )
02630     if( KMessageBox::questionYesNo( parentWidget(),
02631           i18n( "The part %1 of the message is encrypted. Do you want to keep the encryption when saving?" ).
02632           arg( url.fileName() ),
02633           i18n( "KMail Question" ), i18n("Keep Encryption"), i18n("Do Not Keep") ) ==
02634         KMessageBox::Yes )
02635       bSaveEncrypted = true;
02636 
02637   bool bSaveWithSig = true;
02638   if( node->signatureState() != KMMsgNotSigned )
02639     if( KMessageBox::questionYesNo( parentWidget(),
02640           i18n( "The part %1 of the message is signed. Do you want to keep the signature when saving?" ).
02641           arg( url.fileName() ),
02642           i18n( "KMail Question" ), i18n("Keep Signature"), i18n("Do Not Keep") ) !=
02643         KMessageBox::Yes )
02644       bSaveWithSig = false;
02645 
02646   QByteArray data;
02647   if ( mEncoded )
02648   {
02649     // This does not decode the Message Content-Transfer-Encoding
02650     // but saves the _original_ content of the message part
02651     data = KMail::Util::ByteArray( node->msgPart().dwBody() );
02652   }
02653   else
02654   {
02655     if( bSaveEncrypted || !bEncryptedParts) {
02656       partNode *dataNode = node;
02657       QCString rawReplyString;
02658       bool gotRawReplyString = false;
02659       if( !bSaveWithSig ) {
02660         if( DwMime::kTypeMultipart == node->type() &&
02661             DwMime::kSubtypeSigned == node->subType() ){
02662           // carefully look for the part that is *not* the signature part:
02663           if( node->findType( DwMime::kTypeApplication,
02664                 DwMime::kSubtypePgpSignature,
02665                 true, false ) ){
02666             dataNode = node->findTypeNot( DwMime::kTypeApplication,
02667                 DwMime::kSubtypePgpSignature,
02668                 true, false );
02669           }else if( node->findType( DwMime::kTypeApplication,
02670                 DwMime::kSubtypePkcs7Mime,
02671                 true, false ) ){
02672             dataNode = node->findTypeNot( DwMime::kTypeApplication,
02673                 DwMime::kSubtypePkcs7Mime,
02674                 true, false );
02675           }else{
02676             dataNode = node->findTypeNot( DwMime::kTypeMultipart,
02677                 DwMime::kSubtypeUnknown,
02678                 true, false );
02679           }
02680     }else{
02681       ObjectTreeParser otp( 0, 0, false, false, false );
02682 
02683       // process this node and all it's siblings and descendants
02684       dataNode->setProcessed( false, true );
02685       otp.parseObjectTree( dataNode );
02686 
02687       rawReplyString = otp.rawReplyString();
02688       gotRawReplyString = true;
02689         }
02690       }
02691       QByteArray cstr = gotRawReplyString
02692                          ? rawReplyString
02693                          : dataNode->msgPart().bodyDecodedBinary();
02694       data = cstr;
02695       size_t size = cstr.size();
02696       if ( dataNode->msgPart().type() == DwMime::kTypeText ) {
02697         // convert CRLF to LF before writing text attachments to disk
02698         size = KMail::Util::crlf2lf( cstr.data(), size );
02699       }
02700       data.resize( size );
02701     }
02702   }
02703   QDataStream ds;
02704   QFile file;
02705   KTempFile tf;
02706   tf.setAutoDelete( true );
02707   if ( url.isLocalFile() )
02708   {
02709     // save directly
02710     file.setName( url.path() );
02711     if ( !file.open( IO_WriteOnly ) )
02712     {
02713       KMessageBox::error( parentWidget(),
02714           i18n( "%2 is detailed error description",
02715             "Could not write the file %1:\n%2" )
02716           .arg( file.name() )
02717           .arg( QString::fromLocal8Bit( strerror( errno ) ) ),
02718           i18n( "KMail Error" ) );
02719       return Failed;
02720     }
02721 
02722     // #79685 by default use the umask the user defined, but let it be configurable
02723     if ( GlobalSettings::self()->disregardUmask() )
02724       fchmod( file.handle(), S_IRUSR | S_IWUSR );
02725 
02726     ds.setDevice( &file );
02727   } else
02728   {
02729     // tmp file for upload
02730     ds.setDevice( tf.file() );
02731   }
02732 
02733   ds.writeRawBytes( data.data(), data.size() );
02734   if ( !url.isLocalFile() )
02735   {
02736     tf.close();
02737     if ( !KIO::NetAccess::upload( tf.name(), url, parentWidget() ) )
02738     {
02739       KMessageBox::error( parentWidget(),
02740           i18n( "Could not write the file %1." )
02741           .arg( url.path() ),
02742           i18n( "KMail Error" ) );
02743       return Failed;
02744     }
02745   } else
02746     file.close();
02747   return OK;
02748 }
02749 
02750 KMLoadPartsCommand::KMLoadPartsCommand( QPtrList<partNode>& parts, KMMessage *msg )
02751   : mNeedsRetrieval( 0 )
02752 {
02753   for ( QPtrListIterator<partNode> it( parts ); it.current(); ++it ) {
02754     mPartMap.insert( it.current(), msg );
02755   }
02756 }
02757 
02758 KMLoadPartsCommand::KMLoadPartsCommand( partNode *node, KMMessage *msg )
02759   : mNeedsRetrieval( 0 )
02760 {
02761   mPartMap.insert( node, msg );
02762 }
02763 
02764 KMLoadPartsCommand::KMLoadPartsCommand( PartNodeMessageMap& partMap )
02765   : mNeedsRetrieval( 0 ), mPartMap( partMap )
02766 {
02767 }
02768 
02769 void KMLoadPartsCommand::slotStart()
02770 {
02771   for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
02772         it != mPartMap.end();
02773         ++it ) {
02774     if ( !it.key()->msgPart().isComplete() &&
02775          !it.key()->msgPart().partSpecifier().isEmpty() ) {
02776       // incomplete part, so retrieve it first
02777       ++mNeedsRetrieval;
02778       KMFolder* curFolder = it.data()->parent();
02779       if ( curFolder ) {
02780         FolderJob *job =
02781           curFolder->createJob( it.data(), FolderJob::tGetMessage,
02782                                 0, it.key()->msgPart().partSpecifier() );
02783         job->setCancellable( false );
02784         connect( job, SIGNAL(messageUpdated(KMMessage*, QString)),
02785                  this, SLOT(slotPartRetrieved(KMMessage*, QString)) );
02786         job->start();
02787       } else
02788         kdWarning(5006) << "KMLoadPartsCommand - msg has no parent" << endl;
02789     }
02790   }
02791   if ( mNeedsRetrieval == 0 )
02792     execute();
02793 }
02794 
02795 void KMLoadPartsCommand::slotPartRetrieved( KMMessage *msg,
02796                                             QString partSpecifier )
02797 {
02798   DwBodyPart *part =
02799     msg->findDwBodyPart( msg->getFirstDwBodyPart(), partSpecifier );
02800   if ( part ) {
02801     // update the DwBodyPart in the partNode
02802     for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
02803           it != mPartMap.end();
02804           ++it ) {
02805       if ( it.key()->dwPart()->partId() == part->partId() )
02806         it.key()->setDwPart( part );
02807     }
02808   } else
02809     kdWarning(5006) << "KMLoadPartsCommand::slotPartRetrieved - could not find bodypart!" << endl;
02810   --mNeedsRetrieval;
02811   if ( mNeedsRetrieval == 0 )
02812     execute();
02813 }
02814 
02815 KMCommand::Result KMLoadPartsCommand::execute()
02816 {
02817   emit partsRetrieved();
02818   setResult( OK );
02819   emit completed( this );
02820   deleteLater();
02821   return OK;
02822 }
02823 
02824 KMResendMessageCommand::KMResendMessageCommand( QWidget *parent,
02825    KMMessage *msg )
02826   :KMCommand( parent, msg )
02827 {
02828 }
02829 
02830 KMCommand::Result KMResendMessageCommand::execute()
02831 {
02832    KMMessage *msg = retrievedMessage();
02833    if ( !msg || !msg->codec() ) {
02834      return Failed;
02835    }
02836    KMMessage *newMsg = new KMMessage(*msg);
02837 
02838    QStringList whiteList;
02839    whiteList << "To" << "Cc" << "Bcc" << "Subject";
02840    newMsg->sanitizeHeaders( whiteList );
02841 
02842    newMsg->setCharset(msg->codec()->mimeName());
02843    newMsg->setParent( 0 );
02844 
02845    // make sure we have an identity set, default, if necessary
02846    newMsg->setHeaderField("X-KMail-Identity", QString::number( newMsg->identityUoid() ));
02847    newMsg->applyIdentity( newMsg->identityUoid() );
02848 
02849    KMail::Composer * win = KMail::makeComposer();
02850    win->setMsg(newMsg, false, true);
02851    win->show();
02852 
02853    return OK;
02854 }
02855 
02856 KMMailingListCommand::KMMailingListCommand( QWidget *parent, KMFolder *folder )
02857   : KMCommand( parent ), mFolder( folder )
02858 {
02859 }
02860 
02861 KMCommand::Result KMMailingListCommand::execute()
02862 {
02863   KURL::List lst = urls();
02864   QString handler = ( mFolder->mailingList().handler() == MailingList::KMail )
02865     ? "mailto" : "https";
02866 
02867   KMCommand *command = 0;
02868   for ( KURL::List::Iterator itr = lst.begin(); itr != lst.end(); ++itr ) {
02869     if ( handler == (*itr).protocol() ) {
02870       command = new KMUrlClickedCommand( *itr, mFolder->identity(), 0, false );
02871     }
02872   }
02873   if ( !command && !lst.empty() ) {
02874     command =
02875       new KMUrlClickedCommand( lst.first(), mFolder->identity(), 0, false );
02876   }
02877   if ( command ) {
02878     connect( command, SIGNAL( completed( KMCommand * ) ),
02879              this, SLOT( commandCompleted( KMCommand * ) ) );
02880     setDeletesItself( true );
02881     setEmitsCompletedItself( true );
02882     command->start();
02883     return OK;
02884   }
02885   return Failed;
02886 }
02887 
02888 void KMMailingListCommand::commandCompleted( KMCommand *command )
02889 {
02890   setResult( command->result() );
02891   emit completed( this );
02892   deleteLater();
02893 }
02894 
02895 KMMailingListPostCommand::KMMailingListPostCommand( QWidget *parent, KMFolder *folder )
02896   : KMMailingListCommand( parent, folder )
02897 {
02898 }
02899 KURL::List KMMailingListPostCommand::urls() const
02900 {
02901   return mFolder->mailingList().postURLS();
02902 }
02903 
02904 KMMailingListSubscribeCommand::KMMailingListSubscribeCommand( QWidget *parent, KMFolder *folder )
02905   : KMMailingListCommand( parent, folder )
02906 {
02907 }
02908 KURL::List KMMailingListSubscribeCommand::urls() const
02909 {
02910   return mFolder->mailingList().subscribeURLS();
02911 }
02912 
02913 KMMailingListUnsubscribeCommand::KMMailingListUnsubscribeCommand( QWidget *parent, KMFolder *folder )
02914   : KMMailingListCommand( parent, folder )
02915 {
02916 }
02917 KURL::List KMMailingListUnsubscribeCommand::urls() const
02918 {
02919   return mFolder->mailingList().unsubscribeURLS();
02920 }
02921 
02922 KMMailingListArchivesCommand::KMMailingListArchivesCommand( QWidget *parent, KMFolder *folder )
02923   : KMMailingListCommand( parent, folder )
02924 {
02925 }
02926 KURL::List KMMailingListArchivesCommand::urls() const
02927 {
02928   return mFolder->mailingList().archiveURLS();
02929 }
02930 
02931 KMMailingListHelpCommand::KMMailingListHelpCommand( QWidget *parent, KMFolder *folder )
02932   : KMMailingListCommand( parent, folder )
02933 {
02934 }
02935 KURL::List KMMailingListHelpCommand::urls() const
02936 {
02937   return mFolder->mailingList().helpURLS();
02938 }
02939 
02940 KMIMChatCommand::KMIMChatCommand( const KURL &url, KMMessage *msg )
02941   :mUrl( url ), mMessage( msg )
02942 {
02943 }
02944 
02945 KMCommand::Result KMIMChatCommand::execute()
02946 {
02947   kdDebug( 5006 ) << k_funcinfo << " URL is: " << mUrl << endl;
02948   QString addr = KMMessage::decodeMailtoUrl( mUrl.path() );
02949   // find UID for mail address
02950   KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
02951   KABC::AddresseeList addressees = addressBook->findByEmail( KPIM::getEmailAddress( addr ) ) ;
02952 
02953   // start chat
02954   if( addressees.count() == 1 ) {
02955     kmkernel->imProxy()->chatWithContact( addressees[0].uid() );
02956     return OK;
02957   }
02958   else
02959   {
02960     kdDebug( 5006 ) << "Didn't find exactly one addressee, couldn't tell who to chat to for that email address.  Count = " << addressees.count() << endl;
02961 
02962     QString apology;
02963     if ( addressees.isEmpty() )
02964       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." );
02965     else
02966     {
02967       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." );
02968       QStringList nameList;
02969       KABC::AddresseeList::const_iterator it = addressees.begin();
02970       KABC::AddresseeList::const_iterator end = addressees.end();
02971       for ( ; it != end; ++it )
02972       {
02973           nameList.append( (*it).realName() );
02974       }
02975       QString names = nameList.join( QString::fromLatin1( ",\n" ) );
02976       apology = apology.arg( names );
02977     }
02978 
02979     KMessageBox::sorry( parentWidget(), apology );
02980     return Failed;
02981   }
02982 }
02983 
02984 KMHandleAttachmentCommand::KMHandleAttachmentCommand( partNode* node,
02985      KMMessage* msg, int atmId, const QString& atmName,
02986      AttachmentAction action, KService::Ptr offer, QWidget* parent )
02987 : KMCommand( parent ), mNode( node ), mMsg( msg ), mAtmId( atmId ), mAtmName( atmName ),
02988   mAction( action ), mOffer( offer ), mJob( 0 )
02989 {
02990 }
02991 
02992 void KMHandleAttachmentCommand::slotStart()
02993 {
02994   if ( !mNode->msgPart().isComplete() )
02995   {
02996     // load the part
02997     kdDebug(5006) << "load part" << endl;
02998     KMLoadPartsCommand *command = new KMLoadPartsCommand( mNode, mMsg );
02999     connect( command, SIGNAL( partsRetrieved() ),
03000         this, SLOT( slotPartComplete() ) );
03001     command->start();
03002   } else
03003   {
03004     execute();
03005   }
03006 }
03007 
03008 void KMHandleAttachmentCommand::slotPartComplete()
03009 {
03010   execute();
03011 }
03012 
03013 KMCommand::Result KMHandleAttachmentCommand::execute()
03014 {
03015   switch( mAction )
03016   {
03017     case Open:
03018       atmOpen();
03019       break;
03020     case OpenWith:
03021       atmOpenWith();
03022       break;
03023     case View:
03024       atmView();
03025       break;
03026     case Save:
03027       atmSave();
03028       break;
03029     case Properties:
03030       atmProperties();
03031       break;
03032     case ChiasmusEncrypt:
03033       atmEncryptWithChiasmus();
03034       return Undefined;
03035       break;
03036     default:
03037       kdDebug(5006) << "unknown action " << mAction << endl;
03038       break;
03039   }
03040   setResult( OK );
03041   emit completed( this );
03042   deleteLater();
03043   return OK;
03044 }
03045 
03046 QString KMHandleAttachmentCommand::createAtmFileLink() const
03047 {
03048   QFileInfo atmFileInfo( mAtmName );
03049 
03050   if ( atmFileInfo.size() == 0 )
03051   {
03052     kdDebug(5006) << k_funcinfo << "rewriting attachment" << endl;
03053     // there is something wrong so write the file again
03054     QByteArray data = mNode->msgPart().bodyDecodedBinary();
03055     size_t size = data.size();
03056     if ( mNode->msgPart().type() == DwMime::kTypeText && size) {
03057       // convert CRLF to LF before writing text attachments to disk
03058       size = KMail::Util::crlf2lf( data.data(), size );
03059     }
03060     KPIM::kBytesToFile( data.data(), size, mAtmName, false, false, false );
03061   }
03062 
03063   KTempFile *linkFile = new KTempFile( locateLocal("tmp", atmFileInfo.fileName() +"_["),
03064                           "]."+ atmFileInfo.extension() );
03065 
03066   linkFile->setAutoDelete(true);
03067   QString linkName = linkFile->name();
03068   delete linkFile;
03069 
03070   if ( ::link(QFile::encodeName( mAtmName ), QFile::encodeName( linkName )) == 0 ) {
03071     return linkName; // success
03072   }
03073   return QString::null;
03074 }
03075 
03076 KService::Ptr KMHandleAttachmentCommand::getServiceOffer()
03077 {
03078   KMMessagePart& msgPart = mNode->msgPart();
03079   const QString contentTypeStr =
03080     ( msgPart.typeStr() + '/' + msgPart.subtypeStr() ).lower();
03081 
03082   if ( contentTypeStr == "text/x-vcard" ) {
03083     atmView();
03084     return 0;
03085   }
03086   // determine the MIME type of the attachment
03087   KMimeType::Ptr mimetype;
03088   // prefer the value of the Content-Type header
03089   mimetype = KMimeType::mimeType( contentTypeStr );
03090   if ( mimetype->name() == "application/octet-stream" ) {
03091     // consider the filename if Content-Type is application/octet-stream
03092     mimetype = KMimeType::findByPath( mAtmName, 0, true /* no disk access */ );
03093   }
03094   if ( ( mimetype->name() == "application/octet-stream" )
03095        && msgPart.isComplete() ) {
03096     // consider the attachment's contents if neither the Content-Type header
03097     // nor the filename give us a clue
03098     mimetype = KMimeType::findByFileContent( mAtmName );
03099   }
03100   return KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
03101 }
03102 
03103 void KMHandleAttachmentCommand::atmOpen()
03104 {
03105   if ( !mOffer )
03106     mOffer = getServiceOffer();
03107   if ( !mOffer ) {
03108     kdDebug(5006) << k_funcinfo << "got no offer" << endl;
03109     return;
03110   }
03111 
03112   KURL::List lst;
03113   KURL url;
03114   bool autoDelete = true;
03115   QString fname = createAtmFileLink();
03116 
03117   if ( fname.isNull() ) {
03118     autoDelete = false;
03119     fname = mAtmName;
03120   }
03121 
03122   url.setPath( fname );
03123   lst.append( url );
03124   if ( (KRun::run( *mOffer, lst, autoDelete ) <= 0) && autoDelete ) {
03125       QFile::remove(url.path());
03126   }
03127 }
03128 
03129 void KMHandleAttachmentCommand::atmOpenWith()
03130 {
03131   KURL::List lst;
03132   KURL url;
03133   bool autoDelete = true;
03134   QString fname = createAtmFileLink();
03135 
03136   if ( fname.isNull() ) {
03137     autoDelete = false;
03138     fname = mAtmName;
03139   }
03140 
03141   url.setPath( fname );
03142   lst.append( url );
03143   if ( (! KRun::displayOpenWithDialog(lst, autoDelete)) && autoDelete ) {
03144     QFile::remove( url.path() );
03145   }
03146 }
03147 
03148 void KMHandleAttachmentCommand::atmView()
03149 {
03150   // we do not handle this ourself
03151   emit showAttachment( mAtmId, mAtmName );
03152 }
03153 
03154 void KMHandleAttachmentCommand::atmSave()
03155 {
03156   QPtrList<partNode> parts;
03157   parts.append( mNode );
03158   // save, do not leave encoded
03159   KMSaveAttachmentsCommand *command =
03160     new KMSaveAttachmentsCommand( 0, parts, mMsg, false );
03161   command->start();
03162 }
03163 
03164 void KMHandleAttachmentCommand::atmProperties()
03165 {
03166   KMMsgPartDialogCompat dlg( parentWidget() , 0, true );
03167   KMMessagePart& msgPart = mNode->msgPart();
03168   dlg.setMsgPart( &msgPart );
03169   dlg.exec();
03170 }
03171 
03172 void KMHandleAttachmentCommand::atmEncryptWithChiasmus()
03173 {
03174   const partNode * node = mNode;
03175   Q_ASSERT( node );
03176   if ( !node )
03177     return;
03178 
03179   // FIXME: better detection of mimetype??
03180   if ( !mAtmName.endsWith( ".xia", false ) )
03181     return;
03182 
03183   const Kleo::CryptoBackend::Protocol * chiasmus =
03184     Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
03185   Q_ASSERT( chiasmus );
03186   if ( !chiasmus )
03187     return;
03188 
03189   const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
03190   if ( !listjob.get() ) {
03191     const QString msg = i18n( "Chiasmus backend does not offer the "
03192                               "\"x-obtain-keys\" function. Please report this bug." );
03193     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03194     return;
03195   }
03196 
03197   if ( listjob->exec() ) {
03198     listjob->showErrorDialog( parentWidget(), i18n( "Chiasmus Backend Error" ) );
03199     return;
03200   }
03201 
03202   const QVariant result = listjob->property( "result" );
03203   if ( result.type() != QVariant::StringList ) {
03204     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
03205                               "The \"x-obtain-keys\" function did not return a "
03206                               "string list. Please report this bug." );
03207     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03208     return;
03209   }
03210 
03211   const QStringList keys = result.toStringList();
03212   if ( keys.empty() ) {
03213     const QString msg = i18n( "No keys have been found. Please check that a "
03214                               "valid key path has been set in the Chiasmus "
03215                               "configuration." );
03216     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03217     return;
03218   }
03219 
03220   ChiasmusKeySelector selectorDlg( parentWidget(), i18n( "Chiasmus Decryption Key Selection" ),
03221                                    keys, GlobalSettings::chiasmusDecryptionKey(),
03222                                    GlobalSettings::chiasmusDecryptionOptions() );
03223   if ( selectorDlg.exec() != QDialog::Accepted )
03224     return;
03225 
03226   GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
03227   GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
03228   assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
03229 
03230   Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", QMap<QString,QVariant>() );
03231   if ( !job ) {
03232     const QString msg = i18n( "Chiasmus backend does not offer the "
03233                               "\"x-decrypt\" function. Please report this bug." );
03234     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03235     return;
03236   }
03237 
03238   const QByteArray input = node->msgPart().bodyDecodedBinary();
03239 
03240   if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
03241        !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
03242        !job->setProperty( "input", input ) ) {
03243     const QString msg = i18n( "The \"x-decrypt\" function does not accept "
03244                               "the expected parameters. Please report this bug." );
03245     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03246     return;
03247   }
03248 
03249   setDeletesItself( true ); // the job below is async, we have to cleanup ourselves
03250   if ( job->start() ) {
03251     job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
03252     return;
03253   }
03254 
03255   mJob = job;
03256   connect( job, SIGNAL(result(const GpgME::Error&,const QVariant&)),
03257            this, SLOT(slotAtmDecryptWithChiasmusResult(const GpgME::Error&,const QVariant&)) );
03258 }
03259 
03260 static const QString chomp( const QString & base, const QString & suffix, bool cs ) {
03261   return base.endsWith( suffix, cs ) ? base.left( base.length() - suffix.length() ) : base ;
03262 }
03263 
03264 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusResult( const GpgME::Error & err, const QVariant & result )
03265 {
03266   LaterDeleterWithCommandCompletion d( this );
03267   if ( !mJob )
03268     return;
03269   Q_ASSERT( mJob == sender() );
03270   if ( mJob != sender() )
03271     return;
03272   Kleo::Job * job = mJob;
03273   mJob = 0;
03274   if ( err.isCanceled() )
03275     return;
03276   if ( err ) {
03277     job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
03278     return;
03279   }
03280 
03281   if ( result.type() != QVariant::ByteArray ) {
03282     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
03283                               "The \"x-decrypt\" function did not return a "
03284                               "byte array. Please report this bug." );
03285     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03286     return;
03287   }
03288 
03289   const KURL url = KFileDialog::getSaveURL( chomp( mAtmName, ".xia", false ), QString::null, parentWidget() );
03290   if ( url.isEmpty() )
03291     return;
03292 
03293   bool overwrite = KMail::Util::checkOverwrite( url, parentWidget() );
03294   if ( !overwrite )
03295     return;
03296 
03297   d.setDisabled( true ); // we got this far, don't delete yet
03298   KIO::Job * uploadJob = KIO::storedPut( result.toByteArray(), url, -1, overwrite, false /*resume*/ );
03299   uploadJob->setWindow( parentWidget() );
03300   connect( uploadJob, SIGNAL(result(KIO::Job*)),
03301            this, SLOT(slotAtmDecryptWithChiasmusUploadResult(KIO::Job*)) );
03302 }
03303 
03304 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusUploadResult( KIO::Job * job )
03305 {
03306   if ( job->error() )
03307     job->showErrorDialog();
03308   LaterDeleterWithCommandCompletion d( this );
03309   d.setResult( OK );
03310 }
03311 
03312 
03313 AttachmentModifyCommand::AttachmentModifyCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03314     KMCommand( parent, msg ),
03315     mPartIndex( node->nodeId() ),
03316     mSernum( 0 )
03317 {
03318 }
03319 
03320 AttachmentModifyCommand::AttachmentModifyCommand( int nodeId, KMMessage *msg, QWidget *parent )
03321   : KMCommand( parent, msg ),
03322     mPartIndex( nodeId ),
03323     mSernum( 0 )
03324 {
03325 }
03326 
03327 AttachmentModifyCommand::~ AttachmentModifyCommand()
03328 {
03329 }
03330 
03331 KMCommand::Result AttachmentModifyCommand::execute()
03332 {
03333   KMMessage *msg = retrievedMessage();
03334   if ( !msg )
03335     return Failed;
03336   mSernum = msg->getMsgSerNum();
03337 
03338   mFolder = msg->parent();
03339   if ( !mFolder || !mFolder->storage() )
03340     return Failed;
03341 
03342   Result res = doAttachmentModify();
03343   if ( res != OK )
03344     return res;
03345 
03346   setEmitsCompletedItself( true );
03347   setDeletesItself( true );
03348   return OK;
03349 }
03350 
03351 void AttachmentModifyCommand::storeChangedMessage(KMMessage * msg)
03352 {
03353   if ( !mFolder || !mFolder->storage() ) {
03354     kdWarning(5006) << k_funcinfo << "We lost the folder!" << endl;
03355     setResult( Failed );
03356     emit completed( this );
03357     deleteLater();
03358   }
03359   int res = mFolder->addMsg( msg ) != 0;
03360   if ( mFolder->folderType() == KMFolderTypeImap ) {
03361     KMFolderImap *f = static_cast<KMFolderImap*>( mFolder->storage() );
03362     connect( f, SIGNAL(folderComplete(KMFolderImap*,bool)),
03363              SLOT(messageStoreResult(KMFolderImap*,bool)) );
03364   } else {
03365     messageStoreResult( 0, res == 0 );
03366   }
03367 }
03368 
03369 void AttachmentModifyCommand::messageStoreResult(KMFolderImap* folder, bool success )
03370 {
03371   Q_UNUSED( folder );
03372   if ( success ) {
03373     KMCommand *delCmd = new KMDeleteMsgCommand( mSernum );
03374     connect( delCmd, SIGNAL(completed(KMCommand*)), SLOT(messageDeleteResult(KMCommand*)) );
03375     delCmd->start();
03376     return;
03377   }
03378   kdWarning(5006) << k_funcinfo << "Adding modified message failed." << endl;
03379   setResult( Failed );
03380   emit completed( this );
03381   deleteLater();
03382 }
03383 
03384 void AttachmentModifyCommand::messageDeleteResult(KMCommand * cmd)
03385 {
03386   setResult( cmd->result() );
03387   emit completed( this );
03388   deleteLater();
03389 }
03390 
03391 KMDeleteAttachmentCommand::KMDeleteAttachmentCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03392     AttachmentModifyCommand( node, msg, parent )
03393 {
03394   kdDebug(5006) << k_funcinfo << endl;
03395 }
03396 
03397 KMDeleteAttachmentCommand::KMDeleteAttachmentCommand( int nodeId, KMMessage *msg, QWidget *parent )
03398   : AttachmentModifyCommand( nodeId, msg, parent )
03399 {
03400   kdDebug(5006) << k_funcinfo << endl;
03401 }
03402 
03403 KMDeleteAttachmentCommand::~KMDeleteAttachmentCommand()
03404 {
03405   kdDebug(5006) << k_funcinfo << endl;
03406 }
03407 
03408 KMCommand::Result KMDeleteAttachmentCommand::doAttachmentModify()
03409 {
03410   KMMessage *msg = retrievedMessage();
03411   if ( !msg || !msg->deleteBodyPart( mPartIndex ) )
03412     return Failed;
03413 
03414   KMMessage *newMsg = new KMMessage();
03415   newMsg->fromDwString( msg->asDwString() );
03416   newMsg->setStatus( msg->status() );
03417 
03418   storeChangedMessage( newMsg );
03419   return OK;
03420 }
03421 
03422 
03423 KMEditAttachmentCommand::KMEditAttachmentCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03424     AttachmentModifyCommand( node, msg, parent )
03425 {
03426   kdDebug(5006) << k_funcinfo << endl;
03427   mTempFile.setAutoDelete( true );
03428 }
03429 
03430 KMEditAttachmentCommand::KMEditAttachmentCommand( int nodeId, KMMessage *msg, QWidget *parent )
03431   : AttachmentModifyCommand( nodeId, msg, parent )
03432 {
03433   kdDebug(5006) << k_funcinfo << endl;
03434   mTempFile.setAutoDelete( true );
03435 }
03436 
03437 KMEditAttachmentCommand::~ KMEditAttachmentCommand()
03438 {
03439 }
03440 
03441 KMCommand::Result KMEditAttachmentCommand::doAttachmentModify()
03442 {
03443   KMMessage *msg = retrievedMessage();
03444   if ( !msg )
03445     return Failed;
03446 
03447   KMMessagePart part;
03448   DwBodyPart *dwpart = msg->findPart( mPartIndex );
03449   if ( !dwpart )
03450     return Failed;
03451   KMMessage::bodyPart( dwpart, &part, true );
03452   if ( !part.isComplete() )
03453      return Failed;
03454 
03455   if( !dynamic_cast<DwBody*>( dwpart->Parent() ) )
03456     return Failed;
03457 
03458   mTempFile.file()->writeBlock( part.bodyDecodedBinary() );
03459   mTempFile.file()->flush();
03460 
03461   KMail::EditorWatcher *watcher =
03462           new KMail::EditorWatcher( KURL( mTempFile.file()->name() ),
03463                                     part.typeStr() + "/" + part.subtypeStr(),
03464                                     false, this, parentWidget() );
03465   connect( watcher, SIGNAL(editDone(KMail::EditorWatcher*)), SLOT(editDone(KMail::EditorWatcher*)) );
03466   if ( !watcher->start() )
03467     return Failed;
03468   setEmitsCompletedItself( true );
03469   setDeletesItself( true );
03470   return OK;
03471 }
03472 
03473 void KMEditAttachmentCommand::editDone(KMail::EditorWatcher * watcher)
03474 {
03475   kdDebug(5006) << k_funcinfo << endl;
03476   // anything changed?
03477   if ( !watcher->fileChanged() ) {
03478     kdDebug(5006) << k_funcinfo << "File has not been changed" << endl;
03479     setResult( Canceled );
03480     emit completed( this );
03481     deleteLater();
03482   }
03483 
03484   mTempFile.file()->reset();
03485   QByteArray data = mTempFile.file()->readAll();
03486 
03487   // build the new message
03488   KMMessage *msg = retrievedMessage();
03489   KMMessagePart part;
03490   DwBodyPart *dwpart = msg->findPart( mPartIndex );
03491   KMMessage::bodyPart( dwpart, &part, true );
03492 
03493   DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
03494   assert( parentNode );
03495   parentNode->RemoveBodyPart( dwpart );
03496 
03497   KMMessagePart att;
03498   att.duplicate( part );
03499   att.setBodyEncodedBinary( data );
03500 
03501   DwBodyPart* newDwPart = msg->createDWBodyPart( &att );
03502   parentNode->AddBodyPart( newDwPart );
03503   msg->getTopLevelPart()->Assemble();
03504 
03505   KMMessage *newMsg = new KMMessage();
03506   newMsg->fromDwString( msg->asDwString() );
03507   newMsg->setStatus( msg->status() );
03508 
03509   storeChangedMessage( newMsg );
03510 }
03511 
03512 
03513 CreateTodoCommand::CreateTodoCommand(QWidget * parent, KMMessage * msg)
03514   : KMCommand( parent, msg )
03515 {
03516 }
03517 
03518 KMCommand::Result CreateTodoCommand::execute()
03519 {
03520   KMMessage *msg = retrievedMessage();
03521   if ( !msg || !msg->codec() ) {
03522     return Failed;
03523   }
03524 
03525   KMail::KorgHelper::ensureRunning();
03526 
03527   QString txt = i18n("From: %1\nTo: %2\nSubject: %3").arg( msg->from() )
03528                 .arg( msg->to() ).arg( msg->subject() );
03529 
03530   KTempFile tf;
03531   tf.setAutoDelete( true );
03532   QString uri = "kmail:" + QString::number( msg->getMsgSerNum() ) + "/" + msg->msgId();
03533   tf.file()->writeBlock( msg->asDwString().c_str(), msg->asDwString().length() );
03534   tf.close();
03535 
03536   KCalendarIface_stub *iface = new KCalendarIface_stub( kapp->dcopClient(), "korganizer", "CalendarIface" );
03537   iface->openTodoEditor( i18n("Mail: %1").arg( msg->subject() ), txt, uri,
03538                          tf.name(), QStringList(), "message/rfc822", true );
03539   delete iface;
03540 
03541   return OK;
03542 }
03543 
03544 #include "kmcommands.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys