kmail

kmcommands.cpp
1/*
2 This file is part of KMail, the KDE mail client.
3 Copyright (c) 2002 Don Sanders <sanders@kde.org>
4
5 KMail is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License, version 2, as
7 published by the Free Software Foundation.
8
9 KMail is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17*/
18
19//
20// This file implements various "command" classes. These command classes
21// are based on the command design pattern.
22//
23// Historically various operations were implemented as slots of KMMainWin.
24// This proved inadequate as KMail has multiple top level windows
25// (KMMainWin, KMReaderMainWin, SearchWindow, KMComposeWin) that may
26// benefit from using these operations. It is desirable that these
27// classes can operate without depending on or altering the state of
28// a KMMainWin, in fact it is possible no KMMainWin object even exists.
29//
30// Now these operations have been rewritten as KMCommand based classes,
31// making them independent of KMMainWin.
32//
33// The base command class KMCommand is async, which is a difference
34// from the conventional command pattern. As normal derived classes implement
35// the execute method, but client classes call start() instead of
36// calling execute() directly. start() initiates async operations,
37// and on completion of these operations calls execute() and then deletes
38// the command. (So the client must not construct commands on the stack).
39//
40// The type of async operation supported by KMCommand is retrieval
41// of messages from an IMAP server.
42
43#include "kmcommands.h"
44
45#ifdef HAVE_CONFIG_H
46#include <config.h>
47#endif
48
49#include <errno.h>
50#include <mimelib/enum.h>
51#include <mimelib/field.h>
52#include <mimelib/mimepp.h>
53#include <mimelib/string.h>
54#include <tdeapplication.h>
55#include <dcopclient.h>
56
57#include <tqtextcodec.h>
58#include <tqpopupmenu.h>
59#include <tqeventloop.h>
60
61#include <libemailfunctions/email.h>
62#include <kdcopservicestarter.h>
63#include <kdebug.h>
64#include <tdefiledialog.h>
65#include <tdeabc/stdaddressbook.h>
66#include <tdeabc/addresseelist.h>
67#include <kdirselectdialog.h>
68#include <tdelocale.h>
69#include <tdemessagebox.h>
70#include <tdeparts/browserextension.h>
71#include <kprogress.h>
72#include <krun.h>
73#include <kbookmarkmanager.h>
74#include <tdestandarddirs.h>
75#include <tdetempfile.h>
76#include <tdeimproxy.h>
77#include <kuserprofile.h>
78// TDEIO headers
79#include <tdeio/job.h>
80#include <tdeio/netaccess.h>
81
82#include <libkpimidentities/identitymanager.h>
83
84#include "actionscheduler.h"
85using KMail::ActionScheduler;
86#include "mailinglist-magic.h"
87#include "kmaddrbook.h"
88#include <kaddrbook.h>
89#include "composer.h"
90#include "kmfiltermgr.h"
91#include "kmfoldermbox.h"
92#include "kmfolderimap.h"
93#include "kmfoldermgr.h"
94#include "kmheaders.h"
95#include "headeritem.h"
96#include "kmmainwidget.h"
97#include "kmmsgdict.h"
98#include "messagesender.h"
99#include "kmmsgpartdlg.h"
100#include "undostack.h"
101#include "kcursorsaver.h"
102#include "partNode.h"
103#include "objecttreeparser.h"
104#include "csshelper.h"
105using KMail::ObjectTreeParser;
106using KMail::FolderJob;
107#include "chiasmuskeyselector.h"
108#include "mailsourceviewer.h"
109using KMail::MailSourceViewer;
110#include "kmreadermainwin.h"
111#include "secondarywindow.h"
113#include "redirectdialog.h"
115#include "util.h"
116#include "templateparser.h"
117#include "editorwatcher.h"
118#include "korghelper.h"
119
120#include "broadcaststatus.h"
121#include "globalsettings.h"
122
123#include <libtdepim/tdefileio.h>
124#include "kcalendariface_stub.h"
125
126#include "progressmanager.h"
127using KPIM::ProgressManager;
128using KPIM::ProgressItem;
129#include <kmime_mdn.h>
130using namespace KMime;
131
132#include <kleo/specialjob.h>
133#include <kleo/cryptobackend.h>
134#include <kleo/cryptobackendfactory.h>
135
136#include <tqclipboard.h>
137
138#include <memory>
139
140class LaterDeleterWithCommandCompletion : public KMail::Util::LaterDeleter
141{
142public:
143 LaterDeleterWithCommandCompletion( KMCommand* command )
144 :LaterDeleter( command ), m_result( KMCommand::Failed )
145 {
146 }
147 ~LaterDeleterWithCommandCompletion()
148 {
149 setResult( m_result );
150 KMCommand *command = static_cast<KMCommand*>( m_object );
151 emit command->completed( command );
152 }
153 void setResult( KMCommand::Result v ) { m_result = v; }
154private:
155 KMCommand::Result m_result;
156};
157
158
159KMCommand::KMCommand( TQWidget *parent )
160 : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ),
161 mEmitsCompletedItself( false ), mParent( parent )
162{
163}
164
165KMCommand::KMCommand( TQWidget *parent, const TQPtrList<KMMsgBase> &msgList )
166 : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ),
167 mEmitsCompletedItself( false ), mParent( parent ), mMsgList( msgList )
168{
169}
170
171KMCommand::KMCommand( TQWidget *parent, KMMsgBase *msgBase )
172 : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ),
173 mEmitsCompletedItself( false ), mParent( parent )
174{
175 mMsgList.append( msgBase );
176}
177
178KMCommand::KMCommand( TQWidget *parent, KMMessage *msg )
179 : mProgressDialog( 0 ), mResult( Undefined ), mDeletesItself( false ),
180 mEmitsCompletedItself( false ), mParent( parent )
181{
182 if (msg)
183 mMsgList.append( &msg->toMsgBase() );
184}
185
186KMCommand::~KMCommand()
187{
188 TQValueListIterator<TQGuardedPtr<KMFolder> > fit;
189 for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) {
190 if (!(*fit))
191 continue;
192 (*fit)->close("kmcommand");
193 }
194}
195
196KMCommand::Result KMCommand::result()
197{
198 if ( mResult == Undefined )
199 kdDebug(5006) << k_funcinfo << "mResult is Undefined" << endl;
200 return mResult;
201}
202
203void KMCommand::start()
204{
205 TQTimer::singleShot( 0, this, TQ_SLOT( slotStart() ) );
206}
207
208
209const TQPtrList<KMMessage> KMCommand::retrievedMsgs() const
210{
211 return mRetrievedMsgs;
212}
213
214KMMessage *KMCommand::retrievedMessage() const
215{
216 return mRetrievedMsgs.getFirst();
217}
218
219TQWidget *KMCommand::parentWidget() const
220{
221 return mParent;
222}
223
224int KMCommand::mCountJobs = 0;
225
226void KMCommand::slotStart()
227{
228 connect( this, TQ_SIGNAL( messagesTransfered( KMCommand::Result ) ),
229 this, TQ_SLOT( slotPostTransfer( KMCommand::Result ) ) );
230 kmkernel->filterMgr()->ref();
231
232 if (mMsgList.find(0) != -1) {
233 emit messagesTransfered( Failed );
234 return;
235 }
236
237 if ((mMsgList.count() == 1) &&
238 (mMsgList.getFirst()->isMessage()) &&
239 (mMsgList.getFirst()->parent() == 0))
240 {
241 // Special case of operating on message that isn't in a folder
242 mRetrievedMsgs.append((KMMessage*)mMsgList.getFirst());
243 emit messagesTransfered( OK );
244 return;
245 }
246
247 for ( KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next() ) {
248 if ( mb ) {
249 if ( !mb->parent() ) {
250 emit messagesTransfered( Failed );
251 return;
252 } else {
253 keepFolderOpen( mb->parent() );
254 }
255 }
256 }
257
258 // transfer the selected messages first
259 transferSelectedMsgs();
260}
261
262void KMCommand::slotPostTransfer( KMCommand::Result result )
263{
264 disconnect( this, TQ_SIGNAL( messagesTransfered( KMCommand::Result ) ),
265 this, TQ_SLOT( slotPostTransfer( KMCommand::Result ) ) );
266 if ( result == OK )
267 result = execute();
268 mResult = result;
269 TQPtrListIterator<KMMessage> it( mRetrievedMsgs );
270 KMMessage* msg;
271 while ( (msg = it.current()) != 0 )
272 {
273 ++it;
274 if (msg->parent())
275 msg->setTransferInProgress(false);
276 }
277 kmkernel->filterMgr()->deref();
278 if ( !emitsCompletedItself() )
279 emit completed( this );
280 if ( !deletesItself() )
281 deleteLater();
282}
283
284void KMCommand::transferSelectedMsgs()
285{
286 // make sure no other transfer is active
287 if (KMCommand::mCountJobs > 0) {
288 emit messagesTransfered( Failed );
289 return;
290 }
291
292 bool complete = true;
293 KMCommand::mCountJobs = 0;
294 mCountMsgs = 0;
295 mRetrievedMsgs.clear();
296 mCountMsgs = mMsgList.count();
297 uint totalSize = 0;
298 // the KProgressDialog for the user-feedback. Only enable it if it's needed.
299 // For some commands like KMSeStatusCommand it's not needed. Note, that
300 // for some reason the KProgressDialog eats the MouseReleaseEvent (if a
301 // command is executed after the MousePressEvent), cf. bug #71761.
302 if ( mCountMsgs > 0 ) {
303 mProgressDialog = new KProgressDialog(mParent, "transferProgress",
304 i18n("Please wait"),
305 i18n("Please wait while the message is transferred",
306 "Please wait while the %n messages are transferred", mMsgList.count()),
307 true);
308 mProgressDialog->setMinimumDuration(1000);
309 }
310 for (KMMsgBase *mb = mMsgList.first(); mb; mb = mMsgList.next())
311 {
312 // check if all messages are complete
313 KMMessage *thisMsg = 0;
314 if ( mb->isMessage() )
315 thisMsg = static_cast<KMMessage*>(mb);
316 else
317 {
318 KMFolder *folder = mb->parent();
319 int idx = folder->find(mb);
320 if (idx < 0) continue;
321 thisMsg = folder->getMsg(idx);
322 }
323 if (!thisMsg) continue;
324 if ( thisMsg->transferInProgress() &&
325 thisMsg->parent()->folderType() == KMFolderTypeImap )
326 {
327 thisMsg->setTransferInProgress( false, true );
328 thisMsg->parent()->ignoreJobsForMessage( thisMsg );
329 }
330
331 if ( thisMsg->parent() && !thisMsg->isComplete() &&
332 ( !mProgressDialog || !mProgressDialog->wasCancelled() ) )
333 {
334 kdDebug(5006)<<"### INCOMPLETE\n";
335 // the message needs to be transferred first
336 complete = false;
337 KMCommand::mCountJobs++;
338 FolderJob *job = thisMsg->parent()->createJob(thisMsg);
339 job->setCancellable( false );
340 totalSize += thisMsg->msgSizeServer();
341 // emitted when the message was transferred successfully
342 connect(job, TQ_SIGNAL(messageRetrieved(KMMessage*)),
343 this, TQ_SLOT(slotMsgTransfered(KMMessage*)));
344 // emitted when the job is destroyed
345 connect(job, TQ_SIGNAL(finished()),
346 this, TQ_SLOT(slotJobFinished()));
347 connect(job, TQ_SIGNAL(progress(unsigned long, unsigned long)),
348 this, TQ_SLOT(slotProgress(unsigned long, unsigned long)));
349 // msg musn't be deleted
350 thisMsg->setTransferInProgress(true);
351 job->start();
352 } else {
353 thisMsg->setTransferInProgress(true);
354 mRetrievedMsgs.append(thisMsg);
355 }
356 }
357
358 if (complete)
359 {
360 delete mProgressDialog;
361 mProgressDialog = 0;
362 emit messagesTransfered( OK );
363 } else {
364 // wait for the transfer and tell the progressBar the necessary steps
365 if ( mProgressDialog ) {
366 connect(mProgressDialog, TQ_SIGNAL(cancelClicked()),
367 this, TQ_SLOT(slotTransferCancelled()));
368 mProgressDialog->progressBar()->setTotalSteps(totalSize);
369 }
370 }
371}
372
373void KMCommand::slotMsgTransfered(KMMessage* msg)
374{
375 if ( mProgressDialog && mProgressDialog->wasCancelled() ) {
376 emit messagesTransfered( Canceled );
377 return;
378 }
379
380 // save the complete messages
381 mRetrievedMsgs.append(msg);
382}
383
384void KMCommand::slotProgress( unsigned long done, unsigned long /*total*/ )
385{
386 mProgressDialog->progressBar()->setProgress( done );
387}
388
389void KMCommand::slotJobFinished()
390{
391 // the job is finished (with / without error)
392 KMCommand::mCountJobs--;
393
394 if ( mProgressDialog && mProgressDialog->wasCancelled() ) return;
395
396 if ( (mCountMsgs - static_cast<int>(mRetrievedMsgs.count())) > KMCommand::mCountJobs )
397 {
398 // the message wasn't retrieved before => error
399 if ( mProgressDialog )
400 mProgressDialog->hide();
401 slotTransferCancelled();
402 return;
403 }
404 // update the progressbar
405 if ( mProgressDialog ) {
406 mProgressDialog->setLabel(i18n("Please wait while the message is transferred",
407 "Please wait while the %n messages are transferred", KMCommand::mCountJobs));
408 }
409 if (KMCommand::mCountJobs == 0)
410 {
411 // all done
412 delete mProgressDialog;
413 mProgressDialog = 0;
414 emit messagesTransfered( OK );
415 }
416}
417
418void KMCommand::slotTransferCancelled()
419{
420 // kill the pending jobs
421 TQValueListIterator<TQGuardedPtr<KMFolder> > fit;
422 for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) {
423 if (!(*fit))
424 continue;
425 KMFolder *folder = *fit;
426 KMFolderImap *imapFolder = dynamic_cast<KMFolderImap*>(folder);
427 if (imapFolder && imapFolder->account()) {
428 imapFolder->account()->killAllJobs();
429 }
430 }
431
432 KMCommand::mCountJobs = 0;
433 mCountMsgs = 0;
434 // unget the transfered messages
435 TQPtrListIterator<KMMessage> it( mRetrievedMsgs );
436 KMMessage* msg;
437 while ( (msg = it.current()) != 0 )
438 {
439 KMFolder *folder = msg->parent();
440 ++it;
441 if (!folder)
442 continue;
443 msg->setTransferInProgress(false);
444 int idx = folder->find(msg);
445 if (idx > 0) folder->unGetMsg(idx);
446 }
447 mRetrievedMsgs.clear();
448 emit messagesTransfered( Canceled );
449}
450
451void KMCommand::keepFolderOpen( KMFolder *folder )
452{
453 folder->open( "kmcommand" );
454 mFolders.append( folder );
455}
456
457KMMailtoComposeCommand::KMMailtoComposeCommand( const KURL &url,
458 KMMessage *msg )
459 :mUrl( url ), mMessage( msg )
460{
461}
462
463KMCommand::Result KMMailtoComposeCommand::execute()
464{
465 KMMessage *msg = new KMMessage;
466 uint id = 0;
467
468 if ( mMessage && mMessage->parent() )
469 id = mMessage->parent()->identity();
470
471 msg->initHeader(id);
472 msg->setCharset("utf-8");
473 msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
474
475 KMail::Composer * win = KMail::makeComposer( msg, id );
476 win->setCharset("", true);
477 win->setFocusToSubject();
478 win->show();
479
480 return OK;
481}
482
483
484KMMailtoReplyCommand::KMMailtoReplyCommand( TQWidget *parent,
485 const KURL &url, KMMessage *msg, const TQString &selection )
486 :KMCommand( parent, msg ), mUrl( url ), mSelection( selection )
487{
488}
489
490KMCommand::Result KMMailtoReplyCommand::execute()
491{
492 //TODO : consider factoring createReply into this method.
493 KMMessage *msg = retrievedMessage();
494 if ( !msg || !msg->codec() ) {
495 return Failed;
496 }
497 KMMessage *rmsg = msg->createReply( KMail::ReplyNone, mSelection );
498 rmsg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
499
500 KMail::Composer * win = KMail::makeComposer( rmsg, 0 );
501 win->setCharset(msg->codec()->mimeName(), true);
502 win->setReplyFocus();
503 win->show();
504
505 return OK;
506}
507
508
509KMMailtoForwardCommand::KMMailtoForwardCommand( TQWidget *parent,
510 const KURL &url, KMMessage *msg )
511 :KMCommand( parent, msg ), mUrl( url )
512{
513}
514
515KMCommand::Result KMMailtoForwardCommand::execute()
516{
517 //TODO : consider factoring createForward into this method.
518 KMMessage *msg = retrievedMessage();
519 if ( !msg || !msg->codec() ) {
520 return Failed;
521 }
522 KMMessage *fmsg = msg->createForward();
523 fmsg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
524
525 KMail::Composer * win = KMail::makeComposer( fmsg );
526 win->setCharset(msg->codec()->mimeName(), true);
527 win->show();
528
529 return OK;
530}
531
532
533KMAddBookmarksCommand::KMAddBookmarksCommand( const KURL &url, TQWidget *parent )
534 : KMCommand( parent ), mUrl( url )
535{
536}
537
538KMCommand::Result KMAddBookmarksCommand::execute()
539{
540 TQString filename = locateLocal( "data", TQString::fromLatin1("konqueror/bookmarks.xml") );
541 KBookmarkManager *bookManager = KBookmarkManager::managerForFile( filename,
542 false );
543 KBookmarkGroup group = bookManager->root();
544 group.addBookmark( bookManager, mUrl.path(), KURL( mUrl ) );
545 if( bookManager->save() ) {
546 bookManager->emitChanged( group );
547 }
548
549 return OK;
550}
551
552KMMailtoAddAddrBookCommand::KMMailtoAddAddrBookCommand( const KURL &url,
553 TQWidget *parent )
554 : KMCommand( parent ), mUrl( url )
555{
556}
557
558KMCommand::Result KMMailtoAddAddrBookCommand::execute()
559{
560 KAddrBookExternal::addEmail( KMMessage::decodeMailtoUrl( mUrl.path() ),
561 parentWidget() );
562
563 return OK;
564}
565
566
567KMMailtoOpenAddrBookCommand::KMMailtoOpenAddrBookCommand( const KURL &url,
568 TQWidget *parent )
569 : KMCommand( parent ), mUrl( url )
570{
571}
572
573KMCommand::Result KMMailtoOpenAddrBookCommand::execute()
574{
575 KAddrBookExternal::openEmail( KMMessage::decodeMailtoUrl( mUrl.path() ),
576 parentWidget() );
577
578 return OK;
579}
580
581
582KMUrlCopyCommand::KMUrlCopyCommand( const KURL &url, KMMainWidget *mainWidget )
583 :mUrl( url ), mMainWidget( mainWidget )
584{
585}
586
587KMCommand::Result KMUrlCopyCommand::execute()
588{
589 TQClipboard* clip = TQApplication::clipboard();
590
591 if (mUrl.protocol() == "mailto") {
592 // put the url into the mouse selection and the clipboard
593 TQString address = KMMessage::decodeMailtoUrl( mUrl.path() );
594 clip->setSelectionMode( true );
595 clip->setText( address );
596 clip->setSelectionMode( false );
597 clip->setText( address );
598 KPIM::BroadcastStatus::instance()->setStatusMsg( i18n( "Address copied to clipboard." ));
599 } else {
600 // put the url into the mouse selection and the clipboard
601 clip->setSelectionMode( true );
602 clip->setText( mUrl.url() );
603 clip->setSelectionMode( false );
604 clip->setText( mUrl.url() );
605 KPIM::BroadcastStatus::instance()->setStatusMsg( i18n( "URL copied to clipboard." ));
606 }
607
608 return OK;
609}
610
611
612KMUrlOpenCommand::KMUrlOpenCommand( const KURL &url, KMReaderWin *readerWin )
613 :mUrl( url ), mReaderWin( readerWin )
614{
615}
616
617KMCommand::Result KMUrlOpenCommand::execute()
618{
619 if ( !mUrl.isEmpty() )
620 mReaderWin->slotUrlOpen( mUrl, KParts::URLArgs() );
621
622 return OK;
623}
624
625
626KMUrlSaveCommand::KMUrlSaveCommand( const KURL &url, TQWidget *parent )
627 : KMCommand( parent ), mUrl( url )
628{
629}
630
631KMCommand::Result KMUrlSaveCommand::execute()
632{
633 if ( mUrl.isEmpty() )
634 return OK;
635 KURL saveUrl = KFileDialog::getSaveURL(mUrl.fileName(), TQString(),
636 parentWidget() );
637 if ( saveUrl.isEmpty() )
638 return Canceled;
639 if ( TDEIO::NetAccess::exists( saveUrl, false, parentWidget() ) )
640 {
641 if (KMessageBox::warningContinueCancel(0,
642 i18n("<qt>File <b>%1</b> exists.<br>Do you want to replace it?</qt>")
643 .arg(saveUrl.prettyURL()), i18n("Save to File"), i18n("&Replace"))
644 != KMessageBox::Continue)
645 return Canceled;
646 }
647 TDEIO::Job *job = TDEIO::file_copy(mUrl, saveUrl, -1, true);
648 connect(job, TQ_SIGNAL(result(TDEIO::Job*)), TQ_SLOT(slotUrlSaveResult(TDEIO::Job*)));
649 setEmitsCompletedItself( true );
650 return OK;
651}
652
653void KMUrlSaveCommand::slotUrlSaveResult( TDEIO::Job *job )
654{
655 if ( job->error() ) {
656 job->showErrorDialog();
657 setResult( Failed );
658 emit completed( this );
659 }
660 else {
661 setResult( OK );
662 emit completed( this );
663 }
664}
665
666
667KMEditMsgCommand::KMEditMsgCommand( TQWidget *parent, KMMessage *msg )
668 :KMCommand( parent, msg )
669{
670}
671
672KMCommand::Result KMEditMsgCommand::execute()
673{
674 KMMessage *msg = retrievedMessage();
675 if ( !msg || !msg->parent() ||
676 ( !kmkernel->folderIsDraftOrOutbox( msg->parent() ) &&
677 !kmkernel->folderIsTemplates( msg->parent() ) ) )
678 return Failed;
679
680 // Remember the old parent, we need it a bit further down to be able
681 // to put the unchanged messsage back in the original folder if the nth
682 // edit is discarded, for n > 1.
683 KMFolder *parent = msg->parent();
684 if ( parent )
685 parent->take( parent->find( msg ) );
686
687 KMail::Composer * win = KMail::makeComposer();
688 msg->setTransferInProgress(false); // From here on on, the composer owns the message.
689 win->setMsg(msg, false, true);
690 win->setFolder( parent );
691 win->show();
692
693 return OK;
694}
695
696KMUseTemplateCommand::KMUseTemplateCommand( TQWidget *parent, KMMessage *msg )
697 :KMCommand( parent, msg )
698{
699}
700
701KMCommand::Result KMUseTemplateCommand::execute()
702{
703 KMMessage *msg = retrievedMessage();
704 if ( !msg || !msg->parent() ||
705 !kmkernel->folderIsTemplates( msg->parent() ) )
706 return Failed;
707
708 // Take a copy of the original message, which remains unchanged.
709 KMMessage *newMsg = new KMMessage( new DwMessage( *msg->asDwMessage() ) );
710 newMsg->setComplete( msg->isComplete() );
711
712 // these fields need to be regenerated for the new message
713 newMsg->removeHeaderField("Date");
714 newMsg->removeHeaderField("Message-ID");
715
716 KMail::Composer *win = KMail::makeComposer();
717 newMsg->setTransferInProgress( false ); // From here on on, the composer owns the message.
718 win->setMsg( newMsg, false, true );
719 win->show();
720
721 return OK;
722}
723
724KMShowMsgSrcCommand::KMShowMsgSrcCommand( TQWidget *parent,
725 KMMessage *msg, bool fixedFont )
726 :KMCommand( parent, msg ), mFixedFont( fixedFont )
727{
728 // remember complete state
729 mMsgWasComplete = msg->isComplete();
730}
731
732KMCommand::Result KMShowMsgSrcCommand::execute()
733{
734 KMMessage *msg = retrievedMessage();
735 if ( !msg || !msg->codec() ) {
736 return Failed;
737 }
738 if ( msg->isComplete() && !mMsgWasComplete )
739 msg->notify(); // notify observers as msg was transfered
740 TQString str = msg->codec()->toUnicode( msg->asString() );
741
742 MailSourceViewer *viewer = new MailSourceViewer(); // deletes itself upon close
743 viewer->setCaption( i18n("Message as Plain Text") );
744 viewer->setText(str);
745 if( mFixedFont )
746 viewer->setFont(TDEGlobalSettings::fixedFont());
747
748 // Well, there is no widget to be seen here, so we have to use TQCursor::pos()
749 // Update: (GS) I'm not going to make this code behave according to Xinerama
750 // configuration because this is quite the hack.
751 if (TQApplication::desktop()->isVirtualDesktop()) {
752 int scnum = TQApplication::desktop()->screenNumber(TQCursor::pos());
753 viewer->resize(TQApplication::desktop()->screenGeometry(scnum).width()/2,
754 2*TQApplication::desktop()->screenGeometry(scnum).height()/3);
755 } else {
756 viewer->resize(TQApplication::desktop()->geometry().width()/2,
757 2*TQApplication::desktop()->geometry().height()/3);
758 }
759 viewer->show();
760
761 return OK;
762}
763
764static KURL subjectToUrl( const TQString & subject )
765{
766 // We need to replace colons with underscores since those cause problems with KFileDialog (bug
767 // in KFileDialog though) and also on Windows filesystems.
768 // We also look at the special case of ": ", since converting that to "_ " would look strange,
769 // simply "_" looks better.
770 // We also don't allow filenames starting with a dot, since then the file is hidden and the poor
771 // user can't find it anymore.
772 // Don't allow filenames starting with a tilde either, since that will cause the file dialog to
773 // discard the filename entirely.
774 // https://issues.kolab.org/issue3805
775 const TQString filter = i18n( "*.mbox|email messages (*.mbox)\n*|all files (*)" );
776 TQString cleanSubject = subject.stripWhiteSpace()
777 .replace( TQDir::separator(), '_' )
778 .replace( ": ", "_" )
779 .replace( ':', '_' )
780 .replace( '.', '_' )
781 .replace( '~', '_' );
782 return KFileDialog::getSaveURL( cleanSubject, filter );
783}
784
785KMSaveMsgCommand::KMSaveMsgCommand( TQWidget *parent, KMMessage * msg )
786 : KMCommand( parent ),
787 mMsgListIndex( 0 ),
788 mStandAloneMessage( 0 ),
789 mOffset( 0 ),
790 mTotalSize( msg ? msg->msgSize() : 0 )
791{
792 if ( !msg ) return;
793 setDeletesItself( true );
794 // If the mail has a serial number, operate on sernums, if it does not
795 // we need to work with the pointer, but can be reasonably sure it won't
796 // go away, since it'll be an encapsulated message or one that was opened
797 // from an .eml file.
798 if ( msg->getMsgSerNum() != 0 ) {
799 mMsgList.append( msg->getMsgSerNum() );
800 if ( msg->parent() ) {
801 msg->parent()->open( "kmsavemsgcommand" );
802 }
803 } else {
804 mStandAloneMessage = msg;
805 }
806 mUrl = subjectToUrl( msg->cleanSubject() );
807}
808
809KMSaveMsgCommand::KMSaveMsgCommand( TQWidget *parent,
810 const TQPtrList<KMMsgBase> &msgList )
811 : KMCommand( parent ),
812 mMsgListIndex( 0 ),
813 mStandAloneMessage( 0 ),
814 mOffset( 0 ),
815 mTotalSize( 0 )
816{
817 if (!msgList.getFirst())
818 return;
819 setDeletesItself( true );
820 KMMsgBase *msgBase = msgList.getFirst();
821
822 // We operate on serNums and not the KMMsgBase pointers, as those can
823 // change, or become invalid when changing the current message, switching
824 // folders, etc.
825 TQPtrListIterator<KMMsgBase> it(msgList);
826 while ( it.current() ) {
827 mMsgList.append( (*it)->getMsgSerNum() );
828 mTotalSize += (*it)->msgSize();
829 if ((*it)->parent() != 0)
830 (*it)->parent()->open("kmcommand");
831 ++it;
832 }
833 mMsgListIndex = 0;
834 mUrl = subjectToUrl( msgBase->cleanSubject() );
835}
836
837KURL KMSaveMsgCommand::url()
838{
839 return mUrl;
840}
841
842KMCommand::Result KMSaveMsgCommand::execute()
843{
844 mJob = TDEIO::put( mUrl, S_IRUSR|S_IWUSR, false, false );
845 mJob->slotTotalSize( mTotalSize );
846 mJob->setAsyncDataEnabled( true );
847 mJob->setReportDataSent( true );
848 connect(mJob, TQ_SIGNAL(dataReq(TDEIO::Job*, TQByteArray &)),
849 TQ_SLOT(slotSaveDataReq()));
850 connect(mJob, TQ_SIGNAL(result(TDEIO::Job*)),
851 TQ_SLOT(slotSaveResult(TDEIO::Job*)));
852 setEmitsCompletedItself( true );
853 return OK;
854}
855
856void KMSaveMsgCommand::slotSaveDataReq()
857{
858 int remainingBytes = mData.size() - mOffset;
859 if ( remainingBytes > 0 ) {
860 // eat leftovers first
861 if ( remainingBytes > MAX_CHUNK_SIZE )
862 remainingBytes = MAX_CHUNK_SIZE;
863
864 TQByteArray data;
865 data.duplicate( mData.data() + mOffset, remainingBytes );
866 mJob->sendAsyncData( data );
867 mOffset += remainingBytes;
868 return;
869 }
870 // No leftovers, process next message.
871 if ( mMsgListIndex < mMsgList.size() ) {
872 KMMessage *msg = 0;
873 int idx = -1;
874 KMFolder * p = 0;
875 KMMsgDict::instance()->getLocation( mMsgList[mMsgListIndex], &p, &idx );
876 assert( p );
877 assert( idx >= 0 );
878 //kdDebug() << "SERNUM: " << mMsgList[mMsgListIndex] << " idx: " << idx << " folder: " << p->prettyURL() << endl;
879
880 const bool alreadyGot = p->isMessage( idx );
881
882 msg = p->getMsg(idx);
883
884 if ( msg ) {
885 // Only unGet the message if it isn't already got.
886 if ( !alreadyGot ) {
887 mUngetMsgs.append( msg );
888 }
889 if ( msg->transferInProgress() ) {
890 TQByteArray data = TQByteArray();
891 mJob->sendAsyncData( data );
892 }
893 msg->setTransferInProgress( true );
894 if ( msg->isComplete() ) {
895 slotMessageRetrievedForSaving( msg );
896 } else {
897 // retrieve Message first
898 if ( msg->parent() && !msg->isComplete() ) {
899 FolderJob *job = msg->parent()->createJob( msg );
900 job->setCancellable( false );
901 connect(job, TQ_SIGNAL( messageRetrieved( KMMessage* ) ),
902 this, TQ_SLOT( slotMessageRetrievedForSaving( KMMessage* ) ) );
903 job->start();
904 }
905 }
906 } else {
907 mJob->slotError( TDEIO::ERR_ABORTED,
908 i18n("The message was removed while saving it. "
909 "It has not been saved.") );
910 }
911 } else {
912 if ( mStandAloneMessage ) {
913 // do the special case of a standalone message
914 slotMessageRetrievedForSaving( mStandAloneMessage );
915 mStandAloneMessage = 0;
916 } else {
917 // No more messages. Tell the putjob we are done.
918 TQByteArray data = TQByteArray();
919 mJob->sendAsyncData( data );
920 }
921 }
922}
923
924void KMSaveMsgCommand::slotMessageRetrievedForSaving(KMMessage *msg)
925{
926 if ( msg ) {
927 mData = KMFolderMbox::escapeFrom( msg->asDwString() );
928 KMail::Util::insert( mData, 0, msg->mboxMessageSeparator() );
929 KMail::Util::append( mData, "\n" );
930 msg->setTransferInProgress(false);
931
932 mOffset = 0;
933 TQByteArray data;
934 int size;
935 // Unless it is great than 64 k send the whole message. tdeio buffers for us.
936 if( mData.size() > (unsigned int) MAX_CHUNK_SIZE )
937 size = MAX_CHUNK_SIZE;
938 else
939 size = mData.size();
940
941 data.duplicate( mData, size );
942 mJob->sendAsyncData( data );
943 mOffset += size;
944 }
945 ++mMsgListIndex;
946 // Get rid of the message.
947 if ( msg && msg->parent() && msg->getMsgSerNum() &&
948 mUngetMsgs.contains( msg ) ) {
949 int idx = -1;
950 KMFolder * p = 0;
951 KMMsgDict::instance()->getLocation( msg, &p, &idx );
952 assert( p == msg->parent() ); assert( idx >= 0 );
953 p->unGetMsg( idx );
954 p->close("kmcommand");
955 }
956}
957
958void KMSaveMsgCommand::slotSaveResult(TDEIO::Job *job)
959{
960 if (job->error())
961 {
962 if (job->error() == TDEIO::ERR_FILE_ALREADY_EXIST)
963 {
964 if (KMessageBox::warningContinueCancel(0,
965 i18n("File %1 exists.\nDo you want to replace it?")
966 .arg(mUrl.prettyURL()), i18n("Save to File"), i18n("&Replace"))
967 == KMessageBox::Continue) {
968 mOffset = 0;
969
970 mJob = TDEIO::put( mUrl, S_IRUSR|S_IWUSR, true, false );
971 mJob->slotTotalSize( mTotalSize );
972 mJob->setAsyncDataEnabled( true );
973 mJob->setReportDataSent( true );
974 connect(mJob, TQ_SIGNAL(dataReq(TDEIO::Job*, TQByteArray &)),
975 TQ_SLOT(slotSaveDataReq()));
976 connect(mJob, TQ_SIGNAL(result(TDEIO::Job*)),
977 TQ_SLOT(slotSaveResult(TDEIO::Job*)));
978 }
979 }
980 else
981 {
982 job->showErrorDialog();
983 setResult( Failed );
984 emit completed( this );
985 deleteLater();
986 }
987 } else {
988 setResult( OK );
989 emit completed( this );
990 deleteLater();
991 }
992}
993
994//-----------------------------------------------------------------------------
995
996KMOpenMsgCommand::KMOpenMsgCommand( TQWidget *parent, const KURL & url,
997 const TQString & encoding )
998 : KMCommand( parent ),
999 mUrl( url ),
1000 mEncoding( encoding )
1001{
1002 setDeletesItself( true );
1003}
1004
1005KMCommand::Result KMOpenMsgCommand::execute()
1006{
1007 if ( mUrl.isEmpty() ) {
1008 mUrl = KFileDialog::getOpenURL( ":OpenMessage", "message/rfc822 application/mbox",
1009 parentWidget(), i18n("Open Message") );
1010 }
1011 if ( mUrl.isEmpty() ) {
1012 setDeletesItself( false );
1013 return Canceled;
1014 }
1015 mJob = TDEIO::get( mUrl, false, false );
1016 mJob->setReportDataSent( true );
1017 connect( mJob, TQ_SIGNAL( data( TDEIO::Job *, const TQByteArray & ) ),
1018 this, TQ_SLOT( slotDataArrived( TDEIO::Job*, const TQByteArray & ) ) );
1019 connect( mJob, TQ_SIGNAL( result( TDEIO::Job * ) ),
1020 TQ_SLOT( slotResult( TDEIO::Job * ) ) );
1021 setEmitsCompletedItself( true );
1022 return OK;
1023}
1024
1025void KMOpenMsgCommand::slotDataArrived( TDEIO::Job *, const TQByteArray & data )
1026{
1027 if ( data.isEmpty() )
1028 return;
1029
1030 mMsgString.append( data.data(), data.size() );
1031}
1032
1033void KMOpenMsgCommand::slotResult( TDEIO::Job *job )
1034{
1035 if ( job->error() ) {
1036 // handle errors
1037 job->showErrorDialog();
1038 setResult( Failed );
1039 emit completed( this );
1040 }
1041 else {
1042 int startOfMessage = 0;
1043 if ( mMsgString.compare( 0, 5, "From ", 5 ) == 0 ) {
1044 startOfMessage = mMsgString.find( '\n' );
1045 if ( startOfMessage == -1 ) {
1046 KMessageBox::sorry( parentWidget(),
1047 i18n( "The file does not contain a message." ) );
1048 setResult( Failed );
1049 emit completed( this );
1050 // Emulate closing of a secondary window so that KMail exits in case it
1051 // was started with the --view command line option. Otherwise an
1052 // invisible KMail would keep running.
1053 SecondaryWindow *win = new SecondaryWindow();
1054 win->close();
1055 win->deleteLater();
1056 deleteLater();
1057 return;
1058 }
1059 startOfMessage += 1; // the message starts after the '\n'
1060 }
1061 // check for multiple messages in the file
1062 bool multipleMessages = true;
1063 int endOfMessage = mMsgString.find( "\nFrom " );
1064 if ( endOfMessage == -1 ) {
1065 endOfMessage = mMsgString.length();
1066 multipleMessages = false;
1067 }
1068 DwMessage *dwMsg = new DwMessage;
1069 dwMsg->FromString( mMsgString.substr( startOfMessage,
1070 endOfMessage - startOfMessage ) );
1071 dwMsg->Parse();
1072 // check whether we have a message ( no headers => this isn't a message )
1073 if ( dwMsg->Headers().NumFields() == 0 ) {
1074 KMessageBox::sorry( parentWidget(),
1075 i18n( "The file does not contain a message." ) );
1076 delete dwMsg; dwMsg = 0;
1077 setResult( Failed );
1078 emit completed( this );
1079 // Emulate closing of a secondary window (see above).
1080 SecondaryWindow *win = new SecondaryWindow();
1081 win->close();
1082 win->deleteLater();
1083 deleteLater();
1084 return;
1085 }
1086 KMMessage *msg = new KMMessage( dwMsg );
1087 msg->setReadyToShow( true );
1088 KMReaderMainWin *win = new KMReaderMainWin();
1089 win->showMsg( mEncoding, msg );
1090 win->show();
1091 if ( multipleMessages )
1092 KMessageBox::information( win,
1093 i18n( "The file contains multiple messages. "
1094 "Only the first message is shown." ) );
1095 setResult( OK );
1096 emit completed( this );
1097 }
1098 deleteLater();
1099}
1100
1101//-----------------------------------------------------------------------------
1102
1103//TODO: ReplyTo, NoQuoteReplyTo, ReplyList, ReplyToAll, ReplyAuthor
1104// are all similar and should be factored
1105KMReplyToCommand::KMReplyToCommand( TQWidget *parent, KMMessage *msg,
1106 const TQString &selection )
1107 : KMCommand( parent, msg ), mSelection( selection )
1108{
1109}
1110
1111KMCommand::Result KMReplyToCommand::execute()
1112{
1113 KCursorSaver busy(KBusyPtr::busy());
1114 KMMessage *msg = retrievedMessage();
1115 if ( !msg || !msg->codec() ) {
1116 return Failed;
1117 }
1118
1119 // Find the account that held the original message
1120 TQString accountName;
1121 KMFolder* parentFolder = msg->parent();
1122 if (parentFolder) {
1123 KMFolderDir* parentFolderDir = parentFolder->parent();
1124 while (parentFolderDir) {
1125 TQString prettyURL = parentFolderDir->prettyURL();
1126 if (prettyURL != "") {
1127 accountName = prettyURL;
1128 }
1129 parentFolderDir = parentFolderDir->parent();
1130 }
1131 }
1132
1133 KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection, false, true, TQString(), accountName );
1134 KMail::Composer * win = KMail::makeComposer( reply );
1135 win->setCharset( msg->codec()->mimeName(), true );
1136 win->setReplyFocus();
1137 win->show();
1138
1139 return OK;
1140}
1141
1142
1143KMNoQuoteReplyToCommand::KMNoQuoteReplyToCommand( TQWidget *parent,
1144 KMMessage *msg )
1145 : KMCommand( parent, msg )
1146{
1147}
1148
1149KMCommand::Result KMNoQuoteReplyToCommand::execute()
1150{
1151 KCursorSaver busy(KBusyPtr::busy());
1152 KMMessage *msg = retrievedMessage();
1153 if ( !msg || !msg->codec() ) {
1154 return Failed;
1155 }
1156 KMMessage *reply = msg->createReply( KMail::ReplySmart, "", true);
1157 KMail::Composer * win = KMail::makeComposer( reply );
1158 win->setCharset(msg->codec()->mimeName(), true);
1159 win->setReplyFocus(false);
1160 win->show();
1161
1162 return OK;
1163}
1164
1165
1166KMReplyListCommand::KMReplyListCommand( TQWidget *parent,
1167 KMMessage *msg, const TQString &selection )
1168 : KMCommand( parent, msg ), mSelection( selection )
1169{
1170}
1171
1172KMCommand::Result KMReplyListCommand::execute()
1173{
1174 KCursorSaver busy(KBusyPtr::busy());
1175 KMMessage *msg = retrievedMessage();
1176 if ( !msg || !msg->codec() ) {
1177 return Failed;
1178 }
1179 KMMessage *reply = msg->createReply( KMail::ReplyList, mSelection);
1180 KMail::Composer * win = KMail::makeComposer( reply );
1181 win->setCharset(msg->codec()->mimeName(), true);
1182 win->setReplyFocus(false);
1183 win->show();
1184
1185 return OK;
1186}
1187
1188
1189KMReplyToAllCommand::KMReplyToAllCommand( TQWidget *parent,
1190 KMMessage *msg, const TQString &selection )
1191 :KMCommand( parent, msg ), mSelection( selection )
1192{
1193}
1194
1195KMCommand::Result KMReplyToAllCommand::execute()
1196{
1197 KCursorSaver busy(KBusyPtr::busy());
1198 KMMessage *msg = retrievedMessage();
1199 if ( !msg || !msg->codec() ) {
1200 return Failed;
1201 }
1202 KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection );
1203 KMail::Composer * win = KMail::makeComposer( reply );
1204 win->setCharset( msg->codec()->mimeName(), true );
1205 win->setReplyFocus();
1206 win->show();
1207
1208 return OK;
1209}
1210
1211
1212KMReplyAuthorCommand::KMReplyAuthorCommand( TQWidget *parent, KMMessage *msg,
1213 const TQString &selection )
1214 : KMCommand( parent, msg ), mSelection( selection )
1215{
1216}
1217
1218KMCommand::Result KMReplyAuthorCommand::execute()
1219{
1220 KCursorSaver busy(KBusyPtr::busy());
1221 KMMessage *msg = retrievedMessage();
1222 if ( !msg || !msg->codec() ) {
1223 return Failed;
1224 }
1225 KMMessage *reply = msg->createReply( KMail::ReplyAuthor, mSelection );
1226 KMail::Composer * win = KMail::makeComposer( reply );
1227 win->setCharset( msg->codec()->mimeName(), true );
1228 win->setReplyFocus();
1229 win->show();
1230
1231 return OK;
1232}
1233
1234
1235KMForwardInlineCommand::KMForwardInlineCommand( TQWidget *parent,
1236 const TQPtrList<KMMsgBase> &msgList, uint identity )
1237 : KMCommand( parent, msgList ),
1238 mIdentity( identity )
1239{
1240}
1241
1242KMForwardInlineCommand::KMForwardInlineCommand( TQWidget *parent,
1243 KMMessage *msg, uint identity )
1244 : KMCommand( parent, msg ),
1245 mIdentity( identity )
1246{
1247}
1248
1249KMCommand::Result KMForwardInlineCommand::execute()
1250{
1251 TQPtrList<KMMessage> msgList = retrievedMsgs();
1252
1253 if (msgList.count() >= 2) { // Multiple forward
1254
1255 uint id = 0;
1256 TQPtrList<KMMessage> linklist;
1257 for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
1258 // set the identity
1259 if (id == 0)
1260 id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
1261
1262 // msgText += msg->createForwardBody();
1263 linklist.append( msg );
1264 }
1265 if ( id == 0 )
1266 id = mIdentity; // use folder identity if no message had an id set
1267 KMMessage *fwdMsg = new KMMessage;
1268 fwdMsg->initHeader( id );
1269 fwdMsg->setAutomaticFields( true );
1270 fwdMsg->setCharset( "utf-8" );
1271 // fwdMsg->setBody( msgText );
1272
1273 for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) {
1274 TemplateParser parser( fwdMsg, TemplateParser::Forward );
1275 parser.setSelection( msg->body() ); // FIXME: Why is this needed?
1276 parser.process( msg, 0, true );
1277
1278 fwdMsg->link( msg, KMMsgStatusForwarded );
1279 }
1280
1281 KCursorSaver busy( KBusyPtr::busy() );
1282 KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
1283 win->setCharset("");
1284 win->show();
1285
1286 } else { // forward a single message at most
1287
1288 KMMessage *msg = msgList.getFirst();
1289 if ( !msg || !msg->codec() )
1290 return Failed;
1291
1292 KCursorSaver busy( KBusyPtr::busy() );
1293 KMMessage *fwdMsg = msg->createForward();
1294
1295 uint id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
1296 if ( id == 0 )
1297 id = mIdentity;
1298 {
1299 KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
1300 win->setCharset( fwdMsg->codec()->mimeName(), true );
1301 win->show();
1302 }
1303 }
1304 return OK;
1305}
1306
1307
1308KMForwardAttachedCommand::KMForwardAttachedCommand( TQWidget *parent,
1309 const TQPtrList<KMMsgBase> &msgList, uint identity, KMail::Composer *win )
1310 : KMCommand( parent, msgList ), mIdentity( identity ),
1311 mWin( TQGuardedPtr<KMail::Composer>( win ))
1312{
1313}
1314
1315KMForwardAttachedCommand::KMForwardAttachedCommand( TQWidget *parent,
1316 KMMessage * msg, uint identity, KMail::Composer *win )
1317 : KMCommand( parent, msg ), mIdentity( identity ),
1318 mWin( TQGuardedPtr< KMail::Composer >( win ))
1319{
1320}
1321
1322KMCommand::Result KMForwardAttachedCommand::execute()
1323{
1324 TQPtrList<KMMessage> msgList = retrievedMsgs();
1325 KMMessage *fwdMsg = new KMMessage;
1326
1327 if (msgList.count() >= 2) {
1328 // don't respect X-KMail-Identity headers because they might differ for
1329 // the selected mails
1330 fwdMsg->initHeader(mIdentity);
1331 }
1332 else if (msgList.count() == 1) {
1333 KMMessage *msg = msgList.getFirst();
1334 fwdMsg->initFromMessage(msg);
1335 fwdMsg->setSubject( msg->forwardSubject() );
1336 }
1337
1338 fwdMsg->setAutomaticFields(true);
1339
1340 KCursorSaver busy(KBusyPtr::busy());
1341 if (!mWin)
1342 mWin = KMail::makeComposer(fwdMsg, mIdentity);
1343
1344 // iterate through all the messages to be forwarded
1345 for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) {
1346 // remove headers that shouldn't be forwarded
1348 msg->removeHeaderField("BCC");
1349 // set the part
1350 KMMessagePart *msgPart = new KMMessagePart;
1351 msgPart->setTypeStr("message");
1352 msgPart->setSubtypeStr("rfc822");
1353 msgPart->setName("forwarded message");
1354 msgPart->setContentDescription(msg->from()+": "+msg->subject());
1355 msgPart->setContentDisposition( "inline" );
1356 msgPart->setMessageBody( KMail::Util::ByteArray( msg->asDwString() ) );
1357
1358 fwdMsg->link(msg, KMMsgStatusForwarded);
1359 mWin->addAttach(msgPart);
1360 }
1361
1362 mWin->show();
1363
1364 return OK;
1365}
1366
1367
1368KMForwardDigestCommand::KMForwardDigestCommand( TQWidget *parent,
1369 const TQPtrList<KMMsgBase> &msgList, uint identity, KMail::Composer *win )
1370 : KMCommand( parent, msgList ), mIdentity( identity ),
1371 mWin( TQGuardedPtr<KMail::Composer>( win ))
1372{
1373}
1374
1375KMForwardDigestCommand::KMForwardDigestCommand( TQWidget *parent,
1376 KMMessage * msg, uint identity, KMail::Composer *win )
1377 : KMCommand( parent, msg ), mIdentity( identity ),
1378 mWin( TQGuardedPtr< KMail::Composer >( win ))
1379{
1380}
1381
1382KMCommand::Result KMForwardDigestCommand::execute()
1383{
1384 TQPtrList<KMMessage> msgList = retrievedMsgs();
1385
1386 if ( msgList.count() < 2 )
1387 return Undefined; // must have more than 1 for a digest
1388
1389 uint id = 0;
1390 KMMessage *fwdMsg = new KMMessage;
1391 KMMessagePart *msgPart = new KMMessagePart;
1392 TQString msgPartText;
1393 int msgCnt = 0; // incase there are some we can't forward for some reason
1394
1395 // dummy header initialization; initialization with the correct identity
1396 // is done below
1397 fwdMsg->initHeader( id );
1398 fwdMsg->setAutomaticFields( true );
1399 fwdMsg->mMsg->Headers().ContentType().CreateBoundary( 1 );
1400 TQCString boundary( fwdMsg->mMsg->Headers().ContentType().Boundary().c_str() );
1401 msgPartText = i18n("\nThis is a MIME digest forward. The content of the"
1402 " message is contained in the attachment(s).\n\n\n");
1403 // iterate through all the messages to be forwarded
1404 for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
1405 // set the identity
1406 if ( id == 0 )
1407 id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
1408 // set the part header
1409 msgPartText += "--";
1410 msgPartText += TQString::fromLatin1( boundary );
1411 msgPartText += "\nContent-Type: MESSAGE/RFC822";
1412 msgPartText += TQString( "; CHARSET=%1" ).arg( TQString(msg->charset()) );
1413 msgPartText += '\n';
1414 DwHeaders dwh;
1415 dwh.MessageId().CreateDefault();
1416 msgPartText += TQString( "Content-ID: %1\n" ).arg( dwh.MessageId().AsString().c_str() );
1417 msgPartText += TQString( "Content-Description: %1" ).arg( msg->subject() );
1418 if ( !msg->subject().contains( "(fwd)" ) )
1419 msgPartText += " (fwd)";
1420 msgPartText += "\n\n";
1421 // remove headers that shouldn't be forwarded
1423 msg->removeHeaderField( "BCC" );
1424 // set the part
1425 msgPartText += msg->headerAsString();
1426 msgPartText += '\n';
1427 msgPartText += msg->body();
1428 msgPartText += '\n'; // eot
1429 msgCnt++;
1430 fwdMsg->link( msg, KMMsgStatusForwarded );
1431 }
1432
1433 if ( id == 0 )
1434 id = mIdentity; // use folder identity if no message had an id set
1435 fwdMsg->initHeader( id );
1436 msgPartText += "--";
1437 msgPartText += TQString::fromLatin1( boundary );
1438 msgPartText += "--\n";
1439 TQCString tmp;
1440 msgPart->setTypeStr( "MULTIPART" );
1441 tmp.sprintf( "Digest; boundary=\"%s\"", boundary.data() );
1442 msgPart->setSubtypeStr( tmp );
1443 msgPart->setName( "unnamed" );
1444 msgPart->setCte( DwMime::kCte7bit ); // does it have to be 7bit?
1445 msgPart->setContentDescription( TQString( "Digest of %1 messages." ).arg( msgCnt ) );
1446 // THIS HAS TO BE AFTER setCte()!!!!
1447 msgPart->setBodyEncoded( TQCString( msgPartText.ascii() ) );
1448 KCursorSaver busy( KBusyPtr::busy() );
1449 KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
1450 win->addAttach( msgPart );
1451 win->show();
1452 return OK;
1453}
1454
1455KMRedirectCommand::KMRedirectCommand( TQWidget *parent,
1456 KMMessage *msg )
1457 : KMCommand( parent, msg )
1458{
1459}
1460
1461KMCommand::Result KMRedirectCommand::execute()
1462{
1463 KMMessage *msg = retrievedMessage();
1464 if ( !msg || !msg->codec() )
1465 return Failed;
1466
1467 RedirectDialog dlg( parentWidget(), "redirect", true,
1468 kmkernel->msgSender()->sendImmediate() );
1469 if (dlg.exec()==TQDialog::Rejected) return Failed;
1470
1471 KMMessage *newMsg = msg->createRedirect( dlg.to() );
1472 KMFilterAction::sendMDN( msg, KMime::MDN::Dispatched );
1473
1474 const KMail::MessageSender::SendMethod method = dlg.sendImmediate()
1475 ? KMail::MessageSender::SendImmediate
1476 : KMail::MessageSender::SendLater;
1477 if ( !kmkernel->msgSender()->send( newMsg, method ) ) {
1478 kdDebug(5006) << "KMRedirectCommand: could not redirect message (sending failed)" << endl;
1479 return Failed; // error: couldn't send
1480 }
1481 return OK;
1482}
1483
1484
1485KMCustomReplyToCommand::KMCustomReplyToCommand( TQWidget *parent, KMMessage *msg,
1486 const TQString &selection,
1487 const TQString &tmpl )
1488 : KMCommand( parent, msg ), mSelection( selection ), mTemplate( tmpl )
1489{
1490}
1491
1492KMCommand::Result KMCustomReplyToCommand::execute()
1493{
1494 KCursorSaver busy(KBusyPtr::busy());
1495 KMMessage *msg = retrievedMessage();
1496 if ( !msg || !msg->codec() ) {
1497 return Failed;
1498 }
1499 KMMessage *reply = msg->createReply( KMail::ReplySmart, mSelection,
1500 false, true, mTemplate );
1501 KMail::Composer * win = KMail::makeComposer( reply );
1502 win->setCharset( msg->codec()->mimeName(), true );
1503 win->setReplyFocus();
1504 win->show();
1505
1506 return OK;
1507}
1508
1509
1510KMCustomReplyAllToCommand::KMCustomReplyAllToCommand( TQWidget *parent, KMMessage *msg,
1511 const TQString &selection,
1512 const TQString &tmpl )
1513 : KMCommand( parent, msg ), mSelection( selection ), mTemplate( tmpl )
1514{
1515}
1516
1517KMCommand::Result KMCustomReplyAllToCommand::execute()
1518{
1519 KCursorSaver busy(KBusyPtr::busy());
1520 KMMessage *msg = retrievedMessage();
1521 if ( !msg || !msg->codec() ) {
1522 return Failed;
1523 }
1524 KMMessage *reply = msg->createReply( KMail::ReplyAll, mSelection,
1525 false, true, mTemplate );
1526 KMail::Composer * win = KMail::makeComposer( reply );
1527 win->setCharset( msg->codec()->mimeName(), true );
1528 win->setReplyFocus();
1529 win->show();
1530
1531 return OK;
1532}
1533
1534
1535KMCustomForwardCommand::KMCustomForwardCommand( TQWidget *parent,
1536 const TQPtrList<KMMsgBase> &msgList, uint identity, const TQString &tmpl )
1537 : KMCommand( parent, msgList ),
1538 mIdentity( identity ), mTemplate( tmpl )
1539{
1540}
1541
1542KMCustomForwardCommand::KMCustomForwardCommand( TQWidget *parent,
1543 KMMessage *msg, uint identity, const TQString &tmpl )
1544 : KMCommand( parent, msg ),
1545 mIdentity( identity ), mTemplate( tmpl )
1546{
1547}
1548
1549KMCommand::Result KMCustomForwardCommand::execute()
1550{
1551 TQPtrList<KMMessage> msgList = retrievedMsgs();
1552
1553 if (msgList.count() >= 2) { // Multiple forward
1554
1555 uint id = 0;
1556 TQPtrList<KMMessage> linklist;
1557 for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
1558 // set the identity
1559 if (id == 0)
1560 id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
1561
1562 // msgText += msg->createForwardBody();
1563 linklist.append( msg );
1564 }
1565 if ( id == 0 )
1566 id = mIdentity; // use folder identity if no message had an id set
1567 KMMessage *fwdMsg = new KMMessage;
1568 fwdMsg->initHeader( id );
1569 fwdMsg->setAutomaticFields( true );
1570 fwdMsg->setCharset( "utf-8" );
1571 // fwdMsg->setBody( msgText );
1572
1573 for ( KMMessage *msg = linklist.first(); msg; msg = linklist.next() ) {
1574 TemplateParser parser( fwdMsg, TemplateParser::Forward );
1575 parser.setSelection( msg->body() ); // FIXME: Why is this needed?
1576 parser.process( msg, 0, true );
1577
1578 fwdMsg->link( msg, KMMsgStatusForwarded );
1579 }
1580
1581 KCursorSaver busy( KBusyPtr::busy() );
1582 KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
1583 win->setCharset("");
1584 win->show();
1585
1586 } else { // forward a single message at most
1587
1588 KMMessage *msg = msgList.getFirst();
1589 if ( !msg || !msg->codec() )
1590 return Failed;
1591
1592 KCursorSaver busy( KBusyPtr::busy() );
1593 KMMessage *fwdMsg = msg->createForward( mTemplate );
1594
1595 uint id = msg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt();
1596 if ( id == 0 )
1597 id = mIdentity;
1598 {
1599 KMail::Composer * win = KMail::makeComposer( fwdMsg, id );
1600 win->setCharset( fwdMsg->codec()->mimeName(), true );
1601 win->show();
1602 }
1603 }
1604 return OK;
1605}
1606
1607
1608KMPrintCommand::KMPrintCommand( TQWidget *parent, KMMessage *msg,
1609 const KMail::HeaderStyle *headerStyle,
1610 const KMail::HeaderStrategy *headerStrategy,
1611 bool htmlOverride, bool htmlLoadExtOverride,
1612 bool useFixedFont, const TQString & encoding )
1613 : KMCommand( parent, msg ),
1614 mHeaderStyle( headerStyle ), mHeaderStrategy( headerStrategy ),
1615 mHtmlOverride( htmlOverride ),
1616 mHtmlLoadExtOverride( htmlLoadExtOverride ),
1617 mUseFixedFont( useFixedFont ), mEncoding( encoding )
1618{
1619 if ( GlobalSettings::useDefaultFonts() )
1620 mOverrideFont = TDEGlobalSettings::generalFont();
1621 else {
1622 TDEConfigGroup fonts( KMKernel::config(), "Fonts" );
1623 TQString tmp = fonts.readEntry( "print-font", TDEGlobalSettings::generalFont().toString() );
1624 mOverrideFont.fromString( tmp );
1625 }
1626}
1627
1628
1629void KMPrintCommand::setOverrideFont( const TQFont& font )
1630{
1631 mOverrideFont = font;
1632}
1633
1634KMCommand::Result KMPrintCommand::execute()
1635{
1636 KMReaderWin printWin( 0, 0, 0 );
1637 printWin.setPrinting( true );
1638 printWin.readConfig();
1639 if ( mHeaderStyle != 0 && mHeaderStrategy != 0 )
1640 printWin.setHeaderStyleAndStrategy( mHeaderStyle, mHeaderStrategy );
1641 printWin.setHtmlOverride( mHtmlOverride );
1642 printWin.setHtmlLoadExtOverride( mHtmlLoadExtOverride );
1643 printWin.setUseFixedFont( mUseFixedFont );
1644 printWin.setOverrideEncoding( mEncoding );
1645 printWin.cssHelper()->setPrintFont( mOverrideFont );
1646 printWin.setDecryptMessageOverwrite( true );
1647 printWin.setMsg( retrievedMessage(), true );
1648 printWin.printMsg();
1649
1650 return OK;
1651}
1652
1653
1654KMSeStatusCommand::KMSeStatusCommand( KMMsgStatus status,
1655 const TQValueList<TQ_UINT32> &serNums, bool toggle )
1656 : mStatus( status ), mSerNums( serNums ), mToggle( toggle )
1657{
1658}
1659
1660KMCommand::Result KMSeStatusCommand::execute()
1661{
1662 TQValueListIterator<TQ_UINT32> it;
1663 int idx = -1;
1664 KMFolder *folder = 0;
1665 bool parenStatus = false;
1666
1667 // Toggle actions on threads toggle the whole thread
1668 // depending on the state of the parent.
1669 if (mToggle) {
1670 KMMsgBase *msg;
1671 KMMsgDict::instance()->getLocation( *mSerNums.begin(), &folder, &idx );
1672 if (folder) {
1673 msg = folder->getMsgBase(idx);
1674 if (msg && (msg->status()&mStatus))
1675 parenStatus = true;
1676 else
1677 parenStatus = false;
1678 }
1679 }
1680 TQMap< KMFolder*, TQValueList<int> > folderMap;
1681 for ( it = mSerNums.begin(); it != mSerNums.end(); ++it ) {
1682 KMMsgDict::instance()->getLocation( *it, &folder, &idx );
1683 if (folder) {
1684 if (mToggle) {
1685 KMMsgBase *msg = folder->getMsgBase(idx);
1686 // check if we are already at the target toggle state
1687 if (msg) {
1688 bool myStatus;
1689 if (msg->status()&mStatus)
1690 myStatus = true;
1691 else
1692 myStatus = false;
1693 if (myStatus != parenStatus)
1694 continue;
1695 }
1696 }
1697 /* Collect the ids for each folder in a separate list and
1698 send them off in one go at the end. */
1699 folderMap[folder].append(idx);
1700 }
1701 }
1702 TQMapIterator< KMFolder*, TQValueList<int> > it2 = folderMap.begin();
1703 while ( it2 != folderMap.end() ) {
1704 KMFolder *f = it2.key();
1705 f->setStatus( (*it2), mStatus, mToggle );
1706 ++it2;
1707 }
1708 //tdeApp->dcopClient()->emitDCOPSignal( "unreadCountChanged()", TQByteArray() );
1709
1710 return OK;
1711}
1712
1713
1714KMFilterCommand::KMFilterCommand( const TQCString &field, const TQString &value )
1715 : mField( field ), mValue( value )
1716{
1717}
1718
1719KMCommand::Result KMFilterCommand::execute()
1720{
1721 kmkernel->filterMgr()->createFilter( mField, mValue );
1722
1723 return OK;
1724}
1725
1726
1727KMFilterActionCommand::KMFilterActionCommand( TQWidget *parent,
1728 const TQPtrList<KMMsgBase> &msgList,
1729 KMFilter *filter )
1730 : KMCommand( parent, msgList ), mFilter( filter )
1731{
1732 TQPtrListIterator<KMMsgBase> it(msgList);
1733 while ( it.current() ) {
1734 serNumList.append( (*it)->getMsgSerNum() );
1735 ++it;
1736 }
1737}
1738
1739KMCommand::Result KMFilterActionCommand::execute()
1740{
1741 KCursorSaver busy( KBusyPtr::busy() );
1742
1743 int msgCount = 0;
1744 int msgCountToFilter = serNumList.count();
1745 ProgressItem* progressItem =
1746 ProgressManager::createProgressItem ( "filter"+ProgressManager::getUniqueID(),
1747 i18n( "Filtering messages" ) );
1748 progressItem->setTotalItems( msgCountToFilter );
1749 TQValueList<TQ_UINT32>::const_iterator it;
1750 for ( it = serNumList.begin(); it != serNumList.end(); it++ ) {
1751 TQ_UINT32 serNum = *it;
1752 int diff = msgCountToFilter - ++msgCount;
1753 if ( diff < 10 || !( msgCount % 20 ) || msgCount <= 10 ) {
1754 progressItem->updateProgress();
1755 TQString statusMsg = i18n("Filtering message %1 of %2");
1756 statusMsg = statusMsg.arg( msgCount ).arg( msgCountToFilter );
1757 KPIM::BroadcastStatus::instance()->setStatusMsg( statusMsg );
1758 tdeApp->eventLoop()->processEvents( TQEventLoop::ExcludeUserInput, 50 );
1759 }
1760
1761 int filterResult = kmkernel->filterMgr()->process( serNum, mFilter );
1762 if (filterResult == 2) {
1763 // something went horribly wrong (out of space?)
1764 perror("Critical error");
1765 kmkernel->emergencyExit( i18n("Not enough free disk space?" ));
1766 }
1767 progressItem->incCompletedItems();
1768 }
1769
1770 progressItem->setComplete();
1771 progressItem = 0;
1772 return OK;
1773}
1774
1775
1776KMMetaFilterActionCommand::KMMetaFilterActionCommand( KMFilter *filter,
1777 KMHeaders *headers,
1778 KMMainWidget *main )
1779 : TQObject( main ),
1780 mFilter( filter ), mHeaders( headers ), mMainWidget( main )
1781{
1782}
1783
1784void KMMetaFilterActionCommand::start()
1785{
1786 if (ActionScheduler::isEnabled() ) {
1787 // use action scheduler
1788 KMFilterMgr::FilterSet set = KMFilterMgr::All;
1789 TQValueList<KMFilter*> filters;
1790 filters.append( mFilter );
1791 ActionScheduler *scheduler = new ActionScheduler( set, filters, mHeaders );
1792 scheduler->setAlwaysMatch( true );
1793 scheduler->setAutoDestruct( true );
1794
1795 int contentX, contentY;
1796 HeaderItem *nextItem = mHeaders->prepareMove( &contentX, &contentY );
1797 TQPtrList<KMMsgBase> msgList = *mHeaders->selectedMsgs(true);
1798 mHeaders->finalizeMove( nextItem, contentX, contentY );
1799
1800 for (KMMsgBase *msg = msgList.first(); msg; msg = msgList.next())
1801 scheduler->execFilters( msg );
1802 } else {
1803 KMCommand *filterCommand =
1804 new KMFilterActionCommand( mMainWidget,
1805 *mHeaders->selectedMsgs(), mFilter );
1806 filterCommand->start();
1807 int contentX, contentY;
1808 HeaderItem *item = mHeaders->prepareMove( &contentX, &contentY );
1809 mHeaders->finalizeMove( item, contentX, contentY );
1810 }
1811}
1812
1813FolderShortcutCommand::FolderShortcutCommand( KMMainWidget *mainwidget,
1814 KMFolder *folder )
1815 : mMainWidget( mainwidget ), mFolder( folder ), mAction( 0 )
1816{
1817}
1818
1819
1820FolderShortcutCommand::~FolderShortcutCommand()
1821{
1822 if ( mAction ) mAction->unplugAll();
1823 delete mAction;
1824}
1825
1826void FolderShortcutCommand::start()
1827{
1828 mMainWidget->slotSelectFolder( mFolder );
1829}
1830
1831void FolderShortcutCommand::setAction( TDEAction* action )
1832{
1833 mAction = action;
1834}
1835
1836KMMailingListFilterCommand::KMMailingListFilterCommand( TQWidget *parent,
1837 KMMessage *msg )
1838 : KMCommand( parent, msg )
1839{
1840}
1841
1842KMCommand::Result KMMailingListFilterCommand::execute()
1843{
1844 TQCString name;
1845 TQString value;
1846 KMMessage *msg = retrievedMessage();
1847 if (!msg)
1848 return Failed;
1849
1850 if ( !MailingList::name( msg, name, value ).isEmpty() ) {
1851 kmkernel->filterMgr()->createFilter( name, value );
1852 return OK;
1853 }
1854 else
1855 return Failed;
1856}
1857
1858
1859void KMMenuCommand::folderToPopupMenu(bool move,
1860 TQObject *receiver, KMMenuToFolder *aMenuToFolder, TQPopupMenu *menu )
1861{
1862 while ( menu->count() )
1863 {
1864 TQPopupMenu *popup = menu->findItem( menu->idAt( 0 ) )->popup();
1865 if (popup)
1866 delete popup;
1867 else
1868 menu->removeItemAt( 0 );
1869 }
1870
1871 if (!kmkernel->imapFolderMgr()->dir().first() &&
1872 !kmkernel->dimapFolderMgr()->dir().first())
1873 { // only local folders
1874 makeFolderMenu( &kmkernel->folderMgr()->dir(), move,
1875 receiver, aMenuToFolder, menu );
1876 } else {
1877 // operate on top-level items
1878 TQPopupMenu* subMenu = new TQPopupMenu(menu);
1879 makeFolderMenu( &kmkernel->folderMgr()->dir(),
1880 move, receiver, aMenuToFolder, subMenu );
1881 menu->insertItem( i18n( "Local Folders" ), subMenu );
1882 KMFolderDir* fdir = &kmkernel->imapFolderMgr()->dir();
1883 for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) {
1884 if (node->isDir())
1885 continue;
1886 subMenu = new TQPopupMenu(menu);
1887 makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu );
1888 menu->insertItem( node->label(), subMenu );
1889 }
1890 fdir = &kmkernel->dimapFolderMgr()->dir();
1891 for (KMFolderNode *node = fdir->first(); node; node = fdir->next()) {
1892 if (node->isDir())
1893 continue;
1894 subMenu = new TQPopupMenu(menu);
1895 makeFolderMenu( node, move, receiver, aMenuToFolder, subMenu );
1896 menu->insertItem( node->label(), subMenu );
1897 }
1898 }
1899}
1900
1901void KMMenuCommand::makeFolderMenu(KMFolderNode* node, bool move,
1902 TQObject *receiver, KMMenuToFolder *aMenuToFolder, TQPopupMenu *menu )
1903{
1904 // connect the signals
1905 if (move)
1906 {
1907 disconnect(menu, TQ_SIGNAL(activated(int)), receiver,
1908 TQ_SLOT(moveSelectedToFolder(int)));
1909 connect(menu, TQ_SIGNAL(activated(int)), receiver,
1910 TQ_SLOT(moveSelectedToFolder(int)));
1911 } else {
1912 disconnect(menu, TQ_SIGNAL(activated(int)), receiver,
1913 TQ_SLOT(copySelectedToFolder(int)));
1914 connect(menu, TQ_SIGNAL(activated(int)), receiver,
1915 TQ_SLOT(copySelectedToFolder(int)));
1916 }
1917
1918 KMFolder *folder = 0;
1919 KMFolderDir *folderDir = 0;
1920 if (node->isDir()) {
1921 folderDir = static_cast<KMFolderDir*>(node);
1922 } else {
1923 folder = static_cast<KMFolder*>(node);
1924 folderDir = folder->child();
1925 }
1926
1927 if (folder && !folder->noContent())
1928 {
1929 int menuId;
1930 if (move)
1931 menuId = menu->insertItem(i18n("Move to This Folder"));
1932 else
1933 menuId = menu->insertItem(i18n("Copy to This Folder"));
1934 aMenuToFolder->insert( menuId, folder );
1935 menu->setItemEnabled( menuId, !folder->isReadOnly() );
1936 menu->insertSeparator();
1937 }
1938
1939 if (!folderDir)
1940 return;
1941
1942 for (KMFolderNode *it = folderDir->first(); it; it = folderDir->next() ) {
1943 if (it->isDir())
1944 continue;
1945 KMFolder *child = static_cast<KMFolder*>(it);
1946 TQString label = child->label();
1947 label.replace("&","&&");
1948 if (child->child() && child->child()->first()) {
1949 // descend
1950 TQPopupMenu *subMenu = new TQPopupMenu(menu, "subMenu");
1951 makeFolderMenu( child, move, receiver,
1952 aMenuToFolder, subMenu );
1953 menu->insertItem( label, subMenu );
1954 } else {
1955 // insert an item
1956 int menuId = menu->insertItem( label );
1957 aMenuToFolder->insert( menuId, child );
1958 menu->setItemEnabled( menuId, !child->isReadOnly() );
1959 }
1960 }
1961 return;
1962}
1963
1964
1965KMCopyCommand::KMCopyCommand( KMFolder* destFolder,
1966 const TQPtrList<KMMsgBase> &msgList )
1967:mDestFolder( destFolder ), mMsgList( msgList )
1968{
1969 setDeletesItself( true );
1970}
1971
1972KMCopyCommand::KMCopyCommand( KMFolder* destFolder, KMMessage * msg )
1973 :mDestFolder( destFolder )
1974{
1975 setDeletesItself( true );
1976 mMsgList.append( &msg->toMsgBase() );
1977}
1978
1979KMCommand::Result KMCopyCommand::execute()
1980{
1981 KMMsgBase *msgBase;
1982 KMMessage *msg, *newMsg;
1983 int idx = -1;
1984 bool isMessage;
1985 TQPtrList<KMMessage> list;
1986 TQPtrList<KMMessage> localList;
1987
1988 if (mDestFolder && mDestFolder->open("kmcommand") != 0)
1989 {
1990 deleteLater();
1991 return Failed;
1992 }
1993
1994 setEmitsCompletedItself( true );
1995 KCursorSaver busy(KBusyPtr::busy());
1996
1997 for (msgBase = mMsgList.first(); msgBase; msgBase = mMsgList.next() )
1998 {
1999 KMFolder *srcFolder = msgBase->parent();
2000 if (( isMessage = msgBase->isMessage() ))
2001 {
2002 msg = static_cast<KMMessage*>(msgBase);
2003 } else {
2004 idx = srcFolder->find(msgBase);
2005 assert(idx != -1);
2006 msg = srcFolder->getMsg(idx);
2007 // corrupt IMAP cache, see FolderStorage::getMsg()
2008 if ( msg == 0 ) {
2009 KMessageBox::error( parentWidget(), i18n("Corrupt IMAP cache detected in folder %1. "
2010 "Copying of messages aborted.").arg( srcFolder->prettyURL() ) );
2011 deleteLater();
2012 return Failed;
2013 }
2014 }
2015
2016 if (srcFolder && mDestFolder &&
2017 (srcFolder->folderType()== KMFolderTypeImap) &&
2018 (mDestFolder->folderType() == KMFolderTypeImap) &&
2019 (static_cast<KMFolderImap*>(srcFolder->storage())->account() ==
2020 static_cast<KMFolderImap*>(mDestFolder->storage())->account()))
2021 {
2022 // imap => imap with same account
2023 list.append(msg);
2024 } else {
2025 newMsg = new KMMessage( new DwMessage( *msg->asDwMessage() ) );
2026 newMsg->setComplete(msg->isComplete());
2027 // make sure the attachment state is only calculated when it's complete
2028 if (!newMsg->isComplete())
2029 newMsg->setReadyToShow(false);
2030 newMsg->setStatus(msg->status());
2031
2032 if (srcFolder && !newMsg->isComplete())
2033 {
2034 // imap => others
2035 newMsg->setParent(msg->parent());
2036 FolderJob *job = srcFolder->createJob(newMsg);
2037 job->setCancellable( false );
2038 mPendingJobs << job;
2039 connect(job, TQ_SIGNAL(messageRetrieved(KMMessage*)),
2040 mDestFolder, TQ_SLOT(reallyAddCopyOfMsg(KMMessage*)));
2041 connect( job, TQ_SIGNAL(result(KMail::FolderJob*)),
2042 this, TQ_SLOT(slotJobFinished(KMail::FolderJob*)) );
2043 job->start();
2044 } else {
2045 // local => others
2046 localList.append(newMsg);
2047 }
2048 }
2049
2050 if (srcFolder && !isMessage && list.isEmpty())
2051 {
2052 assert(idx != -1);
2053 srcFolder->unGetMsg( idx );
2054 }
2055
2056 } // end for
2057
2058 bool deleteNow = false;
2059 if (!localList.isEmpty())
2060 {
2061 TQValueList<int> index;
2062 mDestFolder->addMsg( localList, index );
2063 for ( TQValueListIterator<int> it = index.begin(); it != index.end(); ++it ) {
2064 mDestFolder->unGetMsg( *it );
2065 }
2066 if ( mDestFolder->folderType() == KMFolderTypeImap ) {
2067 if ( mPendingJobs.isEmpty() ) {
2068 // wait for the end of the copy before closing the folder
2069 KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
2070 connect( imapDestFolder, TQ_SIGNAL( folderComplete( KMFolderImap*, bool ) ),
2071 this, TQ_SLOT( slotFolderComplete( KMFolderImap*, bool ) ) );
2072 }
2073 } else {
2074 deleteNow = list.isEmpty() && mPendingJobs.isEmpty(); // we're done if there are no other mails we need to fetch
2075 }
2076 }
2077
2078//TODO: Get rid of the other cases just use this one for all types of folder
2079//TODO: requires adding copyMsg and getFolder methods to KMFolder.h
2080 if (!list.isEmpty())
2081 {
2082 // copy the message(s); note: the list is empty afterwards!
2083 KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
2084 connect( imapDestFolder, TQ_SIGNAL( folderComplete( KMFolderImap*, bool ) ),
2085 this, TQ_SLOT( slotFolderComplete( KMFolderImap*, bool ) ) );
2086 imapDestFolder->copyMsg(list);
2087 imapDestFolder->getFolder();
2088 }
2089
2090 // only close the folder and delete the job if we're done
2091 // otherwise this is done in slotMsgAdded or slotFolderComplete
2092 if ( deleteNow )
2093 {
2094 mDestFolder->close("kmcommand");
2095 setResult( OK );
2096 emit completed( this );
2097 deleteLater();
2098 }
2099
2100 return OK;
2101}
2102
2103void KMCopyCommand::slotJobFinished(KMail::FolderJob * job)
2104{
2105 mPendingJobs.remove( job );
2106 if ( job->error() ) {
2107 kdDebug(5006) << k_funcinfo << "folder job failed: " << job->error() << endl;
2108 // kill all pending jobs
2109 for ( TQValueList<KMail::FolderJob*>::Iterator it = mPendingJobs.begin(); it != mPendingJobs.end(); ++it ) {
2110 disconnect( (*it), TQ_SIGNAL(result(KMail::FolderJob*)),
2111 this, TQ_SLOT(slotJobFinished(KMail::FolderJob*)) );
2112 (*it)->kill();
2113 }
2114 mPendingJobs.clear();
2115 setResult( Failed );
2116 }
2117
2118 if ( mPendingJobs.isEmpty() )
2119 {
2120 mDestFolder->close("kmcommand");
2121 emit completed( this );
2122 deleteLater();
2123 }
2124}
2125
2126void KMCopyCommand::slotFolderComplete( KMFolderImap*, bool success )
2127{
2128 kdDebug(5006) << k_funcinfo << success << endl;
2129 if ( !success )
2130 setResult( Failed );
2131 mDestFolder->close( "kmcommand" );
2132 emit completed( this );
2133 deleteLater();
2134}
2135
2136
2137KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
2138 const TQPtrList<KMMsgBase> &msgList)
2139 : mDestFolder( destFolder ), mProgressItem( 0 )
2140{
2141 TQPtrList<KMMsgBase> tmp = msgList;
2142 for ( KMMsgBase *msgBase = tmp.first(); msgBase; msgBase = tmp.next() )
2143 mSerNumList.append( msgBase->getMsgSerNum() );
2144}
2145
2146KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
2147 KMMessage *msg )
2148 : mDestFolder( destFolder ), mProgressItem( 0 )
2149{
2150 mSerNumList.append( msg->getMsgSerNum() );
2151}
2152
2153KMMoveCommand::KMMoveCommand( KMFolder* destFolder,
2154 KMMsgBase *msgBase )
2155 : mDestFolder( destFolder ), mProgressItem( 0 )
2156{
2157 mSerNumList.append( msgBase->getMsgSerNum() );
2158}
2159
2160KMMoveCommand::KMMoveCommand( TQ_UINT32 )
2161 : mProgressItem( 0 )
2162{
2163}
2164
2165KMCommand::Result KMMoveCommand::execute()
2166{
2167 setEmitsCompletedItself( true );
2168 setDeletesItself( true );
2169 typedef TQMap< KMFolder*, TQPtrList<KMMessage>* > FolderToMessageListMap;
2170 FolderToMessageListMap folderDeleteList;
2171
2172 if (mDestFolder && mDestFolder->open("kmcommand") != 0) {
2173 completeMove( Failed );
2174 return Failed;
2175 }
2176 KCursorSaver busy(KBusyPtr::busy());
2177
2178 // TODO set SSL state according to source and destfolder connection?
2179 Q_ASSERT( !mProgressItem );
2180 mProgressItem =
2181 ProgressManager::createProgressItem (
2182 "move"+ProgressManager::getUniqueID(),
2183 mDestFolder ? i18n( "Moving messages" ) : i18n( "Deleting messages" ) );
2184 connect( mProgressItem, TQ_SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
2185 this, TQ_SLOT( slotMoveCanceled() ) );
2186
2187 KMMessage *msg;
2188 int rc = 0;
2189 int index;
2190 TQPtrList<KMMessage> list;
2191 int undoId = -1;
2192 mCompleteWithAddedMsg = false;
2193
2194 if (mDestFolder) {
2195 connect (mDestFolder, TQ_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)),
2196 this, TQ_SLOT(slotMsgAddedToDestFolder(KMFolder*, TQ_UINT32)));
2197 mLostBoys = mSerNumList;
2198 }
2199 mProgressItem->setTotalItems( mSerNumList.count() );
2200
2201 for ( TQValueList<TQ_UINT32>::ConstIterator it = mSerNumList.constBegin(); it != mSerNumList.constEnd(); ++it ) {
2202 if ( *it == 0 ) {
2203 kdDebug(5006) << k_funcinfo << "serial number == 0!" << endl;
2204 continue; // invalid message
2205 }
2206 KMFolder *srcFolder = 0;
2207 int idx = -1;
2208 KMMsgDict::instance()->getLocation( *it, &srcFolder, &idx );
2209 if (srcFolder == mDestFolder)
2210 continue;
2211 assert(srcFolder);
2212 assert(idx != -1);
2213 if ( !srcFolder->isOpened() ) {
2214 srcFolder->open( "kmmovecommand" );
2215 mOpenedFolders.append( srcFolder );
2216 }
2217 msg = srcFolder->getMsg(idx);
2218 if ( !msg ) {
2219 kdDebug(5006) << k_funcinfo << "No message found for serial number " << *it << endl;
2220 continue;
2221 }
2222 bool undo = msg->enableUndo();
2223
2224 if ( msg && msg->transferInProgress() &&
2225 srcFolder->folderType() == KMFolderTypeImap )
2226 {
2227 // cancel the download
2228 msg->setTransferInProgress( false, true );
2229 static_cast<KMFolderImap*>(srcFolder->storage())->ignoreJobsForMessage( msg );
2230 }
2231
2232 if (mDestFolder) {
2233 if (mDestFolder->folderType() == KMFolderTypeImap) {
2234 /* If we are moving to an imap folder, connect to it's completed
2235 * signal so we notice when all the mails should have showed up in it
2236 * but haven't for some reason. */
2237 KMFolderImap *imapFolder = static_cast<KMFolderImap*> ( mDestFolder->storage() );
2238 disconnect (imapFolder, TQ_SIGNAL(folderComplete( KMFolderImap*, bool )),
2239 this, TQ_SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
2240
2241 connect (imapFolder, TQ_SIGNAL(folderComplete( KMFolderImap*, bool )),
2242 this, TQ_SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
2243 list.append(msg);
2244 } else {
2245 // We are moving to a local folder.
2246 if ( srcFolder->folderType() == KMFolderTypeImap )
2247 {
2248 // do not complete here but wait until all messages are transferred
2249 mCompleteWithAddedMsg = true;
2250 }
2251 rc = mDestFolder->moveMsg(msg, &index);
2252 if (rc == 0 && index != -1) {
2253 KMMsgBase *mb = mDestFolder->unGetMsg( mDestFolder->count() - 1 );
2254 if (undo && mb)
2255 {
2256 if ( undoId == -1 )
2257 undoId = kmkernel->undoStack()->newUndoAction( srcFolder, mDestFolder );
2258 kmkernel->undoStack()->addMsgToAction( undoId, mb->getMsgSerNum() );
2259 }
2260 } else if (rc != 0) {
2261 // Something went wrong. Stop processing here, it is likely that the
2262 // other moves would fail as well.
2263 completeMove( Failed );
2264 return Failed;
2265 }
2266 }
2267 } else {
2268 // really delete messages that are already in the trash folder or if
2269 // we are really, really deleting, not just moving to trash
2270 if (srcFolder->folderType() == KMFolderTypeImap) {
2271 if (!folderDeleteList[srcFolder])
2272 folderDeleteList[srcFolder] = new TQPtrList<KMMessage>;
2273 folderDeleteList[srcFolder]->append( msg );
2274 } else {
2275 srcFolder->removeMsg(idx);
2276 delete msg;
2277 }
2278 }
2279 }
2280 if (!list.isEmpty() && mDestFolder) {
2281 // will be completed with folderComplete signal
2282 mDestFolder->moveMsg(list, &index);
2283 } else {
2284 FolderToMessageListMap::Iterator it;
2285 for ( it = folderDeleteList.begin(); it != folderDeleteList.end(); ++it ) {
2286 it.key()->removeMsg(*it.data());
2287 delete it.data();
2288 }
2289 if ( !mCompleteWithAddedMsg ) {
2290 // imap folders will be completed in slotMsgAddedToDestFolder
2291 completeMove( OK );
2292 }
2293 }
2294
2295 return OK;
2296}
2297
2298void KMMoveCommand::slotImapFolderCompleted(KMFolderImap* imapFolder, bool success)
2299{
2300 disconnect (imapFolder, TQ_SIGNAL(folderComplete( KMFolderImap*, bool )),
2301 this, TQ_SLOT(slotImapFolderCompleted( KMFolderImap*, bool )));
2302 if ( success ) {
2303 // the folder was checked successfully but we were still called, so check
2304 // if we are still waiting for messages to show up. If so, uidValidity
2305 // changed, or something else went wrong. Clean up.
2306
2307 /* Unfortunately older UW imap servers change uid validity for each put job.
2308 * Yes, it is really that broken. *sigh* So we cannot report error here, I guess. */
2309 if ( !mLostBoys.isEmpty() ) {
2310 kdDebug(5006) << "### Not all moved messages reported back that they were " << endl
2311 << "### added to the target folder. Did uidValidity change? " << endl;
2312 }
2313 completeMove( OK );
2314 } else {
2315 // Should we inform the user here or leave that to the caller?
2316 completeMove( Failed );
2317 }
2318}
2319
2320void KMMoveCommand::slotMsgAddedToDestFolder(KMFolder *folder, TQ_UINT32 serNum)
2321{
2322 if ( folder != mDestFolder || mLostBoys.find( serNum ) == mLostBoys.end() ) {
2323 //kdDebug(5006) << "KMMoveCommand::msgAddedToDestFolder different "
2324 // "folder or invalid serial number." << endl;
2325 return;
2326 }
2327 mLostBoys.remove(serNum);
2328 if ( mLostBoys.isEmpty() ) {
2329 // we are done. All messages transferred to the host succesfully
2330 disconnect (mDestFolder, TQ_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)),
2331 this, TQ_SLOT(slotMsgAddedToDestFolder(KMFolder*, TQ_UINT32)));
2332 if (mDestFolder && mDestFolder->folderType() != KMFolderTypeImap) {
2333 mDestFolder->sync();
2334 }
2335 if ( mCompleteWithAddedMsg ) {
2336 completeMove( OK );
2337 }
2338 } else {
2339 if ( mProgressItem ) {
2340 mProgressItem->incCompletedItems();
2341 mProgressItem->updateProgress();
2342 }
2343 }
2344}
2345
2346void KMMoveCommand::completeMove( Result result )
2347{
2348 if ( mDestFolder )
2349 mDestFolder->close("kmcommand");
2350 while ( !mOpenedFolders.empty() ) {
2351 KMFolder *folder = mOpenedFolders.back();
2352 mOpenedFolders.pop_back();
2353 folder->close("kmcommand");
2354 }
2355 if ( mProgressItem ) {
2356 mProgressItem->setComplete();
2357 mProgressItem = 0;
2358 }
2359 setResult( result );
2360 emit completed( this );
2361 deleteLater();
2362}
2363
2364void KMMoveCommand::slotMoveCanceled()
2365{
2366 completeMove( Canceled );
2367}
2368
2369// srcFolder doesn't make much sense for searchFolders
2370KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder,
2371 const TQPtrList<KMMsgBase> &msgList )
2372:KMMoveCommand( findTrashFolder( srcFolder ), msgList)
2373{
2374 srcFolder->open("kmcommand");
2375 mOpenedFolders.push_back( srcFolder );
2376}
2377
2378KMDeleteMsgCommand::KMDeleteMsgCommand( KMFolder* srcFolder, KMMessage * msg )
2379:KMMoveCommand( findTrashFolder( srcFolder ), msg)
2380{
2381 srcFolder->open("kmcommand");
2382 mOpenedFolders.push_back( srcFolder );
2383}
2384
2385KMDeleteMsgCommand::KMDeleteMsgCommand( TQ_UINT32 sernum )
2386:KMMoveCommand( sernum )
2387{
2388 if ( !sernum ) {
2389 setDestFolder( 0 );
2390 return;
2391 }
2392
2393 KMFolder *srcFolder = 0;
2394 int idx;
2395 KMMsgDict::instance()->getLocation( sernum, &srcFolder, &idx );
2396 if ( srcFolder ) {
2397 KMMsgBase *msg = srcFolder->getMsgBase( idx );
2398 srcFolder->open("kmcommand");
2399 mOpenedFolders.push_back( srcFolder );
2400 addMsg( msg );
2401 }
2402 setDestFolder( findTrashFolder( srcFolder ) );
2403}
2404
2405KMFolder * KMDeleteMsgCommand::findTrashFolder( KMFolder * folder )
2406{
2407 KMFolder* trash = folder->trashFolder();
2408 if( !trash )
2409 trash = kmkernel->trashFolder();
2410 if( trash != folder )
2411 return trash;
2412 return 0;
2413}
2414
2415
2416KMUrlClickedCommand::KMUrlClickedCommand( const KURL &url, uint identity,
2417 KMReaderWin *readerWin, bool htmlPref, KMMainWidget *mainWidget )
2418 :mUrl( url ), mIdentity( identity ), mReaderWin( readerWin ),
2419 mHtmlPref( htmlPref ), mMainWidget( mainWidget )
2420{
2421}
2422
2423KMCommand::Result KMUrlClickedCommand::execute()
2424{
2425 KMMessage* msg;
2426
2427 if (mUrl.protocol() == "mailto")
2428 {
2429 msg = new KMMessage;
2430 msg->initHeader(mIdentity);
2431 msg->setCharset("utf-8");
2432 msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
2433 TQString query=mUrl.query();
2434 while (!query.isEmpty()) {
2435 TQString queryPart;
2436 int secondQuery = query.find('?',1);
2437 if (secondQuery != -1)
2438 queryPart = query.left(secondQuery);
2439 else
2440 queryPart = query;
2441 query = query.mid(queryPart.length());
2442
2443 if (queryPart.left(9) == "?subject=")
2444 msg->setSubject( KURL::decode_string(queryPart.mid(9)) );
2445 else if (queryPart.left(6) == "?body=")
2446 // It is correct to convert to latin1() as URL should not contain
2447 // anything except ascii.
2448 msg->setBody( KURL::decode_string(queryPart.mid(6)).latin1() );
2449 else if (queryPart.left(4) == "?cc=")
2450 msg->setCc( KURL::decode_string(queryPart.mid(4)) );
2451 }
2452
2453 KMail::Composer * win = KMail::makeComposer( msg, mIdentity );
2454 win->setCharset("", true);
2455 win->show();
2456 }
2457 else if ( mUrl.protocol() == "im" )
2458 {
2459 kmkernel->imProxy()->chatWithContact( mUrl.path() );
2460 }
2461 else if ((mUrl.protocol() == "http") || (mUrl.protocol() == "https") ||
2462 (mUrl.protocol() == "ftp") || (mUrl.protocol() == "file") ||
2463 (mUrl.protocol() == "ftps") || (mUrl.protocol() == "sftp" ) ||
2464 (mUrl.protocol() == "help") || (mUrl.protocol() == "vnc") ||
2465 (mUrl.protocol() == "smb") || (mUrl.protocol() == "fish") ||
2466 (mUrl.protocol() == "news"))
2467 {
2468 KPIM::BroadcastStatus::instance()->setStatusMsg( i18n("Opening URL..."));
2469 KMimeType::Ptr mime = KMimeType::findByURL( mUrl );
2470 if (mime->name() == "application/x-desktop" ||
2471 mime->name() == "application/x-executable" ||
2472 mime->name() == "application/x-msdos-program" ||
2473 mime->name() == "application/x-shellscript" )
2474 {
2475 if (KMessageBox::warningYesNo( 0, i18n( "<qt>Do you really want to execute <b>%1</b>?</qt>" )
2476 .arg( mUrl.prettyURL() ), TQString(), i18n("Execute"), KStdGuiItem::cancel() ) != KMessageBox::Yes)
2477 return Canceled;
2478 }
2479 KRun * runner = new KRun( mUrl );
2480 runner->setRunExecutables( false );
2481 }
2482 else
2483 return Failed;
2484
2485 return OK;
2486}
2487
2488KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( TQWidget *parent, KMMessage *msg )
2489 : KMCommand( parent, msg ), mImplicitAttachments( true ), mEncoded( false )
2490{
2491}
2492
2493KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( TQWidget *parent, const TQPtrList<KMMsgBase>& msgs )
2494 : KMCommand( parent, msgs ), mImplicitAttachments( true ), mEncoded( false )
2495{
2496}
2497
2498KMSaveAttachmentsCommand::KMSaveAttachmentsCommand( TQWidget *parent, TQPtrList<partNode>& attachments,
2499 KMMessage *msg, bool encoded )
2500 : KMCommand( parent ), mImplicitAttachments( false ), mEncoded( encoded )
2501{
2502 for ( TQPtrListIterator<partNode> it( attachments ); it.current(); ++it ) {
2503 mAttachmentMap.insert( it.current(), msg );
2504 }
2505}
2506
2507KMCommand::Result KMSaveAttachmentsCommand::execute()
2508{
2509 setEmitsCompletedItself( true );
2510 if ( mImplicitAttachments ) {
2511 TQPtrList<KMMessage> msgList = retrievedMsgs();
2512 KMMessage *msg;
2513 for ( TQPtrListIterator<KMMessage> itr( msgList );
2514 ( msg = itr.current() );
2515 ++itr ) {
2516 partNode *rootNode = partNode::fromMessage( msg );
2517 for ( partNode *child = rootNode; child;
2518 child = child->firstChild() ) {
2519 for ( partNode *node = child; node; node = node->nextSibling() ) {
2520 if ( node->type() != DwMime::kTypeMultipart )
2521 mAttachmentMap.insert( node, msg );
2522 }
2523 }
2524 }
2525 }
2526 setDeletesItself( true );
2527 // load all parts
2528 KMLoadPartsCommand *command = new KMLoadPartsCommand( mAttachmentMap );
2529 connect( command, TQ_SIGNAL( partsRetrieved() ),
2530 this, TQ_SLOT( slotSaveAll() ) );
2531 command->start();
2532
2533 return OK;
2534}
2535
2536void KMSaveAttachmentsCommand::slotSaveAll()
2537{
2538 // now that all message parts have been retrieved, remove all parts which
2539 // don't represent an attachment if they were not explicitely passed in the
2540 // c'tor
2541 if ( mImplicitAttachments ) {
2542 for ( PartNodeMessageMap::iterator it = mAttachmentMap.begin();
2543 it != mAttachmentMap.end(); ) {
2544 // only body parts which have a filename or a name parameter (except for
2545 // the root node for which name is set to the message's subject) are
2546 // considered attachments
2547 if ( it.key()->msgPart().fileName().stripWhiteSpace().isEmpty() &&
2548 ( it.key()->msgPart().name().stripWhiteSpace().isEmpty() ||
2549 !it.key()->parentNode() ) ) {
2550 PartNodeMessageMap::iterator delIt = it;
2551 ++it;
2552 mAttachmentMap.remove( delIt );
2553 }
2554 else
2555 ++it;
2556 }
2557 if ( mAttachmentMap.isEmpty() ) {
2558 KMessageBox::information( 0, i18n("Found no attachments to save.") );
2559 setResult( OK ); // The user has already been informed.
2560 emit completed( this );
2561 deleteLater();
2562 return;
2563 }
2564 }
2565
2566 KURL url, dirUrl;
2567 if ( mAttachmentMap.count() > 1 ) {
2568 // get the dir
2569 dirUrl = KDirSelectDialog::selectDirectory( TQString(), false,
2570 parentWidget(),
2571 i18n("Save Attachments To") );
2572 if ( !dirUrl.isValid() ) {
2573 setResult( Canceled );
2574 emit completed( this );
2575 deleteLater();
2576 return;
2577 }
2578
2579 // we may not get a slash-terminated url out of KDirSelectDialog
2580 dirUrl.adjustPath( 1 );
2581 }
2582 else {
2583 // only one item, get the desired filename
2584 partNode *node = mAttachmentMap.begin().key();
2585 // replace all ':' with '_' because ':' isn't allowed on FAT volumes
2586 TQString s =
2587 node->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
2588 if ( s.isEmpty() )
2589 s = node->msgPart().name().stripWhiteSpace().replace( ':', '_' );
2590 if ( s.isEmpty() )
2591 s = i18n("filename for an unnamed attachment", "attachment.1");
2592 url = KFileDialog::getSaveURL( s, TQString(), parentWidget(),
2593 TQString() );
2594 if ( url.isEmpty() ) {
2595 setResult( Canceled );
2596 emit completed( this );
2597 deleteLater();
2598 return;
2599 }
2600 }
2601
2602 TQMap< TQString, int > renameNumbering;
2603
2604 Result globalResult = OK;
2605 int unnamedAtmCount = 0;
2606 for ( PartNodeMessageMap::const_iterator it = mAttachmentMap.begin();
2607 it != mAttachmentMap.end();
2608 ++it ) {
2609 KURL curUrl;
2610 if ( !dirUrl.isEmpty() ) {
2611 curUrl = dirUrl;
2612 TQString s =
2613 it.key()->msgPart().fileName().stripWhiteSpace().replace( ':', '_' );
2614 if ( s.isEmpty() )
2615 s = it.key()->msgPart().name().stripWhiteSpace().replace( ':', '_' );
2616 if ( s.isEmpty() ) {
2617 ++unnamedAtmCount;
2618 s = i18n("filename for the %1-th unnamed attachment",
2619 "attachment.%1")
2620 .arg( unnamedAtmCount );
2621 }
2622 curUrl.setFileName( s );
2623 } else {
2624 curUrl = url;
2625 }
2626
2627 if ( !curUrl.isEmpty() ) {
2628
2629 // Rename the file if we have already saved one with the same name:
2630 // try appending a number before extension (e.g. "pic.jpg" => "pic_2.jpg")
2631 TQString origFile = curUrl.fileName();
2632 TQString file = origFile;
2633
2634 while ( renameNumbering.contains(file) ) {
2635 file = origFile;
2636 int num = renameNumbering[file] + 1;
2637 int dotIdx = file.findRev('.');
2638 file = file.insert( (dotIdx>=0) ? dotIdx : file.length(), TQString("_") + TQString::number(num) );
2639 }
2640 curUrl.setFileName(file);
2641
2642 // Increment the counter for both the old and the new filename
2643 if ( !renameNumbering.contains(origFile))
2644 renameNumbering[origFile] = 1;
2645 else
2646 renameNumbering[origFile]++;
2647
2648 if ( file != origFile ) {
2649 if ( !renameNumbering.contains(file))
2650 renameNumbering[file] = 1;
2651 else
2652 renameNumbering[file]++;
2653 }
2654
2655
2656 if ( TDEIO::NetAccess::exists( curUrl, false, parentWidget() ) ) {
2657 if ( KMessageBox::warningContinueCancel( parentWidget(),
2658 i18n( "A file named %1 already exists. Do you want to overwrite it?" )
2659 .arg( curUrl.fileName() ),
2660 i18n( "File Already Exists" ), i18n("&Overwrite") ) == KMessageBox::Cancel) {
2661 continue;
2662 }
2663 }
2664 // save
2665 const Result result = saveItem( it.key(), curUrl );
2666 if ( result != OK )
2667 globalResult = result;
2668 }
2669 }
2670 setResult( globalResult );
2671 emit completed( this );
2672 deleteLater();
2673}
2674
2675KMCommand::Result KMSaveAttachmentsCommand::saveItem( partNode *node,
2676 const KURL& url )
2677{
2678 bool bSaveEncrypted = false;
2679 bool bEncryptedParts = node->encryptionState() != KMMsgNotEncrypted;
2680 if( bEncryptedParts )
2681 if( KMessageBox::questionYesNo( parentWidget(),
2682 i18n( "The part %1 of the message is encrypted. Do you want to keep the encryption when saving?" ).
2683 arg( url.fileName() ),
2684 i18n( "KMail Question" ), i18n("Keep Encryption"), i18n("Do Not Keep") ) ==
2685 KMessageBox::Yes )
2686 bSaveEncrypted = true;
2687
2688 bool bSaveWithSig = true;
2689 if( node->signatureState() != KMMsgNotSigned )
2690 if( KMessageBox::questionYesNo( parentWidget(),
2691 i18n( "The part %1 of the message is signed. Do you want to keep the signature when saving?" ).
2692 arg( url.fileName() ),
2693 i18n( "KMail Question" ), i18n("Keep Signature"), i18n("Do Not Keep") ) !=
2694 KMessageBox::Yes )
2695 bSaveWithSig = false;
2696
2697 TQByteArray data;
2698 if ( mEncoded )
2699 {
2700 // This does not decode the Message Content-Transfer-Encoding
2701 // but saves the _original_ content of the message part
2702 data = KMail::Util::ByteArray( node->msgPart().dwBody() );
2703 }
2704 else
2705 {
2706 if( bSaveEncrypted || !bEncryptedParts) {
2707 partNode *dataNode = node;
2708 TQCString rawReplyString;
2709 bool gotRawReplyString = false;
2710 if( !bSaveWithSig ) {
2711 if( DwMime::kTypeMultipart == node->type() &&
2712 DwMime::kSubtypeSigned == node->subType() ){
2713 // carefully look for the part that is *not* the signature part:
2714 if( node->findType( DwMime::kTypeApplication,
2715 DwMime::kSubtypePgpSignature,
2716 true, false ) ){
2717 dataNode = node->findTypeNot( DwMime::kTypeApplication,
2718 DwMime::kSubtypePgpSignature,
2719 true, false );
2720 }else if( node->findType( DwMime::kTypeApplication,
2721 DwMime::kSubtypePkcs7Mime,
2722 true, false ) ){
2723 dataNode = node->findTypeNot( DwMime::kTypeApplication,
2724 DwMime::kSubtypePkcs7Mime,
2725 true, false );
2726 }else{
2727 dataNode = node->findTypeNot( DwMime::kTypeMultipart,
2728 DwMime::kSubtypeUnknown,
2729 true, false );
2730 }
2731 }else{
2732 ObjectTreeParser otp( 0, 0, false, false, false );
2733
2734 // process this node and all it's siblings and descendants
2735 dataNode->setProcessed( false, true );
2736 otp.parseObjectTree( dataNode );
2737
2738 rawReplyString = otp.rawReplyString();
2739 gotRawReplyString = true;
2740 }
2741 }
2742 TQByteArray cstr = gotRawReplyString
2743 ? rawReplyString
2744 : dataNode->msgPart().bodyDecodedBinary();
2745 data = cstr;
2746 size_t size = cstr.size();
2747 if ( dataNode->msgPart().type() == DwMime::kTypeText ) {
2748 // convert CRLF to LF before writing text attachments to disk
2749 size = KMail::Util::crlf2lf( cstr.data(), size );
2750 }
2751 data.resize( size );
2752 }
2753 }
2754 TQDataStream ds;
2755 TQFile file;
2756 KTempFile tf;
2757 tf.setAutoDelete( true );
2758 if ( url.isLocalFile() )
2759 {
2760 // save directly
2761 file.setName( url.path() );
2762 if ( !file.open( IO_WriteOnly ) )
2763 {
2764 KMessageBox::error( parentWidget(),
2765 i18n( "%2 is detailed error description",
2766 "Could not write the file %1:\n%2" )
2767 .arg( file.name() )
2768 .arg( TQString::fromLocal8Bit( strerror( errno ) ) ),
2769 i18n( "KMail Error" ) );
2770 return Failed;
2771 }
2772
2773 // #79685 by default use the umask the user defined, but let it be configurable
2774 if ( GlobalSettings::self()->disregardUmask() )
2775 fchmod( file.handle(), S_IRUSR | S_IWUSR );
2776
2777 ds.setDevice( &file );
2778 } else
2779 {
2780 // tmp file for upload
2781 ds.setDevice( tf.file() );
2782 }
2783
2784 ds.writeRawBytes( data.data(), data.size() );
2785 if ( !url.isLocalFile() )
2786 {
2787 tf.close();
2788 if ( !TDEIO::NetAccess::upload( tf.name(), url, parentWidget() ) )
2789 {
2790 KMessageBox::error( parentWidget(),
2791 i18n( "Could not write the file %1." )
2792 .arg( url.path() ),
2793 i18n( "KMail Error" ) );
2794 return Failed;
2795 }
2796 } else
2797 file.close();
2798 return OK;
2799}
2800
2801KMLoadPartsCommand::KMLoadPartsCommand( TQPtrList<partNode>& parts, KMMessage *msg )
2802 : mNeedsRetrieval( 0 )
2803{
2804 for ( TQPtrListIterator<partNode> it( parts ); it.current(); ++it ) {
2805 mPartMap.insert( it.current(), msg );
2806 }
2807}
2808
2809KMLoadPartsCommand::KMLoadPartsCommand( partNode *node, KMMessage *msg )
2810 : mNeedsRetrieval( 0 )
2811{
2812 mPartMap.insert( node, msg );
2813}
2814
2815KMLoadPartsCommand::KMLoadPartsCommand( PartNodeMessageMap& partMap )
2816 : mNeedsRetrieval( 0 ), mPartMap( partMap )
2817{
2818}
2819
2820void KMLoadPartsCommand::slotStart()
2821{
2822 for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
2823 it != mPartMap.end();
2824 ++it ) {
2825 if ( !it.key()->msgPart().isComplete() &&
2826 !it.key()->msgPart().partSpecifier().isEmpty() ) {
2827 // incomplete part, so retrieve it first
2828 ++mNeedsRetrieval;
2829 KMFolder* curFolder = it.data()->parent();
2830 if ( curFolder ) {
2831 FolderJob *job =
2832 curFolder->createJob( it.data(), FolderJob::tGetMessage,
2833 0, it.key()->msgPart().partSpecifier() );
2834 job->setCancellable( false );
2835 connect( job, TQ_SIGNAL(messageUpdated(KMMessage*, TQString)),
2836 this, TQ_SLOT(slotPartRetrieved(KMMessage*, TQString)) );
2837 job->start();
2838 } else
2839 kdWarning(5006) << "KMLoadPartsCommand - msg has no parent" << endl;
2840 }
2841 }
2842 if ( mNeedsRetrieval == 0 )
2843 execute();
2844}
2845
2846void KMLoadPartsCommand::slotPartRetrieved( KMMessage *msg,
2847 TQString partSpecifier )
2848{
2849 DwBodyPart *part =
2850 msg->findDwBodyPart( msg->getFirstDwBodyPart(), partSpecifier );
2851 if ( part ) {
2852 // update the DwBodyPart in the partNode
2853 for ( PartNodeMessageMap::const_iterator it = mPartMap.begin();
2854 it != mPartMap.end();
2855 ++it ) {
2856 if ( it.key()->dwPart()->partId() == part->partId() )
2857 it.key()->setDwPart( part );
2858 }
2859 } else
2860 kdWarning(5006) << "KMLoadPartsCommand::slotPartRetrieved - could not find bodypart!" << endl;
2861 --mNeedsRetrieval;
2862 if ( mNeedsRetrieval == 0 )
2863 execute();
2864}
2865
2866KMCommand::Result KMLoadPartsCommand::execute()
2867{
2868 emit partsRetrieved();
2869 setResult( OK );
2870 emit completed( this );
2871 deleteLater();
2872 return OK;
2873}
2874
2875KMResendMessageCommand::KMResendMessageCommand( TQWidget *parent,
2876 KMMessage *msg )
2877 :KMCommand( parent, msg )
2878{
2879}
2880
2881KMCommand::Result KMResendMessageCommand::execute()
2882{
2883 KMMessage *msg = retrievedMessage();
2884 if ( !msg || !msg->codec() ) {
2885 return Failed;
2886 }
2887 KMMessage *newMsg = new KMMessage(*msg);
2888
2889 TQStringList whiteList;
2890 whiteList << "To" << "Cc" << "Bcc" << "Subject";
2891 newMsg->sanitizeHeaders( whiteList );
2892
2893 if( newMsg->type() == DwMime::kTypeText) {
2894 newMsg->setCharset(msg->codec()->mimeName());
2895 }
2896 newMsg->setParent( 0 );
2897
2898 // make sure we have an identity set, default, if necessary
2899 newMsg->setHeaderField("X-KMail-Identity", TQString::number( newMsg->identityUoid() ));
2900 newMsg->applyIdentity( newMsg->identityUoid() );
2901
2902 KMail::Composer * win = KMail::makeComposer();
2903 win->setMsg(newMsg, false, true);
2904 win->show();
2905
2906 return OK;
2907}
2908
2909KMMailingListCommand::KMMailingListCommand( TQWidget *parent, KMFolder *folder )
2910 : KMCommand( parent ), mFolder( folder )
2911{
2912}
2913
2914KMCommand::Result KMMailingListCommand::execute()
2915{
2916 KURL::List lst = urls();
2917 TQString handler = ( mFolder->mailingList().handler() == MailingList::KMail )
2918 ? "mailto" : "https";
2919
2920 KMCommand *command = 0;
2921 for ( KURL::List::Iterator itr = lst.begin(); itr != lst.end(); ++itr ) {
2922 if ( handler == (*itr).protocol() ) {
2923 command = new KMUrlClickedCommand( *itr, mFolder->identity(), 0, false );
2924 }
2925 }
2926 if ( !command && !lst.empty() ) {
2927 command =
2928 new KMUrlClickedCommand( lst.first(), mFolder->identity(), 0, false );
2929 }
2930 if ( command ) {
2931 connect( command, TQ_SIGNAL( completed( KMCommand * ) ),
2932 this, TQ_SLOT( commandCompleted( KMCommand * ) ) );
2933 setDeletesItself( true );
2934 setEmitsCompletedItself( true );
2935 command->start();
2936 return OK;
2937 }
2938 return Failed;
2939}
2940
2941void KMMailingListCommand::commandCompleted( KMCommand *command )
2942{
2943 setResult( command->result() );
2944 emit completed( this );
2945 deleteLater();
2946}
2947
2948KMMailingListPostCommand::KMMailingListPostCommand( TQWidget *parent, KMFolder *folder )
2949 : KMMailingListCommand( parent, folder )
2950{
2951}
2952KURL::List KMMailingListPostCommand::urls() const
2953{
2954 return mFolder->mailingList().postURLS();
2955}
2956
2957KMMailingListSubscribeCommand::KMMailingListSubscribeCommand( TQWidget *parent, KMFolder *folder )
2958 : KMMailingListCommand( parent, folder )
2959{
2960}
2961KURL::List KMMailingListSubscribeCommand::urls() const
2962{
2963 return mFolder->mailingList().subscribeURLS();
2964}
2965
2966KMMailingListUnsubscribeCommand::KMMailingListUnsubscribeCommand( TQWidget *parent, KMFolder *folder )
2967 : KMMailingListCommand( parent, folder )
2968{
2969}
2970KURL::List KMMailingListUnsubscribeCommand::urls() const
2971{
2972 return mFolder->mailingList().unsubscribeURLS();
2973}
2974
2975KMMailingListArchivesCommand::KMMailingListArchivesCommand( TQWidget *parent, KMFolder *folder )
2976 : KMMailingListCommand( parent, folder )
2977{
2978}
2979KURL::List KMMailingListArchivesCommand::urls() const
2980{
2981 return mFolder->mailingList().archiveURLS();
2982}
2983
2984KMMailingListHelpCommand::KMMailingListHelpCommand( TQWidget *parent, KMFolder *folder )
2985 : KMMailingListCommand( parent, folder )
2986{
2987}
2988KURL::List KMMailingListHelpCommand::urls() const
2989{
2990 return mFolder->mailingList().helpURLS();
2991}
2992
2993KMIMChatCommand::KMIMChatCommand( const KURL &url, KMMessage *msg )
2994 :mUrl( url ), mMessage( msg )
2995{
2996}
2997
2998KMCommand::Result KMIMChatCommand::execute()
2999{
3000 kdDebug( 5006 ) << k_funcinfo << " URL is: " << mUrl << endl;
3001 TQString addr = KMMessage::decodeMailtoUrl( mUrl.path() );
3002 // find UID for mail address
3003 TDEABC::AddressBook *addressBook = TDEABC::StdAddressBook::self( true );
3004 TDEABC::AddresseeList addressees = addressBook->findByEmail( KPIM::getEmailAddress( addr ) ) ;
3005
3006 // start chat
3007 if( addressees.count() == 1 ) {
3008 kmkernel->imProxy()->chatWithContact( addressees[0].uid() );
3009 return OK;
3010 }
3011 else
3012 {
3013 kdDebug( 5006 ) << "Didn't find exactly one addressee, couldn't tell who to chat to for that email address. Count = " << addressees.count() << endl;
3014
3015 TQString apology;
3016 if ( addressees.isEmpty() )
3017 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." );
3018 else
3019 {
3020 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." );
3021 TQStringList nameList;
3022 TDEABC::AddresseeList::const_iterator it = addressees.begin();
3023 TDEABC::AddresseeList::const_iterator end = addressees.end();
3024 for ( ; it != end; ++it )
3025 {
3026 nameList.append( (*it).realName() );
3027 }
3028 TQString names = nameList.join( TQString::fromLatin1( ",\n" ) );
3029 apology = apology.arg( names );
3030 }
3031
3032 KMessageBox::sorry( parentWidget(), apology );
3033 return Failed;
3034 }
3035}
3036
3037KMHandleAttachmentCommand::KMHandleAttachmentCommand( partNode* node,
3038 KMMessage* msg, int atmId, const TQString& atmName,
3039 AttachmentAction action, KService::Ptr offer, TQWidget* parent )
3040: KMCommand( parent ), mNode( node ), mMsg( msg ), mAtmId( atmId ), mAtmName( atmName ),
3041 mAction( action ), mOffer( offer ), mJob( 0 )
3042{
3043}
3044
3045void KMHandleAttachmentCommand::slotStart()
3046{
3047 if ( !mNode->msgPart().isComplete() )
3048 {
3049 // load the part
3050 kdDebug(5006) << "load part" << endl;
3051 KMLoadPartsCommand *command = new KMLoadPartsCommand( mNode, mMsg );
3052 connect( command, TQ_SIGNAL( partsRetrieved() ),
3053 this, TQ_SLOT( slotPartComplete() ) );
3054 command->start();
3055 } else
3056 {
3057 execute();
3058 }
3059}
3060
3061void KMHandleAttachmentCommand::slotPartComplete()
3062{
3063 execute();
3064}
3065
3066KMCommand::Result KMHandleAttachmentCommand::execute()
3067{
3068 switch( mAction )
3069 {
3070 case Open:
3071 atmOpen();
3072 break;
3073 case OpenWith:
3074 atmOpenWith();
3075 break;
3076 case View:
3077 atmView();
3078 break;
3079 case Save:
3080 atmSave();
3081 break;
3082 case Properties:
3083 atmProperties();
3084 break;
3085 case ChiasmusEncrypt:
3086 atmEncryptWithChiasmus();
3087 return Undefined;
3088 break;
3089 default:
3090 kdDebug(5006) << "unknown action " << mAction << endl;
3091 break;
3092 }
3093 setResult( OK );
3094 emit completed( this );
3095 deleteLater();
3096 return OK;
3097}
3098
3099TQString KMHandleAttachmentCommand::createAtmFileLink() const
3100{
3101 TQFileInfo atmFileInfo( mAtmName );
3102
3103 if ( atmFileInfo.size() == 0 )
3104 {
3105 kdDebug(5006) << k_funcinfo << "rewriting attachment" << endl;
3106 // there is something wrong so write the file again
3107 TQByteArray data = mNode->msgPart().bodyDecodedBinary();
3108 size_t size = data.size();
3109 if ( mNode->msgPart().type() == DwMime::kTypeText && size) {
3110 // convert CRLF to LF before writing text attachments to disk
3111 size = KMail::Util::crlf2lf( data.data(), size );
3112 }
3113 KPIM::kBytesToFile( data.data(), size, mAtmName, false, false, false );
3114 }
3115
3116 KTempFile *linkFile = new KTempFile( locateLocal("tmp", atmFileInfo.fileName() +"_["),
3117 "]."+ atmFileInfo.extension() );
3118
3119 linkFile->setAutoDelete(true);
3120 TQString linkName = linkFile->name();
3121 delete linkFile;
3122
3123 if ( ::link(TQFile::encodeName( mAtmName ), TQFile::encodeName( linkName )) == 0 ) {
3124 return linkName; // success
3125 }
3126 return TQString();
3127}
3128
3129KService::Ptr KMHandleAttachmentCommand::getServiceOffer()
3130{
3131 KMMessagePart& msgPart = mNode->msgPart();
3132 const TQString contentTypeStr =
3133 ( msgPart.typeStr() + '/' + msgPart.subtypeStr() ).lower();
3134
3135 if ( contentTypeStr == "text/x-vcard" ) {
3136 atmView();
3137 return 0;
3138 }
3139 // determine the MIME type of the attachment
3140 KMimeType::Ptr mimetype;
3141 // prefer the value of the Content-Type header
3142 mimetype = KMimeType::mimeType( contentTypeStr );
3143 if ( mimetype->name() == "application/octet-stream" ) {
3144 // consider the filename if Content-Type is application/octet-stream
3145 mimetype = KMimeType::findByPath( mAtmName, 0, true /* no disk access */ );
3146 }
3147 if ( ( mimetype->name() == "application/octet-stream" )
3148 && msgPart.isComplete() ) {
3149 // consider the attachment's contents if neither the Content-Type header
3150 // nor the filename give us a clue
3151 mimetype = KMimeType::findByFileContent( mAtmName );
3152 }
3153 return KServiceTypeProfile::preferredService( mimetype->name(), "Application" );
3154}
3155
3156void KMHandleAttachmentCommand::atmOpen()
3157{
3158 if ( !mOffer )
3159 mOffer = getServiceOffer();
3160 if ( !mOffer ) {
3161 kdDebug(5006) << k_funcinfo << "got no offer" << endl;
3162 return;
3163 }
3164
3165 KURL::List lst;
3166 KURL url;
3167 bool autoDelete = true;
3168 TQString fname = createAtmFileLink();
3169
3170 if ( fname.isNull() ) {
3171 autoDelete = false;
3172 fname = mAtmName;
3173 }
3174
3175 url.setPath( fname );
3176 lst.append( url );
3177 if ( (KRun::run( *mOffer, lst, autoDelete ) <= 0) && autoDelete ) {
3178 TQFile::remove(url.path());
3179 }
3180}
3181
3182void KMHandleAttachmentCommand::atmOpenWith()
3183{
3184 KURL::List lst;
3185 KURL url;
3186 bool autoDelete = true;
3187 TQString fname = createAtmFileLink();
3188
3189 if ( fname.isNull() ) {
3190 autoDelete = false;
3191 fname = mAtmName;
3192 }
3193
3194 url.setPath( fname );
3195 lst.append( url );
3196 if ( (! KRun::displayOpenWithDialog(lst, autoDelete)) && autoDelete ) {
3197 TQFile::remove( url.path() );
3198 }
3199}
3200
3201void KMHandleAttachmentCommand::atmView()
3202{
3203 // we do not handle this ourself
3204 emit showAttachment( mAtmId, mAtmName );
3205}
3206
3207void KMHandleAttachmentCommand::atmSave()
3208{
3209 TQPtrList<partNode> parts;
3210 parts.append( mNode );
3211 // save, do not leave encoded
3212 KMSaveAttachmentsCommand *command =
3213 new KMSaveAttachmentsCommand( parentWidget(), parts, mMsg, false );
3214 command->start();
3215}
3216
3217void KMHandleAttachmentCommand::atmProperties()
3218{
3219 KMMsgPartDialogCompat dlg( parentWidget() , 0, true );
3220 KMMessagePart& msgPart = mNode->msgPart();
3221 dlg.setMsgPart( &msgPart );
3222 dlg.exec();
3223}
3224
3225void KMHandleAttachmentCommand::atmEncryptWithChiasmus()
3226{
3227 const partNode * node = mNode;
3228 Q_ASSERT( node );
3229 if ( !node )
3230 return;
3231
3232 // FIXME: better detection of mimetype??
3233 if ( !mAtmName.endsWith( ".xia", false ) )
3234 return;
3235
3236 const Kleo::CryptoBackend::Protocol * chiasmus =
3237 Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
3238 Q_ASSERT( chiasmus );
3239 if ( !chiasmus )
3240 return;
3241
3242 const STD_NAMESPACE_PREFIX unique_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", TQStringVariantMap() ) );
3243 if ( !listjob ) {
3244 const TQString msg = i18n( "Chiasmus backend does not offer the "
3245 "\"x-obtain-keys\" function. Please report this bug." );
3246 KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
3247 return;
3248 }
3249
3250 if ( listjob->exec() ) {
3251 listjob->showErrorDialog( parentWidget(), i18n( "Chiasmus Backend Error" ) );
3252 return;
3253 }
3254
3255 const TQVariant result = listjob->property( "result" );
3256 if ( result.type() != TQVariant::StringList ) {
3257 const TQString msg = i18n( "Unexpected return value from Chiasmus backend: "
3258 "The \"x-obtain-keys\" function did not return a "
3259 "string list. Please report this bug." );
3260 KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
3261 return;
3262 }
3263
3264 const TQStringList keys = result.toStringList();
3265 if ( keys.empty() ) {
3266 const TQString msg = i18n( "No keys have been found. Please check that a "
3267 "valid key path has been set in the Chiasmus "
3268 "configuration." );
3269 KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
3270 return;
3271 }
3272
3273 ChiasmusKeySelector selectorDlg( parentWidget(), i18n( "Chiasmus Decryption Key Selection" ),
3274 keys, GlobalSettings::chiasmusDecryptionKey(),
3275 GlobalSettings::chiasmusDecryptionOptions() );
3276 if ( selectorDlg.exec() != TQDialog::Accepted )
3277 return;
3278
3279 GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
3280 GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
3281 assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
3282
3283 Kleo::SpecialJob * job = chiasmus->specialJob( "x-decrypt", TQStringVariantMap() );
3284 if ( !job ) {
3285 const TQString msg = i18n( "Chiasmus backend does not offer the "
3286 "\"x-decrypt\" function. Please report this bug." );
3287 KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
3288 return;
3289 }
3290
3291 const TQByteArray input = node->msgPart().bodyDecodedBinary();
3292
3293 if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
3294 !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
3295 !job->setProperty( "input", input ) ) {
3296 const TQString msg = i18n( "The \"x-decrypt\" function does not accept "
3297 "the expected parameters. Please report this bug." );
3298 KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
3299 return;
3300 }
3301
3302 setDeletesItself( true ); // the job below is async, we have to cleanup ourselves
3303 if ( job->start() ) {
3304 job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
3305 return;
3306 }
3307
3308 mJob = job;
3309 connect( job, TQ_SIGNAL(result(const GpgME::Error&,const TQVariant&)),
3310 this, TQ_SLOT(slotAtmDecryptWithChiasmusResult(const GpgME::Error&,const TQVariant&)) );
3311}
3312
3313static const TQString chomp( const TQString & base, const TQString & suffix, bool cs ) {
3314 return base.endsWith( suffix, cs ) ? base.left( base.length() - suffix.length() ) : base ;
3315}
3316
3317void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusResult( const GpgME::Error & err, const TQVariant & result )
3318{
3319 LaterDeleterWithCommandCompletion d( this );
3320 if ( !mJob )
3321 return;
3322 Q_ASSERT( mJob == sender() );
3323 if ( mJob != sender() )
3324 return;
3325 Kleo::Job * job = mJob;
3326 mJob = 0;
3327 if ( err.isCanceled() )
3328 return;
3329 if ( err ) {
3330 job->showErrorDialog( parentWidget(), i18n( "Chiasmus Decryption Error" ) );
3331 return;
3332 }
3333
3334 if ( result.type() != TQVariant::ByteArray ) {
3335 const TQString msg = i18n( "Unexpected return value from Chiasmus backend: "
3336 "The \"x-decrypt\" function did not return a "
3337 "byte array. Please report this bug." );
3338 KMessageBox::error( parentWidget(), msg, i18n( "Chiasmus Backend Error" ) );
3339 return;
3340 }
3341
3342 const KURL url = KFileDialog::getSaveURL( chomp( mAtmName, ".xia", false ), TQString(), parentWidget() );
3343 if ( url.isEmpty() )
3344 return;
3345
3346 bool overwrite = KMail::Util::checkOverwrite( url, parentWidget() );
3347 if ( !overwrite )
3348 return;
3349
3350 d.setDisabled( true ); // we got this far, don't delete yet
3351 TDEIO::Job * uploadJob = TDEIO::storedPut( result.toByteArray(), url, -1, overwrite, false /*resume*/ );
3352 uploadJob->setWindow( parentWidget() );
3353 connect( uploadJob, TQ_SIGNAL(result(TDEIO::Job*)),
3354 this, TQ_SLOT(slotAtmDecryptWithChiasmusUploadResult(TDEIO::Job*)) );
3355}
3356
3357void KMHandleAttachmentCommand::slotAtmDecryptWithChiasmusUploadResult( TDEIO::Job * job )
3358{
3359 if ( job->error() )
3360 job->showErrorDialog();
3361 LaterDeleterWithCommandCompletion d( this );
3362 d.setResult( OK );
3363}
3364
3365
3366AttachmentModifyCommand::AttachmentModifyCommand(partNode * node, KMMessage * msg, TQWidget * parent) :
3367 KMCommand( parent, msg ),
3368 mPartIndex( node->nodeId() ),
3369 mSernum( 0 )
3370{
3371}
3372
3373AttachmentModifyCommand::AttachmentModifyCommand( int nodeId, KMMessage *msg, TQWidget *parent )
3374 : KMCommand( parent, msg ),
3375 mPartIndex( nodeId ),
3376 mSernum( 0 )
3377{
3378}
3379
3380AttachmentModifyCommand::~ AttachmentModifyCommand()
3381{
3382}
3383
3384KMCommand::Result AttachmentModifyCommand::execute()
3385{
3386 KMMessage *msg = retrievedMessage();
3387 if ( !msg )
3388 return Failed;
3389 mSernum = msg->getMsgSerNum();
3390
3391 mFolder = msg->parent();
3392 if ( !mFolder || !mFolder->storage() )
3393 return Failed;
3394
3395 Result res = doAttachmentModify();
3396 if ( res != OK )
3397 return res;
3398
3399 setEmitsCompletedItself( true );
3400 setDeletesItself( true );
3401 return OK;
3402}
3403
3404void AttachmentModifyCommand::storeChangedMessage(KMMessage * msg)
3405{
3406 if ( !mFolder || !mFolder->storage() ) {
3407 kdWarning(5006) << k_funcinfo << "We lost the folder!" << endl;
3408 setResult( Failed );
3409 emit completed( this );
3410 deleteLater();
3411 }
3412 int res = mFolder->addMsg( msg ) != 0;
3413 if ( mFolder->folderType() == KMFolderTypeImap ) {
3414 KMFolderImap *f = static_cast<KMFolderImap*>( mFolder->storage() );
3415 connect( f, TQ_SIGNAL(folderComplete(KMFolderImap*,bool)),
3416 TQ_SLOT(messageStoreResult(KMFolderImap*,bool)) );
3417 } else {
3418 messageStoreResult( 0, res == 0 );
3419 }
3420}
3421
3422void AttachmentModifyCommand::messageStoreResult(KMFolderImap* folder, bool success )
3423{
3424 Q_UNUSED( folder );
3425 if ( success ) {
3426 KMCommand *delCmd = new KMDeleteMsgCommand( mSernum );
3427 connect( delCmd, TQ_SIGNAL(completed(KMCommand*)), TQ_SLOT(messageDeleteResult(KMCommand*)) );
3428 delCmd->start();
3429 return;
3430 }
3431 kdWarning(5006) << k_funcinfo << "Adding modified message failed." << endl;
3432 setResult( Failed );
3433 emit completed( this );
3434 deleteLater();
3435}
3436
3437void AttachmentModifyCommand::messageDeleteResult(KMCommand * cmd)
3438{
3439 setResult( cmd->result() );
3440 emit completed( this );
3441 deleteLater();
3442}
3443
3444KMDeleteAttachmentCommand::KMDeleteAttachmentCommand(partNode * node, KMMessage * msg, TQWidget * parent) :
3445 AttachmentModifyCommand( node, msg, parent )
3446{
3447 kdDebug(5006) << k_funcinfo << endl;
3448}
3449
3450KMDeleteAttachmentCommand::KMDeleteAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent )
3451 : AttachmentModifyCommand( nodeId, msg, parent )
3452{
3453 kdDebug(5006) << k_funcinfo << endl;
3454}
3455
3456KMDeleteAttachmentCommand::~KMDeleteAttachmentCommand()
3457{
3458 kdDebug(5006) << k_funcinfo << endl;
3459}
3460
3461KMCommand::Result KMDeleteAttachmentCommand::doAttachmentModify()
3462{
3463 KMMessage *msg = retrievedMessage();
3464 if ( !msg || !msg->deleteBodyPart( mPartIndex ) )
3465 return Failed;
3466
3467 KMMessage *newMsg = new KMMessage();
3468 newMsg->fromDwString( msg->asDwString() );
3469 newMsg->setStatus( msg->status() );
3470
3471 storeChangedMessage( newMsg );
3472 return OK;
3473}
3474
3475
3476KMEditAttachmentCommand::KMEditAttachmentCommand(partNode * node, KMMessage * msg, TQWidget * parent) :
3477 AttachmentModifyCommand( node, msg, parent )
3478{
3479 kdDebug(5006) << k_funcinfo << endl;
3480 mTempFile.setAutoDelete( true );
3481}
3482
3483KMEditAttachmentCommand::KMEditAttachmentCommand( int nodeId, KMMessage *msg, TQWidget *parent )
3484 : AttachmentModifyCommand( nodeId, msg, parent )
3485{
3486 kdDebug(5006) << k_funcinfo << endl;
3487 mTempFile.setAutoDelete( true );
3488}
3489
3490KMEditAttachmentCommand::~ KMEditAttachmentCommand()
3491{
3492}
3493
3494KMCommand::Result KMEditAttachmentCommand::doAttachmentModify()
3495{
3496 KMMessage *msg = retrievedMessage();
3497 if ( !msg )
3498 return Failed;
3499
3500 KMMessagePart part;
3501 DwBodyPart *dwpart = msg->findPart( mPartIndex );
3502 if ( !dwpart )
3503 return Failed;
3504 KMMessage::bodyPart( dwpart, &part, true );
3505 if ( !part.isComplete() )
3506 return Failed;
3507
3508 if( !dynamic_cast<DwBody*>( dwpart->Parent() ) )
3509 return Failed;
3510
3511 mTempFile.file()->writeBlock( part.bodyDecodedBinary() );
3512 mTempFile.file()->flush();
3513
3514 KMail::EditorWatcher *watcher =
3515 new KMail::EditorWatcher( KURL( mTempFile.file()->name() ),
3516 part.typeStr() + "/" + part.subtypeStr(),
3517 false, this, parentWidget() );
3518 connect( watcher, TQ_SIGNAL(editDone(KMail::EditorWatcher*)), TQ_SLOT(editDone(KMail::EditorWatcher*)) );
3519 if ( !watcher->start() )
3520 return Failed;
3521 setEmitsCompletedItself( true );
3522 setDeletesItself( true );
3523 return OK;
3524}
3525
3526void KMEditAttachmentCommand::editDone(KMail::EditorWatcher * watcher)
3527{
3528 kdDebug(5006) << k_funcinfo << endl;
3529 // anything changed?
3530 if ( !watcher->fileChanged() ) {
3531 kdDebug(5006) << k_funcinfo << "File has not been changed" << endl;
3532 setResult( Canceled );
3533 emit completed( this );
3534 deleteLater();
3535 }
3536
3537 mTempFile.file()->reset();
3538 TQByteArray data = mTempFile.file()->readAll();
3539
3540 // build the new message
3541 KMMessage *msg = retrievedMessage();
3542 KMMessagePart part;
3543 DwBodyPart *dwpart = msg->findPart( mPartIndex );
3544 KMMessage::bodyPart( dwpart, &part, true );
3545
3546 DwBody *parentNode = dynamic_cast<DwBody*>( dwpart->Parent() );
3547 assert( parentNode );
3548 parentNode->RemoveBodyPart( dwpart );
3549
3550 KMMessagePart att;
3551 att.duplicate( part );
3552 att.setBodyEncodedBinary( data );
3553
3554 DwBodyPart* newDwPart = msg->createDWBodyPart( &att );
3555 parentNode->AddBodyPart( newDwPart );
3556 msg->getTopLevelPart()->Assemble();
3557
3558 KMMessage *newMsg = new KMMessage();
3559 newMsg->fromDwString( msg->asDwString() );
3560 newMsg->setStatus( msg->status() );
3561
3562 storeChangedMessage( newMsg );
3563}
3564
3565
3566CreateTodoCommand::CreateTodoCommand(TQWidget * parent, KMMessage * msg)
3567 : KMCommand( parent, msg )
3568{
3569}
3570
3571KMCommand::Result CreateTodoCommand::execute()
3572{
3573 KMMessage *msg = retrievedMessage();
3574 if ( !msg || !msg->codec() ) {
3575 return Failed;
3576 }
3577
3578 KMail::KorgHelper::ensureRunning();
3579
3580 TQString txt = i18n("From: %1\nTo: %2\nSubject: %3").arg( msg->from() )
3581 .arg( msg->to() ).arg( msg->subject() );
3582
3583 KTempFile tf;
3584 tf.setAutoDelete( true );
3585 TQString uri = "kmail:" + TQString::number( msg->getMsgSerNum() ) + "/" + msg->msgId();
3586 tf.file()->writeBlock( msg->asDwString().c_str(), msg->asDwString().length() );
3587 tf.close();
3588
3589 KCalendarIface_stub *iface = new KCalendarIface_stub( tdeApp->dcopClient(), "korganizer", "CalendarIface" );
3590 iface->openTodoEditor( i18n("Mail: %1").arg( msg->subject() ), txt, uri,
3591 tf.name(), TQStringList(), "message/rfc822", true );
3592 delete iface;
3593
3594 return OK;
3595}
3596
3597#include "kmcommands.moc"
Base class for commands modifying attachements of existing messages.
sets a cursor and makes sure it's restored on destruction Create a KCursorSaver object when you want ...
static void sendMDN(KMMessage *msg, KMime::MDN::DispositionType d, const TQValueList< KMime::MDN::DispositionModifier > &m=TQValueList< KMime::MDN::DispositionModifier >())
Automates the sending of MDNs from filter actions.
KMail list that manages the contents of one directory that may contain folders and/or other directori...
Definition kmfolderdir.h:16
virtual TQString prettyURL() const
URL of the node for visualization purposes.
Mail folder.
Definition kmfolder.h:69
bool isOpened() const
Test if folder is opened.
Definition kmfolder.cpp:500
KMFolder * trashFolder() const
If this folder has a special trash folder set, return it.
Definition kmfolder.cpp:821
virtual TQString prettyURL() const
URL of the node for visualization purposes.
Definition kmfolder.cpp:593
virtual TQString label() const
Returns the label of the folder for visualization.
Definition kmfolder.cpp:581
KMMsgInfo * unGetMsg(int idx)
Replace KMMessage with KMMsgInfo and delete KMMessage
Definition kmfolder.cpp:326
FolderJob * createJob(KMMessage *msg, FolderJob::JobType jt=FolderJob::tGetMessage, KMFolder *folder=0, TQString partSpecifier=TQString(), const AttachmentStrategy *as=0) const
These methods create respective FolderJob (You should derive FolderJob for each derived KMFolder).
Definition kmfolder.cpp:346
bool isMessage(int idx)
Checks if the message is already "gotten" with getMsg.
Definition kmfolder.cpp:331
KMMessage * take(int idx)
Detach message from this folder.
Definition kmfolder.cpp:380
void close(const char *owner, bool force=false)
Close folder.
Definition kmfolder.cpp:489
KMMessage * getMsg(int idx)
Read message at given index.
Definition kmfolder.cpp:321
const KMMsgBase * getMsgBase(int idx) const
Provides access to the basic message fields that are also stored in the index.
Definition kmfolder.cpp:360
KMFolderType folderType() const
Returns the type of this folder.
Definition kmfolder.cpp:233
void setStatus(int idx, KMMsgStatus status, bool toggle=false)
Set the status of the message at index idx to status.
Definition kmfolder.cpp:831
int open(const char *owner)
Open folder for access.
Definition kmfolder.cpp:479
bool noContent() const
Returns, if the folder can't contain mails, but only subfolder.
Definition kmfolder.cpp:301
KMFolderDir * child() const
Returns the folder directory associated with this node or 0 if no such directory exists.
Definition kmfolder.h:157
int find(const KMMsgBase *msg) const
Returns the index of the given message or -1 if not found.
Definition kmfolder.cpp:435
bool isReadOnly() const
Is the folder read-only?
Definition kmfolder.cpp:561
The widget that shows the contents of folders.
Definition kmheaders.h:47
This is a Mime Message.
Definition kmmessage.h:68
uint identityUoid() const
void link(const KMMessage *aMsg, KMMsgStatus aStatus)
Links this message to aMsg, setting link type to aStatus.
void setBody(const TQCString &aStr)
Set the message body.
DwBodyPart * getFirstDwBodyPart() const
Get the 1st DwBodyPart.
void setReadyToShow(bool v)
Set if the message is ready to be shown.
Definition kmmessage.h:874
static void bodyPart(DwBodyPart *aDwBodyPart, KMMessagePart *aPart, bool withBody=true)
Fill the KMMessagePart structure for a given DwBodyPart.
size_t msgSizeServer() const
Get/set size on server.
void setTransferInProgress(bool value, bool force=false)
Set that the message shall not be deleted because it is still required.
TQCString body() const
Get the message body.
TQString msgId() const
Get or set the 'Message-Id' header field.
TQString from() const
Get or set the 'From' header field.
TQCString charset() const
Get the message charset.
void setAutomaticFields(bool isMultipart=false)
Set fields that are either automatically set (Message-id) or that do not change from one message to a...
void setCharset(const TQCString &charset, DwEntity *entity=0)
Sets the charset of the message or a subpart of the message.
void setStatus(const KMMsgStatus status, int idx=-1)
Set status and mark dirty.
bool deleteBodyPart(int partIndex)
Delete a body part with the specified part index.
TQString to() const
Get or set the 'To' header field.
TQString subject() const
Get or set the 'Subject' header field.
KMMessage * createForward(const TQString &tmpl=TQString())
Create a new message that is a forward of this message, filling all required header fields with the p...
void removeHeaderField(const TQCString &name)
Remove header field with given name.
void fromDwString(const DwString &str, bool setStatus=false)
Parse the string and create this message from it.
KMMessage * createReply(KMail::ReplyStrategy replyStrategy=KMail::ReplySmart, TQString selection=TQString(), bool noQuote=false, bool allowDecryption=true, const TQString &tmpl=TQString(), const TQString &originatingAccount=TQString())
Create a new message that is a reply to this message, filling all required header fields with the pro...
TQCString asString() const
Return the entire message contents as a string.
KMMessage * createRedirect(const TQString &toStr)
Create a new message that is a redirect to this message, filling all required header fields with the ...
KMMsgBase & toMsgBase()
Get KMMsgBase for this object.
Definition kmmessage.h:114
void removePrivateHeaderFields()
Remove all private header fields: Status: and X-KMail-
const DwString & asDwString() const
Return the entire message contents in the DwString.
bool transferInProgress() const
Return, if the message should not be deleted.
void sanitizeHeaders(const TQStringList &whiteList=TQStringList())
Remove all headers but the content description ones, and those in the white list.
static TQString decodeMailtoUrl(const TQString &url)
Decodes a mailto URL.
const TQTextCodec * codec() const
Get a TQTextCodec suitable for this message part.
void setComplete(bool v)
Set if the message is a complete message.
Definition kmmessage.h:869
KMMsgStatus status() const
Status of the message.
Definition kmmessage.h:830
TQString headerField(const TQCString &name) const
Returns the value of a header field with the given name.
void applyIdentity(uint id)
Set the from, to, cc, bcc, encrytion etc headers as specified in the given identity.
TQCString mboxMessageSeparator()
Returns an mbox message separator line for this message, i.e.
void initFromMessage(const KMMessage *msg, bool idHeaders=true)
Initialize headers fields according to the identity and the transport header of the given original me...
bool isComplete() const
Return true if the complete message is available without referring to the backing store.
Definition kmmessage.h:867
TQString headerAsString() const
Return header as string.
void initHeader(uint identity=0)
Initialize header fields.
void setHeaderField(const TQCString &name, const TQString &value, HeaderFieldType type=Unstructured, bool prepend=false)
Set the header field with the given name to the given value.
DwBodyPart * findDwBodyPart(int type, int subtype) const
Return the first DwBodyPart matching a given Content-Type or zero, if no found.
DwBodyPart * createDWBodyPart(const KMMessagePart *aPart)
Compose a DwBodyPart (needed for adding a part to the message).
void getLocation(unsigned long key, KMFolder **retFolder, int *retIndex) const
Returns the folder the message represented by the serial number key is in and the index in that folde...
static const KMMsgDict * instance()
Access the globally unique MessageDict.
The attachment dialog with convenience backward compatible methods.
This class implements a "reader window", that is a window used for reading or viewing messages.
Definition kmreaderwin.h:75
Starts an editor for the given URL and emits an signal when editing has been finished.
Visual representation of a member of the set of displayables (mails in the current folder).
Definition headeritem.h:164
This class encapsulates the visual appearance of message headers.
Definition headerstyle.h:51
KMail message redirection dialog.
Window class for secondary KMail window like the composer window and the separate message window.
A LaterDeleter is intended to be used with the RAII ( Resource Acquisition is Initialization ) paradi...
Definition util.h:180
The TemplateParser transforms a message with a given template.
void append(TQByteArray &that, const TQByteArray &str)
Append a bytearray to a bytearray.
Definition util.cpp:144
TQByteArray ByteArray(const DwString &str)
Construct a TQByteArray from a DwString.
Definition util.cpp:122
size_t crlf2lf(char *str, const size_t strLen)
Convert all sequences of "\r\n" (carriage return followed by a line feed) to a single "\n" (line feed...
Definition util.cpp:44
folderdiaquotatab.h
Definition aboutdata.cpp:40