diff --git a/messagecomposer/job/signjob.cpp b/messagecomposer/job/signjob.cpp index 6cfdfbd55c..a3e2fdd201 100644 --- a/messagecomposer/job/signjob.cpp +++ b/messagecomposer/job/signjob.cpp @@ -1,243 +1,256 @@ /* Copyright (C) 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.net Copyright (c) 2009 Leo Franchi This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "signjob.h" #include "contentjobbase_p.h" #include "kleo/cryptobackendfactory.h" #include "kleo/cryptobackend.h" #include "kleo/enum.h" #include "kleo/signjob.h" #include "util.h" #include #include #include #include #include #include #include #include #include #include using namespace MessageComposer; class MessageComposer::SignJobPrivate : public ContentJobBasePrivate { public: SignJobPrivate( SignJob *qq ) : ContentJobBasePrivate( qq ) , content( 0 ) { } KMime::Content* content; std::vector signers; Kleo::CryptoMessageFormat format; // copied from messagecomposer.cpp bool binaryHint( Kleo::CryptoMessageFormat f ) { switch ( f ) { case Kleo::SMIMEFormat: case Kleo::SMIMEOpaqueFormat: return true; default: case Kleo::OpenPGPMIMEFormat: case Kleo::InlineOpenPGPFormat: return false; } } GpgME::SignatureMode signingMode( Kleo::CryptoMessageFormat f ) { switch ( f ) { case Kleo::SMIMEOpaqueFormat: return GpgME::NormalSignatureMode; case Kleo::InlineOpenPGPFormat: return GpgME::Clearsigned; default: case Kleo::SMIMEFormat: case Kleo::OpenPGPMIMEFormat: return GpgME::Detached; } } Q_DECLARE_PUBLIC( SignJob ) }; SignJob::SignJob( QObject *parent ) : ContentJobBase( *new SignJobPrivate( this ), parent ) { } SignJob::~SignJob() { } void SignJob::setContent( KMime::Content* content ) { Q_D( SignJob ); d->content = content; } void SignJob::setCryptoMessageFormat( Kleo::CryptoMessageFormat format) { Q_D( SignJob ); // There *must* be a concrete format set at this point. Q_ASSERT( format == Kleo::OpenPGPMIMEFormat || format == Kleo::InlineOpenPGPFormat || format == Kleo::SMIMEFormat || format == Kleo::SMIMEOpaqueFormat ); d->format = format; } void SignJob::setSigningKeys( std::vector& signers ) { Q_D( SignJob ); d->signers = signers; } KMime::Content* SignJob::origContent() { Q_D( SignJob ); return d->content; } void SignJob::process() { Q_D( SignJob ); Q_ASSERT( d->resultContent == 0 ); // Not processed before. // if setContent hasn't been called, we assume that a subjob was added // and we want to use that if( !d->content ) { Q_ASSERT( d->subjobContents.size() == 1 ); d->content = d->subjobContents.first(); } //d->resultContent = new KMime::Content; const Kleo::CryptoBackend::Protocol *proto = 0; if( d->format & Kleo::AnyOpenPGP ) { proto = Kleo::CryptoBackendFactory::instance()->openpgp(); } else if( d->format & Kleo::AnySMIME ) { proto = Kleo::CryptoBackendFactory::instance()->smime(); } Q_ASSERT( proto ); kDebug() << "creating signJob from:" << proto->name() << proto->displayName(); std::auto_ptr job( proto->signJob( !d->binaryHint( d->format ), d->format == Kleo::InlineOpenPGPFormat ) ); // for now just do the main recipients QByteArray signature; d->content->assemble(); // replace simple LFs by CRLFs for all MIME supporting CryptPlugs // according to RfC 2633, 3.1.1 Canonicalization QByteArray content; if( d->format & Kleo::InlineOpenPGPFormat ) { content = d->content->body(); } else if( !( d->format & Kleo::SMIMEOpaqueFormat ) ) { // replace "From " and "--" at the beginning of lines // with encoded versions according to RfC 3156, 3 // Note: If any line begins with the string "From ", it is strongly // suggested that either the Quoted-Printable or Base64 MIME encoding // be applied. if (d->content->contentTransferEncoding()->encoding() == KMime::Headers::CEquPr || - d->content->contentTransferEncoding()->encoding() == KMime::Headers::CE7Bit ) - { - QByteArray body = d->content->body(); + d->content->contentTransferEncoding()->encoding() == KMime::Headers::CE7Bit ) { + QByteArray body = d->content->encodedBody(); bool changed = false; - QStringList search; - QStringList replacements; + QList search; + QList replacements; + + search << "From " + << "from " + << "-"; + replacements << "From=20" + << "from=20" + << "=2D"; + + if ( d->content->contentTransferEncoding()->encoding() == KMime::Headers::CE7Bit ) { + for ( int i = 0; i < search.size(); ++i ) { + QByteArray start = "\n" % search[i]; + if ( body.indexOf( start ) > -1 || + body.startsWith( search[i] ) ){ + changed = true; + break; + } + } + if ( changed ) { + d->content->contentTransferEncoding()->setEncoding( KMime::Headers::CEquPr ); + d->content->assemble(); + body = d->content->encodedBody(); + } + } - search << QString::fromAscii( "From " ) - << QString::fromAscii( "from " ) - << QString::fromAscii( "-" ); - replacements << QString::fromAscii( "From=20" ) - << QString::fromAscii( "from=20" ) - << QString::fromAscii( "=2D" ); for ( int i = 0; i < search.size(); ++i ) { - QString start = QString::fromAscii( "\n" ) % search[i]; - QString replace = QString::fromAscii( "\n" ) % replacements[i]; - if ( body.indexOf( start.toAscii() ) > -1 ){ + QByteArray start = "\n" % search[i]; + QByteArray replace = "\n" % replacements[i]; + if ( body.indexOf( start ) > -1 ){ changed = true; - body.replace( start.toAscii(), replace.toAscii() ); + body.replace( start, replace ); } - if ( body.startsWith( search[i].toAscii() ) ) - { + if ( body.startsWith( search[i] ) ) { changed = true; - body.replace( 0, search[i].size(), replacements[i].toAscii()); + body.replace( 0, search[i].size(), replacements[i] ); } } if ( changed ) { kDebug() << "Content changed"; - d->content->contentTransferEncoding()->setEncoding( KMime::Headers::CEquPr ); - d->content->assemble(); d->content->setBody( body ); d->content->contentTransferEncoding()->setDecoded( false ); } } content = KMime::LFtoCRLF( d->content->encodedContent() ); } else { // SMimeOpaque doesn't need LFtoCRLF, else it gets munged content = d->content->encodedContent(); } // FIXME: Make this async GpgME::SigningResult res = job->exec( d->signers, content, d->signingMode( d->format ), signature ); // exec'ed jobs don't delete themselves job->deleteLater(); if ( res.error() ) { kDebug() << "signing failed:" << res.error().asString(); // job->showErrorDialog( globalPart()->parentWidgetForGui() ); setError( res.error().code() ); setErrorText( QString::fromLocal8Bit( res.error().asString() ) ); emitResult(); return; } QByteArray signatureHashAlgo = res.createdSignature( 0 ).hashAlgorithmAsString(); d->resultContent = MessageComposer::Util::composeHeadersAndBody( d->content, signature, d->format, true, signatureHashAlgo ); emitResult(); } #include "signjob.moc" diff --git a/messagecomposer/tests/signjobtest.cpp b/messagecomposer/tests/signjobtest.cpp index f4e957a900..9b81de8c60 100644 --- a/messagecomposer/tests/signjobtest.cpp +++ b/messagecomposer/tests/signjobtest.cpp @@ -1,182 +1,182 @@ /* Copyright (C) 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.net Copyright (c) 2009 Leo Franchi This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "signjobtest.h" #include #include #include "qtest_messagecomposer.h" #include "cryptofunctions.h" #include #include #include #include #include #include #include #include #include #include #include QTEST_KDEMAIN( SignJobTest, GUI ) void SignJobTest::initTestCase() { MessageCore::Test::setupEnv(); } void SignJobTest::testContentDirect() { std::vector< GpgME::Key > keys = MessageCore::Test::getKeys(); MessageComposer::Composer *composer = new MessageComposer::Composer; MessageComposer::SignJob* sJob = new MessageComposer::SignJob( composer ); QVERIFY( composer ); QVERIFY( sJob ); QByteArray data( QString::fromLocal8Bit( "one flew over the cuckoo's nest" ).toUtf8() ); KMime::Content* content = new KMime::Content; content->setBody( data ); sJob->setContent( content ); sJob->setCryptoMessageFormat( Kleo::OpenPGPMIMEFormat ); sJob->setSigningKeys( keys ); bool signWorked = checkSignJob( sJob ); QVERIFY( signWorked ); } void SignJobTest::testContentChained() { std::vector< GpgME::Key > keys = MessageCore::Test::getKeys(); QByteArray data( QString::fromLocal8Bit( "one flew over the cuckoo's nest" ).toUtf8() ); KMime::Content* content = new KMime::Content; content->setBody( data ); MessageComposer::TransparentJob* tJob = new MessageComposer::TransparentJob; tJob->setContent( content ); MessageComposer::Composer *composer = new MessageComposer::Composer; MessageComposer::SignJob* sJob = new MessageComposer::SignJob( composer ); sJob->setCryptoMessageFormat( Kleo::OpenPGPMIMEFormat ); sJob->setSigningKeys( keys ); sJob->appendSubjob( tJob ); bool signWorked = checkSignJob( sJob ); QVERIFY( signWorked ); } void SignJobTest::testHeaders() { std::vector< GpgME::Key > keys = MessageCore::Test::getKeys(); MessageComposer::Composer *composer = new MessageComposer::Composer; MessageComposer::SignJob* sJob = new MessageComposer::SignJob( composer ); QVERIFY( composer ); QVERIFY( sJob ); QByteArray data( QString::fromLocal8Bit( "one flew over the cuckoo's nest" ).toUtf8() ); KMime::Content* content = new KMime::Content; content->setBody( data ); sJob->setContent( content ); sJob->setCryptoMessageFormat( Kleo::OpenPGPMIMEFormat ); sJob->setSigningKeys( keys ); VERIFYEXEC( sJob ); QByteArray mimeType( "multipart/signed" ); QByteArray charset( "ISO-8859-1" ); KMime::Content *result = sJob->content(); result->assemble(); kDebug() << result->encodedContent(); QVERIFY( result->contentType( false ) ); QCOMPARE( result->contentType()->mimeType(), mimeType ); QCOMPARE( result->contentType()->charset(), charset ); QCOMPARE( result->contentType()->parameter( QString::fromLocal8Bit( "micalg" ) ), QString::fromLocal8Bit( "pgp-sha1" ) ); QCOMPARE( result->contentType()->parameter( QString::fromLocal8Bit( "protocol" ) ), QString::fromLocal8Bit( "application/pgp-signature" ) ); QCOMPARE( result->contentTransferEncoding()->encoding(), KMime::Headers::CE7Bit ); } void SignJobTest::testRecommentationRFC3156() { std::vector< GpgME::Key > keys = MessageCore::Test::getKeys(); - QString data = QString::fromUtf8( "Magic foo\nFrom test\n\n-- quaak\nOhno"); + QString data = QString::fromUtf8( "=2D Magic foo\nFrom test\n\n-- quaak\nOhno"); KMime::Headers::contentEncoding cte = KMime::Headers::CEquPr; MessageComposer::Composer *composer = new MessageComposer::Composer; MessageComposer::SignJob* sJob = new MessageComposer::SignJob( composer ); QVERIFY( composer ); QVERIFY( sJob ); KMime::Content* content = new KMime::Content; content->setBody( data.toUtf8() ); sJob->setContent( content ); sJob->setCryptoMessageFormat( Kleo::OpenPGPMIMEFormat ); sJob->setSigningKeys( keys ); VERIFYEXEC( sJob ); KMime::Content *result = sJob->content(); result->assemble(); kDebug() << result->encodedContent(); QByteArray body = MessageCore::NodeHelper::firstChild( result )->body(); QCOMPARE( QString::fromUtf8( body ), - QString::fromUtf8( "Magic foo\nFrom=20test\n\n=2D- quaak\nOhno" ) ); + QString::fromUtf8( "=3D2D Magic foo\nFrom=20test\n\n=2D- quaak\nOhno" ) ); ComposerTestUtil::verify( true, false, result, data.toUtf8(), Kleo::OpenPGPMIMEFormat, cte ); } bool SignJobTest::checkSignJob( MessageComposer::SignJob* sJob ) { sJob->exec(); KMime::Content* result = sJob->content(); Q_ASSERT( result ); result->assemble(); return ComposerTestUtil::verifySignature( result, QString::fromLocal8Bit( "one flew over the cuckoo's nest" ).toUtf8(), Kleo::OpenPGPMIMEFormat, KMime::Headers::CE7Bit ); } #include "signjobtest.moc"