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