You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1344 lines
44 KiB

/* -*- mode: C++; c-file-style: "gnu" -*-
* kmail: KDE mail client
* Copyright (C) 2006 Dmitry Morozhnikov <dmiceman@mail.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "templateparser.h"
#include "templatesconfiguration.h"
#include "templatesconfiguration_kfg.h"
#include "customtemplates_kfg.h"
#include "globalsettings_base.h"
#include "kmkernel.h"
#include "attachmentcollector.h"
#include "stringutil.h"
#include "messageviewer/stringutil.h"
#include "messageviewer/objecttreeparser.h"
#include "messageviewer/objecttreeemptysource.h"
#include "messageviewer/nodehelper.h"
#include "messagehelper.h"
#include <akonadi/collection.h>
#include <libkpgp/kpgpblock.h>
#include <kmime/kmime_message.h>
#include <kmime/kmime_content.h>
#include <kpimidentities/identity.h>
#include <kpimidentities/identitymanager.h>
#include <klocale.h>
#include <kcalendarsystem.h>
#include <kglobal.h>
#include <kprocess.h>
#include <kmessagebox.h>
#include <kshell.h>
#include <QString>
#include <QDateTime>
#include <QRegExp>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QWebElement>
namespace KMail {
static const int PipeTimeout = 15 * 1000;
TemplateParser::TemplateParser( const KMime::Message::Ptr &amsg, const Mode amode,
const QString &aselection,
bool asmartQuote, bool aallowDecryption,
bool aselectionIsBody ) :
mMode( amode ), mFolder( 0 ), mIdentity( 0 ), mSelection( aselection ),
mSmartQuote( asmartQuote ),
mAllowDecryption( aallowDecryption ), mSelectionIsBody( aselectionIsBody ),
mDebug( false ), mQuoteString( "> " ), mAppend( false ), mOrigRoot( 0 )
{
mMsg = amsg;
}
TemplateParser::~TemplateParser()
{
delete mOrigRoot;
mOrigRoot = 0;
}
int TemplateParser::parseQuotes( const QString &prefix, const QString &str,
QString &quote ) const
{
int pos = prefix.length();
int len;
int str_len = str.length();
// Also allow the german lower double-quote sign as quote separator, not only
// the standard ASCII quote ("). This fixes bug 166728.
QList< QChar > quoteChars;
quoteChars.append( '"' );
quoteChars.append( 0x201C );
QChar prev( QChar::Null );
pos++;
len = pos;
while ( pos < str_len ) {
QChar c = str[pos];
pos++;
len++;
if ( !prev.isNull() ) {
quote.append( c );
prev = QChar::Null;
} else {
if ( c == '\\' ) {
prev = c;
} else if ( quoteChars.contains( c ) ) {
break;
} else {
quote.append( c );
}
}
}
return len;
}
QString TemplateParser::getFName( const QString &str )
{
// simple logic:
// if there is ',' in name, than format is 'Last, First'
// else format is 'First Last'
// last resort -- return 'name' from 'name@domain'
int sep_pos;
QString res;
if ( ( sep_pos = str.indexOf( '@' ) ) > 0 ) {
int i;
for ( i = (sep_pos - 1); i >= 0; --i ) {
QChar c = str[i];
if ( c.isLetterOrNumber() ) {
res.prepend( c );
} else {
break;
}
}
} else if ( ( sep_pos = str.indexOf(',') ) > 0 ) {
int i;
bool begin = false;
for ( i = sep_pos; i < str.length(); ++i ) {
QChar c = str[i];
if ( c.isLetterOrNumber() ) {
begin = true;
res.append( c );
} else if ( begin ) {
break;
}
}
} else {
int i;
for ( i = 0; i < str.length(); ++i ) {
QChar c = str[i];
if ( c.isLetterOrNumber() ) {
res.append( c );
} else {
break;
}
}
}
return res;
}
QString TemplateParser::getLName( const QString &str )
{
// simple logic:
// if there is ',' in name, than format is 'Last, First'
// else format is 'First Last'
int sep_pos;
QString res;
if ( ( sep_pos = str.indexOf(',') ) > 0 ) {
int i;
for ( i = sep_pos; i >= 0; --i ) {
QChar c = str[i];
if ( c.isLetterOrNumber() ) {
res.prepend( c );
} else {
break;
}
}
} else {
if ( ( sep_pos = str.indexOf( ' ' ) ) > 0 ) {
int i;
bool begin = false;
for ( i = sep_pos; i < str.length(); ++i ) {
QChar c = str[i];
if ( c.isLetterOrNumber() ) {
begin = true;
res.append( c );
} else if ( begin ) {
break;
}
}
}
}
return res;
}
void TemplateParser::process( const KMime::Message::Ptr &aorig_msg, const Akonadi::Collection & afolder, bool append )
{
if( aorig_msg == 0 ) {
kDebug() << "aorig_msg == 0!";
return;
}
mAppend = append;
mOrigMsg = aorig_msg;
mFolder = afolder;
QString tmpl = findTemplate();
if( tmpl.isEmpty()) return;
return processWithTemplate( tmpl );
}
void TemplateParser::process( const QString &tmplName, const KMime::Message::Ptr &aorig_msg,
const Akonadi::Collection &afolder, bool append )
{
mAppend = append;
mOrigMsg = aorig_msg;
mFolder = afolder;
QString tmpl = findCustomTemplate( tmplName );
return processWithTemplate( tmpl );
}
void TemplateParser::processWithIdentity( uint uoid, const KMime::Message::Ptr &aorig_msg,
const Akonadi::Collection &afolder, bool append )
{
mIdentity = uoid;
return process( aorig_msg, afolder, append );
}
void TemplateParser::processWithTemplate( const QString &tmpl )
{
QString body;
int tmpl_len = tmpl.length();
bool dnl = false;
for ( int i = 0; i < tmpl_len; ++i ) {
QChar c = tmpl[i];
// kDebug() << "Next char: " << c;
if ( c == '%' ) {
QString cmd = tmpl.mid( i + 1 );
if ( cmd.startsWith( '-' ) ) {
// dnl
kDebug() << "Command: -";
dnl = true;
i += 1;
} else if ( cmd.startsWith( QLatin1String("REM=") ) ) {
// comments
kDebug() << "Command: REM=";
QString q;
int len = parseQuotes( "REM=", cmd, q );
i += len;
} else if ( cmd.startsWith( QLatin1String("INSERT=") ) ) {
// insert content of specified file as is
kDebug() << "Command: INSERT=";
QString q;
int len = parseQuotes( "INSERT=", cmd, q );
i += len;
QString path = KShell::tildeExpand( q );
QFileInfo finfo( path );
if (finfo.isRelative() ) {
path = QDir::homePath();
path += '/';
path += q;
}
QFile file( path );
if ( file.open( QIODevice::ReadOnly ) ) {
QByteArray content = file.readAll();
QString str = QString::fromLocal8Bit( content, content.size() );
body.append( str );
} else if ( mDebug ) {
KMessageBox::error( 0,
i18nc( "@info:status", "Cannot insert content from file %1: %2",
path, file.errorString() ) );
}
} else if ( cmd.startsWith( QLatin1String("SYSTEM=") ) ) {
// insert content of specified file as is
kDebug() << "Command: SYSTEM=";
QString q;
int len = parseQuotes( "SYSTEM=", cmd, q );
i += len;
QString pipe_cmd = q;
QString str = pipe( pipe_cmd, "" );
body.append( str );
} else if ( cmd.startsWith( QLatin1String("PUT=") ) ) {
// insert content of specified file as is
kDebug() << "Command: PUT=";
QString q;
int len = parseQuotes( "PUT=", cmd, q );
i += len;
QString path = KShell::tildeExpand( q );
QFileInfo finfo( path );
if (finfo.isRelative() ) {
path = QDir::homePath();
path += '/';
path += q;
}
QFile file( path );
if ( file.open( QIODevice::ReadOnly ) ) {
QByteArray content = file.readAll();
body.append( QString::fromLocal8Bit( content, content.size() ) );
} else if ( mDebug ) {
KMessageBox::error( 0,
i18nc( "@info:status", "Cannot insert content from file %1: %2",
path, file.errorString() ));
}
} else if ( cmd.startsWith( QLatin1String("QUOTEPIPE=") ) ) {
// pipe message body through command and insert it as quotation
kDebug() << "Command: QUOTEPIPE=";
QString q;
int len = parseQuotes( "QUOTEPIPE=", cmd, q );
i += len;
QString pipe_cmd = q;
if ( mOrigMsg ) {
QString str =
pipe( pipe_cmd, messageText( false ) );
QString quote = asQuotedString( mOrigMsg, mQuoteString, str,
mSmartQuote, mAllowDecryption );
if ( quote.endsWith( '\n' ) )
quote.chop( 1 );
body.append( quote );
}
} else if ( cmd.startsWith( QLatin1String("QUOTE") ) ) {
kDebug() << "Command: QUOTE";
i += strlen( "QUOTE" );
if ( mOrigMsg ) {
QString quote = asQuotedString( mOrigMsg, mQuoteString, messageText( true ),
mSmartQuote, mAllowDecryption );
if ( quote.endsWith( '\n' ) )
quote.chop( 1 );
body.append( quote );
}
} else if ( cmd.startsWith( QLatin1String("QHEADERS") ) ) {
kDebug() << "Command: QHEADERS";
i += strlen( "QHEADERS" );
if ( mOrigMsg ) {
QString quote = asQuotedString( mOrigMsg, mQuoteString,
MessageHelper::headerAsSendableString( mOrigMsg ),
mSmartQuote, false );
if ( quote.endsWith( '\n' ) )
quote.chop( 1 );
body.append( quote );
}
} else if ( cmd.startsWith( QLatin1String("HEADERS") ) ) {
kDebug() << "Command: HEADERS";
i += strlen( "HEADERS" );
if ( mOrigMsg ) {
QString str = MessageHelper::headerAsSendableString( mOrigMsg );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("TEXTPIPE=") ) ) {
// pipe message body through command and insert it as is
kDebug() << "Command: TEXTPIPE=";
QString q;
int len = parseQuotes( "TEXTPIPE=", cmd, q );
i += len;
QString pipe_cmd = q;
if ( mOrigMsg ) {
QString str = pipe(pipe_cmd, messageText( false ) );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("MSGPIPE=") ) ) {
// pipe full message through command and insert result as is
kDebug() << "Command: MSGPIPE=";
QString q;
int len = parseQuotes( "MSGPIPE=", cmd, q );
i += len;
QString pipe_cmd = q;
if ( mOrigMsg ) {
QString str = pipe(pipe_cmd, mOrigMsg->encodedContent() );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("BODYPIPE=") ) ) {
// pipe message body generated so far through command and insert result as is
kDebug() << "Command: BODYPIPE=";
QString q;
int len = parseQuotes( "BODYPIPE=", cmd, q );
i += len;
QString pipe_cmd = q;
QString str = pipe( pipe_cmd, body );
body.append( str );
} else if ( cmd.startsWith( QLatin1String("CLEARPIPE=") ) ) {
// pipe message body generated so far through command and
// insert result as is replacing current body
kDebug() << "Command: CLEARPIPE=";
QString q;
int len = parseQuotes( "CLEARPIPE=", cmd, q );
i += len;
QString pipe_cmd = q;
QString str = pipe( pipe_cmd, body );
body = str;
KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-CursorPos", mMsg.get(), QString::number( 0 ), "utf-8" );
mMsg->setHeader( header );
} else if ( cmd.startsWith( QLatin1String("TEXT") ) ) {
kDebug() << "Command: TEXT";
i += strlen( "TEXT" );
if ( mOrigMsg ) {
QString quote = messageText( false );
body.append( quote );
}
} else if ( cmd.startsWith( QLatin1String("OTEXTSIZE") ) ) {
kDebug() << "Command: OTEXTSIZE";
i += strlen( "OTEXTSIZE" );
if ( mOrigMsg ) {
QString str = QString( "%1" ).arg( mOrigMsg->body().length() );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OTEXT") ) ) {
kDebug() << "Command: OTEXT";
i += strlen( "OTEXT" );
if ( mOrigMsg ) {
QString quote = messageText( false );
body.append( quote );
}
} else if ( cmd.startsWith( QLatin1String("OADDRESSEESADDR") ) ) {
kDebug() << "Command: OADDRESSEESADDR";
i += strlen( "OADDRESSEESADDR" );
const QString to = mOrigMsg->to()->asUnicodeString();
const QString cc = mOrigMsg->cc()->asUnicodeString();
if ( !to.isEmpty() )
body.append( i18n( "To:" ) + QLatin1Char( ' ' ) + to );
if ( !to.isEmpty() && !cc.isEmpty() )
body.append( QLatin1Char( '\n' ) );
if ( !cc.isEmpty() )
body.append( i18n( "CC:" ) + QLatin1Char( ' ' ) + cc );
} else if ( cmd.startsWith( QLatin1String("CCADDR") ) ) {
kDebug() << "Command: CCADDR";
i += strlen( "CCADDR" );
QString str = mMsg->cc()->asUnicodeString();
body.append( str );
} else if ( cmd.startsWith( QLatin1String("CCNAME") ) ) {
kDebug() << "Command: CCNAME";
i += strlen( "CCNAME" );
QString str = MessageHelper::ccStrip( mMsg );
body.append( str );
} else if ( cmd.startsWith( QLatin1String("CCFNAME") ) ) {
kDebug() << "Command: CCFNAME";
i += strlen( "CCFNAME" );
QString str = MessageHelper::ccStrip( mMsg );
body.append( getFName( str ) );
} else if ( cmd.startsWith( QLatin1String("CCLNAME") ) ) {
kDebug() << "Command: CCLNAME";
i += strlen( "CCLNAME" );
QString str = MessageHelper::ccStrip( mMsg );
body.append( getLName( str ) );
} else if ( cmd.startsWith( QLatin1String("TOADDR") ) ) {
kDebug() << "Command: TOADDR";
i += strlen( "TOADDR" );
QString str = mMsg->to()->asUnicodeString();
body.append( str );
} else if ( cmd.startsWith( QLatin1String("TONAME") ) ) {
kDebug() << "Command: TONAME";
i += strlen( "TONAME" );
QString str = MessageHelper::toStrip( mMsg );
body.append( str );
} else if ( cmd.startsWith( QLatin1String("TOFNAME") ) ) {
kDebug() << "Command: TOFNAME";
i += strlen( "TOFNAME" );
QString str = MessageHelper::toStrip( mMsg );
body.append( getFName( str ) );
} else if ( cmd.startsWith( QLatin1String("TOLNAME") ) ) {
kDebug() << "Command: TOLNAME";
i += strlen( "TOLNAME" );
QString str = MessageHelper::toStrip( mMsg );
body.append( getLName( str ) );
} else if ( cmd.startsWith( QLatin1String("TOLIST") ) ) {
kDebug() << "Command: TOLIST";
i += strlen( "TOLIST" );
QString str = mMsg->to()->asUnicodeString();
body.append( str );
} else if ( cmd.startsWith( QLatin1String("FROMADDR") ) ) {
kDebug() << "Command: FROMADDR";
i += strlen( "FROMADDR" );
QString str = mMsg->from()->asUnicodeString();
body.append( str );
} else if ( cmd.startsWith( QLatin1String("FROMNAME") ) ) {
kDebug() << "Command: FROMNAME";
i += strlen( "FROMNAME" );
QString str = MessageHelper::fromStrip( mMsg );
body.append( str );
} else if ( cmd.startsWith( QLatin1String("FROMFNAME") ) ) {
kDebug() << "Command: FROMFNAME";
i += strlen( "FROMFNAME" );
QString str = MessageHelper::fromStrip( mMsg );
body.append( getFName( str ) );
} else if ( cmd.startsWith( QLatin1String("FROMLNAME") ) ) {
kDebug() << "Command: FROMLNAME";
i += strlen( "FROMLNAME" );
QString str = MessageHelper::fromStrip( mMsg );
body.append( getLName( str ) );
} else if ( cmd.startsWith( QLatin1String("FULLSUBJECT") ) ) {
kDebug() << "Command: FULLSUBJECT";
i += strlen( "FULLSUBJECT" );
QString str = mMsg->subject()->asUnicodeString();
body.append( str );
} else if ( cmd.startsWith( QLatin1String("FULLSUBJ") ) ) {
kDebug() << "Command: FULLSUBJ";
i += strlen( "FULLSUBJ" );
QString str = mMsg->subject()->asUnicodeString();
body.append( str );
} else if ( cmd.startsWith( QLatin1String("MSGID") ) ) {
kDebug() << "Command: MSGID";
i += strlen( "MSGID" );
QString str = mMsg->messageID()->asUnicodeString();
body.append( str );
} else if ( cmd.startsWith( QLatin1String("OHEADER=") ) ) {
// insert specified content of header from original message
kDebug() << "Command: OHEADER=";
QString q;
int len = parseQuotes( "OHEADER=", cmd, q );
i += len;
if ( mOrigMsg ) {
QString hdr = q;
QString str = mOrigMsg->headerByType(hdr.toLocal8Bit() ) ? mOrigMsg->headerByType(hdr.toLocal8Bit() )->asUnicodeString() : "";
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("HEADER=") ) ) {
// insert specified content of header from current message
kDebug() << "Command: HEADER=";
QString q;
int len = parseQuotes( "HEADER=", cmd, q );
i += len;
QString hdr = q;
QString str = mMsg->headerByType(hdr.toLocal8Bit() ) ? mMsg->headerByType(hdr.toLocal8Bit() )->asUnicodeString() : "";
body.append( str );
} else if ( cmd.startsWith( QLatin1String("HEADER( ") ) ) {
// insert specified content of header from current message
kDebug() << "Command: HEADER(";
QRegExp re = QRegExp( "^HEADER\\((.+)\\)" );
re.setMinimal( true );
int res = re.indexIn( cmd );
if ( res != 0 ) {
// something wrong
i += strlen( "HEADER( " );
} else {
i += re.matchedLength();
QString hdr = re.cap( 1 );
QString str = mMsg->headerByType( hdr.toLocal8Bit() ) ? mMsg->headerByType( hdr.toLocal8Bit() )->asUnicodeString() : "";
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OCCADDR") ) ) {
kDebug() << "Command: OCCADDR";
i += strlen( "OCCADDR" );
if ( mOrigMsg ) {
QString str = mOrigMsg->cc()->asUnicodeString();
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OCCNAME") ) ) {
kDebug() << "Command: OCCNAME";
i += strlen( "OCCNAME" );
if ( mOrigMsg ) {
QString str = MessageHelper::ccStrip( mOrigMsg );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OCCFNAME") ) ) {
kDebug() << "Command: OCCFNAME";
i += strlen( "OCCFNAME" );
if ( mOrigMsg ) {
QString str = MessageHelper::ccStrip( mOrigMsg );
body.append( getFName( str ) );
}
} else if ( cmd.startsWith( QLatin1String("OCCLNAME") ) ) {
kDebug() << "Command: OCCLNAME";
i += strlen( "OCCLNAME" );
if ( mOrigMsg ) {
QString str = MessageHelper::ccStrip( mOrigMsg );
body.append( getLName( str ) );
}
} else if ( cmd.startsWith( QLatin1String("OTOADDR") ) ) {
kDebug() << "Command: OTOADDR";
i += strlen( "OTOADDR" );
if ( mOrigMsg ) {
QString str = mOrigMsg->to()->asUnicodeString();
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OTONAME") ) ) {
kDebug() << "Command: OTONAME";
i += strlen( "OTONAME" );
if ( mOrigMsg ) {
QString str = MessageHelper::toStrip( mOrigMsg );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OTOFNAME") ) ) {
kDebug() << "Command: OTOFNAME";
i += strlen( "OTOFNAME" );
if ( mOrigMsg ) {
QString str = MessageHelper::toStrip( mOrigMsg );
body.append( getFName( str ) );
}
} else if ( cmd.startsWith( QLatin1String("OTOLNAME") ) ) {
kDebug() << "Command: OTOLNAME";
i += strlen( "OTOLNAME" );
if ( mOrigMsg ) {
QString str = MessageHelper::toStrip( mOrigMsg );
body.append( getLName( str ) );
}
} else if ( cmd.startsWith( QLatin1String("OTOLIST") ) ) {
kDebug() << "Command: OTOLIST";
i += strlen( "OTOLIST" );
if ( mOrigMsg ) {
QString str = mOrigMsg->to()->asUnicodeString();
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OTO") ) ) {
kDebug() << "Command: OTO";
i += strlen( "OTO" );
if ( mOrigMsg ) {
QString str = mOrigMsg->to()->asUnicodeString();
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OFROMADDR") ) ) {
kDebug() << "Command: OFROMADDR";
i += strlen( "OFROMADDR" );
if ( mOrigMsg ) {
QString str = mOrigMsg->from()->asUnicodeString();
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OFROMNAME") ) ) {
kDebug() << "Command: OFROMNAME";
i += strlen( "OFROMNAME" );
if ( mOrigMsg ) {
QString str = MessageHelper::fromStrip( mOrigMsg );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OFROMFNAME") ) ) {
kDebug() << "Command: OFROMFNAME";
i += strlen( "OFROMFNAME" );
if ( mOrigMsg ) {
QString str = MessageHelper::fromStrip( mOrigMsg );
body.append( getFName( str ) );
}
} else if ( cmd.startsWith( QLatin1String("OFROMLNAME") ) ) {
kDebug() << "Command: OFROMLNAME";
i += strlen( "OFROMLNAME" );
if ( mOrigMsg ) {
QString str = MessageHelper::fromStrip( mOrigMsg );
body.append( getLName( str ) );
}
} else if ( cmd.startsWith( QLatin1String("OFULLSUBJECT") ) ) {
kDebug() << "Command: OFULLSUBJECT";
i += strlen( "OFULLSUBJECT" );
if ( mOrigMsg ) {
QString str = mOrigMsg->subject()->asUnicodeString();
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OFULLSUBJ") ) ) {
kDebug() << "Command: OFULLSUBJ";
i += strlen( "OFULLSUBJ" );
if ( mOrigMsg ) {
QString str = mOrigMsg->subject()->asUnicodeString();
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OMSGID") ) ) {
kDebug() << "Command: OMSGID";
i += strlen( "OMSGID" );
if ( mOrigMsg ) {
QString str = mOrigMsg->messageID()->asUnicodeString();
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("DATEEN") ) ) {
kDebug() << "Command: DATEEN";
i += strlen( "DATEEN" );
QDateTime date = QDateTime::currentDateTime();
KLocale locale( "C" );
QString str = locale.formatDate( date.date(), KLocale::LongDate );
body.append( str );
} else if ( cmd.startsWith( QLatin1String("DATESHORT") ) ) {
kDebug() << "Command: DATESHORT";
i += strlen( "DATESHORT" );
QDateTime date = QDateTime::currentDateTime();
QString str = KGlobal::locale()->formatDate( date.date(), KLocale::ShortDate );
body.append( str );
} else if ( cmd.startsWith( QLatin1String("DATE") ) ) {
kDebug() << "Command: DATE";
i += strlen( "DATE" );
QDateTime date = QDateTime::currentDateTime();
QString str = KGlobal::locale()->formatDate( date.date(), KLocale::LongDate );
body.append( str );
} else if ( cmd.startsWith( QLatin1String("DOW") ) ) {
kDebug() << "Command: DOW";
i += strlen( "DOW" );
QDateTime date = QDateTime::currentDateTime();
QString str = KGlobal::locale()->calendar()->weekDayName( date.date(),
KCalendarSystem::LongDayName );
body.append( str );
} else if ( cmd.startsWith( QLatin1String("TIMELONGEN") ) ) {
kDebug() << "Command: TIMELONGEN";
i += strlen( "TIMELONGEN" );
QDateTime date = QDateTime::currentDateTime();
KLocale locale( "C");
QString str = locale.formatTime( date.time(), true );
body.append( str );
} else if ( cmd.startsWith( QLatin1String("TIMELONG") ) ) {
kDebug() << "Command: TIMELONG";
i += strlen( "TIMELONG" );
QDateTime date = QDateTime::currentDateTime();
QString str = KGlobal::locale()->formatTime( date.time(), true );
body.append( str );
} else if ( cmd.startsWith( QLatin1String("TIME") ) ) {
kDebug() << "Command: TIME";
i += strlen( "TIME" );
QDateTime date = QDateTime::currentDateTime();
QString str = KGlobal::locale()->formatTime( date.time(), false );
body.append( str );
} else if ( cmd.startsWith( QLatin1String("ODATEEN") ) ) {
kDebug() << "Command: ODATEEN";
i += strlen( "ODATEEN" );
if ( mOrigMsg ) {
QDateTime date = mOrigMsg->date()->dateTime().dateTime();
KLocale locale( "C");
QString str = locale.formatDate( date.date(), KLocale::LongDate );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("ODATESHORT")) ) {
kDebug() << "Command: ODATESHORT";
i += strlen( "ODATESHORT");
if ( mOrigMsg ) {
QDateTime date = mOrigMsg->date()->dateTime().dateTime();
QString str = KGlobal::locale()->formatDate( date.date(), KLocale::ShortDate );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("ODATE")) ) {
kDebug() << "Command: ODATE";
i += strlen( "ODATE");
if ( mOrigMsg ) {
QDateTime date = mOrigMsg->date()->dateTime().dateTime();
QString str = KGlobal::locale()->formatDate( date.date(), KLocale::LongDate );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("ODOW")) ) {
kDebug() << "Command: ODOW";
i += strlen( "ODOW");
if ( mOrigMsg ) {
QDateTime date = mOrigMsg->date()->dateTime().dateTime();
QString str = KGlobal::locale()->calendar()->weekDayName( date.date(),
KCalendarSystem::LongDayName );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OTIMELONGEN")) ) {
kDebug() << "Command: OTIMELONGEN";
i += strlen( "OTIMELONGEN");
if ( mOrigMsg ) {
QDateTime date = mOrigMsg->date()->dateTime().dateTime();
KLocale locale( "C");
QString str = locale.formatTime( date.time(), true );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OTIMELONG")) ) {
kDebug() << "Command: OTIMELONG";
i += strlen( "OTIMELONG");
if ( mOrigMsg ) {
QDateTime date = mOrigMsg->date()->dateTime().dateTime();
QString str = KGlobal::locale()->formatTime( date.time(), true );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("OTIME")) ) {
kDebug() << "Command: OTIME";
i += strlen( "OTIME");
if ( mOrigMsg ) {
QDateTime date = mOrigMsg->date()->dateTime().dateTime();
QString str = KGlobal::locale()->formatTime( date.time(), false );
body.append( str );
}
} else if ( cmd.startsWith( QLatin1String("BLANK") ) ) {
// do nothing
kDebug() << "Command: BLANK";
i += strlen( "BLANK" );
} else if ( cmd.startsWith( QLatin1String("NOP") ) ) {
// do nothing
kDebug() << "Command: NOP";
i += strlen( "NOP" );
} else if ( cmd.startsWith( QLatin1String("CLEAR") ) ) {
// clear body buffer; not too useful yet
kDebug() << "Command: CLEAR";
i += strlen( "CLEAR" );
body = "";
KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-CursorPos", mMsg.get(), QString::number( 0 ), "utf-8" );
mMsg->setHeader( header );
} else if ( cmd.startsWith( QLatin1String("DEBUGOFF") ) ) {
// turn off debug
kDebug() << "Command: DEBUGOFF";
i += strlen( "DEBUGOFF" );
mDebug = false;
} else if ( cmd.startsWith( QLatin1String("DEBUG") ) ) {
// turn on debug
kDebug() << "Command: DEBUG";
i += strlen( "DEBUG" );
mDebug = true;
} else if ( cmd.startsWith( QLatin1String("CURSOR") ) ) {
// turn on debug
kDebug() << "Command: CURSOR";
i += strlen( "CURSOR" );
KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-CursorPos", mMsg.get(), QString::number( body.length()-1 ), "utf-8" );
mMsg->setHeader( header );
} else if ( cmd.startsWith( QLatin1String( "SIGNATURE" ) ) ) {
kDebug() << "Command: SIGNATURE";
i += strlen( "SIGNATURE" );
body.append( getSignature() );
} else {
// wrong command, do nothing
body.append( c );
}
} else if ( dnl && ( c == '\n' || c == '\r') ) {
// skip
if ( ( tmpl.size() > i+1 ) &&
( ( c == '\n' && tmpl[i + 1] == '\r' ) ||
( c == '\r' && tmpl[i + 1] == '\n' ) ) ) {
// skip one more
i += 1;
}
dnl = false;
} else {
body.append( c );
}
}
addProcessedBodyToMessage( body );
}
QString TemplateParser::getSignature() const
{
const KPIMIdentities::Identity &identity =
kmkernel->identityManager()->identityForUoid( mIdentity );
if ( identity.isNull() )
return QString();
KPIMIdentities::Signature signature = const_cast<KPIMIdentities::Identity&>
( identity ).signature();
if ( signature.type() == KPIMIdentities::Signature::Inlined &&
signature.isInlinedHtml() ) {
// templates don't support HTML; convert to plain text
return signature.toPlainText();
}
else {
return signature.rawText();
}
}
QString TemplateParser::messageText( bool allowSelectionOnly )
{
if ( !mSelection.isEmpty() && allowSelectionOnly )
return mSelection;
// FIXME
// No selection text, therefore we need to parse the object tree ourselves to get
//KMime::Content *root = parsedObjectTree();
// ### temporary hack to uncrash reply/forward
mOrigRoot = new KMime::Content;
mOrigRoot->setContent( mOrigMsg->encodedContent() );
mOrigRoot->parse();
MessageViewer::EmptySource emptySource;
MessageViewer::ObjectTreeParser otp(&emptySource); // all defaults are ok
otp.setAllowAsync( false );
otp.parseObjectTree( Akonadi::Item(), mOrigRoot );
return asPlainTextFromObjectTree( mOrigMsg, mOrigRoot, &otp, mSmartQuote, mAllowDecryption );
}
KMime::Content* TemplateParser::parsedObjectTree()
{
if ( mOrigRoot )
return mOrigRoot;
mOrigRoot = new KMime::Content;
mOrigRoot->setContent( mMsg->encodedContent() );
MessageViewer::EmptySource emptySource;
MessageViewer::ObjectTreeParser otp(&emptySource); // all defaults are ok
otp.parseObjectTree( Akonadi::Item(), mOrigRoot );
return mOrigRoot;
}
void TemplateParser::addProcessedBodyToMessage( const QString &body )
{
if ( mAppend ) {
// ### What happens here if the body is multipart or in some way encoded?
QByteArray msg_body = mMsg->body();
msg_body.append( body.toUtf8() );
mMsg->setBody( msg_body );
}
else {
// Get the attachments of the original mail
KMime::Content *root = parsedObjectTree();
KMail::AttachmentCollector ac;
ac.collectAttachmentsFrom( root );
// Now, delete the old content and set the new content, which
// is either only the new text or the new text with some attachments.
KMime::Content::List parts = mMsg->contents();
foreach ( KMime::Content *content, parts )
mMsg->removeContent( content, true /*delete*/ );
// Set To and CC from the template
if ( !mTo.isEmpty() ) {
mMsg->to()->fromUnicodeString( mMsg->to()->asUnicodeString() + ',' + mTo, "utf-8" );
}
if ( !mCC.isEmpty() )
mMsg->cc()->fromUnicodeString( mMsg->cc()->asUnicodeString() + ',' + mCC, "utf-8" );
// If we have no attachment, simply create a text/plain part and
// set the processed template text as the body
if ( ac.attachments().empty() || mMode != Forward ) {
mMsg->contentType()->clear(); // to get rid of old boundary
mMsg->contentType()->setMimeType( "text/plain" );
mMsg->setBody( body.toUtf8() );
mMsg->assemble();
}
// If we have some attachments, create a multipart/mixed mail and
// add the normal body as well as the attachments
else
{
const QByteArray boundary = KMime::multiPartBoundary();
mMsg->contentType()->setMimeType( "multipart/mixed" );
mMsg->contentType()->setBoundary( boundary );
KMime::Content *textPart = new KMime::Content( mMsg.get() );
textPart->contentType()->setMimeType( "text/plain" );
textPart->fromUnicodeString( body );
mMsg->addContent( textPart );
int attachmentNumber = 1;
foreach( KMime::Content *attachment, ac.attachments() ) {
mMsg->addContent( attachment );
// If the content type has no name or filename parameter, add one, since otherwise the name
// would be empty in the attachment view of the composer, which looks confusing
if ( attachment->contentType( false ) ) {
if ( !attachment->contentType()->hasParameter( "name" ) &&
!attachment->contentType()->hasParameter( "filename" ) ) {
attachment->contentType()->setParameter( "name", i18n( "Attachment %1", attachmentNumber ) );
}
}
attachmentNumber++;
}
mMsg->assemble();
}
}
}
QString TemplateParser::findCustomTemplate( const QString &tmplName )
{
CTemplates t( tmplName );
mTo = t.to();
mCC = t.cC();
QString content = t.content();
if ( !content.isEmpty() ) {
return content;
} else {
return findTemplate();
}
}
QString TemplateParser::findTemplate()
{
// kDebug() << "Trying to find template for mode" << mode;
QString tmpl;
#if 0
if ( !mFolder.isValid() ) { // find folder message belongs to
mFolder = mMsg->parentCollection();
if ( !mFolder.isValid() ) {
if ( mOrigMsg ) {
mFolder = mOrigMsg->parentCollection();
}
if ( !mFolder.isValid() ) {
kDebug() << "Oops! No folder for message";
}
}
}
#else
kDebug() << "AKONADI PORT: Disabled code in " << Q_FUNC_INFO;
#endif
kDebug() << "Folder found:" << mFolder;
if ( mFolder.isValid() ) // only if a folder was found
{
QString fid = QString::number( mFolder.id() );
Templates fconf( fid );
if ( fconf.useCustomTemplates() ) { // does folder use custom templates?
switch( mMode ) {
case NewMessage:
tmpl = fconf.templateNewMessage();
break;
case Reply:
tmpl = fconf.templateReply();
break;
case ReplyAll:
tmpl = fconf.templateReplyAll();
break;
case Forward:
tmpl = fconf.templateForward();
break;
default:
kDebug() << "Unknown message mode:" << mMode;
return "";
}
mQuoteString = fconf.quoteString();
if ( !tmpl.isEmpty() ) {
return tmpl; // use folder-specific template
}
}
}
if ( !mIdentity ) { // find identity message belongs to
kDebug() << "AKONADI PORT: verify Akonadi::Item() here " << Q_FUNC_INFO;
mIdentity = KMail::MessageHelper::identityUoid( Akonadi::Item(), mMsg );
if ( !mIdentity && mOrigMsg ) {
kDebug() << "AKONADI PORT: verify Akonadi::Item() here " << Q_FUNC_INFO;
mIdentity = KMail::MessageHelper::identityUoid( Akonadi::Item(), mOrigMsg );
}
mIdentity = kmkernel->identityManager()->identityForUoidOrDefault( mIdentity ).uoid();
if ( !mIdentity ) {
kDebug() << "Oops! No identity for message";
}
}
kDebug() << "Identity found:" << mIdentity;
QString iid;
if ( mIdentity ) {
iid = TemplatesConfiguration::configIdString( mIdentity ); // templates ID for that identity
}
else {
iid = "IDENTITY_NO_IDENTITY"; // templates ID for no identity
}
Templates iconf( iid );
if ( iconf.useCustomTemplates() ) { // does identity use custom templates?
switch( mMode ) {
case NewMessage:
tmpl = iconf.templateNewMessage();
break;
case Reply:
tmpl = iconf.templateReply();
break;
case ReplyAll:
tmpl = iconf.templateReplyAll();
break;
case Forward:
tmpl = iconf.templateForward();
break;
default:
kDebug() << "Unknown message mode:" << mMode;
return "";
}
mQuoteString = iconf.quoteString();
if ( !tmpl.isEmpty() ) {
return tmpl; // use identity-specific template
}
}
switch( mMode ) { // use the global template
case NewMessage:
tmpl = GlobalSettings::self()->templateNewMessage();
break;
case Reply:
tmpl = GlobalSettings::self()->templateReply();
break;
case ReplyAll:
tmpl = GlobalSettings::self()->templateReplyAll();
break;
case Forward:
tmpl = GlobalSettings::self()->templateForward();
break;
default:
kDebug() << "Unknown message mode:" << mMode;
return "";
}
mQuoteString = GlobalSettings::self()->quoteString();
return tmpl;
}
QString TemplateParser::pipe( const QString &cmd, const QString &buf )
{
KProcess process;
bool success, finished;
process.setOutputChannelMode( KProcess::SeparateChannels );
process.setShellCommand( cmd );
process.start();
if ( process.waitForStarted( PipeTimeout ) ) {
if ( !buf.isEmpty() )
process.write( buf.toAscii() );
if ( buf.isEmpty() || process.waitForBytesWritten( PipeTimeout ) ) {
if ( !buf.isEmpty() )
process.closeWriteChannel();
if ( process.waitForFinished( PipeTimeout ) ) {
success = ( process.exitStatus() == QProcess::NormalExit );
finished = true;
}
else {
finished = false;
success = false;
}
}
else {
success = false;
finished = false;
}
// The process has started, but did not finish in time. Kill it.
if ( !finished )
process.kill();
}
else
success = false;
if ( !success && mDebug )
KMessageBox::error( 0, i18nc( "@info",
"Pipe command <command>%1</command> failed.",
cmd ) );
if ( success )
return process.readAllStandardOutput();
else
return QString();
}
void TemplateParser::parseTextStringFromContent( KMime::Content * root,
QByteArray& parsedString,
const QTextCodec*& codec,
bool& isHTML ) const
{
if ( !root )
return;
isHTML = false;
KMime::Content * curNode = root->textContent();
kDebug() << ( curNode ? "text part found!\n" : "sorry, no text node!\n" );
if( curNode ) {
isHTML = curNode->contentType()->isHTMLText();
// now parse the TEXT message part we want to quote
MessageViewer::EmptySource emptySource;
MessageViewer::ObjectTreeParser otp( &emptySource, 0, 0, true, false, true );
otp.parseObjectTree( Akonadi::Item(), curNode );
parsedString = otp.rawReplyString();
codec = otp.nodeHelper()->codec( curNode );
}
}
QString TemplateParser::asPlainTextFromObjectTree( const KMime::Message::Ptr &msg, KMime::Content *root, MessageViewer::ObjectTreeParser *otp, bool aStripSignature,
bool allowDecryption )
{
Q_ASSERT( root );
Q_ASSERT( otp->nodeHelper()->nodeProcessed( root ) );
QByteArray parsedString;
bool isHTML = false;
const QTextCodec * codec = 0;
if ( !root ) return QString();
parseTextStringFromContent( root, parsedString, codec, isHTML );
#if 0 //TODO port to akonadi
if ( mOverrideCodec || !codec )
codec = otp->nodeHelper()->codec( msg );
#else
kDebug() << "AKONADI PORT: Disabled code in " << Q_FUNC_INFO;
#endif
if ( parsedString.isEmpty() )
return QString();
bool clearSigned = false;
QString result;
// decrypt
if ( allowDecryption ) {
QList<Kpgp::Block> pgpBlocks;
QList<QByteArray> nonPgpBlocks;
if ( Kpgp::Module::prepareMessageForDecryption( parsedString,
pgpBlocks,
nonPgpBlocks ) ) {
// Only decrypt/strip off the signature if there is only one OpenPGP
// block in the message
if ( pgpBlocks.count() == 1 ) {
Kpgp::Block &block = pgpBlocks.first();
if ( block.type() == Kpgp::PgpMessageBlock ||
block.type() == Kpgp::ClearsignedBlock ) {
if ( block.type() == Kpgp::PgpMessageBlock ) {
// try to decrypt this OpenPGP block
block.decrypt();
} else {
// strip off the signature
block.verify();
clearSigned = true;
}
result = codec->toUnicode( nonPgpBlocks.first() )
+ codec->toUnicode( block.text() )
+ codec->toUnicode( nonPgpBlocks.last() );
}
}
}
}
if ( result.isEmpty() ) {
result = codec->toUnicode( parsedString );
if ( result.isEmpty() )
return result;
}
// html -> plaintext conversion, if necessary:
if ( isHTML /* TODO port it && mDecodeHTML*/ ) {
// TODO: WEBKIT check to make sure that this does not access external resources or executes any scripts
QWebElement doc = QWebElement();
doc.prependInside( result );
result = doc.toPlainText();
}
// strip the signature (footer):
if ( aStripSignature )
return MessageViewer::StringUtil::stripSignature( result, clearSigned );
else
return result;
}
QString TemplateParser::asPlainText( const KMime::Message::Ptr &msg, bool aStripSignature, bool allowDecryption )
{
if ( !msg )
return QString();
KMime::Content *root = new KMime::Content;
root->setContent( msg->encodedContent() );
root->parse();
MessageViewer::EmptySource emptySource;
MessageViewer::ObjectTreeParser otp(&emptySource);
otp.parseObjectTree( Akonadi::Item(), root );
QString result = asPlainTextFromObjectTree( msg, root, &otp, aStripSignature, allowDecryption );
delete root;
return result;
}
QString TemplateParser::asQuotedString( const KMime::Message::Ptr &msg, const QString& aIndentStr,
const QString& selection /*.clear() */,
bool aStripSignature /* = true */,
bool allowDecryption /* = true */)
{
if ( !msg )
return QString();
QString content = selection.isEmpty() ?
asPlainText( msg, aStripSignature, allowDecryption ) : selection ;
// Remove blank lines at the beginning:
const int firstNonWS = content.indexOf( QRegExp( "\\S" ) );
const int lineStart = content.lastIndexOf( '\n', firstNonWS );
if ( lineStart >= 0 )
content.remove( 0, static_cast<unsigned int>( lineStart ) );
const QString indentStr = MessageViewer::StringUtil::formatString( aIndentStr, msg->from()->asUnicodeString() );
if ( kmkernel->smartQuote() && kmkernel->wordWrap() )
content = MessageViewer::StringUtil::smartQuote( content, kmkernel->wrapCol() - indentStr.length() );
content.replace( '\n', '\n' + indentStr );
content.prepend( indentStr );
content += '\n';
return content;
}
} // namespace KMail
#include "templateparser.moc"