mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-24 19:02:51 +00:00
295 lines
10 KiB
C++
295 lines
10 KiB
C++
/*
|
|
Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com>
|
|
|
|
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 "maintextjob.h"
|
|
|
|
#include "contentjobbase_p.h"
|
|
#include "part/globalpart.h"
|
|
#include "multipartjob.h"
|
|
#include "singlepartjob.h"
|
|
#include "part/textpart.h"
|
|
#include "util.h"
|
|
|
|
#include <QTextCodec>
|
|
|
|
#include <KCharsets>
|
|
#include <KDebug>
|
|
#include <KGlobal>
|
|
#include <KLocalizedString>
|
|
#include <KMessageBox>
|
|
|
|
#include <kmime/kmime_content.h>
|
|
|
|
#include <kpimtextedit/textedit.h>
|
|
|
|
using namespace MessageComposer;
|
|
|
|
class MessageComposer::MainTextJobPrivate : public ContentJobBasePrivate
|
|
{
|
|
public:
|
|
MainTextJobPrivate( MainTextJob *qq )
|
|
: ContentJobBasePrivate( qq )
|
|
, textPart( 0 )
|
|
{
|
|
}
|
|
|
|
bool chooseSourcePlainText();
|
|
bool chooseCharsetAndEncode();
|
|
bool chooseCharset();
|
|
bool encodeTexts();
|
|
SinglepartJob *createPlainTextJob();
|
|
SinglepartJob *createHtmlJob();
|
|
SinglepartJob *createImageJob( const QSharedPointer<KPIMTextEdit::EmbeddedImage> &image );
|
|
|
|
TextPart *textPart;
|
|
QByteArray chosenCharset;
|
|
QString sourcePlainText;
|
|
QByteArray encodedPlainText;
|
|
QByteArray encodedHtml;
|
|
|
|
Q_DECLARE_PUBLIC( MainTextJob )
|
|
};
|
|
|
|
bool MainTextJobPrivate::chooseSourcePlainText()
|
|
{
|
|
Q_Q( MainTextJob );
|
|
Q_ASSERT( textPart );
|
|
if( textPart->isWordWrappingEnabled() ) {
|
|
sourcePlainText = textPart->wrappedPlainText();
|
|
if( sourcePlainText.isEmpty() &&
|
|
!textPart->cleanPlainText().isEmpty() ) {
|
|
q->setError( JobBase::BugError );
|
|
q->setErrorText( i18n( "Asked to use word wrapping, but not given wrapped plain text." ) );
|
|
return false;
|
|
}
|
|
} else {
|
|
sourcePlainText = textPart->cleanPlainText();
|
|
if( sourcePlainText.isEmpty() &&
|
|
!textPart->wrappedPlainText().isEmpty() ) {
|
|
q->setError( JobBase::BugError );
|
|
q->setErrorText( i18n( "Asked not to use word wrapping, but not given clean plain text." ) );
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MainTextJobPrivate::chooseCharsetAndEncode()
|
|
{
|
|
Q_Q( MainTextJob );
|
|
|
|
const QList<QByteArray> charsets = q->globalPart()->charsets( true );
|
|
if( charsets.isEmpty() ) {
|
|
q->setError( JobBase::BugError );
|
|
q->setErrorText( i18n( "No charsets were available for encoding. Please check your configuration and make sure it contains at least one charset for sending." ) );
|
|
return false;
|
|
}
|
|
|
|
Q_ASSERT( textPart );
|
|
QString toTry = sourcePlainText;
|
|
if( textPart->isHtmlUsed() ) {
|
|
toTry = textPart->cleanHtml();
|
|
}
|
|
chosenCharset = MessageComposer::Util::selectCharset( charsets, toTry );
|
|
if( !chosenCharset.isEmpty() ) {
|
|
// Good, found a charset that encodes the data without loss.
|
|
return encodeTexts();
|
|
} else {
|
|
// No good charset was found.
|
|
if( q->globalPart()->isGuiEnabled() && textPart->warnBadCharset() ) {
|
|
// Warn the user and give them a chance to go back.
|
|
int result = KMessageBox::warningYesNo(
|
|
q->globalPart()->parentWidgetForGui(),
|
|
i18n( "Encoding the message with %1 will lose some characters.\n"
|
|
"Do you want to continue?", QString::fromLatin1( charsets.first() ) ),
|
|
i18n( "Some Characters Will Be Lost" ),
|
|
KGuiItem( i18n("Lose Characters") ),
|
|
KGuiItem( i18n("Change Encoding") )
|
|
);
|
|
if( result == KMessageBox::No ) {
|
|
q->setError( JobBase::UserCancelledError );
|
|
q->setErrorText( i18n( "User decided to change the encoding." ) );
|
|
return false;
|
|
} else {
|
|
chosenCharset = charsets.first();
|
|
return encodeTexts();
|
|
}
|
|
} else if( textPart->warnBadCharset() ) {
|
|
// Should warn user but no Gui available.
|
|
kDebug() << "warnBadCharset but Gui is disabled.";
|
|
q->setError( JobBase::UserError );
|
|
q->setErrorText( i18n( "The selected encoding (%1) cannot fully encode the message.",
|
|
QString::fromLatin1( charsets.first() ) ) );
|
|
return false;
|
|
} else {
|
|
// OK to go ahead with a bad charset.
|
|
chosenCharset = charsets.first();
|
|
return encodeTexts();
|
|
|
|
// FIXME: This is based on the assumption that QTextCodec will replace
|
|
// unknown characters with '?' or some other meaningful thing. The code in
|
|
// QTextCodec indeed uses '?', but this behaviour is not documented.
|
|
}
|
|
}
|
|
|
|
// Should not reach here.
|
|
Q_ASSERT( false );
|
|
return false;
|
|
}
|
|
|
|
bool MainTextJobPrivate::encodeTexts()
|
|
{
|
|
Q_Q( MainTextJob );
|
|
QTextCodec *codec = KGlobal::charsets()->codecForName( QString::fromLatin1( chosenCharset ) );
|
|
if( !codec ) {
|
|
kError() << "Could not get text codec for charset" << chosenCharset;
|
|
q->setError( JobBase::BugError );
|
|
q->setErrorText( i18n( "Could not get text codec for charset \"%1\".", QString::fromLatin1( chosenCharset ) ) );
|
|
return false;
|
|
}
|
|
encodedPlainText = codec->fromUnicode( sourcePlainText );
|
|
if( !textPart->cleanHtml().isEmpty() )
|
|
encodedHtml = codec->fromUnicode( textPart->cleanHtml() );
|
|
kDebug() << "Done.";
|
|
return true;
|
|
}
|
|
|
|
SinglepartJob *MainTextJobPrivate::createPlainTextJob()
|
|
{
|
|
SinglepartJob *cjob = new SinglepartJob; // No parent.
|
|
cjob->contentType()->setMimeType( "text/plain" );
|
|
cjob->contentType()->setCharset( chosenCharset );
|
|
cjob->setData( encodedPlainText );
|
|
// TODO standard recommends Content-ID.
|
|
return cjob;
|
|
}
|
|
|
|
SinglepartJob *MainTextJobPrivate::createHtmlJob()
|
|
{
|
|
SinglepartJob *cjob = new SinglepartJob; // No parent.
|
|
cjob->contentType()->setMimeType( "text/html" );
|
|
cjob->contentType()->setCharset( chosenCharset );
|
|
QByteArray data = KPIMTextEdit::TextEdit::imageNamesToContentIds( encodedHtml,
|
|
textPart->embeddedImages() );
|
|
cjob->setData( data );
|
|
// TODO standard recommends Content-ID.
|
|
return cjob;
|
|
}
|
|
|
|
SinglepartJob *MainTextJobPrivate::createImageJob( const QSharedPointer<KPIMTextEdit::EmbeddedImage> &image )
|
|
{
|
|
Q_Q( MainTextJob );
|
|
|
|
// The image is a PNG encoded with base64.
|
|
SinglepartJob *cjob = new SinglepartJob; // No parent.
|
|
cjob->contentType()->setMimeType( "image/png" );
|
|
const QByteArray charset = MessageComposer::Util::selectCharset( q->globalPart()->charsets( true ), image->imageName );
|
|
Q_ASSERT( !charset.isEmpty() );
|
|
cjob->contentType()->setName( image->imageName, charset );
|
|
cjob->contentTransferEncoding()->setEncoding( KMime::Headers::CEbase64 );
|
|
cjob->contentTransferEncoding()->setDecoded( false ); // It is already encoded.
|
|
cjob->contentID()->setIdentifier( image->contentID.toLatin1() );
|
|
kDebug() << "cid" << cjob->contentID()->identifier();
|
|
cjob->setData( image->image );
|
|
return cjob;
|
|
}
|
|
|
|
MainTextJob::MainTextJob( TextPart *textPart, QObject *parent )
|
|
: ContentJobBase( *new MainTextJobPrivate( this ), parent )
|
|
{
|
|
Q_D( MainTextJob );
|
|
d->textPart = textPart;
|
|
}
|
|
|
|
MainTextJob::~MainTextJob()
|
|
{
|
|
}
|
|
|
|
TextPart *MainTextJob::textPart() const
|
|
{
|
|
Q_D( const MainTextJob );
|
|
return d->textPart;
|
|
}
|
|
|
|
void MainTextJob::setTextPart( TextPart *part )
|
|
{
|
|
Q_D( MainTextJob );
|
|
d->textPart = part;
|
|
}
|
|
|
|
void MainTextJob::doStart()
|
|
{
|
|
Q_D( MainTextJob );
|
|
Q_ASSERT( d->textPart );
|
|
|
|
// Word wrapping.
|
|
if( !d->chooseSourcePlainText() ) {
|
|
// chooseSourcePlainText has set an error.
|
|
Q_ASSERT( error() );
|
|
emitResult();
|
|
return;
|
|
}
|
|
|
|
// Charset.
|
|
if( !d->chooseCharsetAndEncode() ) {
|
|
// chooseCharsetAndEncode has set an error.
|
|
Q_ASSERT( error() );
|
|
emitResult();
|
|
return;
|
|
}
|
|
|
|
// Assemble the Content.
|
|
SinglepartJob *plainJob = d->createPlainTextJob();
|
|
if( d->encodedHtml.isEmpty() ) {
|
|
kDebug() << "Making text/plain";
|
|
// Content is text/plain.
|
|
appendSubjob( plainJob );
|
|
} else {
|
|
MultipartJob *alternativeJob = new MultipartJob;
|
|
alternativeJob->setMultipartSubtype( "alternative" );
|
|
alternativeJob->appendSubjob( plainJob ); // text/plain first.
|
|
alternativeJob->appendSubjob( d->createHtmlJob() ); // text/html second.
|
|
if( !d->textPart->hasEmbeddedImages() ) {
|
|
kDebug() << "Have no images. Making multipart/alternative.";
|
|
// Content is multipart/alternative.
|
|
appendSubjob( alternativeJob );
|
|
} else {
|
|
kDebug() << "Have related images. Making multipart/related.";
|
|
// Content is multipart/related with a multipart/alternative sub-Content.
|
|
MultipartJob *multipartJob = new MultipartJob;
|
|
multipartJob->setMultipartSubtype( "related" );
|
|
multipartJob->appendSubjob( alternativeJob );
|
|
foreach( const QSharedPointer<KPIMTextEdit::EmbeddedImage> &image, d->textPart->embeddedImages() ) {
|
|
multipartJob->appendSubjob( d->createImageJob( image ) );
|
|
}
|
|
appendSubjob( multipartJob );
|
|
}
|
|
}
|
|
ContentJobBase::doStart();
|
|
}
|
|
|
|
void MainTextJob::process()
|
|
{
|
|
Q_D( MainTextJob );
|
|
// The content has been created by our subjob.
|
|
Q_ASSERT( d->subjobContents.count() == 1 );
|
|
d->resultContent = d->subjobContents.first();
|
|
emitResult();
|
|
}
|
|
|