kmcommands.cpp
00001 /* -*- mode: C++; c-file-style: "gnu" -*- 00002 This file is part of KMail, the KDE mail client. 00003 Copyright (c) 2002 Don Sanders <sanders@kde.org> 00004 00005 KMail is free software; you can redistribute it and/or modify it 00006 under the terms of the GNU General Public License, version 2, as 00007 published by the Free Software Foundation. 00008 00009 KMail is distributed in the hope that it will be useful, but 00010 WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 General Public License for more details. 00013 00014 You should have received a copy of the GNU General Public License 00015 along with this program; if not, write to the Free Software 00016 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00017 */ 00018 00019 // 00020 // This file implements various "command" classes. These command classes 00021 // are based on the command design pattern. 00022 // 00023 // Historically various operations were implemented as slots of KMMainWin. 00024 // This proved inadequate as KMail has multiple top level windows 00025 // (KMMainWin, KMReaderMainWin, SearchWindow, KMComposeWin) that may 00026 // benefit from using these operations. It is desirable that these 00027 // classes can operate without depending on or altering the state of 00028 // a KMMainWin, in fact it is possible no KMMainWin object even exists. 00029 // 00030 // Now these operations have been rewritten as KMCommand based classes, 00031 // making them independent of KMMainWin. 00032 // 00033 // The base command class KMCommand is async, which is a difference 00034 // from the conventional command pattern. As normal derived classes implement 00035 // the execute method, but client classes call start() instead of 00036 // calling execute() directly. start() initiates async operations, 00037 // and on completion of these operations calls execute() and then deletes 00038 // the command. (So the client must not construct commands on the stack). 00039 // 00040 // The type of async operation supported by KMCommand is retrieval 00041 // of messages from an IMAP server. 00042 00043 #include "kmcommands.h" 00044 00045 #ifdef HAVE_CONFIG_H 00046 #include <config.h> 00047 #endif 00048 00049 #include <errno.h> 00050 #include <mimelib/enum.h> 00051 #include <mimelib/field.h> 00052 #include <mimelib/mimepp.h> 00053 #include <mimelib/string.h> 00054 #include <kapplication.h> 00055 #include <dcopclient.h> 00056 00057 #include <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 <kfiledialog.h> 00065 #include <kabc/stdaddressbook.h> 00066 #include <kabc/addresseelist.h> 00067 #include <kdirselectdialog.h> 00068 #include <klocale.h> 00069 #include <kmessagebox.h> 00070 #include <kparts/browserextension.h> 00071 #include <kprogress.h> 00072 #include <krun.h> 00073 #include <kbookmarkmanager.h> 00074 #include <kstandarddirs.h> 00075 #include <ktempfile.h> 00076 #include <kimproxy.h> 00077 #include <kuserprofile.h> 00078 // KIO headers 00079 #include <kio/job.h> 00080 #include <kio/netaccess.h> 00081 00082 #include <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 <libkdepim/kfileio.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 ( KIO::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 KIO::Job *job = KIO::file_copy(mUrl, saveUrl, -1, true); 00648 connect(job, TQT_SIGNAL(result(KIO::Job*)), TQT_SLOT(slotUrlSaveResult(KIO::Job*))); 00649 setEmitsCompletedItself( true ); 00650 return OK; 00651 } 00652 00653 void KMUrlSaveCommand::slotUrlSaveResult( KIO::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(KGlobalSettings::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 = KIO::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(KIO::Job*, TQByteArray &)), 00849 TQT_SLOT(slotSaveDataReq())); 00850 connect(mJob, TQT_SIGNAL(result(KIO::Job*)), 00851 TQT_SLOT(slotSaveResult(KIO::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( KIO::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. kio 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(KIO::Job *job) 00959 { 00960 if (job->error()) 00961 { 00962 if (job->error() == KIO::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 = KIO::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(KIO::Job*, TQByteArray &)), 00975 TQT_SLOT(slotSaveDataReq())); 00976 connect(mJob, TQT_SIGNAL(result(KIO::Job*)), 00977 TQT_SLOT(slotSaveResult(KIO::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 = KIO::get( mUrl, false, false ); 01016 mJob->setReportDataSent( true ); 01017 connect( mJob, TQT_SIGNAL( data( KIO::Job *, const TQByteArray & ) ), 01018 this, TQT_SLOT( slotDataArrived( KIO::Job*, const TQByteArray & ) ) ); 01019 connect( mJob, TQT_SIGNAL( result( KIO::Job * ) ), 01020 TQT_SLOT( slotResult( KIO::Job * ) ) ); 01021 setEmitsCompletedItself( true ); 01022 return OK; 01023 } 01024 01025 void KMOpenMsgCommand::slotDataArrived( KIO::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( KIO::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 KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection ); 01119 KMail::Composer * win = KMail::makeComposer( reply ); 01120 win->setCharset( msg->codec()->mimeName(), true ); 01121 win->setReplyFocus(); 01122 win->show(); 01123 01124 return OK; 01125 } 01126 01127 01128 KMNoQuoteReplyToCommand::KMNoQuoteReplyToCommand( TQWidget *parent, 01129 KMMessage *msg ) 01130 : KMCommand( parent, msg ) 01131 { 01132 } 01133 01134 KMCommand::Result KMNoQuoteReplyToCommand::execute() 01135 { 01136 KCursorSaver busy(KBusyPtr::busy()); 01137 KMMessage *msg = retrievedMessage(); 01138 if ( !msg || !msg->codec() ) { 01139 return Failed; 01140 } 01141 KMMessage *reply = msg->createReply( KMail::ReplySmart, "", true); 01142 KMail::Composer * win = KMail::makeComposer( reply ); 01143 win->setCharset(msg->codec()->mimeName(), true); 01144 win->setReplyFocus(false); 01145 win->show(); 01146 01147 return OK; 01148 } 01149 01150 01151 KMReplyListCommand::KMReplyListCommand( TQWidget *parent, 01152 KMMessage *msg, const TQString &selection ) 01153 : KMCommand( parent, msg ), mSelection( selection ) 01154 { 01155 } 01156 01157 KMCommand::Result KMReplyListCommand::execute() 01158 { 01159 KCursorSaver busy(KBusyPtr::busy()); 01160 KMMessage *msg = retrievedMessage(); 01161 if ( !msg || !msg->codec() ) { 01162 return Failed; 01163 } 01164 KMMessage *reply = msg->createReply( KMail::ReplyList, mSelection); 01165 KMail::Composer * win = KMail::makeComposer( reply ); 01166 win->setCharset(msg->codec()->mimeName(), true); 01167 win->setReplyFocus(false); 01168 win->show(); 01169 01170 return OK; 01171 } 01172 01173 01174 KMReplyToAllCommand::KMReplyToAllCommand( TQWidget *parent, 01175 KMMessage *msg, const TQString &selection ) 01176 :KMCommand( parent, msg ), mSelection( selection ) 01177 { 01178 } 01179 01180 KMCommand::Result KMReplyToAllCommand::execute() 01181 { 01182 KCursorSaver busy(KBusyPtr::busy()); 01183 KMMessage *msg = retrievedMessage(); 01184 if ( !msg || !msg->codec() ) { 01185 return Failed; 01186 } 01187 KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection ); 01188 KMail::Composer * win = KMail::makeComposer( reply ); 01189 win->setCharset( msg->codec()->mimeName(), true ); 01190 win->setReplyFocus(); 01191 win->show(); 01192 01193 return OK; 01194 } 01195 01196 01197 KMReplyAuthorCommand::KMReplyAuthorCommand( TQWidget *parent, KMMessage *msg, 01198 const TQString &selection ) 01199 : KMCommand( parent, msg ), mSelection( selection ) 01200 { 01201 } 01202 01203 KMCommand::Result KMReplyAuthorCommand::execute() 01204 { 01205 KCursorSaver busy(KBusyPtr::busy()); 01206 KMMessage *msg = retrievedMessage(); 01207 if ( !msg || !msg->codec() ) { 01208 return Failed; 01209 } 01210 KMMessage *reply = msg->createReply( KMail::ReplyAuthor, mSelection ); 01211 KMail::Composer * win = KMail::makeComposer( reply ); 01212 win->setCharset( msg->codec()->mimeName(), true ); 01213 win->setReplyFocus(); 01214 win->show(); 01215 01216 return OK; 01217 } 01218 01219 01220 KMForwardInlineCommand::KMForwardInlineCommand( TQWidget *parent, 01221 const TQPtrList<KMMsgBase> &msgList, uint identity ) 01222 : KMCommand( parent, msgList ), 01223 mIdentity( identity ) 01224 { 01225 } 01226 01227 KMForwardInlineCommand::KMForwardInlineCommand( TQWidget *parent, 01228 KMMessage *msg, uint identity ) 01229 : KMCommand( parent, msg ), 01230 mIdentity( identity ) 01231 { 01232 } 01233 01234 KMCommand::Result KMForwardInlineCommand::execute() 01235 { 01236 TQPtrList<KMMessage> msgList = retrievedMsgs(); 01237 01238 if (msgList.count() >= 2) { // Multiple forward 01239 01240 uint id = 0; 01241 TQPtrList<KMMessage> linklist; 01242 for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) { 01243 // set the identity 01244 if (id == 0) 01245 id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt(); 01246 01247 // msgText += msg->createForwardBody(); 01248 linklist.append( msg ); 01249 } 01250 if ( id == 0 ) 01251 id = mIdentity; // use folder identity if no message had an id set 01252 KMMessage *fwdMsg = new KMMessage; 01253 fwdMsg->initHeader( id ); 01254 fwdMsg->setAutomaticFields( true ); 01255 fwdMsg->setCharset( "utf-8" ); 01256 // fwdMsg->setBody( msgText ); 01257 01258 for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) { 01259 TemplateParser parser( fwdMsg, TemplateParser::Forward ); 01260 parser.setSelection( msg->body() ); // FIXME: Why is this needed? 01261 parser.process( msg, 0, true ); 01262 01263 fwdMsg->link( msg, KMMsgStatusForwarded ); 01264 } 01265 01266 KCursorSaver busy( KBusyPtr::busy() ); 01267 KMail::Composer * win = KMail::makeComposer( fwdMsg, id ); 01268 win->setCharset(""); 01269 win->show(); 01270 01271 } else { // forward a single message at most 01272 01273 KMMessage *msg = msgList.getFirst(); 01274 if ( !msg || !msg->codec() ) 01275 return Failed; 01276 01277 KCursorSaver busy( KBusyPtr::busy() ); 01278 KMMessage *fwdMsg = msg->createForward(); 01279 01280 uint id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt(); 01281 if ( id == 0 ) 01282 id = mIdentity; 01283 { 01284 KMail::Composer * win = KMail::makeComposer( fwdMsg, id ); 01285 win->setCharset( fwdMsg->codec()->mimeName(), true ); 01286 win->show(); 01287 } 01288 } 01289 return OK; 01290 } 01291 01292 01293 KMForwardAttachedCommand::KMForwardAttachedCommand( TQWidget *parent, 01294 const TQPtrList<KMMsgBase> &msgList, uint identity, KMail::Composer *win ) 01295 : KMCommand( parent, msgList ), mIdentity( identity ), 01296 mWin( TQGuardedPtr<KMail::Composer>( win )) 01297 { 01298 } 01299 01300 KMForwardAttachedCommand::KMForwardAttachedCommand( TQWidget *parent, 01301 KMMessage * msg, uint identity, KMail::Composer *win ) 01302 : KMCommand( parent, msg ), mIdentity( identity ), 01303 mWin( TQGuardedPtr< KMail::Composer >( win )) 01304 { 01305 } 01306 01307 KMCommand::Result KMForwardAttachedCommand::execute() 01308 { 01309 TQPtrList<KMMessage> msgList = retrievedMsgs(); 01310 KMMessage *fwdMsg = new KMMessage; 01311 01312 if (msgList.count() >= 2) { 01313 // don't respect X-KMail-Identity headers because they might differ for 01314 // the selected mails 01315 fwdMsg->initHeader(mIdentity); 01316 } 01317 else if (msgList.count() == 1) { 01318 KMMessage *msg = msgList.getFirst(); 01319 fwdMsg->initFromMessage(msg); 01320 fwdMsg->setSubject( msg->forwardSubject() ); 01321 } 01322 01323 fwdMsg->setAutomaticFields(true); 01324 01325 KCursorSaver busy(KBusyPtr::busy()); 01326 if (!mWin) 01327 mWin = KMail::makeComposer(fwdMsg, mIdentity); 01328 01329 // iterate through all the messages to be forwarded 01330 for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) { 01331 // remove headers that shouldn't be forwarded 01332 msg->removePrivateHeaderFields(); 01333 msg->removeHeaderField("BCC"); 01334 // set the part 01335 KMMessagePart *msgPart = new KMMessagePart; 01336 msgPart->setTypeStr("message"); 01337 msgPart->setSubtypeStr("rfc822"); 01338 msgPart->setCharset(msg->charset()); 01339 msgPart->setName("forwarded message"); 01340 msgPart->setContentDescription(msg->from()+": "+msg->subject()); 01341 msgPart->setContentDisposition( "inline" ); 01342 // THIS HAS TO BE AFTER setCte()!!!! 01343 msgPart->setMessageBody( KMail::Util::ByteArray( msg->asDwString() ) ); 01344 msgPart->setCharset(""); 01345 01346 fwdMsg->link(msg, KMMsgStatusForwarded); 01347 mWin->addAttach(msgPart); 01348 } 01349 01350 mWin->show(); 01351 01352 return OK; 01353 } 01354 01355 01356 KMForwardDigestCommand::KMForwardDigestCommand( TQWidget *parent, 01357 const TQPtrList<KMMsgBase> &msgList, uint identity, KMail::Composer *win ) 01358 : KMCommand( parent, msgList ), mIdentity( identity ), 01359 mWin( TQGuardedPtr<KMail::Composer>( win )) 01360 { 01361 } 01362 01363 KMForwardDigestCommand::KMForwardDigestCommand( TQWidget *parent, 01364 KMMessage * msg, uint identity, KMail::Composer *win ) 01365 : KMCommand( parent, msg ), mIdentity( identity ), 01366 mWin( TQGuardedPtr< KMail::Composer >( win )) 01367 { 01368 } 01369 01370 KMCommand::Result KMForwardDigestCommand::execute() 01371 { 01372 TQPtrList<KMMessage> msgList = retrievedMsgs(); 01373 01374 if ( msgList.count() < 2 ) 01375 return Undefined; // must have more than 1 for a digest 01376 01377 uint id = 0; 01378 KMMessage *fwdMsg = new KMMessage; 01379 KMMessagePart *msgPart = new KMMessagePart; 01380 TQString msgPartText; 01381 int msgCnt = 0; // incase there are some we can't forward for some reason 01382 01383 // dummy header initialization; initialization with the correct identity 01384 // is done below 01385 fwdMsg->initHeader( id ); 01386 fwdMsg->setAutomaticFields( true ); 01387 fwdMsg->mMsg->Headers().ContentType().CreateBoundary( 1 ); 01388 TQCString boundary( fwdMsg->mMsg->Headers().ContentType().Boundary().c_str() ); 01389 msgPartText = i18n("\nThis is a MIME digest forward. The content of the" 01390 " message is contained in the attachment(s).\n\n\n"); 01391 // iterate through all the messages to be forwarded 01392 for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) { 01393 // set the identity 01394 if ( id == 0 ) 01395 id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt(); 01396 // set the part header 01397 msgPartText += "--"; 01398 msgPartText += TQString::fromLatin1( boundary ); 01399 msgPartText += "\nContent-Type: MESSAGE/RFC822"; 01400 msgPartText += TQString( "; CHARSET=%1" ).arg( TQString(msg->charset()) ); 01401 msgPartText += '\n'; 01402 DwHeaders dwh; 01403 dwh.MessageId().CreateDefault(); 01404 msgPartText += TQString( "Content-ID: %1\n" ).arg( dwh.MessageId().AsString().c_str() ); 01405 msgPartText += TQString( "Content-Description: %1" ).arg( msg->subject() ); 01406 if ( !msg->subject().contains( "(fwd)" ) ) 01407 msgPartText += " (fwd)"; 01408 msgPartText += "\n\n"; 01409 // remove headers that shouldn't be forwarded 01410 msg->removePrivateHeaderFields(); 01411 msg->removeHeaderField( "BCC" ); 01412 // set the part 01413 msgPartText += msg->headerAsString(); 01414 msgPartText += '\n'; 01415 msgPartText += msg->body(); 01416 msgPartText += '\n'; // eot 01417 msgCnt++; 01418 fwdMsg->link( msg, KMMsgStatusForwarded ); 01419 } 01420 01421 if ( id == 0 ) 01422 id = mIdentity; // use folder identity if no message had an id set 01423 fwdMsg->initHeader( id ); 01424 msgPartText += "--"; 01425 msgPartText += TQString::fromLatin1( boundary ); 01426 msgPartText += "--\n"; 01427 TQCString tmp; 01428 msgPart->setTypeStr( "MULTIPART" ); 01429 tmp.sprintf( "Digest; boundary=\"%s\"", boundary.data() ); 01430 msgPart->setSubtypeStr( tmp ); 01431 msgPart->setName( "unnamed" ); 01432 msgPart->setCte( DwMime::kCte7bit ); // does it have to be 7bit? 01433 msgPart->setContentDescription( TQString( "Digest of %1 messages." ).arg( msgCnt ) ); 01434 // THIS HAS TO BE AFTER setCte()!!!! 01435 msgPart->setBodyEncoded( TQCString( msgPartText.ascii() ) ); 01436 KCursorSaver busy( KBusyPtr::busy() ); 01437 KMail::Composer * win = KMail::makeComposer( fwdMsg, id ); 01438 win->addAttach( msgPart ); 01439 win->show(); 01440 return OK; 01441 } 01442 01443 KMRedirectCommand::KMRedirectCommand( TQWidget *parent, 01444 KMMessage *msg ) 01445 : KMCommand( parent, msg ) 01446 { 01447 } 01448 01449 KMCommand::Result KMRedirectCommand::execute() 01450 { 01451 KMMessage *msg = retrievedMessage(); 01452 if ( !msg || !msg->codec() ) 01453 return Failed; 01454 01455 RedirectDialog dlg( parentWidget(), "redirect", true, 01456 kmkernel->msgSender()->sendImmediate() ); 01457 if (dlg.exec()==TQDialog::Rejected) return Failed; 01458 01459 KMMessage *newMsg = msg->createRedirect( dlg.to() ); 01460 KMFilterAction::sendMDN( msg, KMime::MDN::Dispatched ); 01461 01462 const KMail::MessageSender::SendMethod method = dlg.sendImmediate() 01463 ? KMail::MessageSender::SendImmediate 01464 : KMail::MessageSender::SendLater; 01465 if ( !kmkernel->msgSender()->send( newMsg, method ) ) { 01466 kdDebug(5006) << "KMRedirectCommand: could not redirect message (sending failed)" << endl; 01467 return Failed; // error: couldn't send 01468 } 01469 return OK; 01470 } 01471 01472 01473 KMCustomReplyToCommand::KMCustomReplyToCommand( TQWidget *parent, KMMessage *msg, 01474 const TQString &selection, 01475 const TQString &tmpl ) 01476 : KMCommand( parent, msg ), mSelection( selection ), mTemplate( tmpl ) 01477 { 01478 } 01479 01480 KMCommand::Result KMCustomReplyToCommand::execute() 01481 { 01482 KCursorSaver busy(KBusyPtr::busy()); 01483 KMMessage *msg = retrievedMessage(); 01484 if ( !msg || !msg->codec() ) { 01485 return Failed; 01486 } 01487 KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection, 01488 false, true, mTemplate ); 01489 KMail::Composer * win = KMail::makeComposer( reply ); 01490 win->setCharset( msg->codec()->mimeName(), true ); 01491 win->setReplyFocus(); 01492 win->show(); 01493 01494 return OK; 01495 } 01496 01497 01498 KMCustomReplyAllToCommand::KMCustomReplyAllToCommand( TQWidget *parent, KMMessage *msg, 01499 const TQString &selection, 01500 const TQString &tmpl ) 01501 : KMCommand( parent, msg ), mSelection( selection ), mTemplate( tmpl ) 01502 { 01503 } 01504 01505 KMCommand::Result KMCustomReplyAllToCommand::execute() 01506 { 01507 KCursorSaver busy(KBusyPtr::busy()); 01508 KMMessage *msg = retrievedMessage(); 01509 if ( !msg || !msg->codec() ) { 01510 return Failed; 01511 } 01512 KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection, 01513 false, true, mTemplate ); 01514 KMail::Composer * win = KMail::makeComposer( reply ); 01515 win->setCharset( msg->codec()->mimeName(), true ); 01516 win->setReplyFocus(); 01517 win->show(); 01518 01519 return OK; 01520 } 01521 01522 01523 KMCustomForwardCommand::KMCustomForwardCommand( TQWidget *parent, 01524 const TQPtrList<KMMsgBase> &msgList, uint identity, const TQString &tmpl ) 01525 : KMCommand( parent, msgList ), 01526 mIdentity( identity ), mTemplate( tmpl ) 01527 { 01528 } 01529 01530 KMCustomForwardCommand::KMCustomForwardCommand( TQWidget *parent, 01531 KMMessage *msg, uint identity, const TQString &tmpl ) 01532 : KMCommand( parent, msg ), 01533 mIdentity( identity ), mTemplate( tmpl ) 01534 { 01535 } 01536 01537 KMCommand::Result KMCustomForwardCommand::execute() 01538 { 01539 TQPtrList<KMMessage> msgList = retrievedMsgs(); 01540 01541 if (msgList.count() >= 2) { // Multiple forward 01542 01543 uint id = 0; 01544 TQPtrList<KMMessage> linklist; 01545 for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) { 01546 // set the identity 01547 if (id == 0) 01548 id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt(); 01549 01550 // msgText += msg->createForwardBody(); 01551 linklist.append( msg ); 01552 } 01553 if ( id == 0 ) 01554 id = mIdentity; // use folder identity if no message had an id set 01555 KMMessage *fwdMsg = new KMMessage; 01556 fwdMsg->initHeader( id ); 01557 fwdMsg->setAutomaticFields( true ); 01558 fwdMsg->setCharset( "utf-8" ); 01559 // fwdMsg->setBody( msgText ); 01560 01561 for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) { 01562 TemplateParser parser( fwdMsg, TemplateParser::Forward ); 01563 parser.setSelection( msg->body() ); // FIXME: Why is this needed? 01564 parser.process( msg, 0, true ); 01565 01566 fwdMsg->link( msg, KMMsgStatusForwarded ); 01567 } 01568 01569 KCursorSaver busy( KBusyPtr::busy() ); 01570 KMail::Composer * win = KMail::makeComposer( fwdMsg, id ); 01571 win->setCharset(""); 01572 win->show(); 01573 01574 } else { // forward a single message at most 01575 01576 KMMessage *msg = msgList.getFirst(); 01577 if ( !msg || !msg->codec() ) 01578 return Failed; 01579 01580 KCursorSaver busy( KBusyPtr::busy() ); 01581 KMMessage *fwdMsg = msg->createForward( mTemplate ); 01582 01583 uint id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt(); 01584 if ( id == 0 ) 01585 id = mIdentity; 01586 { 01587 KMail::Composer * win = KMail::makeComposer( fwdMsg, id ); 01588 win->setCharset( fwdMsg->codec()->mimeName(), true ); 01589 win->show(); 01590 } 01591 } 01592 return OK; 01593 } 01594 01595 01596 KMPrintCommand::KMPrintCommand( TQWidget *parent, KMMessage *msg, 01597 const KMail::HeaderStyle *headerStyle, 01598 const KMail::HeaderStrategy *headerStrategy, 01599 bool htmlOverride, bool htmlLoadExtOverride, 01600 bool useFixedFont, const TQString & encoding ) 01601 : KMCommand( parent, msg ), 01602 mHeaderStyle( headerStyle ), mHeaderStrategy( headerStrategy ), 01603 mHtmlOverride( htmlOverride ), 01604 mHtmlLoadExtOverride( htmlLoadExtOverride ), 01605 mUseFixedFont( useFixedFont ), mEncoding( encoding ) 01606 { 01607 if ( GlobalSettings::useDefaultFonts() ) 01608 mOverrideFont = KGlobalSettings::generalFont(); 01609 else { 01610 KConfigGroup fonts( KMKernel::config(), "Fonts" ); 01611 TQString tmp = fonts.readEntry( "print-font", KGlobalSettings::generalFont().toString() ); 01612 mOverrideFont.fromString( tmp ); 01613 } 01614 } 01615 01616 01617 void KMPrintCommand::setOverrideFont( const TQFont& font ) 01618 { 01619 mOverrideFont = font; 01620 } 01621 01622 KMCommand::Result KMPrintCommand::execute() 01623 { 01624 KMReaderWin printWin( 0, 0, 0 ); 01625 printWin.setPrinting( true ); 01626 printWin.readConfig(); 01627 if ( mHeaderStyle != 0 && mHeaderStrategy != 0 ) 01628 printWin.setHeaderStyleAndStrategy( mHeaderStyle, mHeaderStrategy ); 01629 printWin.setHtmlOverride( mHtmlOverride ); 01630 printWin.setHtmlLoadExtOverride( mHtmlLoadExtOverride ); 01631 printWin.setUseFixedFont( mUseFixedFont ); 01632 printWin.setOverrideEncoding( mEncoding ); 01633 printWin.cssHelper()->setPrintFont( mOverrideFont ); 01634 printWin.setDecryptMessageOverwrite( true ); 01635 printWin.setMsg( retrievedMessage(), true ); 01636 printWin.printMsg(); 01637 01638 return OK; 01639 } 01640 01641 01642 KMSeStatusCommand::KMSeStatusCommand( KMMsgStatus status, 01643 const TQValueList<TQ_UINT32> &serNums, bool toggle ) 01644 : mStatus( status ), mSerNums( serNums ), mToggle( toggle ) 01645 { 01646 } 01647 01648 KMCommand::Result KMSeStatusCommand::execute() 01649 { 01650 TQValueListIterator<TQ_UINT32> it; 01651 int idx = -1; 01652 KMFolder *folder = 0; 01653 bool parenStatus = false; 01654 01655 // Toggle actions on threads toggle the whole thread 01656 // depending on the state of the parent. 01657 if (mToggle) { 01658 KMMsgBase *msg; 01659 KMMsgDict::instance()->getLocation( *mSerNums.begin(), &folder, &idx ); 01660 if (folder) { 01661 msg = folder->getMsgBase(idx); 01662 if (msg && (msg->status()&mStatus)) 01663 parenStatus = true; 01664 else 01665 parenStatus = false; 01666 } 01667 } 01668 TQMap< KMFolder*, TQValueList<int> > folderMap; 01669 for ( it = mSerNums.begin(); it != mSerNums.end(); ++it ) { 01670 KMMsgDict::instance()->getLocation( *it, &folder, &idx ); 01671 if (folder) { 01672 if (mToggle) { 01673 KMMsgBase *msg = folder->getMsgBase(idx); 01674 // check if we are already at the target toggle state 01675 if (msg) { 01676 bool myStatus; 01677 if (msg->status()&mStatus) 01678 myStatus = true; 01679 else 01680 myStatus = false; 01681 if (myStatus != parenStatus) 01682 continue; 01683 } 01684 } 01685 /* Collect the ids for each folder in a separate list and 01686 send them off in one go at the end. */ 01687 folderMap[folder].append(idx); 01688 } 01689 } 01690 TQMapIterator< KMFolder*, TQValueList<int> > it2 = folderMap.begin(); 01691 while ( it2 != folderMap.end() ) { 01692 KMFolder *f = it2.key(); 01693 f->setStatus( (*it2), mStatus, mToggle ); 01694 ++it2; 01695 } 01696 //kapp->dcopClient()->emitDCOPSignal( "unreadCountChanged()", TQByteArray() ); 01697 01698 return OK; 01699 } 01700 01701 01702 KMFilterCommand::KMFilterCommand( const TQCString &field, const TQString &value ) 01703 : mField( field ), mValue( value ) 01704 { 01705 } 01706 01707 KMCommand::Result KMFilterCommand::execute() 01708 { 01709 kmkernel->filterMgr()->createFilter( mField, mValue ); 01710 01711 return OK; 01712 } 01713 01714 01715 KMFilterActionCommand::KMFilterActionCommand( TQWidget *parent, 01716 const TQPtrList<KMMsgBase> &msgList, 01717 KMFilter *filter ) 01718 : KMCommand( parent, msgList ), mFilter( filter ) 01719 { 01720 TQPtrListIterator<KMMsgBase> it(msgList); 01721 while ( it.current() ) { 01722 serNumList.append( (*it)->getMsgSerNum() ); 01723 ++it; 01724 } 01725 } 01726 01727 KMCommand::Result KMFilterActionCommand::execute() 01728 { 01729 KCursorSaver busy( KBusyPtr::busy() ); 01730 01731 int msgCount = 0; 01732 int msgCountToFilter = serNumList.count(); 01733 ProgressItem* progressItem = 01734 ProgressManager::createProgressItem ( "filter"+ProgressManager::getUniqueID(), 01735 i18n( "Filtering messages" ) ); 01736 progressItem->setTotalItems( msgCountToFilter ); 01737 TQValueList<TQ_UINT32>::const_iterator it; 01738 for ( it = serNumList.begin(); it != serNumList.end(); it++ ) { 01739 TQ_UINT32 serNum = *it; 01740 int diff = msgCountToFilter - ++msgCount; 01741 if ( diff < 10 || !( msgCount % 20 ) || msgCount <= 10 ) { 01742 progressItem->updateProgress(); 01743 TQString statusMsg = i18n("Filtering message %1 of %2"); 01744 statusMsg = statusMsg.arg( msgCount ).arg( msgCountToFilter ); 01745 KPIM::BroadcastStatus::instance()->setStatusMsg( statusMsg ); 01746 KApplication::kApplication()->eventLoop()->processEvents( TQEventLoop::ExcludeUserInput, 50 ); 01747 } 01748 01749 int filterResult = kmkernel->filterMgr()->process( serNum, mFilter ); 01750 if (filterResult == 2) { 01751 // something went horribly wrong (out of space?) 01752 perror("Critical error"); 01753 kmkernel->emergencyExit( i18n("Not enough free disk space?" )); 01754 } 01755 progressItem->incCompletedItems(); 01756 } 01757 01758 progressItem->setComplete(); 01759 progressItem = 0; 01760 return OK; 01761 } 01762 01763 01764 KMMetaFilterActionCommand::KMMetaFilterActionCommand( KMFilter *filter, 01765 KMHeaders *headers, 01766 KMMainWidget *main ) 01767 : TQObject( main ), 01768 mFilter( filter ), mHeaders( headers ), mMainWidget( main ) 01769 { 01770 } 01771 01772 void KMMetaFilterActionCommand::start() 01773 { 01774 if (ActionScheduler::isEnabled() ) { 01775 // use action scheduler 01776 KMFilterMgr::FilterSet set = KMFilterMgr::All; 01777 TQValueList<KMFilter*> filters; 01778 filters.append( mFilter ); 01779 ActionScheduler *scheduler = new ActionScheduler( set, filters, mHeaders ); 01780 scheduler->setAlwaysMatch( true ); 01781 scheduler->setAutoDestruct( true ); 01782 01783 int contentX, contentY; 01784 HeaderItem *nextItem = mHeaders->prepareMove( &contentX, &contentY ); 01785 TQPtrList<KMMsgBase> msgList = *mHeaders->selectedMsgs(true); 01786 mHeaders->finalizeMove( nextItem, contentX, contentY ); 01787 01788 for (KMMsgBase *msg = msgList.first(); msg; msg = msgList.next()) 01789 scheduler->execFilters( msg ); 01790 } else { 01791 KMCommand *filterCommand = 01792 new KMFilterActionCommand( mMainWidget, 01793 *mHeaders->selectedMsgs(), mFilter ); 01794 filterCommand->start(); 01795 int contentX, contentY; 01796 HeaderItem *item = mHeaders->prepareMove( &contentX, &contentY ); 01797 mHeaders->finalizeMove( item, contentX, contentY ); 01798 } 01799 } 01800 01801 FolderShortcutCommand::FolderShortcutCommand( KMMainWidget *mainwidget, 01802 KMFolder *folder ) 01803 : mMainWidget( mainwidget ), mFolder( folder ), mAction( 0 ) 01804 { 01805 } 01806 01807 01808 FolderShortcutCommand::~FolderShortcutCommand() 01809 { 01810 if ( mAction ) mAction->unplugAll(); 01811 delete mAction; 01812 } 01813 01814 void FolderShortcutCommand::start() 01815 { 01816 mMainWidget->slotSelectFolder( mFolder ); 01817 } 01818 01819 void FolderShortcutCommand::setAction( KAction* action ) 01820 { 01821 mAction = action; 01822 } 01823 01824 KMMailingListFilterCommand::KMMailingListFilterCommand( TQWidget *parent, 01825 KMMessage *msg ) 01826 : KMCommand( parent, msg ) 01827 { 01828 } 01829 01830 KMCommand::Result KMMailingListFilterCommand::execute() 01831 { 01832 TQCString name; 01833 TQString value; 01834 KMMessage *msg = retrievedMessage(); 01835 if (!msg) 01836 return Failed; 01837 01838 if ( !MailingList::name( msg, name, value ).isEmpty() ) { 01839 kmkernel->filterMgr()->createFilter( name, value ); 01840 return OK; 01841 } 01842 else 01843 return Failed; 01844 } 01845 01846 01847 void KMMenuCommand::folderToPopupMenu(bool move, 01848 TQObject *receiver, KMMenuToFolder *aMenuToFolder, TQPopupMenu *menu ) 01849 { 01850 while ( menu->count() ) 01851 { 01852 TQPopupMenu *popup = menu->findItem( menu->idAt( 0 ) )->popup(); 01853 if (popup) 01854 delete popup; 01855 else 01856 menu->removeItemAt( 0 ); 01857 } 01858 01859 if (!kmkernel->imapFolderMgr()->dir().first() && 01860 !kmkernel->dimapFolderMgr()->dir().first()) 01861 { // only local folders 01862 makeFolderMenu( &kmkernel->folderMgr()->dir(), move, 01863 receiver, aMenuToFolder, menu ); 01864 } else { 01865 // operate on top-level items 01866 TQPopupMenu* subMenu = new TQPopupMenu(menu); 01867 makeFolderMenu( &kmkernel->folderMgr()->dir(), 01868 move, receiver, aMenuToFolder, subMenu ); 01869 menu->insertItem( i18n( "Local Folders" ), subMenu ); 01870 KMFolderDir* fdir = &kmkernel->imapFolderMgr()->dir(); 01871 for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) { 01872 if (node->isDir()) 01873 continue; 01874 subMenu = new TQPopupMenu(menu); 01875 makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu ); 01876 menu->insertItem( node->label(), subMenu ); 01877 } 01878 fdir = &kmkernel->dimapFolderMgr()->dir(); 01879 for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) { 01880 if (node->isDir()) 01881 continue; 01882 subMenu = new TQPopupMenu(menu); 01883 makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu ); 01884 menu->insertItem( node->label(), subMenu ); 01885 } 01886 } 01887 } 01888 01889 void KMMenuCommand::makeFolderMenu(KMFolderNode* node, bool move, 01890 TQObject *receiver, KMMenuToFolder *aMenuToFolder, TQPopupMenu *menu ) 01891 { 01892 // connect the signals 01893 if (move) 01894 { 01895 disconnect(menu, TQT_SIGNAL(activated(int)), receiver, 01896 TQT_SLOT(moveSelectedToFolder(int))); 01897 connect(menu, TQT_SIGNAL(activated(int)), receiver, 01898 TQT_SLOT(moveSelectedToFolder(int))); 01899 } else { 01900 disconnect(menu, TQT_SIGNAL(activated(int)), receiver, 01901 TQT_SLOT(copySelectedToFolder(int))); 01902 connect(menu, TQT_SIGNAL(activated(int)), receiver, 01903 TQT_SLOT(copySelectedToFolder(int))); 01904 } 01905 01906 KMFolder *folder = 0; 01907 KMFolderDir *folderDir = 0; 01908 if (node->isDir()) { 01909 folderDir = static_cast<KMFolderDir*>(node); 01910 } else { 01911 folder = static_cast<KMFolder*>(node); 01912 folderDir = folder->child(); 01913 } 01914 01915 if (folder && !folder->noContent()) 01916 { 01917 int menuId; 01918 if (move) 01919 menuId = menu->insertItem(i18n("Move to This Folder")); 01920 else 01921 menuId = menu->insertItem(i18n("Copy to This Folder")); 01922 aMenuToFolder->insert( menuId, folder ); 01923 menu->setItemEnabled( menuId, !folder->isReadOnly() ); 01924 menu->insertSeparator(); 01925 } 01926 01927 if (!folderDir) 01928 return; 01929 01930 for (KMFolderNode *it = folderDir->first(); it; it = folderDir->next() ) { 01931 if (it->isDir()) 01932 continue; 01933 KMFolder *child = static_cast<KMFolder*>(it); 01934 TQString label = child->label(); 01935 label.replace("&","&&"); 01936 if (child->child() && child->child()->first()) { 01937 // descend 01938 TQPopupMenu *subMenu = new TQPopupMenu(menu, "subMenu"); 01939 makeFolderMenu( child, move, receiver, 01940 aMenuToFolder, subMenu ); 01941 menu->insertItem( label, subMenu ); 01942 } else { 01943 // insert an item 01944 int menuId = menu->insertItem( label ); 01945 aMenuToFolder->insert( menuId, child ); 01946 menu->setItemEnabled( menuId, !child->isReadOnly() ); 01947 } 01948 } 01949 return; 01950 } 01951 01952 01953 KMCopyCommand::KMCopyCommand( KMFolder* destFolder, 01954 const TQPtrList<KMMsgBase> &msgList ) 01955 :mDestFolder( destFolder ), mMsgList( msgList ) 01956 { 01957 setDeletesItself( true ); 01958 } 01959 01960 KMCopyCommand::KMCopyCommand( KMFolder* destFolder, KMMessage * msg ) 01961 :mDestFolder( destFolder ) 01962 { 01963 setDeletesItself( true ); 01964 mMsgList.append( &msg->toMsgBase() ); 01965 } 01966 01967 KMCommand::Result KMCopyCommand::execute() 01968 { 01969 KMMsgBase *msgBase; 01970 KMMessage *msg, *newMsg; 01971 int idx = -1; 01972 bool isMessage; 01973 TQPtrList<KMMessage> list; 01974 TQPtrList<KMMessage> localList; 01975 01976 if (mDestFolder && mDestFolder->open("kmcommand") != 0) 01977 { 01978 deleteLater(); 01979 return Failed; 01980 } 01981 01982 setEmitsCompletedItself( true ); 01983 KCursorSaver busy(KBusyPtr::busy()); 01984 01985 for (msgBase = mMsgList.first(); msgBase; msgBase = mMsgList.next() ) 01986 { 01987 KMFolder *srcFolder = msgBase->parent(); 01988 if (( isMessage = msgBase->isMessage() )) 01989 { 01990 msg = static_cast<KMMessage*>(msgBase); 01991 } else { 01992 idx = srcFolder->find(msgBase); 01993 assert(idx != -1); 01994 msg = srcFolder->getMsg(idx); 01995 // corrupt IMAP cache, see FolderStorage::getMsg() 01996 if ( msg == 0 ) { 01997 KMessageBox::error( parentWidget(), i18n("Corrupt IMAP cache detected in folder %1. " 01998 "Copying of messages aborted.").arg( srcFolder->prettyURL() ) ); 01999 deleteLater(); 02000 return Failed; 02001 } 02002 } 02003 02004 if (srcFolder && mDestFolder && 02005 (srcFolder->folderType()== KMFolderTypeImap) && 02006 (mDestFolder->folderType() == KMFolderTypeImap) && 02007 (static_cast<KMFolderImap*>(srcFolder->storage())->account() == 02008 static_cast<KMFolderImap*>(mDestFolder->storage())->account())) 02009 { 02010 // imap => imap with same account 02011 list.append(msg); 02012 } else { 02013 newMsg = new KMMessage( new DwMessage( *msg->asDwMessage() ) ); 02014 newMsg->setComplete(msg->isComplete()); 02015 // make sure the attachment state is only calculated when it's complete 02016 if (!newMsg->isComplete()) 02017 newMsg->setReadyToShow(false); 02018 newMsg->setStatus(msg->status()); 02019 02020 if (srcFolder && !newMsg->isComplete()) 02021 { 02022 // imap => others 02023 newMsg->setParent(msg->parent()); 02024 FolderJob *job = srcFolder->createJob(newMsg); 02025 job->setCancellable( false ); 02026 mPendingJobs << job; 02027 connect(job, TQT_SIGNAL(messageRetrieved(KMMessage*)), 02028 mDestFolder, TQT_SLOT(reallyAddCopyOfMsg(KMMessage*))); 02029 connect( job, TQT_SIGNAL(result(KMail::FolderJob*)), 02030 this, TQT_SLOT(slotJobFinished(KMail::FolderJob*)) ); 02031 job->start(); 02032 } else { 02033 // local => others 02034 localList.append(newMsg); 02035 } 02036 } 02037 02038 if (srcFolder && !isMessage && list.isEmpty()) 02039 { 02040 assert(idx != -1); 02041 srcFolder->unGetMsg( idx ); 02042 } 02043 02044 } // end for 02045 02046 bool deleteNow = false; 02047 if (!localList.isEmpty()) 02048 { 02049 TQValueList<int> index; 02050 mDestFolder->addMsg( localList, index ); 02051 for ( TQValueListIterator<int> it = index.begin(); it != index.end(); ++it ) { 02052 mDestFolder->unGetMsg( *it ); 02053 } 02054 if ( mDestFolder->folderType() == KMFolderTypeImap ) { 02055 if ( mPendingJobs.isEmpty() ) { 02056 // wait for the end of the copy before closing the folder 02057 KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage()); 02058 connect( imapDestFolder, TQT_SIGNAL( folderComplete( KMFolderImap*, bool ) ), 02059 this, TQT_SLOT( slotFolderComplete( KMFolderImap*, bool ) ) ); 02060 } 02061 } else { 02062 deleteNow = list.isEmpty() && mPendingJobs.isEmpty(); // we're done if there are no other mails we need to fetch 02063 } 02064 } 02065 02066 //TODO: Get rid of the other cases just use this one for all types of folder 02067 //TODO: requires adding copyMsg and getFolder methods to KMFolder.h 02068 if (!list.isEmpty()) 02069 { 02070 // copy the message(s); note: the list is empty afterwards! 02071 KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage()); 02072 connect( imapDestFolder, TQT_SIGNAL( folderComplete( KMFolderImap*, bool ) ), 02073 this, TQT_SLOT( slotFolderComplete( KMFolderImap*, bool ) ) ); 02074 imapDestFolder->copyMsg(list); 02075 imapDestFolder->getFolder(); 02076 } 02077 02078 // only close the folder and delete the job if we're done 02079 // otherwise this is done in slotMsgAdded or slotFolderComplete 02080 if ( deleteNow ) 02081 { 02082 mDestFolder->close("kmcommand"); 02083 setResult( OK ); 02084 emit completed( this ); 02085 deleteLater(); 02086 } 02087 02088 return OK; 02089 } 02090 02091 void KMCopyCommand::slotJobFinished(KMail::FolderJob * job) 02092 { 02093 mPendingJobs.remove( job ); 02094 if ( job->error() ) { 02095 kdDebug(5006) << k_funcinfo << "folder job failed: " << job->error() << endl; 02096 // kill all pending jobs 02097 for ( TQValueList<KMail::FolderJob*>::Iterator it = mPendingJobs.begin(); it != mPendingJobs.end(); ++it ) { 02098 disconnect( (*it), TQT_SIGNAL(result(KMail::FolderJob*)), 02099 this, TQT_SLOT(slotJobFinished(KMail::FolderJob*)) ); 02100 (*it)->kill(); 02101 } 02102 mPendingJobs.clear(); 02103 setResult( Failed ); 02104 } 02105 02106 if ( mPendingJobs.isEmpty() ) 02107 { 02108 mDestFolder->close("kmcommand"); 02109 emit completed( this ); 02110 deleteLater(); 02111 } 02112 } 02113 02114 void KMCopyCommand::slotFolderComplete( KMFolderImap*, bool success ) 02115 { 02116 kdDebug(5006) << k_funcinfo << success << endl; 02117 if ( !success ) 02118 setResult( Failed ); 02119 mDestFolder->close( "kmcommand" ); 02120 emit completed( this ); 02121 deleteLater(); 02122 } 02123 02124 02125 KMMoveCommand::KMMoveCommand( KMFolder* destFolder, 02126 const TQPtrList<KMMsgBase> &msgList) 02127 : mDestFolder( destFolder ), mProgressItem( 0 ) 02128 { 02129 TQPtrList<KMMsgBase> tmp = msgList; 02130 for ( KMMsgBase *msgBase = tmp.first(); msgBase; msgBase = tmp.next() ) 02131 mSerNumList.append( msgBase->getMsgSerNum() ); 02132 } 02133 02134 KMMoveCommand::KMMoveCommand( KMFolder* destFolder, 02135 KMMessage *msg ) 02136 : mDestFolder( destFolder ), mProgressItem( 0 ) 02137 { 02138 mSerNumList.append( msg->getMsgSerNum() ); 02139 } 02140 02141 KMMoveCommand::KMMoveCommand( KMFolder* destFolder, 02142 KMMsgBase *msgBase ) 02143 : mDestFolder( destFolder ), mProgressItem( 0 ) 02144 { 02145 mSerNumList.append( msgBase->getMsgSerNum() ); 02146 } 02147 02148 KMMoveCommand::KMMoveCommand( TQ_UINT32 ) 02149 : mProgressItem( 0 ) 02150 { 02151 } 02152 02153 KMCommand::Result KMMoveCommand::execute() 02154 { 02155 setEmitsCompletedItself( true ); 02156 setDeletesItself( true ); 02157 typedef TQMap< KMFolder*, TQPtrList<KMMessage>* > FolderToMessageListMap; 02158 FolderToMessageListMap folderDeleteList; 02159 02160 if (mDestFolder && mDestFolder->open("kmcommand") != 0) { 02161 completeMove( Failed ); 02162 return Failed; 02163 } 02164 KCursorSaver busy(KBusyPtr::busy()); 02165 02166 // TODO set SSL state according to source and destfolder connection? 02167 Q_ASSERT( !mProgressItem ); 02168 mProgressItem = 02169 ProgressManager::createProgressItem ( 02170 "move"+ProgressManager::getUniqueID(), 02171 mDestFolder ? i18n( "Moving messages" ) : i18n( "Deleting messages" ) ); 02172 connect( mProgressItem, TQT_SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ), 02173 this, TQT_SLOT( slotMoveCanceled() ) ); 02174 02175 KMMessage *msg; 02176 int rc = 0; 02177 int index; 02178 TQPtrList<KMMessage> list; 02179 int undoId = -1; 02180 mCompleteWithAddedMsg = false; 02181 02182 if (mDestFolder) { 02183 connect (mDestFolder, TQT_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)), 02184 this, TQT_SLOT(slotMsgAddedToDestFolder(KMFolder*, TQ_UINT32))); 02185 mLostBoys = mSerNumList; 02186 } 02187 mProgressItem->setTotalItems( mSerNumList.count() ); 02188 02189 for ( TQValueList<TQ_UINT32>::ConstIterator it = mSerNumList.constBegin(); it != mSerNumList.constEnd(); ++it ) { 02190 if ( *it == 0 ) { 02191 kdDebug(5006) << k_funcinfo << "serial number == 0!" << endl; 02192 continue; // invalid message 02193 } 02194 KMFolder *srcFolder = 0; 02195 int idx = -1; 02196 KMMsgDict::instance()->getLocation( *it, &srcFolder, &idx ); 02197 if (srcFolder == mDestFolder) 02198 continue; 02199 assert(srcFolder); 02200 assert(idx != -1); 02201 if ( !srcFolder->isOpened() ) { 02202 srcFolder->open( "kmmovecommand" ); 02203 mOpenedFolders.append( srcFolder ); 02204 } 02205 msg = srcFolder->getMsg(idx); 02206 if ( !msg ) { 02207 kdDebug(5006) << k_funcinfo << "No message found for serial number " << *it << endl; 02208 continue; 02209 } 02210 bool undo = msg->enableUndo(); 02211 02212 if ( msg && msg->transferInProgress() && 02213 srcFolder->folderType() == KMFolderTypeImap ) 02214 { 02215 // cancel the download 02216 msg->setTransferInProgress( false, true ); 02217 static_cast<KMFolderImap*>(srcFolder->storage())->ignoreJobsForMessage( msg ); 02218 } 02219 02220 if (mDestFolder) { 02221 if (mDestFolder->folderType() == KMFolderTypeImap) { 02222 /* If we are moving to an imap folder, connect to it's completed 02223 * signal so we notice when all the mails should have showed up in it 02224 * but haven't for some reason. */ 02225 KMFolderImap *imapFolder = static_cast<KMFolderImap*> ( mDestFolder->storage() ); 02226 disconnect (imapFolder, TQT_SIGNAL(folderComplete( KMFolderImap*, bool )), 02227 this, TQT_SLOT(slotImapFolderCompleted( KMFolderImap*, bool ))); 02228 02229 connect (imapFolder, TQT_SIGNAL(folderComplete( KMFolderImap*, bool )), 02230 this, TQT_SLOT(slotImapFolderCompleted( KMFolderImap*, bool ))); 02231 list.append(msg); 02232 } else { 02233 // We are moving to a local folder. 02234 if ( srcFolder->folderType() == KMFolderTypeImap ) 02235 { 02236 // do not complete here but wait until all messages are transferred 02237 mCompleteWithAddedMsg = true; 02238 } 02239 rc = mDestFolder->moveMsg(msg, &index); 02240 if (rc == 0 && index != -1) { 02241 KMMsgBase *mb = mDestFolder->unGetMsg( mDestFolder->count() - 1 ); 02242 if (undo && mb) 02243 { 02244 if ( undoId == -1 ) 02245 undoId = kmkernel->undoStack()->newUndoAction( srcFolder, mDestFolder ); 02246 kmkernel->undoStack()->addMsgToAction( undoId, mb->getMsgSerNum() ); 02247 } 02248 } else if (rc != 0) { 02249 // Something went wrong. Stop processing here, it is likely that the 02250 // other moves would fail as well. 02251 completeMove( Failed ); 02252 return Failed; 02253 } 02254 } 02255 } else { 02256 // really delete messages that are already in the trash folder or if 02257 // we are really, really deleting, not just moving to trash 02258 if (srcFolder->folderType() == KMFolderTypeImap) { 02259 if (!folderDeleteList[srcFolder]) 02260 folderDeleteList[srcFolder] = new TQPtrList<KMMessage>; 02261 folderDeleteList[srcFolder]->append( msg ); 02262 } else { 02263 srcFolder->removeMsg(idx); 02264 delete msg; 02265 } 02266 } 02267 } 02268 if (!list.isEmpty() && mDestFolder) { 02269 // will be completed with folderComplete signal 02270 mDestFolder->moveMsg(list, &index); 02271 } else { 02272 FolderToMessageListMap::Iterator it; 02273 for ( it = folderDeleteList.begin(); it != folderDeleteList.end(); ++it ) { 02274 it.key()->removeMsg(*it.data()); 02275 delete it.data(); 02276 } 02277 if ( !mCompleteWithAddedMsg ) { 02278 // imap folders will be completed in slotMsgAddedToDestFolder 02279 completeMove( OK ); 02280 } 02281 } 02282 02283 return OK; 02284 } 02285 02286 void KMMoveCommand::slotImapFolderCompleted(KMFolderImap* imapFolder, bool success) 02287 { 02288 disconnect (imapFolder, TQT_SIGNAL(folderComplete( KMFolderImap*, bool )), 02289 this, TQT_SLOT(slotImapFolderCompleted( KMFolderImap*, bool ))); 02290 if ( success ) { 02291 // the folder was checked successfully but we were still called, so check 02292 // if we are still waiting for messages to show up. If so, uidValidity 02293 // changed, or something else went wrong. Clean up. 02294 02295 /* Unfortunately older UW imap servers change uid validity for each put job. 02296 * Yes, it is really that broken. *sigh* So we cannot report error here, I guess. */ 02297 if ( !mLostBoys.isEmpty() ) { 02298 kdDebug(5006) << "### Not all moved messages reported back that they were " << endl 02299 << "### added to the target folder. Did uidValidity change? " << endl; 02300 } 02301 completeMove( OK ); 02302 } else { 02303 // Should we inform the user here or leave that to the caller? 02304 completeMove( Failed ); 02305 } 02306 } 02307 02308 void KMMoveCommand::slotMsgAddedToDestFolder(KMFolder *folder, TQ_UINT32 serNum) 02309 { 02310 if ( folder != mDestFolder || mLostBoys.find( serNum ) == mLostBoys.end() ) { 02311 //kdDebug(5006) << "KMMoveCommand::msgAddedToDestFolder different " 02312 // "folder or invalid serial number." << endl; 02313 return; 02314 } 02315 mLostBoys.remove(serNum); 02316 if ( mLostBoys.isEmpty() ) { 02317 // we are done. All messages transferred to the host succesfully 02318 disconnect (mDestFolder, TQT_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)), 02319 this, TQT_SLOT(slotMsgAddedToDestFolder(KMFolder*, TQ_UINT32))); 02320 if (mDestFolder && mDestFolder->folderType() != KMFolderTypeImap) { 02321 mDestFolder->sync(); 02322 } 02323 if ( mCompleteWithAddedMsg ) { 02324 completeMove( OK ); 02325 } 02326 } else { 02327 if ( mProgressItem ) { 02328 mProgressItem->incCompletedItems(); 02329 mProgressItem->updateProgress(); 02330 } 02331 } 02332 } 02333 02334 void KMMoveCommand::completeMove( Result result ) 02335 { 02336 if ( mDestFolder ) 02337 mDestFolder->close("kmcommand"); 02338 while ( !mOpenedFolders.empty() ) { 02339 KMFolder *folder = mOpenedFolders.back(); 02340 mOpenedFolders.pop_back(); 02341 folder->close("kmcommand"); 02342 } 02343 if ( mProgressItem ) { 02344 mProgressItem->setComplete(); 02345 mProgressItem = 0; 02346 } 02347 setResult( result ); 02348 emit completed( this ); 02349 deleteLater(); 02350 } 02351 02352 void KMMoveCommand::slotMoveCanceled() 02353 { 02354 completeMove( Canceled ); 02355 } 02356 02357 // srcFolder doesn't make much sense for searchFolders 02358 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder, 02359 const TQPtrList<KMMsgBase> &msgList ) 02360 :KMMoveCommand( findTrashFolder( srcFolder ), msgList) 02361 { 02362 srcFolder->open("kmcommand"); 02363 mOpenedFolders.push_back( srcFolder ); 02364 } 02365 02366 KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder, KMMessage * msg ) 02367 :KMMoveCommand( findTrashFolder( srcFolder ), msg) 02368 { 02369 srcFolder->open("kmcommand"); 02370 mOpenedFolders.push_back( srcFolder ); 02371 } 02372 02373 KMDeleteMsgCommand::KMDeleteMsgCommand( TQ_UINT32 sernum ) 02374 :KMMoveCommand( sernum ) 02375 { 02376 if ( !sernum ) { 02377 setDestFolder( 0 ); 02378 return; 02379 } 02380 02381 KMFolder *srcFolder = 0; 02382 int idx; 02383 KMMsgDict::instance()->getLocation( sernum, &srcFolder, &idx ); 02384 if ( srcFolder ) { 02385 KMMsgBase *msg = srcFolder->getMsgBase( idx ); 02386 srcFolder->open("kmcommand"); 02387 mOpenedFolders.push_back( srcFolder ); 02388 addMsg( msg ); 02389 } 02390 setDestFolder( findTrashFolder( srcFolder ) ); 02391 } 02392 02393 KMFolder * KMDeleteMsgCommand::findTrashFolder( KMFolder * folder ) 02394 { 02395 KMFolder* trash = folder->trashFolder(); 02396 if( !trash ) 02397 trash = kmkernel->trashFolder(); 02398 if( trash != folder ) 02399 return trash; 02400 return 0; 02401 } 02402 02403 02404 KMUrlClickedCommand::KMUrlClickedCommand( const KURL &url, uint identity, 02405 KMReaderWin *readerWin, bool htmlPref, KMMainWidget *mainWidget ) 02406 :mUrl( url ), mIdentity( identity ), mReaderWin( readerWin ), 02407 mHtmlPref( htmlPref ), mMainWidget( mainWidget ) 02408 { 02409 } 02410 02411 KMCommand::Result KMUrlClickedCommand::execute() 02412 { 02413 KMMessage* msg; 02414 02415 if (mUrl.protocol() == "mailto") 02416 { 02417 msg = new KMMessage; 02418 msg->initHeader(mIdentity); 02419 msg->setCharset("utf-8"); 02420 msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) ); 02421 TQString query=mUrl.query(); 02422 while (!query.isEmpty()) { 02423 TQString queryPart; 02424 int secondQuery = query.find('?',1); 02425 if (secondQuery != -1) 02426 queryPart = query.left(secondQuery); 02427 else 02428 queryPart = query; 02429 query = query.mid(queryPart.length()); 02430 02431 if (queryPart.left(9) == "?subject=") 02432 msg->setSubject( KURL::decode_string(queryPart.mid(9)) ); 02433 else if (queryPart.left(6) == "?body=") 02434 // It is correct to convert to latin1() as URL should not contain 02435 // anything except ascii. 02436 msg->setBody( KURL::decode_string(queryPart.mid(6)).latin1() ); 02437 else if (queryPart.left(4) == "?cc=") 02438 msg->setCc( KURL::decode_string(queryPart.mid(4)) ); 02439 } 02440 02441 KMail::Composer * win = KMail::makeComposer( msg, mIdentity ); 02442 win->setCharset("", true); 02443 win->show(); 02444 } 02445 else if ( mUrl.protocol() == "im" ) 02446 { 02447 kmkernel->imProxy()->chatWithContact( mUrl.path() ); 02448 } 02449 else if ((mUrl.protocol() == "http") || (mUrl.protocol() == "https") || 02450 (mUrl.protocol() == "ftp") || (mUrl.protocol() == "file") || 02451 (mUrl.protocol() == "ftps") || (mUrl.protocol() == "sftp" ) || 02452 (mUrl.protocol() == "help") || (mUrl.protocol() == "vnc") || 02453 (mUrl.protocol() == "smb") || (mUrl.protocol() == "fish") || 02454 (mUrl.protocol() == "news")) 02455 { 02456 KPIM::BroadcastStatus::instance()->setStatusMsg( i18n("Opening URL...")); 02457 KMimeType::Ptr mime = KMimeType::findByURL( mUrl ); 02458 if (mime->name() == "application/x-desktop" || 02459 mime->name() == "application/x-executable" || 02460 mime->name() == "application/x-msdos-program" || 02461 mime->name() == "application/x-shellscript" ) 02462 { 02463 if (KMessageBox::warningYesNo( 0, i18n( "<qt>Do you really want to execute <b>%1</b>?</qt>" ) 02464 .arg( mUrl.prettyURL() ), TQString(), i18n("Execute"), KStdGuiItem::cancel() ) != KMessageBox::Yes) 02465 return Canceled; 02466 } 02467 KRun * runner = new KRun( mUrl ); 02468 runner->setRunExecutables( false ); 02469 } 02470 else 02471 return Failed; 02472 02473 return OK; 02474 } 02475 02476 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( TQWidget *parent, KMMessage *msg ) 02477 : KMCommand( parent, msg ), mImplicitAttachments( true ), mEncoded( false ) 02478 { 02479 } 02480 02481 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( TQWidget *parent, const TQPtrList<KMMsgBase>& msgs ) 02482 : KMCommand( parent, msgs ), mImplicitAttachments( true ), mEncoded( false ) 02483 { 02484 } 02485 02486 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( TQWidget *parent, TQPtrList<partNode>& attachments, 02487 KMMessage *msg, bool encoded ) 02488 : KMCommand( parent ), mImplicitAttachments( false ), mEncoded( encoded ) 02489 { 02490 for ( TQPtrListIterator<partNode> it( attachments ); it.current(); ++it ) { 02491 mAttachmentMap.insert( it.current(), msg ); 02492 } 02493 } 02494 02495 KMCommand::Result KMSaveAttachmentsCommand::execute() 02496 { 02497 setEmitsCompletedItself( true ); 02498 if ( mImplicitAttachments ) { 02499 TQPtrList<KMMessage> msgList = retrievedMsgs(); 02500 KMMessage *msg; 02501 for ( TQPtrListIterator<KMMessage> itr( msgList ); 02502 ( msg = itr.current() ); 02503 ++itr ) { 02504 partNode *rootNode = partNode::fromMessage( msg ); 02505 for ( partNode *child = rootNode; child; 02506 child = child->firstChild() ) { 02507 for ( partNode *node = child; node; node = node->nextSibling() ) { 02508 if ( node->type() != DwMime::kTypeMultipart ) 02509 mAttachmentMap.insert( node, msg ); 02510 } 02511 } 02512 } 02513 } 02514 setDeletesItself( true ); 02515 // load all parts 02516 KMLoadPartsCommand *command = new KMLoadPartsCommand( mAttachmentMap ); 02517 connect( command, TQT_SIGNAL( partsRetrieved() ), 02518 this, TQT_SLOT( slotSaveAll() ) ); 02519 command->start(); 02520 02521 return OK; 02522 } 02523 02524 void KMSaveAttachmentsCommand::slotSaveAll() 02525 { 02526 // now that all message parts have been retrieved, remove all parts which 02527 // don't represent an attachment if they were not explicitely passed in the 02528 // c'tor 02529 if ( mImplicitAttachments ) { 02530 for ( PartNodeMessageMap::iterator it = mAttachmentMap.begin(); 02531 it != mAttachmentMap.end(); ) { 02532 // only body parts which have a filename or a name parameter (except for 02533 // the root node for which name is set to the message's subject) are 02534 // considered attachments 02535 if ( it.key()->msgPart().fileName().stripWhiteSpace().isEmpty() && 02536 ( it.key()->msgPart().name().stripWhiteSpace().isEmpty() || 02537 !it.key()->parentNode() ) ) { 02538 PartNodeMessageMap::iterator delIt = it; 02539 ++it; 02540 mAttachmentMap.remove( delIt ); 02541 } 02542 else 02543 ++it; 02544 } 02545 if ( mAttachmentMap.isEmpty() ) { 02546 KMessageBox::information( 0, i18n("Found no attachments to save.") ); 02547 setResult( OK ); // The user has already been informed. 02548 emit completed( this ); 02549 deleteLater(); 02550 return; 02551 } 02552 } 02553 02554 KURL url, dirUrl; 02555 if ( mAttachmentMap.count() > 1 ) { 02556 // get the dir 02557 dirUrl = KDirSelectDialog::selectDirectory( TQString(), false, 02558 parentWidget(), 02559 i18n("Save Attachments To") ); 02560 if ( !dirUrl.isValid() ) { 02561 setResult( Canceled ); 02562 emit completed( this ); 02563 deleteLater(); 02564 return; 02565 } 02566 02567 // we may not get a slash-terminated url out of KDirSelectDialog 02568 dirUrl.adjustPath( 1 ); 02569 } 02570 else { 02571 // only one item, get the desired filename 02572 partNode *node = mAttachmentMap.begin().key(); 02573 // replace all ':' with '_' because ':' isn't allowed on FAT volumes 02574 TQString s = 02575 node->msgPart().fileName().stripWhiteSpace().replace( ':', '_' ); 02576 if ( s.isEmpty() ) 02577 s = node->msgPart().name().stripWhiteSpace().replace( ':', '_' ); 02578 if ( s.isEmpty() ) 02579 s = i18n("filename for an unnamed attachment", "attachment.1"); 02580 url = KFileDialog::getSaveURL( s, TQString(), parentWidget(), 02581 TQString() ); 02582 if ( url.isEmpty() ) { 02583 setResult( Canceled ); 02584 emit completed( this ); 02585 deleteLater(); 02586 return; 02587 } 02588 } 02589 02590 TQMap< TQString, int > renameNumbering; 02591 02592 Result globalResult = OK; 02593 int unnamedAtmCount = 0; 02594 for ( PartNodeMessageMap::const_iterator it = mAttachmentMap.begin(); 02595 it != mAttachmentMap.end(); 02596 ++it ) { 02597 KURL curUrl; 02598 if ( !dirUrl.isEmpty() ) { 02599 curUrl = dirUrl; 02600 TQString s = 02601 it.key()->msgPart().fileName().stripWhiteSpace().replace( ':', '_' ); 02602 if ( s.isEmpty() ) 02603 s = it.key()->msgPart().name().stripWhiteSpace().replace( ':', '_' ); 02604 if ( s.isEmpty() ) { 02605 ++unnamedAtmCount; 02606 s = i18n("filename for the %1-th unnamed attachment", 02607 "attachment.%1") 02608 .arg( unnamedAtmCount ); 02609 } 02610 curUrl.setFileName( s ); 02611 } else { 02612 curUrl = url; 02613 } 02614 02615 if ( !curUrl.isEmpty() ) { 02616 02617 // Rename the file if we have already saved one with the same name: 02618 // try appending a number before extension (e.g. "pic.jpg" => "pic_2.jpg") 02619 TQString origFile = curUrl.fileName(); 02620 TQString file = origFile; 02621 02622 while ( renameNumbering.contains(file) ) { 02623 file = origFile; 02624 int num = renameNumbering[file] + 1; 02625 int dotIdx = file.findRev('.'); 02626 file = file.insert( (dotIdx>=0) ? dotIdx : file.length(), TQString("_") + TQString::number(num) ); 02627 } 02628 curUrl.setFileName(file); 02629 02630 // Increment the counter for both the old and the new filename 02631 if ( !renameNumbering.contains(origFile)) 02632 renameNumbering[origFile] = 1; 02633 else 02634 renameNumbering[origFile]++; 02635 02636 if ( file != origFile ) { 02637 if ( !renameNumbering.contains(file)) 02638 renameNumbering[file] = 1; 02639 else 02640 renameNumbering[file]++; 02641 } 02642 02643 02644 if ( KIO::NetAccess::exists( curUrl, false, parentWidget() ) ) { 02645 if ( KMessageBox::warningContinueCancel( parentWidget(), 02646 i18n( "A file named %1 already exists. Do you want to overwrite it?" ) 02647 .arg( curUrl.fileName() ), 02648 i18n( "File Already Exists" ), i18n("&Overwrite") ) == KMessageBox::Cancel) { 02649 continue; 02650 } 02651 } 02652 // save 02653 const Result result = saveItem( it.key(), curUrl ); 02654 if ( result != OK ) 02655 globalResult = result; 02656 } 02657 } 02658 setResult( globalResult ); 02659 emit completed( this ); 02660 deleteLater(); 02661 } 02662 02663 KMCommand::Result KMSaveAttachmentsCommand::saveItem( partNode *node, 02664 const KURL& url ) 02665 { 02666 bool bSaveEncrypted = false; 02667 bool bEncryptedParts = node->encryptionState() != KMMsgNotEncrypted; 02668 if( bEncryptedParts ) 02669 if( KMessageBox::questionYesNo( parentWidget(), 02670 i18n( "The part %1 of the message is encrypted. Do you want to keep the encryption when saving?" ). 02671 arg( url.fileName() ), 02672 i18n( "KMail Question" ), i18n("Keep Encryption"), i18n("Do Not Keep") ) == 02673 KMessageBox::Yes ) 02674 bSaveEncrypted = true; 02675 02676 bool bSaveWithSig = true; 02677 if( node->signatureState() != KMMsgNotSigned ) 02678 if( KMessageBox::questionYesNo( parentWidget(), 02679 i18n( "The part %1 of the message is signed. Do you want to keep the signature when saving?" ). 02680 arg( url.fileName() ), 02681 i18n( "KMail Question" ), i18n("Keep Signature"), i18n("Do Not Keep") ) != 02682 KMessageBox::Yes ) 02683 bSaveWithSig = false; 02684 02685 TQByteArray data; 02686 if ( mEncoded ) 02687 { 02688 // This does not decode the Message Content-Transfer-Encoding 02689 // but saves the _original_ content of the message part 02690 data = KMail::Util::ByteArray( node->msgPart().dwBody() ); 02691 } 02692 else 02693 { 02694 if( bSaveEncrypted || !bEncryptedParts) { 02695 partNode *dataNode = node; 02696 TQCString rawReplyString; 02697 bool gotRawReplyString = false; 02698 if( !bSaveWithSig ) { 02699 if( DwMime::kTypeMultipart == node->type() && 02700 DwMime::kSubtypeSigned == node->subType() ){ 02701 // carefully look for the part that is *not* the signature part: 02702 if( node->findType( DwMime::kTypeApplication, 02703 DwMime::kSubtypePgpSignature, 02704 true, false ) ){ 02705 dataNode = node->findTypeNot( DwMime::kTypeApplication, 02706 DwMime::kSubtypePgpSignature, 02707 true, false ); 02708 }else if( node->findType( DwMime::kTypeApplication, 02709 DwMime::kSubtypePkcs7Mime, 02710 true, false ) ){ 02711 dataNode = node->findTypeNot( DwMime::kTypeApplication, 02712 DwMime::kSubtypePkcs7Mime, 02713 true, false ); 02714 }else{ 02715 dataNode = node->findTypeNot( DwMime::kTypeMultipart, 02716 DwMime::kSubtypeUnknown, 02717 true, false ); 02718 } 02719 }else{ 02720 ObjectTreeParser otp( 0, 0, false, false, false ); 02721 02722 // process this node and all it's siblings and descendants 02723 dataNode->setProcessed( false, true ); 02724 otp.parseObjectTree( dataNode ); 02725 02726 rawReplyString = otp.rawReplyString(); 02727 gotRawReplyString = true; 02728 } 02729 } 02730 TQByteArray cstr = gotRawReplyString 02731 ? rawReplyString 02732 : dataNode->msgPart().bodyDecodedBinary(); 02733 data = cstr; 02734 size_t size = cstr.size(); 02735 if ( dataNode->msgPart().type() == DwMime::kTypeText ) { 02736 // convert CRLF to LF before writing text attachments to disk 02737 size = KMail::Util::crlf2lf( cstr.data(), size ); 02738 } 02739 data.resize( size ); 02740 } 02741 } 02742 TQDataStream ds; 02743 TQFile file; 02744 KTempFile tf; 02745 tf.setAutoDelete( true ); 02746 if ( url.isLocalFile() ) 02747 { 02748 // save directly 02749 file.setName( url.path() ); 02750 if ( !file.open( IO_WriteOnly ) ) 02751 { 02752 KMessageBox::error( parentWidget(), 02753 i18n( "%2 is detailed error description", 02754 "Could not write the file %1:\n%2" ) 02755 .arg( file.name() ) 02756 .arg( TQString::fromLocal8Bit( strerror( errno ) ) ), 02757 i18n( "KMail Error" ) ); 02758 return Failed; 02759 } 02760 02761 // #79685 by default use the umask the user defined, but let it be configurable 02762 if ( GlobalSettings::self()->disregardUmask() ) 02763 fchmod( file.handle(), S_IRUSR | S_IWUSR ); 02764 02765 ds.setDevice( &file ); 02766 } else 02767 { 02768 // tmp file for upload 02769 ds.setDevice( tf.file() ); 02770 } 02771 02772 ds.writeRawBytes( data.data(), data.size() ); 02773 if ( !url.isLocalFile() ) 02774 { 02775 tf.close(); 02776 if ( !KIO::NetAccess::upload( tf.name(), url, parentWidget() ) ) 02777 { 02778 KMessageBox::error( parentWidget(), 02779 i18n( "Could not write the file %1." ) 02780 .arg( url.path() ), 02781 i18n( "KMail Error" ) ); 02782 return Failed; 02783 } 02784 } else 02785 file.close(); 02786 return OK; 02787 } 02788 02789 KMLoadPartsCommand::KMLoadPartsCommand( TQPtrList<partNode>& parts, KMMessage *msg ) 02790 : mNeedsRetrieval( 0 ) 02791 { 02792 for ( TQPtrListIterator<partNode> it( parts ); it.current(); ++it ) { 02793 mPartMap.insert( it.current(), msg ); 02794 } 02795 } 02796 02797 KMLoadPartsCommand::KMLoadPartsCommand( partNode *node, KMMessage *msg ) 02798 : mNeedsRetrieval( 0 ) 02799 { 02800 mPartMap.insert( node, msg ); 02801 } 02802 02803 KMLoadPartsCommand::KMLoadPartsCommand( PartNodeMessageMap& partMap ) 02804 : mNeedsRetrieval( 0 ), mPartMap( partMap ) 02805 { 02806 } 02807 02808 void KMLoadPartsCommand::slotStart() 02809 { 02810 for ( PartNodeMessageMap::const_iterator it = mPartMap.begin(); 02811 it != mPartMap.end(); 02812 ++it ) { 02813 if ( !it.key()->msgPart().isComplete() && 02814 !it.key()->msgPart().partSpecifier().isEmpty() ) { 02815 // incomplete part, so retrieve it first 02816 ++mNeedsRetrieval; 02817 KMFolder* curFolder = it.data()->parent(); 02818 if ( curFolder ) { 02819 FolderJob *job = 02820 curFolder->createJob( it.data(), FolderJob::tGetMessage, 02821 0, it.key()->msgPart().partSpecifier() ); 02822 job->setCancellable( false ); 02823 connect( job, TQT_SIGNAL(messageUpdated(KMMessage*, TQString)), 02824 this, TQT_SLOT(slotPartRetrieved(KMMessage*, TQString)) ); 02825 job->start(); 02826 } else 02827 kdWarning(5006) << "KMLoadPartsCommand - msg has no parent" << endl; 02828 } 02829 } 02830 if ( mNeedsRetrieval == 0 ) 02831 execute(); 02832 } 02833 02834 void KMLoadPartsCommand::slotPartRetrieved( KMMessage *msg, 02835 TQString partSpecifier ) 02836 { 02837 DwBodyPart *part = 02838 msg->findDwBodyPart( msg->getFirstDwBodyPart(), partSpecifier ); 02839 if ( part ) { 02840 // update the DwBodyPart in the partNode 02841 for ( PartNodeMessageMap::const_iterator it = mPartMap.begin(); 02842 it != mPartMap.end(); 02843 ++it ) { 02844 if ( it.key()->dwPart()->partId() == part->partId() ) 02845 it.key()->setDwPart( part ); 02846 } 02847 } else 02848 kdWarning(5006) << "KMLoadPartsCommand::slotPartRetrieved - could not find bodypart!" << endl; 02849 --mNeedsRetrieval; 02850 if ( mNeedsRetrieval == 0 ) 02851 execute(); 02852 } 02853 02854 KMCommand::Result KMLoadPartsCommand::execute() 02855 { 02856 emit partsRetrieved(); 02857 setResult( OK ); 02858 emit completed( this ); 02859 deleteLater(); 02860 return OK; 02861 } 02862 02863 KMResendMessageCommand::KMResendMessageCommand( TQWidget *parent, 02864 KMMessage *msg ) 02865 :KMCommand( parent, msg ) 02866 { 02867 } 02868 02869 KMCommand::Result KMResendMessageCommand::execute() 02870 { 02871 KMMessage *msg = retrievedMessage(); 02872 if ( !msg || !msg->codec() ) { 02873 return Failed; 02874 } 02875 KMMessage *newMsg = new KMMessage(*msg); 02876 02877 TQStringList whiteList; 02878 whiteList << "To" << "Cc" << "Bcc" << "Subject"; 02879 newMsg->sanitizeHeaders( whiteList ); 02880 02881 newMsg->setCharset(msg->codec()->mimeName()); 02882 newMsg->setParent( 0 ); 02883 02884 // make sure we have an identity set, default, if necessary 02885 newMsg->setHeaderField("X-KMail-Identity", TQString::number( newMsg->identityUoid() )); 02886 newMsg->applyIdentity( newMsg->identityUoid() ); 02887 02888 KMail::Composer * win = KMail::makeComposer(); 02889 win->setMsg(newMsg, false, true); 02890 win->show(); 02891 02892 return OK; 02893 } 02894 02895 KMMailingListCommand::KMMailingListCommand( TQWidget *parent, KMFolder *folder ) 02896 : KMCommand( parent ), mFolder( folder ) 02897 { 02898 } 02899 02900 KMCommand::Result KMMailingListCommand::execute() 02901 { 02902 KURL::List lst = urls(); 02903 TQString handler = ( mFolder->mailingList().handler() == MailingList::KMail ) 02904 ? "mailto" : "https"; 02905 02906 KMCommand *command = 0; 02907 for ( KURL::List::Iterator itr = lst.begin(); itr != lst.end(); ++itr ) { 02908 if ( handler == (*itr).protocol() ) { 02909 command = new KMUrlClickedCommand( *itr, mFolder->identity(), 0, false ); 02910 } 02911 } 02912 if ( !command && !lst.empty() ) { 02913 command = 02914 new KMUrlClickedCommand( lst.first(), mFolder->identity(), 0, false ); 02915 } 02916 if ( command ) { 02917 connect( command, TQT_SIGNAL( completed( KMCommand * ) ), 02918 this, TQT_SLOT( commandCompleted( KMCommand * ) ) ); 02919 setDeletesItself( true ); 02920 setEmitsCompletedItself( true ); 02921 command->start(); 02922 return OK; 02923 } 02924 return Failed; 02925 } 02926 02927 void KMMailingListCommand::commandCompleted( KMCommand *command ) 02928 { 02929 setResult( command->result() ); 02930 emit completed( this ); 02931 deleteLater(); 02932 } 02933 02934 KMMailingListPostCommand::KMMailingListPostCommand( TQWidget *parent, KMFolder *folder ) 02935 : KMMailingListCommand( parent, folder ) 02936 { 02937 } 02938 KURL::List KMMailingListPostCommand::urls() const 02939 { 02940 return mFolder->mailingList().postURLS(); 02941 } 02942 02943 KMMailingListSubscribeCommand::KMMailingListSubscribeCommand( TQWidget *parent, KMFolder *folder ) 02944 : KMMailingListCommand( parent, folder ) 02945 { 02946 } 02947 KURL::List KMMailingListSubscribeCommand::urls() const 02948 { 02949 return mFolder->mailingList().subscribeURLS(); 02950 } 02951 02952 KMMailingListUnsubscribeCommand::KMMailingListUnsubscribeCommand( TQWidget *parent, KMFolder *folder ) 02953 : KMMailingListCommand( parent, folder ) 02954 { 02955 } 02956 KURL::List KMMailingListUnsubscribeCommand::urls() const 02957 { 02958 return mFolder->mailingList().unsubscribeURLS(); 02959 } 02960 02961 KMMailingListArchivesCommand::KMMailingListArchivesCommand( TQWidget *parent, KMFolder *folder ) 02962 : KMMailingListCommand( parent, folder ) 02963 { 02964 } 02965 KURL::List KMMailingListArchivesCommand::urls() const 02966 { 02967 return mFolder->mailingList().archiveURLS(); 02968 } 02969 02970 KMMailingListHelpCommand::KMMailingListHelpCommand( TQWidget *parent, KMFolder *folder ) 02971 : KMMailingListCommand( parent, folder ) 02972 { 02973 } 02974 KURL::List KMMailingListHelpCommand::urls() const 02975 { 02976 return mFolder->mailingList().helpURLS(); 02977 } 02978 02979 KMIMChatCommand::KMIMChatCommand( const KURL &url, KMMessage *msg ) 02980 :mUrl( url ), mMessage( msg ) 02981 { 02982 } 02983 02984 KMCommand::Result KMIMChatCommand::execute() 02985 { 02986 kdDebug( 5006 ) << k_funcinfo << " URL is: " << mUrl << endl; 02987 TQString addr = KMMessage::decodeMailtoUrl( mUrl.path() ); 02988 // find UID for mail address 02989 KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true ); 02990 KABC::AddresseeList addressees = addressBook->findByEmail( KPIM::getEmailAddress( addr ) ) ; 02991 02992 // start chat 02993 if( addressees.count() == 1 ) { 02994 kmkernel->imProxy()->chatWithContact( addressees[0].uid() ); 02995 return OK; 02996 } 02997 else 02998 { 02999 kdDebug( 5006 ) << "Didn't find exactly one addressee, couldn't tell who to chat to for that email address. Count = " << addressees.count() << endl; 03000 03001 TQString apology; 03002 if ( addressees.isEmpty() ) 03003 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." ); 03004 else 03005 { 03006 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." ); 03007 TQStringList nameList; 03008 KABC::AddresseeList::const_iterator it = addressees.begin(); 03009 KABC::AddresseeList::const_iterator end = addressees.end(); 03010 for ( ; it != end; ++it ) 03011 { 03012 nameList.append( (*it).realName() ); 03013 } 03014 TQString names = nameList.join( TQString::fromLatin1( ",\n" ) ); 03015 apology = apology.arg( names ); 03016 } 03017 03018 KMessageBox::sorry( parentWidget(), apology ); 03019 return Failed; 03020 } 03021 } 03022 03023 KMHandleAttachmentCommand::KMHandleAttachmentCommand( partNode* node, 03024 KMMessage* msg, int atmId, const TQString& atmName, 03025 AttachmentAction action, KService::Ptr offer, TQWidget* parent ) 03026 : KMCommand( parent ), mNode( node ), mMsg( msg ), mAtmId( atmId ), mAtmName( atmName ), 03027 mAction( action ), mOffer( offer ), mJob( 0 ) 03028 { 03029 } 03030 03031 void KMHandleAttachmentCommand::slotStart() 03032 { 03033 if ( !mNode->msgPart().isComplete() ) 03034 { 03035 // load the part 03036 kdDebug(5006) << "load part" << endl; 03037 KMLoadPartsCommand *command = new KMLoadPartsCommand( mNode, mMsg ); 03038 connect( command, TQT_SIGNAL( partsRetrieved() ), 03039 this, TQT_SLOT( slotPartComplete() ) ); 03040 command->start(); 03041 } else 03042 { 03043 execute(); 03044 } 03045 } 03046 03047 void KMHandleAttachmentCommand::slotPartComplete() 03048 { 03049 execute(); 03050 } 03051 03052 KMCommand::Result KMHandleAttachmentCommand::execute() 03053 { 03054 switch( mAction ) 03055 { 03056 case Open: 03057 atmOpen(); 03058 break; 03059 case OpenWith: 03060 atmOpenWith(); 03061 break; 03062 case View: 03063 atmView(); 03064 break; 03065 case Save: 03066 atmSave(); 03067 break; 03068 case Properties: 03069 atmProperties(); 03070 break; 03071 case ChiasmusEncrypt: 03072 atmEncryptWithChiasmus(); 03073 return Undefined; 03074 break; 03075 default: 03076 kdDebug(5006) << "unknown action " << mAction << endl; 03077 break; 03078 } 03079 setResult( OK ); 03080 emit completed( this ); 03081 deleteLater(); 03082 return OK; 03083 } 03084 03085 TQString KMHandleAttachmentCommand::createAtmFileLink() const 03086 { 03087 TQFileInfo atmFileInfo( mAtmName ); 03088 03089 if ( atmFileInfo.size() == 0 ) 03090 { 03091 kdDebug(5006) << k_funcinfo << "rewriting attachment" << endl; 03092 // there is something wrong so write the file again 03093 TQByteArray data = mNode->msgPart().bodyDecodedBinary(); 03094 size_t size = data.size(); 03095 if ( mNode->msgPart().type() == DwMime::kTypeText && size) { 03096 // convert CRLF to LF before writing text attachments to disk 03097 size = KMail::Util::crlf2lf( data.data(), size ); 03098 } 03099 KPIM::kBytesToFile( data.data(), size, mAtmName, false, false, false ); 03100 } 03101 03102 KTempFile *linkFile = new KTempFile( locateLocal("tmp", atmFileInfo.fileName() +"_["), 03103 "]."+ atmFileInfo.extension() ); 03104 03105 linkFile->setAutoDelete(true); 03106 TQString linkName = linkFile->name(); 03107 delete linkFile; 03108 03109 if ( ::link(TQFile::encodeName( mAtmName ), TQFile::encodeName( linkName )) == 0 ) { 03110 return linkName; // success 03111 } 03112 return TQString(); 03113 } 03114 03115 KService::Ptr KMHandleAttachmentCommand::getServiceOffer() 03116 { 03117 KMMessagePart& msgPart = mNode->msgPart(); 03118 const TQString contentTypeStr = 03119 ( msgPart.typeStr() + '/' + msgPart.subtypeStr() ).lower(); 03120 03121 if ( contentTypeStr == "text/x-vcard" ) { 03122 atmView(); 03123 return 0; 03124 } 03125 // determine the MIME type of the attachment 03126 KMimeType::Ptr mimetype; 03127 // prefer the value of the Content-Type header 03128 mimetype = KMimeType::mimeType( contentTypeStr ); 03129 if ( mimetype->name() == "application/octet-stream" ) { 03130 // consider the filename if Content-Type is application/octet-stream 03131 mimetype = KMimeType::findByPath( mAtmName, 0, true /* no disk access */ ); 03132 } 03133 if ( ( mimetype->name() == "application/octet-stream" ) 03134 && msgPart.isComplete() ) { 03135 // consider the attachment's contents if neither the Content-Type header 03136 // nor the filename give us a clue 03137 mimetype = KMimeType::findByFileContent( mAtmName ); 03138 } 03139 return KServiceTypeProfile::preferredService( mimetype->name(), "Application" ); 03140 } 03141 03142 void KMHandleAttachmentCommand::atmOpen() 03143 { 03144 if ( !mOffer ) 03145 mOffer = getServiceOffer(); 03146 if ( !mOffer ) { 03147 kdDebug(5006) << k_funcinfo << "got no offer" << endl; 03148 return; 03149 } 03150 03151 KURL::List lst; 03152 KURL url; 03153 bool autoDelete = true; 03154 TQString fname = createAtmFileLink(); 03155 03156 if ( fname.isNull() ) { 03157 autoDelete = false; 03158 fname = mAtmName; 03159 } 03160 03161 url.setPath( fname ); 03162 lst.append( url ); 03163 if ( (KRun::run( *mOffer, lst, autoDelete ) <= 0) && autoDelete ) { 03164 TQFile::remove(url.path()); 03165 } 03166 } 03167 03168 void KMHandleAttachmentCommand::atmOpenWith() 03169 { 03170 KURL::List lst; 03171 KURL url; 03172 bool autoDelete = true; 03173 TQString fname = createAtmFileLink(); 03174 03175 if ( fname.isNull() ) { 03176 autoDelete = false; 03177 fname = mAtmName; 03178 } 03179 03180 url.setPath( fname ); 03181 lst.append( url ); 03182 if ( (! KRun::displayOpenWithDialog(lst, autoDelete)) && autoDelete ) { 03183 TQFile::remove( url.path() ); 03184 } 03185 } 03186 03187 void KMHandleAttachmentCommand::atmView() 03188 { 03189 // we do not handle this ourself 03190 emit showAttachment( mAtmId, mAtmName ); 03191 } 03192 03193 void KMHandleAttachmentCommand::atmSave() 03194 { 03195 TQPtrList<partNode> parts; 03196 parts.append( mNode ); 03197 // save, do not leave encoded 03198 KMSaveAttachmentsCommand *command = 03199 new KMSaveAttachmentsCommand( parentWidget(), parts, mMsg, false ); 03200 command->start(); 03201 } 03202 03203 void KMHandleAttachmentCommand::atmProperties() 03204 { 03205 KMMsgPartDialogCompat dlg( parentWidget() , 0, true ); 03206 KMMessagePart& msgPart = mNode->msgPart(); 03207 dlg.setMsgPart( &msgPart ); 03208 dlg.exec(); 03209 } 03210 03211 void KMHandleAttachmentCommand::atmEncryptWithChiasmus() 03212 { 03213 const partNode * node = mNode; 03214 Q_ASSERT( node ); 03215 if ( !node ) 03216 return; 03217 03218 // FIXME: better detection of mimetype?? 03219 if ( !mAtmName.endsWith( ".xia", false ) ) 03220 return; 03221 03222 const Kleo::CryptoBackend::Protocol * chiasmus = 03223 Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" ); 03224 Q_ASSERT( chiasmus ); 03225 if ( !chiasmus ) 03226 return; 03227 03228 const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", TQMap<TQString,TQVariant>() ) ); 03229 if ( !listjob.get() ) { 03230 const TQString msg = i18n( "Chiasmus backend does not offer the " 03231 "\"x-obtain-keys\" function. Please report this bug." ); 03232 KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) ); 03233 return; 03234 } 03235 03236 if ( listjob->exec() ) { 03237 listjob->showErrorDialog( parentWidget(), i18n( "Chiasmus Backend Error" ) ); 03238 return; 03239 } 03240 03241 const TQVariant result = listjob->property( "result" ); 03242 if ( result.type() != TQVariant::StringList ) { 03243 const TQString msg = i18n( "Unexpected return value from Chiasmus backend: " 03244 "The \"x-obtain-keys\" function did not return a " 03245 "string list. Please report this bug." ); 03246 KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) ); 03247 return; 03248 } 03249 03250 const TQStringList keys = result.toStringList(); 03251 if ( keys.empty() ) { 03252 const TQString msg = i18n( "No keys have been found. Please check that a " 03253 "valid key path has been set in the Chiasmus " 03254 "configuration." ); 03255 KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) ); 03256 return; 03257 } 03258 03259 ChiasmusKeySelector selectorDlg( parentWidget(), i18n( "Chiasmus Decryption Key Selection" ), 03260 keys, GlobalSettings::chiasmusDecryptionKey(), 03261 GlobalSettings::chiasmusDecryptionOptions() ); 03262 if ( selectorDlg.exec() != TQDialog::Accepted ) 03263 return; 03264 03265 GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() ); 03266 GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() ); 03267 assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() ); 03268 03269 Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", TQMap<TQString,TQVariant>() ); 03270 if ( !job ) { 03271 const TQString msg = i18n( "Chiasmus backend does not offer the " 03272 "\"x-decrypt\" function. Please report this bug." ); 03273 KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) ); 03274 return; 03275 } 03276 03277 const TQByteArray input = node->msgPart().bodyDecodedBinary(); 03278 03279 if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) || 03280 !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) || 03281 !job->setProperty( "input", input ) ) { 03282 const TQString msg = i18n( "The \"x-decrypt\" function does not accept " 03283 "the expected parameters. Please report this bug." ); 03284 KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) ); 03285 return; 03286 } 03287 03288 setDeletesItself( true ); // the job below is async, we have to cleanup ourselves 03289 if ( job->start() ) { 03290 job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) ); 03291 return; 03292 } 03293 03294 mJob = job; 03295 connect( job, TQT_SIGNAL(result(const GpgME::Error&,const TQVariant&)), 03296 this, TQT_SLOT(slotAtmDecryptWithChiasmusResult(const GpgME::Error&,const TQVariant&)) ); 03297 } 03298 03299 static const TQString chomp( const TQString & base, const TQString & suffix, bool cs ) { 03300 return base.endsWith( suffix, cs ) ? base.left( base.length() - suffix.length() ) : base ; 03301 } 03302 03303 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusResult( const GpgME::Error & err, const TQVariant & result ) 03304 { 03305 LaterDeleterWithCommandCompletion d( this ); 03306 if ( !mJob ) 03307 return; 03308 Q_ASSERT( mJob == sender() ); 03309 if ( mJob != sender() ) 03310 return; 03311 Kleo::Job * job = mJob; 03312 mJob = 0; 03313 if ( err.isCanceled() ) 03314 return; 03315 if ( err ) { 03316 job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) ); 03317 return; 03318 } 03319 03320 if ( result.type() != TQVariant::ByteArray ) { 03321 const TQString msg = i18n( "Unexpected return value from Chiasmus backend: " 03322 "The \"x-decrypt\" function did not return a " 03323 "byte array. Please report this bug." ); 03324 KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) ); 03325 return; 03326 } 03327 03328 const KURL url = KFileDialog::getSaveURL( chomp( mAtmName, ".xia", false ), TQString(), parentWidget() ); 03329 if ( url.isEmpty() ) 03330 return; 03331 03332 bool overwrite = KMail::Util::checkOverwrite( url, parentWidget() ); 03333 if ( !overwrite ) 03334 return; 03335 03336 d.setDisabled( true ); // we got this far, don't delete yet 03337 KIO::Job * uploadJob = KIO::storedPut( result.toByteArray(), url, -1, overwrite, false /*resume*/ ); 03338 uploadJob->setWindow( parentWidget() ); 03339 connect( uploadJob, TQT_SIGNAL(result(KIO::Job*)), 03340 this, TQT_SLOT(slotAtmDecryptWithChiasmusUploadResult(KIO::Job*)) ); 03341 } 03342 03343 void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusUploadResult( KIO::Job * job ) 03344 { 03345 if ( job->error() ) 03346 job->showErrorDialog(); 03347 LaterDeleterWithCommandCompletion d( this ); 03348 d.setResult( OK ); 03349 } 03350 03351 03352 AttachmentModifyCommand::AttachmentModifyCommand(partNode * node, KMMessage * msg, TQWidget * parent) : 03353 KMCommand( parent, msg ), 03354 mPartIndex( node->nodeId() ), 03355 mSernum( 0 ) 03356 { 03357 } 03358 03359 AttachmentModifyCommand::AttachmentModifyCommand( int nodeId, KMMessage *msg, TQWidget *parent ) 03360 : KMCommand( parent, msg ), 03361 mPartIndex( nodeId ), 03362 mSernum( 0 ) 03363 { 03364 } 03365 03366 AttachmentModifyCommand::~ AttachmentModifyCommand() 03367 { 03368 } 03369 03370 KMCommand::Result AttachmentModifyCommand::execute() 03371 { 03372 KMMessage *msg = retrievedMessage(); 03373 if ( !msg ) 03374 return Failed; 03375 mSernum = msg->getMsgSerNum(); 03376 03377 mFolder = msg->parent(); 03378 if ( !mFolder || !mFolder->storage() ) 03379 return Failed; 03380 03381 Result res = doAttachmentModify(); 03382 if ( res != OK ) 03383 return res; 03384 03385 setEmitsCompletedItself( true ); 03386 setDeletesItself( true ); 03387 return OK; 03388 } 03389 03390 void AttachmentModifyCommand::storeChangedMessage(KMMessage * msg) 03391 { 03392 if ( !mFolder || !mFolder->storage() ) { 03393 kdWarning(5006) << k_funcinfo << "We lost the folder!" << endl; 03394 setResult( Failed ); 03395 emit completed( this ); 03396 deleteLater(); 03397 } 03398 int res = mFolder->addMsg( msg ) != 0; 03399 if ( mFolder->folderType() == KMFolderTypeImap ) { 03400 KMFolderImap *f = static_cast<KMFolderImap*>( mFolder->storage() ); 03401 connect( f, TQT_SIGNAL(folderComplete(KMFolderImap*,bool)), 03402 TQT_SLOT(messageStoreResult(KMFolderImap*,bool)) ); 03403 } else { 03404 messageStoreResult( 0, res == 0 ); 03405 } 03406 } 03407 03408 void AttachmentModifyCommand::messageStoreResult(KMFolderImap* folder, bool success ) 03409 { 03410 Q_UNUSED( folder ); 03411 if ( success ) { 03412 KMCommand *delCmd = new KMDeleteMsgCommand( mSernum ); 03413 connect( delCmd, TQT_SIGNAL(completed(KMCommand*)), TQT_SLOT(messageDeleteResult(KMCommand*)) ); 03414 delCmd->start(); 03415 return; 03416 } 03417 kdWarning(5006) << k_funcinfo << "Adding modified message failed." << endl; 03418 setResult( Failed ); 03419 emit completed( this ); 03420 deleteLater(); 03421 } 03422 03423 void AttachmentModifyCommand::messageDeleteResult(KMCommand * cmd) 03424 { 03425 setResult( cmd->result() ); 03426 emit completed( this ); 03427 deleteLater(); 03428 } 03429 03430 KMDeleteAttachmentCommand::KMDeleteAttachmentCommand(partNode * node, KMMessage * msg, TQWidget * parent) : 03431 AttachmentModifyCommand( node, msg, parent ) 03432 { 03433 kdDebug(5006) << k_funcinfo << endl; 03434 } 03435 03436 KMDeleteAttachmentCommand::KMDeleteAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent ) 03437 : AttachmentModifyCommand( nodeId, msg, parent ) 03438 { 03439 kdDebug(5006) << k_funcinfo << endl; 03440 } 03441 03442 KMDeleteAttachmentCommand::~KMDeleteAttachmentCommand() 03443 { 03444 kdDebug(5006) << k_funcinfo << endl; 03445 } 03446 03447 KMCommand::Result KMDeleteAttachmentCommand::doAttachmentModify() 03448 { 03449 KMMessage *msg = retrievedMessage(); 03450 if ( !msg || !msg->deleteBodyPart( mPartIndex ) ) 03451 return Failed; 03452 03453 KMMessage *newMsg = new KMMessage(); 03454 newMsg->fromDwString( msg->asDwString() ); 03455 newMsg->setStatus( msg->status() ); 03456 03457 storeChangedMessage( newMsg ); 03458 return OK; 03459 } 03460 03461 03462 KMEditAttachmentCommand::KMEditAttachmentCommand(partNode * node, KMMessage * msg, TQWidget * parent) : 03463 AttachmentModifyCommand( node, msg, parent ) 03464 { 03465 kdDebug(5006) << k_funcinfo << endl; 03466 mTempFile.setAutoDelete( true ); 03467 } 03468 03469 KMEditAttachmentCommand::KMEditAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent ) 03470 : AttachmentModifyCommand( nodeId, msg, parent ) 03471 { 03472 kdDebug(5006) << k_funcinfo << endl; 03473 mTempFile.setAutoDelete( true ); 03474 } 03475 03476 KMEditAttachmentCommand::~ KMEditAttachmentCommand() 03477 { 03478 } 03479 03480 KMCommand::Result KMEditAttachmentCommand::doAttachmentModify() 03481 { 03482 KMMessage *msg = retrievedMessage(); 03483 if ( !msg ) 03484 return Failed; 03485 03486 KMMessagePart part; 03487 DwBodyPart *dwpart = msg->findPart( mPartIndex ); 03488 if ( !dwpart ) 03489 return Failed; 03490 KMMessage::bodyPart( dwpart, &part, true ); 03491 if ( !part.isComplete() ) 03492 return Failed; 03493 03494 if( !dynamic_cast<DwBody*>( dwpart->Parent() ) ) 03495 return Failed; 03496 03497 mTempFile.file()->writeBlock( part.bodyDecodedBinary() ); 03498 mTempFile.file()->flush(); 03499 03500 KMail::EditorWatcher *watcher = 03501 new KMail::EditorWatcher( KURL( mTempFile.file()->name() ), 03502 part.typeStr() + "/" + part.subtypeStr(), 03503 false, this, parentWidget() ); 03504 connect( watcher, TQT_SIGNAL(editDone(KMail::EditorWatcher*)), TQT_SLOT(editDone(KMail::EditorWatcher*)) ); 03505 if ( !watcher->start() ) 03506 return Failed; 03507 setEmitsCompletedItself( true ); 03508 setDeletesItself( true ); 03509 return OK; 03510 } 03511 03512 void KMEditAttachmentCommand::editDone(KMail::EditorWatcher * watcher) 03513 { 03514 kdDebug(5006) << k_funcinfo << endl; 03515 // anything changed? 03516 if ( !watcher->fileChanged() ) { 03517 kdDebug(5006) << k_funcinfo << "File has not been changed" << endl; 03518 setResult( Canceled ); 03519 emit completed( this ); 03520 deleteLater(); 03521 } 03522 03523 mTempFile.file()->reset(); 03524 TQByteArray data = mTempFile.file()->readAll(); 03525 03526 // build the new message 03527 KMMessage *msg = retrievedMessage(); 03528 KMMessagePart part; 03529 DwBodyPart *dwpart = msg->findPart( mPartIndex ); 03530 KMMessage::bodyPart( dwpart, &part, true ); 03531 03532 DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() ); 03533 assert( parentNode ); 03534 parentNode->RemoveBodyPart( dwpart ); 03535 03536 KMMessagePart att; 03537 att.duplicate( part ); 03538 att.setBodyEncodedBinary( data ); 03539 03540 DwBodyPart* newDwPart = msg->createDWBodyPart( &att ); 03541 parentNode->AddBodyPart( newDwPart ); 03542 msg->getTopLevelPart()->Assemble(); 03543 03544 KMMessage *newMsg = new KMMessage(); 03545 newMsg->fromDwString( msg->asDwString() ); 03546 newMsg->setStatus( msg->status() ); 03547 03548 storeChangedMessage( newMsg ); 03549 } 03550 03551 03552 CreateTodoCommand::CreateTodoCommand(TQWidget * parent, KMMessage * msg) 03553 : KMCommand( parent, msg ) 03554 { 03555 } 03556 03557 KMCommand::Result CreateTodoCommand::execute() 03558 { 03559 KMMessage *msg = retrievedMessage(); 03560 if ( !msg || !msg->codec() ) { 03561 return Failed; 03562 } 03563 03564 KMail::KorgHelper::ensureRunning(); 03565 03566 TQString txt = i18n("From: %1\nTo: %2\nSubject: %3").arg( msg->from() ) 03567 .arg( msg->to() ).arg( msg->subject() ); 03568 03569 KTempFile tf; 03570 tf.setAutoDelete( true ); 03571 TQString uri = "kmail:" + TQString::number( msg->getMsgSerNum() ) + "/" + msg->msgId(); 03572 tf.file()->writeBlock( msg->asDwString().c_str(), msg->asDwString().length() ); 03573 tf.close(); 03574 03575 KCalendarIface_stub *iface = new KCalendarIface_stub( kapp->dcopClient(), "korganizer", "CalendarIface" ); 03576 iface->openTodoEditor( i18n("Mail: %1").arg( msg->subject() ), txt, uri, 03577 tf.name(), TQStringList(), "message/rfc822", true ); 03578 delete iface; 03579 03580 return OK; 03581 } 03582 03583 #include "kmcommands.moc"