kmail

kmcommands.cpp

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