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