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 
00879     const bool alreadyGot = p->isMessage( idx );
00880 
00881     msg = p->getMsg(idx);
00882 
00883     if ( msg ) {
00884       // Only unGet the message if it isn't already got.
00885       if ( !alreadyGot ) {
00886         mUngetMsgs.append( msg );
00887       }
00888       if ( msg->transferInProgress() ) {
00889         QByteArray data = QByteArray();
00890         mJob->sendAsyncData( data );
00891       }
00892       msg->setTransferInProgress( true );
00893       if ( msg->isComplete() ) {
00894         slotMessageRetrievedForSaving( msg );
00895       } else {
00896         // retrieve Message first
00897         if ( msg->parent()  && !msg->isComplete() ) {
00898           FolderJob *job = msg->parent()->createJob( msg );
00899           job->setCancellable( false );
00900           connect(job, SIGNAL( messageRetrieved( KMMessage* ) ),
00901                   this, SLOT( slotMessageRetrievedForSaving( KMMessage* ) ) );
00902           job->start();
00903         }
00904       }
00905     } else {
00906       mJob->slotError( KIO::ERR_ABORTED,
00907                        i18n("The message was removed while saving it. "
00908                             "It has not been saved.") );
00909     }
00910   } else {
00911     if ( mStandAloneMessage ) {
00912       // do the special case of a standalone message
00913       slotMessageRetrievedForSaving( mStandAloneMessage );
00914       mStandAloneMessage = 0;
00915     } else {
00916       // No more messages. Tell the putjob we are done.
00917       QByteArray data = QByteArray();
00918       mJob->sendAsyncData( data );
00919     }
00920   }
00921 }
00922 
00923 void KMSaveMsgCommand::slotMessageRetrievedForSaving(KMMessage *msg)
00924 {
00925   if ( msg ) {
00926     mData = KMFolderMbox::escapeFrom( msg->asDwString() );
00927     KMail::Util::insert( mData, 0, msg->mboxMessageSeparator() );
00928     KMail::Util::append( mData, "\n" );
00929     msg->setTransferInProgress(false);
00930 
00931     mOffset = 0;
00932     QByteArray data;
00933     int size;
00934     // Unless it is great than 64 k send the whole message. kio buffers for us.
00935     if( mData.size() > (unsigned int) MAX_CHUNK_SIZE )
00936       size = MAX_CHUNK_SIZE;
00937     else
00938       size = mData.size();
00939 
00940     data.duplicate( mData, size );
00941     mJob->sendAsyncData( data );
00942     mOffset += size;
00943   }
00944   ++mMsgListIndex;
00945   // Get rid of the message.
00946   if ( msg && msg->parent() && msg->getMsgSerNum() &&
00947        mUngetMsgs.contains( msg ) ) {
00948     int idx = -1;
00949     KMFolder * p = 0;
00950     KMMsgDict::instance()->getLocation( msg, &p, &idx );
00951     assert( p == msg->parent() ); assert( idx >= 0 );
00952     p->unGetMsg( idx );
00953     p->close("kmcommand");
00954   }
00955 }
00956 
00957 void KMSaveMsgCommand::slotSaveResult(KIO::Job *job)
00958 {
00959   if (job->error())
00960   {
00961     if (job->error() == KIO::ERR_FILE_ALREADY_EXIST)
00962     {
00963       if (KMessageBox::warningContinueCancel(0,
00964         i18n("File %1 exists.\nDo you want to replace it?")
00965         .arg(mUrl.prettyURL()), i18n("Save to File"), i18n("&Replace"))
00966         == KMessageBox::Continue) {
00967         mOffset = 0;
00968 
00969         mJob = KIO::put( mUrl, S_IRUSR|S_IWUSR, true, false );
00970         mJob->slotTotalSize( mTotalSize );
00971         mJob->setAsyncDataEnabled( true );
00972         mJob->setReportDataSent( true );
00973         connect(mJob, SIGNAL(dataReq(KIO::Job*, QByteArray &)),
00974             SLOT(slotSaveDataReq()));
00975         connect(mJob, SIGNAL(result(KIO::Job*)),
00976             SLOT(slotSaveResult(KIO::Job*)));
00977       }
00978     }
00979     else
00980     {
00981       job->showErrorDialog();
00982       setResult( Failed );
00983       emit completed( this );
00984       deleteLater();
00985     }
00986   } else {
00987     setResult( OK );
00988     emit completed( this );
00989     deleteLater();
00990   }
00991 }
00992 
00993 //-----------------------------------------------------------------------------
00994 
00995 KMOpenMsgCommand::KMOpenMsgCommand( QWidget *parent, const KURL & url,
00996                                     const QString & encoding )
00997   : KMCommand( parent ),
00998     mUrl( url ),
00999     mEncoding( encoding )
01000 {
01001   setDeletesItself( true );
01002 }
01003 
01004 KMCommand::Result KMOpenMsgCommand::execute()
01005 {
01006   if ( mUrl.isEmpty() ) {
01007     mUrl = KFileDialog::getOpenURL( ":OpenMessage", "message/rfc822 application/mbox",
01008                                     parentWidget(), i18n("Open Message") );
01009   }
01010   if ( mUrl.isEmpty() ) {
01011     setDeletesItself( false );
01012     return Canceled;
01013   }
01014   mJob = KIO::get( mUrl, false, false );
01015   mJob->setReportDataSent( true );
01016   connect( mJob, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
01017            this, SLOT( slotDataArrived( KIO::Job*, const QByteArray & ) ) );
01018   connect( mJob, SIGNAL( result( KIO::Job * ) ),
01019            SLOT( slotResult( KIO::Job * ) ) );
01020   setEmitsCompletedItself( true );
01021   return OK;
01022 }
01023 
01024 void KMOpenMsgCommand::slotDataArrived( KIO::Job *, const QByteArray & data )
01025 {
01026   if ( data.isEmpty() )
01027     return;
01028 
01029   mMsgString.append( data.data(), data.size() );
01030 }
01031 
01032 void KMOpenMsgCommand::slotResult( KIO::Job *job )
01033 {
01034   if ( job->error() ) {
01035     // handle errors
01036     job->showErrorDialog();
01037     setResult( Failed );
01038     emit completed( this );
01039   }
01040   else {
01041     int startOfMessage = 0;
01042     if ( mMsgString.compare( 0, 5, "From ", 5 ) == 0 ) {
01043       startOfMessage = mMsgString.find( '\n' );
01044       if ( startOfMessage == -1 ) {
01045         KMessageBox::sorry( parentWidget(),
01046                             i18n( "The file does not contain a message." ) );
01047         setResult( Failed );
01048         emit completed( this );
01049         // Emulate closing of a secondary window so that KMail exits in case it
01050         // was started with the --view command line option. Otherwise an
01051         // invisible KMail would keep running.
01052         SecondaryWindow *win = new SecondaryWindow();
01053         win->close();
01054         win->deleteLater();
01055         deleteLater();
01056         return;
01057       }
01058       startOfMessage += 1; // the message starts after the '\n'
01059     }
01060     // check for multiple messages in the file
01061     bool multipleMessages = true;
01062     int endOfMessage = mMsgString.find( "\nFrom " );
01063     if ( endOfMessage == -1 ) {
01064       endOfMessage = mMsgString.length();
01065       multipleMessages = false;
01066     }
01067     DwMessage *dwMsg = new DwMessage;
01068     dwMsg->FromString( mMsgString.substr( startOfMessage,
01069                                           endOfMessage - startOfMessage ) );
01070     dwMsg->Parse();
01071     // check whether we have a message ( no headers => this isn't a message )
01072     if ( dwMsg->Headers().NumFields() == 0 ) {
01073       KMessageBox::sorry( parentWidget(),
01074                           i18n( "The file does not contain a message." ) );
01075       delete dwMsg; dwMsg = 0;
01076       setResult( Failed );
01077       emit completed( this );
01078       // Emulate closing of a secondary window (see above).
01079       SecondaryWindow *win = new SecondaryWindow();
01080       win->close();
01081       win->deleteLater();
01082       deleteLater();
01083       return;
01084     }
01085     KMMessage *msg = new KMMessage( dwMsg );
01086     msg->setReadyToShow( true );
01087     KMReaderMainWin *win = new KMReaderMainWin();
01088     win->showMsg( mEncoding, msg );
01089     win->show();
01090     if ( multipleMessages )
01091       KMessageBox::information( win,
01092                                 i18n( "The file contains multiple messages. "
01093                                       "Only the first message is shown." ) );
01094     setResult( OK );
01095     emit completed( this );
01096   }
01097   deleteLater();
01098 }
01099 
01100 //-----------------------------------------------------------------------------
01101 
01102 //TODO: ReplyTo, NoQuoteReplyTo, ReplyList, ReplyToAll, ReplyAuthor
01103 //      are all similar and should be factored
01104 KMReplyToCommand::KMReplyToCommand( QWidget *parent, KMMessage *msg,
01105                                     const QString &selection )
01106   : KMCommand( parent, msg ), mSelection( selection )
01107 {
01108 }
01109 
01110 KMCommand::Result KMReplyToCommand::execute()
01111 {
01112   KCursorSaver busy(KBusyPtr::busy());
01113   KMMessage *msg = retrievedMessage();
01114   if ( !msg || !msg->codec() ) {
01115     return Failed;
01116   }
01117   KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection );
01118   KMail::Composer * win = KMail::makeComposer( reply );
01119   win->setCharset( msg->codec()->mimeName(), true );
01120   win->setReplyFocus();
01121   win->show();
01122 
01123   return OK;
01124 }
01125 
01126 
01127 KMNoQuoteReplyToCommand::KMNoQuoteReplyToCommand( QWidget *parent,
01128                                                   KMMessage *msg )
01129   : KMCommand( parent, msg )
01130 {
01131 }
01132 
01133 KMCommand::Result KMNoQuoteReplyToCommand::execute()
01134 {
01135   KCursorSaver busy(KBusyPtr::busy());
01136   KMMessage *msg = retrievedMessage();
01137   if ( !msg || !msg->codec() ) {
01138     return Failed;
01139   }
01140   KMMessage *reply = msg->createReply( KMail::ReplySmart, "", true);
01141   KMail::Composer * win = KMail::makeComposer( reply );
01142   win->setCharset(msg->codec()->mimeName(), true);
01143   win->setReplyFocus(false);
01144   win->show();
01145 
01146   return OK;
01147 }
01148 
01149 
01150 KMReplyListCommand::KMReplyListCommand( QWidget *parent,
01151   KMMessage *msg, const QString &selection )
01152  : KMCommand( parent, msg ), mSelection( selection )
01153 {
01154 }
01155 
01156 KMCommand::Result KMReplyListCommand::execute()
01157 {
01158   KCursorSaver busy(KBusyPtr::busy());
01159   KMMessage *msg = retrievedMessage();
01160   if ( !msg || !msg->codec() ) {
01161     return Failed;
01162   }
01163   KMMessage *reply = msg->createReply( KMail::ReplyList, mSelection);
01164   KMail::Composer * win = KMail::makeComposer( reply );
01165   win->setCharset(msg->codec()->mimeName(), true);
01166   win->setReplyFocus(false);
01167   win->show();
01168 
01169   return OK;
01170 }
01171 
01172 
01173 KMReplyToAllCommand::KMReplyToAllCommand( QWidget *parent,
01174   KMMessage *msg, const QString &selection )
01175   :KMCommand( parent, msg ), mSelection( selection )
01176 {
01177 }
01178 
01179 KMCommand::Result KMReplyToAllCommand::execute()
01180 {
01181   KCursorSaver busy(KBusyPtr::busy());
01182   KMMessage *msg = retrievedMessage();
01183   if ( !msg || !msg->codec() ) {
01184     return Failed;
01185   }
01186   KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection );
01187   KMail::Composer * win = KMail::makeComposer( reply );
01188   win->setCharset( msg->codec()->mimeName(), true );
01189   win->setReplyFocus();
01190   win->show();
01191 
01192   return OK;
01193 }
01194 
01195 
01196 KMReplyAuthorCommand::KMReplyAuthorCommand( QWidget *parent, KMMessage *msg,
01197                                             const QString &selection )
01198   : KMCommand( parent, msg ), mSelection( selection )
01199 {
01200 }
01201 
01202 KMCommand::Result KMReplyAuthorCommand::execute()
01203 {
01204   KCursorSaver busy(KBusyPtr::busy());
01205   KMMessage *msg = retrievedMessage();
01206   if ( !msg || !msg->codec() ) {
01207     return Failed;
01208   }
01209   KMMessage *reply = msg->createReply( KMail::ReplyAuthor, mSelection );
01210   KMail::Composer * win = KMail::makeComposer( reply );
01211   win->setCharset( msg->codec()->mimeName(), true );
01212   win->setReplyFocus();
01213   win->show();
01214 
01215   return OK;
01216 }
01217 
01218 
01219 KMForwardInlineCommand::KMForwardInlineCommand( QWidget *parent,
01220   const QPtrList<KMMsgBase> &msgList, uint identity )
01221   : KMCommand( parent, msgList ),
01222     mIdentity( identity )
01223 {
01224 }
01225 
01226 KMForwardInlineCommand::KMForwardInlineCommand( QWidget *parent,
01227   KMMessage *msg, uint identity )
01228   : KMCommand( parent, msg ),
01229     mIdentity( identity )
01230 {
01231 }
01232 
01233 KMCommand::Result KMForwardInlineCommand::execute()
01234 {
01235   QPtrList<KMMessage> msgList = retrievedMsgs();
01236 
01237   if (msgList.count() >= 2) { // Multiple forward
01238 
01239     uint id = 0;
01240     QPtrList<KMMessage> linklist;
01241     for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
01242       // set the identity
01243       if (id == 0)
01244         id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
01245 
01246       // msgText += msg->createForwardBody();
01247       linklist.append( msg );
01248     }
01249     if ( id == 0 )
01250       id = mIdentity; // use folder identity if no message had an id set
01251     KMMessage *fwdMsg = new KMMessage;
01252     fwdMsg->initHeader( id );
01253     fwdMsg->setAutomaticFields( true );
01254     fwdMsg->setCharset( "utf-8" );
01255     // fwdMsg->setBody( msgText );
01256 
01257     for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) {
01258       TemplateParser parser( fwdMsg, TemplateParser::Forward );
01259       parser.setSelection( msg->body() ); // FIXME: Why is this needed?
01260       parser.process( msg, 0, true );
01261 
01262       fwdMsg->link( msg, KMMsgStatusForwarded );
01263     }
01264 
01265     KCursorSaver busy( KBusyPtr::busy() );
01266     KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
01267     win->setCharset("");
01268     win->show();
01269 
01270   } else { // forward a single message at most
01271 
01272     KMMessage *msg = msgList.getFirst();
01273     if ( !msg || !msg->codec() )
01274       return Failed;
01275 
01276     KCursorSaver busy( KBusyPtr::busy() );
01277     KMMessage *fwdMsg = msg->createForward();
01278 
01279     uint id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
01280     if ( id == 0 )
01281       id = mIdentity;
01282     {
01283       KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
01284       win->setCharset( fwdMsg->codec()->mimeName(), true );
01285       win->show();
01286     }
01287   }
01288   return OK;
01289 }
01290 
01291 
01292 KMForwardAttachedCommand::KMForwardAttachedCommand( QWidget *parent,
01293            const QPtrList<KMMsgBase> &msgList, uint identity, KMail::Composer *win )
01294   : KMCommand( parent, msgList ), mIdentity( identity ),
01295     mWin( QGuardedPtr<KMail::Composer>( win ))
01296 {
01297 }
01298 
01299 KMForwardAttachedCommand::KMForwardAttachedCommand( QWidget *parent,
01300            KMMessage * msg, uint identity, KMail::Composer *win )
01301   : KMCommand( parent, msg ), mIdentity( identity ),
01302     mWin( QGuardedPtr< KMail::Composer >( win ))
01303 {
01304 }
01305 
01306 KMCommand::Result KMForwardAttachedCommand::execute()
01307 {
01308   QPtrList<KMMessage> msgList = retrievedMsgs();
01309   KMMessage *fwdMsg = new KMMessage;
01310 
01311   if (msgList.count() >= 2) {
01312     // don't respect X-KMail-Identity headers because they might differ for
01313     // the selected mails
01314     fwdMsg->initHeader(mIdentity);
01315   }
01316   else if (msgList.count() == 1) {
01317     KMMessage *msg = msgList.getFirst();
01318     fwdMsg->initFromMessage(msg);
01319     fwdMsg->setSubject( msg->forwardSubject() );
01320   }
01321 
01322   fwdMsg->setAutomaticFields(true);
01323 
01324   KCursorSaver busy(KBusyPtr::busy());
01325   if (!mWin)
01326     mWin = KMail::makeComposer(fwdMsg, mIdentity);
01327 
01328   // iterate through all the messages to be forwarded
01329   for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) {
01330     // remove headers that shouldn't be forwarded
01331     msg->removePrivateHeaderFields();
01332     msg->removeHeaderField("BCC");
01333     // set the part
01334     KMMessagePart *msgPart = new KMMessagePart;
01335     msgPart->setTypeStr("message");
01336     msgPart->setSubtypeStr("rfc822");
01337     msgPart->setCharset(msg->charset());
01338     msgPart->setName("forwarded message");
01339     msgPart->setContentDescription(msg->from()+": "+msg->subject());
01340     msgPart->setContentDisposition( "inline" );
01341     // THIS HAS TO BE AFTER setCte()!!!!
01342     msgPart->setMessageBody( KMail::Util::ByteArray( msg->asDwString() ) );
01343     msgPart->setCharset("");
01344 
01345     fwdMsg->link(msg, KMMsgStatusForwarded);
01346     mWin->addAttach(msgPart);
01347   }
01348 
01349   mWin->show();
01350 
01351   return OK;
01352 }
01353 
01354 
01355 KMForwardDigestCommand::KMForwardDigestCommand( QWidget *parent,
01356            const QPtrList<KMMsgBase> &msgList, uint identity, KMail::Composer *win )
01357   : KMCommand( parent, msgList ), mIdentity( identity ),
01358     mWin( QGuardedPtr<KMail::Composer>( win ))
01359 {
01360 }
01361 
01362 KMForwardDigestCommand::KMForwardDigestCommand( QWidget *parent,
01363            KMMessage * msg, uint identity, KMail::Composer *win )
01364   : KMCommand( parent, msg ), mIdentity( identity ),
01365     mWin( QGuardedPtr< KMail::Composer >( win ))
01366 {
01367 }
01368 
01369 KMCommand::Result KMForwardDigestCommand::execute()
01370 {
01371   QPtrList<KMMessage> msgList = retrievedMsgs();
01372 
01373   if ( msgList.count() < 2 )
01374     return Undefined; // must have more than 1 for a digest
01375 
01376   uint id = 0;
01377   KMMessage *fwdMsg = new KMMessage;
01378   KMMessagePart *msgPart = new KMMessagePart;
01379   QString msgPartText;
01380   int msgCnt = 0; // incase there are some we can't forward for some reason
01381 
01382   // dummy header initialization; initialization with the correct identity
01383   // is done below
01384   fwdMsg->initHeader( id );
01385   fwdMsg->setAutomaticFields( true );
01386   fwdMsg->mMsg->Headers().ContentType().CreateBoundary( 1 );
01387   QCString boundary( fwdMsg->mMsg->Headers().ContentType().Boundary().c_str() );
01388   msgPartText = i18n("\nThis is a MIME digest forward. The content of the"
01389                      " message is contained in the attachment(s).\n\n\n");
01390   // iterate through all the messages to be forwarded
01391   for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
01392     // set the identity
01393     if ( id == 0 )
01394       id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
01395     // set the part header
01396     msgPartText += "--";
01397     msgPartText += QString::fromLatin1( boundary );
01398     msgPartText += "\nContent-Type: MESSAGE/RFC822";
01399     msgPartText += QString( "; CHARSET=%1" ).arg( msg->charset() );
01400     msgPartText += '\n';
01401     DwHeaders dwh;
01402     dwh.MessageId().CreateDefault();
01403     msgPartText += QString( "Content-ID: %1\n" ).arg( dwh.MessageId().AsString().c_str() );
01404     msgPartText += QString( "Content-Description: %1" ).arg( msg->subject() );
01405     if ( !msg->subject().contains( "(fwd)" ) )
01406       msgPartText += " (fwd)";
01407     msgPartText += "\n\n";
01408     // remove headers that shouldn't be forwarded
01409     msg->removePrivateHeaderFields();
01410     msg->removeHeaderField( "BCC" );
01411     // set the part
01412     msgPartText += msg->headerAsString();
01413     msgPartText += '\n';
01414     msgPartText += msg->body();
01415     msgPartText += '\n';     // eot
01416     msgCnt++;
01417     fwdMsg->link( msg, KMMsgStatusForwarded );
01418   }
01419 
01420   if ( id == 0 )
01421     id = mIdentity; // use folder identity if no message had an id set
01422   fwdMsg->initHeader( id );
01423   msgPartText += "--";
01424   msgPartText += QString::fromLatin1( boundary );
01425   msgPartText += "--\n";
01426   QCString tmp;
01427   msgPart->setTypeStr( "MULTIPART" );
01428   tmp.sprintf( "Digest; boundary=\"%s\"", boundary.data() );
01429   msgPart->setSubtypeStr( tmp );
01430   msgPart->setName( "unnamed" );
01431   msgPart->setCte( DwMime::kCte7bit );   // does it have to be 7bit?
01432   msgPart->setContentDescription( QString( "Digest of %1 messages." ).arg( msgCnt ) );
01433   // THIS HAS TO BE AFTER setCte()!!!!
01434   msgPart->setBodyEncoded( QCString( msgPartText.ascii() ) );
01435   KCursorSaver busy( KBusyPtr::busy() );
01436   KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
01437   win->addAttach( msgPart );
01438   win->show();
01439   return OK;
01440 }
01441 
01442 KMRedirectCommand::KMRedirectCommand( QWidget *parent,
01443                                       KMMessage *msg )
01444   : KMCommand( parent, msg )
01445 {
01446 }
01447 
01448 KMCommand::Result KMRedirectCommand::execute()
01449 {
01450   KMMessage *msg = retrievedMessage();
01451   if ( !msg || !msg->codec() )
01452     return Failed;
01453 
01454   RedirectDialog dlg( parentWidget(), "redirect", true,
01455                       kmkernel->msgSender()->sendImmediate() );
01456   if (dlg.exec()==QDialog::Rejected) return Failed;
01457 
01458   KMMessage *newMsg = msg->createRedirect( dlg.to() );
01459   KMFilterAction::sendMDN( msg, KMime::MDN::Dispatched );
01460 
01461   const KMail::MessageSender::SendMethod method = dlg.sendImmediate()
01462     ? KMail::MessageSender::SendImmediate
01463     : KMail::MessageSender::SendLater;
01464   if ( !kmkernel->msgSender()->send( newMsg, method ) ) {
01465     kdDebug(5006) << "KMRedirectCommand: could not redirect message (sending failed)" << endl;
01466     return Failed; // error: couldn't send
01467   }
01468   return OK;
01469 }
01470 
01471 
01472 KMCustomReplyToCommand::KMCustomReplyToCommand( QWidget *parent, KMMessage *msg,
01473                                                 const QString &selection,
01474                                                 const QString &tmpl )
01475   : KMCommand( parent, msg ), mSelection( selection ), mTemplate( tmpl )
01476 {
01477 }
01478 
01479 KMCommand::Result KMCustomReplyToCommand::execute()
01480 {
01481   KCursorSaver busy(KBusyPtr::busy());
01482   KMMessage *msg = retrievedMessage();
01483   if ( !msg || !msg->codec() ) {
01484     return Failed;
01485   }
01486   KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection,
01487                                        false, true, mTemplate );
01488   KMail::Composer * win = KMail::makeComposer( reply );
01489   win->setCharset( msg->codec()->mimeName(), true );
01490   win->setReplyFocus();
01491   win->show();
01492 
01493   return OK;
01494 }
01495 
01496 
01497 KMCustomReplyAllToCommand::KMCustomReplyAllToCommand( QWidget *parent, KMMessage *msg,
01498                                                       const QString &selection,
01499                                                       const QString &tmpl )
01500   : KMCommand( parent, msg ), mSelection( selection ), mTemplate( tmpl )
01501 {
01502 }
01503 
01504 KMCommand::Result KMCustomReplyAllToCommand::execute()
01505 {
01506   KCursorSaver busy(KBusyPtr::busy());
01507   KMMessage *msg = retrievedMessage();
01508   if ( !msg || !msg->codec() ) {
01509     return Failed;
01510   }
01511   KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection,
01512                                        false, true, mTemplate );
01513   KMail::Composer * win = KMail::makeComposer( reply );
01514   win->setCharset( msg->codec()->mimeName(), true );
01515   win->setReplyFocus();
01516   win->show();
01517 
01518   return OK;
01519 }
01520 
01521 
01522 KMCustomForwardCommand::KMCustomForwardCommand( QWidget *parent,
01523   const QPtrList<KMMsgBase> &msgList, uint identity, const QString &tmpl )
01524   : KMCommand( parent, msgList ),
01525     mIdentity( identity ), mTemplate( tmpl )
01526 {
01527 }
01528 
01529 KMCustomForwardCommand::KMCustomForwardCommand( QWidget *parent,
01530   KMMessage *msg, uint identity, const QString &tmpl )
01531   : KMCommand( parent, msg ),
01532     mIdentity( identity ), mTemplate( tmpl )
01533 {
01534 }
01535 
01536 KMCommand::Result KMCustomForwardCommand::execute()
01537 {
01538   QPtrList<KMMessage> msgList = retrievedMsgs();
01539 
01540   if (msgList.count() >= 2) { // Multiple forward
01541 
01542     uint id = 0;
01543     QPtrList<KMMessage> linklist;
01544     for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
01545       // set the identity
01546       if (id == 0)
01547         id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
01548 
01549       // msgText += msg->createForwardBody();
01550       linklist.append( msg );
01551     }
01552     if ( id == 0 )
01553       id = mIdentity; // use folder identity if no message had an id set
01554     KMMessage *fwdMsg = new KMMessage;
01555     fwdMsg->initHeader( id );
01556     fwdMsg->setAutomaticFields( true );
01557     fwdMsg->setCharset( "utf-8" );
01558     // fwdMsg->setBody( msgText );
01559 
01560     for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) {
01561       TemplateParser parser( fwdMsg, TemplateParser::Forward );
01562       parser.setSelection( msg->body() ); // FIXME: Why is this needed?
01563       parser.process( msg, 0, true );
01564 
01565       fwdMsg->link( msg, KMMsgStatusForwarded );
01566     }
01567 
01568     KCursorSaver busy( KBusyPtr::busy() );
01569     KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
01570     win->setCharset("");
01571     win->show();
01572 
01573   } else { // forward a single message at most
01574 
01575     KMMessage *msg = msgList.getFirst();
01576     if ( !msg || !msg->codec() )
01577       return Failed;
01578 
01579     KCursorSaver busy( KBusyPtr::busy() );
01580     KMMessage *fwdMsg = msg->createForward( mTemplate );
01581 
01582     uint id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
01583     if ( id == 0 )
01584       id = mIdentity;
01585     {
01586       KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
01587       win->setCharset( fwdMsg->codec()->mimeName(), true );
01588       win->show();
01589     }
01590   }
01591   return OK;
01592 }
01593 
01594 
01595 KMPrintCommand::KMPrintCommand( QWidget *parent, KMMessage *msg,
01596                                 const KMail::HeaderStyle *headerStyle,
01597                                 const KMail::HeaderStrategy *headerStrategy,
01598                                 bool htmlOverride, bool htmlLoadExtOverride,
01599                                 bool useFixedFont, const QString & encoding )
01600   : KMCommand( parent, msg ),
01601     mHeaderStyle( headerStyle ), mHeaderStrategy( headerStrategy ),
01602     mHtmlOverride( htmlOverride ),
01603     mHtmlLoadExtOverride( htmlLoadExtOverride ),
01604     mUseFixedFont( useFixedFont ), mEncoding( encoding )
01605 {
01606   mOverrideFont = KGlobalSettings::generalFont();
01607 }
01608 
01609 
01610 void KMPrintCommand::setOverrideFont( const QFont& font )
01611 {
01612   mOverrideFont = font;
01613 }
01614 
01615 KMCommand::Result KMPrintCommand::execute()
01616 {
01617   KMReaderWin printWin( 0, 0, 0 );
01618   printWin.setPrinting( true );
01619   printWin.readConfig();
01620   if ( mHeaderStyle != 0 && mHeaderStrategy != 0 )
01621     printWin.setHeaderStyleAndStrategy( mHeaderStyle, mHeaderStrategy );
01622   printWin.setHtmlOverride( mHtmlOverride );
01623   printWin.setHtmlLoadExtOverride( mHtmlLoadExtOverride );
01624   printWin.setUseFixedFont( mUseFixedFont );
01625   printWin.setOverrideEncoding( mEncoding );
01626   printWin.setPrintFont( mOverrideFont );
01627   printWin.setDecryptMessageOverwrite( true );
01628   printWin.setMsg( retrievedMessage(), true );
01629   printWin.printMsg();
01630 
01631   return OK;
01632 }
01633 
01634 
01635 KMSetStatusCommand::KMSetStatusCommand( KMMsgStatus status,
01636   const QValueList<Q_UINT32> &serNums, bool toggle )
01637   : mStatus( status ), mSerNums( serNums ), mToggle( toggle )
01638 {
01639 }
01640 
01641 KMCommand::Result KMSetStatusCommand::execute()
01642 {
01643   QValueListIterator<Q_UINT32> it;
01644   int idx = -1;
01645   KMFolder *folder = 0;
01646   bool parentStatus = false;
01647 
01648   // Toggle actions on threads toggle the whole thread
01649   // depending on the state of the parent.
01650   if (mToggle) {
01651     KMMsgBase *msg;
01652     KMMsgDict::instance()->getLocation( *mSerNums.begin(), &folder, &idx );
01653     if (folder) {
01654       msg = folder->getMsgBase(idx);
01655       if (msg && (msg->status()&mStatus))
01656         parentStatus = true;
01657       else
01658         parentStatus = false;
01659     }
01660   }
01661   QMap< KMFolder*, QValueList<int> > folderMap;
01662   for ( it = mSerNums.begin(); it != mSerNums.end(); ++it ) {
01663     KMMsgDict::instance()->getLocation( *it, &folder, &idx );
01664     if (folder) {
01665       if (mToggle) {
01666         KMMsgBase *msg = folder->getMsgBase(idx);
01667         // check if we are already at the target toggle state
01668         if (msg) {
01669           bool myStatus;
01670           if (msg->status()&mStatus)
01671             myStatus = true;
01672           else
01673             myStatus = false;
01674           if (myStatus != parentStatus)
01675             continue;
01676         }
01677       }
01678       /* Collect the ids for each folder in a separate list and
01679          send them off in one go at the end. */
01680       folderMap[folder].append(idx);
01681     }
01682   }
01683   QMapIterator< KMFolder*, QValueList<int> > it2 = folderMap.begin();
01684   while ( it2 != folderMap.end() ) {
01685      KMFolder *f = it2.key();
01686      f->setStatus( (*it2), mStatus, mToggle );
01687      ++it2;
01688   }
01689   //kapp->dcopClient()->emitDCOPSignal( "unreadCountChanged()", QByteArray() );
01690 
01691   return OK;
01692 }
01693 
01694 
01695 KMFilterCommand::KMFilterCommand( const QCString &field, const QString &value )
01696   : mField( field ), mValue( value )
01697 {
01698 }
01699 
01700 KMCommand::Result KMFilterCommand::execute()
01701 {
01702   kmkernel->filterMgr()->createFilter( mField, mValue );
01703 
01704   return OK;
01705 }
01706 
01707 
01708 KMFilterActionCommand::KMFilterActionCommand( QWidget *parent,
01709                                               const QPtrList<KMMsgBase> &msgList,
01710                                               KMFilter *filter )
01711   : KMCommand( parent, msgList ), mFilter( filter  )
01712 {
01713   QPtrListIterator<KMMsgBase> it(msgList);
01714   while ( it.current() ) {
01715     serNumList.append( (*it)->getMsgSerNum() );
01716     ++it;
01717   }
01718 }
01719 
01720 KMCommand::Result KMFilterActionCommand::execute()
01721 {
01722   KCursorSaver busy( KBusyPtr::busy() );
01723 
01724   int msgCount = 0;
01725   int msgCountToFilter = serNumList.count();
01726   ProgressItem* progressItem =
01727     ProgressManager::createProgressItem ( "filter"+ProgressManager::getUniqueID(),
01728                                           i18n( "Filtering messages" ) );
01729   progressItem->setTotalItems( msgCountToFilter );
01730   QValueList<Q_UINT32>::const_iterator it;
01731   for ( it = serNumList.begin(); it != serNumList.end(); it++ ) {
01732     Q_UINT32 serNum = *it;
01733     int diff = msgCountToFilter - ++msgCount;
01734     if ( diff < 10 || !( msgCount % 20 ) || msgCount <= 10 ) {
01735       progressItem->updateProgress();
01736       QString statusMsg = i18n("Filtering message %1 of %2");
01737       statusMsg = statusMsg.arg( msgCount ).arg( msgCountToFilter );
01738       KPIM::BroadcastStatus::instance()->setStatusMsg( statusMsg );
01739       KApplication::kApplication()->eventLoop()->processEvents( QEventLoop::ExcludeUserInput, 50 );
01740     }
01741 
01742     int filterResult = kmkernel->filterMgr()->process( serNum, mFilter );
01743     if (filterResult == 2) {
01744       // something went horribly wrong (out of space?)
01745       perror("Critical error");
01746       kmkernel->emergencyExit( i18n("Not enough free disk space?" ));
01747     }
01748     progressItem->incCompletedItems();
01749   }
01750 
01751   progressItem->setComplete();
01752   progressItem = 0;
01753   return OK;
01754 }
01755 
01756 
01757 KMMetaFilterActionCommand::KMMetaFilterActionCommand( KMFilter *filter,
01758                                                       KMHeaders *headers,
01759                                                       KMMainWidget *main )
01760     : QObject( main ),
01761       mFilter( filter ), mHeaders( headers ), mMainWidget( main )
01762 {
01763 }
01764 
01765 void KMMetaFilterActionCommand::start()
01766 {
01767   if (ActionScheduler::isEnabled() ) {
01768     // use action scheduler
01769     KMFilterMgr::FilterSet set = KMFilterMgr::All;
01770     QValueList<KMFilter*> filters;
01771     filters.append( mFilter );
01772     ActionScheduler *scheduler = new ActionScheduler( set, filters, mHeaders );
01773     scheduler->setAlwaysMatch( true );
01774     scheduler->setAutoDestruct( true );
01775 
01776     int contentX, contentY;
01777     HeaderItem *nextItem = mHeaders->prepareMove( &contentX, &contentY );
01778     QPtrList<KMMsgBase> msgList = *mHeaders->selectedMsgs(true);
01779     mHeaders->finalizeMove( nextItem, contentX, contentY );
01780 
01781     for (KMMsgBase *msg = msgList.first(); msg; msg = msgList.next())
01782       scheduler->execFilters( msg );
01783   } else {
01784     KMCommand *filterCommand =
01785       new KMFilterActionCommand( mMainWidget,
01786                                  *mHeaders->selectedMsgs(), mFilter );
01787     filterCommand->start();
01788     int contentX, contentY;
01789     HeaderItem *item = mHeaders->prepareMove( &contentX, &contentY );
01790     mHeaders->finalizeMove( item, contentX, contentY );
01791   }
01792 }
01793 
01794 FolderShortcutCommand::FolderShortcutCommand( KMMainWidget *mainwidget,
01795                                               KMFolder *folder )
01796     : mMainWidget( mainwidget ), mFolder( folder ), mAction( 0 )
01797 {
01798 }
01799 
01800 
01801 FolderShortcutCommand::~FolderShortcutCommand()
01802 {
01803   if ( mAction ) mAction->unplugAll();
01804   delete mAction;
01805 }
01806 
01807 void FolderShortcutCommand::start()
01808 {
01809   mMainWidget->slotSelectFolder( mFolder );
01810 }
01811 
01812 void FolderShortcutCommand::setAction( KAction* action )
01813 {
01814   mAction = action;
01815 }
01816 
01817 KMMailingListFilterCommand::KMMailingListFilterCommand( QWidget *parent,
01818                                                         KMMessage *msg )
01819   : KMCommand( parent, msg )
01820 {
01821 }
01822 
01823 KMCommand::Result KMMailingListFilterCommand::execute()
01824 {
01825   QCString name;
01826   QString value;
01827   KMMessage *msg = retrievedMessage();
01828   if (!msg)
01829     return Failed;
01830 
01831   if ( !MailingList::name( msg, name, value ).isEmpty() ) {
01832     kmkernel->filterMgr()->createFilter( name, value );
01833     return OK;
01834   }
01835   else
01836     return Failed;
01837 }
01838 
01839 
01840 void KMMenuCommand::folderToPopupMenu(bool move,
01841   QObject *receiver, KMMenuToFolder *aMenuToFolder, QPopupMenu *menu )
01842 {
01843   while ( menu->count() )
01844   {
01845     QPopupMenu *popup = menu->findItem( menu->idAt( 0 ) )->popup();
01846     if (popup)
01847       delete popup;
01848     else
01849       menu->removeItemAt( 0 );
01850   }
01851 
01852   if (!kmkernel->imapFolderMgr()->dir().first() &&
01853       !kmkernel->dimapFolderMgr()->dir().first())
01854   { // only local folders
01855     makeFolderMenu( &kmkernel->folderMgr()->dir(), move,
01856                     receiver, aMenuToFolder, menu );
01857   } else {
01858     // operate on top-level items
01859     QPopupMenu* subMenu = new QPopupMenu(menu);
01860     makeFolderMenu( &kmkernel->folderMgr()->dir(),
01861                     move, receiver, aMenuToFolder, subMenu );
01862     menu->insertItem( i18n( "Local Folders" ), subMenu );
01863     KMFolderDir* fdir = &kmkernel->imapFolderMgr()->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     fdir = &kmkernel->dimapFolderMgr()->dir();
01872     for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) {
01873       if (node->isDir())
01874         continue;
01875       subMenu = new QPopupMenu(menu);
01876       makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu );
01877       menu->insertItem( node->label(), subMenu );
01878     }
01879   }
01880 }
01881 
01882 void KMMenuCommand::makeFolderMenu(KMFolderNode* node, bool move,
01883   QObject *receiver, KMMenuToFolder *aMenuToFolder, QPopupMenu *menu )
01884 {
01885   // connect the signals
01886   if (move)
01887   {
01888     disconnect(menu, SIGNAL(activated(int)), receiver,
01889            SLOT(moveSelectedToFolder(int)));
01890     connect(menu, SIGNAL(activated(int)), receiver,
01891              SLOT(moveSelectedToFolder(int)));
01892   } else {
01893     disconnect(menu, SIGNAL(activated(int)), receiver,
01894            SLOT(copySelectedToFolder(int)));
01895     connect(menu, SIGNAL(activated(int)), receiver,
01896              SLOT(copySelectedToFolder(int)));
01897   }
01898 
01899   KMFolder *folder = 0;
01900   KMFolderDir *folderDir = 0;
01901   if (node->isDir()) {
01902     folderDir = static_cast<KMFolderDir*>(node);
01903   } else {
01904     folder = static_cast<KMFolder*>(node);
01905     folderDir = folder->child();
01906   }
01907 
01908   if (folder && !folder->noContent())
01909   {
01910     int menuId;
01911     if (move)
01912       menuId = menu->insertItem(i18n("Move to This Folder"));
01913     else
01914       menuId = menu->insertItem(i18n("Copy to This Folder"));
01915     aMenuToFolder->insert( menuId, folder );
01916     menu->setItemEnabled( menuId, !folder->isReadOnly() );
01917     menu->insertSeparator();
01918   }
01919 
01920   if (!folderDir)
01921     return;
01922 
01923   for (KMFolderNode *it = folderDir->first(); it; it = folderDir->next() ) {
01924     if (it->isDir())
01925       continue;
01926     KMFolder *child = static_cast<KMFolder*>(it);
01927     QString label = child->label();
01928     label.replace("&","&&");
01929     if (child->child() && child->child()->first()) {
01930       // descend
01931       QPopupMenu *subMenu = new QPopupMenu(menu, "subMenu");
01932       makeFolderMenu( child, move, receiver,
01933                       aMenuToFolder, subMenu );
01934       menu->insertItem( label, subMenu );
01935     } else {
01936       // insert an item
01937       int menuId = menu->insertItem( label );
01938       aMenuToFolder->insert( menuId, child );
01939       menu->setItemEnabled( menuId, !child->isReadOnly() );
01940     }
01941   }
01942   return;
01943 }
01944 
01945 
01946 KMCopyCommand::KMCopyCommand( KMFolder* destFolder,
01947                               const QPtrList<KMMsgBase> &msgList )
01948 :mDestFolder( destFolder ), mMsgList( msgList )
01949 {
01950   setDeletesItself( true );
01951 }
01952 
01953 KMCopyCommand::KMCopyCommand( KMFolder* destFolder, KMMessage * msg )
01954   :mDestFolder( destFolder )
01955 {
01956   setDeletesItself( true );
01957   mMsgList.append( &msg->toMsgBase() );
01958 }
01959 
01960 KMCommand::Result KMCopyCommand::execute()
01961 {
01962   KMMsgBase *msgBase;
01963   KMMessage *msg, *newMsg;
01964   int idx = -1;
01965   bool isMessage;
01966   QPtrList<KMMessage> list;
01967   QPtrList<KMMessage> localList;
01968 
01969   if (mDestFolder && mDestFolder->open("kmcommand") != 0)
01970   {
01971     deleteLater();
01972     return Failed;
01973   }
01974 
01975   setEmitsCompletedItself( true );
01976   KCursorSaver busy(KBusyPtr::busy());
01977 
01978   for (msgBase = mMsgList.first(); msgBase; msgBase = mMsgList.next() )
01979   {
01980     KMFolder *srcFolder = msgBase->parent();
01981     if (( isMessage = msgBase->isMessage() ))
01982     {
01983       msg = static_cast<KMMessage*>(msgBase);
01984     } else {
01985       idx = srcFolder->find(msgBase);
01986       assert(idx != -1);
01987       msg = srcFolder->getMsg(idx);
01988       // corrupt IMAP cache, see FolderStorage::getMsg()
01989       if ( msg == 0 ) {
01990         KMessageBox::error( parentWidget(), i18n("Corrupt IMAP cache detected in folder %1. "
01991             "Copying of messages aborted.").arg( srcFolder->prettyURL() ) );
01992         deleteLater();
01993         return Failed;
01994       }
01995     }
01996 
01997     if (srcFolder && mDestFolder &&
01998         (srcFolder->folderType()== KMFolderTypeImap) &&
01999         (mDestFolder->folderType() == KMFolderTypeImap) &&
02000         (static_cast<KMFolderImap*>(srcFolder->storage())->account() ==
02001          static_cast<KMFolderImap*>(mDestFolder->storage())->account()))
02002     {
02003       // imap => imap with same account
02004       list.append(msg);
02005     } else {
02006       newMsg = new KMMessage( new DwMessage( *msg->asDwMessage() ) );
02007       newMsg->setComplete(msg->isComplete());
02008       // make sure the attachment state is only calculated when it's complete
02009       if (!newMsg->isComplete())
02010         newMsg->setReadyToShow(false);
02011       newMsg->setStatus(msg->status());
02012 
02013       if (srcFolder && !newMsg->isComplete())
02014       {
02015         // imap => others
02016         newMsg->setParent(msg->parent());
02017         FolderJob *job = srcFolder->createJob(newMsg);
02018         job->setCancellable( false );
02019         mPendingJobs << job;
02020         connect(job, SIGNAL(messageRetrieved(KMMessage*)),
02021                 mDestFolder, SLOT(reallyAddCopyOfMsg(KMMessage*)));
02022         connect( job, SIGNAL(result(KMail::FolderJob*)),
02023                  this, SLOT(slotJobFinished(KMail::FolderJob*)) );
02024         job->start();
02025       } else {
02026         // local => others
02027         localList.append(newMsg);
02028       }
02029     }
02030 
02031     if (srcFolder && !isMessage && list.isEmpty())
02032     {
02033       assert(idx != -1);
02034       srcFolder->unGetMsg( idx );
02035     }
02036 
02037   } // end for
02038 
02039   bool deleteNow = false;
02040   if (!localList.isEmpty())
02041   {
02042     QValueList<int> index;
02043     mDestFolder->addMsg( localList, index );
02044     for ( QValueListIterator<int> it = index.begin(); it != index.end(); ++it ) {
02045       mDestFolder->unGetMsg( *it );
02046     }
02047     if ( mDestFolder->folderType() == KMFolderTypeImap ) {
02048       if ( mPendingJobs.isEmpty() ) {
02049         // wait for the end of the copy before closing the folder
02050         KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
02051         connect( imapDestFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
02052             this, SLOT( slotFolderComplete( KMFolderImap*, bool ) ) );
02053       }
02054     } else {
02055       deleteNow = list.isEmpty() && mPendingJobs.isEmpty(); // we're done if there are no other mails we need to fetch
02056     }
02057   }
02058 
02059 //TODO: Get rid of the other cases just use this one for all types of folder
02060 //TODO: requires adding copyMsg and getFolder methods to KMFolder.h
02061   if (!list.isEmpty())
02062   {
02063     // copy the message(s); note: the list is empty afterwards!
02064     KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
02065     connect( imapDestFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
02066         this, SLOT( slotFolderComplete( KMFolderImap*, bool ) ) );
02067     imapDestFolder->copyMsg(list);
02068     imapDestFolder->getFolder();
02069   }
02070 
02071   // only close the folder and delete the job if we're done
02072   // otherwise this is done in slotMsgAdded or slotFolderComplete
02073   if ( deleteNow )
02074   {
02075     mDestFolder->close("kmcommand");
02076     setResult( OK );
02077     emit completed( this );
02078     deleteLater();
02079   }
02080 
02081   return OK;
02082 }
02083 
02084 void KMCopyCommand::slotJobFinished(KMail::FolderJob * job)
02085 {
02086   mPendingJobs.remove( job );
02087   if ( job->error() ) {
02088     kdDebug(5006) << k_funcinfo << "folder job failed: " << job->error() << endl;
02089     // kill all pending jobs
02090     for ( QValueList<KMail::FolderJob*>::Iterator it = mPendingJobs.begin(); it != mPendingJobs.end(); ++it ) {
02091       disconnect( (*it), SIGNAL(result(KMail::FolderJob*)),
02092                   this, SLOT(slotJobFinished(KMail::FolderJob*)) );
02093       (*it)->kill();
02094     }
02095     mPendingJobs.clear();
02096     setResult( Failed );
02097   }
02098 
02099   if ( mPendingJobs.isEmpty() )
02100   {
02101     mDestFolder->close("kmcommand");
02102     emit completed( this );
02103     deleteLater();
02104   }
02105 }
02106 
02107 void KMCopyCommand::slotFolderComplete( KMFolderImap*, bool success )
02108 {
02109   kdDebug(5006) << k_funcinfo << success << endl;
02110   if ( !success )
02111     setResult( Failed );
02112   mDestFolder->close( "kmcommand" );
02113   emit completed( this );
02114   deleteLater();
02115 }
02116 
02117 
02118 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
02119                               const QPtrList<KMMsgBase> &msgList)
02120   : mDestFolder( destFolder ), mProgressItem( 0 )
02121 {
02122   QPtrList<KMMsgBase> tmp = msgList;
02123   for ( KMMsgBase *msgBase = tmp.first(); msgBase; msgBase = tmp.next() )
02124     mSerNumList.append( msgBase->getMsgSerNum() );
02125 }
02126 
02127 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
02128                               KMMessage *msg )
02129   : mDestFolder( destFolder ), mProgressItem( 0 )
02130 {
02131   mSerNumList.append( msg->getMsgSerNum() );
02132 }
02133 
02134 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
02135                               KMMsgBase *msgBase )
02136   : mDestFolder( destFolder ), mProgressItem( 0 )
02137 {
02138   mSerNumList.append( msgBase->getMsgSerNum() );
02139 }
02140 
02141 KMMoveCommand::KMMoveCommand( Q_UINT32 )
02142   : mProgressItem( 0 )
02143 {
02144 }
02145 
02146 KMCommand::Result KMMoveCommand::execute()
02147 {
02148   setEmitsCompletedItself( true );
02149   setDeletesItself( true );
02150   typedef QMap< KMFolder*, QPtrList<KMMessage>* > FolderToMessageListMap;
02151   FolderToMessageListMap folderDeleteList;
02152 
02153   if (mDestFolder && mDestFolder->open("kmcommand") != 0) {
02154     completeMove( Failed );
02155     return Failed;
02156   }
02157   KCursorSaver busy(KBusyPtr::busy());
02158 
02159   // TODO set SSL state according to source and destfolder connection?
02160   Q_ASSERT( !mProgressItem );
02161   mProgressItem =
02162      ProgressManager::createProgressItem (
02163          "move"+ProgressManager::getUniqueID(),
02164          mDestFolder ? i18n( "Moving messages" ) : i18n( "Deleting messages" ) );
02165   connect( mProgressItem, SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
02166            this, SLOT( slotMoveCanceled() ) );
02167 
02168   KMMessage *msg;
02169   int rc = 0;
02170   int index;
02171   QPtrList<KMMessage> list;
02172   int undoId = -1;
02173   mCompleteWithAddedMsg = false;
02174 
02175   if (mDestFolder) {
02176     connect (mDestFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
02177              this, SLOT(slotMsgAddedToDestFolder(KMFolder*, Q_UINT32)));
02178     mLostBoys = mSerNumList;
02179   }
02180   mProgressItem->setTotalItems( mSerNumList.count() );
02181 
02182   for ( QValueList<Q_UINT32>::ConstIterator it = mSerNumList.constBegin(); it != mSerNumList.constEnd(); ++it ) {
02183     if ( *it == 0 ) {
02184       kdDebug(5006) << k_funcinfo << "serial number == 0!" << endl;
02185       continue; // invalid message
02186     }
02187     KMFolder *srcFolder = 0;
02188     int idx = -1;
02189     KMMsgDict::instance()->getLocation( *it, &srcFolder, &idx );
02190     if (srcFolder == mDestFolder)
02191       continue;
02192     assert(srcFolder);
02193     assert(idx != -1);
02194     if ( !srcFolder->isOpened() ) {
02195       srcFolder->open( "kmmovecommand" );
02196       mOpenedFolders.append( srcFolder );
02197     }
02198     msg = srcFolder->getMsg(idx);
02199     if ( !msg ) {
02200       kdDebug(5006) << k_funcinfo << "No message found for serial number " << *it << endl;
02201       continue;
02202     }
02203     bool undo = msg->enableUndo();
02204 
02205     if ( msg && msg->transferInProgress() &&
02206          srcFolder->folderType() == KMFolderTypeImap )
02207     {
02208       // cancel the download
02209       msg->setTransferInProgress( false, true );
02210       static_cast<KMFolderImap*>(srcFolder->storage())->ignoreJobsForMessage( msg );
02211     }
02212 
02213     if (mDestFolder) {
02214       if (mDestFolder->folderType() == KMFolderTypeImap) {
02215         /* If we are moving to an imap folder, connect to it's completed
02216          * signal so we notice when all the mails should have showed up in it
02217          * but haven't for some reason. */
02218         KMFolderImap *imapFolder = static_cast<KMFolderImap*> ( mDestFolder->storage() );
02219         disconnect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )),
02220                  this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
02221 
02222         connect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )),
02223                  this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
02224         list.append(msg);
02225       } else {
02226         // We are moving to a local folder.
02227         if ( srcFolder->folderType() == KMFolderTypeImap )
02228         {
02229           // do not complete here but wait until all messages are transferred
02230           mCompleteWithAddedMsg = true;
02231         }
02232         rc = mDestFolder->moveMsg(msg, &index);
02233         if (rc == 0 && index != -1) {
02234           KMMsgBase *mb = mDestFolder->unGetMsg( mDestFolder->count() - 1 );
02235           if (undo && mb)
02236           {
02237             if ( undoId == -1 )
02238               undoId = kmkernel->undoStack()->newUndoAction( srcFolder, mDestFolder );
02239             kmkernel->undoStack()->addMsgToAction( undoId, mb->getMsgSerNum() );
02240           }
02241         } else if (rc != 0) {
02242           // Something  went wrong. Stop processing here, it is likely that the
02243           // other moves would fail as well.
02244           completeMove( Failed );
02245           return Failed;
02246         }
02247       }
02248     } else {
02249       // really delete messages that are already in the trash folder or if
02250       // we are really, really deleting, not just moving to trash
02251       if (srcFolder->folderType() == KMFolderTypeImap) {
02252         if (!folderDeleteList[srcFolder])
02253           folderDeleteList[srcFolder] = new QPtrList<KMMessage>;
02254         folderDeleteList[srcFolder]->append( msg );
02255       } else {
02256         srcFolder->removeMsg(idx);
02257         delete msg;
02258       }
02259     }
02260   }
02261   if (!list.isEmpty() && mDestFolder) {
02262     // will be completed with folderComplete signal
02263     mDestFolder->moveMsg(list, &index);
02264   } else {
02265     FolderToMessageListMap::Iterator it;
02266     for ( it = folderDeleteList.begin(); it != folderDeleteList.end(); ++it ) {
02267       it.key()->removeMsg(*it.data());
02268       delete it.data();
02269     }
02270     if ( !mCompleteWithAddedMsg ) {
02271       // imap folders will be completed in slotMsgAddedToDestFolder
02272       completeMove( OK );
02273     }
02274   }
02275 
02276   return OK;
02277 }
02278 
02279 void KMMoveCommand::slotImapFolderCompleted(KMFolderImap* imapFolder, bool success)
02280 {
02281   disconnect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )),
02282       this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
02283   if ( success ) {
02284     // the folder was checked successfully but we were still called, so check
02285     // if we are still waiting for messages to show up. If so, uidValidity
02286     // changed, or something else went wrong. Clean up.
02287 
02288     /* Unfortunately older UW imap servers change uid validity for each put job.
02289      * Yes, it is really that broken. *sigh* So we cannot report error here, I guess. */
02290     if ( !mLostBoys.isEmpty() ) {
02291       kdDebug(5006) <<  "### Not all moved messages reported back that they were " << endl
02292                     <<  "### added to the target folder. Did uidValidity change? " << endl;
02293     }
02294     completeMove( OK );
02295   } else {
02296     // Should we inform the user here or leave that to the caller?
02297     completeMove( Failed );
02298   }
02299 }
02300 
02301 void KMMoveCommand::slotMsgAddedToDestFolder(KMFolder *folder, Q_UINT32 serNum)
02302 {
02303   if ( folder != mDestFolder || mLostBoys.find( serNum ) == mLostBoys.end() ) {
02304     //kdDebug(5006) << "KMMoveCommand::msgAddedToDestFolder different "
02305     //                 "folder or invalid serial number." << endl;
02306     return;
02307   }
02308   mLostBoys.remove(serNum);
02309   if ( mLostBoys.isEmpty() ) {
02310     // we are done. All messages transferred to the host succesfully
02311     disconnect (mDestFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
02312              this, SLOT(slotMsgAddedToDestFolder(KMFolder*, Q_UINT32)));
02313     if (mDestFolder && mDestFolder->folderType() != KMFolderTypeImap) {
02314       mDestFolder->sync();
02315     }
02316     if ( mCompleteWithAddedMsg ) {
02317       completeMove( OK );
02318     }
02319   } else {
02320     if ( mProgressItem ) {
02321       mProgressItem->incCompletedItems();
02322       mProgressItem->updateProgress();
02323     }
02324   }
02325 }
02326 
02327 void KMMoveCommand::completeMove( Result result )
02328 {
02329   if ( mDestFolder )
02330     mDestFolder->close("kmcommand");
02331   while ( !mOpenedFolders.empty() ) {
02332     KMFolder *folder = mOpenedFolders.back();
02333     mOpenedFolders.pop_back();
02334     folder->close("kmcommand");
02335   }
02336   if ( mProgressItem ) {
02337     mProgressItem->setComplete();
02338     mProgressItem = 0;
02339   }
02340   setResult( result );
02341   emit completed( this );
02342   deleteLater();
02343 }
02344 
02345 void KMMoveCommand::slotMoveCanceled()
02346 {
02347   completeMove( Canceled );
02348 }
02349 
02350 // srcFolder doesn't make much sense for searchFolders
02351 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder,
02352   const QPtrList<KMMsgBase> &msgList )
02353 :KMMoveCommand( findTrashFolder( srcFolder ), msgList)
02354 {
02355   srcFolder->open("kmcommand");
02356   mOpenedFolders.push_back( srcFolder );
02357 }
02358 
02359 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder, KMMessage * msg )
02360 :KMMoveCommand( findTrashFolder( srcFolder ), msg)
02361 {
02362   srcFolder->open("kmcommand");
02363   mOpenedFolders.push_back( srcFolder );
02364 }
02365 
02366 KMDeleteMsgCommand::KMDeleteMsgCommand( Q_UINT32 sernum )
02367 :KMMoveCommand( sernum )
02368 {
02369   if ( !sernum ) {
02370     setDestFolder( 0 );
02371     return;
02372   }
02373 
02374   KMFolder *srcFolder = 0;
02375   int idx;
02376   KMMsgDict::instance()->getLocation( sernum, &srcFolder, &idx );
02377   if ( srcFolder ) {
02378     KMMsgBase *msg = srcFolder->getMsgBase( idx );
02379     srcFolder->open("kmcommand");
02380     mOpenedFolders.push_back( srcFolder );
02381     addMsg( msg );
02382   }
02383   setDestFolder( findTrashFolder( srcFolder ) );
02384 }
02385 
02386 KMFolder * KMDeleteMsgCommand::findTrashFolder( KMFolder * folder )
02387 {
02388   KMFolder* trash = folder->trashFolder();
02389   if( !trash )
02390     trash = kmkernel->trashFolder();
02391   if( trash != folder )
02392     return trash;
02393   return 0;
02394 }
02395 
02396 
02397 KMUrlClickedCommand::KMUrlClickedCommand( const KURL &url, uint identity,
02398   KMReaderWin *readerWin, bool htmlPref, KMMainWidget *mainWidget )
02399   :mUrl( url ), mIdentity( identity ), mReaderWin( readerWin ),
02400    mHtmlPref( htmlPref ), mMainWidget( mainWidget )
02401 {
02402 }
02403 
02404 KMCommand::Result KMUrlClickedCommand::execute()
02405 {
02406   KMMessage* msg;
02407 
02408   if (mUrl.protocol() == "mailto")
02409   {
02410     msg = new KMMessage;
02411     msg->initHeader(mIdentity);
02412     msg->setCharset("utf-8");
02413     msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
02414     QString query=mUrl.query();
02415     while (!query.isEmpty()) {
02416       QString queryPart;
02417       int secondQuery = query.find('?',1);
02418       if (secondQuery != -1)
02419         queryPart = query.left(secondQuery);
02420       else
02421         queryPart = query;
02422       query = query.mid(queryPart.length());
02423 
02424       if (queryPart.left(9) == "?subject=")
02425         msg->setSubject( KURL::decode_string(queryPart.mid(9)) );
02426       else if (queryPart.left(6) == "?body=")
02427         // It is correct to convert to latin1() as URL should not contain
02428         // anything except ascii.
02429         msg->setBody( KURL::decode_string(queryPart.mid(6)).latin1() );
02430       else if (queryPart.left(4) == "?cc=")
02431         msg->setCc( KURL::decode_string(queryPart.mid(4)) );
02432     }
02433 
02434     KMail::Composer * win = KMail::makeComposer( msg, mIdentity );
02435     win->setCharset("", true);
02436     win->show();
02437   }
02438   else if ( mUrl.protocol() == "im" )
02439   {
02440     kmkernel->imProxy()->chatWithContact( mUrl.path() );
02441   }
02442   else if ((mUrl.protocol() == "http") || (mUrl.protocol() == "https") ||
02443            (mUrl.protocol() == "ftp")  || (mUrl.protocol() == "file")  ||
02444            (mUrl.protocol() == "ftps") || (mUrl.protocol() == "sftp" ) ||
02445            (mUrl.protocol() == "help") || (mUrl.protocol() == "vnc")   ||
02446            (mUrl.protocol() == "smb")  || (mUrl.protocol() == "fish")  ||
02447            (mUrl.protocol() == "news"))
02448   {
02449     KPIM::BroadcastStatus::instance()->setStatusMsg( i18n("Opening URL..."));
02450     KMimeType::Ptr mime = KMimeType::findByURL( mUrl );
02451     if (mime->name() == "application/x-desktop" ||
02452         mime->name() == "application/x-executable" ||
02453         mime->name() == "application/x-msdos-program" ||
02454         mime->name() == "application/x-shellscript" )
02455     {
02456       if (KMessageBox::warningYesNo( 0, i18n( "<qt>Do you really want to execute <b>%1</b>?</qt>" )
02457         .arg( mUrl.prettyURL() ), QString::null, i18n("Execute"), KStdGuiItem::cancel() ) != KMessageBox::Yes)
02458         return Canceled;
02459     }
02460     KRun * runner = new KRun( mUrl );
02461     runner->setRunExecutables( false );
02462   }
02463   else
02464     return Failed;
02465 
02466   return OK;
02467 }
02468 
02469 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, KMMessage *msg )
02470   : KMCommand( parent, msg ), mImplicitAttachments( true ), mEncoded( false )
02471 {
02472 }
02473 
02474 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, const QPtrList<KMMsgBase>& msgs )
02475   : KMCommand( parent, msgs ), mImplicitAttachments( true ), mEncoded( false )
02476 {
02477 }
02478 
02479 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, QPtrList<partNode>& attachments,
02480                                                     KMMessage *msg, bool encoded )
02481   : KMCommand( parent ), mImplicitAttachments( false ), mEncoded( encoded )
02482 {
02483   for ( QPtrListIterator<partNode> it( attachments ); it.current(); ++it ) {
02484     mAttachmentMap.insert( it.current(), msg );
02485   }
02486 }
02487 
02488 KMCommand::Result KMSaveAttachmentsCommand::execute()
02489 {
02490   setEmitsCompletedItself( true );
02491   if ( mImplicitAttachments ) {
02492     QPtrList<KMMessage> msgList = retrievedMsgs();
02493     KMMessage *msg;
02494     for ( QPtrListIterator<KMMessage> itr( msgList );
02495           ( msg = itr.current() );
02496           ++itr ) {
02497       partNode *rootNode = partNode::fromMessage( msg );
02498       for ( partNode *child = rootNode; child;
02499             child = child->firstChild() ) {
02500         for ( partNode *node = child; node; node = node->nextSibling() ) {
02501           if ( node->type() != DwMime::kTypeMultipart )
02502             mAttachmentMap.insert( node, msg );
02503         }
02504       }
02505     }
02506   }
02507   setDeletesItself( true );
02508   // load all parts
02509   KMLoadPartsCommand *command = new KMLoadPartsCommand( mAttachmentMap );
02510   connect( command, SIGNAL( partsRetrieved() ),
02511            this, SLOT( slotSaveAll() ) );
02512   command->start();
02513 
02514   return OK;
02515 }
02516 
02517 void KMSaveAttachmentsCommand::slotSaveAll()
02518 {
02519   // now that all message parts have been retrieved, remove all parts which
02520   // don't represent an attachment if they were not explicitely passed in the
02521   // c'tor
02522   if ( mImplicitAttachments ) {
02523     for ( PartNodeMessageMap::iterator it = mAttachmentMap.begin();
02524           it != mAttachmentMap.end(); ) {
02525       // only body parts which have a filename or a name parameter (except for
02526       // the root node for which name is set to the message's subject) are
02527       // considered attachments
02528       if ( it.key()->msgPart().fileName().stripWhiteSpace().isEmpty() &&
02529            ( it.key()->msgPart().name().stripWhiteSpace().isEmpty() ||
02530              !it.key()->parentNode() ) ) {
02531         PartNodeMessageMap::iterator delIt = it;
02532         ++it;
02533         mAttachmentMap.remove( delIt );
02534       }
02535       else
02536         ++it;
02537     }
02538     if ( mAttachmentMap.isEmpty() ) {
02539       KMessageBox::information( 0, i18n("Found no attachments to save.") );
02540       setResult( OK ); // The user has already been informed.
02541       emit completed( this );
02542       deleteLater();
02543       return;
02544     }
02545   }
02546 
02547   KURL url, dirUrl;
02548   if ( mAttachmentMap.count() > 1 ) {
02549     // get the dir
02550     dirUrl = KDirSelectDialog::selectDirectory( QString::null, false,
02551                                                 parentWidget(),
02552                                                 i18n("Save Attachments To") );
02553     if ( !dirUrl.isValid() ) {
02554       setResult( Canceled );
02555       emit completed( this );
02556       deleteLater();
02557       return;
02558     }
02559 
02560     // we may not get a slash-terminated url out of KDirSelectDialog
02561     dirUrl.adjustPath( 1 );
02562   }
02563   else {
02564     // only one item, get the desired filename
02565     partNode *node = mAttachmentMap.begin().key();
02566     // replace all ':' with '_' because ':' isn't allowed on FAT volumes
02567     QString s =
02568       node->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
02569     if ( s.isEmpty() )
02570       s = node->msgPart().name().stripWhiteSpace().replace( ':', '_' );
02571     if ( s.isEmpty() )
02572       s = i18n("filename for an unnamed attachment", "attachment.1");
02573     url = KFileDialog::getSaveURL( s, QString::null, parentWidget(),
02574                                    QString::null );
02575     if ( url.isEmpty() ) {
02576       setResult( Canceled );
02577       emit completed( this );
02578       deleteLater();
02579       return;
02580     }
02581   }
02582 
02583   QMap< QString, int > renameNumbering;
02584 
02585   Result globalResult = OK;
02586   int unnamedAtmCount = 0;
02587   for ( PartNodeMessageMap::const_iterator it = mAttachmentMap.begin();
02588         it != mAttachmentMap.end();
02589         ++it ) {
02590     KURL curUrl;
02591     if ( !dirUrl.isEmpty() ) {
02592       curUrl = dirUrl;
02593       QString s =
02594         it.key()->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
02595       if ( s.isEmpty() )
02596         s = it.key()->msgPart().name().stripWhiteSpace().replace( ':', '_' );
02597       if ( s.isEmpty() ) {
02598         ++unnamedAtmCount;
02599         s = i18n("filename for the %1-th unnamed attachment",
02600                  "attachment.%1")
02601             .arg( unnamedAtmCount );
02602       }
02603       curUrl.setFileName( s );
02604     } else {
02605       curUrl = url;
02606     }
02607 
02608     if ( !curUrl.isEmpty() ) {
02609 
02610      // Rename the file if we have already saved one with the same name:
02611      // try appending a number before extension (e.g. "pic.jpg" => "pic_2.jpg")
02612      QString origFile = curUrl.fileName();
02613      QString file = origFile;
02614 
02615      while ( renameNumbering.contains(file) ) {
02616        file = origFile;
02617        int num = renameNumbering[file] + 1;
02618        int dotIdx = file.findRev('.');
02619        file = file.insert( (dotIdx>=0) ? dotIdx : file.length(), QString("_") + QString::number(num) );
02620      }
02621      curUrl.setFileName(file);
02622 
02623      // Increment the counter for both the old and the new filename
02624      if ( !renameNumbering.contains(origFile))
02625          renameNumbering[origFile] = 1;
02626      else
02627          renameNumbering[origFile]++;
02628 
02629      if ( file != origFile ) {
02630         if ( !renameNumbering.contains(file))
02631             renameNumbering[file] = 1;
02632         else
02633             renameNumbering[file]++;
02634      }
02635 
02636 
02637       if ( KIO::NetAccess::exists( curUrl, false, parentWidget() ) ) {
02638         if ( KMessageBox::warningContinueCancel( parentWidget(),
02639               i18n( "A file named %1 already exists. Do you want to overwrite it?" )
02640               .arg( curUrl.fileName() ),
02641               i18n( "File Already Exists" ), i18n("&Overwrite") ) == KMessageBox::Cancel) {
02642           continue;
02643         }
02644       }
02645       // save
02646       const Result result = saveItem( it.key(), curUrl );
02647       if ( result != OK )
02648         globalResult = result;
02649     }
02650   }
02651   setResult( globalResult );
02652   emit completed( this );
02653   deleteLater();
02654 }
02655 
02656 KMCommand::Result KMSaveAttachmentsCommand::saveItem( partNode *node,
02657                                                       const KURL& url )
02658 {
02659   bool bSaveEncrypted = false;
02660   bool bEncryptedParts = node->encryptionState() != KMMsgNotEncrypted;
02661   if( bEncryptedParts )
02662     if( KMessageBox::questionYesNo( parentWidget(),
02663           i18n( "The part %1 of the message is encrypted. Do you want to keep the encryption when saving?" ).
02664           arg( url.fileName() ),
02665           i18n( "KMail Question" ), i18n("Keep Encryption"), i18n("Do Not Keep") ) ==
02666         KMessageBox::Yes )
02667       bSaveEncrypted = true;
02668 
02669   bool bSaveWithSig = true;
02670   if( node->signatureState() != KMMsgNotSigned )
02671     if( KMessageBox::questionYesNo( parentWidget(),
02672           i18n( "The part %1 of the message is signed. Do you want to keep the signature when saving?" ).
02673           arg( url.fileName() ),
02674           i18n( "KMail Question" ), i18n("Keep Signature"), i18n("Do Not Keep") ) !=
02675         KMessageBox::Yes )
02676       bSaveWithSig = false;
02677 
02678   QByteArray data;
02679   if ( mEncoded )
02680   {
02681     // This does not decode the Message Content-Transfer-Encoding
02682     // but saves the _original_ content of the message part
02683     data = KMail::Util::ByteArray( node->msgPart().dwBody() );
02684   }
02685   else
02686   {
02687     if( bSaveEncrypted || !bEncryptedParts) {
02688       partNode *dataNode = node;
02689       QCString rawReplyString;
02690       bool gotRawReplyString = false;
02691       if( !bSaveWithSig ) {
02692         if( DwMime::kTypeMultipart == node->type() &&
02693             DwMime::kSubtypeSigned == node->subType() ){
02694           // carefully look for the part that is *not* the signature part:
02695           if( node->findType( DwMime::kTypeApplication,
02696                 DwMime::kSubtypePgpSignature,
02697                 true, false ) ){
02698             dataNode = node->findTypeNot( DwMime::kTypeApplication,
02699                 DwMime::kSubtypePgpSignature,
02700                 true, false );
02701           }else if( node->findType( DwMime::kTypeApplication,
02702                 DwMime::kSubtypePkcs7Mime,
02703                 true, false ) ){
02704             dataNode = node->findTypeNot( DwMime::kTypeApplication,
02705                 DwMime::kSubtypePkcs7Mime,
02706                 true, false );
02707           }else{
02708             dataNode = node->findTypeNot( DwMime::kTypeMultipart,
02709                 DwMime::kSubtypeUnknown,
02710                 true, false );
02711           }
02712     }else{
02713       ObjectTreeParser otp( 0, 0, false, false, false );
02714 
02715       // process this node and all it's siblings and descendants
02716       dataNode->setProcessed( false, true );
02717       otp.parseObjectTree( dataNode );
02718 
02719       rawReplyString = otp.rawReplyString();
02720       gotRawReplyString = true;
02721         }
02722       }
02723       QByteArray cstr = gotRawReplyString
02724                          ? rawReplyString
02725                          : dataNode->msgPart().bodyDecodedBinary();
02726       data = cstr;
02727       size_t size = cstr.size();
02728       if ( dataNode->msgPart().type() == DwMime::kTypeText ) {
02729         // convert CRLF to LF before writing text attachments to disk
02730         size = KMail::Util::crlf2lf( cstr.data(), size );
02731       }
02732       data.resize( size );
02733     }
02734   }
02735   QDataStream ds;
02736   QFile file;
02737   KTempFile tf;
02738   tf.setAutoDelete( true );
02739   if ( url.isLocalFile() )
02740   {
02741     // save directly
02742     file.setName( url.path() );
02743     if ( !file.open( IO_WriteOnly ) )
02744     {
02745       KMessageBox::error( parentWidget(),
02746           i18n( "%2 is detailed error description",
02747             "Could not write the file %1:\n%2" )
02748           .arg( file.name() )
02749           .arg( QString::fromLocal8Bit( strerror( errno ) ) ),
02750           i18n( "KMail Error" ) );
02751       return Failed;
02752     }
02753 
02754     // #79685 by default use the umask the user defined, but let it be configurable
02755     if ( GlobalSettings::self()->disregardUmask() )
02756       fchmod( file.handle(), S_IRUSR | S_IWUSR );
02757 
02758     ds.setDevice( &file );
02759   } else
02760   {
02761     // tmp file for upload
02762     ds.setDevice( tf.file() );
02763   }
02764 
02765   ds.writeRawBytes( data.data(), data.size() );
02766   if ( !url.isLocalFile() )
02767   {
02768     tf.close();
02769     if ( !KIO::NetAccess::upload( tf.name(), url, parentWidget() ) )
02770     {
02771       KMessageBox::error( parentWidget(),
02772           i18n( "Could not write the file %1." )
02773           .arg( url.path() ),
02774           i18n( "KMail Error" ) );
02775       return Failed;
02776     }
02777   } else
02778     file.close();
02779   return OK;
02780 }
02781 
02782 KMLoadPartsCommand::KMLoadPartsCommand( QPtrList<partNode>& parts, KMMessage *msg )
02783   : mNeedsRetrieval( 0 )
02784 {
02785   for ( QPtrListIterator<partNode> it( parts ); it.current(); ++it ) {
02786     mPartMap.insert( it.current(), msg );
02787   }
02788 }
02789 
02790 KMLoadPartsCommand::KMLoadPartsCommand( partNode *node, KMMessage *msg )
02791   : mNeedsRetrieval( 0 )
02792 {
02793   mPartMap.insert( node, msg );
02794 }
02795 
02796 KMLoadPartsCommand::KMLoadPartsCommand( PartNodeMessageMap& partMap )
02797   : mNeedsRetrieval( 0 ), mPartMap( partMap )
02798 {
02799 }
02800 
02801 void KMLoadPartsCommand::slotStart()
02802 {
02803   for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
02804         it != mPartMap.end();
02805         ++it ) {
02806     if ( !it.key()->msgPart().isComplete() &&
02807          !it.key()->msgPart().partSpecifier().isEmpty() ) {
02808       // incomplete part, so retrieve it first
02809       ++mNeedsRetrieval;
02810       KMFolder* curFolder = it.data()->parent();
02811       if ( curFolder ) {
02812         FolderJob *job =
02813           curFolder->createJob( it.data(), FolderJob::tGetMessage,
02814                                 0, it.key()->msgPart().partSpecifier() );
02815         job->setCancellable( false );
02816         connect( job, SIGNAL(messageUpdated(KMMessage*, QString)),
02817                  this, SLOT(slotPartRetrieved(KMMessage*, QString)) );
02818         job->start();
02819       } else
02820         kdWarning(5006) << "KMLoadPartsCommand - msg has no parent" << endl;
02821     }
02822   }
02823   if ( mNeedsRetrieval == 0 )
02824     execute();
02825 }
02826 
02827 void KMLoadPartsCommand::slotPartRetrieved( KMMessage *msg,
02828                                             QString partSpecifier )
02829 {
02830   DwBodyPart *part =
02831     msg->findDwBodyPart( msg->getFirstDwBodyPart(), partSpecifier );
02832   if ( part ) {
02833     // update the DwBodyPart in the partNode
02834     for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
02835           it != mPartMap.end();
02836           ++it ) {
02837       if ( it.key()->dwPart()->partId() == part->partId() )
02838         it.key()->setDwPart( part );
02839     }
02840   } else
02841     kdWarning(5006) << "KMLoadPartsCommand::slotPartRetrieved - could not find bodypart!" << endl;
02842   --mNeedsRetrieval;
02843   if ( mNeedsRetrieval == 0 )
02844     execute();
02845 }
02846 
02847 KMCommand::Result KMLoadPartsCommand::execute()
02848 {
02849   emit partsRetrieved();
02850   setResult( OK );
02851   emit completed( this );
02852   deleteLater();
02853   return OK;
02854 }
02855 
02856 KMResendMessageCommand::KMResendMessageCommand( QWidget *parent,
02857    KMMessage *msg )
02858   :KMCommand( parent, msg )
02859 {
02860 }
02861 
02862 KMCommand::Result KMResendMessageCommand::execute()
02863 {
02864    KMMessage *msg = retrievedMessage();
02865    if ( !msg || !msg->codec() ) {
02866      return Failed;
02867    }
02868    KMMessage *newMsg = new KMMessage(*msg);
02869 
02870    QStringList whiteList;
02871    whiteList << "To" << "Cc" << "Bcc" << "Subject";
02872    newMsg->sanitizeHeaders( whiteList );
02873 
02874    newMsg->setCharset(msg->codec()->mimeName());
02875    newMsg->setParent( 0 );
02876 
02877    // make sure we have an identity set, default, if necessary
02878    newMsg->setHeaderField("X-KMail-Identity", QString::number( newMsg->identityUoid() ));
02879    newMsg->applyIdentity( newMsg->identityUoid() );
02880 
02881    KMail::Composer * win = KMail::makeComposer();
02882    win->setMsg(newMsg, false, true);
02883    win->show();
02884 
02885    return OK;
02886 }
02887 
02888 KMMailingListCommand::KMMailingListCommand( QWidget *parent, KMFolder *folder )
02889   : KMCommand( parent ), mFolder( folder )
02890 {
02891 }
02892 
02893 KMCommand::Result KMMailingListCommand::execute()
02894 {
02895   KURL::List lst = urls();
02896   QString handler = ( mFolder->mailingList().handler() == MailingList::KMail )
02897     ? "mailto" : "https";
02898 
02899   KMCommand *command = 0;
02900   for ( KURL::List::Iterator itr = lst.begin(); itr != lst.end(); ++itr ) {
02901     if ( handler == (*itr).protocol() ) {
02902       command = new KMUrlClickedCommand( *itr, mFolder->identity(), 0, false );
02903     }
02904   }
02905   if ( !command && !lst.empty() ) {
02906     command =
02907       new KMUrlClickedCommand( lst.first(), mFolder->identity(), 0, false );
02908   }
02909   if ( command ) {
02910     connect( command, SIGNAL( completed( KMCommand * ) ),
02911              this, SLOT( commandCompleted( KMCommand * ) ) );
02912     setDeletesItself( true );
02913     setEmitsCompletedItself( true );
02914     command->start();
02915     return OK;
02916   }
02917   return Failed;
02918 }
02919 
02920 void KMMailingListCommand::commandCompleted( KMCommand *command )
02921 {
02922   setResult( command->result() );
02923   emit completed( this );
02924   deleteLater();
02925 }
02926 
02927 KMMailingListPostCommand::KMMailingListPostCommand( QWidget *parent, KMFolder *folder )
02928   : KMMailingListCommand( parent, folder )
02929 {
02930 }
02931 KURL::List KMMailingListPostCommand::urls() const
02932 {
02933   return mFolder->mailingList().postURLS();
02934 }
02935 
02936 KMMailingListSubscribeCommand::KMMailingListSubscribeCommand( QWidget *parent, KMFolder *folder )
02937   : KMMailingListCommand( parent, folder )
02938 {
02939 }
02940 KURL::List KMMailingListSubscribeCommand::urls() const
02941 {
02942   return mFolder->mailingList().subscribeURLS();
02943 }
02944 
02945 KMMailingListUnsubscribeCommand::KMMailingListUnsubscribeCommand( QWidget *parent, KMFolder *folder )
02946   : KMMailingListCommand( parent, folder )
02947 {
02948 }
02949 KURL::List KMMailingListUnsubscribeCommand::urls() const
02950 {
02951   return mFolder->mailingList().unsubscribeURLS();
02952 }
02953 
02954 KMMailingListArchivesCommand::KMMailingListArchivesCommand( QWidget *parent, KMFolder *folder )
02955   : KMMailingListCommand( parent, folder )
02956 {
02957 }
02958 KURL::List KMMailingListArchivesCommand::urls() const
02959 {
02960   return mFolder->mailingList().archiveURLS();
02961 }
02962 
02963 KMMailingListHelpCommand::KMMailingListHelpCommand( QWidget *parent, KMFolder *folder )
02964   : KMMailingListCommand( parent, folder )
02965 {
02966 }
02967 KURL::List KMMailingListHelpCommand::urls() const
02968 {
02969   return mFolder->mailingList().helpURLS();
02970 }
02971 
02972 KMIMChatCommand::KMIMChatCommand( const KURL &url, KMMessage *msg )
02973   :mUrl( url ), mMessage( msg )
02974 {
02975 }
02976 
02977 KMCommand::Result KMIMChatCommand::execute()
02978 {
02979   kdDebug( 5006 ) << k_funcinfo << " URL is: " << mUrl << endl;
02980   QString addr = KMMessage::decodeMailtoUrl( mUrl.path() );
02981   // find UID for mail address
02982   KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
02983   KABC::AddresseeList addressees = addressBook->findByEmail( KPIM::getEmailAddress( addr ) ) ;
02984 
02985   // start chat
02986   if( addressees.count() == 1 ) {
02987     kmkernel->imProxy()->chatWithContact( addressees[0].uid() );
02988     return OK;
02989   }
02990   else
02991   {
02992     kdDebug( 5006 ) << "Didn't find exactly one addressee, couldn't tell who to chat to for that email address.  Count = " << addressees.count() << endl;
02993 
02994     QString apology;
02995     if ( addressees.isEmpty() )
02996       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." );
02997     else
02998     {
02999       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." );
03000       QStringList nameList;
03001       KABC::AddresseeList::const_iterator it = addressees.begin();
03002       KABC::AddresseeList::const_iterator end = addressees.end();
03003       for ( ; it != end; ++it )
03004       {
03005           nameList.append( (*it).realName() );
03006       }
03007       QString names = nameList.join( QString::fromLatin1( ",\n" ) );
03008       apology = apology.arg( names );
03009     }
03010 
03011     KMessageBox::sorry( parentWidget(), apology );
03012     return Failed;
03013   }
03014 }
03015 
03016 KMHandleAttachmentCommand::KMHandleAttachmentCommand( partNode* node,
03017      KMMessage* msg, int atmId, const QString& atmName,
03018      AttachmentAction action, KService::Ptr offer, QWidget* parent )
03019 : KMCommand( parent ), mNode( node ), mMsg( msg ), mAtmId( atmId ), mAtmName( atmName ),
03020   mAction( action ), mOffer( offer ), mJob( 0 )
03021 {
03022 }
03023 
03024 void KMHandleAttachmentCommand::slotStart()
03025 {
03026   if ( !mNode->msgPart().isComplete() )
03027   {
03028     // load the part
03029     kdDebug(5006) << "load part" << endl;
03030     KMLoadPartsCommand *command = new KMLoadPartsCommand( mNode, mMsg );
03031     connect( command, SIGNAL( partsRetrieved() ),
03032         this, SLOT( slotPartComplete() ) );
03033     command->start();
03034   } else
03035   {
03036     execute();
03037   }
03038 }
03039 
03040 void KMHandleAttachmentCommand::slotPartComplete()
03041 {
03042   execute();
03043 }
03044 
03045 KMCommand::Result KMHandleAttachmentCommand::execute()
03046 {
03047   switch( mAction )
03048   {
03049     case Open:
03050       atmOpen();
03051       break;
03052     case OpenWith:
03053       atmOpenWith();
03054       break;
03055     case View:
03056       atmView();
03057       break;
03058     case Save:
03059       atmSave();
03060       break;
03061     case Properties:
03062       atmProperties();
03063       break;
03064     case ChiasmusEncrypt:
03065       atmEncryptWithChiasmus();
03066       return Undefined;
03067       break;
03068     default:
03069       kdDebug(5006) << "unknown action " << mAction << endl;
03070       break;
03071   }
03072   setResult( OK );
03073   emit completed( this );
03074   deleteLater();
03075   return OK;
03076 }
03077 
03078 QString KMHandleAttachmentCommand::createAtmFileLink() const
03079 {
03080   QFileInfo atmFileInfo( mAtmName );
03081 
03082   if ( atmFileInfo.size() == 0 )
03083   {
03084     kdDebug(5006) << k_funcinfo << "rewriting attachment" << endl;
03085     // there is something wrong so write the file again
03086     QByteArray data = mNode->msgPart().bodyDecodedBinary();
03087     size_t size = data.size();
03088     if ( mNode->msgPart().type() == DwMime::kTypeText && size) {
03089       // convert CRLF to LF before writing text attachments to disk
03090       size = KMail::Util::crlf2lf( data.data(), size );
03091     }
03092     KPIM::kBytesToFile( data.data(), size, mAtmName, false, false, false );
03093   }
03094 
03095   KTempFile *linkFile = new KTempFile( locateLocal("tmp", atmFileInfo.fileName() +"_["),
03096                           "]."+ atmFileInfo.extension() );
03097 
03098   linkFile->setAutoDelete(true);
03099   QString linkName = linkFile->name();
03100   delete linkFile;
03101 
03102   if ( ::link(QFile::encodeName( mAtmName ), QFile::encodeName( linkName )) == 0 ) {
03103     return linkName; // success
03104   }
03105   return QString::null;
03106 }
03107 
03108 KService::Ptr KMHandleAttachmentCommand::getServiceOffer()
03109 {
03110   KMMessagePart& msgPart = mNode->msgPart();
03111   const QString contentTypeStr =
03112     ( msgPart.typeStr() + '/' + msgPart.subtypeStr() ).lower();
03113 
03114   if ( contentTypeStr == "text/x-vcard" ) {
03115     atmView();
03116     return 0;
03117   }
03118   // determine the MIME type of the attachment
03119   KMimeType::Ptr mimetype;
03120   // prefer the value of the Content-Type header
03121   mimetype = KMimeType::mimeType( contentTypeStr );
03122   if ( mimetype->name() == "application/octet-stream" ) {
03123     // consider the filename if Content-Type is application/octet-stream
03124     mimetype = KMimeType::findByPath( mAtmName, 0, true /* no disk access */ );
03125   }
03126   if ( ( mimetype->name() == "application/octet-stream" )
03127        && msgPart.isComplete() ) {
03128     // consider the attachment's contents if neither the Content-Type header
03129     // nor the filename give us a clue
03130     mimetype = KMimeType::findByFileContent( mAtmName );
03131   }
03132   return KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
03133 }
03134 
03135 void KMHandleAttachmentCommand::atmOpen()
03136 {
03137   if ( !mOffer )
03138     mOffer = getServiceOffer();
03139   if ( !mOffer ) {
03140     kdDebug(5006) << k_funcinfo << "got no offer" << endl;
03141     return;
03142   }
03143 
03144   KURL::List lst;
03145   KURL url;
03146   bool autoDelete = true;
03147   QString fname = createAtmFileLink();
03148 
03149   if ( fname.isNull() ) {
03150     autoDelete = false;
03151     fname = mAtmName;
03152   }
03153 
03154   url.setPath( fname );
03155   lst.append( url );
03156   if ( (KRun::run( *mOffer, lst, autoDelete ) <= 0) && autoDelete ) {
03157       QFile::remove(url.path());
03158   }
03159 }
03160 
03161 void KMHandleAttachmentCommand::atmOpenWith()
03162 {
03163   KURL::List lst;
03164   KURL url;
03165   bool autoDelete = true;
03166   QString fname = createAtmFileLink();
03167 
03168   if ( fname.isNull() ) {
03169     autoDelete = false;
03170     fname = mAtmName;
03171   }
03172 
03173   url.setPath( fname );
03174   lst.append( url );
03175   if ( (! KRun::displayOpenWithDialog(lst, autoDelete)) && autoDelete ) {
03176     QFile::remove( url.path() );
03177   }
03178 }
03179 
03180 void KMHandleAttachmentCommand::atmView()
03181 {
03182   // we do not handle this ourself
03183   emit showAttachment( mAtmId, mAtmName );
03184 }
03185 
03186 void KMHandleAttachmentCommand::atmSave()
03187 {
03188   QPtrList<partNode> parts;
03189   parts.append( mNode );
03190   // save, do not leave encoded
03191   KMSaveAttachmentsCommand *command =
03192     new KMSaveAttachmentsCommand( parentWidget(), parts, mMsg, false );
03193   command->start();
03194 }
03195 
03196 void KMHandleAttachmentCommand::atmProperties()
03197 {
03198   KMMsgPartDialogCompat dlg( parentWidget() , 0, true );
03199   KMMessagePart& msgPart = mNode->msgPart();
03200   dlg.setMsgPart( &msgPart );
03201   dlg.exec();
03202 }
03203 
03204 void KMHandleAttachmentCommand::atmEncryptWithChiasmus()
03205 {
03206   const partNode * node = mNode;
03207   Q_ASSERT( node );
03208   if ( !node )
03209     return;
03210 
03211   // FIXME: better detection of mimetype??
03212   if ( !mAtmName.endsWith( ".xia", false ) )
03213     return;
03214 
03215   const Kleo::CryptoBackend::Protocol * chiasmus =
03216     Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
03217   Q_ASSERT( chiasmus );
03218   if ( !chiasmus )
03219     return;
03220 
03221   const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
03222   if ( !listjob.get() ) {
03223     const QString msg = i18n( "Chiasmus backend does not offer the "
03224                               "\"x-obtain-keys\" function. Please report this bug." );
03225     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03226     return;
03227   }
03228 
03229   if ( listjob->exec() ) {
03230     listjob->showErrorDialog( parentWidget(), i18n( "Chiasmus Backend Error" ) );
03231     return;
03232   }
03233 
03234   const QVariant result = listjob->property( "result" );
03235   if ( result.type() != QVariant::StringList ) {
03236     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
03237                               "The \"x-obtain-keys\" function did not return a "
03238                               "string list. Please report this bug." );
03239     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03240     return;
03241   }
03242 
03243   const QStringList keys = result.toStringList();
03244   if ( keys.empty() ) {
03245     const QString msg = i18n( "No keys have been found. Please check that a "
03246                               "valid key path has been set in the Chiasmus "
03247                               "configuration." );
03248     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03249     return;
03250   }
03251 
03252   ChiasmusKeySelector selectorDlg( parentWidget(), i18n( "Chiasmus Decryption Key Selection" ),
03253                                    keys, GlobalSettings::chiasmusDecryptionKey(),
03254                                    GlobalSettings::chiasmusDecryptionOptions() );
03255   if ( selectorDlg.exec() != QDialog::Accepted )
03256     return;
03257 
03258   GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
03259   GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
03260   assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
03261 
03262   Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", QMap<QString,QVariant>() );
03263   if ( !job ) {
03264     const QString msg = i18n( "Chiasmus backend does not offer the "
03265                               "\"x-decrypt\" function. Please report this bug." );
03266     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03267     return;
03268   }
03269 
03270   const QByteArray input = node->msgPart().bodyDecodedBinary();
03271 
03272   if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
03273        !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
03274        !job->setProperty( "input", input ) ) {
03275     const QString msg = i18n( "The \"x-decrypt\" function does not accept "
03276                               "the expected parameters. Please report this bug." );
03277     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03278     return;
03279   }
03280 
03281   setDeletesItself( true ); // the job below is async, we have to cleanup ourselves
03282   if ( job->start() ) {
03283     job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
03284     return;
03285   }
03286 
03287   mJob = job;
03288   connect( job, SIGNAL(result(const GpgME::Error&,const QVariant&)),
03289            this, SLOT(slotAtmDecryptWithChiasmusResult(const GpgME::Error&,const QVariant&)) );
03290 }
03291 
03292 static const QString chomp( const QString & base, const QString & suffix, bool cs ) {
03293   return base.endsWith( suffix, cs ) ? base.left( base.length() - suffix.length() ) : base ;
03294 }
03295 
03296 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusResult( const GpgME::Error & err, const QVariant & result )
03297 {
03298   LaterDeleterWithCommandCompletion d( this );
03299   if ( !mJob )
03300     return;
03301   Q_ASSERT( mJob == sender() );
03302   if ( mJob != sender() )
03303     return;
03304   Kleo::Job * job = mJob;
03305   mJob = 0;
03306   if ( err.isCanceled() )
03307     return;
03308   if ( err ) {
03309     job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
03310     return;
03311   }
03312 
03313   if ( result.type() != QVariant::ByteArray ) {
03314     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
03315                               "The \"x-decrypt\" function did not return a "
03316                               "byte array. Please report this bug." );
03317     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03318     return;
03319   }
03320 
03321   const KURL url = KFileDialog::getSaveURL( chomp( mAtmName, ".xia", false ), QString::null, parentWidget() );
03322   if ( url.isEmpty() )
03323     return;
03324 
03325   bool overwrite = KMail::Util::checkOverwrite( url, parentWidget() );
03326   if ( !overwrite )
03327     return;
03328 
03329   d.setDisabled( true ); // we got this far, don't delete yet
03330   KIO::Job * uploadJob = KIO::storedPut( result.toByteArray(), url, -1, overwrite, false /*resume*/ );
03331   uploadJob->setWindow( parentWidget() );
03332   connect( uploadJob, SIGNAL(result(KIO::Job*)),
03333            this, SLOT(slotAtmDecryptWithChiasmusUploadResult(KIO::Job*)) );
03334 }
03335 
03336 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusUploadResult( KIO::Job * job )
03337 {
03338   if ( job->error() )
03339     job->showErrorDialog();
03340   LaterDeleterWithCommandCompletion d( this );
03341   d.setResult( OK );
03342 }
03343 
03344 
03345 AttachmentModifyCommand::AttachmentModifyCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03346     KMCommand( parent, msg ),
03347     mPartIndex( node->nodeId() ),
03348     mSernum( 0 )
03349 {
03350 }
03351 
03352 AttachmentModifyCommand::AttachmentModifyCommand( int nodeId, KMMessage *msg, QWidget *parent )
03353   : KMCommand( parent, msg ),
03354     mPartIndex( nodeId ),
03355     mSernum( 0 )
03356 {
03357 }
03358 
03359 AttachmentModifyCommand::~ AttachmentModifyCommand()
03360 {
03361 }
03362 
03363 KMCommand::Result AttachmentModifyCommand::execute()
03364 {
03365   KMMessage *msg = retrievedMessage();
03366   if ( !msg )
03367     return Failed;
03368   mSernum = msg->getMsgSerNum();
03369 
03370   mFolder = msg->parent();
03371   if ( !mFolder || !mFolder->storage() )
03372     return Failed;
03373 
03374   Result res = doAttachmentModify();
03375   if ( res != OK )
03376     return res;
03377 
03378   setEmitsCompletedItself( true );
03379   setDeletesItself( true );
03380   return OK;
03381 }
03382 
03383 void AttachmentModifyCommand::storeChangedMessage(KMMessage * msg)
03384 {
03385   if ( !mFolder || !mFolder->storage() ) {
03386     kdWarning(5006) << k_funcinfo << "We lost the folder!" << endl;
03387     setResult( Failed );
03388     emit completed( this );
03389     deleteLater();
03390   }
03391   int res = mFolder->addMsg( msg ) != 0;
03392   if ( mFolder->folderType() == KMFolderTypeImap ) {
03393     KMFolderImap *f = static_cast<KMFolderImap*>( mFolder->storage() );
03394     connect( f, SIGNAL(folderComplete(KMFolderImap*,bool)),
03395              SLOT(messageStoreResult(KMFolderImap*,bool)) );
03396   } else {
03397     messageStoreResult( 0, res == 0 );
03398   }
03399 }
03400 
03401 void AttachmentModifyCommand::messageStoreResult(KMFolderImap* folder, bool success )
03402 {
03403   Q_UNUSED( folder );
03404   if ( success ) {
03405     KMCommand *delCmd = new KMDeleteMsgCommand( mSernum );
03406     connect( delCmd, SIGNAL(completed(KMCommand*)), SLOT(messageDeleteResult(KMCommand*)) );
03407     delCmd->start();
03408     return;
03409   }
03410   kdWarning(5006) << k_funcinfo << "Adding modified message failed." << endl;
03411   setResult( Failed );
03412   emit completed( this );
03413   deleteLater();
03414 }
03415 
03416 void AttachmentModifyCommand::messageDeleteResult(KMCommand * cmd)
03417 {
03418   setResult( cmd->result() );
03419   emit completed( this );
03420   deleteLater();
03421 }
03422 
03423 KMDeleteAttachmentCommand::KMDeleteAttachmentCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03424     AttachmentModifyCommand( node, msg, parent )
03425 {
03426   kdDebug(5006) << k_funcinfo << endl;
03427 }
03428 
03429 KMDeleteAttachmentCommand::KMDeleteAttachmentCommand( int nodeId, KMMessage *msg, QWidget *parent )
03430   : AttachmentModifyCommand( nodeId, msg, parent )
03431 {
03432   kdDebug(5006) << k_funcinfo << endl;
03433 }
03434 
03435 KMDeleteAttachmentCommand::~KMDeleteAttachmentCommand()
03436 {
03437   kdDebug(5006) << k_funcinfo << endl;
03438 }
03439 
03440 KMCommand::Result KMDeleteAttachmentCommand::doAttachmentModify()
03441 {
03442   KMMessage *msg = retrievedMessage();
03443   if ( !msg || !msg->deleteBodyPart( mPartIndex ) )
03444     return Failed;
03445 
03446   KMMessage *newMsg = new KMMessage();
03447   newMsg->fromDwString( msg->asDwString() );
03448   newMsg->setStatus( msg->status() );
03449 
03450   storeChangedMessage( newMsg );
03451   return OK;
03452 }
03453 
03454 
03455 KMEditAttachmentCommand::KMEditAttachmentCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03456     AttachmentModifyCommand( node, msg, parent )
03457 {
03458   kdDebug(5006) << k_funcinfo << endl;
03459   mTempFile.setAutoDelete( true );
03460 }
03461 
03462 KMEditAttachmentCommand::KMEditAttachmentCommand( int nodeId, KMMessage *msg, QWidget *parent )
03463   : AttachmentModifyCommand( nodeId, msg, parent )
03464 {
03465   kdDebug(5006) << k_funcinfo << endl;
03466   mTempFile.setAutoDelete( true );
03467 }
03468 
03469 KMEditAttachmentCommand::~ KMEditAttachmentCommand()
03470 {
03471 }
03472 
03473 KMCommand::Result KMEditAttachmentCommand::doAttachmentModify()
03474 {
03475   KMMessage *msg = retrievedMessage();
03476   if ( !msg )
03477     return Failed;
03478 
03479   KMMessagePart part;
03480   DwBodyPart *dwpart = msg->findPart( mPartIndex );
03481   if ( !dwpart )
03482     return Failed;
03483   KMMessage::bodyPart( dwpart, &part, true );
03484   if ( !part.isComplete() )
03485      return Failed;
03486 
03487   if( !dynamic_cast<DwBody*>( dwpart->Parent() ) )
03488     return Failed;
03489 
03490   mTempFile.file()->writeBlock( part.bodyDecodedBinary() );
03491   mTempFile.file()->flush();
03492 
03493   KMail::EditorWatcher *watcher =
03494           new KMail::EditorWatcher( KURL( mTempFile.file()->name() ),
03495                                     part.typeStr() + "/" + part.subtypeStr(),
03496                                     false, this, parentWidget() );
03497   connect( watcher, SIGNAL(editDone(KMail::EditorWatcher*)), SLOT(editDone(KMail::EditorWatcher*)) );
03498   if ( !watcher->start() )
03499     return Failed;
03500   setEmitsCompletedItself( true );
03501   setDeletesItself( true );
03502   return OK;
03503 }
03504 
03505 void KMEditAttachmentCommand::editDone(KMail::EditorWatcher * watcher)
03506 {
03507   kdDebug(5006) << k_funcinfo << endl;
03508   // anything changed?
03509   if ( !watcher->fileChanged() ) {
03510     kdDebug(5006) << k_funcinfo << "File has not been changed" << endl;
03511     setResult( Canceled );
03512     emit completed( this );
03513     deleteLater();
03514   }
03515 
03516   mTempFile.file()->reset();
03517   QByteArray data = mTempFile.file()->readAll();
03518 
03519   // build the new message
03520   KMMessage *msg = retrievedMessage();
03521   KMMessagePart part;
03522   DwBodyPart *dwpart = msg->findPart( mPartIndex );
03523   KMMessage::bodyPart( dwpart, &part, true );
03524 
03525   DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
03526   assert( parentNode );
03527   parentNode->RemoveBodyPart( dwpart );
03528 
03529   KMMessagePart att;
03530   att.duplicate( part );
03531   att.setBodyEncodedBinary( data );
03532 
03533   DwBodyPart* newDwPart = msg->createDWBodyPart( &att );
03534   parentNode->AddBodyPart( newDwPart );
03535   msg->getTopLevelPart()->Assemble();
03536 
03537   KMMessage *newMsg = new KMMessage();
03538   newMsg->fromDwString( msg->asDwString() );
03539   newMsg->setStatus( msg->status() );
03540 
03541   storeChangedMessage( newMsg );
03542 }
03543 
03544 
03545 CreateTodoCommand::CreateTodoCommand(QWidget * parent, KMMessage * msg)
03546   : KMCommand( parent, msg )
03547 {
03548 }
03549 
03550 KMCommand::Result CreateTodoCommand::execute()
03551 {
03552   KMMessage *msg = retrievedMessage();
03553   if ( !msg || !msg->codec() ) {
03554     return Failed;
03555   }
03556 
03557   KMail::KorgHelper::ensureRunning();
03558 
03559   QString txt = i18n("From: %1\nTo: %2\nSubject: %3").arg( msg->from() )
03560                 .arg( msg->to() ).arg( msg->subject() );
03561 
03562   KTempFile tf;
03563   tf.setAutoDelete( true );
03564   QString uri = "kmail:" + QString::number( msg->getMsgSerNum() ) + "/" + msg->msgId();
03565   tf.file()->writeBlock( msg->asDwString().c_str(), msg->asDwString().length() );
03566   tf.close();
03567 
03568   KCalendarIface_stub *iface = new KCalendarIface_stub( kapp->dcopClient(), "korganizer", "CalendarIface" );
03569   iface->openTodoEditor( i18n("Mail: %1").arg( msg->subject() ), txt, uri,
03570                          tf.name(), QStringList(), "message/rfc822", true );
03571   delete iface;
03572 
03573   return OK;
03574 }
03575 
03576 #include "kmcommands.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys