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