2
Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com>
3
Copyright (C) 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
4
Copyright (c) 2009 Leo Franchi <lfranchi@kde.org>
6
This library is free software; you can redistribute it and/or modify it
7
under the terms of the GNU Library General Public License as published by
8
the Free Software Foundation; either version 2 of the License, or (at your
9
option) any later version.
11
This library is distributed in the hope that it will be useful, but WITHOUT
12
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
14
License for more details.
16
You should have received a copy of the GNU Library General Public License
17
along with this library; see the file COPYING.LIB. If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24
#include "attachmentjob.h"
25
#include "globalpart.h"
27
#include "jobbase_p.h"
29
#include "maintextjob.h"
30
#include "multipartjob.h"
32
#include "encryptjob.h"
33
#include "signencryptjob.h"
34
#include "skeletonmessagejob.h"
35
#include "transparentjob.h"
40
#include <klocalizedstring.h>
42
using namespace Message;
43
using MessageCore::AttachmentPart;
45
class Message::ComposerPrivate : public JobBasePrivate
48
ComposerPrivate( Composer *qq )
49
: JobBasePrivate( qq )
59
, skeletonMessage( 0 )
65
void doStart(); // slot
67
void skeletonJobFinished( KJob *job ); // slot
69
void contentJobFinished( KJob *job ); // slot
70
void contentJobPreCryptFinished( KJob *job ); // slot
71
void contentJobPreInlineFinished( KJob *job ); // slot
72
void signBeforeEncryptJobFinished( KJob *job ); // slot
73
void startEncryptJobs( KMime::Content* content );
74
void composeWithLateAttachments( KMime::Message* headers, KMime::Content* content, AttachmentPart::List parts, std::vector<GpgME::Key> keys, QStringList recipients );
75
void attachmentsFinished( KJob* job ); // slot
77
void composeFinalStep( KMime::Content* headers, KMime::Content* content );
85
Kleo::CryptoMessageFormat format;
86
std::vector<GpgME::Key> signers;
87
QList<QPair<QStringList, std::vector<GpgME::Key> > > encData;
89
QList<KMime::Message::Ptr> resultMessages;
91
// Stuff that the application plays with.
92
GlobalPart *globalPart;
95
AttachmentPart::List attachmentParts;
96
// attachments with different sign/encrypt settings from
97
// main message body. added at the end of the process
98
AttachmentPart::List lateAttachmentParts;
101
// Stuff that we play with.
102
KMime::Message *skeletonMessage;
103
KMime::Content *resultContent;
105
Q_DECLARE_PUBLIC( Composer )
108
void ComposerPrivate::init()
112
// We cannot create these in ComposerPrivate's constructor, because
113
// their parent q is not fully constructed at that time.
114
globalPart = new GlobalPart( q );
115
infoPart = new InfoPart( q );
116
textPart = new TextPart( q );
119
void ComposerPrivate::doStart()
121
Q_ASSERT( !started );
126
void ComposerPrivate::composeStep1()
130
// Create skeleton message (containing headers only; no content).
131
SkeletonMessageJob *skeletonJob = new SkeletonMessageJob( infoPart, globalPart, q );
132
QObject::connect( skeletonJob, SIGNAL(finished(KJob*)), q, SLOT(skeletonJobFinished(KJob*)) );
133
q->addSubjob( skeletonJob );
134
skeletonJob->start();
137
void ComposerPrivate::skeletonJobFinished( KJob *job )
140
return; // KCompositeJob takes care of the error.
143
Q_ASSERT( dynamic_cast<SkeletonMessageJob*>( job ) );
144
SkeletonMessageJob *sjob = static_cast<SkeletonMessageJob*>( job );
145
// SkeletonMessageJob is a special job creating a Message instead of a Content.
146
Q_ASSERT( skeletonMessage == 0 );
147
skeletonMessage = sjob->message();
148
Q_ASSERT( skeletonMessage );
149
skeletonMessage->assemble();
154
void ComposerPrivate::composeStep2()
158
ContentJobBase *mainJob = 0;
159
MainTextJob *mainTextJob = new MainTextJob( textPart, q );
160
if( attachmentParts.isEmpty() ) {
161
// We have no attachments. Use the content given by the MainTextJob.
162
mainJob = mainTextJob;
164
// We have attachments. Create a multipart/mixed content.
165
QMutableListIterator<AttachmentPart::Ptr> iter( attachmentParts );
166
while( iter.hasNext() ) {
167
AttachmentPart::Ptr part = iter.next();
168
kDebug() << "Checking attachment crypto policy..." << part->isSigned() << part->isEncrypted();
169
if( !noCrypto && !autoSaving && ( sign != part->isSigned() || encrypt != part->isEncrypted() ) ) { // different policy
170
kDebug() << "got attachment with different crypto policy!";
171
lateAttachmentParts.append( part );
175
MultipartJob *multipartJob = new MultipartJob( q );
176
multipartJob->setMultipartSubtype( "mixed" );
177
multipartJob->appendSubjob( mainTextJob );
178
foreach( AttachmentPart::Ptr part, attachmentParts ) {
179
multipartJob->appendSubjob( new AttachmentJob( part ) );
181
mainJob = multipartJob;
183
if( sign && encrypt && format & Kleo::InlineOpenPGPFormat ) { // needs custom handling--- one SignEncryptJob by itself
184
kDebug() << "sending to sign/enc inline job!";
185
QObject::connect( mainJob, SIGNAL(finished(KJob*)), q, SLOT(contentJobPreInlineFinished(KJob*)) );
186
} else if( sign || encrypt ) {
187
QObject::connect( mainJob, SIGNAL(finished(KJob*)), q, SLOT(contentJobPreCryptFinished(KJob*)) );
189
QObject::connect( mainJob, SIGNAL(finished(KJob*)), q, SLOT(contentJobFinished(KJob*)) );
191
q->addSubjob( mainJob );
196
void ComposerPrivate::contentJobPreInlineFinished( KJob *job )
198
if ( job->error() ) // taken care of by KCompositeJob
203
Q_ASSERT( format & Kleo::InlineOpenPGPFormat );
204
Q_ASSERT( sign && encrypt ); // for safety... we shouldn't be here otherwise
205
Q_ASSERT( dynamic_cast<ContentJobBase*>( job ) );
206
ContentJobBase *cjob = static_cast<ContentJobBase*>( job );
208
kDebug() << "creaeting inline signandenc job";
209
if( encData.size() == 0 ) { // no key data! bail!
210
q->setErrorText( i18n( "No key data for recipients found." ) );
211
q->setError( Composer::IncompleteError );
217
for( int i = 0; i < encData.size(); ++i ) {
218
QPair<QStringList, std::vector<GpgME::Key> > recipients = encData[ i ];
219
kDebug() << "got first list of recipients:" << recipients.first;
220
SignEncryptJob* seJob = new SignEncryptJob( q );
221
seJob->setContent( cjob->content() );
222
seJob->setCryptoMessageFormat( format );
223
seJob->setEncryptionKeys( recipients.second );
224
seJob->setSigningKeys( signers );
225
seJob->setRecipients( recipients.first );
227
QObject::connect( seJob, SIGNAL( finished( KJob* ) ), q, SLOT( contentJobFinished( KJob* ) ) );
229
q->addSubjob( seJob );
235
void ComposerPrivate::contentJobPreCryptFinished( KJob *job )
237
if ( job->error() ) // taken care of by KCompositeJob
242
// we're signing or encrypting or both, so add an additional job to the process
243
Q_ASSERT( dynamic_cast<ContentJobBase*>( job ) );
244
ContentJobBase *cjob = static_cast<ContentJobBase*>( job );
248
SignJob* sJob = new SignJob( q );
249
sJob->setContent( cjob->content() );
250
sJob->setCryptoMessageFormat( format );
251
sJob->setSigningKeys( signers );
254
QObject::connect( sJob, SIGNAL( finished( KJob* ) ), q, SLOT( signBeforeEncryptJobFinished( KJob* ) ) );
256
QObject::connect( sJob, SIGNAL( finished( KJob* ) ), q, SLOT( contentJobFinished( KJob* ) ) );
258
q->addSubjob( sJob );
261
} else if( encrypt ) {
262
// just encrypting, so setup the jobs directly
263
startEncryptJobs( cjob->content() );
268
void ComposerPrivate::signBeforeEncryptJobFinished( KJob* job )
272
return; // KCompositeJob takes care of the error.
275
Q_ASSERT( dynamic_cast<ContentJobBase*>( job ) );
276
ContentJobBase *cjob = static_cast<ContentJobBase*>( job );
278
// cjob holds the signed content, now we encrypt per recipient
279
startEncryptJobs( cjob->content() );
283
void ComposerPrivate::startEncryptJobs( KMime::Content* content ) {
286
// each SplitInfo holds a list of recipients/keys, if there is more than
287
// one item in it then it means there are secondary recipients that need
288
// different messages w/ clean headers
289
kDebug() << "starting enc jobs";
290
kDebug() << "format:" << format;
291
kDebug() << "enc data:" << encData.size();
293
if( encData.size() == 0 ) { // no key data! bail!
294
q->setErrorText( i18n( "No key data for recipients found." ) );
295
q->setError( Composer::IncompleteError );
300
for( int i = 0; i < encData.size(); ++i ) {
301
QPair<QStringList, std::vector<GpgME::Key> > recipients = encData[ i ];
302
kDebug() << "got first list of recipients:" << recipients.first;
303
EncryptJob* eJob = new EncryptJob( q );
304
eJob->setContent( content );
305
eJob->setCryptoMessageFormat( format );
306
eJob->setEncryptionKeys( recipients.second );
307
eJob->setRecipients( recipients.first );
309
QObject::connect( eJob, SIGNAL( finished( KJob* ) ), q, SLOT( contentJobFinished( KJob* ) ) );
311
q->addSubjob( eJob );
317
void ComposerPrivate::contentJobFinished( KJob *job )
320
return; // KCompositeJob takes care of the error.
322
kDebug() << "composing final message";
324
KMime::Message* headers;
325
KMime::Content* resultContent;
326
std::vector<GpgME::Key> keys;
327
QStringList recipients;
329
Q_ASSERT( dynamic_cast<ContentJobBase*>( job ) == static_cast<ContentJobBase*>( job ) );
330
ContentJobBase* contentJob = static_cast<ContentJobBase*>( job );
332
// create the final headers and body,
333
// taking into account secondary recipients for encryption
334
if( encData.size() > 1 ) { // crypto job with secondary recipients..
335
Q_ASSERT( dynamic_cast<AbstractEncryptJob*>( job ) ); // we need to get the recipients for this job
336
AbstractEncryptJob* eJob = dynamic_cast<AbstractEncryptJob*>( job );
338
keys = eJob->encryptionKeys();
339
recipients = eJob->recipients();
341
resultContent = contentJob->content(); // content() comes from superclass
342
headers = new KMime::Message;
343
headers->setHeader( skeletonMessage->from() );
344
headers->setHeader( skeletonMessage->to() );
345
headers->setHeader( skeletonMessage->cc() );
346
headers->setHeader( skeletonMessage->subject() );
347
headers->setHeader( skeletonMessage->date() );
348
headers->setHeader( skeletonMessage->messageID() );
350
KMime::Headers::Generic *realTo =
351
new KMime::Headers::Generic( "X-KMail-EncBccRecipients",
352
headers, eJob->recipients().join( QLatin1String( "%" ) ), "utf-8" );
354
kDebug() << "got one of multiple messages sending to:" << realTo->asUnicodeString();
355
kDebug() << "sending to recipients:" << recipients;
356
headers->setHeader( realTo );
358
} else { // just use the saved headers from before
359
if( encData.size() > 0 ) {
360
kDebug() << "setting enc data:" << encData[ 0 ].first << "with num keys:" << encData[ 0 ].second.size();
361
keys = encData[ 0 ].second;
362
recipients = encData[ 0 ].first;
365
headers = skeletonMessage;
366
resultContent = contentJob->content();
369
if( lateAttachmentParts.isEmpty() ) {
370
composeFinalStep( headers, resultContent );
372
composeWithLateAttachments( headers, resultContent, lateAttachmentParts, keys, recipients );
377
void ComposerPrivate::composeWithLateAttachments( KMime::Message* headers, KMime::Content* content, AttachmentPart::List parts, std::vector<GpgME::Key> keys, QStringList recipients )
381
MultipartJob* multiJob = new MultipartJob( q );
382
multiJob->setMultipartSubtype( "mixed" );
384
// wrap the content into a job for the multijob to handle it
385
TransparentJob* tJob = new TransparentJob( q );
386
tJob->setContent( content );
387
multiJob->appendSubjob( tJob );
388
multiJob->setExtraContent( headers );
390
kDebug() << "attachment encr key size:" << keys.size() << recipients;
392
// operate correctly on each attachment that has a different crypto policy than body.
393
foreach( AttachmentPart::Ptr attachment, parts ) {
394
AttachmentJob* attachJob = new AttachmentJob( attachment, q );
396
kDebug() << "got a late attachment";
397
if( attachment->isSigned() ) {
398
kDebug() << "adding signjob for late attachment";
399
SignJob* sJob = new SignJob( q );
400
sJob->setContent( 0 );
401
sJob->setCryptoMessageFormat( format );
402
sJob->setSigningKeys( signers );
404
sJob->appendSubjob( attachJob );
405
if( attachment->isEncrypted() ) {
406
kDebug() << "adding sign + encrypt job for late attachment";
407
EncryptJob* eJob = new EncryptJob( q );
408
eJob->setCryptoMessageFormat( format );
409
eJob->setEncryptionKeys( keys );
410
eJob->setRecipients( recipients );
412
eJob->appendSubjob( sJob );
414
multiJob->appendSubjob( eJob );
416
kDebug() << "Just signing late attachment";
417
multiJob->appendSubjob( sJob );
419
} else if( attachment->isEncrypted() ) { // only encryption
420
kDebug() << "just encrypting late attachment";
421
EncryptJob* eJob = new EncryptJob( q );
422
eJob->setCryptoMessageFormat( format );
423
eJob->setEncryptionKeys( keys );
424
eJob->setRecipients( recipients );
426
eJob->appendSubjob( attachJob );
427
multiJob->appendSubjob( eJob );
429
kDebug() << "attaching plain non-crypto attachment";
430
AttachmentJob* attachJob = new AttachmentJob( attachment, q );
431
multiJob->appendSubjob( attachJob );
435
QObject::connect( multiJob, SIGNAL( finished( KJob* ) ), q, SLOT( attachmentsFinished( KJob* ) ) );
437
q->addSubjob( multiJob );
441
void ComposerPrivate::attachmentsFinished( KJob* job )
444
return; // KCompositeJob takes care of the error.
446
kDebug() << "composing final message with late attachments";
448
Q_ASSERT( dynamic_cast<ContentJobBase*>( job ) );
449
ContentJobBase* contentJob = static_cast<ContentJobBase*>( job );
451
KMime::Content* content = contentJob->content();
452
KMime::Content* headers = contentJob->extraContent();
454
composeFinalStep( headers, content );
458
void ComposerPrivate::composeFinalStep( KMime::Content* headers, KMime::Content* content )
462
QByteArray allData = headers->head() + content->encodedContent();
463
KMime::Message::Ptr resultMessage( new KMime::Message );
464
resultMessage->setContent( allData );
465
resultMessage->parse(); // Not strictly necessary.
466
resultMessages.append( resultMessage );
469
Composer::Composer( QObject *parent )
470
: JobBase( *new ComposerPrivate( this ), parent )
476
Composer::~Composer()
480
QList<KMime::Message::Ptr> Composer::resultMessages() const
482
Q_D( const Composer );
483
Q_ASSERT( d->finished );
484
Q_ASSERT( !error() );
485
QList<KMime::Message::Ptr> results = d->resultMessages;
489
GlobalPart *Composer::globalPart() const
491
Q_D( const Composer );
492
return d->globalPart;
495
InfoPart* Composer::infoPart() const
497
Q_D( const Composer );
501
TextPart *Composer::textPart() const
503
Q_D( const Composer );
507
AttachmentPart::List Composer::attachmentParts() const
509
Q_D( const Composer );
510
return d->attachmentParts;
513
void Composer::addAttachmentPart( AttachmentPart::Ptr part )
516
Q_ASSERT( !d->started );
517
Q_ASSERT( !d->attachmentParts.contains( part ) );
518
d->attachmentParts.append( part );
521
void Composer::addAttachmentParts( const AttachmentPart::List &parts )
523
foreach( AttachmentPart::Ptr part, parts ) {
524
addAttachmentPart( part );
528
void Composer::removeAttachmentPart( AttachmentPart::Ptr part )
531
Q_ASSERT( !d->started );
532
if( d->attachmentParts.contains( part ) ) {
533
d->attachmentParts.removeAll( part );
535
kError() << "Unknown attachment part" << part;
541
void Composer::setSignAndEncrypt( const bool doSign, const bool doEncrypt )
545
d->encrypt = doEncrypt;
549
void Composer::setMessageCryptoFormat( Kleo::CryptoMessageFormat format )
556
void Composer::setSigningKeys( std::vector<GpgME::Key>& signers )
560
d->signers = signers;
563
void Composer::setEncryptionKeys( QList<QPair<QStringList, std::vector<GpgME::Key> > > encData )
567
d->encData = encData;
570
void Composer::setNoCrypto(bool noCrypto)
574
d->noCrypto = noCrypto;
577
bool Composer::finished() const
579
Q_D( const Composer );
581
return d->autoSaving;
584
bool Composer::autoSave() const
586
Q_D( const Composer );
588
return d->autoSaving;
591
void Composer::setAutoSave(bool isAutoSave)
595
d->autoSaving = isAutoSave;
598
void Composer::start()
604
void Composer::slotResult( KJob *job )
607
JobBase::slotResult( job );
609
if ( !hasSubjobs() ) {
615
#include "composer.moc"