kmail

kmcommands.cpp

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