kmail

kmcommands.cpp

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