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 00029 #include "kmcommands.h" 00030 00031 #ifdef HAVE_CONFIG_H 00032 #include <config.h> 00033 #endif 00034 00035 #include <errno.h> 00036 #include <mimelib/enum.h> 00037 #include <mimelib/field.h> 00038 #include <mimelib/mimepp.h> 00039 #include <mimelib/string.h> 00040 #include <kapplication.h> 00041 #include <dcopclient.h> 00042 00043 #include <qtextcodec.h> 00044 00045 #include <libkdepim/email.h> 00046 #include <kdebug.h> 00047 #include <kfiledialog.h> 00048 #include <kio/netaccess.h> 00049 #include <kabc/stdaddressbook.h> 00050 #include <kabc/addresseelist.h> 00051 #include <klocale.h> 00052 #include <kmessagebox.h> 00053 #include <kparts/browserextension.h> 00054 #include <kprogress.h> 00055 #include <krun.h> 00056 #include <kbookmarkmanager.h> 00057 #include <kstandarddirs.h> 00058 #include <ktempfile.h> 00059 #include "actionscheduler.h" 00060 using KMail::ActionScheduler; 00061 #include "mailinglist-magic.h" 00062 #include "kmaddrbook.h" 00063 #include <kaddrbook.h> 00064 #include "kmcomposewin.h" 00065 #include "kmfiltermgr.h" 00066 #include "kmfoldermbox.h" 00067 #include "kmfolderimap.h" 00068 #include "kmfoldermgr.h" 00069 #include "kmheaders.h" 00070 #include "kmmainwidget.h" 00071 #include "kmmsgdict.h" 00072 #include "kmsender.h" 00073 #include "undostack.h" 00074 #include "kcursorsaver.h" 00075 #include "partNode.h" 00076 #include "objecttreeparser.h" 00077 using KMail::ObjectTreeParser; 00078 using KMail::FolderJob; 00079 #include "mailsourceviewer.h" 00080 using KMail::MailSourceViewer; 00081 #include "kmreadermainwin.h" 00082 #include "secondarywindow.h" 00083 using KMail::SecondaryWindow; 00084 #include "kimproxy.h" 00085 00086 #include "progressmanager.h" 00087 using KPIM::ProgressManager; 00088 using KPIM::ProgressItem; 00089 00090 #include "broadcaststatus.h" 00091 00092 #include "kmcommands.moc" 00093 00094 KMCommand::KMCommand( QWidget *parent ) 00095 : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ), 00096 mEmitsCompletedItself( false ), mParent( parent ) 00097 { 00098 } 00099 00100 KMCommand::KMCommand( QWidget *parent, const QPtrList<KMMsgBase> &msgList ) 00101 : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ), 00102 mEmitsCompletedItself( false ), mParent( parent ), mMsgList( msgList ) 00103 { 00104 } 00105 00106 KMCommand::KMCommand( QWidget *parent, KMMsgBase *msgBase ) 00107 : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ), 00108 mEmitsCompletedItself( false ), mParent( parent ) 00109 { 00110 mMsgList.append( msgBase ); 00111 } 00112 00113 KMCommand::KMCommand( QWidget *parent, KMMessage *msg ) 00114 : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ), 00115 mEmitsCompletedItself( false ), mParent( parent ) 00116 { 00117 mMsgList.append( &msg->toMsgBase() ); 00118 } 00119 00120 KMCommand::~KMCommand() 00121 { 00122 QValueListIterator<QGuardedPtr<KMFolder> > fit; 00123 for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) { 00124 if (!(*fit)) 00125 continue; 00126 (*fit)->close(); 00127 } 00128 } 00129 00130 KMCommand::Result KMCommand::result() 00131 { 00132 if ( mResult == Undefined ) 00133 kdDebug(5006) << k_funcinfo << "mResult is Undefined" << endl; 00134 return mResult; 00135 } 00136 00137 void KMCommand::start() 00138 { 00139 QTimer::singleShot( 0, this, SLOT( slotStart() ) ); 00140 } 00141 00142 00143 const QPtrList<KMMessage> KMCommand::retrievedMsgs() const 00144 { 00145 return mRetrievedMsgs; 00146 } 00147 00148 KMMessage *KMCommand::retrievedMessage() const 00149 { 00150 return mRetrievedMsgs.getFirst(); 00151 } 00152 00153 QWidget *KMCommand::parentWidget() const 00154 { 00155 return mParent; 00156 } 00157 00158 int KMCommand::mCountJobs = 0; 00159 00160 void KMCommand::slotStart() 00161 { 00162 connect( this, SIGNAL( messagesTransfered( KMCommand::Result ) ), 00163 this, SLOT( slotPostTransfer( KMCommand::Result ) ) ); 00164 kmkernel->filterMgr()->ref(); 00165 00166 if (mMsgList.find(0) != -1) { 00167 emit messagesTransfered( Failed ); 00168 return; 00169 } 00170 00171 if ((mMsgList.count() == 1) && 00172 (mMsgList.getFirst()->isMessage()) && 00173 (mMsgList.getFirst()->parent() == 0)) 00174 { 00175 // Special case of operating on message that isn't in a folder 00176 mRetrievedMsgs.append((KMMessage*)mMsgList.getFirst()); 00177 emit messagesTransfered( OK ); 00178 return; 00179 } 00180 00181 for (KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next()) 00182 if (!mb->parent()) { 00183 emit messagesTransfered( Failed ); 00184 return; 00185 } else { 00186 mFolders.append( mb->parent() ); 00187 mb->parent()->open(); 00188 } 00189 00190 // transfer the selected messages first 00191 transferSelectedMsgs(); 00192 } 00193 00194 void KMCommand::slotPostTransfer( KMCommand::Result result ) 00195 { 00196 disconnect( this, SIGNAL( messagesTransfered( KMCommand::Result ) ), 00197 this, SLOT( slotPostTransfer( KMCommand::Result ) ) ); 00198 if ( result == OK ) 00199 result = execute(); 00200 mResult = result; 00201 QPtrListIterator<KMMessage> it( mRetrievedMsgs ); 00202 KMMessage* msg; 00203 while ( (msg = it.current()) != 0 ) 00204 { 00205 ++it; 00206 if (msg->parent()) 00207 msg->setTransferInProgress(false); 00208 } 00209 kmkernel->filterMgr()->deref(); 00210 if ( !emitsCompletedItself() ) 00211 emit completed( this ); 00212 if ( !deletesItself() ) 00213 delete this; 00214 } 00215 00216 void KMCommand::transferSelectedMsgs() 00217 { 00218 // make sure no other transfer is active 00219 if (KMCommand::mCountJobs > 0) { 00220 emit messagesTransfered( Failed ); 00221 return; 00222 } 00223 00224 bool complete = true; 00225 KMCommand::mCountJobs = 0; 00226 mCountMsgs = 0; 00227 mRetrievedMsgs.clear(); 00228 mCountMsgs = mMsgList.count(); 00229 uint totalSize = 0; 00230 // the KProgressDialog for the user-feedback. Only enable it if it's needed. 00231 // For some commands like KMSetStatusCommand it's not needed. Note, that 00232 // for some reason the KProgressDialog eats the MouseReleaseEvent (if a 00233 // command is executed after the MousePressEvent), cf. bug #71761. 00234 if ( mCountMsgs > 0 ) { 00235 mProgressDialog = new KProgressDialog(mParent, "transferProgress", 00236 i18n("Please wait"), 00237 i18n("Please wait while the message is transferred", 00238 "Please wait while the %n messages are transferred", mMsgList.count()), 00239 true); 00240 mProgressDialog->setMinimumDuration(1000); 00241 } 00242 for (KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next()) 00243 { 00244 // check if all messages are complete 00245 KMMessage *thisMsg = 0; 00246 if ( mb->isMessage() ) 00247 thisMsg = static_cast<KMMessage*>(mb); 00248 else 00249 { 00250 KMFolder *folder = mb->parent(); 00251 int idx = folder->find(mb); 00252 if (idx < 0) continue; 00253 thisMsg = folder->getMsg(idx); 00254 } 00255 if (!thisMsg) continue; 00256 if ( thisMsg->transferInProgress() && 00257 thisMsg->parent()->folderType() == KMFolderTypeImap ) 00258 { 00259 thisMsg->setTransferInProgress( false, true ); 00260 thisMsg->parent()->ignoreJobsForMessage( thisMsg ); 00261 } 00262 00263 if ( thisMsg->parent() && !thisMsg->isComplete() && 00264 ( !mProgressDialog || !mProgressDialog->wasCancelled() ) ) 00265 { 00266 kdDebug(5006)<<"### INCOMPLETE\n"; 00267 // the message needs to be transferred first 00268 complete = false; 00269 KMCommand::mCountJobs++; 00270 FolderJob *job = thisMsg->parent()->createJob(thisMsg); 00271 job->setCancellable( false ); 00272 totalSize += thisMsg->msgSizeServer(); 00273 // emitted when the message was transferred successfully 00274 connect(job, SIGNAL(messageRetrieved(KMMessage*)), 00275 this, SLOT(slotMsgTransfered(KMMessage*))); 00276 // emitted when the job is destroyed 00277 connect(job, SIGNAL(finished()), 00278 this, SLOT(slotJobFinished())); 00279 connect(job, SIGNAL(progress(unsigned long, unsigned long)), 00280 this, SLOT(slotProgress(unsigned long, unsigned long))); 00281 // msg musn't be deleted 00282 thisMsg->setTransferInProgress(true); 00283 job->start(); 00284 } else { 00285 thisMsg->setTransferInProgress(true); 00286 mRetrievedMsgs.append(thisMsg); 00287 } 00288 } 00289 00290 if (complete) 00291 { 00292 delete mProgressDialog; 00293 emit messagesTransfered( OK ); 00294 } else { 00295 // wait for the transfer and tell the progressBar the necessary steps 00296 if ( mProgressDialog ) { 00297 connect(mProgressDialog, SIGNAL(cancelClicked()), 00298 this, SLOT(slotTransferCancelled())); 00299 mProgressDialog->progressBar()->setTotalSteps(totalSize); 00300 } 00301 } 00302 } 00303 00304 void KMCommand::slotMsgTransfered(KMMessage* msg) 00305 { 00306 if ( mProgressDialog && mProgressDialog->wasCancelled() ) { 00307 emit messagesTransfered( Canceled ); 00308 return; 00309 } 00310 00311 // save the complete messages 00312 mRetrievedMsgs.append(msg); 00313 } 00314 00315 void KMCommand::slotProgress( unsigned long done, unsigned long /*total*/ ) 00316 { 00317 mProgressDialog->progressBar()->setProgress( done ); 00318 } 00319 00320 void KMCommand::slotJobFinished() 00321 { 00322 // the job is finished (with / without error) 00323 KMCommand::mCountJobs--; 00324 00325 if ( mProgressDialog && mProgressDialog->wasCancelled() ) return; 00326 00327 if ( (mCountMsgs - static_cast<int>(mRetrievedMsgs.count())) > KMCommand::mCountJobs ) 00328 { 00329 // the message wasn't retrieved before => error 00330 if ( mProgressDialog ) 00331 mProgressDialog->hide(); 00332 slotTransferCancelled(); 00333 return; 00334 } 00335 // update the progressbar 00336 if ( mProgressDialog ) { 00337 mProgressDialog->setLabel(i18n("Please wait while the message is transferred", 00338 "Please wait while the %n messages are transferred", KMCommand::mCountJobs)); 00339 } 00340 if (KMCommand::mCountJobs == 0) 00341 { 00342 // all done 00343 delete mProgressDialog; 00344 emit messagesTransfered( OK ); 00345 } 00346 } 00347 00348 void KMCommand::slotTransferCancelled() 00349 { 00350 // kill the pending jobs 00351 QValueListIterator<QGuardedPtr<KMFolder> > fit; 00352 for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) { 00353 if (!(*fit)) 00354 continue; 00355 KMFolder *folder = *fit; 00356 KMFolderImap *imapFolder = dynamic_cast<KMFolderImap*>(folder); 00357 if (imapFolder && imapFolder->account()) { 00358 imapFolder->account()->killAllJobs(); 00359 } 00360 } 00361 00362 KMCommand::mCountJobs = 0; 00363 mCountMsgs = 0; 00364 // unget the transfered messages 00365 QPtrListIterator<KMMessage> it( mRetrievedMsgs ); 00366 KMMessage* msg; 00367 while ( (msg = it.current()) != 0 ) 00368 { 00369 KMFolder *folder = msg->parent(); 00370 ++it; 00371 if (!folder) 00372 continue; 00373 msg->setTransferInProgress(false); 00374 int idx = folder->find(msg); 00375 if (idx > 0) folder->unGetMsg(idx); 00376 } 00377 mRetrievedMsgs.clear(); 00378 emit messagesTransfered( Canceled ); 00379 } 00380 00381 void KMCommand::keepFolderOpen( KMFolder *folder ) 00382 { 00383 folder->open(); 00384 mFolders.append( folder ); 00385 } 00386 00387 KMMailtoComposeCommand::KMMailtoComposeCommand( const KURL &url, 00388 KMMessage *msg ) 00389 :mUrl( url ), mMessage( msg ) 00390 { 00391 } 00392 00393 KMCommand::Result KMMailtoComposeCommand::execute() 00394 { 00395 KMComposeWin *win; 00396 KMMessage *msg = new KMMessage; 00397 uint id = 0; 00398 00399 if ( mMessage && mMessage->parent() ) 00400 id = mMessage->parent()->identity(); 00401 00402 msg->initHeader(id); 00403 msg->setCharset("utf-8"); 00404 msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) ); 00405 00406 win = new KMComposeWin(msg, id); 00407 win->setCharset("", TRUE); 00408 win->setFocusToSubject(); 00409 win->show(); 00410 00411 return OK; 00412 } 00413 00414 00415 KMMailtoReplyCommand::KMMailtoReplyCommand( QWidget *parent, 00416 const KURL &url, KMMessage *msg, const QString &selection ) 00417 :KMCommand( parent, msg ), mUrl( url ), mSelection( selection ) 00418 { 00419 } 00420 00421 KMCommand::Result KMMailtoReplyCommand::execute() 00422 { 00423 //TODO : consider factoring createReply into this method. 00424 KMMessage *msg = retrievedMessage(); 00425 KMComposeWin *win; 00426 KMMessage *rmsg = msg->createReply( KMail::ReplyNone, mSelection ); 00427 rmsg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) ); 00428 00429 win = new KMComposeWin(rmsg, 0); 00430 win->setCharset(msg->codec()->mimeName(), TRUE); 00431 win->setReplyFocus(); 00432 win->show(); 00433 00434 return OK; 00435 } 00436 00437 00438 KMMailtoForwardCommand::KMMailtoForwardCommand( QWidget *parent, 00439 const KURL &url, KMMessage *msg ) 00440 :KMCommand( parent, msg ), mUrl( url ) 00441 { 00442 } 00443 00444 KMCommand::Result KMMailtoForwardCommand::execute() 00445 { 00446 //TODO : consider factoring createForward into this method. 00447 KMMessage *msg = retrievedMessage(); 00448 KMComposeWin *win; 00449 KMMessage *fmsg = msg->createForward(); 00450 fmsg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) ); 00451 00452 win = new KMComposeWin(fmsg); 00453 win->setCharset(msg->codec()->mimeName(), TRUE); 00454 win->show(); 00455 00456 return OK; 00457 } 00458 00459 00460 KMAddBookmarksCommand::KMAddBookmarksCommand( const KURL &url, QWidget *parent ) 00461 : KMCommand( parent ), mUrl( url ) 00462 { 00463 } 00464 00465 KMCommand::Result KMAddBookmarksCommand::execute() 00466 { 00467 QString filename = locateLocal( "data", QString::fromLatin1("konqueror/bookmarks.xml") ); 00468 KBookmarkManager *bookManager = KBookmarkManager::managerForFile( filename, 00469 false ); 00470 KBookmarkGroup group = bookManager->root(); 00471 group.addBookmark( bookManager, mUrl.path(), KURL( mUrl ) ); 00472 if( bookManager->save() ) { 00473 bookManager->emitChanged( group ); 00474 } 00475 00476 return OK; 00477 } 00478 00479 KMMailtoAddAddrBookCommand::KMMailtoAddAddrBookCommand( const KURL &url, 00480 QWidget *parent ) 00481 : KMCommand( parent ), mUrl( url ) 00482 { 00483 } 00484 00485 KMCommand::Result KMMailtoAddAddrBookCommand::execute() 00486 { 00487 KAddrBookExternal::addEmail( KMMessage::decodeMailtoUrl( mUrl.path() ), 00488 parentWidget() ); 00489 00490 return OK; 00491 } 00492 00493 00494 KMMailtoOpenAddrBookCommand::KMMailtoOpenAddrBookCommand( const KURL &url, 00495 QWidget *parent ) 00496 : KMCommand( parent ), mUrl( url ) 00497 { 00498 } 00499 00500 KMCommand::Result KMMailtoOpenAddrBookCommand::execute() 00501 { 00502 QString addr = KMMessage::decodeMailtoUrl( mUrl.path() ); 00503 KAddrBookExternal::openEmail( KPIM::getEmailAddr(addr), addr , 00504 parentWidget() ); 00505 00506 return OK; 00507 } 00508 00509 00510 KMUrlCopyCommand::KMUrlCopyCommand( const KURL &url, KMMainWidget *mainWidget ) 00511 :mUrl( url ), mMainWidget( mainWidget ) 00512 { 00513 } 00514 00515 KMCommand::Result KMUrlCopyCommand::execute() 00516 { 00517 QClipboard* clip = QApplication::clipboard(); 00518 00519 if (mUrl.protocol() == "mailto") { 00520 // put the url into the mouse selection and the clipboard 00521 QString address = KMMessage::decodeMailtoUrl( mUrl.path() ); 00522 clip->setSelectionMode( true ); 00523 clip->setText( address ); 00524 clip->setSelectionMode( false ); 00525 clip->setText( address ); 00526 KPIM::BroadcastStatus::instance()->setStatusMsg( i18n( "Address copied to clipboard." )); 00527 } else { 00528 // put the url into the mouse selection and the clipboard 00529 clip->setSelectionMode( true ); 00530 clip->setText( mUrl.url() ); 00531 clip->setSelectionMode( false ); 00532 clip->setText( mUrl.url() ); 00533 KPIM::BroadcastStatus::instance()->setStatusMsg( i18n( "URL copied to clipboard." )); 00534 } 00535 00536 return OK; 00537 } 00538 00539 00540 KMUrlOpenCommand::KMUrlOpenCommand( const KURL &url, KMReaderWin *readerWin ) 00541 :mUrl( url ), mReaderWin( readerWin ) 00542 { 00543 } 00544 00545 KMCommand::Result KMUrlOpenCommand::execute() 00546 { 00547 if ( !mUrl.isEmpty() ) 00548 mReaderWin->slotUrlOpen( mUrl, KParts::URLArgs() ); 00549 00550 return OK; 00551 } 00552 00553 00554 KMUrlSaveCommand::KMUrlSaveCommand( const KURL &url, QWidget *parent ) 00555 : KMCommand( parent ), mUrl( url ) 00556 { 00557 } 00558 00559 KMCommand::Result KMUrlSaveCommand::execute() 00560 { 00561 if ( mUrl.isEmpty() ) 00562 return OK; 00563 KURL saveUrl = KFileDialog::getSaveURL(mUrl.fileName(), QString::null, 00564 parentWidget() ); 00565 if ( saveUrl.isEmpty() ) 00566 return Canceled; 00567 if ( KIO::NetAccess::exists( saveUrl, false, parentWidget() ) ) 00568 { 00569 if (KMessageBox::warningContinueCancel(0, 00570 i18n("<qt>File <b>%1</b> exists.<br>Do you want to replace it?</qt>") 00571 .arg(saveUrl.prettyURL()), i18n("Save to File"), i18n("&Replace")) 00572 != KMessageBox::Continue) 00573 return Canceled; 00574 } 00575 KIO::Job *job = KIO::file_copy(mUrl, saveUrl, -1, true); 00576 connect(job, SIGNAL(result(KIO::Job*)), SLOT(slotUrlSaveResult(KIO::Job*))); 00577 setEmitsCompletedItself( true ); 00578 return OK; 00579 } 00580 00581 void KMUrlSaveCommand::slotUrlSaveResult( KIO::Job *job ) 00582 { 00583 if ( job->error() ) { 00584 job->showErrorDialog(); 00585 setResult( Failed ); 00586 emit completed( this ); 00587 } 00588 else { 00589 setResult( OK ); 00590 emit completed( this ); 00591 } 00592 } 00593 00594 00595 KMEditMsgCommand::KMEditMsgCommand( QWidget *parent, KMMessage *msg ) 00596 :KMCommand( parent, msg ) 00597 { 00598 } 00599 00600 KMCommand::Result KMEditMsgCommand::execute() 00601 { 00602 KMMessage *msg = retrievedMessage(); 00603 if (!msg || !msg->parent() || 00604 !kmkernel->folderIsDraftOrOutbox( msg->parent() )) 00605 return Failed; 00606 00607 // Remember the old parent, we need it a bit further down to be able 00608 // to put the unchanged messsage back in the drafts folder if the nth 00609 // edit is discarded, for n > 1. 00610 KMFolder *parent = msg->parent(); 00611 if ( parent ) 00612 parent->take( parent->find( msg ) ); 00613 00614 KMComposeWin *win = new KMComposeWin(); 00615 msg->setTransferInProgress(false); // From here on on, the composer owns the message. 00616 win->setMsg(msg, FALSE, TRUE); 00617 win->setFolder( parent ); 00618 win->show(); 00619 00620 return OK; 00621 } 00622 00623 00624 KMShowMsgSrcCommand::KMShowMsgSrcCommand( KMMessage *msg, bool fixedFont ) 00625 : mFixedFont( fixedFont ), mMsg ( msg ) 00626 { 00627 } 00628 00629 void KMShowMsgSrcCommand::start() 00630 { 00631 QString str = mMsg->codec()->toUnicode( mMsg->asString() ); 00632 00633 MailSourceViewer *viewer = new MailSourceViewer(); // deletes itself upon close 00634 viewer->setCaption( i18n("Message as Plain Text") ); 00635 viewer->setText(str); 00636 if( mFixedFont ) 00637 viewer->setFont(KGlobalSettings::fixedFont()); 00638 00639 // Well, there is no widget to be seen here, so we have to use QCursor::pos() 00640 // Update: (GS) I'm not going to make this code behave according to Xinerama 00641 // configuration because this is quite the hack. 00642 if (QApplication::desktop()->isVirtualDesktop()) { 00643 int scnum = QApplication::desktop()->screenNumber(QCursor::pos()); 00644 viewer->resize(QApplication::desktop()->screenGeometry(scnum).width()/2, 00645 2*QApplication::desktop()->screenGeometry(scnum).height()/3); 00646 } else { 00647 viewer->resize(QApplication::desktop()->geometry().width()/2, 00648 2*QApplication::desktop()->geometry().height()/3); 00649 } 00650 viewer->show(); 00651 } 00652 00653 static KURL subjectToUrl( const QString & subject ) { 00654 return KFileDialog::getSaveURL( subject.stripWhiteSpace() 00655 .replace( QDir::separator(), '_' ), 00656 QString::null ); 00657 } 00658 00659 KMSaveMsgCommand::KMSaveMsgCommand( QWidget *parent, KMMessage * msg ) 00660 : KMCommand( parent ), 00661 mMsgListIndex( 0 ), 00662 mOffset( 0 ), 00663 mTotalSize( msg ? msg->msgSize() : 0 ) 00664 { 00665 if ( !msg ) return; 00666 setDeletesItself( true ); 00667 mMsgList.append( msg->getMsgSerNum() ); 00668 mUrl = subjectToUrl( msg->cleanSubject() ); 00669 } 00670 00671 KMSaveMsgCommand::KMSaveMsgCommand( QWidget *parent, 00672 const QPtrList<KMMsgBase> &msgList ) 00673 : KMCommand( parent ), 00674 mMsgListIndex( 0 ), 00675 mOffset( 0 ), 00676 mTotalSize( 0 ) 00677 { 00678 if (!msgList.getFirst()) 00679 return; 00680 setDeletesItself( true ); 00681 KMMsgBase *msgBase = msgList.getFirst(); 00682 00683 // We operate on serNums and not the KMMsgBase pointers, as those can 00684 // change, or become invalid when changing the current message, switching 00685 // folders, etc. 00686 QPtrListIterator<KMMsgBase> it(msgList); 00687 while ( it.current() ) { 00688 mMsgList.append( (*it)->getMsgSerNum() ); 00689 mTotalSize += (*it)->msgSize(); 00690 if ((*it)->parent() != 0) 00691 (*it)->parent()->open(); 00692 ++it; 00693 } 00694 mMsgListIndex = 0; 00695 mUrl = subjectToUrl( msgBase->cleanSubject() ); 00696 } 00697 00698 KURL KMSaveMsgCommand::url() 00699 { 00700 return mUrl; 00701 } 00702 00703 KMCommand::Result KMSaveMsgCommand::execute() 00704 { 00705 mJob = KIO::put( mUrl, S_IRUSR|S_IWUSR, false, false ); 00706 mJob->slotTotalSize( mTotalSize ); 00707 mJob->setAsyncDataEnabled( true ); 00708 mJob->setReportDataSent( true ); 00709 connect(mJob, SIGNAL(dataReq(KIO::Job*, QByteArray &)), 00710 SLOT(slotSaveDataReq())); 00711 connect(mJob, SIGNAL(result(KIO::Job*)), 00712 SLOT(slotSaveResult(KIO::Job*))); 00713 setEmitsCompletedItself( true ); 00714 return OK; 00715 } 00716 00717 void KMSaveMsgCommand::slotSaveDataReq() 00718 { 00719 int remainingBytes = mData.size() - mOffset; 00720 if ( remainingBytes > 0 ) { 00721 // eat leftovers first 00722 if ( remainingBytes > MAX_CHUNK_SIZE ) 00723 remainingBytes = MAX_CHUNK_SIZE; 00724 00725 QByteArray data; 00726 data.duplicate( mData.data() + mOffset, remainingBytes ); 00727 mJob->sendAsyncData( data ); 00728 mOffset += remainingBytes; 00729 return; 00730 } 00731 // No leftovers, process next message. 00732 if ( mMsgListIndex < mMsgList.size() ) { 00733 KMMessage *msg = 0; 00734 int idx = -1; 00735 KMFolder * p = 0; 00736 kmkernel->msgDict()->getLocation( mMsgList[mMsgListIndex], &p, &idx ); 00737 assert( p ); 00738 assert( idx >= 0 ); 00739 msg = p->getMsg(idx); 00740 00741 if (msg->transferInProgress()) { 00742 QByteArray data = QByteArray(); 00743 mJob->sendAsyncData( data ); 00744 } 00745 msg->setTransferInProgress( true ); 00746 if (msg->isComplete() ) { 00747 slotMessageRetrievedForSaving(msg); 00748 } else { 00749 // retrieve Message first 00750 if (msg->parent() && !msg->isComplete() ) { 00751 FolderJob *job = msg->parent()->createJob(msg); 00752 job->setCancellable( false ); 00753 connect(job, SIGNAL(messageRetrieved(KMMessage*)), 00754 this, SLOT(slotMessageRetrievedForSaving(KMMessage*))); 00755 job->start(); 00756 } 00757 } 00758 } else { 00759 // No more messages. Tell the putjob we are done. 00760 QByteArray data = QByteArray(); 00761 mJob->sendAsyncData( data ); 00762 } 00763 } 00764 00765 void KMSaveMsgCommand::slotMessageRetrievedForSaving(KMMessage *msg) 00766 { 00767 QCString str( msg->mboxMessageSeparator() ); 00768 str += KMFolderMbox::escapeFrom( msg->asString() ); 00769 str += "\n"; 00770 msg->setTransferInProgress(false); 00771 00772 mData = str; 00773 mData.resize(mData.size() - 1); 00774 mOffset = 0; 00775 QByteArray data; 00776 int size; 00777 // Unless it is great than 64 k send the whole message. kio buffers for us. 00778 if( mData.size() > (unsigned int) MAX_CHUNK_SIZE ) 00779 size = MAX_CHUNK_SIZE; 00780 else 00781 size = mData.size(); 00782 00783 data.duplicate( mData, size ); 00784 mJob->sendAsyncData( data ); 00785 mOffset += size; 00786 ++mMsgListIndex; 00787 // Get rid of the message. 00788 if (msg->parent()) { 00789 int idx = -1; 00790 KMFolder * p = 0; 00791 kmkernel->msgDict()->getLocation( msg, &p, &idx ); 00792 assert( p == msg->parent() ); assert( idx >= 0 ); 00793 p->unGetMsg( idx ); 00794 p->close(); 00795 } 00796 } 00797 00798 void KMSaveMsgCommand::slotSaveResult(KIO::Job *job) 00799 { 00800 if (job->error()) 00801 { 00802 if (job->error() == KIO::ERR_FILE_ALREADY_EXIST) 00803 { 00804 if (KMessageBox::warningContinueCancel(0, 00805 i18n("File %1 exists.\nDo you want to replace it?") 00806 .arg(mUrl.prettyURL()), i18n("Save to File"), i18n("&Replace")) 00807 == KMessageBox::Continue) { 00808 mOffset = 0; 00809 00810 mJob = KIO::put( mUrl, S_IRUSR|S_IWUSR, true, false ); 00811 mJob->slotTotalSize( mTotalSize ); 00812 mJob->setAsyncDataEnabled( true ); 00813 mJob->setReportDataSent( true ); 00814 connect(mJob, SIGNAL(dataReq(KIO::Job*, QByteArray &)), 00815 SLOT(slotSaveDataReq())); 00816 connect(mJob, SIGNAL(result(KIO::Job*)), 00817 SLOT(slotSaveResult(KIO::Job*))); 00818 } 00819 } 00820 else 00821 { 00822 job->showErrorDialog(); 00823 setResult( Failed ); 00824 emit completed( this ); 00825 delete this; 00826 } 00827 } else { 00828 setResult( OK ); 00829 emit completed( this ); 00830 delete this; 00831 } 00832 } 00833 00834 //----------------------------------------------------------------------------- 00835 00836 KMOpenMsgCommand::KMOpenMsgCommand( QWidget *parent, const KURL & url ) 00837 : KMCommand( parent ), 00838 mUrl( url ) 00839 { 00840 setDeletesItself( true ); 00841 } 00842 00843 KMCommand::Result KMOpenMsgCommand::execute() 00844 { 00845 if ( mUrl.isEmpty() ) { 00846 mUrl = KFileDialog::getOpenURL( ":OpenMessage", "message/rfc822", 00847 parentWidget(), i18n("Open Message") ); 00848 } 00849 if ( mUrl.isEmpty() ) { 00850 setDeletesItself( false ); 00851 return Canceled; 00852 } 00853 mJob = KIO::get( mUrl, false, false ); 00854 mJob->setReportDataSent( true ); 00855 connect( mJob, SIGNAL( data( KIO::Job *, const QByteArray & ) ), 00856 this, SLOT( slotDataArrived( KIO::Job*, const QByteArray & ) ) ); 00857 connect( mJob, SIGNAL( result( KIO::Job * ) ), 00858 SLOT( slotResult( KIO::Job * ) ) ); 00859 setEmitsCompletedItself( true ); 00860 return OK; 00861 } 00862 00863 void KMOpenMsgCommand::slotDataArrived( KIO::Job *, const QByteArray & data ) 00864 { 00865 if ( data.isEmpty() ) 00866 return; 00867 00868 mMsgString.append( data.data(), data.size() ); 00869 } 00870 00871 void KMOpenMsgCommand::slotResult( KIO::Job *job ) 00872 { 00873 if ( job->error() ) { 00874 // handle errors 00875 job->showErrorDialog(); 00876 setResult( Failed ); 00877 emit completed( this ); 00878 } 00879 else { 00880 int startOfMessage = 0; 00881 if ( mMsgString.compare( 0, 5, "From ", 5 ) == 0 ) { 00882 startOfMessage = mMsgString.find( '\n' ); 00883 if ( startOfMessage == -1 ) { 00884 KMessageBox::sorry( parentWidget(), 00885 i18n( "The file doesn't contain a message." ) ); 00886 setResult( Failed ); 00887 emit completed( this ); 00888 // Emulate closing of a secondary window so that KMail exits in case it 00889 // was started with the --view command line option. Otherwise an 00890 // invisible KMail would keep running. 00891 SecondaryWindow *win = new SecondaryWindow(); 00892 win->close(); 00893 win->deleteLater(); 00894 deleteLater(); 00895 return; 00896 } 00897 startOfMessage += 1; // the message starts after the '\n' 00898 } 00899 // check for multiple messages in the file 00900 bool multipleMessages = true; 00901 int endOfMessage = mMsgString.find( "\nFrom " ); 00902 if ( endOfMessage == -1 ) { 00903 endOfMessage = mMsgString.length(); 00904 multipleMessages = false; 00905 } 00906 DwMessage *dwMsg = new DwMessage; 00907 dwMsg->FromString( mMsgString.substr( startOfMessage, 00908 endOfMessage - startOfMessage ) ); 00909 dwMsg->Parse(); 00910 // check whether we have a message ( no headers => this isn't a message ) 00911 if ( dwMsg->Headers().NumFields() == 0 ) { 00912 KMessageBox::sorry( parentWidget(), 00913 i18n( "The file doesn't contain a message." ) ); 00914 delete dwMsg; dwMsg = 0; 00915 setResult( Failed ); 00916 emit completed( this ); 00917 // Emulate closing of a secondary window (see above). 00918 SecondaryWindow *win = new SecondaryWindow(); 00919 win->close(); 00920 win->deleteLater(); 00921 deleteLater(); 00922 return; 00923 } 00924 KMMessage *msg = new KMMessage( dwMsg ); 00925 msg->setReadyToShow( true ); 00926 KMReaderMainWin *win = new KMReaderMainWin(); 00927 win->showMsg( kmkernel->networkCodec(), msg ); 00928 win->show(); 00929 if ( multipleMessages ) 00930 KMessageBox::information( win, 00931 i18n( "The file contains multiple messages. " 00932 "Only the first message is shown." ) ); 00933 setResult( OK ); 00934 emit completed( this ); 00935 } 00936 delete this; 00937 } 00938 00939 //----------------------------------------------------------------------------- 00940 00941 //TODO: ReplyTo, NoQuoteReplyTo, ReplyList, ReplyToAll, ReplyAuthor 00942 // are all similar and should be factored 00943 KMReplyToCommand::KMReplyToCommand( QWidget *parent, KMMessage *msg, 00944 const QString &selection ) 00945 : KMCommand( parent, msg ), mSelection( selection ) 00946 { 00947 } 00948 00949 KMCommand::Result KMReplyToCommand::execute() 00950 { 00951 KCursorSaver busy(KBusyPtr::busy()); 00952 KMMessage *msg = retrievedMessage(); 00953 KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection ); 00954 KMComposeWin *win = new KMComposeWin( reply ); 00955 win->setCharset( msg->codec()->mimeName(), TRUE ); 00956 win->setReplyFocus(); 00957 win->show(); 00958 00959 return OK; 00960 } 00961 00962 00963 KMNoQuoteReplyToCommand::KMNoQuoteReplyToCommand( QWidget *parent, 00964 KMMessage *msg ) 00965 : KMCommand( parent, msg ) 00966 { 00967 } 00968 00969 KMCommand::Result KMNoQuoteReplyToCommand::execute() 00970 { 00971 KCursorSaver busy(KBusyPtr::busy()); 00972 KMMessage *msg = retrievedMessage(); 00973 KMMessage *reply = msg->createReply( KMail::ReplySmart, "", TRUE); 00974 KMComposeWin *win = new KMComposeWin( reply ); 00975 win->setCharset(msg->codec()->mimeName(), TRUE); 00976 win->setReplyFocus(false); 00977 win->show(); 00978 00979 return OK; 00980 } 00981 00982 00983 KMReplyListCommand::KMReplyListCommand( QWidget *parent, 00984 KMMessage *msg, const QString &selection ) 00985 : KMCommand( parent, msg ), mSelection( selection ) 00986 { 00987 } 00988 00989 KMCommand::Result KMReplyListCommand::execute() 00990 { 00991 KCursorSaver busy(KBusyPtr::busy()); 00992 KMMessage *msg = retrievedMessage(); 00993 KMMessage *reply = msg->createReply( KMail::ReplyList, mSelection); 00994 KMComposeWin *win = new KMComposeWin( reply ); 00995 win->setCharset(msg->codec()->mimeName(), TRUE); 00996 win->setReplyFocus(false); 00997 win->show(); 00998 00999 return OK; 01000 } 01001 01002 01003 KMReplyToAllCommand::KMReplyToAllCommand( QWidget *parent, 01004 KMMessage *msg, const QString &selection ) 01005 :KMCommand( parent, msg ), mSelection( selection ) 01006 { 01007 } 01008 01009 KMCommand::Result KMReplyToAllCommand::execute() 01010 { 01011 KCursorSaver busy(KBusyPtr::busy()); 01012 KMMessage *msg = retrievedMessage(); 01013 KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection ); 01014 KMComposeWin *win = new KMComposeWin( reply ); 01015 win->setCharset( msg->codec()->mimeName(), TRUE ); 01016 win->setReplyFocus(); 01017 win->show(); 01018 01019 return OK; 01020 } 01021 01022 01023 KMReplyAuthorCommand::KMReplyAuthorCommand( QWidget *parent, KMMessage *msg, 01024 const QString &selection ) 01025 : KMCommand( parent, msg ), mSelection( selection ) 01026 { 01027 } 01028 01029 KMCommand::Result KMReplyAuthorCommand::execute() 01030 { 01031 KCursorSaver busy(KBusyPtr::busy()); 01032 KMMessage *msg = retrievedMessage(); 01033 KMMessage *reply = msg->createReply( KMail::ReplyAuthor, mSelection ); 01034 KMComposeWin *win = new KMComposeWin( reply ); 01035 win->setCharset( msg->codec()->mimeName(), TRUE ); 01036 win->setReplyFocus(); 01037 win->show(); 01038 01039 return OK; 01040 } 01041 01042 01043 KMForwardCommand::KMForwardCommand( QWidget *parent, 01044 const QPtrList<KMMsgBase> &msgList, uint identity ) 01045 : KMCommand( parent, msgList ), 01046 mIdentity( identity ) 01047 { 01048 } 01049 01050 KMForwardCommand::KMForwardCommand( QWidget *parent, KMMessage *msg, 01051 uint identity ) 01052 : KMCommand( parent, msg ), 01053 mIdentity( identity ) 01054 { 01055 } 01056 01057 KMCommand::Result KMForwardCommand::execute() 01058 { 01059 KMComposeWin *win; 01060 QPtrList<KMMessage> msgList = retrievedMsgs(); 01061 01062 if (msgList.count() >= 2) { 01063 // ask if they want a mime digest forward 01064 01065 if (KMessageBox::questionYesNo( parentWidget(), 01066 i18n("Forward selected messages as " 01067 "a MIME digest?") ) 01068 == KMessageBox::Yes) { 01069 uint id = 0; 01070 KMMessage *fwdMsg = new KMMessage; 01071 KMMessagePart *msgPart = new KMMessagePart; 01072 QString msgPartText; 01073 int msgCnt = 0; // incase there are some we can't forward for some reason 01074 01075 // dummy header initialization; initialization with the correct identity 01076 // is done below 01077 fwdMsg->initHeader(id); 01078 fwdMsg->setAutomaticFields(true); 01079 fwdMsg->mMsg->Headers().ContentType().CreateBoundary(1); 01080 QCString boundary( fwdMsg->mMsg->Headers().ContentType().Boundary().c_str() ); 01081 msgPartText = i18n("\nThis is a MIME digest forward. The content of the" 01082 " message is contained in the attachment(s).\n\n\n"); 01083 // iterate through all the messages to be forwarded 01084 for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) { 01085 // set the identity 01086 if (id == 0) 01087 id = msg->headerField("X-KMail-Identity").stripWhiteSpace().toUInt(); 01088 // set the part header 01089 msgPartText += "--"; 01090 msgPartText += QString::fromLatin1( boundary ); 01091 msgPartText += "\nContent-Type: MESSAGE/RFC822"; 01092 msgPartText += QString("; CHARSET=%1").arg(msg->charset()); 01093 msgPartText += "\n"; 01094 DwHeaders dwh; 01095 dwh.MessageId().CreateDefault(); 01096 msgPartText += QString("Content-ID: %1\n").arg(dwh.MessageId().AsString().c_str()); 01097 msgPartText += QString("Content-Description: %1").arg(msg->subject()); 01098 if (!msg->subject().contains("(fwd)")) 01099 msgPartText += " (fwd)"; 01100 msgPartText += "\n\n"; 01101 // remove headers that shouldn't be forwarded 01102 msg->removePrivateHeaderFields(); 01103 msg->removeHeaderField("BCC"); 01104 // set the part 01105 msgPartText += msg->headerAsString(); 01106 msgPartText += "\n"; 01107 msgPartText += msg->body(); 01108 msgPartText += "\n"; // eot 01109 msgCnt++; 01110 fwdMsg->link(msg, KMMsgStatusForwarded); 01111 } 01112 if ( id == 0 ) 01113 id = mIdentity; // use folder identity if no message had an id set 01114 fwdMsg->initHeader(id); 01115 msgPartText += "--"; 01116 msgPartText += QString::fromLatin1( boundary ); 01117 msgPartText += "--\n"; 01118 QCString tmp; 01119 msgPart->setTypeStr("MULTIPART"); 01120 tmp.sprintf( "Digest; boundary=\"%s\"", boundary.data() ); 01121 msgPart->setSubtypeStr( tmp ); 01122 msgPart->setName("unnamed"); 01123 msgPart->setCte(DwMime::kCte7bit); // does it have to be 7bit? 01124 msgPart->setContentDescription(QString("Digest of %1 messages.").arg(msgCnt)); 01125 // THIS HAS TO BE AFTER setCte()!!!! 01126 msgPart->setBodyEncoded(QCString(msgPartText.ascii())); 01127 KCursorSaver busy(KBusyPtr::busy()); 01128 win = new KMComposeWin(fwdMsg, id); 01129 win->addAttach(msgPart); 01130 win->show(); 01131 return OK; 01132 } else { // NO MIME DIGEST, Multiple forward 01133 uint id = 0; 01134 QCString msgText = ""; 01135 QPtrList<KMMessage> linklist; 01136 for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) { 01137 // set the identity 01138 if (id == 0) 01139 id = msg->headerField("X-KMail-Identity").stripWhiteSpace().toUInt(); 01140 01141 msgText += msg->createForwardBody(); 01142 linklist.append(msg); 01143 } 01144 if ( id == 0 ) 01145 id = mIdentity; // use folder identity if no message had an id set 01146 KMMessage *fwdMsg = new KMMessage; 01147 fwdMsg->initHeader(id); 01148 fwdMsg->setAutomaticFields(true); 01149 fwdMsg->setCharset("utf-8"); 01150 fwdMsg->setBody(msgText); 01151 01152 for (KMMessage *msg = linklist.first(); msg; msg = linklist.next()) 01153 fwdMsg->link(msg, KMMsgStatusForwarded); 01154 01155 KCursorSaver busy(KBusyPtr::busy()); 01156 win = new KMComposeWin(fwdMsg, id); 01157 win->setCharset(""); 01158 win->show(); 01159 return OK; 01160 } 01161 } 01162 01163 // forward a single message at most. 01164 01165 KMMessage *msg = msgList.getFirst(); 01166 if ( !msg || !msg->codec() ) 01167 return Failed; 01168 01169 KCursorSaver busy(KBusyPtr::busy()); 01170 win = new KMComposeWin(msg->createForward()); 01171 win->setCharset(msg->codec()->mimeName(), TRUE); 01172 win->show(); 01173 01174 return OK; 01175 } 01176 01177 01178 KMForwardAttachedCommand::KMForwardAttachedCommand( QWidget *parent, 01179 const QPtrList<KMMsgBase> &msgList, uint identity, KMComposeWin *win ) 01180 : KMCommand( parent, msgList ), mIdentity( identity ), 01181 mWin( QGuardedPtr< KMComposeWin >( win )) 01182 { 01183 } 01184 01185 KMForwardAttachedCommand::KMForwardAttachedCommand( QWidget *parent, 01186 KMMessage * msg, uint identity, KMComposeWin *win ) 01187 : KMCommand( parent, msg ), mIdentity( identity ), 01188 mWin( QGuardedPtr< KMComposeWin >( win )) 01189 { 01190 } 01191 01192 KMCommand::Result KMForwardAttachedCommand::execute() 01193 { 01194 QPtrList<KMMessage> msgList = retrievedMsgs(); 01195 KMMessage *fwdMsg = new KMMessage; 01196 01197 if (msgList.count() >= 2) { 01198 // don't respect X-KMail-Identity headers because they might differ for 01199 // the selected mails 01200 fwdMsg->initHeader(mIdentity); 01201 } 01202 else if (msgList.count() == 1) { 01203 KMMessage *msg = msgList.getFirst(); 01204 fwdMsg->initFromMessage(msg); 01205 fwdMsg->setSubject( msg->forwardSubject() ); 01206 } 01207 01208 fwdMsg->setAutomaticFields(true); 01209 01210 KCursorSaver busy(KBusyPtr::busy()); 01211 if (!mWin) 01212 mWin = new KMComposeWin(fwdMsg, mIdentity); 01213 01214 // iterate through all the messages to be forwarded 01215 for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) { 01216 // remove headers that shouldn't be forwarded 01217 msg->removePrivateHeaderFields(); 01218 msg->removeHeaderField("BCC"); 01219 // set the part 01220 KMMessagePart *msgPart = new KMMessagePart; 01221 msgPart->setTypeStr("message"); 01222 msgPart->setSubtypeStr("rfc822"); 01223 msgPart->setCharset(msg->charset()); 01224 msgPart->setName("forwarded message"); 01225 msgPart->setContentDescription(msg->from()+": "+msg->subject()); 01226 msgPart->setContentDisposition( "inline" ); 01227 // THIS HAS TO BE AFTER setCte()!!!! 01228 QValueList<int> dummy; 01229 msgPart->setBodyAndGuessCte(msg->asString(), dummy, true); 01230 msgPart->setCharset(""); 01231 01232 fwdMsg->link(msg, KMMsgStatusForwarded); 01233 mWin->addAttach(msgPart); 01234 } 01235 01236 mWin->show(); 01237 01238 return OK; 01239 } 01240 01241 01242 KMRedirectCommand::KMRedirectCommand( QWidget *parent, 01243 KMMessage *msg ) 01244 : KMCommand( parent, msg ) 01245 { 01246 } 01247 01248 KMCommand::Result KMRedirectCommand::execute() 01249 { 01250 //TODO: move KMMessage::createRedirect to here 01251 KMComposeWin *win; 01252 KMMessage *msg = retrievedMessage(); 01253 if ( !msg || !msg->codec() ) 01254 return Failed; 01255 01256 KCursorSaver busy(KBusyPtr::busy()); 01257 win = new KMComposeWin(); 01258 win->setMsg(msg->createRedirect(), FALSE); 01259 win->setCharset(msg->codec()->mimeName()); 01260 win->show(); 01261 01262 return OK; 01263 } 01264 01265 01266 KMBounceCommand::KMBounceCommand( QWidget *parent, 01267 KMMessage *msg ) 01268 : KMCommand( parent, msg ) 01269 { 01270 } 01271 01272 KMCommand::Result KMBounceCommand::execute() 01273 { 01274 KMMessage *msg = retrievedMessage(); 01275 KMMessage *newMsg = msg->createBounce( TRUE /* with UI */); 01276 if (newMsg) 01277 kmkernel->msgSender()->send(newMsg, kmkernel->msgSender()->sendImmediate()); 01278 01279 return OK; 01280 } 01281 01282 01283 KMPrintCommand::KMPrintCommand( QWidget *parent, 01284 KMMessage *msg, bool htmlOverride, const QTextCodec* codec ) 01285 : KMCommand( parent, msg ), mHtmlOverride( htmlOverride ), mCodec( codec ) 01286 { 01287 } 01288 01289 KMCommand::Result KMPrintCommand::execute() 01290 { 01291 KMReaderWin printWin( 0, 0, 0 ); 01292 printWin.setPrinting(TRUE); 01293 printWin.readConfig(); 01294 printWin.setHtmlOverride( mHtmlOverride ); 01295 printWin.setOverrideCodec( mCodec ); 01296 printWin.setMsg(retrievedMessage(), TRUE); 01297 printWin.printMsg(); 01298 01299 return OK; 01300 } 01301 01302 01303 KMSetStatusCommand::KMSetStatusCommand( KMMsgStatus status, 01304 const QValueList<Q_UINT32> &serNums, bool toggle ) 01305 : mStatus( status ), mSerNums( serNums ), mToggle( toggle ) 01306 { 01307 } 01308 01309 KMCommand::Result KMSetStatusCommand::execute() 01310 { 01311 QValueListIterator<Q_UINT32> it; 01312 int idx = -1; 01313 KMFolder *folder = 0; 01314 bool parentStatus = false; 01315 01316 // Toggle actions on threads toggle the whole thread 01317 // depending on the state of the parent. 01318 if (mToggle) { 01319 KMMsgBase *msg; 01320 kmkernel->msgDict()->getLocation( *mSerNums.begin(), &folder, &idx ); 01321 if (folder) { 01322 msg = folder->getMsgBase(idx); 01323 if (msg && (msg->status()&mStatus)) 01324 parentStatus = true; 01325 else 01326 parentStatus = false; 01327 } 01328 } 01329 QMap< KMFolder*, QValueList<int> > folderMap; 01330 for ( it = mSerNums.begin(); it != mSerNums.end(); ++it ) { 01331 kmkernel->msgDict()->getLocation( *it, &folder, &idx ); 01332 if (folder) { 01333 if (mToggle) { 01334 KMMsgBase *msg = folder->getMsgBase(idx); 01335 // check if we are already at the target toggle state 01336 if (msg) { 01337 bool myStatus; 01338 if (msg->status()&mStatus) 01339 myStatus = true; 01340 else 01341 myStatus = false; 01342 if (myStatus != parentStatus) 01343 continue; 01344 } 01345 } 01346 /* Collect the ids for each folder in a separate list and 01347 send them off in one go at the end. */ 01348 folderMap[folder].append(idx); 01349 } 01350 } 01351 QMapIterator< KMFolder*, QValueList<int> > it2 = folderMap.begin(); 01352 while ( it2 != folderMap.end() ) { 01353 KMFolder *f = it2.key(); 01354 f->setStatus( (*it2), mStatus, mToggle ); 01355 ++it2; 01356 } 01357 //kapp->dcopClient()->emitDCOPSignal( "unreadCountChanged()", QByteArray() ); 01358 01359 return OK; 01360 } 01361 01362 01363 KMFilterCommand::KMFilterCommand( const QCString &field, const QString &value ) 01364 : mField( field ), mValue( value ) 01365 { 01366 } 01367 01368 KMCommand::Result KMFilterCommand::execute() 01369 { 01370 kmkernel->filterMgr()->createFilter( mField, mValue ); 01371 01372 return OK; 01373 } 01374 01375 01376 KMFilterActionCommand::KMFilterActionCommand( QWidget *parent, 01377 const QPtrList<KMMsgBase> &msgList, 01378 KMFilter *filter ) 01379 : KMCommand( parent, msgList ), mFilter( filter ) 01380 { 01381 } 01382 01383 KMCommand::Result KMFilterActionCommand::execute() 01384 { 01385 QPtrList<KMMessage> msgList = retrievedMsgs(); 01386 01387 for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) 01388 if( msg->parent() ) 01389 kmkernel->filterMgr()->tempOpenFolder(msg->parent()); 01390 01391 for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) { 01392 msg->setTransferInProgress(false); 01393 01394 int filterResult = kmkernel->filterMgr()->process(msg, mFilter); 01395 if (filterResult == 2) { 01396 // something went horribly wrong (out of space?) 01397 perror("Critical error"); 01398 kmkernel->emergencyExit( i18n("Not enough free disk space?" )); 01399 } 01400 msg->setTransferInProgress(true); 01401 } 01402 01403 return OK; 01404 } 01405 01406 01407 KMMetaFilterActionCommand::KMMetaFilterActionCommand( KMFilter *filter, 01408 KMHeaders *headers, 01409 KMMainWidget *main ) 01410 : QObject( main ), 01411 mFilter( filter ), mHeaders( headers ), mMainWidget( main ) 01412 { 01413 } 01414 01415 void KMMetaFilterActionCommand::start() 01416 { 01417 #if 0 // use action scheduler 01418 KMFilterMgr::FilterSet set = KMFilterMgr::All; 01419 QPtrList<KMFilter> filters; 01420 filters.append( mFilter ); 01421 ActionScheduler *scheduler = new ActionScheduler( set, filters, mHeaders ); 01422 scheduler->setAlwaysMatch( true ); 01423 scheduler->setAutoDestruct( true ); 01424 01425 int contentX, contentY; 01426 KMHeaderItem *nextItem = mHeaders->prepareMove( &contentX, &contentY ); 01427 QPtrList<KMMsgBase> msgList = *mHeaders->selectedMsgs(true); 01428 mHeaders->finalizeMove( nextItem, contentX, contentY ); 01429 01430 01431 for (KMMsgBase *msg = msgList.first(); msg; msg = msgList.next()) 01432 scheduler->execFilters( msg ); 01433 #else 01434 KMCommand *filterCommand = new KMFilterActionCommand( mMainWidget, 01435 *mHeaders->selectedMsgs(), mFilter); 01436 filterCommand->start(); 01437 int contentX, contentY; 01438 KMHeaderItem *item = mHeaders->prepareMove( &contentX, &contentY ); 01439 mHeaders->finalizeMove( item, contentX, contentY ); 01440 #endif 01441 } 01442 01443 01444 KMMailingListFilterCommand::KMMailingListFilterCommand( QWidget *parent, 01445 KMMessage *msg ) 01446 : KMCommand( parent, msg ) 01447 { 01448 } 01449 01450 KMCommand::Result KMMailingListFilterCommand::execute() 01451 { 01452 QCString name; 01453 QString value; 01454 KMMessage *msg = retrievedMessage(); 01455 if (!msg) 01456 return Failed; 01457 01458 if ( !MailingList::name( msg, name, value ).isEmpty() ) { 01459 kmkernel->filterMgr()->createFilter( name, value ); 01460 return OK; 01461 } 01462 else 01463 return Failed; 01464 } 01465 01466 01467 void KMMenuCommand::folderToPopupMenu(bool move, 01468 QObject *receiver, KMMenuToFolder *aMenuToFolder, QPopupMenu *menu ) 01469 { 01470 while ( menu->count() ) 01471 { 01472 QPopupMenu *popup = menu->findItem( menu->idAt( 0 ) )->popup(); 01473 if (popup) 01474 delete popup; 01475 else 01476 menu->removeItemAt( 0 ); 01477 } 01478 01479 if (!kmkernel->imapFolderMgr()->dir().first() && 01480 !kmkernel->dimapFolderMgr()->dir().first()) 01481 { // only local folders 01482 makeFolderMenu( &kmkernel->folderMgr()->dir(), move, 01483 receiver, aMenuToFolder, menu ); 01484 } else { 01485 // operate on top-level items 01486 QPopupMenu* subMenu = new QPopupMenu(menu); 01487 makeFolderMenu( &kmkernel->folderMgr()->dir(), 01488 move, receiver, aMenuToFolder, subMenu ); 01489 menu->insertItem( i18n( "Local Folders" ), subMenu ); 01490 KMFolderDir* fdir = &kmkernel->imapFolderMgr()->dir(); 01491 for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) { 01492 if (node->isDir()) 01493 continue; 01494 subMenu = new QPopupMenu(menu); 01495 makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu ); 01496 menu->insertItem( node->label(), subMenu ); 01497 } 01498 fdir = &kmkernel->dimapFolderMgr()->dir(); 01499 for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) { 01500 if (node->isDir()) 01501 continue; 01502 subMenu = new QPopupMenu(menu); 01503 makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu ); 01504 menu->insertItem( node->label(), subMenu ); 01505 } 01506 } 01507 } 01508 01509 void KMMenuCommand::makeFolderMenu(KMFolderNode* node, bool move, 01510 QObject *receiver, KMMenuToFolder *aMenuToFolder, QPopupMenu *menu ) 01511 { 01512 // connect the signals 01513 if (move) 01514 { 01515 disconnect(menu, SIGNAL(activated(int)), receiver, 01516 SLOT(moveSelectedToFolder(int))); 01517 connect(menu, SIGNAL(activated(int)), receiver, 01518 SLOT(moveSelectedToFolder(int))); 01519 } else { 01520 disconnect(menu, SIGNAL(activated(int)), receiver, 01521 SLOT(copySelectedToFolder(int))); 01522 connect(menu, SIGNAL(activated(int)), receiver, 01523 SLOT(copySelectedToFolder(int))); 01524 } 01525 01526 KMFolder *folder = 0; 01527 KMFolderDir *folderDir = 0; 01528 if (node->isDir()) { 01529 folderDir = static_cast<KMFolderDir*>(node); 01530 } else { 01531 folder = static_cast<KMFolder*>(node); 01532 folderDir = folder->child(); 01533 } 01534 01535 if (folder && !folder->noContent()) 01536 { 01537 int menuId; 01538 if (move) 01539 menuId = menu->insertItem(i18n("Move to This Folder")); 01540 else 01541 menuId = menu->insertItem(i18n("Copy to This Folder")); 01542 aMenuToFolder->insert( menuId, folder ); 01543 menu->setItemEnabled( menuId, !folder->isReadOnly() ); 01544 menu->insertSeparator(); 01545 } 01546 01547 if (!folderDir) 01548 return; 01549 01550 for (KMFolderNode *it = folderDir->first(); it; it = folderDir->next() ) { 01551 if (it->isDir()) 01552 continue; 01553 KMFolder *child = static_cast<KMFolder*>(it); 01554 QString label = child->label(); 01555 label.replace("&","&&"); 01556 if (child->child() && child->child()->first()) { 01557 // descend 01558 QPopupMenu *subMenu = new QPopupMenu(menu, "subMenu"); 01559 makeFolderMenu( child, move, receiver, 01560 aMenuToFolder, subMenu ); 01561 menu->insertItem( label, subMenu ); 01562 } else { 01563 // insert an item 01564 int menuId = menu->insertItem( label ); 01565 aMenuToFolder->insert( menuId, child ); 01566 menu->setItemEnabled( menuId, !child->isReadOnly() ); 01567 } 01568 } 01569 return; 01570 } 01571 01572 01573 KMCopyCommand::KMCopyCommand( KMFolder* destFolder, 01574 const QPtrList<KMMsgBase> &msgList ) 01575 :mDestFolder( destFolder ), mMsgList( msgList ) 01576 { 01577 } 01578 01579 KMCopyCommand::KMCopyCommand( KMFolder* destFolder, KMMessage * msg ) 01580 :mDestFolder( destFolder ) 01581 { 01582 mMsgList.append( &msg->toMsgBase() ); 01583 } 01584 01585 KMCommand::Result KMCopyCommand::execute() 01586 { 01587 KMMsgBase *msgBase; 01588 KMMessage *msg, *newMsg; 01589 int idx = -1; 01590 bool isMessage; 01591 QPtrList<KMMessage> list; 01592 01593 if (mDestFolder && mDestFolder->open() != 0) 01594 return Failed; 01595 01596 KCursorSaver busy(KBusyPtr::busy()); 01597 01598 for (msgBase = mMsgList.first(); msgBase; msgBase = mMsgList.next() ) 01599 { 01600 KMFolder *srcFolder = msgBase->parent(); 01601 if (isMessage = msgBase->isMessage()) 01602 { 01603 msg = static_cast<KMMessage*>(msgBase); 01604 } else { 01605 idx = srcFolder->find(msgBase); 01606 assert(idx != -1); 01607 msg = srcFolder->getMsg(idx); 01608 } 01609 01610 if (srcFolder && 01611 (srcFolder->folderType()== KMFolderTypeImap) && 01612 (mDestFolder->folderType() == KMFolderTypeImap) && 01613 (static_cast<KMFolderImap*>(srcFolder->storage())->account() == 01614 static_cast<KMFolderImap*>(mDestFolder->storage())->account())) 01615 { 01616 list.append(msg); 01617 } else { 01618 newMsg = new KMMessage; 01619 newMsg->setComplete(msg->isComplete()); 01620 // make sure the attachment state is only calculated when it's complete 01621 if (!newMsg->isComplete()) 01622 newMsg->setReadyToShow(false); 01623 newMsg->fromString(msg->asString()); 01624 newMsg->setStatus(msg->status()); 01625 01626 if (srcFolder && !newMsg->isComplete()) 01627 { 01628 newMsg->setParent(msg->parent()); 01629 FolderJob *job = srcFolder->createJob(newMsg); 01630 job->setCancellable( false ); 01631 connect(job, SIGNAL(messageRetrieved(KMMessage*)), 01632 mDestFolder, SLOT(reallyAddCopyOfMsg(KMMessage*))); 01633 // msg musn't be deleted 01634 newMsg->setTransferInProgress(true); 01635 job->start(); 01636 } else { 01637 int rc, index; 01638 mDestFolder->open(); 01639 rc = mDestFolder->addMsg(newMsg, &index); 01640 if (rc == 0 && index != -1) 01641 mDestFolder->unGetMsg( mDestFolder->count() - 1 ); 01642 mDestFolder->close(); 01643 } 01644 } 01645 01646 if (!isMessage && list.isEmpty()) 01647 { 01648 assert(idx != -1); 01649 srcFolder->unGetMsg( idx ); 01650 } 01651 01652 } // end for 01653 mDestFolder->close(); 01654 01655 //TODO: Get rid of the other cases just use this one for all types of folder 01656 //TODO: requires adding copyMsg and getFolder methods to KMFolder.h 01657 01658 if (!list.isEmpty()) 01659 { 01660 // copy the message(s); note: the list is empty afterwards! 01661 KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage()); 01662 imapDestFolder->copyMsg(list); 01663 imapDestFolder->getFolder(); 01664 } 01665 01666 return OK; 01667 } 01668 01669 01670 KMMoveCommand::KMMoveCommand( KMFolder* destFolder, 01671 const QPtrList<KMMsgBase> &msgList) 01672 :mDestFolder( destFolder ), mMsgList( msgList ), mProgressItem( 0 ) 01673 { 01674 setDeletesItself( true ); 01675 } 01676 01677 KMMoveCommand::KMMoveCommand( KMFolder* destFolder, 01678 KMMessage *msg ) 01679 :mDestFolder( destFolder ), mProgressItem( 0 ) 01680 { 01681 setDeletesItself( true ); 01682 mMsgList.append( &msg->toMsgBase() ); 01683 } 01684 01685 KMMoveCommand::KMMoveCommand( KMFolder* destFolder, 01686 KMMsgBase *msgBase ) 01687 :mDestFolder( destFolder ), mProgressItem( 0 ) 01688 { 01689 setDeletesItself( true ); 01690 mMsgList.append( msgBase ); 01691 } 01692 01693 KMCommand::Result KMMoveCommand::execute() 01694 { 01695 setEmitsCompletedItself( true ); 01696 typedef QMap< KMFolder*, QPtrList<KMMessage>* > FolderToMessageListMap; 01697 FolderToMessageListMap folderDeleteList; 01698 01699 if (mDestFolder && mDestFolder->open() != 0) { 01700 completeMove( Failed ); 01701 return Failed; 01702 } 01703 KCursorSaver busy(KBusyPtr::busy()); 01704 01705 // TODO set SSL state according to source and destfolder connection? 01706 Q_ASSERT( !mProgressItem ); 01707 mProgressItem = 01708 ProgressManager::createProgressItem ( 01709 "move"+ProgressManager::getUniqueID(), 01710 mDestFolder ? i18n( "Moving messages" ) : i18n( "Deleting messages" ) ); 01711 connect( mProgressItem, SIGNAL( progressItemCanceled( ProgressItem* ) ), 01712 this, SLOT( slotMoveCanceled() ) ); 01713 01714 KMMessage *msg; 01715 KMMsgBase *msgBase; 01716 int rc = 0; 01717 int index; 01718 QPtrList<KMMessage> list; 01719 int undoId = -1; 01720 01721 if (mDestFolder) { 01722 connect (mDestFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)), 01723 this, SLOT(slotMsgAddedToDestFolder(KMFolder*, Q_UINT32))); 01724 01725 } 01726 for ( msgBase=mMsgList.first(); msgBase; msgBase=mMsgList.next() ) { 01727 mLostBoys.append( msgBase->getMsgSerNum() ); 01728 } 01729 mProgressItem->setTotalItems( mMsgList.count() ); 01730 01731 for (msgBase=mMsgList.first(); msgBase && !rc; msgBase=mMsgList.next()) { 01732 KMFolder *srcFolder = msgBase->parent(); 01733 if (srcFolder == mDestFolder) 01734 continue; 01735 bool undo = msgBase->enableUndo(); 01736 int idx = srcFolder->find(msgBase); 01737 assert(idx != -1); 01738 if ( msgBase->isMessage() ) 01739 msg = static_cast<KMMessage*>(msgBase); 01740 else 01741 msg = srcFolder->getMsg(idx); 01742 01743 if ( msg->transferInProgress() && 01744 srcFolder->folderType() == KMFolderTypeImap ) 01745 { 01746 // cancel the download 01747 msg->setTransferInProgress( false, true ); 01748 static_cast<KMFolderImap*>(srcFolder->storage())->ignoreJobsForMessage( msg ); 01749 } 01750 01751 if (mDestFolder) { 01752 if (mDestFolder->folderType() == KMFolderTypeImap) { 01753 /* If we are moving to an imap folder, connect to it's completed 01754 * signal so we notice when all the mails should have showed up in it 01755 * but haven't for some reason. */ 01756 KMFolderImap *imapFolder = static_cast<KMFolderImap*> ( mDestFolder->storage() ); 01757 disconnect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )), 01758 this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool ))); 01759 01760 connect (imapFolder, SIGNAL(folderComplete( KMFolderImap*, bool )), 01761 this, SLOT(slotImapFolderCompleted( KMFolderImap*, bool ))); 01762 list.append(msg); 01763 } else { 01764 // We are moving to a local folder. 01765 mDestFolder->open(); 01766 rc = mDestFolder->moveMsg(msg, &index); 01767 if (rc == 0 && index != -1) { 01768 KMMsgBase *mb = mDestFolder->unGetMsg( mDestFolder->count() - 1 ); 01769 if (undo && mb) 01770 { 01771 if ( undoId == -1 ) 01772 undoId = kmkernel->undoStack()->newUndoAction( srcFolder, mDestFolder ); 01773 kmkernel->undoStack()->addMsgToAction( undoId, mb->getMsgSerNum() ); 01774 } 01775 mDestFolder->close(); 01776 } else if (rc != 0) { 01777 // Something went wrong. Stop processing here, it is likely that the 01778 // other moves would fail as well. 01779 completeMove( Failed ); 01780 mDestFolder->close(); 01781 return Failed; 01782 } 01783 } 01784 } else { 01785 // really delete messages that are already in the trash folder or if 01786 // we are really, really deleting, not just moving to trash 01787 if (srcFolder->folderType() == KMFolderTypeImap) { 01788 if (!folderDeleteList[srcFolder]) 01789 folderDeleteList[srcFolder] = new QPtrList<KMMessage>; 01790 folderDeleteList[srcFolder]->append( msg ); 01791 } else { 01792 srcFolder->removeMsg(idx); 01793 delete msg; 01794 } 01795 } 01796 } 01797 if (!list.isEmpty() && mDestFolder) { 01798 mDestFolder->moveMsg(list, &index); 01799 } else { 01800 FolderToMessageListMap::Iterator it; 01801 for ( it = folderDeleteList.begin(); it != folderDeleteList.end(); ++it ) { 01802 it.key()->removeMsg(*it.data()); 01803 delete it.data(); 01804 } 01805 /* The list is empty, which means that either all messages were to be 01806 * deleted, which is done above, or all of them were already in this folder. 01807 * In both cases make sure a completed() signal is emitted nonetheless. */ 01808 KMFolder *srcFolder = 0; 01809 if ( mMsgList.first() ) { 01810 srcFolder = mMsgList.first()->parent(); 01811 if ( mDestFolder && mDestFolder == srcFolder ) { 01812 completeMove( OK ); 01813 } 01814 } 01815 if ( !mDestFolder ) { 01816 completeMove( OK ); 01817 } 01818 } 01819 01820 return OK; 01821 } 01822 01823 void KMMoveCommand::slotImapFolderCompleted(KMFolderImap *, bool success) 01824 { 01825 if ( success ) { 01826 // the folder was checked successfully but we were still called, so check 01827 // if we are still waiting for messages to show up. If so, uidValidity 01828 // changed, or something else went wrong. Clean up. 01829 01830 /* Unfortunately older UW imap servers change uid validity for each put job. 01831 * Yes, it is really that broken. *sigh* So we cannot report error here, I guess. */ 01832 if ( !mLostBoys.isEmpty() ) { 01833 kdDebug(5006) << "### Not all moved messages reported back that they were " << endl 01834 << "### added to the target folder. Did uidValidity change? " << endl; 01835 } 01836 completeMove( OK ); 01837 } else { 01838 // Should we inform the user here or leave that to the caller? 01839 completeMove( Failed ); 01840 } 01841 } 01842 01843 void KMMoveCommand::slotMsgAddedToDestFolder(KMFolder *folder, Q_UINT32 serNum) 01844 { 01845 if (folder != mDestFolder || !mLostBoys.contains( serNum ) ) { 01846 kdDebug(5006) << "KMMoveCommand::msgAddedToDestFolder different " 01847 "folder or invalid serial number." << endl; 01848 return; 01849 } 01850 mLostBoys.remove(serNum); 01851 if ( mLostBoys.isEmpty() ) { 01852 // we are done. All messages transferred to the host succesfully 01853 if (mDestFolder && mDestFolder->folderType() != KMFolderTypeImap) { 01854 mDestFolder->sync(); 01855 } 01856 completeMove( OK ); 01857 } else { 01858 mProgressItem->incCompletedItems(); 01859 mProgressItem->updateProgress(); 01860 } 01861 } 01862 01863 void KMMoveCommand::completeMove( Result result ) 01864 { 01865 if ( mDestFolder ) 01866 mDestFolder->close(); 01867 if ( mProgressItem ) 01868 mProgressItem->setComplete(); 01869 setResult( result ); 01870 emit completed( this ); 01871 deleteLater(); 01872 } 01873 01874 void KMMoveCommand::slotMoveCanceled() 01875 { 01876 completeMove( Canceled ); 01877 } 01878 01879 // srcFolder doesn't make much sense for searchFolders 01880 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder, 01881 const QPtrList<KMMsgBase> &msgList ) 01882 :KMMoveCommand(findTrashFolder( srcFolder ), msgList) 01883 { 01884 } 01885 01886 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder, KMMessage * msg ) 01887 :KMMoveCommand(findTrashFolder( srcFolder ), msg) 01888 { 01889 } 01890 01891 01892 KMFolder * KMDeleteMsgCommand::findTrashFolder( KMFolder * folder ) 01893 { 01894 KMFolder* trash = folder->trashFolder(); 01895 if( !trash ) 01896 trash = kmkernel->trashFolder(); 01897 if( trash != folder ) 01898 return trash; 01899 return 0; 01900 } 01901 01902 KMUrlClickedCommand::KMUrlClickedCommand( const KURL &url, uint identity, 01903 KMReaderWin *readerWin, bool htmlPref, KMMainWidget *mainWidget ) 01904 :mUrl( url ), mIdentity( identity ), mReaderWin( readerWin ), 01905 mHtmlPref( htmlPref ), mMainWidget( mainWidget ) 01906 { 01907 } 01908 01909 KMCommand::Result KMUrlClickedCommand::execute() 01910 { 01911 KMComposeWin *win; 01912 KMMessage* msg; 01913 01914 if (mUrl.protocol() == "mailto") 01915 { 01916 msg = new KMMessage; 01917 msg->initHeader(mIdentity); 01918 msg->setCharset("utf-8"); 01919 msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) ); 01920 QString query=mUrl.query(); 01921 while (!query.isEmpty()) { 01922 QString queryPart; 01923 int secondQuery = query.find('?',1); 01924 if (secondQuery != -1) 01925 queryPart = query.left(secondQuery); 01926 else 01927 queryPart = query; 01928 query = query.mid(queryPart.length()); 01929 01930 if (queryPart.left(9) == "?subject=") 01931 msg->setSubject( KURL::decode_string(queryPart.mid(9)) ); 01932 else if (queryPart.left(6) == "?body=") 01933 // It is correct to convert to latin1() as URL should not contain 01934 // anything except ascii. 01935 msg->setBody( KURL::decode_string(queryPart.mid(6)).latin1() ); 01936 else if (queryPart.left(4) == "?cc=") 01937 msg->setCc( KURL::decode_string(queryPart.mid(4)) ); 01938 } 01939 01940 win = new KMComposeWin(msg, mIdentity); 01941 win->setCharset("", TRUE); 01942 win->show(); 01943 } 01944 else if ( mUrl.protocol() == "im" ) 01945 { 01946 kmkernel->imProxy()->chatWithContact( mUrl.path() ); 01947 } 01948 else if ((mUrl.protocol() == "http") || (mUrl.protocol() == "https") || 01949 (mUrl.protocol() == "ftp") || (mUrl.protocol() == "file") || 01950 (mUrl.protocol() == "ftps") || (mUrl.protocol() == "sftp" ) || 01951 (mUrl.protocol() == "help") || (mUrl.protocol() == "vnc") || 01952 (mUrl.protocol() == "smb")) 01953 { 01954 KPIM::BroadcastStatus::instance()->setStatusMsg( i18n("Opening URL...")); 01955 KMimeType::Ptr mime = KMimeType::findByURL( mUrl ); 01956 if (mime->name() == "application/x-desktop" || 01957 mime->name() == "application/x-executable" || 01958 mime->name() == "application/x-msdos-program" || 01959 mime->name() == "application/x-shellscript" ) 01960 { 01961 if (KMessageBox::warningYesNo( 0, i18n( "<qt>Do you really want to execute <b>%1</b>?</qt>" ) 01962 .arg( mUrl.prettyURL() ) ) != KMessageBox::Yes) 01963 return Canceled; 01964 } 01965 (void) new KRun( mUrl ); 01966 } 01967 else 01968 return Failed; 01969 01970 return OK; 01971 } 01972 01973 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, KMMessage *msg ) 01974 : KMCommand( parent, msg ), mImplicitAttachments( true ), mEncoded( false ) 01975 { 01976 } 01977 01978 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, const QPtrList<KMMsgBase>& msgs ) 01979 : KMCommand( parent, msgs ), mImplicitAttachments( true ), mEncoded( false ) 01980 { 01981 } 01982 01983 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( QWidget *parent, QPtrList<partNode>& attachments, 01984 KMMessage *msg, bool encoded ) 01985 : KMCommand( parent, msg ), mImplicitAttachments( false ), 01986 mEncoded( encoded ) 01987 { 01988 // do not load the complete message but only parts 01989 msg->setComplete( true ); 01990 for ( QPtrListIterator<partNode> it( attachments ); it.current(); ++it ) { 01991 mAttachmentMap.insert( it.current(), msg ); 01992 } 01993 } 01994 01995 KMCommand::Result KMSaveAttachmentsCommand::execute() 01996 { 01997 setEmitsCompletedItself( true ); 01998 if ( mImplicitAttachments ) { 01999 QPtrList<KMMessage> msgList = retrievedMsgs(); 02000 KMMessage *msg; 02001 for ( QPtrListIterator<KMMessage> itr( msgList ); 02002 ( msg = itr.current() ); 02003 ++itr ) { 02004 partNode *rootNode = partNode::fromMessage( msg ); 02005 for ( partNode *child = rootNode; child; 02006 child = child->firstChild() ) { 02007 for ( partNode *node = child; node; node = node->nextSibling() ) { 02008 if ( node->type() != DwMime::kTypeMultipart ) 02009 mAttachmentMap.insert( node, msg ); 02010 } 02011 } 02012 } 02013 } 02014 setDeletesItself( true ); 02015 // load all parts 02016 KMLoadPartsCommand *command = new KMLoadPartsCommand( mAttachmentMap ); 02017 connect( command, SIGNAL( partsRetrieved() ), 02018 this, SLOT( slotSaveAll() ) ); 02019 command->start(); 02020 02021 return OK; 02022 } 02023 02024 void KMSaveAttachmentsCommand::slotSaveAll() 02025 { 02026 // now that all message parts have been retrieved, remove all parts which 02027 // don't represent an attachment if they were not explicitely passed in the 02028 // c'tor 02029 if ( mImplicitAttachments ) { 02030 for ( PartNodeMessageMap::iterator it = mAttachmentMap.begin(); 02031 it != mAttachmentMap.end(); ) { 02032 // only body parts which have a filename or a name parameter (except for 02033 // the root node for which name is set to the message's subject) are 02034 // considered attachments 02035 if ( it.key()->msgPart().fileName().stripWhiteSpace().isEmpty() && 02036 ( it.key()->msgPart().name().stripWhiteSpace().isEmpty() || 02037 !it.key()->parentNode() ) ) { 02038 PartNodeMessageMap::iterator delIt = it; 02039 ++it; 02040 mAttachmentMap.remove( delIt ); 02041 } 02042 else 02043 ++it; 02044 } 02045 if ( mAttachmentMap.isEmpty() ) { 02046 KMessageBox::information( 0, i18n("Found no attachments to save.") ); 02047 setResult( OK ); // The user has already been informed. 02048 emit completed( this ); 02049 delete this; 02050 return; 02051 } 02052 } 02053 02054 KURL url, dirUrl; 02055 if ( mAttachmentMap.count() > 1 ) { 02056 // get the dir 02057 KFileDialog fdlg( ":saveAttachments", QString::null, parentWidget(), 02058 "save attachments dialog", true ); 02059 fdlg.setCaption( i18n("Save Attachments To") ); 02060 fdlg.setOperationMode( KFileDialog::Saving ); 02061 fdlg.setMode( (unsigned int) KFile::Directory ); 02062 if ( fdlg.exec() == QDialog::Rejected || !fdlg.selectedURL().isValid() ) { 02063 setResult( Canceled ); 02064 emit completed( this ); 02065 delete this; 02066 return; 02067 } 02068 dirUrl = fdlg.selectedURL(); 02069 } 02070 else { 02071 // only one item, get the desired filename 02072 partNode *node = mAttachmentMap.begin().key(); 02073 // replace all ':' with '_' because ':' isn't allowed on FAT volumes 02074 QString s = 02075 node->msgPart().fileName().stripWhiteSpace().replace( ':', '_' ); 02076 if ( s.isEmpty() ) 02077 s = node->msgPart().name().stripWhiteSpace().replace( ':', '_' ); 02078 if ( s.isEmpty() ) 02079 s = i18n("filename for an unnamed attachment", "attachment.1"); 02080 url = KFileDialog::getSaveURL( s, QString::null, parentWidget(), 02081 QString::null ); 02082 if ( url.isEmpty() ) { 02083 setResult( Canceled ); 02084 emit completed( this ); 02085 delete this; 02086 return; 02087 } 02088 } 02089 02090 Result globalResult = OK; 02091 int unnamedAtmCount = 0; 02092 for ( PartNodeMessageMap::const_iterator it = mAttachmentMap.begin(); 02093 it != mAttachmentMap.end(); 02094 ++it ) { 02095 KURL curUrl; 02096 if ( !dirUrl.isEmpty() ) { 02097 curUrl = dirUrl; 02098 QString s = 02099 it.key()->msgPart().fileName().stripWhiteSpace().replace( ':', '_' ); 02100 if ( s.isEmpty() ) 02101 s = it.key()->msgPart().name().stripWhiteSpace().replace( ':', '_' ); 02102 if ( s.isEmpty() ) { 02103 ++unnamedAtmCount; 02104 s = i18n("filename for the %1-th unnamed attachment", 02105 "attachment.%1") 02106 .arg( unnamedAtmCount ); 02107 } 02108 curUrl.setFileName( s ); 02109 } else { 02110 curUrl = url; 02111 } 02112 02113 if ( !curUrl.isEmpty() ) { 02114 if ( KIO::NetAccess::exists( curUrl, false, parentWidget() ) ) { 02115 if ( KMessageBox::warningContinueCancel( parentWidget(), 02116 i18n( "A file named %1 already exists. Do you want to overwrite it?" ) 02117 .arg( curUrl.fileName() ), 02118 i18n( "File Already Exists" ), i18n("Overwrite") ) == KMessageBox::Cancel) { 02119 continue; 02120 } 02121 } 02122 // save 02123 const Result result = saveItem( it.key(), curUrl ); 02124 if ( result != OK ) 02125 globalResult = result; 02126 } 02127 } 02128 setResult( globalResult ); 02129 emit completed( this ); 02130 delete this; 02131 } 02132 02133 KMCommand::Result KMSaveAttachmentsCommand::saveItem( partNode *node, 02134 const KURL& url ) 02135 { 02136 bool bSaveEncrypted = false; 02137 bool bEncryptedParts = node->encryptionState() != KMMsgNotEncrypted; 02138 if( bEncryptedParts ) 02139 if( KMessageBox::questionYesNo( parentWidget(), 02140 i18n( "The part %1 of the message is encrypted. Do you want to keep the encryption when saving?" ). 02141 arg( url.fileName() ), 02142 i18n( "KMail Question" ) ) == 02143 KMessageBox::Yes ) 02144 bSaveEncrypted = true; 02145 02146 bool bSaveWithSig = true; 02147 if( node->signatureState() != KMMsgNotSigned ) 02148 if( KMessageBox::questionYesNo( parentWidget(), 02149 i18n( "The part %1 of the message is signed. Do you want to keep the signature when saving?" ). 02150 arg( url.fileName() ), 02151 i18n( "KMail Question" ) ) != 02152 KMessageBox::Yes ) 02153 bSaveWithSig = false; 02154 02155 QByteArray data; 02156 if ( mEncoded ) 02157 { 02158 // This does not decode the Message Content-Transfer-Encoding 02159 // but saves the _original_ content of the message part 02160 QCString cstr( node->msgPart().body() ); 02161 data = cstr; 02162 data.resize(data.size() - 1); 02163 } 02164 else 02165 { 02166 if( bSaveEncrypted || !bEncryptedParts) { 02167 partNode *dataNode = node; 02168 QCString rawReplyString; 02169 bool gotRawReplyString = false; 02170 if( !bSaveWithSig ) { 02171 if( DwMime::kTypeMultipart == node->type() && 02172 DwMime::kSubtypeSigned == node->subType() ){ 02173 // carefully look for the part that is *not* the signature part: 02174 if( node->findType( DwMime::kTypeApplication, 02175 DwMime::kSubtypePgpSignature, 02176 TRUE, false ) ){ 02177 dataNode = node->findTypeNot( DwMime::kTypeApplication, 02178 DwMime::kSubtypePgpSignature, 02179 TRUE, false ); 02180 }else if( node->findType( DwMime::kTypeApplication, 02181 DwMime::kSubtypePkcs7Mime, 02182 TRUE, false ) ){ 02183 dataNode = node->findTypeNot( DwMime::kTypeApplication, 02184 DwMime::kSubtypePkcs7Mime, 02185 TRUE, false ); 02186 }else{ 02187 dataNode = node->findTypeNot( DwMime::kTypeMultipart, 02188 DwMime::kSubtypeUnknown, 02189 TRUE, false ); 02190 } 02191 }else{ 02192 ObjectTreeParser otp( 0, 0, false, false, false ); 02193 02194 // process this node and all it's siblings and descendants 02195 dataNode->setProcessed( false, true ); 02196 otp.parseObjectTree( dataNode ); 02197 02198 rawReplyString = otp.rawReplyString(); 02199 gotRawReplyString = true; 02200 } 02201 } 02202 QByteArray cstr = gotRawReplyString 02203 ? rawReplyString 02204 : dataNode->msgPart().bodyDecodedBinary(); 02205 data = cstr; 02206 size_t size = cstr.size(); 02207 if ( dataNode->msgPart().type() == DwMime::kTypeText ) { 02208 // convert CRLF to LF before writing text attachments to disk 02209 size = KMFolder::crlf2lf( cstr.data(), size ); 02210 } 02211 data.resize( size ); 02212 } 02213 } 02214 QDataStream ds; 02215 QFile file; 02216 KTempFile tf; 02217 tf.setAutoDelete( true ); 02218 if ( url.isLocalFile() ) 02219 { 02220 // save directly 02221 file.setName( url.path() ); 02222 if ( !file.open( IO_WriteOnly ) ) 02223 { 02224 KMessageBox::error( parentWidget(), 02225 i18n( "%2 is detailed error description", 02226 "Could not write the file %1:\n%2" ) 02227 .arg( file.name() ) 02228 .arg( QString::fromLocal8Bit( strerror( errno ) ) ), 02229 i18n( "KMail Error" ) ); 02230 return Failed; 02231 } 02232 fchmod( file.handle(), S_IRUSR | S_IWUSR ); 02233 ds.setDevice( &file ); 02234 } else 02235 { 02236 // tmp file for upload 02237 ds.setDevice( tf.file() ); 02238 } 02239 02240 ds.writeRawBytes( data.data(), data.size() ); 02241 if ( !url.isLocalFile() ) 02242 { 02243 tf.close(); 02244 if ( !KIO::NetAccess::upload( tf.name(), url, parentWidget() ) ) 02245 { 02246 KMessageBox::error( parentWidget(), 02247 i18n( "Could not write the file %1." ) 02248 .arg( url.path() ), 02249 i18n( "KMail Error" ) ); 02250 return Failed; 02251 } 02252 } else 02253 file.close(); 02254 return OK; 02255 } 02256 02257 KMLoadPartsCommand::KMLoadPartsCommand( QPtrList<partNode>& parts, KMMessage *msg ) 02258 : mNeedsRetrieval( 0 ) 02259 { 02260 for ( QPtrListIterator<partNode> it( parts ); it.current(); ++it ) { 02261 mPartMap.insert( it.current(), msg ); 02262 } 02263 } 02264 02265 KMLoadPartsCommand::KMLoadPartsCommand( partNode *node, KMMessage *msg ) 02266 : mNeedsRetrieval( 0 ) 02267 { 02268 mPartMap.insert( node, msg ); 02269 } 02270 02271 KMLoadPartsCommand::KMLoadPartsCommand( PartNodeMessageMap& partMap ) 02272 : mNeedsRetrieval( 0 ), mPartMap( partMap ) 02273 { 02274 } 02275 02276 void KMLoadPartsCommand::slotStart() 02277 { 02278 for ( PartNodeMessageMap::const_iterator it = mPartMap.begin(); 02279 it != mPartMap.end(); 02280 ++it ) { 02281 if ( !it.key()->msgPart().isComplete() && 02282 !it.key()->msgPart().partSpecifier().isEmpty() ) { 02283 // incomplete part, so retrieve it first 02284 ++mNeedsRetrieval; 02285 KMFolder* curFolder = it.data()->parent(); 02286 if ( curFolder ) { 02287 FolderJob *job = 02288 curFolder->createJob( it.data(), FolderJob::tGetMessage, 02289 0, it.key()->msgPart().partSpecifier() ); 02290 job->setCancellable( false ); 02291 connect( job, SIGNAL(messageUpdated(KMMessage*, QString)), 02292 this, SLOT(slotPartRetrieved(KMMessage*, QString)) ); 02293 job->start(); 02294 } else 02295 kdWarning(5006) << "KMLoadPartsCommand - msg has no parent" << endl; 02296 } 02297 } 02298 if ( mNeedsRetrieval == 0 ) 02299 execute(); 02300 } 02301 02302 void KMLoadPartsCommand::slotPartRetrieved( KMMessage *msg, 02303 QString partSpecifier ) 02304 { 02305 DwBodyPart *part = 02306 msg->findDwBodyPart( msg->getFirstDwBodyPart(), partSpecifier ); 02307 if ( part ) { 02308 // update the DwBodyPart in the partNode 02309 for ( PartNodeMessageMap::const_iterator it = mPartMap.begin(); 02310 it != mPartMap.end(); 02311 ++it ) { 02312 if ( it.key()->dwPart()->partId() == part->partId() ) 02313 it.key()->setDwPart( part ); 02314 } 02315 } else 02316 kdWarning(5006) << "KMLoadPartsCommand::slotPartRetrieved - could not find bodypart!" << endl; 02317 --mNeedsRetrieval; 02318 if ( mNeedsRetrieval == 0 ) 02319 execute(); 02320 } 02321 02322 KMCommand::Result KMLoadPartsCommand::execute() 02323 { 02324 emit partsRetrieved(); 02325 setResult( OK ); 02326 emit completed( this ); 02327 deleteLater(); 02328 return OK; 02329 } 02330 02331 KMResendMessageCommand::KMResendMessageCommand( QWidget *parent, 02332 KMMessage *msg ) 02333 :KMCommand( parent, msg ) 02334 { 02335 } 02336 02337 KMCommand::Result KMResendMessageCommand::execute() 02338 { 02339 KMComposeWin *win; 02340 KMMessage *msg = retrievedMessage(); 02341 02342 KMMessage *newMsg = new KMMessage(*msg); 02343 newMsg->setCharset(msg->codec()->mimeName()); 02344 // the message needs a new Message-Id 02345 newMsg->removeHeaderField( "Message-Id" ); 02346 newMsg->setParent( 0 ); 02347 02348 // adds the new date to the message 02349 newMsg->removeHeaderField( "Date" ); 02350 02351 win = new KMComposeWin(); 02352 win->setMsg(newMsg, false, true); 02353 win->show(); 02354 02355 return OK; 02356 } 02357 02358 KMMailingListCommand::KMMailingListCommand( QWidget *parent, KMFolder *folder ) 02359 : KMCommand( parent ), mFolder( folder ) 02360 { 02361 } 02362 02363 KMCommand::Result KMMailingListCommand::execute() 02364 { 02365 KURL::List lst = urls(); 02366 QString handler = ( mFolder->mailingList().handler() == MailingList::KMail ) 02367 ? "mailto" : "https"; 02368 02369 KMCommand *command = 0; 02370 for ( KURL::List::Iterator itr = lst.begin(); itr != lst.end(); ++itr ) { 02371 if ( handler == (*itr).protocol() ) { 02372 command = new KMUrlClickedCommand( *itr, mFolder->identity(), 0, false ); 02373 } 02374 } 02375 if ( !command && !lst.empty() ) { 02376 command = 02377 new KMUrlClickedCommand( lst.first(), mFolder->identity(), 0, false ); 02378 } 02379 if ( command ) { 02380 connect( command, SIGNAL( completed( KMCommand * ) ), 02381 this, SLOT( commandCompleted( KMCommand * ) ) ); 02382 setDeletesItself( true ); 02383 setEmitsCompletedItself( true ); 02384 command->start(); 02385 return OK; 02386 } 02387 return Failed; 02388 } 02389 02390 void KMMailingListCommand::commandCompleted( KMCommand *command ) 02391 { 02392 setResult( command->result() ); 02393 emit completed( this ); 02394 delete this; 02395 } 02396 02397 KMMailingListPostCommand::KMMailingListPostCommand( QWidget *parent, KMFolder *folder ) 02398 : KMMailingListCommand( parent, folder ) 02399 { 02400 } 02401 KURL::List KMMailingListPostCommand::urls() const 02402 { 02403 return mFolder->mailingList().postURLS(); 02404 } 02405 02406 KMMailingListSubscribeCommand::KMMailingListSubscribeCommand( QWidget *parent, KMFolder *folder ) 02407 : KMMailingListCommand( parent, folder ) 02408 { 02409 } 02410 KURL::List KMMailingListSubscribeCommand::urls() const 02411 { 02412 return mFolder->mailingList().subscribeURLS(); 02413 } 02414 02415 KMMailingListUnsubscribeCommand::KMMailingListUnsubscribeCommand( QWidget *parent, KMFolder *folder ) 02416 : KMMailingListCommand( parent, folder ) 02417 { 02418 } 02419 KURL::List KMMailingListUnsubscribeCommand::urls() const 02420 { 02421 return mFolder->mailingList().unsubscribeURLS(); 02422 } 02423 02424 KMMailingListArchivesCommand::KMMailingListArchivesCommand( QWidget *parent, KMFolder *folder ) 02425 : KMMailingListCommand( parent, folder ) 02426 { 02427 } 02428 KURL::List KMMailingListArchivesCommand::urls() const 02429 { 02430 return mFolder->mailingList().archiveURLS(); 02431 } 02432 02433 KMMailingListHelpCommand::KMMailingListHelpCommand( QWidget *parent, KMFolder *folder ) 02434 : KMMailingListCommand( parent, folder ) 02435 { 02436 } 02437 KURL::List KMMailingListHelpCommand::urls() const 02438 { 02439 return mFolder->mailingList().helpURLS(); 02440 } 02441 02442 KMIMChatCommand::KMIMChatCommand( const KURL &url, KMMessage *msg ) 02443 :mUrl( url ), mMessage( msg ) 02444 { 02445 } 02446 02447 KMCommand::Result KMIMChatCommand::execute() 02448 { 02449 kdDebug( 5006 ) << k_funcinfo << " URL is: " << mUrl << endl; 02450 // find UID for mail address 02451 KABC::AddressBook *addressBook = KABC::StdAddressBook::self(); 02452 KABC::AddresseeList addresses = addressBook->findByEmail( KPIM::getEmailAddr( mUrl.path() ) ) ; 02453 02454 // start chat 02455 if( addresses.count() == 1 ) { 02456 kmkernel->imProxy()->chatWithContact( addresses[0].uid() ); 02457 return OK; 02458 } 02459 else 02460 { 02461 kdDebug( 5006 ) << "Didn't find exactly one addressee, couldn't tell who to chat to for that email address. Count = " << addresses.count() << endl; 02462 02463 QString apology; 02464 if ( addresses.isEmpty() ) 02465 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." ); 02466 else 02467 { 02468 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." ); 02469 QStringList nameList; 02470 KABC::AddresseeList::const_iterator it = addresses.begin(); 02471 KABC::AddresseeList::const_iterator end = addresses.end(); 02472 for ( ; it != end; ++it ) 02473 { 02474 nameList.append( (*it).realName() ); 02475 } 02476 QString names = nameList.join( QString::fromLatin1( ",\n" ) ); 02477 apology = apology.arg( names ); 02478 } 02479 02480 KMessageBox::sorry( parentWidget(), apology ); 02481 return Failed; 02482 } 02483 }
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Oct 21 19:46:46 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003