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