kmail

kmcommands.cpp

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