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