kmail

kmcommands.cpp

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