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