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