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