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