kmail

kmcommands.cpp

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