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     KRun *runner = new KRun( mUrl ); // will delete itself
02414     runner->setRunExecutables( false );
02415   }
02416   else
02417     return Failed;
02418 
02419   return OK;
02420 }
02421 
02422 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, KMMessage *msg )
02423   : KMCommand( parent, msg ), mImplicitAttachments( true ), mEncoded( false )
02424 {
02425 }
02426 
02427 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, const QPtrList<KMMsgBase>& msgs )
02428   : KMCommand( parent, msgs ), mImplicitAttachments( true ), mEncoded( false )
02429 {
02430 }
02431 
02432 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, QPtrList<partNode>& attachments,
02433                                                     KMMessage *msg, bool encoded )
02434   : KMCommand( parent ), mImplicitAttachments( false ), mEncoded( encoded )
02435 {
02436   for ( QPtrListIterator<partNode> it( attachments ); it.current(); ++it ) {
02437     mAttachmentMap.insert( it.current(), msg );
02438   }
02439 }
02440 
02441 KMCommand::Result KMSaveAttachmentsCommand::execute()
02442 {
02443   setEmitsCompletedItself( true );
02444   if ( mImplicitAttachments ) {
02445     QPtrList<KMMessage> msgList = retrievedMsgs();
02446     KMMessage *msg;
02447     for ( QPtrListIterator<KMMessage> itr( msgList );
02448           ( msg = itr.current() );
02449           ++itr ) {
02450       partNode *rootNode = partNode::fromMessage( msg );
02451       for ( partNode *child = rootNode; child;
02452             child = child->firstChild() ) {
02453         for ( partNode *node = child; node; node = node->nextSibling() ) {
02454           if ( node->type() != DwMime::kTypeMultipart )
02455             mAttachmentMap.insert( node, msg );
02456         }
02457       }
02458     }
02459   }
02460   setDeletesItself( true );
02461   // load all parts
02462   KMLoadPartsCommand *command = new KMLoadPartsCommand( mAttachmentMap );
02463   connect( command, SIGNAL( partsRetrieved() ),
02464            this, SLOT( slotSaveAll() ) );
02465   command->start();
02466 
02467   return OK;
02468 }
02469 
02470 void KMSaveAttachmentsCommand::slotSaveAll()
02471 {
02472   // now that all message parts have been retrieved, remove all parts which
02473   // don't represent an attachment if they were not explicitely passed in the
02474   // c'tor
02475   if ( mImplicitAttachments ) {
02476     for ( PartNodeMessageMap::iterator it = mAttachmentMap.begin();
02477           it != mAttachmentMap.end(); ) {
02478       // only body parts which have a filename or a name parameter (except for
02479       // the root node for which name is set to the message's subject) are
02480       // considered attachments
02481       if ( it.key()->msgPart().fileName().stripWhiteSpace().isEmpty() &&
02482            ( it.key()->msgPart().name().stripWhiteSpace().isEmpty() ||
02483              !it.key()->parentNode() ) ) {
02484         PartNodeMessageMap::iterator delIt = it;
02485         ++it;
02486         mAttachmentMap.remove( delIt );
02487       }
02488       else
02489         ++it;
02490     }
02491     if ( mAttachmentMap.isEmpty() ) {
02492       KMessageBox::information( 0, i18n("Found no attachments to save.") );
02493       setResult( OK ); // The user has already been informed.
02494       emit completed( this );
02495       deleteLater();
02496       return;
02497     }
02498   }
02499 
02500   KURL url, dirUrl;
02501   if ( mAttachmentMap.count() > 1 ) {
02502     // get the dir
02503     dirUrl = KDirSelectDialog::selectDirectory( QString::null, false,
02504                                                 parentWidget(),
02505                                                 i18n("Save Attachments To") );
02506     if ( !dirUrl.isValid() ) {
02507       setResult( Canceled );
02508       emit completed( this );
02509       deleteLater();
02510       return;
02511     }
02512 
02513     // we may not get a slash-terminated url out of KDirSelectDialog
02514     dirUrl.adjustPath( 1 );
02515   }
02516   else {
02517     // only one item, get the desired filename
02518     partNode *node = mAttachmentMap.begin().key();
02519     // replace all ':' with '_' because ':' isn't allowed on FAT volumes
02520     QString s =
02521       node->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
02522     if ( s.isEmpty() )
02523       s = node->msgPart().name().stripWhiteSpace().replace( ':', '_' );
02524     if ( s.isEmpty() )
02525       s = i18n("filename for an unnamed attachment", "attachment.1");
02526     url = KFileDialog::getSaveURL( s, QString::null, parentWidget(),
02527                                    QString::null );
02528     if ( url.isEmpty() ) {
02529       setResult( Canceled );
02530       emit completed( this );
02531       deleteLater();
02532       return;
02533     }
02534   }
02535 
02536   QMap< QString, int > renameNumbering;
02537 
02538   Result globalResult = OK;
02539   int unnamedAtmCount = 0;
02540   for ( PartNodeMessageMap::const_iterator it = mAttachmentMap.begin();
02541         it != mAttachmentMap.end();
02542         ++it ) {
02543     KURL curUrl;
02544     if ( !dirUrl.isEmpty() ) {
02545       curUrl = dirUrl;
02546       QString s =
02547         it.key()->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
02548       if ( s.isEmpty() )
02549         s = it.key()->msgPart().name().stripWhiteSpace().replace( ':', '_' );
02550       if ( s.isEmpty() ) {
02551         ++unnamedAtmCount;
02552         s = i18n("filename for the %1-th unnamed attachment",
02553                  "attachment.%1")
02554             .arg( unnamedAtmCount );
02555       }
02556       curUrl.setFileName( s );
02557     } else {
02558       curUrl = url;
02559     }
02560 
02561     if ( !curUrl.isEmpty() ) {
02562 
02563      // Rename the file if we have already saved one with the same name:
02564      // try appending a number before extension (e.g. "pic.jpg" => "pic_2.jpg")
02565      QString origFile = curUrl.fileName();
02566      QString file = origFile;
02567 
02568      while ( renameNumbering.contains(file) ) {
02569        file = origFile;
02570        int num = renameNumbering[file] + 1;
02571        int dotIdx = file.findRev('.');
02572        file = file.insert( (dotIdx>=0) ? dotIdx : file.length(), QString("_") + QString::number(num) );
02573      }
02574      curUrl.setFileName(file);
02575 
02576      // Increment the counter for both the old and the new filename
02577      if ( !renameNumbering.contains(origFile))
02578          renameNumbering[origFile] = 1;
02579      else
02580          renameNumbering[origFile]++;
02581 
02582      if ( file != origFile ) {
02583         if ( !renameNumbering.contains(file))
02584             renameNumbering[file] = 1;
02585         else
02586             renameNumbering[file]++;
02587      }
02588 
02589 
02590       if ( KIO::NetAccess::exists( curUrl, false, parentWidget() ) ) {
02591         if ( KMessageBox::warningContinueCancel( parentWidget(),
02592               i18n( "A file named %1 already exists. Do you want to overwrite it?" )
02593               .arg( curUrl.fileName() ),
02594               i18n( "File Already Exists" ), i18n("&Overwrite") ) == KMessageBox::Cancel) {
02595           continue;
02596         }
02597       }
02598       // save
02599       const Result result = saveItem( it.key(), curUrl );
02600       if ( result != OK )
02601         globalResult = result;
02602     }
02603   }
02604   setResult( globalResult );
02605   emit completed( this );
02606   deleteLater();
02607 }
02608 
02609 KMCommand::Result KMSaveAttachmentsCommand::saveItem( partNode *node,
02610                                                       const KURL& url )
02611 {
02612   bool bSaveEncrypted = false;
02613   bool bEncryptedParts = node->encryptionState() != KMMsgNotEncrypted;
02614   if( bEncryptedParts )
02615     if( KMessageBox::questionYesNo( parentWidget(),
02616           i18n( "The part %1 of the message is encrypted. Do you want to keep the encryption when saving?" ).
02617           arg( url.fileName() ),
02618           i18n( "KMail Question" ), i18n("Keep Encryption"), i18n("Do Not Keep") ) ==
02619         KMessageBox::Yes )
02620       bSaveEncrypted = true;
02621 
02622   bool bSaveWithSig = true;
02623   if( node->signatureState() != KMMsgNotSigned )
02624     if( KMessageBox::questionYesNo( parentWidget(),
02625           i18n( "The part %1 of the message is signed. Do you want to keep the signature when saving?" ).
02626           arg( url.fileName() ),
02627           i18n( "KMail Question" ), i18n("Keep Signature"), i18n("Do Not Keep") ) !=
02628         KMessageBox::Yes )
02629       bSaveWithSig = false;
02630 
02631   QByteArray data;
02632   if ( mEncoded )
02633   {
02634     // This does not decode the Message Content-Transfer-Encoding
02635     // but saves the _original_ content of the message part
02636     data = KMail::Util::ByteArray( node->msgPart().dwBody() );
02637   }
02638   else
02639   {
02640     if( bSaveEncrypted || !bEncryptedParts) {
02641       partNode *dataNode = node;
02642       QCString rawReplyString;
02643       bool gotRawReplyString = false;
02644       if( !bSaveWithSig ) {
02645         if( DwMime::kTypeMultipart == node->type() &&
02646             DwMime::kSubtypeSigned == node->subType() ){
02647           // carefully look for the part that is *not* the signature part:
02648           if( node->findType( DwMime::kTypeApplication,
02649                 DwMime::kSubtypePgpSignature,
02650                 true, false ) ){
02651             dataNode = node->findTypeNot( DwMime::kTypeApplication,
02652                 DwMime::kSubtypePgpSignature,
02653                 true, false );
02654           }else if( node->findType( DwMime::kTypeApplication,
02655                 DwMime::kSubtypePkcs7Mime,
02656                 true, false ) ){
02657             dataNode = node->findTypeNot( DwMime::kTypeApplication,
02658                 DwMime::kSubtypePkcs7Mime,
02659                 true, false );
02660           }else{
02661             dataNode = node->findTypeNot( DwMime::kTypeMultipart,
02662                 DwMime::kSubtypeUnknown,
02663                 true, false );
02664           }
02665     }else{
02666       ObjectTreeParser otp( 0, 0, false, false, false );
02667 
02668       // process this node and all it's siblings and descendants
02669       dataNode->setProcessed( false, true );
02670       otp.parseObjectTree( dataNode );
02671 
02672       rawReplyString = otp.rawReplyString();
02673       gotRawReplyString = true;
02674         }
02675       }
02676       QByteArray cstr = gotRawReplyString
02677                          ? rawReplyString
02678                          : dataNode->msgPart().bodyDecodedBinary();
02679       data = cstr;
02680       size_t size = cstr.size();
02681       if ( dataNode->msgPart().type() == DwMime::kTypeText ) {
02682         // convert CRLF to LF before writing text attachments to disk
02683         size = KMail::Util::crlf2lf( cstr.data(), size );
02684       }
02685       data.resize( size );
02686     }
02687   }
02688   QDataStream ds;
02689   QFile file;
02690   KTempFile tf;
02691   tf.setAutoDelete( true );
02692   if ( url.isLocalFile() )
02693   {
02694     // save directly
02695     file.setName( url.path() );
02696     if ( !file.open( IO_WriteOnly ) )
02697     {
02698       KMessageBox::error( parentWidget(),
02699           i18n( "%2 is detailed error description",
02700             "Could not write the file %1:\n%2" )
02701           .arg( file.name() )
02702           .arg( QString::fromLocal8Bit( strerror( errno ) ) ),
02703           i18n( "KMail Error" ) );
02704       return Failed;
02705     }
02706 
02707     // #79685 by default use the umask the user defined, but let it be configurable
02708     if ( GlobalSettings::self()->disregardUmask() )
02709       fchmod( file.handle(), S_IRUSR | S_IWUSR );
02710 
02711     ds.setDevice( &file );
02712   } else
02713   {
02714     // tmp file for upload
02715     ds.setDevice( tf.file() );
02716   }
02717 
02718   ds.writeRawBytes( data.data(), data.size() );
02719   if ( !url.isLocalFile() )
02720   {
02721     tf.close();
02722     if ( !KIO::NetAccess::upload( tf.name(), url, parentWidget() ) )
02723     {
02724       KMessageBox::error( parentWidget(),
02725           i18n( "Could not write the file %1." )
02726           .arg( url.path() ),
02727           i18n( "KMail Error" ) );
02728       return Failed;
02729     }
02730   } else
02731     file.close();
02732   return OK;
02733 }
02734 
02735 KMLoadPartsCommand::KMLoadPartsCommand( QPtrList<partNode>& parts, KMMessage *msg )
02736   : mNeedsRetrieval( 0 )
02737 {
02738   for ( QPtrListIterator<partNode> it( parts ); it.current(); ++it ) {
02739     mPartMap.insert( it.current(), msg );
02740   }
02741 }
02742 
02743 KMLoadPartsCommand::KMLoadPartsCommand( partNode *node, KMMessage *msg )
02744   : mNeedsRetrieval( 0 )
02745 {
02746   mPartMap.insert( node, msg );
02747 }
02748 
02749 KMLoadPartsCommand::KMLoadPartsCommand( PartNodeMessageMap& partMap )
02750   : mNeedsRetrieval( 0 ), mPartMap( partMap )
02751 {
02752 }
02753 
02754 void KMLoadPartsCommand::slotStart()
02755 {
02756   for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
02757         it != mPartMap.end();
02758         ++it ) {
02759     if ( !it.key()->msgPart().isComplete() &&
02760          !it.key()->msgPart().partSpecifier().isEmpty() ) {
02761       // incomplete part, so retrieve it first
02762       ++mNeedsRetrieval;
02763       KMFolder* curFolder = it.data()->parent();
02764       if ( curFolder ) {
02765         FolderJob *job =
02766           curFolder->createJob( it.data(), FolderJob::tGetMessage,
02767                                 0, it.key()->msgPart().partSpecifier() );
02768         job->setCancellable( false );
02769         connect( job, SIGNAL(messageUpdated(KMMessage*, QString)),
02770                  this, SLOT(slotPartRetrieved(KMMessage*, QString)) );
02771         job->start();
02772       } else
02773         kdWarning(5006) << "KMLoadPartsCommand - msg has no parent" << endl;
02774     }
02775   }
02776   if ( mNeedsRetrieval == 0 )
02777     execute();
02778 }
02779 
02780 void KMLoadPartsCommand::slotPartRetrieved( KMMessage *msg,
02781                                             QString partSpecifier )
02782 {
02783   DwBodyPart *part =
02784     msg->findDwBodyPart( msg->getFirstDwBodyPart(), partSpecifier );
02785   if ( part ) {
02786     // update the DwBodyPart in the partNode
02787     for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
02788           it != mPartMap.end();
02789           ++it ) {
02790       if ( it.key()->dwPart()->partId() == part->partId() )
02791         it.key()->setDwPart( part );
02792     }
02793   } else
02794     kdWarning(5006) << "KMLoadPartsCommand::slotPartRetrieved - could not find bodypart!" << endl;
02795   --mNeedsRetrieval;
02796   if ( mNeedsRetrieval == 0 )
02797     execute();
02798 }
02799 
02800 KMCommand::Result KMLoadPartsCommand::execute()
02801 {
02802   emit partsRetrieved();
02803   setResult( OK );
02804   emit completed( this );
02805   deleteLater();
02806   return OK;
02807 }
02808 
02809 KMResendMessageCommand::KMResendMessageCommand( QWidget *parent,
02810    KMMessage *msg )
02811   :KMCommand( parent, msg )
02812 {
02813 }
02814 
02815 KMCommand::Result KMResendMessageCommand::execute()
02816 {
02817   KMMessage *msg = retrievedMessage();
02818   if ( !msg || !msg->codec() ) {
02819     return Failed;
02820   }
02821   KMMessage *newMsg = new KMMessage(*msg);
02822   newMsg->setCharset(msg->codec()->mimeName());
02823   // the message needs a new Message-Id
02824   newMsg->removeHeaderField( "Message-Id" );
02825   newMsg->setParent( 0 );
02826 
02827   // adds the new date to the message
02828   newMsg->removeHeaderField( "Date" );
02829 
02830   KMail::Composer * win = KMail::makeComposer();
02831   win->setMsg(newMsg, false, true);
02832   win->show();
02833 
02834   return OK;
02835 }
02836 
02837 KMMailingListCommand::KMMailingListCommand( QWidget *parent, KMFolder *folder )
02838   : KMCommand( parent ), mFolder( folder )
02839 {
02840 }
02841 
02842 KMCommand::Result KMMailingListCommand::execute()
02843 {
02844   KURL::List lst = urls();
02845   QString handler = ( mFolder->mailingList().handler() == MailingList::KMail )
02846     ? "mailto" : "https";
02847 
02848   KMCommand *command = 0;
02849   for ( KURL::List::Iterator itr = lst.begin(); itr != lst.end(); ++itr ) {
02850     if ( handler == (*itr).protocol() ) {
02851       command = new KMUrlClickedCommand( *itr, mFolder->identity(), 0, false );
02852     }
02853   }
02854   if ( !command && !lst.empty() ) {
02855     command =
02856       new KMUrlClickedCommand( lst.first(), mFolder->identity(), 0, false );
02857   }
02858   if ( command ) {
02859     connect( command, SIGNAL( completed( KMCommand * ) ),
02860              this, SLOT( commandCompleted( KMCommand * ) ) );
02861     setDeletesItself( true );
02862     setEmitsCompletedItself( true );
02863     command->start();
02864     return OK;
02865   }
02866   return Failed;
02867 }
02868 
02869 void KMMailingListCommand::commandCompleted( KMCommand *command )
02870 {
02871   setResult( command->result() );
02872   emit completed( this );
02873   deleteLater();
02874 }
02875 
02876 KMMailingListPostCommand::KMMailingListPostCommand( QWidget *parent, KMFolder *folder )
02877   : KMMailingListCommand( parent, folder )
02878 {
02879 }
02880 KURL::List KMMailingListPostCommand::urls() const
02881 {
02882   return mFolder->mailingList().postURLS();
02883 }
02884 
02885 KMMailingListSubscribeCommand::KMMailingListSubscribeCommand( QWidget *parent, KMFolder *folder )
02886   : KMMailingListCommand( parent, folder )
02887 {
02888 }
02889 KURL::List KMMailingListSubscribeCommand::urls() const
02890 {
02891   return mFolder->mailingList().subscribeURLS();
02892 }
02893 
02894 KMMailingListUnsubscribeCommand::KMMailingListUnsubscribeCommand( QWidget *parent, KMFolder *folder )
02895   : KMMailingListCommand( parent, folder )
02896 {
02897 }
02898 KURL::List KMMailingListUnsubscribeCommand::urls() const
02899 {
02900   return mFolder->mailingList().unsubscribeURLS();
02901 }
02902 
02903 KMMailingListArchivesCommand::KMMailingListArchivesCommand( QWidget *parent, KMFolder *folder )
02904   : KMMailingListCommand( parent, folder )
02905 {
02906 }
02907 KURL::List KMMailingListArchivesCommand::urls() const
02908 {
02909   return mFolder->mailingList().archiveURLS();
02910 }
02911 
02912 KMMailingListHelpCommand::KMMailingListHelpCommand( QWidget *parent, KMFolder *folder )
02913   : KMMailingListCommand( parent, folder )
02914 {
02915 }
02916 KURL::List KMMailingListHelpCommand::urls() const
02917 {
02918   return mFolder->mailingList().helpURLS();
02919 }
02920 
02921 KMIMChatCommand::KMIMChatCommand( const KURL &url, KMMessage *msg )
02922   :mUrl( url ), mMessage( msg )
02923 {
02924 }
02925 
02926 KMCommand::Result KMIMChatCommand::execute()
02927 {
02928   kdDebug( 5006 ) << k_funcinfo << " URL is: " << mUrl << endl;
02929   QString addr = KMMessage::decodeMailtoUrl( mUrl.path() );
02930   // find UID for mail address
02931   KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
02932   KABC::AddresseeList addressees = addressBook->findByEmail( KPIM::getEmailAddress( addr ) ) ;
02933 
02934   // start chat
02935   if( addressees.count() == 1 ) {
02936     kmkernel->imProxy()->chatWithContact( addressees[0].uid() );
02937     return OK;
02938   }
02939   else
02940   {
02941     kdDebug( 5006 ) << "Didn't find exactly one addressee, couldn't tell who to chat to for that email address.  Count = " << addressees.count() << endl;
02942 
02943     QString apology;
02944     if ( addressees.isEmpty() )
02945       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." );
02946     else
02947     {
02948       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." );
02949       QStringList nameList;
02950       KABC::AddresseeList::const_iterator it = addressees.begin();
02951       KABC::AddresseeList::const_iterator end = addressees.end();
02952       for ( ; it != end; ++it )
02953       {
02954           nameList.append( (*it).realName() );
02955       }
02956       QString names = nameList.join( QString::fromLatin1( ",\n" ) );
02957       apology = apology.arg( names );
02958     }
02959 
02960     KMessageBox::sorry( parentWidget(), apology );
02961     return Failed;
02962   }
02963 }
02964 
02965 KMHandleAttachmentCommand::KMHandleAttachmentCommand( partNode* node,
02966      KMMessage* msg, int atmId, const QString& atmName,
02967      AttachmentAction action, KService::Ptr offer, QWidget* parent )
02968 : KMCommand( parent ), mNode( node ), mMsg( msg ), mAtmId( atmId ), mAtmName( atmName ),
02969   mAction( action ), mOffer( offer ), mJob( 0 )
02970 {
02971 }
02972 
02973 void KMHandleAttachmentCommand::slotStart()
02974 {
02975   if ( !mNode->msgPart().isComplete() )
02976   {
02977     // load the part
02978     kdDebug(5006) << "load part" << endl;
02979     KMLoadPartsCommand *command = new KMLoadPartsCommand( mNode, mMsg );
02980     connect( command, SIGNAL( partsRetrieved() ),
02981         this, SLOT( slotPartComplete() ) );
02982     command->start();
02983   } else
02984   {
02985     execute();
02986   }
02987 }
02988 
02989 void KMHandleAttachmentCommand::slotPartComplete()
02990 {
02991   execute();
02992 }
02993 
02994 KMCommand::Result KMHandleAttachmentCommand::execute()
02995 {
02996   switch( mAction )
02997   {
02998     case Open:
02999       atmOpen();
03000       break;
03001     case OpenWith:
03002       atmOpenWith();
03003       break;
03004     case View:
03005       atmView();
03006       break;
03007     case Save:
03008       atmSave();
03009       break;
03010     case Properties:
03011       atmProperties();
03012       break;
03013     case ChiasmusEncrypt:
03014       atmEncryptWithChiasmus();
03015       return Undefined;
03016       break;
03017     default:
03018       kdDebug(5006) << "unknown action " << mAction << endl;
03019       break;
03020   }
03021   setResult( OK );
03022   emit completed( this );
03023   deleteLater();
03024   return OK;
03025 }
03026 
03027 QString KMHandleAttachmentCommand::createAtmFileLink() const
03028 {
03029   QFileInfo atmFileInfo( mAtmName );
03030 
03031   if ( atmFileInfo.size() == 0 )
03032   {
03033     kdDebug(5006) << k_funcinfo << "rewriting attachment" << endl;
03034     // there is something wrong so write the file again
03035     QByteArray data = mNode->msgPart().bodyDecodedBinary();
03036     size_t size = data.size();
03037     if ( mNode->msgPart().type() == DwMime::kTypeText && size) {
03038       // convert CRLF to LF before writing text attachments to disk
03039       size = KMail::Util::crlf2lf( data.data(), size );
03040     }
03041     KPIM::kBytesToFile( data.data(), size, mAtmName, false, false, false );
03042   }
03043 
03044   KTempFile *linkFile = new KTempFile( locateLocal("tmp", atmFileInfo.fileName() +"_["),
03045                           "]."+ atmFileInfo.extension() );
03046 
03047   linkFile->setAutoDelete(true);
03048   QString linkName = linkFile->name();
03049   delete linkFile;
03050 
03051   if ( ::link(QFile::encodeName( mAtmName ), QFile::encodeName( linkName )) == 0 ) {
03052     return linkName; // success
03053   }
03054   return QString::null;
03055 }
03056 
03057 KService::Ptr KMHandleAttachmentCommand::getServiceOffer()
03058 {
03059   KMMessagePart& msgPart = mNode->msgPart();
03060   const QString contentTypeStr =
03061     ( msgPart.typeStr() + '/' + msgPart.subtypeStr() ).lower();
03062 
03063   if ( contentTypeStr == "text/x-vcard" ) {
03064     atmView();
03065     return 0;
03066   }
03067   // determine the MIME type of the attachment
03068   KMimeType::Ptr mimetype;
03069   // prefer the value of the Content-Type header
03070   mimetype = KMimeType::mimeType( contentTypeStr );
03071   if ( mimetype->name() == "application/octet-stream" ) {
03072     // consider the filename if Content-Type is application/octet-stream
03073     mimetype = KMimeType::findByPath( mAtmName, 0, true /* no disk access */ );
03074   }
03075   if ( ( mimetype->name() == "application/octet-stream" )
03076        && msgPart.isComplete() ) {
03077     // consider the attachment's contents if neither the Content-Type header
03078     // nor the filename give us a clue
03079     mimetype = KMimeType::findByFileContent( mAtmName );
03080   }
03081   return KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
03082 }
03083 
03084 void KMHandleAttachmentCommand::atmOpen()
03085 {
03086   if ( !mOffer )
03087     mOffer = getServiceOffer();
03088   if ( !mOffer ) {
03089     kdDebug(5006) << k_funcinfo << "got no offer" << endl;
03090     return;
03091   }
03092 
03093   KURL::List lst;
03094   KURL url;
03095   bool autoDelete = true;
03096   QString fname = createAtmFileLink();
03097 
03098   if ( fname.isNull() ) {
03099     autoDelete = false;
03100     fname = mAtmName;
03101   }
03102 
03103   url.setPath( fname );
03104   lst.append( url );
03105   if ( (KRun::run( *mOffer, lst, autoDelete ) <= 0) && autoDelete ) {
03106       QFile::remove(url.path());
03107   }
03108 }
03109 
03110 void KMHandleAttachmentCommand::atmOpenWith()
03111 {
03112   KURL::List lst;
03113   KURL url;
03114   bool autoDelete = true;
03115   QString fname = createAtmFileLink();
03116 
03117   if ( fname.isNull() ) {
03118     autoDelete = false;
03119     fname = mAtmName;
03120   }
03121 
03122   url.setPath( fname );
03123   lst.append( url );
03124   if ( (! KRun::displayOpenWithDialog(lst, autoDelete)) && autoDelete ) {
03125     QFile::remove( url.path() );
03126   }
03127 }
03128 
03129 void KMHandleAttachmentCommand::atmView()
03130 {
03131   // we do not handle this ourself
03132   emit showAttachment( mAtmId, mAtmName );
03133 }
03134 
03135 void KMHandleAttachmentCommand::atmSave()
03136 {
03137   QPtrList<partNode> parts;
03138   parts.append( mNode );
03139   // save, do not leave encoded
03140   KMSaveAttachmentsCommand *command =
03141     new KMSaveAttachmentsCommand( 0, parts, mMsg, false );
03142   command->start();
03143 }
03144 
03145 void KMHandleAttachmentCommand::atmProperties()
03146 {
03147   KMMsgPartDialogCompat dlg( parentWidget() , 0, true );
03148   KMMessagePart& msgPart = mNode->msgPart();
03149   dlg.setMsgPart( &msgPart );
03150   dlg.exec();
03151 }
03152 
03153 void KMHandleAttachmentCommand::atmEncryptWithChiasmus()
03154 {
03155   const partNode * node = mNode;
03156   Q_ASSERT( node );
03157   if ( !node )
03158     return;
03159 
03160   // FIXME: better detection of mimetype??
03161   if ( !mAtmName.endsWith( ".xia", false ) )
03162     return;
03163 
03164   const Kleo::CryptoBackend::Protocol * chiasmus =
03165     Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
03166   Q_ASSERT( chiasmus );
03167   if ( !chiasmus )
03168     return;
03169 
03170   const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", QMap<QString,QVariant>() ) );
03171   if ( !listjob.get() ) {
03172     const QString msg = i18n( "Chiasmus backend does not offer the "
03173                               "\"x-obtain-keys\" function. Please report this bug." );
03174     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03175     return;
03176   }
03177 
03178   if ( listjob->exec() ) {
03179     listjob->showErrorDialog( parentWidget(), i18n( "Chiasmus Backend Error" ) );
03180     return;
03181   }
03182 
03183   const QVariant result = listjob->property( "result" );
03184   if ( result.type() != QVariant::StringList ) {
03185     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
03186                               "The \"x-obtain-keys\" function did not return a "
03187                               "string list. Please report this bug." );
03188     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03189     return;
03190   }
03191 
03192   const QStringList keys = result.toStringList();
03193   if ( keys.empty() ) {
03194     const QString msg = i18n( "No keys have been found. Please check that a "
03195                               "valid key path has been set in the Chiasmus "
03196                               "configuration." );
03197     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03198     return;
03199   }
03200 
03201   ChiasmusKeySelector selectorDlg( parentWidget(), i18n( "Chiasmus Decryption Key Selection" ),
03202                                    keys, GlobalSettings::chiasmusDecryptionKey(),
03203                                    GlobalSettings::chiasmusDecryptionOptions() );
03204   if ( selectorDlg.exec() != QDialog::Accepted )
03205     return;
03206 
03207   GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
03208   GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
03209   assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
03210 
03211   Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", QMap<QString,QVariant>() );
03212   if ( !job ) {
03213     const QString msg = i18n( "Chiasmus backend does not offer the "
03214                               "\"x-decrypt\" function. Please report this bug." );
03215     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03216     return;
03217   }
03218 
03219   const QByteArray input = node->msgPart().bodyDecodedBinary();
03220 
03221   if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
03222        !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
03223        !job->setProperty( "input", input ) ) {
03224     const QString msg = i18n( "The \"x-decrypt\" function does not accept "
03225                               "the expected parameters. Please report this bug." );
03226     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03227     return;
03228   }
03229 
03230   setDeletesItself( true ); // the job below is async, we have to cleanup ourselves
03231   if ( job->start() ) {
03232     job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
03233     return;
03234   }
03235 
03236   mJob = job;
03237   connect( job, SIGNAL(result(const GpgME::Error&,const QVariant&)),
03238            this, SLOT(slotAtmDecryptWithChiasmusResult(const GpgME::Error&,const QVariant&)) );
03239 }
03240 
03241 // return true if we should proceed, false if we should abort
03242 static bool checkOverwrite( const KURL& url, bool& overwrite, QWidget* w )
03243 {
03244   if ( KIO::NetAccess::exists( url, false /*dest*/, w ) ) {
03245     if ( KMessageBox::Cancel ==
03246          KMessageBox::warningContinueCancel(
03247                                             w,
03248                                             i18n( "A file named \"%1\" already exists. "
03249                                                   "Are you sure you want to overwrite it?" ).arg( url.prettyURL() ),
03250                                             i18n( "Overwrite File?" ),
03251                                             i18n( "&Overwrite" ) ) )
03252       return false;
03253     overwrite = true;
03254   }
03255   return true;
03256 }
03257 
03258 static const QString chomp( const QString & base, const QString & suffix, bool cs ) {
03259   return base.endsWith( suffix, cs ) ? base.left( base.length() - suffix.length() ) : base ;
03260 }
03261 
03262 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusResult( const GpgME::Error & err, const QVariant & result )
03263 {
03264   LaterDeleterWithCommandCompletion d( this );
03265   if ( !mJob )
03266     return;
03267   Q_ASSERT( mJob == sender() );
03268   if ( mJob != sender() )
03269     return;
03270   Kleo::Job * job = mJob;
03271   mJob = 0;
03272   if ( err.isCanceled() )
03273     return;
03274   if ( err ) {
03275     job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
03276     return;
03277   }
03278 
03279   if ( result.type() != QVariant::ByteArray ) {
03280     const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
03281                               "The \"x-decrypt\" function did not return a "
03282                               "byte array. Please report this bug." );
03283     KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
03284     return;
03285   }
03286 
03287   const KURL url = KFileDialog::getSaveURL( chomp( mAtmName, ".xia", false ), QString::null, parentWidget() );
03288   if ( url.isEmpty() )
03289     return;
03290 
03291   bool overwrite = false;
03292   if ( !checkOverwrite( url, overwrite, parentWidget() ) )
03293     return;
03294 
03295   d.setDisabled( true ); // we got this far, don't delete yet
03296   KIO::Job * uploadJob = KIO::storedPut( result.toByteArray(), url, -1, overwrite, false /*resume*/ );
03297   uploadJob->setWindow( parentWidget() );
03298   connect( uploadJob, SIGNAL(result(KIO::Job*)),
03299            this, SLOT(slotAtmDecryptWithChiasmusUploadResult(KIO::Job*)) );
03300 }
03301 
03302 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusUploadResult( KIO::Job * job )
03303 {
03304   if ( job->error() )
03305     job->showErrorDialog();
03306   LaterDeleterWithCommandCompletion d( this );
03307   d.setResult( OK );
03308 }
03309 
03310 
03311 AttachmentModifyCommand::AttachmentModifyCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03312     KMCommand( parent, msg ),
03313     mPartIndex( node->nodeId() ),
03314     mSernum( 0 )
03315 {
03316 }
03317 
03318 AttachmentModifyCommand::~ AttachmentModifyCommand()
03319 {
03320 }
03321 
03322 KMCommand::Result AttachmentModifyCommand::execute()
03323 {
03324   KMMessage *msg = retrievedMessage();
03325   if ( !msg )
03326     return Failed;
03327   mSernum = msg->getMsgSerNum();
03328 
03329   mFolder = msg->parent();
03330   if ( !mFolder || !mFolder->storage() )
03331     return Failed;
03332 
03333   Result res = doAttachmentModify();
03334   if ( res != OK )
03335     return res;
03336 
03337   setEmitsCompletedItself( true );
03338   setDeletesItself( true );
03339   return OK;
03340 }
03341 
03342 void AttachmentModifyCommand::storeChangedMessage(KMMessage * msg)
03343 {
03344   if ( !mFolder || !mFolder->storage() ) {
03345     kdWarning(5006) << k_funcinfo << "We lost the folder!" << endl;
03346     setResult( Failed );
03347     emit completed( this );
03348     deleteLater();
03349   }
03350   int res = mFolder->addMsg( msg ) != 0;
03351   if ( mFolder->folderType() == KMFolderTypeImap ) {
03352     KMFolderImap *f = static_cast<KMFolderImap*>( mFolder->storage() );
03353     connect( f, SIGNAL(folderComplete(KMFolderImap*,bool)),
03354              SLOT(messageStoreResult(KMFolderImap*,bool)) );
03355   } else {
03356     messageStoreResult( 0, res == 0 );
03357   }
03358 }
03359 
03360 void AttachmentModifyCommand::messageStoreResult(KMFolderImap* folder, bool success )
03361 {
03362   Q_UNUSED( folder );
03363   if ( success ) {
03364     KMCommand *delCmd = new KMDeleteMsgCommand( mSernum );
03365     connect( delCmd, SIGNAL(completed(KMCommand*)), SLOT(messageDeleteResult(KMCommand*)) );
03366     delCmd->start();
03367     return;
03368   }
03369   kdWarning(5006) << k_funcinfo << "Adding modified message failed." << endl;
03370   setResult( Failed );
03371   emit completed( this );
03372   deleteLater();
03373 }
03374 
03375 void AttachmentModifyCommand::messageDeleteResult(KMCommand * cmd)
03376 {
03377   setResult( cmd->result() );
03378   emit completed( this );
03379   deleteLater();
03380 }
03381 
03382 
03383 KMDeleteAttachmentCommand::KMDeleteAttachmentCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03384     AttachmentModifyCommand( node, msg, parent )
03385 {
03386   kdDebug(5006) << k_funcinfo << endl;
03387 }
03388 
03389 KMDeleteAttachmentCommand::~KMDeleteAttachmentCommand()
03390 {
03391   kdDebug(5006) << k_funcinfo << endl;
03392 }
03393 
03394 KMCommand::Result KMDeleteAttachmentCommand::doAttachmentModify()
03395 {
03396   KMMessage *msg = retrievedMessage();
03397   KMMessagePart part;
03398   // -2 because partNode counts root and body of the message as well
03399   DwBodyPart *dwpart = msg->dwBodyPart( mPartIndex - 2 );
03400   if ( !dwpart )
03401     return Failed;
03402   KMMessage::bodyPart( dwpart, &part, true );
03403   if ( !part.isComplete() )
03404      return Failed;
03405   msg->removeBodyPart( dwpart );
03406 
03407   // add dummy part to show that a attachment has been deleted
03408   KMMessagePart dummyPart;
03409   dummyPart.duplicate( part );
03410   QString comment = i18n("This attachment has been deleted.");
03411   if ( !part.fileName().isEmpty() )
03412     comment = i18n( "The attachment '%1' has been deleted." ).arg( part.fileName() );
03413   dummyPart.setContentDescription( comment );
03414   dummyPart.setBodyEncodedBinary( QByteArray() );
03415   QCString cd = dummyPart.contentDisposition();
03416   if ( cd.find( "inline", 0, false ) == 0 ) {
03417     cd.replace( 0, 10, "attachment" );
03418     dummyPart.setContentDisposition( cd );
03419   } else if ( cd.isEmpty() ) {
03420     dummyPart.setContentDisposition( "attachment" );
03421   }
03422   msg->addBodyPart( &dummyPart );
03423 
03424   KMMessage *newMsg = new KMMessage();
03425   newMsg->fromDwString( msg->asDwString() );
03426   newMsg->setStatus( msg->status() );
03427 
03428   storeChangedMessage( newMsg );
03429   return OK;
03430 }
03431 
03432 
03433 KMEditAttachmentCommand::KMEditAttachmentCommand(partNode * node, KMMessage * msg, QWidget * parent) :
03434     AttachmentModifyCommand( node, msg, parent )
03435 {
03436   kdDebug(5006) << k_funcinfo << endl;
03437   mTempFile.setAutoDelete( true );
03438 }
03439 
03440 KMEditAttachmentCommand::~ KMEditAttachmentCommand()
03441 {
03442 }
03443 
03444 KMCommand::Result KMEditAttachmentCommand::doAttachmentModify()
03445 {
03446   KMMessage *msg = retrievedMessage();
03447   KMMessagePart part;
03448   // -2 because partNode counts root and body of the message as well
03449   DwBodyPart *dwpart = msg->dwBodyPart( mPartIndex - 2 );
03450   if ( !dwpart )
03451     return Failed;
03452   KMMessage::bodyPart( dwpart, &part, true );
03453   if ( !part.isComplete() )
03454      return Failed;
03455 
03456   mTempFile.file()->writeBlock( part.bodyDecodedBinary() );
03457   mTempFile.file()->flush();
03458 
03459   KMail::EditorWatcher *watcher = new KMail::EditorWatcher( KURL(mTempFile.file()->name()), part.typeStr() + "/" + part.subtypeStr(), false, this );
03460   connect( watcher, SIGNAL(editDone(KMail::EditorWatcher*)), SLOT(editDone(KMail::EditorWatcher*)) );
03461   if ( !watcher->start() )
03462     return Failed;
03463   setEmitsCompletedItself( true );
03464   setDeletesItself( true );
03465   return OK;
03466 }
03467 
03468 void KMEditAttachmentCommand::editDone(KMail::EditorWatcher * watcher)
03469 {
03470   kdDebug(5006) << k_funcinfo << endl;
03471   // anything changed?
03472   if ( !watcher->fileChanged() ) {
03473     kdDebug(5006) << k_funcinfo << "File has not been changed" << endl;
03474     setResult( Canceled );
03475     emit completed( this );
03476     deleteLater();
03477   }
03478 
03479   mTempFile.file()->reset();
03480   QByteArray data = mTempFile.file()->readAll();
03481 
03482   // build the new message
03483   KMMessage *msg = retrievedMessage();
03484   KMMessagePart part;
03485   // -2 because partNode counts root and body of the message as well
03486   DwBodyPart *dwpart = msg->dwBodyPart( mPartIndex - 2 );
03487   KMMessage::bodyPart( dwpart, &part, true );
03488   msg->removeBodyPart( dwpart );
03489 
03490   KMMessagePart att;
03491   att.duplicate( part );
03492   att.setBodyEncodedBinary( data );
03493   msg->addBodyPart( &att );
03494 
03495   KMMessage *newMsg = new KMMessage();
03496   newMsg->fromDwString( msg->asDwString() );
03497   newMsg->setStatus( msg->status() );
03498 
03499   storeChangedMessage( newMsg );
03500 }
03501 
03502 
03503 CreateTodoCommand::CreateTodoCommand(QWidget * parent, KMMessage * msg)
03504   : KMCommand( parent, msg )
03505 {
03506 }
03507 
03508 KMCommand::Result CreateTodoCommand::execute()
03509 {
03510   KMMessage *msg = retrievedMessage();
03511   if ( !msg || !msg->codec() ) {
03512     return Failed;
03513   }
03514 
03515   // korganizer starting code taken from the ical bpf plugin
03516   QString error;
03517   QCString dcopService;
03518   int result = KDCOPServiceStarter::self()->findServiceFor( "DCOP/Organizer",
03519                                          QString::null, QString::null, &error, &dcopService );
03520   if ( result == 0 ) {
03521     // OK, so korganizer (or kontact) is running. Now ensure the object we want is available
03522     // [that's not the case when kontact was already running, but korganizer not loaded into it...]
03523     static const char* const dcopObjectId = "KOrganizerIface";
03524     QCString dummy;
03525     if ( !kapp->dcopClient()->findObject( dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) ) {
03526       DCOPRef ref( dcopService, dcopService ); // talk to the KUniqueApplication or its kontact wrapper
03527       DCOPReply reply = ref.call( "load()" );
03528       if ( reply.isValid() && (bool)reply ) {
03529         kdDebug() << "Loaded " << dcopService << " successfully" << endl;
03530         Q_ASSERT( kapp->dcopClient()->findObject( dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) );
03531       } else
03532         kdWarning() << "Error loading " << dcopService << endl;
03533     }
03534   }
03535 
03536   QString txt = i18n("From: %1\nTo: %2\nSubject: %3").arg( msg->from() )
03537                 .arg( msg->to() ).arg( msg->subject() );
03538 
03539   KTempFile tf;
03540   tf.setAutoDelete( true );
03541   QString uri = "kmail:" + QString::number( msg->getMsgSerNum() ) + "/" + msg->msgId();
03542   tf.file()->writeBlock( msg->asDwString().c_str(), msg->asDwString().length() );
03543   tf.close();
03544 
03545   KCalendarIface_stub *iface = new KCalendarIface_stub( kapp->dcopClient(), "korganizer", "CalendarIface" );
03546   iface->openTodoEditor( i18n("Mail: %1").arg( msg->subject() ), txt,
03547                          uri, tf.name(), QStringList(), "message/rfc822" );
03548   delete iface;
03549 
03550   return OK;
03551 }
03552 
03553 #include "kmcommands.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys