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