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