kmail Library API Documentation

kmcommands.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*-
00002 // kmcommands
00003 // (c) 2002 Don Sanders <sanders@kde.org>
00004 // License: GPL
00005 //
00006 // This file implements various "command" classes. These command classes
00007 // are based on the command design pattern.
00008 //
00009 // Historically various operations were implemented as slots of KMMainWin.
00010 // This proved inadequate as KMail has multiple top level windows
00011 // (KMMainWin, KMReaderMainWin, KMFldSearch, KMComposeWin) that may
00012 // benefit from using these operations. It is desirable that these
00013 // classes can operate without depending on or altering the state of
00014 // a KMMainWin, in fact it is possible no KMMainWin object even exists.
00015 //
00016 // Now these operations have been rewritten as KMCommand based classes,
00017 // making them independent of KMMainWin.
00018 //
00019 // The base command class KMCommand is async, which is a difference
00020 // from the conventional command pattern. As normal derived classes implement
00021 // the execute method, but client classes call start() instead of
00022 // calling execute() directly. start() initiates async operations,
00023 // and on completion of these operations calls execute() and then deletes
00024 // the command. (So the client must not construct commands on the stack).
00025 //
00026 // The type of async operation supported by KMCommand is retrieval
00027 // of messages from an IMAP server.
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include <errno.h>
00033 #include <mimelib/enum.h>
00034 #include <mimelib/field.h>
00035 #include <mimelib/mimepp.h>
00036 
00037 #include <qtextcodec.h>
00038 
00039 #include <kdebug.h>
00040 #include <kfiledialog.h>
00041 #include <kio/netaccess.h>
00042 #include <klocale.h>
00043 #include <kmessagebox.h>
00044 #include <kparts/browserextension.h>
00045 #include <kprogress.h>
00046 #include <krun.h>
00047 #include <kbookmarkmanager.h>
00048 #include <kstandarddirs.h>
00049 #include "actionscheduler.h"
00050 using KMail::ActionScheduler;
00051 #include "mailinglist-magic.h"
00052 #include "kmaddrbook.h"
00053 #include "kmcomposewin.h"
00054 #include "kmfiltermgr.h"
00055 #include "kmfolderimap.h"
00056 #include "kmfoldermgr.h"
00057 #include "kmheaders.h"
00058 #include "kmmainwidget.h"
00059 #include "kmmsgdict.h"
00060 #include "kmsender.h"
00061 #include "undostack.h"
00062 #include "partNode.h"
00063 #include "kcursorsaver.h"
00064 #include "partNode.h"
00065 using KMail::FolderJob;
00066 #include "mailsourceviewer.h"
00067 using KMail::MailSourceViewer;
00068 
00069 #include "kmcommands.h"
00070 #include "kmcommands.moc"
00071 
00072 KMCommand::KMCommand( QWidget *parent )
00073   : mProgressDialog( 0 ), mDeletesItself( false ), mParent( parent )
00074 {
00075 }
00076 
00077 KMCommand::KMCommand( QWidget *parent, const QPtrList<KMMsgBase> &msgList )
00078   : mProgressDialog( 0 ), mDeletesItself( false ), mParent( parent ), mMsgList( msgList )
00079 {
00080 }
00081 
00082 KMCommand::KMCommand( QWidget *parent, KMMsgBase *msgBase )
00083   : mProgressDialog( 0 ), mDeletesItself( false ), mParent( parent )
00084 {
00085   mMsgList.append( msgBase );
00086 }
00087 
00088 KMCommand::KMCommand( QWidget *parent, KMMessage *msg )
00089   : mProgressDialog( 0 ), mDeletesItself( false ), mParent( parent )
00090 {
00091   mMsgList.append( &msg->toMsgBase() );
00092 }
00093 
00094 KMCommand::~KMCommand()
00095 {
00096   QValueListIterator<QGuardedPtr<KMFolder> > fit;
00097   for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) {
00098     if (!(*fit))
00099       continue;
00100     (*fit)->close();
00101   }
00102 }
00103 
00104 void KMCommand::start()
00105 {
00106   preTransfer();
00107 }
00108 
00109 
00110 const QPtrList<KMMessage> KMCommand::retrievedMsgs() const
00111 {
00112   return mRetrievedMsgs;
00113 }
00114 
00115 KMMessage *KMCommand::retrievedMessage() const
00116 {
00117   return mRetrievedMsgs.getFirst();
00118 }
00119 
00120 int KMCommand::mCountJobs = 0;
00121 
00122 void KMCommand::preTransfer()
00123 {
00124   connect(this, SIGNAL(messagesTransfered(bool)),
00125           this, SLOT(slotPostTransfer(bool)));
00126   kmkernel->filterMgr()->ref();
00127 
00128   if (mMsgList.find(0) != -1) {
00129       emit messagesTransfered(false);
00130       return;
00131   }
00132 
00133   if ((mMsgList.count() == 1) &&
00134       (mMsgList.getFirst()->isMessage()) &&
00135       (mMsgList.getFirst()->parent() == 0))
00136   {
00137     // Special case of operating on message that isn't in a folder
00138     mRetrievedMsgs.append((KMMessage*)mMsgList.getFirst());
00139     emit messagesTransfered(true);
00140     return;
00141   }
00142 
00143   for (KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next())
00144     if (!mb->parent()) {
00145       emit messagesTransfered(false);
00146       return;
00147     } else {
00148       mFolders.append( mb->parent() );
00149       mb->parent()->open();
00150     }
00151 
00152   // transfer the selected messages first
00153   transferSelectedMsgs();
00154 }
00155 
00156 void KMCommand::slotPostTransfer(bool success)
00157 {
00158   disconnect(this, SIGNAL(messagesTransfered(bool)),
00159              this, SLOT(slotPostTransfer(bool)));
00160   if (success)
00161     execute();
00162   QPtrListIterator<KMMessage> it( mRetrievedMsgs );
00163   KMMessage* msg;
00164   while ( (msg = it.current()) != 0 )
00165   {
00166     ++it;
00167     if (msg->parent())
00168       msg->setTransferInProgress(false);
00169   }
00170   kmkernel->filterMgr()->deref();
00171   if ( !deletesItself() )
00172     delete this;
00173 }
00174 
00175 void KMCommand::transferSelectedMsgs()
00176 {
00177   // make sure no other transfer is active
00178   if (KMCommand::mCountJobs > 0) {
00179     emit messagesTransfered(false);
00180     return;
00181   }
00182 
00183   bool complete = true;
00184   KMCommand::mCountJobs = 0;
00185   mCountMsgs = 0;
00186   mRetrievedMsgs.clear();
00187   mCountMsgs = mMsgList.count();
00188   // the KProgressDialog for the user-feedback. Only enable it if it's needed.
00189   // For some commands like KMSetStatusCommand it's not needed. Note, that
00190   // for some reason the KProgressDialog eats the MouseReleaseEvent (if a
00191   // command is executed after the MousePressEvent), cf. bug #71761.
00192   if ( mCountMsgs > 0 ) {
00193     mProgressDialog = new KProgressDialog(mParent, "transferProgress",
00194       i18n("Please wait"),
00195       i18n("Please wait while the message is transferred",
00196         "Please wait while the %n messages are transferred", mMsgList.count()),
00197       true);
00198     mProgressDialog->setMinimumDuration(1000);
00199   }
00200   for (KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next())
00201   {
00202     // check if all messages are complete
00203     KMMessage *thisMsg = 0;
00204     if ( mb->isMessage() )
00205       thisMsg = static_cast<KMMessage*>(mb);
00206     else
00207     {
00208       KMFolder *folder = mb->parent();
00209       int idx = folder->find(mb);
00210       if (idx < 0) continue;
00211       thisMsg = folder->getMsg(idx);
00212     }
00213     if (!thisMsg) continue;
00214     if ( thisMsg->transferInProgress() &&
00215          thisMsg->parent()->folderType() == KMFolderTypeImap )
00216     {
00217       thisMsg->setTransferInProgress( false, true );
00218       thisMsg->parent()->ignoreJobsForMessage( thisMsg );
00219     }
00220 
00221     if ( thisMsg->parent() && !thisMsg->isComplete() &&
00222          ( !mProgressDialog || !mProgressDialog->wasCancelled() ) )
00223     {
00224       kdDebug(5006)<<"### INCOMPLETE with protocol = "<<thisMsg->parent()->protocol() <<endl;
00225       // the message needs to be transferred first
00226       complete = false;
00227       KMCommand::mCountJobs++;
00228       FolderJob *job = thisMsg->parent()->createJob(thisMsg);
00229       // emitted when the message was transferred successfully
00230       connect(job, SIGNAL(messageRetrieved(KMMessage*)),
00231               this, SLOT(slotMsgTransfered(KMMessage*)));
00232       // emitted when the job is destroyed
00233       connect(job, SIGNAL(finished()),
00234               this, SLOT(slotJobFinished()));
00235       // msg musn't be deleted
00236       thisMsg->setTransferInProgress(true);
00237       job->start();
00238     } else {
00239       thisMsg->setTransferInProgress(true);
00240       mRetrievedMsgs.append(thisMsg);
00241     }
00242   }
00243 
00244   if (complete)
00245   {
00246     delete mProgressDialog;
00247     emit messagesTransfered(true);
00248   } else {
00249     // wait for the transfer and tell the progressBar the necessary steps
00250     if ( mProgressDialog ) {
00251       connect(mProgressDialog, SIGNAL(cancelClicked()),
00252               this, SLOT(slotTransferCancelled()));
00253       mProgressDialog->progressBar()->setTotalSteps(KMCommand::mCountJobs);
00254     }
00255   }
00256 }
00257 
00258 void KMCommand::slotMsgTransfered(KMMessage* msg)
00259 {
00260   if ( mProgressDialog && mProgressDialog->wasCancelled() ) {
00261     emit messagesTransfered(false);
00262     return;
00263   }
00264 
00265   // save the complete messages
00266   mRetrievedMsgs.append(msg);
00267 }
00268 
00269 void KMCommand::slotJobFinished()
00270 {
00271   // the job is finished (with / without error)
00272   KMCommand::mCountJobs--;
00273 
00274   if ( mProgressDialog && mProgressDialog->wasCancelled() ) return;
00275 
00276   if ( (mCountMsgs - static_cast<int>(mRetrievedMsgs.count())) > KMCommand::mCountJobs )
00277   {
00278     // the message wasn't retrieved before => error
00279     if ( mProgressDialog )
00280       mProgressDialog->hide();
00281     slotTransferCancelled();
00282     return;
00283   }
00284   // update the progressbar
00285   if ( mProgressDialog ) {
00286     mProgressDialog->progressBar()->advance(1);
00287     mProgressDialog->setLabel(i18n("Please wait while the message is transferred",
00288           "Please wait while the %n messages are transferred", KMCommand::mCountJobs));
00289   }
00290   if (KMCommand::mCountJobs == 0)
00291   {
00292     // all done
00293     delete mProgressDialog;
00294     emit messagesTransfered(true);
00295   }
00296 }
00297 
00298 void KMCommand::slotTransferCancelled()
00299 {
00300   // kill the pending jobs
00301   QValueListIterator<QGuardedPtr<KMFolder> > fit;
00302   for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) {
00303     if (!(*fit))
00304       continue;
00305     KMFolder *folder = *fit;
00306     KMFolderImap *imapFolder = dynamic_cast<KMFolderImap*>(folder);
00307     if (imapFolder && imapFolder->account()) {
00308       imapFolder->account()->killAllJobs();
00309       imapFolder->account()->setIdle(true);
00310     }
00311   }
00312 
00313   KMCommand::mCountJobs = 0;
00314   mCountMsgs = 0;
00315   // unget the transfered messages
00316   QPtrListIterator<KMMessage> it( mRetrievedMsgs );
00317   KMMessage* msg;
00318   while ( (msg = it.current()) != 0 )
00319   {
00320     KMFolder *folder = msg->parent();
00321     ++it;
00322     if (!folder)
00323       continue;
00324     msg->setTransferInProgress(false);
00325     int idx = folder->find(msg);
00326     if (idx > 0) folder->unGetMsg(idx);
00327   }
00328   mRetrievedMsgs.clear();
00329   emit messagesTransfered(false);
00330 }
00331 
00332 KMMailtoComposeCommand::KMMailtoComposeCommand( const KURL &url,
00333                                                 KMMessage *msg )
00334   :mUrl( url ), mMessage( msg )
00335 {
00336 }
00337 
00338 void KMMailtoComposeCommand::execute()
00339 {
00340   KMComposeWin *win;
00341   KMMessage *msg = new KMMessage;
00342   uint id = 0;
00343 
00344   if ( mMessage && mMessage->parent() )
00345     id = mMessage->parent()->identity();
00346 
00347   msg->initHeader(id);
00348   msg->setCharset("utf-8");
00349   msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
00350 
00351   win = new KMComposeWin(msg, id);
00352   win->setCharset("", TRUE);
00353   win->setFocusToSubject();
00354   win->show();
00355 }
00356 
00357 
00358 KMMailtoReplyCommand::KMMailtoReplyCommand( QWidget *parent,
00359    const KURL &url, KMMessage *msg, const QString &selection )
00360   :KMCommand( parent, msg ), mUrl( url ), mSelection( selection  )
00361 {
00362 }
00363 
00364 void KMMailtoReplyCommand::execute()
00365 {
00366   //TODO : consider factoring createReply into this method.
00367   KMMessage *msg = retrievedMessage();
00368   KMComposeWin *win;
00369   KMMessage *rmsg = msg->createReply( KMail::ReplyNone, mSelection );
00370   rmsg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
00371 
00372   win = new KMComposeWin(rmsg, 0);
00373   win->setCharset(msg->codec()->mimeName(), TRUE);
00374   win->setReplyFocus();
00375   win->show();
00376 }
00377 
00378 
00379 KMMailtoForwardCommand::KMMailtoForwardCommand( QWidget *parent,
00380    const KURL &url, KMMessage *msg )
00381   :KMCommand( parent, msg ), mUrl( url )
00382 {
00383 }
00384 
00385 void KMMailtoForwardCommand::execute()
00386 {
00387   //TODO : consider factoring createForward into this method.
00388   KMMessage *msg = retrievedMessage();
00389   KMComposeWin *win;
00390   KMMessage *fmsg = msg->createForward();
00391   fmsg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
00392 
00393   win = new KMComposeWin(fmsg);
00394   win->setCharset(msg->codec()->mimeName(), TRUE);
00395   win->show();
00396 }
00397 
00398 
00399 KMAddBookmarksCommand::KMAddBookmarksCommand( const KURL &url, QWidget *parent )
00400   :mUrl( url ), mParent( parent )
00401 {
00402 }
00403 
00404 void KMAddBookmarksCommand::execute()
00405 {
00406   QString filename = locateLocal( "data", QString::fromLatin1("konqueror/bookmarks.xml") );
00407   KBookmarkManager *bookManager = KBookmarkManager::managerForFile( filename,
00408                                                                     false );
00409   KBookmarkGroup group = bookManager->root();
00410   group.addBookmark( bookManager, mUrl.path(), KURL( mUrl ) );
00411   bookManager->save();
00412 }
00413 
00414 KMMailtoAddAddrBookCommand::KMMailtoAddAddrBookCommand( const KURL &url,
00415    QWidget *parent )
00416   :mUrl( url ), mParent( parent )
00417 {
00418 }
00419 
00420 void KMMailtoAddAddrBookCommand::execute()
00421 {
00422   KMAddrBookExternal::addEmail( KMMessage::decodeMailtoUrl( mUrl.path() ), mParent );
00423 }
00424 
00425 
00426 KMMailtoOpenAddrBookCommand::KMMailtoOpenAddrBookCommand( const KURL &url,
00427    QWidget *parent )
00428   :mUrl( url ), mParent( parent )
00429 {
00430 }
00431 
00432 void KMMailtoOpenAddrBookCommand::execute()
00433 {
00434   KMAddrBookExternal::openEmail( KMMessage::decodeMailtoUrl( mUrl.path() ), mParent );
00435 }
00436 
00437 
00438 KMUrlCopyCommand::KMUrlCopyCommand( const KURL &url, KMMainWidget *mainWidget )
00439   :mUrl( url ), mMainWidget( mainWidget )
00440 {
00441 }
00442 
00443 void KMUrlCopyCommand::execute()
00444 {
00445   QClipboard* clip = QApplication::clipboard();
00446 
00447   if (mUrl.protocol() == "mailto") {
00448     // put the url into the mouse selection and the clipboard
00449     QString address = KMMessage::decodeMailtoUrl( mUrl.path() );
00450     clip->setSelectionMode( true );
00451     clip->setText( address );
00452     clip->setSelectionMode( false );
00453     clip->setText( address );
00454     if (mMainWidget)
00455       mMainWidget->statusMsg( i18n( "Address copied to clipboard." ));
00456   } else {
00457     // put the url into the mouse selection and the clipboard
00458     clip->setSelectionMode( true );
00459     clip->setText( mUrl.url() );
00460     clip->setSelectionMode( false );
00461     clip->setText( mUrl.url() );
00462     if ( mMainWidget )
00463       mMainWidget->statusMsg( i18n( "URL copied to clipboard." ));
00464   }
00465 }
00466 
00467 
00468 KMUrlOpenCommand::KMUrlOpenCommand( const KURL &url, KMReaderWin *readerWin )
00469   :mUrl( url ), mReaderWin( readerWin )
00470 {
00471 }
00472 
00473 void KMUrlOpenCommand::execute()
00474 {
00475   if (mUrl.isEmpty()) return;
00476   mReaderWin->slotUrlOpen( mUrl, KParts::URLArgs() );
00477 }
00478 
00479 
00480 KMUrlSaveCommand::KMUrlSaveCommand( const KURL &url, QWidget *parent )
00481   :mUrl( url ), mParent( parent )
00482 {
00483 }
00484 
00485 void KMUrlSaveCommand::execute()
00486 {
00487   if (mUrl.isEmpty()) return;
00488   KURL saveUrl = KFileDialog::getSaveURL(mUrl.fileName(), QString::null,
00489     mParent);
00490   if (saveUrl.isEmpty()) return;
00491   if (KIO::NetAccess::exists(saveUrl, false, mParent))
00492   {
00493     if (KMessageBox::warningContinueCancel(0,
00494         i18n("<qt>File <b>%1</b> exists.<br>Do you want to replace it?</qt>")
00495         .arg(saveUrl.prettyURL()), i18n("Save to File"), i18n("&Replace"))
00496         != KMessageBox::Continue)
00497       return;
00498   }
00499   KIO::Job *job = KIO::file_copy(mUrl, saveUrl, -1, true);
00500   connect(job, SIGNAL(result(KIO::Job*)), SLOT(slotUrlSaveResult(KIO::Job*)));
00501 }
00502 
00503 void KMUrlSaveCommand::slotUrlSaveResult( KIO::Job *job )
00504 {
00505   if (job->error()) job->showErrorDialog();
00506 }
00507 
00508 
00509 KMEditMsgCommand::KMEditMsgCommand( QWidget *parent, KMMessage *msg )
00510   :KMCommand( parent, msg )
00511 {
00512 }
00513 
00514 void KMEditMsgCommand::execute()
00515 {
00516   KMMessage *msg = retrievedMessage();
00517   if (!msg || !msg->parent() ||
00518       !kmkernel->folderIsDraftOrOutbox( msg->parent() ))
00519     return;
00520 
00521   // Remember the old parent, we need it a bit further down to be able
00522   // to put the unchanged messsage back in the drafts folder if the nth
00523   // edit is discarded, for n > 1.
00524   KMFolder *parent = msg->parent();
00525   if ( parent )
00526     parent->take( parent->find( msg ) );
00527 #if 0
00528   // Useful?
00529   mHeaders->setSelected(mHeaders->currentItem(), TRUE);
00530   mHeaders->highlightMessage(mHeaders->currentItem(), true);
00531 #endif
00532 
00533   KMComposeWin *win = new KMComposeWin();
00534 #if 0
00535   // FIXME: Poor solution, won't work for multiple readerwins should use kmkernel as an observer
00536   QObject::connect( win, SIGNAL( messageQueuedOrDrafted()),
00537                     this, SLOT( slotMessageQueuedOrDrafted()) );
00538 #endif
00539   msg->setTransferInProgress(false); // From here on on, the composer owns the message.
00540   win->setMsg(msg, FALSE, TRUE);
00541   win->setFolder( parent );
00542   win->show();
00543 }
00544 
00545 
00546 KMShowMsgSrcCommand::KMShowMsgSrcCommand( QWidget *parent,
00547   KMMessage *msg, bool fixedFont )
00548   :KMCommand( parent, msg ), mFixedFont( fixedFont )
00549 {
00550 }
00551 
00552 void KMShowMsgSrcCommand::execute()
00553 {
00554   KMMessage *msg = retrievedMessage();
00555   QString str = QString::fromLatin1( msg->asString() );
00556 
00557   MailSourceViewer *viewer = new MailSourceViewer(); // deletes itself upon close
00558   viewer->setCaption( i18n("Message as Plain Text") );
00559   viewer->setText(str);
00560   if( mFixedFont )
00561     viewer->setFont(KGlobalSettings::fixedFont());
00562 
00563   // Well, there is no widget to be seen here, so we have to use QCursor::pos()
00564   // Update: (GS) I'm not going to make this code behave according to Xinerama
00565   //         configuration because this is quite the hack.
00566   if (QApplication::desktop()->isVirtualDesktop()) {
00567     int scnum = QApplication::desktop()->screenNumber(QCursor::pos());
00568     viewer->resize(QApplication::desktop()->screenGeometry(scnum).width()/2,
00569                   2*QApplication::desktop()->screenGeometry(scnum).height()/3);
00570   } else {
00571     viewer->resize(QApplication::desktop()->geometry().width()/2,
00572                   2*QApplication::desktop()->geometry().height()/3);
00573   }
00574   viewer->show();
00575 }
00576 
00577 namespace {
00578   KURL subjectToUrl( const QString & subject ) {
00579     return KFileDialog::getSaveURL( subject.mid( subject.findRev(':') + 1 )
00580                                             .stripWhiteSpace()
00581                                             .replace( QDir::separator(), '_' ),
00582                                     QString::null );
00583   }
00584 }
00585 
00586 KMSaveMsgCommand::KMSaveMsgCommand( QWidget *parent, KMMessage * msg )
00587   : KMCommand( parent ),
00588     mMsgListIndex( 0 ),
00589     mOffset( 0 ),
00590     mTotalSize( msg ? msg->msgSize() : 0 )
00591 {
00592   if ( !msg ) return;
00593   setDeletesItself( true );
00594   mMsgList.append( msg->getMsgSerNum() );
00595   mUrl = subjectToUrl( msg->subject() );
00596 }
00597 
00598 KMSaveMsgCommand::KMSaveMsgCommand( QWidget *parent,
00599                                     const QPtrList<KMMsgBase> &msgList )
00600   : KMCommand( parent ),
00601     mMsgListIndex( 0 ),
00602     mOffset( 0 ),
00603     mTotalSize( 0 )
00604 {
00605   if (!msgList.getFirst())
00606     return;
00607   setDeletesItself( true );
00608   KMMsgBase *msgBase = msgList.getFirst();
00609 
00610   // We operate on serNums and not the KMMsgBase pointers, as those can
00611   // change, or become invalid when changing the current message, switching
00612   // folders, etc.
00613   QPtrListIterator<KMMsgBase> it(msgList);
00614   while ( it.current() ) {
00615     mMsgList.append( (*it)->getMsgSerNum() );
00616     mTotalSize += (*it)->msgSize();
00617     if ((*it)->parent() != 0)
00618       (*it)->parent()->open();
00619     ++it;
00620   }
00621   mMsgListIndex = 0;
00622   mUrl = subjectToUrl( msgBase->subject() );
00623 }
00624 
00625 KURL KMSaveMsgCommand::url()
00626 {
00627   return mUrl;
00628 }
00629 
00630 void KMSaveMsgCommand::execute()
00631 {
00632   mJob = KIO::put( mUrl, S_IRUSR|S_IWUSR, false, false );
00633   mJob->slotTotalSize( mTotalSize );
00634   mJob->setAsyncDataEnabled( true );
00635   mJob->setReportDataSent( true );
00636   connect(mJob, SIGNAL(dataReq(KIO::Job*, QByteArray &)),
00637     SLOT(slotSaveDataReq()));
00638   connect(mJob, SIGNAL(result(KIO::Job*)),
00639     SLOT(slotSaveResult(KIO::Job*)));
00640 }
00641 
00642 void KMSaveMsgCommand::slotSaveDataReq()
00643 {
00644   int remainingBytes = mData.size() - mOffset;
00645   if ( remainingBytes > 0 ) {
00646     // eat leftovers first
00647     if ( remainingBytes > MAX_CHUNK_SIZE )
00648       remainingBytes = MAX_CHUNK_SIZE;
00649 
00650     QByteArray data;
00651     data.duplicate( mData.data() + mOffset, remainingBytes );
00652     mJob->sendAsyncData( data );
00653     mOffset += remainingBytes;
00654     return;
00655   }
00656   // No leftovers, process next message.
00657   if ( mMsgListIndex < mMsgList.size() ) {
00658     KMMessage *msg = 0;
00659     int idx = -1;
00660     KMFolder * p = 0;
00661     kmkernel->msgDict()->getLocation( mMsgList[mMsgListIndex], &p, &idx );
00662     assert( p );
00663     assert( idx >= 0 );
00664     msg = p->getMsg(idx);
00665 
00666     if (msg->transferInProgress()) {
00667       QByteArray data = QByteArray();
00668       mJob->sendAsyncData( data );
00669     }
00670     msg->setTransferInProgress( true );
00671     if (msg->isComplete() ) {
00672       slotMessageRetrievedForSaving(msg);
00673     } else {
00674       // retrieve Message first
00675       if (msg->parent()  && !msg->isComplete() ) {
00676         FolderJob *job = msg->parent()->createJob(msg);
00677         connect(job, SIGNAL(messageRetrieved(KMMessage*)),
00678             this, SLOT(slotMessageRetrievedForSaving(KMMessage*)));
00679         job->start();
00680       }
00681     }
00682   } else {
00683     // No more messages. Tell the putjob we are done.
00684     QByteArray data = QByteArray();
00685     mJob->sendAsyncData( data );
00686   }
00687 }
00688 
00689 void KMSaveMsgCommand::slotMessageRetrievedForSaving(KMMessage *msg)
00690 {
00691   QCString str( msg->fromEmail() );
00692   if ( str.isEmpty() )
00693     str = "unknown@unknown.invalid";
00694   str = "From " + str + " " + msg->dateShortStr() + "\n";
00695   str += KMFolderMbox::escapeFrom( msg->asString() );
00696   str += "\n";
00697   msg->setTransferInProgress(false);
00698 
00699   mData = str;
00700   mData.resize(mData.size() - 1);
00701   mOffset = 0;
00702   QByteArray data;
00703   int size;
00704   // Unless it is great than 64 k send the whole message. kio buffers for us.
00705   if( mData.size() > (unsigned int) MAX_CHUNK_SIZE )
00706     size = MAX_CHUNK_SIZE;
00707   else
00708     size = mData.size();
00709 
00710   data.duplicate( mData, size );
00711   mJob->sendAsyncData( data );
00712   mOffset += size;
00713   ++mMsgListIndex;
00714   // Get rid of the message.
00715   if (msg->parent()) {
00716     int idx = -1;
00717     KMFolder * p = 0;
00718     kmkernel->msgDict()->getLocation( msg, &p, &idx );
00719     assert( p == msg->parent() ); assert( idx >= 0 );
00720     p->unGetMsg( idx );
00721     p->close();
00722   }
00723 }
00724 
00725 void KMSaveMsgCommand::slotSaveResult(KIO::Job *job)
00726 {
00727   if (job->error())
00728   {
00729     if (job->error() == KIO::ERR_FILE_ALREADY_EXIST)
00730     {
00731       if (KMessageBox::warningContinueCancel(0,
00732         i18n("File %1 exists.\nDo you want to replace it?")
00733         .arg(mUrl.prettyURL()), i18n("Save to File"), i18n("&Replace"))
00734         == KMessageBox::Continue) {
00735         mOffset = 0;
00736 
00737         mJob = KIO::put( mUrl, S_IRUSR|S_IWUSR, true, false );
00738         mJob->slotTotalSize( mTotalSize );
00739         mJob->setAsyncDataEnabled( true );
00740         mJob->setReportDataSent( true );
00741         connect(mJob, SIGNAL(dataReq(KIO::Job*, QByteArray &)),
00742             SLOT(slotSaveDataReq()));
00743         connect(mJob, SIGNAL(result(KIO::Job*)),
00744             SLOT(slotSaveResult(KIO::Job*)));
00745       }
00746     }
00747     else
00748     {
00749       job->showErrorDialog();
00750       delete this;
00751     }
00752   } else {
00753     delete this;
00754   }
00755 }
00756 
00757 //TODO: ReplyTo, NoQuoteReplyTo, ReplyList, ReplyToAll, ReplyAuthor
00758 //      are all similar and should be factored
00759 KMReplyToCommand::KMReplyToCommand( QWidget *parent, KMMessage *msg,
00760                                     const QString &selection )
00761   : KMCommand( parent, msg ), mSelection( selection )
00762 {
00763 }
00764 
00765 void KMReplyToCommand::execute()
00766 {
00767   KCursorSaver busy(KBusyPtr::busy());
00768   KMMessage *msg = retrievedMessage();
00769   KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection );
00770   KMComposeWin *win = new KMComposeWin( reply );
00771   win->setCharset( msg->codec()->mimeName(), TRUE );
00772   win->setReplyFocus();
00773   win->show();
00774 }
00775 
00776 
00777 KMNoQuoteReplyToCommand::KMNoQuoteReplyToCommand( QWidget *parent,
00778                                                   KMMessage *msg )
00779   : KMCommand( parent, msg )
00780 {
00781 }
00782 
00783 void KMNoQuoteReplyToCommand::execute()
00784 {
00785   KCursorSaver busy(KBusyPtr::busy());
00786   KMMessage *msg = retrievedMessage();
00787   KMMessage *reply = msg->createReply( KMail::ReplySmart, "", TRUE);
00788   KMComposeWin *win = new KMComposeWin( reply );
00789   win->setCharset(msg->codec()->mimeName(), TRUE);
00790   win->setReplyFocus(false);
00791   win->show();
00792 }
00793 
00794 
00795 KMReplyListCommand::KMReplyListCommand( QWidget *parent,
00796   KMMessage *msg, const QString &selection )
00797  : KMCommand( parent, msg ), mSelection( selection )
00798 {
00799 }
00800 
00801 void KMReplyListCommand::execute()
00802 {
00803   KCursorSaver busy(KBusyPtr::busy());
00804   KMMessage *msg = retrievedMessage();
00805   KMMessage *reply = msg->createReply( KMail::ReplyList, mSelection);
00806   KMComposeWin *win = new KMComposeWin( reply );
00807   win->setCharset(msg->codec()->mimeName(), TRUE);
00808   win->setReplyFocus(false);
00809   win->show();
00810 }
00811 
00812 
00813 KMReplyToAllCommand::KMReplyToAllCommand( QWidget *parent,
00814   KMMessage *msg, const QString &selection )
00815   :KMCommand( parent, msg ), mSelection( selection )
00816 {
00817 }
00818 
00819 void KMReplyToAllCommand::execute()
00820 {
00821   KCursorSaver busy(KBusyPtr::busy());
00822   KMMessage *msg = retrievedMessage();
00823   KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection );
00824   KMComposeWin *win = new KMComposeWin( reply );
00825   win->setCharset( msg->codec()->mimeName(), TRUE );
00826   win->setReplyFocus();
00827   win->show();
00828 }
00829 
00830 
00831 KMReplyAuthorCommand::KMReplyAuthorCommand( QWidget *parent, KMMessage *msg,
00832                                             const QString &selection )
00833   : KMCommand( parent, msg ), mSelection( selection )
00834 {
00835 }
00836 
00837 void KMReplyAuthorCommand::execute()
00838 {
00839   KCursorSaver busy(KBusyPtr::busy());
00840   KMMessage *msg = retrievedMessage();
00841   KMMessage *reply = msg->createReply( KMail::ReplyAuthor, mSelection );
00842   KMComposeWin *win = new KMComposeWin( reply );
00843   win->setCharset( msg->codec()->mimeName(), TRUE );
00844   win->setReplyFocus();
00845   win->show();
00846 }
00847 
00848 
00849 KMForwardCommand::KMForwardCommand( QWidget *parent,
00850   const QPtrList<KMMsgBase> &msgList, uint identity )
00851   : KMCommand( parent, msgList ),
00852     mParent( parent ),
00853     mIdentity( identity )
00854 {
00855 }
00856 
00857 KMForwardCommand::KMForwardCommand( QWidget *parent, KMMessage *msg,
00858                                     uint identity )
00859   : KMCommand( parent, msg ),
00860     mParent( parent ),
00861     mIdentity( identity )
00862 {
00863 }
00864 
00865 void KMForwardCommand::execute()
00866 {
00867   KMComposeWin *win;
00868   QPtrList<KMMessage> msgList = retrievedMsgs();
00869 
00870   if (msgList.count() >= 2) {
00871     // ask if they want a mime digest forward
00872 
00873     if (KMessageBox::questionYesNo(mParent, i18n("Forward selected messages as"
00874                                                  " a MIME digest?"))
00875         == KMessageBox::Yes) {
00876       uint id = 0;
00877       KMMessage *fwdMsg = new KMMessage;
00878       KMMessagePart *msgPart = new KMMessagePart;
00879       QString msgPartText;
00880       int msgCnt = 0; // incase there are some we can't forward for some reason
00881 
00882       // dummy header initialization; initialization with the correct identity
00883       // is done below
00884       fwdMsg->initHeader(id);
00885       fwdMsg->setAutomaticFields(true);
00886       fwdMsg->mMsg->Headers().ContentType().CreateBoundary(1);
00887       QCString boundary( fwdMsg->mMsg->Headers().ContentType().Boundary().c_str() );
00888       msgPartText = i18n("\nThis is a MIME digest forward. The content of the"
00889                          " message is contained in the attachment(s).\n\n\n");
00890       // iterate through all the messages to be forwarded
00891       for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) {
00892         // set the identity
00893         if (id == 0)
00894           id = msg->headerField("X-KMail-Identity").stripWhiteSpace().toUInt();
00895         // set the part header
00896         msgPartText += "--";
00897         msgPartText += QString::fromLatin1( boundary );
00898         msgPartText += "\nContent-Type: MESSAGE/RFC822";
00899         msgPartText += QString("; CHARSET=%1").arg(msg->charset());
00900         msgPartText += "\n";
00901         DwHeaders dwh;
00902         dwh.MessageId().CreateDefault();
00903         msgPartText += QString("Content-ID: %1\n").arg(dwh.MessageId().AsString().c_str());
00904         msgPartText += QString("Content-Description: %1").arg(msg->subject());
00905         if (!msg->subject().contains("(fwd)"))
00906           msgPartText += " (fwd)";
00907         msgPartText += "\n\n";
00908         // remove headers that shouldn't be forwarded
00909         msg->removePrivateHeaderFields();
00910         msg->removeHeaderField("BCC");
00911         // set the part
00912         msgPartText += msg->headerAsString();
00913         msgPartText += "\n";
00914         msgPartText += msg->body();
00915         msgPartText += "\n";     // eot
00916         msgCnt++;
00917         fwdMsg->link(msg, KMMsgStatusForwarded);
00918       }
00919       if ( id == 0 )
00920         id = mIdentity; // use folder identity if no message had an id set
00921       fwdMsg->initHeader(id);
00922       msgPartText += "--";
00923       msgPartText += QString::fromLatin1( boundary );
00924       msgPartText += "--\n";
00925       QCString tmp;
00926       msgPart->setTypeStr("MULTIPART");
00927       tmp.sprintf( "Digest; boundary=\"%s\"", boundary.data() );
00928       msgPart->setSubtypeStr( tmp );
00929       msgPart->setName("unnamed");
00930       msgPart->setCte(DwMime::kCte7bit);   // does it have to be 7bit?
00931       msgPart->setContentDescription(QString("Digest of %1 messages.").arg(msgCnt));
00932       // THIS HAS TO BE AFTER setCte()!!!!
00933       msgPart->setBodyEncoded(QCString(msgPartText.ascii()));
00934       KCursorSaver busy(KBusyPtr::busy());
00935       win = new KMComposeWin(fwdMsg, id);
00936       win->addAttach(msgPart);
00937       win->show();
00938       return;
00939     } else {            // NO MIME DIGEST, Multiple forward
00940       uint id = 0;
00941       QCString msgText = "";
00942       QPtrList<KMMessage> linklist;
00943       for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) {
00944         // set the identity
00945         if (id == 0)
00946           id = msg->headerField("X-KMail-Identity").stripWhiteSpace().toUInt();
00947 
00948         msgText += msg->createForwardBody();
00949         linklist.append(msg);
00950       }
00951       if ( id == 0 )
00952         id = mIdentity; // use folder identity if no message had an id set
00953       KMMessage *fwdMsg = new KMMessage;
00954       fwdMsg->initHeader(id);
00955       fwdMsg->setAutomaticFields(true);
00956       fwdMsg->setCharset("utf-8");
00957       fwdMsg->setBody(msgText);
00958 
00959       for (KMMessage *msg = linklist.first(); msg; msg = linklist.next())
00960         fwdMsg->link(msg, KMMsgStatusForwarded);
00961 
00962       KCursorSaver busy(KBusyPtr::busy());
00963       win = new KMComposeWin(fwdMsg, id);
00964       win->setCharset("");
00965       win->show();
00966       return;
00967     }
00968   }
00969 
00970   // forward a single message at most.
00971 
00972   KMMessage *msg = msgList.getFirst();
00973   if (!msg || !msg->codec()) return;
00974 
00975   KCursorSaver busy(KBusyPtr::busy());
00976   win = new KMComposeWin(msg->createForward());
00977   win->setCharset(msg->codec()->mimeName(), TRUE);
00978   win->show();
00979 }
00980 
00981 
00982 KMForwardAttachedCommand::KMForwardAttachedCommand( QWidget *parent,
00983   const QPtrList<KMMsgBase> &msgList, uint identity, KMComposeWin *win )
00984   : KMCommand( parent, msgList ), mIdentity( identity ),
00985     mWin( QGuardedPtr< KMComposeWin >( win ))
00986 {
00987 }
00988 
00989 KMForwardAttachedCommand::KMForwardAttachedCommand( QWidget *parent,
00990   KMMessage * msg, uint identity, KMComposeWin *win )
00991   : KMCommand( parent, msg ), mIdentity( identity ),
00992     mWin( QGuardedPtr< KMComposeWin >( win ))
00993 {
00994 }
00995 
00996 void KMForwardAttachedCommand::execute()
00997 {
00998   QPtrList<KMMessage> msgList = retrievedMsgs();
00999   KMMessage *fwdMsg = new KMMessage;
01000 
01001   if (msgList.count() >= 2) {
01002     // don't respect X-KMail-Identity headers because they might differ for
01003     // the selected mails
01004     fwdMsg->initHeader(mIdentity);
01005   }
01006   else if (msgList.count() == 1) {
01007     KMMessage *msg = msgList.getFirst();
01008     fwdMsg->initFromMessage(msg);
01009     fwdMsg->setSubject( msg->forwardSubject() );
01010   }
01011 
01012   fwdMsg->setAutomaticFields(true);
01013 
01014   KCursorSaver busy(KBusyPtr::busy());
01015   if (!mWin)
01016     mWin = new KMComposeWin(fwdMsg, mIdentity);
01017 
01018   // iterate through all the messages to be forwarded
01019   for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) {
01020     // remove headers that shouldn't be forwarded
01021     msg->removePrivateHeaderFields();
01022     msg->removeHeaderField("BCC");
01023     // set the part
01024     KMMessagePart *msgPart = new KMMessagePart;
01025     msgPart->setTypeStr("message");
01026     msgPart->setSubtypeStr("rfc822");
01027     msgPart->setCharset(msg->charset());
01028     msgPart->setName("forwarded message");
01029     msgPart->setContentDescription(msg->from()+": "+msg->subject());
01030     msgPart->setContentDisposition( "inline" );
01031     // THIS HAS TO BE AFTER setCte()!!!!
01032     QValueList<int> dummy;
01033     msgPart->setBodyAndGuessCte(msg->asString(), dummy, true);
01034     msgPart->setCharset("");
01035 
01036     fwdMsg->link(msg, KMMsgStatusForwarded);
01037     mWin->addAttach(msgPart);
01038   }
01039 
01040   mWin->show();
01041 }
01042 
01043 
01044 KMRedirectCommand::KMRedirectCommand( QWidget *parent,
01045   KMMessage *msg )
01046   : KMCommand( parent, msg )
01047 {
01048 }
01049 
01050 void KMRedirectCommand::execute()
01051 {
01052   //TODO: move KMMessage::createRedirect to here
01053   KMComposeWin *win;
01054   KMMessage *msg = retrievedMessage();
01055   if (!msg || !msg->codec()) return;
01056 
01057   KCursorSaver busy(KBusyPtr::busy());
01058   win = new KMComposeWin();
01059   win->setMsg(msg->createRedirect(), FALSE);
01060   win->setCharset(msg->codec()->mimeName());
01061   win->show();
01062 }
01063 
01064 
01065 KMBounceCommand::KMBounceCommand( QWidget *parent,
01066   KMMessage *msg )
01067   : KMCommand( parent, msg )
01068 {
01069 }
01070 
01071 void KMBounceCommand::execute()
01072 {
01073   KMMessage *msg = retrievedMessage();
01074   KMMessage *newMsg = msg->createBounce( TRUE /* with UI */);
01075   if (newMsg)
01076     kmkernel->msgSender()->send(newMsg, kmkernel->msgSender()->sendImmediate());
01077 }
01078 
01079 
01080 KMPrintCommand::KMPrintCommand( QWidget *parent,
01081   KMMessage *msg, bool htmlOverride )
01082   : KMCommand( parent, msg ), mHtmlOverride( htmlOverride )
01083 {
01084 }
01085 
01086 void KMPrintCommand::execute()
01087 {
01088   KMReaderWin printWin( 0, 0, 0 );
01089   printWin.setPrinting(TRUE);
01090   printWin.readConfig();
01091   printWin.setHtmlOverride( mHtmlOverride );
01092   printWin.setMsg(retrievedMessage(), TRUE);
01093   printWin.printMsg();
01094 }
01095 
01096 
01097 KMSetStatusCommand::KMSetStatusCommand( KMMsgStatus status,
01098   const QValueList<Q_UINT32> &serNums, bool toggle )
01099   : mStatus( status ), mSerNums( serNums ), mToggle( toggle )
01100 {
01101 }
01102 
01103 void KMSetStatusCommand::execute()
01104 {
01105   QValueListIterator<Q_UINT32> it;
01106   int idx = -1;
01107   KMFolder *folder = 0;
01108   bool parentStatus = false;
01109 
01110   // Toggle actions on threads toggle the whole thread
01111   // depending on the state of the parent.
01112   if (mToggle) {
01113     KMMsgBase *msg;
01114     kmkernel->msgDict()->getLocation( *mSerNums.begin(), &folder, &idx );
01115     if (folder) {
01116       msg = folder->getMsgBase(idx);
01117       if (msg && (msg->status()&mStatus))
01118         parentStatus = true;
01119       else
01120         parentStatus = false;
01121     }
01122   }
01123   QMap< KMFolder*, QValueList<int> > folderMap;
01124   for ( it = mSerNums.begin(); it != mSerNums.end(); ++it ) {
01125     kmkernel->msgDict()->getLocation( *it, &folder, &idx );
01126     if (folder) {
01127       if (mToggle) {
01128         KMMsgBase *msg = folder->getMsgBase(idx);
01129         // check if we are already at the target toggle state
01130         if (msg) {
01131           bool myStatus;
01132           if (msg->status()&mStatus)
01133             myStatus = true;
01134           else
01135             myStatus = false;
01136           if (myStatus != parentStatus)
01137             continue;
01138         }
01139       }
01140       /* Collect the ids for each folder in a separate list and
01141          send them off in one go at the end. */
01142       folderMap[folder].append(idx);
01143     }
01144   }
01145   QMapIterator< KMFolder*, QValueList<int> > it2 = folderMap.begin();
01146   while ( it2 != folderMap.end() ) {
01147      KMFolder *f = it2.key();
01148      f->setStatus( (*it2), mStatus, mToggle );
01149      ++it2;
01150   }
01151 }
01152 
01153 
01154 KMFilterCommand::KMFilterCommand( const QCString &field, const QString &value )
01155   : mField( field ), mValue( value )
01156 {
01157 }
01158 
01159 void KMFilterCommand::execute()
01160 {
01161   kmkernel->filterMgr()->createFilter( mField, mValue );
01162 }
01163 
01164 
01165 KMMailingListFilterCommand::KMMailingListFilterCommand( QWidget *parent,
01166   KMMessage *msg )
01167   : KMCommand( parent, msg )
01168 {
01169 }
01170 
01171 KMFilterActionCommand::KMFilterActionCommand( QWidget *parent,
01172                                               const QPtrList<KMMsgBase> &msgList,
01173                                               KMFilter *filter )
01174   : KMCommand( parent, msgList ), mFilter( filter  )
01175 {
01176 }
01177 
01178 void KMFilterActionCommand::execute()
01179 {
01180   QPtrList<KMMessage> msgList = retrievedMsgs();
01181 
01182   for (KMMessage *msg = msgList.first(); msg; msg = msgList.next())
01183     kmkernel->filterMgr()->tempOpenFolder(msg->parent());
01184 
01185   for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) {
01186     msg->setTransferInProgress(false);
01187 
01188     int filterResult = kmkernel->filterMgr()->process(msg, mFilter);
01189     if (filterResult == 2) {
01190       // something went horribly wrong (out of space?)
01191       perror("Critical error");
01192       kmkernel->emergencyExit( i18n("Not enough free disk space?" ));
01193     }
01194     msg->setTransferInProgress(true);
01195   }
01196 }
01197 
01198 
01199 KMMetaFilterActionCommand::KMMetaFilterActionCommand( KMFilter *filter,
01200                                                       KMHeaders *headers,
01201                                                       KMMainWidget *main )
01202     : QObject( main ),
01203       mFilter( filter ), mHeaders( headers ), mMainWidget( main )
01204 {
01205 }
01206 
01207 void KMMetaFilterActionCommand::start()
01208 {
01209 #if 0 // use action scheduler
01210   KMFilterMgr::FilterSet set = KMFilterMgr::All;
01211   QPtrList<KMFilter> filters;
01212   filters.append( mFilter );
01213   ActionScheduler *scheduler = new ActionScheduler( set, filters, mHeaders );
01214   scheduler->setAlwaysMatch( true );
01215   scheduler->setAutoDestruct( true );
01216 
01217   int contentX, contentY;
01218   KMHeaderItem *nextItem = mHeaders->prepareMove( &contentX, &contentY );
01219   QPtrList<KMMsgBase> msgList = *mHeaders->selectedMsgs(true);
01220   mHeaders->finalizeMove( nextItem, contentX, contentY );
01221 
01222 
01223   for (KMMsgBase *msg = msgList.first(); msg; msg = msgList.next())
01224     scheduler->execFilters( msg );
01225 #else
01226   KMCommand *filterCommand = new KMFilterActionCommand( mMainWidget,
01227   *mHeaders->selectedMsgs(), mFilter);
01228   filterCommand->start();
01229 #endif
01230 }
01231 
01232 
01233 void KMMailingListFilterCommand::execute()
01234 {
01235   QCString name;
01236   QString value;
01237   KMMessage *msg = retrievedMessage();
01238   if (!msg)
01239     return;
01240 
01241   if (!KMMLInfo::name( msg, name, value ).isNull())
01242     kmkernel->filterMgr()->createFilter( name, value );
01243 }
01244 
01245 
01246 QPopupMenu* KMMenuCommand::folderToPopupMenu(bool move,
01247   QObject *receiver, KMMenuToFolder *aMenuToFolder, QPopupMenu *menu )
01248 {
01249   while ( menu->count() )
01250   {
01251     QPopupMenu *popup = menu->findItem( menu->idAt( 0 ) )->popup();
01252     if (popup)
01253       delete popup;
01254     else
01255       menu->removeItemAt( 0 );
01256   }
01257 
01258   if (!kmkernel->imapFolderMgr()->dir().first()) {
01259     KMMenuCommand::makeFolderMenu(  &kmkernel->folderMgr()->dir(), move,
01260       receiver, aMenuToFolder, menu );
01261   } else {
01262     // operate on top-level items
01263     QPopupMenu* subMenu = new QPopupMenu(menu);
01264     subMenu = KMMenuCommand::makeFolderMenu(  &kmkernel->folderMgr()->dir(),
01265         move, receiver, aMenuToFolder, subMenu );
01266     menu->insertItem( i18n( "Local Folders" ), subMenu );
01267     KMFolderDir* fdir = &kmkernel->imapFolderMgr()->dir();
01268     for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) {
01269       if (node->isDir())
01270         continue;
01271       subMenu = new QPopupMenu(menu);
01272       subMenu = makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu );
01273       menu->insertItem( node->label(), subMenu );
01274     }
01275   }
01276 
01277   return menu;
01278 }
01279 
01280 QPopupMenu* KMMenuCommand::makeFolderMenu(KMFolderNode* node, bool move,
01281   QObject *receiver, KMMenuToFolder *aMenuToFolder, QPopupMenu *menu )
01282 {
01283   // connect the signals
01284   if (move)
01285   {
01286     disconnect(menu, SIGNAL(activated(int)), receiver,
01287            SLOT(moveSelectedToFolder(int)));
01288     connect(menu, SIGNAL(activated(int)), receiver,
01289              SLOT(moveSelectedToFolder(int)));
01290   } else {
01291     disconnect(menu, SIGNAL(activated(int)), receiver,
01292            SLOT(copySelectedToFolder(int)));
01293     connect(menu, SIGNAL(activated(int)), receiver,
01294              SLOT(copySelectedToFolder(int)));
01295   }
01296 
01297   KMFolder *folder = 0;
01298   KMFolderDir *folderDir = 0;
01299   if (node->isDir()) {
01300     folderDir = static_cast<KMFolderDir*>(node);
01301   } else {
01302     folder = static_cast<KMFolder*>(node);
01303     folderDir = folder->child();
01304   }
01305 
01306   if (folder && !folder->noContent())
01307   {
01308     int menuId;
01309     if (move)
01310       menuId = menu->insertItem(i18n("Move to This Folder"));
01311     else
01312       menuId = menu->insertItem(i18n("Copy to This Folder"));
01313     aMenuToFolder->insert( menuId, folder );
01314     menu->insertSeparator();
01315   }
01316 
01317   if (!folderDir)
01318     return menu;
01319 
01320   for (KMFolderNode *it = folderDir->first(); it; it = folderDir->next() ) {
01321     if (it->isDir())
01322       continue;
01323     KMFolder *child = static_cast<KMFolder*>(it);
01324     QString label = child->label();
01325     label.replace("&","&&");
01326     if (child->child() && child->child()->first()) {
01327       // descend
01328       QPopupMenu *subMenu = makeFolderMenu(child, move, receiver,
01329         aMenuToFolder, new QPopupMenu(menu, "subMenu"));
01330       menu->insertItem(label, subMenu);
01331     } else {
01332       // insert an item
01333       int menuId = menu->insertItem(label);
01334       aMenuToFolder->insert( menuId, child );
01335     }
01336   }
01337   return menu;
01338 }
01339 
01340 
01341 KMCopyCommand::KMCopyCommand( KMFolder* destFolder,
01342                               const QPtrList<KMMsgBase> &msgList )
01343   :mDestFolder( destFolder ), mMsgList( msgList )
01344 {
01345 }
01346 
01347 KMCopyCommand::KMCopyCommand( KMFolder* destFolder, KMMessage * msg )
01348   :mDestFolder( destFolder )
01349 {
01350   mMsgList.append( &msg->toMsgBase() );
01351 }
01352 
01353 void KMCopyCommand::execute()
01354 {
01355   KMMsgBase *msgBase;
01356   KMMessage *msg, *newMsg;
01357   int idx = -1;
01358   bool isMessage;
01359   QPtrList<KMMessage> list;
01360 
01361   KCursorSaver busy(KBusyPtr::busy());
01362 
01363   for (msgBase = mMsgList.first(); msgBase; msgBase = mMsgList.next() )
01364   {
01365     KMFolder *srcFolder = msgBase->parent();
01366     if (isMessage = msgBase->isMessage())
01367     {
01368       msg = static_cast<KMMessage*>(msgBase);
01369     } else {
01370       idx = srcFolder->find(msgBase);
01371       assert(idx != -1);
01372       msg = srcFolder->getMsg(idx);
01373     }
01374 
01375     if (srcFolder &&
01376         (srcFolder->folderType()== KMFolderTypeImap) &&
01377         (mDestFolder->folderType() == KMFolderTypeImap) &&
01378         (static_cast<KMFolderImap*>(srcFolder)->account() ==
01379          static_cast<KMFolderImap*>(mDestFolder)->account()))
01380     {
01381       list.append(msg);
01382     } else {
01383       newMsg = new KMMessage;
01384       newMsg->fromString(msg->asString());
01385       newMsg->setStatus(msg->status());
01386       newMsg->setComplete(msg->isComplete());
01387 
01388       if (srcFolder && !newMsg->isComplete())
01389       {
01390         newMsg->setParent(msg->parent());
01391         FolderJob *job = srcFolder->createJob(newMsg);
01392         connect(job, SIGNAL(messageRetrieved(KMMessage*)),
01393                 mDestFolder, SLOT(reallyAddCopyOfMsg(KMMessage*)));
01394         // msg musn't be deleted
01395         newMsg->setTransferInProgress(true);
01396         job->start();
01397       } else {
01398         int rc, index;
01399         rc = mDestFolder->addMsg(newMsg, &index);
01400         if (rc == 0 && index != -1)
01401           mDestFolder->unGetMsg( mDestFolder->count() - 1 );
01402       }
01403     }
01404 
01405     if (!isMessage && list.isEmpty())
01406     {
01407       assert(idx != -1);
01408       srcFolder->unGetMsg( idx );
01409     }
01410 
01411   } // end for
01412 
01413 //TODO: Get rid of the other cases just use this one for all types of folder
01414 //TODO: requires adding copyMsg and getFolder methods to KMFolder.h
01415 
01416   if (!list.isEmpty())
01417   {
01418     // copy the message(s); note: the list is empty afterwards!
01419     KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder);
01420     imapDestFolder->copyMsg(list);
01421     imapDestFolder->getFolder();
01422   }
01423 
01424 }
01425 
01426 
01427 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
01428                               const QPtrList<KMMsgBase> &msgList)
01429   :mDestFolder( destFolder ), mMsgList( msgList )
01430 {
01431   setDeletesItself( true );
01432 }
01433 
01434 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
01435                               KMMessage *msg )
01436   :mDestFolder( destFolder )
01437 {
01438   setDeletesItself( true );
01439   mMsgList.append( &msg->toMsgBase() );
01440 }
01441 
01442 KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
01443                               KMMsgBase *msgBase )
01444   :mDestFolder( destFolder )
01445 {
01446   setDeletesItself( true );
01447   mMsgList.append( msgBase );
01448 }
01449 
01450 void KMMoveCommand::execute()
01451 {
01452   typedef QMap< KMFolder*, QPtrList<KMMessage>* > FolderToMessageListMap;
01453   FolderToMessageListMap folderDeleteList;
01454 
01455   if (mDestFolder && mDestFolder->open() != 0)
01456     return;
01457   KCursorSaver busy(KBusyPtr::busy());
01458 
01459   KMMessage *msg;
01460   KMMsgBase *msgBase;
01461   int rc = 0;
01462   int index;
01463   QPtrList<KMMessage> list;
01464   int undoId = -1;
01465 
01466   if (mDestFolder) {
01467     connect (mDestFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)),
01468              this, SLOT(slotMsgAddedToDestFolder(KMFolder*, Q_UINT32)));
01469 
01470   }
01471 
01472   for (msgBase=mMsgList.first(); msgBase && !rc; msgBase=mMsgList.next()) {
01473     KMFolder *srcFolder = msgBase->parent();
01474     if (srcFolder == mDestFolder)
01475       continue;
01476     bool undo = msgBase->enableUndo();
01477     int idx = srcFolder->find(msgBase);
01478     assert(idx != -1);
01479     if ( msgBase->isMessage() )
01480       msg = static_cast<KMMessage*>(msgBase);
01481     else
01482       msg = srcFolder->getMsg(idx);
01483 
01484     if ( msg->transferInProgress() &&
01485          srcFolder->folderType() == KMFolderTypeImap )
01486     {
01487       // cancel the download
01488       msg->setTransferInProgress( false, true );
01489       static_cast<KMFolderImap*>(srcFolder)->ignoreJobsForMessage( msg );
01490     }
01491 
01492     if (mDestFolder) {
01493       mLostBoys.append(msg->getMsgSerNum());
01494       if (mDestFolder->folderType() == KMFolderTypeImap) {
01495         /* If we are moving to an imap folder, connect to it's completed
01496          * siganl so we notice when all the mails should have showed up in it
01497          * but haven't for some reason. */
01498         connect (mDestFolder, SIGNAL(folderComplete( KMFolderImap*, bool )),
01499             this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
01500         list.append(msg);
01501       } else {
01502         // We are moving to a local folder.
01503         rc = mDestFolder->moveMsg(msg, &index);
01504         if (rc == 0 && index != -1) {
01505           KMMsgBase *mb = mDestFolder->unGetMsg( mDestFolder->count() - 1 );
01506           if (undo && mb)
01507           {
01508             if ( undoId == -1 )
01509               undoId = kmkernel->undoStack()->newUndoAction( srcFolder, mDestFolder );
01510             kmkernel->undoStack()->addMsgToAction( undoId, mb->getMsgSerNum() );
01511           }
01512         } else if (rc != 0) {
01513           // Something  went wrong. Stop processing here, it is likely that the
01514           // other moves would fail as well.
01515           emit completed( false);
01516           deleteLater();
01517           return;
01518         }
01519       }
01520     } else {
01521       // really delete messages that are already in the trash folder or if
01522       // we are really, really deleting, not just moving to trash
01523       if (srcFolder->folderType() == KMFolderTypeImap) {
01524         if (!folderDeleteList[srcFolder])
01525           folderDeleteList[srcFolder] = new QPtrList<KMMessage>;
01526         folderDeleteList[srcFolder]->append( msg );
01527       } else {
01528         srcFolder->removeMsg(idx);
01529         delete msg;
01530       }
01531     }
01532   }
01533   if (!list.isEmpty() && mDestFolder) {
01534        mDestFolder->moveMsg(list, &index);
01535   } else {
01536     FolderToMessageListMap::Iterator it;
01537     for ( it = folderDeleteList.begin(); it != folderDeleteList.end(); ++it ) {
01538       it.key()->removeMsg(*it.data());
01539       delete it.data();
01540     }
01541     /* The list is empty, which means that either all messages were to be
01542      * deleted, which is done above, or all of them were already in this folder.
01543      * In both cases make sure a completed() signal is emitted nonetheless. */
01544     KMFolder *srcFolder = 0;
01545     if ( mMsgList.first() ) {
01546       srcFolder = mMsgList.first()->parent();
01547       if ( mDestFolder && mDestFolder == srcFolder ) {
01548         emit completed( true );
01549         deleteLater();
01550       }
01551     }
01552     if ( !mDestFolder ) {
01553       emit completed( true );
01554       deleteLater();
01555     }
01556   }
01557 }
01558 
01559 void KMMoveCommand::slotImapFolderCompleted(KMFolderImap *, bool success)
01560 {
01561   if ( success ) {
01562     // the folder was checked successfully but we were still called, so check
01563     // if we are still waiting for messages to show up. If so, uidValidity
01564     // changed, or something else went wrong. Clean up.
01565 
01566     /* Unfortunately older UW imap servers change uid validity for each put job.
01567      * Yes, it is really that broken. *sigh* So we cannot report error here, I guess. */
01568     if ( !mLostBoys.isEmpty() ) {
01569       kdDebug(5006) <<  "### Not all moved messages reported back that they were " << endl
01570                     <<  "### added to the target folder. Did uidValidity change? " << endl;
01571     }
01572   } else {
01573     // Should we inform the user here or leave that to the caller?
01574   }
01575   emit completed( success );
01576   deleteLater();
01577 }
01578 
01579 void KMMoveCommand::slotMsgAddedToDestFolder(KMFolder *folder, Q_UINT32 serNum)
01580 {
01581   if (folder != mDestFolder) {
01582     kdDebug(5006) << "KMMoveCommand::msgAddedToDestFolder different "
01583                      "folder or invalid serial number." << endl;
01584     return;
01585   }
01586   mLostBoys.remove(serNum);
01587   if ( mLostBoys.isEmpty() ) {
01588     // we are done. All messages transferred to the host succesfully
01589     if (mDestFolder && mDestFolder->folderType() != KMFolderTypeImap) {
01590       mDestFolder->sync();
01591     }
01592     emit completed( true );
01593     deleteLater();
01594   }
01595 }
01596 
01597 // srcFolder doesn't make much sense for searchFolders
01598 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder,
01599   const QPtrList<KMMsgBase> &msgList )
01600 :KMMoveCommand(findTrashFolder( srcFolder ), msgList)
01601 {
01602 }
01603 
01604 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder, KMMessage * msg )
01605 :KMMoveCommand(findTrashFolder( srcFolder ), msg)
01606 {
01607 }
01608 
01609 
01610 KMFolder * KMDeleteMsgCommand::findTrashFolder( KMFolder * folder )
01611 {
01612   if (folder->folderType()== KMFolderTypeImap)
01613   {
01614     KMFolderImap* fi = static_cast<KMFolderImap*> (folder);
01615     QString trashStr = fi->account()->trash();
01616     KMFolder* trash = kmkernel->imapFolderMgr()->findIdString( trashStr );
01617     if (!trash) trash = kmkernel->trashFolder();
01618     if (folder != trash)
01619       return trash;
01620   } else {
01621     if (folder != kmkernel->trashFolder())
01622       // move to trash folder
01623       return kmkernel->trashFolder();
01624   }
01625   return 0;
01626 }
01627 
01628 KMUrlClickedCommand::KMUrlClickedCommand( const KURL &url, uint identity,
01629   KMReaderWin *readerWin, bool htmlPref, KMMainWidget *mainWidget )
01630   :mUrl( url ), mIdentity( identity ), mReaderWin( readerWin ),
01631    mHtmlPref( htmlPref ), mMainWidget( mainWidget )
01632 {
01633 }
01634 
01635 void KMUrlClickedCommand::execute()
01636 {
01637   KMComposeWin *win;
01638   KMMessage* msg;
01639 
01640   if (mUrl.protocol() == "mailto")
01641   {
01642     msg = new KMMessage;
01643     msg->initHeader(mIdentity);
01644     msg->setCharset("utf-8");
01645     msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
01646     QString query=mUrl.query();
01647     while (!query.isEmpty()) {
01648       QString queryPart;
01649       int secondQuery = query.find('?',1);
01650       if (secondQuery != -1)
01651         queryPart = query.left(secondQuery);
01652       else
01653         queryPart = query;
01654       query = query.mid(queryPart.length());
01655 
01656       if (queryPart.left(9) == "?subject=")
01657         msg->setSubject( KURL::decode_string(queryPart.mid(9)) );
01658       else if (queryPart.left(6) == "?body=")
01659         // It is correct to convert to latin1() as URL should not contain
01660         // anything except ascii.
01661         msg->setBody( KURL::decode_string(queryPart.mid(6)).latin1() );
01662       else if (queryPart.left(4) == "?cc=")
01663         msg->setCc( KURL::decode_string(queryPart.mid(4)) );
01664     }
01665 
01666     win = new KMComposeWin(msg, mIdentity);
01667     win->setCharset("", TRUE);
01668     win->show();
01669   }
01670   else if ((mUrl.protocol() == "http") || (mUrl.protocol() == "https") ||
01671            (mUrl.protocol() == "ftp") || (mUrl.protocol() == "file") ||
01672            (mUrl.protocol() == "ftps") || (mUrl.protocol() == "sftp" ) ||
01673            (mUrl.protocol() == "help") || (mUrl.protocol() == "vnc") ||
01674            (mUrl.protocol() == "smb"))
01675   {
01676     if (mMainWidget)
01677       mMainWidget->statusMsg( i18n("Opening URL..."));
01678     KMimeType::Ptr mime = KMimeType::findByURL( mUrl );
01679     if (mime->name() == "application/x-desktop" ||
01680         mime->name() == "application/x-executable" ||
01681         mime->name() == "application/x-msdos-program" ||
01682         mime->name() == "application/x-shellscript" )
01683     {
01684       if (KMessageBox::warningYesNo( 0, i18n( "<qt>Do you really want to execute <b>%1</b>?</qt>" )
01685         .arg( mUrl.prettyURL() ) ) != KMessageBox::Yes) return;
01686     }
01687     (void) new KRun( mUrl );
01688   }
01689 }
01690 
01691 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, KMMessage *msg )
01692   : KMCommand( parent, msg ), mParent( parent ), mEncoded( false )
01693 {
01694 }
01695 
01696 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, const QPtrList<KMMsgBase>& msgs )
01697   : KMCommand( parent, msgs ), mParent( parent ), mEncoded( false )
01698 {
01699 }
01700 
01701 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, QPtrList<partNode>& attachments,
01702                                                     KMMessage *msg, bool encoded )
01703   : KMCommand( parent, msg ), mParent( parent ), mAttachments( attachments ), mEncoded( encoded )
01704 {
01705   // do not load the complete message but only parts
01706   mMessageComplete = msg->isComplete();
01707   msg->setComplete( true );
01708   setDeletesItself( true );
01709 }
01710 
01711 void KMSaveAttachmentsCommand::execute()
01712 {
01713   QPtrList<KMMessage> lst = retrievedMsgs();
01714   if ( lst.count() == 1 ) // restore original complete state
01715     lst.first()->setComplete(mMessageComplete);
01716   if ( mAttachments.count() > 0 )
01717   {
01718     saveAll( mAttachments );
01719     return;
01720   }
01721   KMMessage *msg = 0;
01722   QPtrListIterator<KMMessage> itr( lst );
01723 
01724   while ( itr.current() ) {
01725     msg = itr.current();
01726     ++itr;
01727     QCString type = msg->typeStr();
01728 
01729     int mainType    = msg->type();
01730     int mainSubType = msg->subtype();
01731     DwBodyPart* mainBody = 0;
01732     DwBodyPart* firstBodyPart = msg->getFirstDwBodyPart();
01733     if( !firstBodyPart ) {
01734       // ATTENTION: This definitely /should/ be optimized.
01735       //            Copying the message text into a new body part
01736       //            surely is not the most efficient way to go.
01737       //            I decided to do so for being able to get a
01738       //            solution working for old style (== non MIME)
01739       //            mails without spending much time on implementing.
01740       //            During code revisal when switching to KMime
01741       //            all this will probably disappear anyway (or it
01742       //            will be optimized, resp.).       (khz, 6.12.2001)
01743       kdDebug(5006) << "*no* first body part found, creating one from Message" << endl;
01744       mainBody = new DwBodyPart( msg->asDwString(), 0 );
01745       mainBody->Parse();
01746     }
01747     partNode *rootNode = new partNode( mainBody, mainType, mainSubType, true );
01748     rootNode->setFromAddress( msg->from() );
01749 
01750     if ( firstBodyPart ) {
01751       partNode* curNode = new partNode(firstBodyPart);
01752       rootNode->setFirstChild( curNode );
01753       curNode->buildObjectTree();
01754     }
01755     parse( rootNode );
01756   }
01757 }
01758 
01759 void KMSaveAttachmentsCommand::parse( partNode *rootNode )
01760 {
01761   QPtrList<partNode> attachments;
01762   for( partNode *child = rootNode; child; child = child->firstChild() ) {
01763     for( partNode *tmp = child; tmp; tmp = tmp->nextSibling() ) {
01764       attachments.append( tmp );
01765     }
01766   }
01767   saveAll( attachments );
01768 }
01769 
01770 void KMSaveAttachmentsCommand::saveAll( const QPtrList<partNode>& attachments )
01771 {
01772   if ( attachments.isEmpty() ) {
01773     KMessageBox::information( 0, i18n("Found no attachments to save.") );
01774     return;
01775   }
01776   mAttachments = attachments;
01777   // load all parts
01778   KMLoadPartsCommand *command = new KMLoadPartsCommand( mAttachments, retrievedMessage() );
01779   connect( command, SIGNAL( partsRetrieved() ),
01780       this, SLOT( slotSaveAll() ) );
01781   command->start();
01782 }
01783 
01784 void KMSaveAttachmentsCommand::slotSaveAll()
01785 {
01786   QPtrListIterator<partNode> itr( mAttachments );
01787 
01788   QString dir, file;
01789   if ( mAttachments.count() > 1 )
01790   {
01791     // get the dir
01792     KFileDialog fdlg( QString::null, QString::null, mParent, 0, true );
01793     fdlg.setMode( (unsigned int) KFile::Directory );
01794     if ( !fdlg.exec() ) return;
01795     dir = fdlg.selectedURL().path();
01796   }
01797   else {
01798     // only one item, get the desired filename
01799     // replace all ':' with '_' because ':' isn't allowed on FAT volumes
01800     QString s =
01801       (*itr)->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
01802     if ( s.isEmpty() )
01803       s = (*itr)->msgPart().name().stripWhiteSpace().replace( ':', '_' );
01804     if ( s.isEmpty() )
01805       s = "unnamed"; // ### this should probably be i18n'ed
01806     file = KFileDialog::getSaveFileName( s, QString::null, mParent,
01807                                          QString::null );
01808   }
01809 
01810   while ( itr.current() ) {
01811     QString s;
01812     QString filename;
01813     if ( !dir.isEmpty() ) {
01814       s = (*itr)->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
01815       if ( s.isEmpty() )
01816         s = (*itr)->msgPart().name().stripWhiteSpace().replace( ':', '_' );
01817       // Check if it has the Content-Disposition... filename: header
01818       // to make sure it's an actual attachment
01819       // we can't do the check earlier as we first need to load the mimeheader
01820       // for imap attachments to do this check
01821       if ( s.isEmpty() ) {
01822         ++itr;
01823         continue;
01824       }
01825       filename = dir + "/" + s;
01826     }
01827     else
01828       filename = file;
01829 
01830     if( !filename.isEmpty() ) {
01831       if( QFile::exists( filename ) ) {
01832         if( KMessageBox::warningYesNo( mParent,
01833                                        i18n( "A file named %1 already exists. Do you want to overwrite it?" ).arg( s.isEmpty() ? filename : s ),
01834                                        i18n( "KMail Warning" ) ) ==
01835             KMessageBox::No ) {
01836           ++itr;
01837           continue;
01838         }
01839       }
01840       saveItem( itr.current(), filename );
01841     }
01842     ++itr;
01843   }
01844 }
01845 
01846 void KMSaveAttachmentsCommand::saveItem( partNode *node, const QString& filename )
01847 {
01848   if ( node && !filename.isEmpty() ) {
01849     bool bSaveEncrypted = false;
01850     bool bEncryptedParts = node->encryptionState() != KMMsgNotEncrypted;
01851     if( bEncryptedParts )
01852       if( KMessageBox::questionYesNo( mParent,
01853                                       i18n( "This part of the message is encrypted. Do you want to keep the encryption when saving?" ),
01854                                       i18n( "KMail Question" ) ) ==
01855           KMessageBox::Yes )
01856         bSaveEncrypted = true;
01857 
01858     bool bSaveWithSig = true;
01859     if( node->signatureState() != KMMsgNotSigned )
01860       if( KMessageBox::questionYesNo( mParent,
01861                                       i18n( "This part of the message is signed. Do you want to keep the signature when saving?" ),
01862                                       i18n( "KMail Question" ) ) !=
01863           KMessageBox::Yes )
01864         bSaveWithSig = false;
01865 
01866     QFile file( filename );
01867     if( file.open( IO_WriteOnly ) ) {
01868       fchmod( file.handle(), S_IRUSR | S_IWUSR );
01869       if ( mEncoded )
01870       {
01871         // This does not decode the Message Content-Transfer-Encoding
01872         // but saves the _original_ content of the message part
01873         QDataStream ds( &file );
01874         QCString cstr( node->msgPart().body() );
01875         ds.writeRawBytes( cstr, cstr.size() );
01876       }
01877       else
01878       {
01879         QDataStream ds( &file );
01880         if( bSaveEncrypted || !bEncryptedParts) {
01881           partNode *dataNode = node;
01882           if( !bSaveWithSig ) {
01883             if( DwMime::kTypeMultipart == node->type() &&
01884                 DwMime::kSubtypeSigned == node->subType() ){
01885               // carefully look for the part that is *not* the signature part:
01886               if( node->findType( DwMime::kTypeApplication,
01887                                   DwMime::kSubtypePgpSignature,
01888                                   TRUE, false ) ){
01889                 dataNode = node->findTypeNot( DwMime::kTypeApplication,
01890                                               DwMime::kSubtypePgpSignature,
01891                                               TRUE, false );
01892               }else if( node->findType( DwMime::kTypeApplication,
01893                                         DwMime::kSubtypePkcs7Mime,
01894                                   TRUE, false ) ){
01895                 dataNode = node->findTypeNot( DwMime::kTypeApplication,
01896                                               DwMime::kSubtypePkcs7Mime,
01897                                               TRUE, false );
01898               }else{
01899                 dataNode = node->findTypeNot( DwMime::kTypeMultipart,
01900                                               DwMime::kSubtypeUnknown,
01901                                               TRUE, false );
01902               }
01903             }
01904           }
01905           QByteArray cstr = dataNode->msgPart().bodyDecodedBinary();
01906           size_t size = cstr.size();
01907           if ( dataNode->msgPart().type() == DwMime::kTypeText ) {
01908             // convert CRLF to LF before writing text attachments to disk
01909             size = KMFolder::crlf2lf( cstr.data(), size );
01910           }
01911           ds.writeRawBytes( cstr.data(), size );
01912         }
01913       }
01914       file.close();
01915     } else
01916       // FIXME: After string freeze is over:
01917       // KMessageBox::error( mParent,
01918       //                     i18n( "%1 is detailed error description",
01919       //                           "Could not write the file:\n%1" )
01920       //                      .arg( QString::fromLocal8Bit( strerror( errno ) ) ),
01921       //                     i18n( "KMail Error" ) );
01922       KMessageBox::error( mParent,
01923                           i18n( "Could not write the file." ) + "\n"
01924                           + QString::fromLocal8Bit( strerror( errno ) ),
01925                           i18n( "KMail Error" ) );
01926   }
01927 }
01928 
01929 KMLoadPartsCommand::KMLoadPartsCommand( QPtrList<partNode>& parts, KMMessage *msg )
01930     : mParts( parts ), mNeedsRetrieval( 0 ), mMsg( msg )
01931 {
01932 }
01933 
01934 KMLoadPartsCommand::KMLoadPartsCommand( partNode* node, KMMessage *msg )
01935     : mNeedsRetrieval( 0 ), mMsg( msg )
01936 {
01937   mParts.append( node );
01938 }
01939 
01940 void KMLoadPartsCommand::start()
01941 {
01942   QPtrListIterator<partNode> it( mParts );
01943   while ( it.current() )
01944   {
01945     if ( !it.current()->msgPart().isComplete() &&
01946          !it.current()->msgPart().partSpecifier().isEmpty() )
01947     {
01948       // incomplete part so retrieve it first
01949       ++mNeedsRetrieval;
01950       KMFolder* curFolder = mMsg->parent();
01951       if ( curFolder )
01952       {
01953         FolderJob *job = curFolder->createJob( mMsg, FolderJob::tGetMessage,
01954             0, it.current()->msgPart().partSpecifier() );
01955         connect( job, SIGNAL(messageUpdated(KMMessage*, QString)),
01956             this, SLOT(slotPartRetrieved(KMMessage*, QString)) );
01957         job->start();
01958       } else
01959         kdWarning(5006) << "KMLoadPartsCommand - msg has no parent" << endl;
01960     }
01961     ++it;
01962   }
01963   if ( mNeedsRetrieval == 0 )
01964     execute();
01965 }
01966 
01967 void KMLoadPartsCommand::slotPartRetrieved( KMMessage* msg, QString partSpecifier )
01968 {
01969   DwBodyPart* part = msg->findDwBodyPart( msg->getFirstDwBodyPart(), partSpecifier );
01970   if ( part )
01971   {
01972     // update the DwBodyPart in the partNode
01973     QPtrListIterator<partNode> it( mParts );
01974     while ( it.current() )
01975     {
01976       if ( it.current()->dwPart() == part )
01977         it.current()->setDwPart( part );
01978       ++it;
01979     }
01980   } else
01981     kdWarning(5006) << "KMLoadPartsCommand::slotPartRetrieved - could not find bodypart!" << endl;
01982   --mNeedsRetrieval;
01983   if ( mNeedsRetrieval == 0 )
01984     execute();
01985 }
01986 
01987 void KMLoadPartsCommand::execute()
01988 {
01989   emit partsRetrieved();
01990   delete this;
01991 }
01992 
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:37:20 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003