kmail

kmcommands.cpp

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