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