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