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 :mDestFolder( destFolder ), mMsgList( msgList )
01957 {
01958   setDeletesItself( true );
01959 }
01960 
01961 KMCopyCommand::KMCopyCommand( KMFolder* destFolder, KMMessage * msg )
01962   :mDestFolder( destFolder )
01963 {
01964   setDeletesItself( true );
01965   mMsgList.append( &msg->toMsgBase() );
01966 }
01967 
01968 KMCommand::Result KMCopyCommand::execute()
01969 {
01970   KMMsgBase *msgBase;
01971   KMMessage *msg, *newMsg;
01972   int idx = -1;
01973   bool isMessage;
01974   QPtrList<KMMessage> list;
01975   QPtrList<KMMessage> localList;
01976 
01977   if (mDestFolder && mDestFolder->open("kmcommand") != 0)
01978   {
01979     deleteLater();
01980     return Failed;
01981   }
01982 
01983   setEmitsCompletedItself( true );
01984   KCursorSaver busy(KBusyPtr::busy());
01985 
01986   for (msgBase = mMsgList.first(); msgBase; msgBase = mMsgList.next() )
01987   {
01988     KMFolder *srcFolder = msgBase->parent();
01989     if (( isMessage = msgBase->isMessage() ))
01990     {
01991       msg = static_cast<KMMessage*>(msgBase);
01992     } else {
01993       idx = srcFolder->find(msgBase);
01994       assert(idx != -1);
01995       msg = srcFolder->getMsg(idx);
01996       // corrupt IMAP cache, see FolderStorage::getMsg()
01997       if ( msg == 0 ) {
01998         KMessageBox::error( parentWidget(), i18n("Corrupt IMAP cache detected in folder %1. "
01999             "Copying of messages aborted.").arg( srcFolder->prettyURL() ) );
02000         deleteLater();
02001         return Failed;
02002       }
02003     }
02004 
02005     if (srcFolder && mDestFolder &&
02006         (srcFolder->folderType()== KMFolderTypeImap) &&
02007         (mDestFolder->folderType() == KMFolderTypeImap) &&
02008         (static_cast<KMFolderImap*>(srcFolder->storage())->account() ==
02009          static_cast<KMFolderImap*>(mDestFolder->storage())->account()))
02010     {
02011       // imap => imap with same account
02012       list.append(msg);
02013     } else {
02014       newMsg = new KMMessage( new DwMessage( *msg->asDwMessage() ) );
02015       newMsg->setComplete(msg->isComplete());
02016       // make sure the attachment state is only calculated when it's complete
02017       if (!newMsg->isComplete())
02018         newMsg->setReadyToShow(false);
02019       newMsg->setStatus(msg->status());
02020 
02021       if (srcFolder && !newMsg->isComplete())
02022       {
02023         // imap => others
02024         newMsg->setParent(msg->parent());
02025         FolderJob *job = srcFolder->createJob(newMsg);
02026         job->setCancellable( false );
02027         mPendingJobs << job;
02028         connect(job, SIGNAL(messageRetrieved(KMMessage*)),
02029                 mDestFolder, SLOT(reallyAddCopyOfMsg(KMMessage*)));
02030         connect( job, SIGNAL(result(KMail::FolderJob*)),
02031                  this, SLOT(slotJobFinished(KMail::FolderJob*)) );
02032         job->start();
02033       } else {
02034         // local => others
02035         localList.append(newMsg);
02036       }
02037     }
02038 
02039     if (srcFolder && !isMessage && list.isEmpty())
02040     {
02041       assert(idx != -1);
02042       srcFolder->unGetMsg( idx );
02043     }
02044 
02045   } // end for
02046 
02047   bool deleteNow = false;
02048   if (!localList.isEmpty())
02049   {
02050     QValueList<int> index;
02051     mDestFolder->addMsg( localList, index );
02052     for ( QValueListIterator<int> it = index.begin(); it != index.end(); ++it ) {
02053       mDestFolder->unGetMsg( *it );
02054     }
02055     if ( mDestFolder->folderType() == KMFolderTypeImap ) {
02056       if ( mPendingJobs.isEmpty() ) {
02057         // wait for the end of the copy before closing the folder
02058         KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
02059         connect( imapDestFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
02060             this, SLOT( slotFolderComplete( KMFolderImap*, bool ) ) );
02061       }
02062     } else {
02063       deleteNow = list.isEmpty() && mPendingJobs.isEmpty(); // we're done if there are no other mails we need to fetch
02064     }
02065   }
02066 
02067 //TODO: Get rid of the other cases just use this one for all types of folder
02068 //TODO: requires adding copyMsg and getFolder methods to KMFolder.h
02069   if (!list.isEmpty())
02070   {
02071     // copy the message(s); note: the list is empty afterwards!
02072     KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
02073     connect( imapDestFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
02074         this, SLOT( slotFolderComplete( KMFolderImap*, bool ) ) );
02075     imapDestFolder->copyMsg(list);
02076     imapDestFolder->getFolder();
02077   }
02078 
02079   // only close the folder and delete the job if we're done
02080   // otherwise this is done in slotMsgAdded or slotFolderComplete
02081   if ( deleteNow )
02082   {
02083     mDestFolder->close("kmcommand");
02084     setResult( OK );
02085     emit completed( this );
02086     deleteLater();
02087   }
02088 
02089   return OK;
02090 }
02091 
02092 void KMCopyCommand::slotJobFinished(KMail::FolderJob * job)
02093 {
02094   mPendingJobs.remove( job );
02095   if ( job->error() ) {
02096     kdDebug(5006) << k_funcinfo << "folder job failed: " << job->error() << endl;
02097     // kill all pending jobs
02098     for ( QValueList<KMail::FolderJob*>::Iterator it = mPendingJobs.begin(); it != mPendingJobs.end(); ++it ) {
02099       disconnect( (*it), SIGNAL(result(KMail::FolderJob*)),
02100                   this, SLOT(slotJobFinished(KMail::FolderJob*)) );
02101       (*it)->kill();
02102     }
02103     mPendingJobs.clear();
02104     setResult( Failed );
02105   }
02106 
02107   if ( mPendingJobs.isEmpty() )
02108   {
02109     mDestFolder->close("kmcommand");
02110     emit completed( this );
02111     deleteLater();
02112   }
02113 }
02114 
02115 void KMCopyCommand::slotFolderComplete( KMFolderImap*, bool success )
02116 {
02117   kdDebug(5006) << k_funcinfo << success << endl;
02118   if ( !success )
02119     setResult( Failed );
02120   mDestFolder->close( "kmcommand" );
02121   emit completed( this );
02122   deleteLater();
02123 }
02124 
02125 
02126 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
02127                               const QPtrList<KMMsgBase> &msgList)
02128   : mDestFolder( destFolder ), mProgressItem( 0 )
02129 {
02130   QPtrList<KMMsgBase> tmp = msgList;
02131   for ( KMMsgBase *msgBase = tmp.first(); msgBase; msgBase = tmp.next() )
02132     mSerNumList.append( msgBase->getMsgSerNum() );
02133 }
02134 
02135 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
02136                               KMMessage *msg )
02137   : mDestFolder( destFolder ), mProgressItem( 0 )
02138 {
02139   mSerNumList.append( msg->getMsgSerNum() );
02140 }
02141 
02142 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
02143                               KMMsgBase *msgBase )
02144   : mDestFolder( destFolder ), mProgressItem( 0 )
02145 {
02146   mSerNumList.append( msgBase->getMsgSerNum() );
02147 }
02148 
02149 KMMoveCommand::KMMoveCommand( Q_UINT32 )
02150   : mProgressItem( 0 )
02151 {
02152 }
02153 
02154 KMCommand::Result KMMoveCommand::execute()
02155 {
02156   setEmitsCompletedItself( true );
02157   setDeletesItself( true );
02158   typedef QMap< KMFolder*, QPtrList<KMMessage>* > FolderToMessageListMap;
02159   FolderToMessageListMap folderDeleteList;
02160 
02161   if (mDestFolder && mDestFolder->open("kmcommand") != 0) {
02162     completeMove( Failed );
02163     return Failed;
02164   }
02165   KCursorSaver busy(KBusyPtr::busy());
02166 
02167   // TODO set SSL state according to source and destfolder connection?
02168   Q_ASSERT( !mProgressItem );
02169   mProgressItem =
02170      ProgressManager::createProgressItem (
02171          "move"+ProgressManager::getUniqueID(),
02172          mDestFolder ? i18n( "Moving messages" ) : i18n( "Deleting messages" ) );
02173   connect( mProgressItem, SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
02174            this, SLOT( slotMoveCanceled() ) );
02175 
02176   KMMessage *msg;
02177   int rc = 0;
02178   int index;
02179   QPtrList<KMMessage> list;
02180   int undoId = -1;
02181   mCompleteWithAddedMsg = false;
02182 
02183   if (mDestFolder) {
02184     connect (mDestFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
02185              this, SLOT(slotMsgAddedToDestFolder(KMFolder*, Q_UINT32)));
02186     mLostBoys = mSerNumList;
02187   }
02188   mProgressItem->setTotalItems( mSerNumList.count() );
02189 
02190   for ( QValueList<Q_UINT32>::ConstIterator it = mSerNumList.constBegin(); it != mSerNumList.constEnd(); ++it ) {
02191     if ( *it == 0 ) {
02192       kdDebug(5006) << k_funcinfo << "serial number == 0!" << endl;
02193       continue; // invalid message
02194     }
02195     KMFolder *srcFolder = 0;
02196     int idx = -1;
02197     KMMsgDict::instance()->getLocation( *it, &srcFolder, &idx );
02198     if (srcFolder == mDestFolder)
02199       continue;
02200     assert(srcFolder);
02201     assert(idx != -1);
02202     if ( !srcFolder->isOpened() ) {
02203       srcFolder->open( "kmmovecommand" );
02204       mOpenedFolders.append( srcFolder );
02205     }
02206     msg = srcFolder->getMsg(idx);
02207     if ( !msg ) {
02208       kdDebug(5006) << k_funcinfo << "No message found for serial number " << *it << endl;
02209       continue;
02210     }
02211     bool undo = msg->enableUndo();
02212 
02213     if ( msg && msg->transferInProgress() &&
02214          srcFolder->folderType() == KMFolderTypeImap )
02215     {
02216       // cancel the download
02217       msg->setTransferInProgress( false, true );
02218       static_cast<KMFolderImap*>(srcFolder->storage())->ignoreJobsForMessage( msg );
02219     }
02220 
02221     if (mDestFolder) {
02222       if (mDestFolder->folderType() == KMFolderTypeImap) {
02223         /* If we are moving to an imap folder, connect to it's completed
02224          * signal so we notice when all the mails should have showed up in it
02225          * but haven't for some reason. */
02226         KMFolderImap *imapFolder = static_cast<KMFolderImap*> ( mDestFolder->storage() );
02227         disconnect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )),
02228                  this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
02229 
02230         connect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )),
02231                  this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
02232         list.append(msg);
02233       } else {
02234         // We are moving to a local folder.
02235         if ( srcFolder->folderType() == KMFolderTypeImap )
02236         {
02237           // do not complete here but wait until all messages are transferred
02238           mCompleteWithAddedMsg = true;
02239         }
02240         rc = mDestFolder->moveMsg(msg, &index);
02241         if (rc == 0 && index != -1) {
02242           KMMsgBase *mb = mDestFolder->unGetMsg( mDestFolder->count() - 1 );
02243           if (undo && mb)
02244           {
02245             if ( undoId == -1 )
02246               undoId = kmkernel->undoStack()->newUndoAction( srcFolder, mDestFolder );
02247             kmkernel->undoStack()->addMsgToAction( undoId, mb->getMsgSerNum() );
02248           }
02249         } else if (rc != 0) {
02250           // Something  went wrong. Stop processing here, it is likely that the
02251           // other moves would fail as well.
02252           completeMove( Failed );
02253           return Failed;
02254         }
02255       }
02256     } else {
02257       // really delete messages that are already in the trash folder or if
02258       // we are really, really deleting, not just moving to trash
02259       if (srcFolder->folderType() == KMFolderTypeImap) {
02260         if (!folderDeleteList[srcFolder])
02261           folderDeleteList[srcFolder] = new QPtrList<KMMessage>;
02262         folderDeleteList[srcFolder]->append( msg );
02263       } else {
02264         srcFolder->removeMsg(idx);
02265         delete msg;
02266       }
02267     }
02268   }
02269   if (!list.isEmpty() && mDestFolder) {
02270     // will be completed with folderComplete signal
02271     mDestFolder->moveMsg(list, &index);
02272   } else {
02273     FolderToMessageListMap::Iterator it;
02274     for ( it = folderDeleteList.begin(); it != folderDeleteList.end(); ++it ) {
02275       it.key()->removeMsg(*it.data());
02276       delete it.data();
02277     }
02278     if ( !mCompleteWithAddedMsg ) {
02279       // imap folders will be completed in slotMsgAddedToDestFolder
02280       completeMove( OK );
02281     }
02282   }
02283 
02284   return OK;
02285 }
02286 
02287 void KMMoveCommand::slotImapFolderCompleted(KMFolderImap* imapFolder, bool success)
02288 {
02289   disconnect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )),
02290       this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
02291   if ( success ) {
02292     // the folder was checked successfully but we were still called, so check
02293     // if we are still waiting for messages to show up. If so, uidValidity
02294     // changed, or something else went wrong. Clean up.
02295 
02296     /* Unfortunately older UW imap servers change uid validity for each put job.
02297      * Yes, it is really that broken. *sigh* So we cannot report error here, I guess. */
02298     if ( !mLostBoys.isEmpty() ) {
02299       kdDebug(5006) <<  "### Not all moved messages reported back that they were " << endl
02300                     <<  "### added to the target folder. Did uidValidity change? " << endl;
02301     }
02302     completeMove( OK );
02303   } else {
02304     // Should we inform the user here or leave that to the caller?
02305     completeMove( Failed );
02306   }
02307 }
02308 
02309 void KMMoveCommand::slotMsgAddedToDestFolder(KMFolder *folder, Q_UINT32 serNum)
02310 {
02311   if ( folder != mDestFolder || mLostBoys.find( serNum ) == mLostBoys.end() ) {
02312     //kdDebug(5006) << "KMMoveCommand::msgAddedToDestFolder different "
02313     //                 "folder or invalid serial number." << endl;
02314     return;
02315   }
02316   mLostBoys.remove(serNum);
02317   if ( mLostBoys.isEmpty() ) {
02318     // we are done. All messages transferred to the host succesfully
02319     disconnect (mDestFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
02320              this, SLOT(slotMsgAddedToDestFolder(KMFolder*, Q_UINT32)));
02321     if (mDestFolder && mDestFolder->folderType() != KMFolderTypeImap) {
02322       mDestFolder->sync();
02323     }
02324     if ( mCompleteWithAddedMsg ) {
02325       completeMove( OK );
02326     }
02327   } else {
02328     if ( mProgressItem ) {
02329       mProgressItem->incCompletedItems();
02330       mProgressItem->updateProgress();
02331     }
02332   }
02333 }
02334 
02335 void KMMoveCommand::completeMove( Result result )
02336 {
02337   if ( mDestFolder )
02338     mDestFolder->close("kmcommand");
02339   while ( !mOpenedFolders.empty() ) {
02340     KMFolder *folder = mOpenedFolders.back();
02341     mOpenedFolders.pop_back();
02342     folder->close("kmcommand");
02343   }
02344   if ( mProgressItem ) {
02345     mProgressItem->setComplete();
02346     mProgressItem = 0;
02347   }
02348   setResult( result );
02349   emit completed( this );
02350   deleteLater();
02351 }
02352 
02353 void KMMoveCommand::slotMoveCanceled()
02354 {
02355   completeMove( Canceled );
02356 }
02357 
02358 // srcFolder doesn't make much sense for searchFolders
02359 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder,
02360   const QPtrList<KMMsgBase> &msgList )
02361 :KMMoveCommand( findTrashFolder( srcFolder ), msgList)
02362 {
02363   srcFolder->open("kmcommand");
02364   mOpenedFolders.push_back( srcFolder );
02365 }
02366 
02367 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder, KMMessage * msg )
02368 :KMMoveCommand( findTrashFolder( srcFolder ), msg)
02369 {
02370   srcFolder->open("kmcommand");
02371   mOpenedFolders.push_back( srcFolder );
02372 }
02373 
02374 KMDeleteMsgCommand::KMDeleteMsgCommand( Q_UINT32 sernum )
02375 :KMMoveCommand( sernum )
02376 {
02377   if ( !sernum ) {
02378     setDestFolder( 0 );
02379     return;
02380   }
02381 
02382   KMFolder *srcFolder = 0;
02383   int idx;
02384   KMMsgDict::instance()->getLocation( sernum, &srcFolder, &idx );
02385   if ( !srcFolder || (idx == -1) ) {
02386     setDestFolder( 0 );
02387     return;
02388   }
02389 
02390   KMMsgBase *msg = srcFolder->getMsgBase( idx );
02391   if ( !msg ) {
02392     setDestFolder( 0 );
02393     return;
02394   }
02395 
02396   srcFolder->open("kmcommand");
02397   mOpenedFolders.push_back( srcFolder );
02398   addMsg( msg );
02399 
02400   setDestFolder( findTrashFolder( srcFolder ) );
02401 }
02402 
02403 KMFolder * KMDeleteMsgCommand::findTrashFolder( KMFolder * folder )
02404 {
02405   KMFolder* trash = folder->trashFolder();
02406   if( !trash )
02407     trash = kmkernel->trashFolder();
02408   if( trash != folder )
02409     return trash;
02410   return 0;
02411 }
02412 
02413 
02414 KMUrlClickedCommand::KMUrlClickedCommand( const KURL &url, uint identity,
02415   KMReaderWin *readerWin, bool htmlPref, KMMainWidget *mainWidget )
02416   :mUrl( url ), mIdentity( identity ), mReaderWin( readerWin ),
02417    mHtmlPref( htmlPref ), mMainWidget( mainWidget )
02418 {
02419 }
02420 
02421 KMCommand::Result KMUrlClickedCommand::execute()
02422 {
02423   KMMessage* msg;
02424 
02425   if (mUrl.protocol() == "mailto")
02426   {
02427     msg = new KMMessage;
02428     msg->initHeader(mIdentity);
02429     msg->setCharset("utf-8");
02430     msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
02431     QString query=mUrl.query();
02432     while (!query.isEmpty()) {
02433       QString queryPart;
02434       int secondQuery = query.find('?',1);
02435       if (secondQuery != -1)
02436         queryPart = query.left(secondQuery);
02437       else
02438         queryPart = query;
02439       query = query.mid(queryPart.length());
02440 
02441       if (queryPart.left(9) == "?subject=")
02442         msg->setSubject( KURL::decode_string(queryPart.mid(9)) );
02443       else if (queryPart.left(6) == "?body=")
02444         // It is correct to convert to latin1() as URL should not contain
02445         // anything except ascii.
02446         msg->setBody( KURL::decode_string(queryPart.mid(6)).latin1() );
02447       else if (queryPart.left(4) == "?cc=")
02448         msg->setCc( KURL::decode_string(queryPart.mid(4)) );
02449     }
02450 
02451     KMail::Composer * win = KMail::makeComposer( msg, mIdentity );
02452     win->setCharset("", true);
02453     win->show();
02454   }
02455   else if ( mUrl.protocol() == "im" )
02456   {
02457     kmkernel->imProxy()->chatWithContact( mUrl.path() );
02458   }
02459   else if ((mUrl.protocol() == "http") || (mUrl.protocol() == "https") ||
02460            (mUrl.protocol() == "ftp")  || (mUrl.protocol() == "file")  ||
02461            (mUrl.protocol() == "ftps") || (mUrl.protocol() == "sftp" ) ||
02462            (mUrl.protocol() == "help") || (mUrl.protocol() == "vnc")   ||
02463            (mUrl.protocol() == "smb")  || (mUrl.protocol() == "fish")  ||
02464            (mUrl.protocol() == "news"))
02465   {
02466     KPIM::BroadcastStatus::instance()->setStatusMsg( i18n("Opening URL..."));
02467     KMimeType::Ptr mime = KMimeType::findByURL( mUrl );
02468     if (mime->name() == "application/x-desktop" ||
02469         mime->name() == "application/x-executable" ||
02470         mime->name() == "application/x-msdos-program" ||
02471         mime->name() == "application/x-shellscript" )
02472     {
02473       if (KMessageBox::warningYesNo( 0, i18n( "<qt>Do you really want to execute <b>%1</b>?</qt>" )
02474         .arg( mUrl.prettyURL() ), QString::null, i18n("Execute"), KStdGuiItem::cancel() ) != KMessageBox::Yes)
02475         return Canceled;
02476     }
02477     KRun * runner = new KRun( mUrl );
02478     runner->setRunExecutables( false );
02479   }
02480   else
02481     return Failed;
02482 
02483   return OK;
02484 }
02485 
02486 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, KMMessage *msg )
02487   : KMCommand( parent, msg ), mImplicitAttachments( true ), mEncoded( false )
02488 {
02489 }
02490 
02491 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, const QPtrList<KMMsgBase>& msgs )
02492   : KMCommand( parent, msgs ), mImplicitAttachments( true ), mEncoded( false )
02493 {
02494 }
02495 
02496 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, QPtrList<partNode>& attachments,
02497                                                     KMMessage *msg, bool encoded )
02498   : KMCommand( parent ), mImplicitAttachments( false ), mEncoded( encoded )
02499 {
02500   for ( QPtrListIterator<partNode> it( attachments ); it.current(); ++it ) {
02501     mAttachmentMap.insert( it.current(), msg );
02502   }
02503 }
02504 
02505 KMCommand::Result KMSaveAttachmentsCommand::execute()
02506 {
02507   setEmitsCompletedItself( true );
02508   if ( mImplicitAttachments ) {
02509     QPtrList<KMMessage> msgList = retrievedMsgs();
02510     KMMessage *msg;
02511     for ( QPtrListIterator<KMMessage> itr( msgList );
02512           ( msg = itr.current() );
02513           ++itr ) {
02514       partNode *rootNode = partNode::fromMessage( msg );
02515       for ( partNode *child = rootNode; child;
02516             child = child->firstChild() ) {
02517         for ( partNode *node = child; node; node = node->nextSibling() ) {
02518           if ( node->type() != DwMime::kTypeMultipart )
02519             mAttachmentMap.insert( node, msg );
02520         }
02521       }
02522     }
02523   }
02524   setDeletesItself( true );
02525   // load all parts
02526   KMLoadPartsCommand *command = new KMLoadPartsCommand( mAttachmentMap );
02527   connect( command, SIGNAL( partsRetrieved() ),
02528            this, SLOT( slotSaveAll() ) );
02529   command->start();
02530 
02531   return OK;
02532 }
02533 
02534 void KMSaveAttachmentsCommand::slotSaveAll()
02535 {
02536   // now that all message parts have been retrieved, remove all parts which
02537   // don't represent an attachment if they were not explicitely passed in the
02538   // c'tor
02539   if ( mImplicitAttachments ) {
02540     for ( PartNodeMessageMap::iterator it = mAttachmentMap.begin();
02541           it != mAttachmentMap.end(); ) {
02542       // only body parts which have a filename or a name parameter (except for
02543       // the root node for which name is set to the message's subject) are
02544       // considered attachments
02545       if ( it.key()->msgPart().fileName().stripWhiteSpace().isEmpty() &&
02546            ( it.key()->msgPart().name().stripWhiteSpace().isEmpty() ||
02547              !it.key()->parentNode() ) ) {
02548         PartNodeMessageMap::iterator delIt = it;
02549         ++it;
02550         mAttachmentMap.remove( delIt );
02551       }
02552       else
02553         ++it;
02554     }
02555     if ( mAttachmentMap.isEmpty() ) {
02556       KMessageBox::information( 0, i18n("Found no attachments to save.") );
02557       setResult( OK ); // The user has already been informed.
02558       emit completed( this );
02559       deleteLater();
02560       return;
02561     }
02562   }
02563 
02564   KURL url, dirUrl;
02565   if ( mAttachmentMap.count() > 1 ) {
02566     // get the dir
02567     dirUrl = KDirSelectDialog::selectDirectory( QString::null, false,
02568                                                 parentWidget(),
02569                                                 i18n("Save Attachments To") );
02570     if ( !dirUrl.isValid() ) {
02571       setResult( Canceled );
02572       emit completed( this );
02573       deleteLater();
02574       return;
02575     }
02576 
02577     // we may not get a slash-terminated url out of KDirSelectDialog
02578     dirUrl.adjustPath( 1 );
02579   }
02580   else {
02581     // only one item, get the desired filename
02582     partNode *node = mAttachmentMap.begin().key();
02583     // replace all ':' with '_' because ':' isn't allowed on FAT volumes
02584     QString s =
02585       node->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
02586     if ( s.isEmpty() )
02587       s = node->msgPart().name().stripWhiteSpace().replace( ':', '_' );
02588     if ( s.isEmpty() )
02589       s = i18n("filename for an unnamed attachment", "attachment.1");
02590     url = KFileDialog::getSaveURL( s, QString::null, parentWidget(),
02591                                    QString::null );
02592     if ( url.isEmpty() ) {
02593       setResult( Canceled );
02594       emit completed( this );
02595       deleteLater();
02596       return;
02597     }
02598   }
02599 
02600   QMap< QString, int > renameNumbering;
02601 
02602   Result globalResult = OK;
02603   int unnamedAtmCount = 0;
02604   for ( PartNodeMessageMap::const_iterator it = mAttachmentMap.begin();
02605         it != mAttachmentMap.end();
02606         ++it ) {
02607     KURL curUrl;
02608     if ( !dirUrl.isEmpty() ) {
02609       curUrl = dirUrl;
02610       QString s =
02611         it.key()->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
02612       if ( s.isEmpty() )
02613         s = it.key()->msgPart().name().stripWhiteSpace().replace( ':', '_' );
02614       if ( s.isEmpty() ) {
02615         ++unnamedAtmCount;
02616         s = i18n("filename for the %1-th unnamed attachment",
02617                  "attachment.%1")
02618             .arg( unnamedAtmCount );
02619       }
02620       curUrl.setFileName( s );
02621     } else {
02622       curUrl = url;
02623     }
02624 
02625     if ( !curUrl.isEmpty() ) {
02626 
02627      // Rename the file if we have already saved one with the same name:
02628      // try appending a number before extension (e.g. "pic.jpg" => "pic_2.jpg")
02629      QString origFile = curUrl.fileName();
02630      QString file = origFile;
02631 
02632      while ( renameNumbering.contains(file) ) {
02633        file = origFile;
02634        int num = renameNumbering[file] + 1;
02635        int dotIdx = file.findRev('.');
02636        file = file.insert( (dotIdx>=0) ? dotIdx : file.length(), QString("_") + QString::number(num) );
02637      }
02638      curUrl.setFileName(file);
02639 
02640      // Increment the counter for both the old and the new filename
02641      if ( !renameNumbering.contains(origFile))
02642          renameNumbering[origFile] = 1;
02643      else
02644          renameNumbering[origFile]++;
02645 
02646      if ( file != origFile ) {
02647         if ( !renameNumbering.contains(file))
02648             renameNumbering[file] = 1;
02649         else
02650             renameNumbering[file]++;
02651      }
02652 
02653 
02654       if ( KIO::NetAccess::exists( curUrl, false, parentWidget() ) ) {
02655         if ( KMessageBox::warningContinueCancel( parentWidget(),
02656               i18n( "A file named %1 already exists. Do you want to overwrite it?" )
02657               .arg( curUrl.fileName() ),
02658               i18n( "File Already Exists" ), i18n("&Overwrite") ) == KMessageBox::Cancel) {
02659           continue;
02660         }
02661       }
02662       // save
02663       const Result result = saveItem( it.key(), curUrl );
02664       if ( result != OK )
02665         globalResult = result;
02666     }
02667   }
02668   setResult( globalResult );
02669   emit completed( this );
02670   deleteLater();
02671 }
02672 
02673 KMCommand::Result KMSaveAttachmentsCommand::saveItem( partNode *node,
02674                                                       const KURL& url )
02675 {
02676   bool bSaveEncrypted = false;
02677   bool bEncryptedParts = node->encryptionState() != KMMsgNotEncrypted;
02678   if( bEncryptedParts )
02679     if( KMessageBox::questionYesNo( parentWidget(),
02680           i18n( "The part %1 of the message is encrypted. Do you want to keep the encryption when saving?" ).
02681           arg( url.fileName() ),
02682           i18n( "KMail Question" ), i18n("Keep Encryption"), i18n("Do Not Keep") ) ==
02683         KMessageBox::Yes )
02684       bSaveEncrypted = true;
02685 
02686   bool bSaveWithSig = true;
02687   if( node->signatureState() != KMMsgNotSigned )
02688     if( KMessageBox::questionYesNo( parentWidget(),
02689           i18n( "The part %1 of the message is signed. Do you want to keep the signature when saving?" ).
02690           arg( url.fileName() ),
02691           i18n( "KMail Question" ), i18n("Keep Signature"), i18n("Do Not Keep") ) !=
02692         KMessageBox::Yes )
02693       bSaveWithSig = false;
02694 
02695   QByteArray data;
02696   if ( mEncoded )
02697   {
02698     // This does not decode the Message Content-Transfer-Encoding
02699     // but saves the _original_ content of the message part
02700     data = KMail::Util::ByteArray( node->msgPart().dwBody() );
02701   }
02702   else
02703   {
02704     if( bSaveEncrypted || !bEncryptedParts) {
02705       partNode *dataNode = node;
02706       QCString rawReplyString;
02707       bool gotRawReplyString = false;
02708       if( !bSaveWithSig ) {
02709         if( DwMime::kTypeMultipart == node->type() &&
02710             DwMime::kSubtypeSigned == node->subType() ){
02711           // carefully look for the part that is *not* the signature part:
02712           if( node->findType( DwMime::kTypeApplication,
02713                 DwMime::kSubtypePgpSignature,
02714                 true, false ) ){
02715             dataNode = node->findTypeNot( DwMime::kTypeApplication,
02716                 DwMime::kSubtypePgpSignature,
02717                 true, false );
02718           }else if( node->findType( DwMime::kTypeApplication,
02719                 DwMime::kSubtypePkcs7Mime,
02720                 true, false ) ){
02721             dataNode = node->findTypeNot( DwMime::kTypeApplication,
02722                 DwMime::kSubtypePkcs7Mime,
02723                 true, false );
02724           }else{
02725             dataNode = node->findTypeNot( DwMime::kTypeMultipart,
02726                 DwMime::kSubtypeUnknown,
02727                 true, false );
02728           }
02729     }else{
02730       ObjectTreeParser otp( 0, 0, false, false, false );
02731 
02732       // process this node and all it's siblings and descendants
02733       dataNode->setProcessed( false, true );
02734       otp.parseObjectTree( dataNode );
02735 
02736       rawReplyString = otp.rawReplyString();
02737       gotRawReplyString = true;
02738         }
02739       }
02740       QByteArray cstr = gotRawReplyString
02741                          ? rawReplyString
02742                          : dataNode->msgPart().bodyDecodedBinary();
02743       data = cstr;
02744       size_t size = cstr.size();
02745       if ( dataNode->msgPart().type() == DwMime::kTypeText ) {
02746         // convert CRLF to LF before writing text attachments to disk
02747         size = KMail::Util::crlf2lf( cstr.data(), size );
02748       }
02749       data.resize( size );
02750     }
02751   }
02752   QDataStream ds;
02753   QFile file;
02754   KTempFile tf;
02755   tf.setAutoDelete( true );
02756   if ( url.isLocalFile() )
02757   {
02758     // save directly
02759     file.setName( url.path() );
02760     if ( !file.open( IO_WriteOnly ) )
02761     {
02762       KMessageBox::error( parentWidget(),
02763           i18n( "%2 is detailed error description",
02764             "Could not write the file %1:\n%2" )
02765           .arg( file.name() )
02766           .arg( QString::fromLocal8Bit( strerror( errno ) ) ),
02767           i18n( "KMail Error" ) );
02768       return Failed;
02769     }
02770 
02771     // #79685 by default use the umask the user defined, but let it be configurable
02772     if ( GlobalSettings::self()->disregardUmask() )
02773       fchmod( file.handle(), S_IRUSR | S_IWUSR );
02774 
02775     ds.setDevice( &file );
02776   } else
02777   {
02778     // tmp file for upload
02779     ds.setDevice( tf.file() );
02780   }
02781 
02782   ds.writeRawBytes( data.data(), data.size() );
02783   if ( !url.isLocalFile() )
02784   {
02785     tf.close();
02786     if ( !KIO::NetAccess::upload( tf.name(), url, parentWidget() ) )
02787     {
02788       KMessageBox::error( parentWidget(),
02789           i18n( "Could not write the file %1." )
02790           .arg( url.path() ),
02791           i18n( "KMail Error" ) );
02792       return Failed;
02793     }
02794   } else
02795     file.close();
02796   return OK;
02797 }
02798 
02799 KMLoadPartsCommand::KMLoadPartsCommand( QPtrList<partNode> &parts, KMMessage *msg )
02800   : mNeedsRetrieval( 0 )
02801 {
02802   for ( QPtrListIterator<partNode> it( parts ); it.current(); ++it ) {
02803     if ( msg ) {
02804       mPartMap.insert( it.current(), msg );
02805     }
02806   }
02807 }
02808 
02809 KMLoadPartsCommand::KMLoadPartsCommand( partNode *node, KMMessage *msg )
02810   : mNeedsRetrieval( 0 )
02811 {
02812   if ( msg ) {
02813     mPartMap.insert( node, msg );
02814   }
02815 }
02816 
02817 KMLoadPartsCommand::KMLoadPartsCommand( PartNodeMessageMap& partMap )
02818   : mNeedsRetrieval( 0 ), mPartMap( partMap )
02819 {
02820 }
02821 
02822 void KMLoadPartsCommand::slotStart()
02823 {
02824   for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
02825         it != mPartMap.end();
02826         ++it ) {
02827     if ( it.data() &&
02828          !it.key()->msgPart().isComplete() &&
02829          !it.key()->msgPart().partSpecifier().isEmpty() ) {
02830       // incomplete part, so retrieve it first
02831       ++mNeedsRetrieval;
02832       KMFolder* curFolder = it.data()->parent();
02833       if ( curFolder ) {
02834         FolderJob *job =
02835           curFolder->createJob( it.data(), FolderJob::tGetMessage,
02836                                 0, it.key()->msgPart().partSpecifier() );
02837         job->setCancellable( false );
02838         connect( job, SIGNAL(messageUpdated(KMMessage*, QString)),
02839                  this, SLOT(slotPartRetrieved(KMMessage*, QString)) );
02840         job->start();
02841       } else
02842         kdWarning(5006) << "KMLoadPartsCommand - msg has no parent" << endl;
02843     }
02844   }
02845   if ( mNeedsRetrieval == 0 )
02846     execute();
02847 }
02848 
02849 void KMLoadPartsCommand::slotPartRetrieved( KMMessage *msg,
02850                                             QString partSpecifier )
02851 {
02852   DwBodyPart *part =
02853     msg->findDwBodyPart( msg->getFirstDwBodyPart(), partSpecifier );
02854   if ( part ) {
02855     // update the DwBodyPart in the partNode
02856     for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
02857           it != mPartMap.end();
02858           ++it ) {
02859       if ( it.key()->dwPart() && ( it.key()->dwPart()->partId() == part->partId() ) ) {
02860         it.key()->setDwPart( part );
02861       }
02862     }
02863   } else
02864     kdWarning(5006) << "KMLoadPartsCommand::slotPartRetrieved - could not find bodypart!" << endl;
02865   --mNeedsRetrieval;
02866   if ( mNeedsRetrieval == 0 )
02867     execute();
02868 }
02869 
02870 KMCommand::Result KMLoadPartsCommand::execute()
02871 {
02872   emit partsRetrieved();
02873   setResult( OK );
02874   emit completed( this );
02875   deleteLater();
02876   return OK;
02877 }
02878 
02879 KMResendMessageCommand::KMResendMessageCommand( QWidget *parent,
02880    KMMessage *msg )
02881   :KMCommand( parent, msg )
02882 {
02883 }
02884 
02885 KMCommand::Result KMResendMessageCommand::execute()
02886 {
02887    KMMessage *msg = retrievedMessage();
02888    if ( !msg || !msg->codec() ) {
02889      return Failed;
02890    }
02891    KMMessage *newMsg = new KMMessage(*msg);
02892 
02893    QStringList whiteList;
02894    whiteList << "To" << "Cc" << "Bcc" << "Subject";
02895    newMsg->sanitizeHeaders( whiteList );
02896 
02897    newMsg->setCharset(msg->codec()->mimeName());
02898    newMsg->setParent( 0 );
02899 
02900    // make sure we have an identity set, default, if necessary
02901    newMsg->setHeaderField("X-KMail-Identity", QString::number( newMsg->identityUoid() ));
02902    newMsg->applyIdentity( newMsg->identityUoid() );
02903 
02904    KMail::Composer * win = KMail::makeComposer();
02905    win->setMsg(newMsg, false, true);
02906    win->show();
02907 
02908    return OK;
02909 }
02910 
02911 KMMailingListCommand::KMMailingListCommand( QWidget *parent, KMFolder *folder )
02912   : KMCommand( parent ), mFolder( folder )
02913 {
02914 }
02915 
02916 KMCommand::Result KMMailingListCommand::execute()
02917 {
02918   KURL::List lst = urls();
02919   QString handler = ( mFolder->mailingList().handler() == MailingList::KMail )
02920     ? "mailto" : "https";
02921 
02922   KMCommand *command = 0;
02923   for ( KURL::List::Iterator itr = lst.begin(); itr != lst.end(); ++itr ) {
02924     if ( handler == (*itr).protocol() ) {
02925       command = new KMUrlClickedCommand( *itr, mFolder->identity(), 0, false );
02926     }
02927   }
02928   if ( !command && !lst.empty() ) {
02929     command =
02930       new KMUrlClickedCommand( lst.first(), mFolder->identity(), 0, false );
02931   }
02932   if ( command ) {
02933     connect( command, SIGNAL( completed( KMCommand * ) ),
02934              this, SLOT( commandCompleted( KMCommand * ) ) );
02935     setDeletesItself( true );
02936     setEmitsCompletedItself( true );
02937     command->start();
02938     return OK;
02939   }
02940   return Failed;
02941 }
02942 
02943 void KMMailingListCommand::commandCompleted( KMCommand *command )
02944 {
02945   setResult( command->result() );
02946   emit completed( this );
02947   deleteLater();
02948 }
02949 
02950 KMMailingListPostCommand::KMMailingListPostCommand( QWidget *parent, KMFolder *folder )
02951   : KMMailingListCommand( parent, folder )
02952 {
02953 }
02954 KURL::List KMMailingListPostCommand::urls() const
02955 {
02956   return mFolder->mailingList().postURLS();
02957 }
02958 
02959 KMMailingListSubscribeCommand::KMMailingListSubscribeCommand( QWidget *parent, KMFolder *folder )
02960   : KMMailingListCommand( parent, folder )
02961 {
02962 }
02963 KURL::List KMMailingListSubscribeCommand::urls() const
02964 {
02965   return mFolder->mailingList().subscribeURLS();
02966 }
02967 
02968 KMMailingListUnsubscribeCommand::KMMailingListUnsubscribeCommand( QWidget *parent, KMFolder *folder )
02969   : KMMailingListCommand( parent, folder )
02970 {
02971 }
02972 KURL::List KMMailingListUnsubscribeCommand::urls() const
02973 {
02974   return mFolder->mailingList().unsubscribeURLS();
02975 }
02976 
02977 KMMailingListArchivesCommand::KMMailingListArchivesCommand( QWidget *parent, KMFolder *folder )
02978   : KMMailingListCommand( parent, folder )
02979 {
02980 }
02981 KURL::List KMMailingListArchivesCommand::urls() const
02982 {
02983   return mFolder->mailingList().archiveURLS();
02984 }
02985 
02986 KMMailingListHelpCommand::KMMailingListHelpCommand( QWidget *parent, KMFolder *folder )
02987   : KMMailingListCommand( parent, folder )
02988 {
02989 }
02990 KURL::List KMMailingListHelpCommand::urls() const
02991 {
02992   return mFolder->mailingList().helpURLS();
02993 }
02994 
02995 KMIMChatCommand::KMIMChatCommand( const KURL &url, KMMessage *msg )
02996   :mUrl( url ), mMessage( msg )
02997 {
02998 }
02999 
03000 KMCommand::Result KMIMChatCommand::execute()
03001 {
03002   kdDebug( 5006 ) << k_funcinfo << " URL is: " << mUrl << endl;
03003   QString addr = KMMessage::decodeMailtoUrl( mUrl.path() );
03004   // find UID for mail address
03005   KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
03006   KABC::AddresseeList addressees = addressBook->findByEmail( KPIM::getEmailAddress( addr ) ) ;
03007 
03008   // start chat
03009   if( addressees.count() == 1 ) {
03010     kmkernel->imProxy()->chatWithContact( addressees[0].uid() );
03011     return OK;
03012   }
03013   else
03014   {
03015     kdDebug( 5006 ) << "Didn't find exactly one addressee, couldn't tell who to chat to for that email address.  Count = " << addressees.count() << endl;
03016 
03017     QString apology;
03018     if ( addressees.isEmpty() )
03019       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." );
03020     else
03021     {
03022       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." );
03023       QStringList nameList;
03024       KABC::AddresseeList::const_iterator it = addressees.begin();
03025       KABC::AddresseeList::const_iterator end = addressees.end();
03026       for ( ; it != end; ++it )
03027       {
03028           nameList.append( (*it).realName() );
03029       }
03030       QString names = nameList.join( QString::fromLatin1( ",\n" ) );
03031       apology = apology.arg( names );
03032     }
03033 
03034     KMessageBox::sorry( parentWidget(), apology );
03035     return Failed;
03036   }
03037 }
03038 
03039 KMHandleAttachmentCommand::KMHandleAttachmentCommand( partNode* node,
03040      KMMessage* msg, int atmId, const QString& atmName,
03041      AttachmentAction action, KService::Ptr offer, QWidget* parent )
03042 : KMCommand( parent ), mNode( node ), mMsg( msg ), mAtmId( atmId ), mAtmName( atmName ),
03043   mAction( action ), mOffer( offer ), mJob( 0 )
03044 {
03045 }
03046 
03047 void KMHandleAttachmentCommand::slotStart()
03048 {
03049   if ( !mNode->msgPart().isComplete() )
03050   {
03051     // load the part
03052     kdDebug(5006) << "load part" << endl;
03053     KMLoadPartsCommand *command = new KMLoadPartsCommand( mNode, mMsg );
03054     connect( command, SIGNAL( partsRetrieved() ),
03055         this, SLOT( slotPartComplete() ) );
03056     command->start();
03057   } else
03058   {
03059     execute();
03060   }
03061 }
03062 
03063 void KMHandleAttachmentCommand::slotPartComplete()
03064 {
03065   execute();
03066 }
03067 
03068 KMCommand::Result KMHandleAttachmentCommand::execute()
03069 {
03070   switch( mAction )
03071   {
03072     case Open:
03073       atmOpen();
03074       break;
03075     case OpenWith:
03076       atmOpenWith();
03077       break;
03078     case View:
03079       atmView();
03080       break;
03081     case Save:
03082       atmSave();
03083       break;
03084     case Properties:
03085       atmProperties();
03086       break;
03087     case ChiasmusEncrypt:
03088       atmEncryptWithChiasmus();
03089       return Undefined;
03090       break;
03091     default:
03092       kdDebug(5006) << "unknown action " << mAction << endl;
03093       break;
03094   }
03095   setResult( OK );
03096   emit completed( this );
03097   deleteLater();
03098   return OK;
03099 }
03100 
03101 QString KMHandleAttachmentCommand::createAtmFileLink() const
03102 {
03103   QFileInfo atmFileInfo( mAtmName );
03104 
03105   if ( atmFileInfo.size() == 0 )
03106   {
03107     kdDebug(5006) << k_funcinfo << "rewriting attachment" << endl;
03108     // there is something wrong so write the file again
03109     QByteArray data = mNode->msgPart().bodyDecodedBinary();
03110     size_t size = data.size();
03111     if ( mNode->msgPart().type() == DwMime::kTypeText && size) {
03112       // convert CRLF to LF before writing text attachments to disk
03113       size = KMail::Util::crlf2lf( data.data(), size );
03114     }
03115     KPIM::kBytesToFile( data.data(), size, mAtmName, false, false, false );
03116   }
03117 
03118   // tempfile name ist /TMP/attachmentsRANDOM/atmFileInfo.fileName()"
03119   KTempDir *linkDir = new KTempDir( locateLocal( "tmp", "attachments" ) );
03120   QString linkPath = linkDir->name() + atmFileInfo.fileName();
03121   QFile *linkFile = new QFile( linkPath );
03122 
03123   linkDir->setAutoDelete( true );
03124   QString linkName = linkFile->name();
03125   delete linkFile;
03126   delete linkDir;
03127 
03128   if ( ::link(QFile::encodeName( mAtmName ), QFile::encodeName( linkName )) == 0 ) {
03129     return linkName; // success
03130   }
03131   return QString::null;
03132 }
03133 
03134 KService::Ptr KMHandleAttachmentCommand::getServiceOffer()
03135 {
03136   KMMessagePart& msgPart = mNode->msgPart();
03137   const QString contentTypeStr =
03138     ( msgPart.typeStr() + '/' + msgPart.subtypeStr() ).lower();
03139 
03140   if ( contentTypeStr == "text/x-vcard" ) {
03141     atmView();
03142     return 0;
03143   }
03144   // determine the MIME type of the attachment
03145   KMimeType::Ptr mimetype;
03146   // prefer the value of the Content-Type header
03147   mimetype = KMimeType::mimeType( contentTypeStr );
03148   if ( mimetype->name() == "application/octet-stream" ) {
03149     // consider the filename if Content-Type is application/octet-stream
03150     mimetype = KMimeType::findByPath( mAtmName, 0, true /* no disk access */ );
03151   }
03152   if ( ( mimetype->name() == "application/octet-stream" )
03153        && msgPart.isComplete() ) {
03154     // consider the attachment's contents if neither the Content-Type header
03155     // nor the filename give us a clue
03156     mimetype = KMimeType::findByFileContent( mAtmName );
03157   }
03158   return KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
03159 }
03160 
03161 void KMHandleAttachmentCommand::atmOpen()
03162 {
03163   if ( !mOffer )
03164     mOffer = getServiceOffer();
03165   if ( !mOffer ) {
03166     kdDebug(5006) << k_funcinfo << "got no offer" << endl;
03167     return;
03168   }
03169 
03170   KURL::List lst;
03171   KURL url;
03172   bool autoDelete = true;
03173   QString fname = createAtmFileLink();
03174 
03175   if ( fname.isNull() ) {
03176     autoDelete = false;
03177     fname = mAtmName;
03178   }
03179 
03180   KPIM::checkAndCorrectPermissionsIfPossible( fname, false, true, true );
03181 
03182   url.setPath( fname );
03183   lst.append( url );
03184   if ( (KRun::run( *mOffer, lst, autoDelete ) <= 0) && autoDelete ) {
03185       QFile::remove(url.path());
03186   }
03187 }
03188 
03189 void KMHandleAttachmentCommand::atmOpenWith()
03190 {
03191   KURL::List lst;
03192   KURL url;
03193   bool autoDelete = true;
03194   QString fname = createAtmFileLink();
03195 
03196   if ( fname.isNull() ) {
03197     autoDelete = false;
03198     fname = mAtmName;
03199   }
03200 
03201   KPIM::checkAndCorrectPermissionsIfPossible( fname, false, true, true );
03202 
03203   url.setPath( fname );
03204   lst.append( url );
03205   if ( (! KRun::displayOpenWithDialog(lst, autoDelete)) && autoDelete ) {
03206     QFile::remove( url.path() );
03207   }
03208 }
03209 
03210 void KMHandleAttachmentCommand::atmView()
03211 {
03212   // we do not handle this ourself
03213   emit showAttachment( mAtmId, mAtmName );
03214 }
03215 
03216 void KMHandleAttachmentCommand::atmSave()
03217 {
03218   QPtrList<partNode> parts;
03219   parts.append( mNode );
03220   // save, do not leave encoded
03221   KMSaveAttachmentsCommand *command =
03222     new KMSaveAttachmentsCommand( parentWidget(), parts, mMsg, false );
03223   command->start();
03224 }
03225 
03226 void KMHandleAttachmentCommand::atmProperties()
03227 {
03228   KMMsgPartDialogCompat dlg( parentWidget() , 0, true );
03229   KMMessagePart& msgPart = mNode->msgPart();
03230   dlg.setMsgPart( &msgPart );
03231   dlg.exec();
03232 }
03233 
03234 void KMHandleAttachmentCommand::atmEncryptWithChiasmus()
03235 {
03236   const partNode * node = mNode;
03237   Q_ASSERT( node );
03238   if ( !node )
03239     return;
03240 
03241   // FIXME: better detection of mimetype??
03242   if ( !mAtmName.endsWith( ".xia", false ) )
03243     return;
03244 
03245   const Kleo::CryptoBackend::Protocol * chiasmus =
03246     Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
03247   Q_ASSERT( chiasmus );
03248   if ( !chiasmus )
03249     return;
03250 
03251   const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
03252   if ( !listjob.get() ) {
03253     const QString msg = i18n( "Chiasmus backend does not offer the "
03254                               "\"x-obtain-keys\" function. Please report this bug." );
03255     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03256     return;
03257   }
03258 
03259   if ( listjob->exec() ) {
03260     listjob->showErrorDialog( parentWidget(), i18n( "Chiasmus Backend Error" ) );
03261     return;
03262   }
03263 
03264   const QVariant result = listjob->property( "result" );
03265   if ( result.type() != QVariant::StringList ) {
03266     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
03267                               "The \"x-obtain-keys\" function did not return a "
03268                               "string list. Please report this bug." );
03269     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03270     return;
03271   }
03272 
03273   const QStringList keys = result.toStringList();
03274   if ( keys.empty() ) {
03275     const QString msg = i18n( "No keys have been found. Please check that a "
03276                               "valid key path has been set in the Chiasmus "
03277                               "configuration." );
03278     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03279     return;
03280   }
03281 
03282   ChiasmusKeySelector selectorDlg( parentWidget(), i18n( "Chiasmus Decryption Key Selection" ),
03283                                    keys, GlobalSettings::chiasmusDecryptionKey(),
03284                                    GlobalSettings::chiasmusDecryptionOptions() );
03285   if ( selectorDlg.exec() != QDialog::Accepted )
03286     return;
03287 
03288   GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
03289   GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
03290   assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
03291 
03292   Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", QMap<QString,QVariant>() );
03293   if ( !job ) {
03294     const QString msg = i18n( "Chiasmus backend does not offer the "
03295                               "\"x-decrypt\" function. Please report this bug." );
03296     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03297     return;
03298   }
03299 
03300   const QByteArray input = node->msgPart().bodyDecodedBinary();
03301 
03302   if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
03303        !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
03304        !job->setProperty( "input", input ) ) {
03305     const QString msg = i18n( "The \"x-decrypt\" function does not accept "
03306                               "the expected parameters. Please report this bug." );
03307     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03308     return;
03309   }
03310 
03311   setDeletesItself( true ); // the job below is async, we have to cleanup ourselves
03312   if ( job->start() ) {
03313     job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
03314     return;
03315   }
03316 
03317   mJob = job;
03318   connect( job, SIGNAL(result(const GpgME::Error&,const QVariant&)),
03319            this, SLOT(slotAtmDecryptWithChiasmusResult(const GpgME::Error&,const QVariant&)) );
03320 }
03321 
03322 static const QString chomp( const QString & base, const QString & suffix, bool cs ) {
03323   return base.endsWith( suffix, cs ) ? base.left( base.length() - suffix.length() ) : base ;
03324 }
03325 
03326 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusResult( const GpgME::Error & err, const QVariant & result )
03327 {
03328   LaterDeleterWithCommandCompletion d( this );
03329   if ( !mJob )
03330     return;
03331   Q_ASSERT( mJob == sender() );
03332   if ( mJob != sender() )
03333     return;
03334   Kleo::Job * job = mJob;
03335   mJob = 0;
03336   if ( err.isCanceled() )
03337     return;
03338   if ( err ) {
03339     job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
03340     return;
03341   }
03342 
03343   if ( result.type() != QVariant::ByteArray ) {
03344     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
03345                               "The \"x-decrypt\" function did not return a "
03346                               "byte array. Please report this bug." );
03347     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03348     return;
03349   }
03350 
03351   const KURL url = KFileDialog::getSaveURL( chomp( mAtmName, ".xia", false ), QString::null, parentWidget() );
03352   if ( url.isEmpty() )
03353     return;
03354 
03355   bool overwrite = KMail::Util::checkOverwrite( url, parentWidget() );
03356   if ( !overwrite )
03357     return;
03358 
03359   d.setDisabled( true ); // we got this far, don't delete yet
03360   KIO::Job * uploadJob = KIO::storedPut( result.toByteArray(), url, -1, overwrite, false /*resume*/ );
03361   uploadJob->setWindow( parentWidget() );
03362   connect( uploadJob, SIGNAL(result(KIO::Job*)),
03363            this, SLOT(slotAtmDecryptWithChiasmusUploadResult(KIO::Job*)) );
03364 }
03365 
03366 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusUploadResult( KIO::Job * job )
03367 {
03368   if ( job->error() )
03369     job->showErrorDialog();
03370   LaterDeleterWithCommandCompletion d( this );
03371   d.setResult( OK );
03372 }
03373 
03374 
03375 AttachmentModifyCommand::AttachmentModifyCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03376     KMCommand( parent, msg ),
03377     mPartIndex( node->nodeId() ),
03378     mSernum( 0 )
03379 {
03380 }
03381 
03382 AttachmentModifyCommand::AttachmentModifyCommand( int nodeId, KMMessage *msg, QWidget *parent )
03383   : KMCommand( parent, msg ),
03384     mPartIndex( nodeId ),
03385     mSernum( 0 )
03386 {
03387 }
03388 
03389 AttachmentModifyCommand::~ AttachmentModifyCommand()
03390 {
03391 }
03392 
03393 KMCommand::Result AttachmentModifyCommand::execute()
03394 {
03395   KMMessage *msg = retrievedMessage();
03396   if ( !msg )
03397     return Failed;
03398   mSernum = msg->getMsgSerNum();
03399 
03400   mFolder = msg->parent();
03401   if ( !mFolder || !mFolder->storage() )
03402     return Failed;
03403 
03404   Result res = doAttachmentModify();
03405   if ( res != OK )
03406     return res;
03407 
03408   setEmitsCompletedItself( true );
03409   setDeletesItself( true );
03410   return OK;
03411 }
03412 
03413 void AttachmentModifyCommand::storeChangedMessage(KMMessage * msg)
03414 {
03415   if ( !mFolder || !mFolder->storage() ) {
03416     kdWarning(5006) << k_funcinfo << "We lost the folder!" << endl;
03417     setResult( Failed );
03418     emit completed( this );
03419     deleteLater();
03420   }
03421   int res = mFolder->addMsg( msg ) != 0;
03422   if ( mFolder->folderType() == KMFolderTypeImap ) {
03423     KMFolderImap *f = static_cast<KMFolderImap*>( mFolder->storage() );
03424     connect( f, SIGNAL(folderComplete(KMFolderImap*,bool)),
03425              SLOT(messageStoreResult(KMFolderImap*,bool)) );
03426   } else {
03427     messageStoreResult( 0, res == 0 );
03428   }
03429 }
03430 
03431 void AttachmentModifyCommand::messageStoreResult(KMFolderImap* folder, bool success )
03432 {
03433   Q_UNUSED( folder );
03434   if ( success ) {
03435     KMCommand *delCmd = new KMDeleteMsgCommand( mSernum );
03436     connect( delCmd, SIGNAL(completed(KMCommand*)), SLOT(messageDeleteResult(KMCommand*)) );
03437     delCmd->start();
03438     return;
03439   }
03440   kdWarning(5006) << k_funcinfo << "Adding modified message failed." << endl;
03441   setResult( Failed );
03442   emit completed( this );
03443   deleteLater();
03444 }
03445 
03446 void AttachmentModifyCommand::messageDeleteResult(KMCommand * cmd)
03447 {
03448   setResult( cmd->result() );
03449   emit completed( this );
03450   deleteLater();
03451 }
03452 
03453 KMDeleteAttachmentCommand::KMDeleteAttachmentCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03454     AttachmentModifyCommand( node, msg, parent )
03455 {
03456   kdDebug(5006) << k_funcinfo << endl;
03457 }
03458 
03459 KMDeleteAttachmentCommand::KMDeleteAttachmentCommand( int nodeId, KMMessage *msg, QWidget *parent )
03460   : AttachmentModifyCommand( nodeId, msg, parent )
03461 {
03462   kdDebug(5006) << k_funcinfo << endl;
03463 }
03464 
03465 KMDeleteAttachmentCommand::~KMDeleteAttachmentCommand()
03466 {
03467   kdDebug(5006) << k_funcinfo << endl;
03468 }
03469 
03470 KMCommand::Result KMDeleteAttachmentCommand::doAttachmentModify()
03471 {
03472   KMMessage *msg = retrievedMessage();
03473   if ( !msg || !msg->deleteBodyPart( mPartIndex ) )
03474     return Failed;
03475 
03476   KMMessage *newMsg = new KMMessage();
03477   newMsg->fromDwString( msg->asDwString() );
03478   newMsg->setStatus( msg->status() );
03479 
03480   storeChangedMessage( newMsg );
03481   return OK;
03482 }
03483 
03484 
03485 KMEditAttachmentCommand::KMEditAttachmentCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03486     AttachmentModifyCommand( node, msg, parent )
03487 {
03488   kdDebug(5006) << k_funcinfo << endl;
03489   mTempFile.setAutoDelete( true );
03490 }
03491 
03492 KMEditAttachmentCommand::KMEditAttachmentCommand( int nodeId, KMMessage *msg, QWidget *parent )
03493   : AttachmentModifyCommand( nodeId, msg, parent )
03494 {
03495   kdDebug(5006) << k_funcinfo << endl;
03496   mTempFile.setAutoDelete( true );
03497 }
03498 
03499 KMEditAttachmentCommand::~ KMEditAttachmentCommand()
03500 {
03501 }
03502 
03503 KMCommand::Result KMEditAttachmentCommand::doAttachmentModify()
03504 {
03505   KMMessage *msg = retrievedMessage();
03506   if ( !msg )
03507     return Failed;
03508 
03509   KMMessagePart part;
03510   DwBodyPart *dwpart = msg->findPart( mPartIndex );
03511   if ( !dwpart )
03512     return Failed;
03513   KMMessage::bodyPart( dwpart, &part, true );
03514   if ( !part.isComplete() )
03515      return Failed;
03516 
03517   if( !dynamic_cast<DwBody*>( dwpart->Parent() ) )
03518     return Failed;
03519 
03520   mTempFile.file()->writeBlock( part.bodyDecodedBinary() );
03521   mTempFile.file()->flush();
03522 
03523   KMail::EditorWatcher *watcher =
03524           new KMail::EditorWatcher( KURL( mTempFile.file()->name() ),
03525                                     part.typeStr() + "/" + part.subtypeStr(),
03526                                     false, this, parentWidget() );
03527   connect( watcher, SIGNAL(editDone(KMail::EditorWatcher*)), SLOT(editDone(KMail::EditorWatcher*)) );
03528   if ( !watcher->start() )
03529     return Failed;
03530   setEmitsCompletedItself( true );
03531   setDeletesItself( true );
03532   return OK;
03533 }
03534 
03535 void KMEditAttachmentCommand::editDone(KMail::EditorWatcher * watcher)
03536 {
03537   kdDebug(5006) << k_funcinfo << endl;
03538   // anything changed?
03539   if ( !watcher->fileChanged() ) {
03540     kdDebug(5006) << k_funcinfo << "File has not been changed" << endl;
03541     setResult( Canceled );
03542     emit completed( this );
03543     deleteLater();
03544   }
03545 
03546   mTempFile.file()->reset();
03547   QByteArray data = mTempFile.file()->readAll();
03548 
03549   // build the new message
03550   KMMessage *msg = retrievedMessage();
03551   KMMessagePart part;
03552   DwBodyPart *dwpart = msg->findPart( mPartIndex );
03553   KMMessage::bodyPart( dwpart, &part, true );
03554 
03555   DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
03556   assert( parentNode );
03557   parentNode->RemoveBodyPart( dwpart );
03558 
03559   KMMessagePart att;
03560   att.duplicate( part );
03561   att.setBodyEncodedBinary( data );
03562 
03563   DwBodyPart* newDwPart = msg->createDWBodyPart( &att );
03564   parentNode->AddBodyPart( newDwPart );
03565   msg->getTopLevelPart()->Assemble();
03566 
03567   KMMessage *newMsg = new KMMessage();
03568   newMsg->fromDwString( msg->asDwString() );
03569   newMsg->setStatus( msg->status() );
03570 
03571   storeChangedMessage( newMsg );
03572 }
03573 
03574 
03575 CreateTodoCommand::CreateTodoCommand(QWidget * parent, KMMessage * msg)
03576   : KMCommand( parent, msg )
03577 {
03578 }
03579 
03580 KMCommand::Result CreateTodoCommand::execute()
03581 {
03582   KMMessage *msg = retrievedMessage();
03583   if ( !msg || !msg->codec() ) {
03584     return Failed;
03585   }
03586 
03587   KMail::KorgHelper::ensureRunning();
03588 
03589   QString txt = i18n("From: %1\nTo: %2\nSubject: %3").arg( msg->from() )
03590                 .arg( msg->to() ).arg( msg->subject() );
03591 
03592   KTempFile tf;
03593   tf.setAutoDelete( true );
03594   QString uri = "kmail:" + QString::number( msg->getMsgSerNum() ) + "/" + msg->msgId();
03595   tf.file()->writeBlock( msg->asDwString().c_str(), msg->asDwString().length() );
03596   tf.close();
03597 
03598   KCalendarIface_stub *iface = new KCalendarIface_stub( kapp->dcopClient(), "korganizer", "CalendarIface" );
03599   iface->openTodoEditor( i18n("Mail: %1").arg( msg->subject() ), txt, uri,
03600                          tf.name(), QStringList(), "message/rfc822", true );
03601   delete iface;
03602 
03603   return OK;
03604 }
03605 
03606 #include "kmcommands.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys