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