kmail

kmcommands.cpp

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