Merge remote-tracking branch 'origin/KDE/4.10'
[kdepim.git] / templateparser / templateparser.cpp
blobbf66aa871a1a31ed6ecac140654f21517bd37a28
1 /*
2 * Copyright (C) 2006 Dmitry Morozhnikov <dmiceman@mail.ru>
3 * Copyright (C) 2011 Sudhendu Kumar <sudhendu.kumar.roy@gmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "templateparser.h"
21 #include "globalsettings_base.h"
22 #ifndef Q_OS_WINCE
23 #include "customtemplates_kfg.h"
24 #include "templatesconfiguration_kfg.h"
25 #include "templatesconfiguration.h"
26 #endif
28 #include <messagecore/attachmentcollector.h>
29 #include <messagecore/imagecollector.h>
30 #include <messagecore/stringutil.h>
32 #include <messageviewer/objecttreeparser.h>
34 #include <KPIMIdentities/Identity>
35 #include <KPIMIdentities/IdentityManager>
37 #include <KCalendarSystem>
38 #include <KCharsets>
39 #include <KGlobal>
40 #include <KLocale>
41 #include <KMessageBox>
42 #include <KProcess>
43 #include <KShell>
45 #include <QDir>
46 #include <QFile>
47 #include <QFileInfo>
48 #include <QTextCodec>
49 #include <QWebFrame>
50 #include <QWebPage>
52 namespace TemplateParser {
54 static const int PipeTimeout = 15 * 1000;
56 QTextCodec *selectCharset( const QStringList &charsets, const QString &text )
58 foreach ( const QString &name, charsets ) {
59 // We use KCharsets::codecForName() instead of QTextCodec::codecForName() here, because
60 // the former knows us-ascii is latin1.
61 bool ok = true;
62 QTextCodec *codec;
63 if ( name == QLatin1String( "locale" ) ) {
64 codec = QTextCodec::codecForLocale();
65 } else {
66 codec = KGlobal::charsets()->codecForName( name, ok );
68 if( !ok || !codec ) {
69 kWarning() << "Could not get text codec for charset" << name;
70 continue;
72 if( codec->canEncode( text ) ) {
73 // Special check for us-ascii (needed because us-ascii is not exactly latin1).
74 if( name == QLatin1String( "us-ascii" ) && !KMime::isUsAscii( text ) ) {
75 continue;
77 kDebug() << "Chosen charset" << name << codec->name();
78 return codec;
81 kDebug() << "No appropriate charset found.";
82 return KGlobal::charsets()->codecForName( "utf-8" );
85 TemplateParser::TemplateParser( const KMime::Message::Ptr &amsg, const Mode amode ) :
86 mMode( amode ), mIdentity( 0 ),
87 mAllowDecryption( true ),
88 mDebug( false ), mQuoteString( "> " ), m_identityManager( 0 ),
89 mWrap( true ),
90 mColWrap( 80 ),
91 mQuotes( ReplyAsOriginalMessage )
93 mMsg = amsg;
95 mEmptySource = new MessageViewer::EmptySource;
96 mEmptySource->setAllowDecryption( mAllowDecryption );
98 mOtp = new MessageViewer::ObjectTreeParser( mEmptySource );
99 mOtp->setAllowAsync( false );
102 void TemplateParser::setSelection( const QString &selection )
104 mSelection = selection;
107 void TemplateParser::setAllowDecryption( const bool allowDecryption )
109 mAllowDecryption = allowDecryption;
110 mEmptySource->setAllowDecryption( mAllowDecryption );
113 bool TemplateParser::shouldStripSignature() const
115 // Only strip the signature when replying, it should be preserved when forwarding
116 return
117 ( mMode == Reply || mMode == ReplyAll )
118 #ifndef Q_OS_WINCE
119 && GlobalSettings::self()->stripSignature()
120 #endif
124 void TemplateParser::setIdentityManager( KPIMIdentities::IdentityManager *ident )
126 m_identityManager = ident;
129 void TemplateParser::setCharsets( const QStringList &charsets )
131 m_charsets = charsets;
134 TemplateParser::~TemplateParser()
136 delete mEmptySource;
139 int TemplateParser::parseQuotes( const QString &prefix, const QString &str,
140 QString &quote ) const
142 int pos = prefix.length();
143 int len;
144 int str_len = str.length();
146 // Also allow the german lower double-quote sign as quote separator, not only
147 // the standard ASCII quote ("). This fixes bug 166728.
148 QList< QChar > quoteChars;
149 quoteChars.append( '"' );
150 quoteChars.append( 0x201C );
152 QChar prev( QChar::Null );
154 pos++;
155 len = pos;
157 while ( pos < str_len ) {
158 QChar c = str[pos];
160 pos++;
161 len++;
163 if ( !prev.isNull() ) {
164 quote.append( c );
165 prev = QChar::Null;
166 } else {
167 if ( c == '\\' ) {
168 prev = c;
169 } else if ( quoteChars.contains( c ) ) {
170 break;
171 } else {
172 quote.append( c );
177 return len;
180 QString TemplateParser::getFName( const QString &str )
182 // simple logic:
183 // if there is ',' in name, than format is 'Last, First'
184 // else format is 'First Last'
185 // last resort -- return 'name' from 'name@domain'
186 int sep_pos;
187 QString res;
188 if ( ( sep_pos = str.indexOf( QLatin1Char( '@' ) ) ) > 0 ) {
189 int i;
190 for ( i = ( sep_pos - 1 ); i >= 0; --i ) {
191 QChar c = str[i];
192 if ( c.isLetterOrNumber() ) {
193 res.prepend( c );
194 } else {
195 break;
198 } else if ( ( sep_pos = str.indexOf( QLatin1Char( ',' ) ) ) > 0 ) {
199 int i;
200 bool begin = false;
201 const int strLength( str.length() );
202 for ( i = sep_pos; i < strLength; ++i ) {
203 QChar c = str[i];
204 if ( c.isLetterOrNumber() ) {
205 begin = true;
206 res.append( c );
207 } else if ( begin ) {
208 break;
211 } else {
212 int i;
213 const int strLength( str.length() );
214 for ( i = 0; i < strLength; ++i ) {
215 QChar c = str[i];
216 if ( c.isLetterOrNumber() ) {
217 res.append( c );
218 } else {
219 break;
223 return res;
226 QString TemplateParser::getLName( const QString &str )
228 // simple logic:
229 // if there is ',' in name, than format is 'Last, First'
230 // else format is 'First Last'
231 int sep_pos;
232 QString res;
233 if ( ( sep_pos = str.indexOf( QLatin1Char( ',' ) ) ) > 0 ) {
234 int i;
235 for ( i = sep_pos; i >= 0; --i ) {
236 QChar c = str[i];
237 if ( c.isLetterOrNumber() ) {
238 res.prepend( c );
239 } else {
240 break;
243 } else {
244 if ( ( sep_pos = str.indexOf( QLatin1Char( ' ' ) ) ) > 0 ) {
245 bool begin = false;
246 const int strLength( str.length() );
247 for ( int i = sep_pos; i < strLength; ++i ) {
248 QChar c = str[i];
249 if ( c.isLetterOrNumber() ) {
250 begin = true;
251 res.append( c );
252 } else if ( begin ) {
253 break;
258 return res;
261 void TemplateParser::process( const KMime::Message::Ptr &aorig_msg,
262 const Akonadi::Collection & afolder )
264 if( aorig_msg == 0 ) {
265 kDebug() << "aorig_msg == 0!";
266 return;
268 mOrigMsg = aorig_msg;
269 mFolder = afolder;
270 const QString tmpl = findTemplate();
271 if ( tmpl.isEmpty() ) {
272 return;
274 processWithTemplate( tmpl );
277 void TemplateParser::process( const QString &tmplName, const KMime::Message::Ptr &aorig_msg,
278 const Akonadi::Collection &afolder )
280 mOrigMsg = aorig_msg;
281 mFolder = afolder;
282 const QString tmpl = findCustomTemplate( tmplName );
283 processWithTemplate( tmpl );
286 void TemplateParser::processWithIdentity( uint uoid, const KMime::Message::Ptr &aorig_msg,
287 const Akonadi::Collection &afolder )
289 mIdentity = uoid;
290 process( aorig_msg, afolder );
293 void TemplateParser::processWithTemplate( const QString &tmpl )
295 mOtp->parseObjectTree( mOrigMsg.get() );
296 const int tmpl_len = tmpl.length();
297 QString plainBody, htmlBody;
299 bool dnl = false;
300 for ( int i = 0; i < tmpl_len; ++i ) {
301 QChar c = tmpl[i];
302 // kDebug() << "Next char: " << c;
303 if ( c == '%' ) {
304 const QString cmd = tmpl.mid( i + 1 );
306 if ( cmd.startsWith( QLatin1Char( '-' ) ) ) {
307 // dnl
308 kDebug() << "Command: -";
309 dnl = true;
310 i += 1;
312 } else if ( cmd.startsWith( QLatin1String( "REM=" ) ) ) {
313 // comments
314 kDebug() << "Command: REM=";
315 QString q;
316 int len = parseQuotes( "REM=", cmd, q );
317 i += len;
319 } else if ( cmd.startsWith( QLatin1String( "INSERT=" ) ) ) {
320 // insert content of specified file as is
321 kDebug() << "Command: INSERT=";
322 QString q;
323 int len = parseQuotes( "INSERT=", cmd, q );
324 i += len;
325 QString path = KShell::tildeExpand( q );
326 QFileInfo finfo( path );
327 if ( finfo.isRelative() ) {
328 path = QDir::homePath();
329 path += '/';
330 path += q;
332 QFile file( path );
333 if ( file.open( QIODevice::ReadOnly ) ) {
334 const QByteArray content = file.readAll();
335 const QString str = QString::fromLocal8Bit( content, content.size() );
336 plainBody.append( str );
337 const QString body = plainToHtml( str );
338 htmlBody.append( body );
339 } else if ( mDebug ) {
340 KMessageBox::error(
342 i18nc( "@info",
343 "Cannot insert content from file %1: %2", path, file.errorString() ) );
346 } else if ( cmd.startsWith( QLatin1String( "SYSTEM=" ) ) ) {
347 // insert content of specified file as is
348 kDebug() << "Command: SYSTEM=";
349 QString q;
350 int len = parseQuotes( "SYSTEM=", cmd, q );
351 i += len;
352 const QString pipe_cmd = q;
353 const QString str = pipe( pipe_cmd, "" );
354 plainBody.append( str );
355 const QString body = plainToHtml( str );
356 htmlBody.append( body );
358 } else if ( cmd.startsWith( QLatin1String( "PUT=" ) ) ) {
359 // insert content of specified file as is
360 kDebug() << "Command: PUT=";
361 QString q;
362 int len = parseQuotes( "PUT=", cmd, q );
363 i += len;
364 QString path = KShell::tildeExpand( q );
365 QFileInfo finfo( path );
366 if ( finfo.isRelative() ) {
367 path = QDir::homePath();
368 path += '/';
369 path += q;
371 QFile file( path );
372 if ( file.open( QIODevice::ReadOnly ) ) {
373 const QByteArray content = file.readAll();
374 plainBody.append( QString::fromLocal8Bit( content, content.size() ) );
376 const QString body = plainToHtml( QString::fromLocal8Bit( content, content.size() ) );
377 htmlBody.append( body );
378 } else if ( mDebug ) {
379 KMessageBox::error(
381 i18nc( "@info",
382 "Cannot insert content from file %1: %2", path, file.errorString() ) );
385 } else if ( cmd.startsWith( QLatin1String( "QUOTEPIPE=" ) ) ) {
386 // pipe message body through command and insert it as quotation
387 kDebug() << "Command: QUOTEPIPE=";
388 QString q;
389 int len = parseQuotes( "QUOTEPIPE=", cmd, q );
390 i += len;
391 const QString pipe_cmd = q;
392 if ( mOrigMsg ) {
393 const QString plainStr =
394 pipe( pipe_cmd, plainMessageText( shouldStripSignature(), NoSelectionAllowed ) );
395 QString plainQuote = quotedPlainText( plainStr );
396 if ( plainQuote.endsWith( '\n' ) ) {
397 plainQuote.chop( 1 );
399 plainBody.append( plainQuote );
401 const QString htmlStr =
402 pipe( pipe_cmd, htmlMessageText( shouldStripSignature(), NoSelectionAllowed ) );
403 const QString htmlQuote = quotedHtmlText( htmlStr );
404 htmlBody.append( htmlQuote );
407 } else if ( cmd.startsWith( QLatin1String( "QUOTE" ) ) ) {
408 kDebug() << "Command: QUOTE";
409 i += strlen( "QUOTE" );
410 if ( mOrigMsg ) {
411 QString plainQuote =
412 quotedPlainText( plainMessageText( shouldStripSignature(), SelectionAllowed ) );
413 if ( plainQuote.endsWith( '\n' ) ) {
414 plainQuote.chop( 1 );
416 plainBody.append( plainQuote );
418 const QString htmlQuote =
419 quotedHtmlText( htmlMessageText( shouldStripSignature(), SelectionAllowed ) );
420 htmlBody.append( htmlQuote );
423 } else if ( cmd.startsWith( QLatin1String( "FORCEDPLAIN" ) ) ) {
424 kDebug() << "Command: FORCEDPLAIN";
425 mQuotes = ReplyAsPlain;
426 i += strlen( "FORCEDPLAIN" );
428 } else if ( cmd.startsWith( QLatin1String( "FORCEDHTML" ) ) ) {
429 kDebug() << "Command: FORCEDHTML";
430 mQuotes = ReplyAsHtml;
431 i += strlen( "FORCEDHTML" );
433 } else if ( cmd.startsWith( QLatin1String( "QHEADERS" ) ) ) {
434 kDebug() << "Command: QHEADERS";
435 i += strlen( "QHEADERS" );
436 if ( mOrigMsg ) {
437 QString plainQuote =
438 quotedPlainText( MessageCore::StringUtil::headerAsSendableString( mOrigMsg ) );
439 if ( plainQuote.endsWith( '\n' ) ) {
440 plainQuote.chop( 1 );
442 plainBody.append( plainQuote );
444 const QString htmlQuote =
445 quotedHtmlText( MessageCore::StringUtil::headerAsSendableString( mOrigMsg ) );
446 const QString str = plainToHtml( htmlQuote );
447 htmlBody.append( str );
450 } else if ( cmd.startsWith( QLatin1String( "HEADERS" ) ) ) {
451 kDebug() << "Command: HEADERS";
452 i += strlen( "HEADERS" );
453 if ( mOrigMsg ) {
454 const QString str = MessageCore::StringUtil::headerAsSendableString( mOrigMsg );
455 plainBody.append( str );
456 const QString body = plainToHtml( str );
457 htmlBody.append( body );
460 } else if ( cmd.startsWith( QLatin1String( "TEXTPIPE=" ) ) ) {
461 // pipe message body through command and insert it as is
462 kDebug() << "Command: TEXTPIPE=";
463 QString q;
464 int len = parseQuotes( "TEXTPIPE=", cmd, q );
465 i += len;
466 const QString pipe_cmd = q;
467 if ( mOrigMsg ) {
468 const QString plainStr =
469 pipe( pipe_cmd, plainMessageText( shouldStripSignature(), NoSelectionAllowed ) );
470 plainBody.append( plainStr );
472 const QString htmlStr =
473 pipe( pipe_cmd, htmlMessageText( shouldStripSignature(), NoSelectionAllowed ) );
474 htmlBody.append( htmlStr );
477 } else if ( cmd.startsWith( QLatin1String( "MSGPIPE=" ) ) ) {
478 // pipe full message through command and insert result as is
479 kDebug() << "Command: MSGPIPE=";
480 QString q;
481 int len = parseQuotes( "MSGPIPE=", cmd, q );
482 i += len;
483 if ( mOrigMsg ) {
484 QString pipe_cmd = q;
485 const QString str = pipe( pipe_cmd, mOrigMsg->encodedContent() );
486 plainBody.append( str );
488 const QString body = plainToHtml( str );
489 htmlBody.append( body );
492 } else if ( cmd.startsWith( QLatin1String( "BODYPIPE=" ) ) ) {
493 // pipe message body generated so far through command and insert result as is
494 kDebug() << "Command: BODYPIPE=";
495 QString q;
496 int len = parseQuotes( "BODYPIPE=", cmd, q );
497 i += len;
498 const QString pipe_cmd = q;
499 const QString plainStr = pipe( pipe_cmd, plainBody );
500 plainBody.append( plainStr );
502 const QString htmlStr = pipe( pipe_cmd, htmlBody );
503 const QString body = plainToHtml( htmlStr );
504 htmlBody.append( body );
506 } else if ( cmd.startsWith( QLatin1String( "CLEARPIPE=" ) ) ) {
507 // pipe message body generated so far through command and
508 // insert result as is replacing current body
509 kDebug() << "Command: CLEARPIPE=";
510 QString q;
511 int len = parseQuotes( "CLEARPIPE=", cmd, q );
512 i += len;
513 const QString pipe_cmd = q;
514 const QString plainStr = pipe( pipe_cmd, plainBody );
515 plainBody = plainStr;
517 const QString htmlStr = pipe( pipe_cmd, htmlBody );
518 htmlBody = htmlStr;
520 KMime::Headers::Generic *header =
521 new KMime::Headers::Generic( "X-KMail-CursorPos", mMsg.get(),
522 QString::number( 0 ), "utf-8" );
523 mMsg->setHeader( header );
525 } else if ( cmd.startsWith( QLatin1String( "TEXT" ) ) ) {
526 kDebug() << "Command: TEXT";
527 i += strlen( "TEXT" );
528 if ( mOrigMsg ) {
529 const QString plainStr = plainMessageText( shouldStripSignature(), NoSelectionAllowed );
530 plainBody.append( plainStr );
532 const QString htmlStr = htmlMessageText( shouldStripSignature(), NoSelectionAllowed );
533 htmlBody.append( htmlStr );
536 } else if ( cmd.startsWith( QLatin1String( "OTEXTSIZE" ) ) ) {
537 kDebug() << "Command: OTEXTSIZE";
538 i += strlen( "OTEXTSIZE" );
539 if ( mOrigMsg ) {
540 const QString str = QString::fromLatin1( "%1" ).arg( mOrigMsg->body().length() );
541 plainBody.append( str );
542 const QString body = plainToHtml( str );
543 htmlBody.append( body );
546 } else if ( cmd.startsWith( QLatin1String( "OTEXT" ) ) ) {
547 kDebug() << "Command: OTEXT";
548 i += strlen( "OTEXT" );
549 if ( mOrigMsg ) {
550 const QString plainStr = plainMessageText( shouldStripSignature(), NoSelectionAllowed );
551 plainBody.append( plainStr );
553 const QString htmlStr = htmlMessageText( shouldStripSignature(), NoSelectionAllowed );
554 htmlBody.append( htmlStr );
557 } else if ( cmd.startsWith( QLatin1String( "OADDRESSEESADDR" ) ) ) {
558 kDebug() << "Command: OADDRESSEESADDR";
559 i += strlen( "OADDRESSEESADDR" );
560 if ( mOrigMsg ) {
561 const QString to = mOrigMsg->to()->asUnicodeString();
562 const QString cc = mOrigMsg->cc()->asUnicodeString();
563 if ( !to.isEmpty() ) {
564 QString toLine = i18nc( "@item:intext email To", "To:" ) + QLatin1Char( ' ' ) + to;
565 plainBody.append( toLine );
566 const QString body = plainToHtml( toLine );
567 htmlBody.append( body );
569 if ( !to.isEmpty() && !cc.isEmpty() ) {
570 plainBody.append( QLatin1Char( '\n' ) );
571 const QString str = plainToHtml( QString( QLatin1Char( '\n' ) ) );
572 htmlBody.append( str );
574 if ( !cc.isEmpty() ) {
575 QString ccLine = i18nc( "@item:intext email CC", "CC:" ) + QLatin1Char( ' ' ) + cc;
576 plainBody.append( ccLine );
577 const QString str = plainToHtml( ccLine );
578 htmlBody.append( str );
582 } else if ( cmd.startsWith( QLatin1String( "CCADDR" ) ) ) {
583 kDebug() << "Command: CCADDR";
584 i += strlen( "CCADDR" );
585 const QString str = mMsg->cc()->asUnicodeString();
586 plainBody.append( str );
587 const QString body = plainToHtml( str );
588 htmlBody.append( body );
590 } else if ( cmd.startsWith( QLatin1String( "CCNAME" ) ) ) {
591 kDebug() << "Command: CCNAME";
592 i += strlen( "CCNAME" );
593 const QString str =
594 MessageCore::StringUtil::stripEmailAddr( mMsg->cc()->asUnicodeString( ) );
595 plainBody.append( str );
596 const QString body = plainToHtml( str );
597 htmlBody.append( body );
599 } else if ( cmd.startsWith( QLatin1String( "CCFNAME" ) ) ) {
600 kDebug() << "Command: CCFNAME";
601 i += strlen( "CCFNAME" );
602 const QString str =
603 MessageCore::StringUtil::stripEmailAddr( mMsg->cc()->asUnicodeString( ) );
604 plainBody.append( getFName( str ) );
605 const QString body = plainToHtml( getFName( str ) );
606 htmlBody.append( body );
608 } else if ( cmd.startsWith( QLatin1String( "CCLNAME" ) ) ) {
609 kDebug() << "Command: CCLNAME";
610 i += strlen( "CCLNAME" );
611 const QString str =
612 MessageCore::StringUtil::stripEmailAddr( mMsg->cc()->asUnicodeString( ) );
613 plainBody.append( getLName( str ) );
614 const QString body = plainToHtml( getLName( str ) );
615 htmlBody.append( body );
617 } else if ( cmd.startsWith( QLatin1String( "TOADDR" ) ) ) {
618 kDebug() << "Command: TOADDR";
619 i += strlen( "TOADDR" );
620 const QString str = mMsg->to()->asUnicodeString();
621 plainBody.append( str );
622 const QString body = plainToHtml( str );
623 htmlBody.append( body );
625 } else if ( cmd.startsWith( QLatin1String( "TONAME" ) ) ) {
626 kDebug() << "Command: TONAME";
627 i += strlen( "TONAME" );
628 const QString str =
629 MessageCore::StringUtil::stripEmailAddr( mMsg->to()->asUnicodeString( ) );
630 plainBody.append( str );
631 const QString body = plainToHtml( str );
632 htmlBody.append( body );
634 } else if ( cmd.startsWith( QLatin1String( "TOFNAME" ) ) ) {
635 kDebug() << "Command: TOFNAME";
636 i += strlen( "TOFNAME" );
637 const QString str =
638 MessageCore::StringUtil::stripEmailAddr( mMsg->to()->asUnicodeString( ) );
639 plainBody.append( getFName( str ) );
640 const QString body = plainToHtml( getFName( str ) );
641 htmlBody.append( body );
643 } else if ( cmd.startsWith( QLatin1String( "TOLNAME" ) ) ) {
644 kDebug() << "Command: TOLNAME";
645 i += strlen( "TOLNAME" );
646 const QString str =
647 MessageCore::StringUtil::stripEmailAddr( mMsg->to()->asUnicodeString( ) );
648 plainBody.append( getLName( str ) );
649 const QString body = plainToHtml( getLName( str ) );
650 htmlBody.append( body );
652 } else if ( cmd.startsWith( QLatin1String( "TOLIST" ) ) ) {
653 kDebug() << "Command: TOLIST";
654 i += strlen( "TOLIST" );
655 const QString str = mMsg->to()->asUnicodeString();
656 plainBody.append( str );
657 const QString body = plainToHtml( str );
658 htmlBody.append( body );
660 } else if ( cmd.startsWith( QLatin1String( "FROMADDR" ) ) ) {
661 kDebug() << "Command: FROMADDR";
662 i += strlen( "FROMADDR" );
663 const QString str = mMsg->from()->asUnicodeString();
664 plainBody.append( str );
665 const QString body = plainToHtml( str );
666 htmlBody.append( body );
668 } else if ( cmd.startsWith( QLatin1String( "FROMNAME" ) ) ) {
669 kDebug() << "Command: FROMNAME";
670 i += strlen( "FROMNAME" );
671 const QString str =
672 MessageCore::StringUtil::stripEmailAddr( mMsg->from()->asUnicodeString( ) );
673 plainBody.append( str );
674 const QString body = plainToHtml( str );
675 htmlBody.append( body );
677 } else if ( cmd.startsWith( QLatin1String( "FROMFNAME" ) ) ) {
678 kDebug() << "Command: FROMFNAME";
679 i += strlen( "FROMFNAME" );
680 const QString str =
681 MessageCore::StringUtil::stripEmailAddr( mMsg->from()->asUnicodeString( ) );
682 plainBody.append( getFName( str ) );
683 const QString body = plainToHtml( getFName( str ) );
684 htmlBody.append( body );
686 } else if ( cmd.startsWith( QLatin1String( "FROMLNAME" ) ) ) {
687 kDebug() << "Command: FROMLNAME";
688 i += strlen( "FROMLNAME" );
689 const QString str =
690 MessageCore::StringUtil::stripEmailAddr( mMsg->from()->asUnicodeString( ) );
691 plainBody.append( getLName( str ) );
692 const QString body = plainToHtml( getLName( str ) );
693 htmlBody.append( body );
695 } else if ( cmd.startsWith( QLatin1String( "FULLSUBJECT" ) ) ) {
696 kDebug() << "Command: FULLSUBJECT";
697 i += strlen( "FULLSUBJECT" );
698 const QString str = mMsg->subject()->asUnicodeString();
699 plainBody.append( str );
700 const QString body = plainToHtml( str );
701 htmlBody.append( body );
703 } else if ( cmd.startsWith( QLatin1String( "FULLSUBJ" ) ) ) {
704 kDebug() << "Command: FULLSUBJ";
705 i += strlen( "FULLSUBJ" );
706 const QString str = mMsg->subject()->asUnicodeString();
707 plainBody.append( str );
708 const QString body = plainToHtml( str );
709 htmlBody.append( body );
711 } else if ( cmd.startsWith( QLatin1String( "MSGID" ) ) ) {
712 kDebug() << "Command: MSGID";
713 i += strlen( "MSGID" );
714 const QString str = mMsg->messageID()->asUnicodeString();
715 plainBody.append( str );
716 const QString body = plainToHtml( str );
717 htmlBody.append( body );
719 } else if ( cmd.startsWith( QLatin1String( "OHEADER=" ) ) ) {
720 // insert specified content of header from original message
721 kDebug() << "Command: OHEADER=";
722 QString q;
723 int len = parseQuotes( "OHEADER=", cmd, q );
724 i += len;
725 if ( mOrigMsg ) {
726 const QString hdr = q;
727 const QString str =
728 mOrigMsg->headerByType( hdr.toLocal8Bit() ) ?
729 mOrigMsg->headerByType( hdr.toLocal8Bit() )->asUnicodeString() :
731 plainBody.append( str );
732 const QString body = plainToHtml( str );
733 htmlBody.append( body );
736 } else if ( cmd.startsWith( QLatin1String( "HEADER=" ) ) ) {
737 // insert specified content of header from current message
738 kDebug() << "Command: HEADER=";
739 QString q;
740 int len = parseQuotes( "HEADER=", cmd, q );
741 i += len;
742 const QString hdr = q;
743 const QString str =
744 mMsg->headerByType( hdr.toLocal8Bit() ) ?
745 mMsg->headerByType( hdr.toLocal8Bit() )->asUnicodeString() :
747 plainBody.append( str );
748 const QString body = plainToHtml( str );
749 htmlBody.append( body );
751 } else if ( cmd.startsWith( QLatin1String( "HEADER( " ) ) ) {
752 // insert specified content of header from current message
753 kDebug() << "Command: HEADER(";
754 QRegExp re = QRegExp( "^HEADER\\((.+)\\)" );
755 re.setMinimal( true );
756 int res = re.indexIn( cmd );
757 if ( res != 0 ) {
758 // something wrong
759 i += strlen( "HEADER( " );
760 } else {
761 i += re.matchedLength();
762 const QString hdr = re.cap( 1 );
763 const QString str =
764 mMsg->headerByType( hdr.toLocal8Bit() ) ?
765 mMsg->headerByType( hdr.toLocal8Bit() )->asUnicodeString() :
767 plainBody.append( str );
768 const QString body = plainToHtml( str );
769 htmlBody.append( body );
772 } else if ( cmd.startsWith( QLatin1String( "OCCADDR" ) ) ) {
773 kDebug() << "Command: OCCADDR";
774 i += strlen( "OCCADDR" );
775 if ( mOrigMsg ) {
776 const QString str = mOrigMsg->cc()->asUnicodeString();
777 plainBody.append( str );
778 const QString body = plainToHtml( str );
779 htmlBody.append( body );
782 } else if ( cmd.startsWith( QLatin1String( "OCCNAME" ) ) ) {
783 kDebug() << "Command: OCCNAME";
784 i += strlen( "OCCNAME" );
785 if ( mOrigMsg ) {
786 const QString str =
787 MessageCore::StringUtil::stripEmailAddr( mOrigMsg->cc()->asUnicodeString( ) );
788 plainBody.append( str );
789 const QString body = plainToHtml( str );
790 htmlBody.append( body );
793 } else if ( cmd.startsWith( QLatin1String( "OCCFNAME" ) ) ) {
794 kDebug() << "Command: OCCFNAME";
795 i += strlen( "OCCFNAME" );
796 if ( mOrigMsg ) {
797 const QString str =
798 MessageCore::StringUtil::stripEmailAddr( mOrigMsg->cc()->asUnicodeString( ) );
799 plainBody.append( getFName( str ) );
800 const QString body = plainToHtml( getFName( str ) );
801 htmlBody.append( body );
804 } else if ( cmd.startsWith( QLatin1String( "OCCLNAME" ) ) ) {
805 kDebug() << "Command: OCCLNAME";
806 i += strlen( "OCCLNAME" );
807 if ( mOrigMsg ) {
808 const QString str =
809 MessageCore::StringUtil::stripEmailAddr( mOrigMsg->cc()->asUnicodeString( ) );
810 plainBody.append( getLName( str ) );
811 const QString body = plainToHtml( getLName( str ) );
812 htmlBody.append( body );
815 } else if ( cmd.startsWith( QLatin1String( "OTOADDR" ) ) ) {
816 kDebug() << "Command: OTOADDR";
817 i += strlen( "OTOADDR" );
818 if ( mOrigMsg ) {
819 const QString str = mOrigMsg->to()->asUnicodeString();
820 plainBody.append( str );
821 const QString body = plainToHtml( str );
822 htmlBody.append( body );
825 } else if ( cmd.startsWith( QLatin1String( "OTONAME" ) ) ) {
826 kDebug() << "Command: OTONAME";
827 i += strlen( "OTONAME" );
828 if ( mOrigMsg ) {
829 const QString str =
830 MessageCore::StringUtil::stripEmailAddr( mOrigMsg->to()->asUnicodeString( ) );
831 plainBody.append( str );
832 const QString body = plainToHtml( str );
833 htmlBody.append( body );
836 } else if ( cmd.startsWith( QLatin1String( "OTOFNAME" ) ) ) {
837 kDebug() << "Command: OTOFNAME";
838 i += strlen( "OTOFNAME" );
839 if ( mOrigMsg ) {
840 const QString str =
841 MessageCore::StringUtil::stripEmailAddr( mOrigMsg->to()->asUnicodeString( ) );
842 plainBody.append( getFName( str ) );
843 const QString body = plainToHtml( getFName( str ) );
844 htmlBody.append( body );
847 } else if ( cmd.startsWith( QLatin1String( "OTOLNAME" ) ) ) {
848 kDebug() << "Command: OTOLNAME";
849 i += strlen( "OTOLNAME" );
850 if ( mOrigMsg ) {
851 const QString str =
852 MessageCore::StringUtil::stripEmailAddr( mOrigMsg->to()->asUnicodeString( ) );
853 plainBody.append( getLName( str ) );
854 const QString body = plainToHtml( getLName( str ) );
855 htmlBody.append( body );
858 } else if ( cmd.startsWith( QLatin1String( "OTOLIST" ) ) ) {
859 kDebug() << "Command: OTOLIST";
860 i += strlen( "OTOLIST" );
861 if ( mOrigMsg ) {
862 const QString str = mOrigMsg->to()->asUnicodeString();
863 plainBody.append( str );
864 const QString body = plainToHtml( str );
865 htmlBody.append( body );
868 } else if ( cmd.startsWith( QLatin1String( "OTO" ) ) ) {
869 kDebug() << "Command: OTO";
870 i += strlen( "OTO" );
871 if ( mOrigMsg ) {
872 const QString str = mOrigMsg->to()->asUnicodeString();
873 plainBody.append( str );
874 const QString body = plainToHtml( str );
875 htmlBody.append( body );
878 } else if ( cmd.startsWith( QLatin1String( "OFROMADDR" ) ) ) {
879 kDebug() << "Command: OFROMADDR";
880 i += strlen( "OFROMADDR" );
881 if ( mOrigMsg ) {
882 const QString str = mOrigMsg->from()->asUnicodeString();
883 plainBody.append( str );
884 const QString body = plainToHtml( str );
885 htmlBody.append( body );
888 } else if ( cmd.startsWith( QLatin1String( "OFROMNAME" ) ) ) {
889 kDebug() << "Command: OFROMNAME";
890 i += strlen( "OFROMNAME" );
891 if ( mOrigMsg ) {
892 const QString str =
893 MessageCore::StringUtil::stripEmailAddr( mOrigMsg->from()->asUnicodeString() );
894 plainBody.append( str );
895 const QString body = plainToHtml( str );
896 htmlBody.append( body );
899 } else if ( cmd.startsWith( QLatin1String( "OFROMFNAME" ) ) ) {
900 kDebug() << "Command: OFROMFNAME";
901 i += strlen( "OFROMFNAME" );
902 if ( mOrigMsg ) {
903 const QString str =
904 MessageCore::StringUtil::stripEmailAddr( mOrigMsg->from()->asUnicodeString() );
905 plainBody.append( getFName( str ) );
906 const QString body = plainToHtml( getFName( str ) );
907 htmlBody.append( body );
910 } else if ( cmd.startsWith( QLatin1String( "OFROMLNAME" ) ) ) {
911 kDebug() << "Command: OFROMLNAME";
912 i += strlen( "OFROMLNAME" );
913 if ( mOrigMsg ) {
914 const QString str =
915 MessageCore::StringUtil::stripEmailAddr( mOrigMsg->from()->asUnicodeString() );
916 plainBody.append( getLName( str ) );
917 const QString body = plainToHtml( getLName( str ) );
918 htmlBody.append( body );
921 } else if ( cmd.startsWith( QLatin1String( "OFULLSUBJECT" ) ) ) {
922 kDebug() << "Command: OFULLSUBJECT";
923 i += strlen( "OFULLSUBJECT" );
924 if ( mOrigMsg ) {
925 const QString str = mOrigMsg->subject()->asUnicodeString();
926 plainBody.append( str );
927 const QString body = plainToHtml( str );
928 htmlBody.append( body );
931 } else if ( cmd.startsWith( QLatin1String( "OFULLSUBJ" ) ) ) {
932 kDebug() << "Command: OFULLSUBJ";
933 i += strlen( "OFULLSUBJ" );
934 if ( mOrigMsg ) {
935 const QString str = mOrigMsg->subject()->asUnicodeString();
936 plainBody.append( str );
937 const QString body = plainToHtml( str );
938 htmlBody.append( body );
941 } else if ( cmd.startsWith( QLatin1String( "OMSGID" ) ) ) {
942 kDebug() << "Command: OMSGID";
943 i += strlen( "OMSGID" );
944 if ( mOrigMsg ) {
945 const QString str = mOrigMsg->messageID()->asUnicodeString();
946 plainBody.append( str );
947 const QString body = plainToHtml( str );
948 htmlBody.append( body );
951 } else if ( cmd.startsWith( QLatin1String( "DATEEN" ) ) ) {
952 kDebug() << "Command: DATEEN";
953 i += strlen( "DATEEN" );
954 const QDateTime date = QDateTime::currentDateTime();
955 KLocale locale( "C" );
956 const QString str = locale.formatDate( date.date(), KLocale::LongDate );
957 plainBody.append( str );
958 const QString body = plainToHtml( str );
959 htmlBody.append( body );
961 } else if ( cmd.startsWith( QLatin1String( "DATESHORT" ) ) ) {
962 kDebug() << "Command: DATESHORT";
963 i += strlen( "DATESHORT" );
964 const QDateTime date = QDateTime::currentDateTime();
965 const QString str = KGlobal::locale()->formatDate( date.date(), KLocale::ShortDate );
966 plainBody.append( str );
967 const QString body = plainToHtml( str );
968 htmlBody.append( body );
970 } else if ( cmd.startsWith( QLatin1String( "DATE" ) ) ) {
971 kDebug() << "Command: DATE";
972 i += strlen( "DATE" );
973 const QDateTime date = QDateTime::currentDateTime();
974 const QString str = KGlobal::locale()->formatDate( date.date(), KLocale::LongDate );
975 plainBody.append( str );
976 const QString body = plainToHtml( str );
977 htmlBody.append( body );
979 } else if ( cmd.startsWith( QLatin1String( "DOW" ) ) ) {
980 kDebug() << "Command: DOW";
981 i += strlen( "DOW" );
982 const QDateTime date = QDateTime::currentDateTime();
983 const QString str = KGlobal::locale()->calendar()->weekDayName( date.date(),
984 KCalendarSystem::LongDayName );
985 plainBody.append( str );
986 const QString body = plainToHtml( str );
987 htmlBody.append( body );
989 } else if ( cmd.startsWith( QLatin1String( "TIMELONGEN" ) ) ) {
990 kDebug() << "Command: TIMELONGEN";
991 i += strlen( "TIMELONGEN" );
992 const QDateTime date = QDateTime::currentDateTime();
993 KLocale locale( "C" );
994 const QString str = locale.formatTime( date.time(), true );
995 plainBody.append( str );
996 const QString body = plainToHtml( str );
997 htmlBody.append( body );
999 } else if ( cmd.startsWith( QLatin1String( "TIMELONG" ) ) ) {
1000 kDebug() << "Command: TIMELONG";
1001 i += strlen( "TIMELONG" );
1002 const QDateTime date = QDateTime::currentDateTime();
1003 const QString str = KGlobal::locale()->formatTime( date.time(), true );
1004 plainBody.append( str );
1005 const QString body = plainToHtml( str );
1006 htmlBody.append( body );
1008 } else if ( cmd.startsWith( QLatin1String( "TIME" ) ) ) {
1009 kDebug() << "Command: TIME";
1010 i += strlen( "TIME" );
1011 const QDateTime date = QDateTime::currentDateTime();
1012 const QString str = KGlobal::locale()->formatTime( date.time(), false );
1013 plainBody.append( str );
1014 const QString body = plainToHtml( str );
1015 htmlBody.append( body );
1017 } else if ( cmd.startsWith( QLatin1String( "ODATEEN" ) ) ) {
1018 kDebug() << "Command: ODATEEN";
1019 i += strlen( "ODATEEN" );
1020 if ( mOrigMsg ) {
1021 const QDateTime date = mOrigMsg->date()->dateTime().dateTime();
1022 KLocale locale( "C" );
1023 const QString str = locale.formatDate( date.date(), KLocale::LongDate );
1024 plainBody.append( str );
1025 const QString body = plainToHtml( str );
1026 htmlBody.append( body );
1029 } else if ( cmd.startsWith( QLatin1String( "ODATESHORT" ) ) ) {
1030 kDebug() << "Command: ODATESHORT";
1031 i += strlen( "ODATESHORT" );
1032 if ( mOrigMsg ) {
1033 const QDateTime date = mOrigMsg->date()->dateTime().dateTime();
1034 const QString str = KGlobal::locale()->formatDate( date.date(), KLocale::ShortDate );
1035 plainBody.append( str );
1036 const QString body = plainToHtml( str );
1037 htmlBody.append( body );
1040 } else if ( cmd.startsWith( QLatin1String( "ODATE" ) ) ) {
1041 kDebug() << "Command: ODATE";
1042 i += strlen( "ODATE" );
1043 if ( mOrigMsg ) {
1044 const QDateTime date = mOrigMsg->date()->dateTime().dateTime();
1045 const QString str = KGlobal::locale()->formatDate( date.date(), KLocale::LongDate );
1046 plainBody.append( str );
1047 const QString body = plainToHtml( str );
1048 htmlBody.append( body );
1051 } else if ( cmd.startsWith( QLatin1String( "ODOW" ) ) ) {
1052 kDebug() << "Command: ODOW";
1053 i += strlen( "ODOW" );
1054 if ( mOrigMsg ) {
1055 const QDateTime date = mOrigMsg->date()->dateTime().dateTime();
1056 const QString str =
1057 KGlobal::locale()->calendar()->weekDayName( date.date(), KCalendarSystem::LongDayName );
1058 plainBody.append( str );
1059 const QString body = plainToHtml( str );
1060 htmlBody.append( body );
1063 } else if ( cmd.startsWith( QLatin1String( "OTIMELONGEN" ) ) ) {
1064 kDebug() << "Command: OTIMELONGEN";
1065 i += strlen( "OTIMELONGEN" );
1066 if ( mOrigMsg ) {
1067 const QDateTime date = mOrigMsg->date()->dateTime().dateTime();
1068 KLocale locale( "C" );
1069 const QString str = locale.formatTime( date.time(), true );
1070 plainBody.append( str );
1071 const QString body = plainToHtml( str );
1072 htmlBody.append( body );
1075 } else if ( cmd.startsWith( QLatin1String( "OTIMELONG" ) ) ) {
1076 kDebug() << "Command: OTIMELONG";
1077 i += strlen( "OTIMELONG" );
1078 if ( mOrigMsg ) {
1079 const QDateTime date = mOrigMsg->date()->dateTime().dateTime();
1080 const QString str = KGlobal::locale()->formatTime( date.time(), true );
1081 plainBody.append( str );
1082 const QString body = plainToHtml( str );
1083 htmlBody.append( body );
1086 } else if ( cmd.startsWith( QLatin1String( "OTIME" ) ) ) {
1087 kDebug() << "Command: OTIME";
1088 i += strlen( "OTIME" );
1089 if ( mOrigMsg ) {
1090 const QDateTime date = mOrigMsg->date()->dateTime().dateTime();
1091 const QString str = KGlobal::locale()->formatTime( date.time(), false );
1092 plainBody.append( str );
1093 const QString body = plainToHtml( str );
1094 htmlBody.append( body );
1097 } else if ( cmd.startsWith( QLatin1String( "BLANK" ) ) ) {
1098 // do nothing
1099 kDebug() << "Command: BLANK";
1100 i += strlen( "BLANK" );
1102 } else if ( cmd.startsWith( QLatin1String( "NOP" ) ) ) {
1103 // do nothing
1104 kDebug() << "Command: NOP";
1105 i += strlen( "NOP" );
1107 } else if ( cmd.startsWith( QLatin1String( "CLEAR" ) ) ) {
1108 // clear body buffer; not too useful yet
1109 kDebug() << "Command: CLEAR";
1110 i += strlen( "CLEAR" );
1111 plainBody = "";
1112 htmlBody = "";
1113 KMime::Headers::Generic *header =
1114 new KMime::Headers::Generic( "X-KMail-CursorPos", mMsg.get(),
1115 QString::number( 0 ), "utf-8" );
1116 mMsg->setHeader( header );
1117 } else if ( cmd.startsWith( QLatin1String( "DEBUGOFF" ) ) ) {
1118 // turn off debug
1119 kDebug() << "Command: DEBUGOFF";
1120 i += strlen( "DEBUGOFF" );
1121 mDebug = false;
1123 } else if ( cmd.startsWith( QLatin1String( "DEBUG" ) ) ) {
1124 // turn on debug
1125 kDebug() << "Command: DEBUG";
1126 i += strlen( "DEBUG" );
1127 mDebug = true;
1129 } else if ( cmd.startsWith( QLatin1String( "CURSOR" ) ) ) {
1130 // turn on debug
1131 kDebug() << "Command: CURSOR";
1132 i += strlen( "CURSOR" );
1133 KMime::Headers::Generic *header =
1134 new KMime::Headers::Generic( "X-KMail-CursorPos", mMsg.get(),
1135 QString::number( plainBody.length() ), "utf-8" );
1136 mMsg->setHeader( header );
1137 //FIXME HTML part for header remaining
1138 } else if ( cmd.startsWith( QLatin1String( "SIGNATURE" ) ) ) {
1139 kDebug() << "Command: SIGNATURE";
1140 i += strlen( "SIGNATURE" );
1141 plainBody.append( getPlainSignature() );
1142 htmlBody.append( getHtmlSignature() );
1144 } else {
1145 // wrong command, do nothing
1146 plainBody.append( c );
1147 htmlBody.append( c );
1150 } else if ( dnl && ( c == '\n' || c == '\r' ) ) {
1151 // skip
1152 if ( ( tmpl.size() > i+1 ) &&
1153 ( ( c == '\n' && tmpl[i + 1] == '\r' ) ||
1154 ( c == '\r' && tmpl[i + 1] == '\n' ) ) ) {
1155 // skip one more
1156 i += 1;
1158 dnl = false;
1159 } else {
1160 plainBody.append( c );
1161 if( c == '\n' || c == '\r' ) {
1162 htmlBody.append( QLatin1String( "<br />" ) );
1163 htmlBody.append( c );
1164 if( tmpl.size() > i+1 &&
1165 ( ( c == '\n' && tmpl[i + 1] == '\r' ) ||
1166 ( c == '\r' && tmpl[i + 1] == '\n' ) ) ) {
1167 htmlBody.append( tmpl[i + 1] );
1168 plainBody.append( tmpl[i + 1] );
1169 i += 1;
1171 } else {
1172 htmlBody.append( c );
1176 // Clear the HTML body if FORCEDPLAIN has set ReplyAsPlain, OR if,
1177 // there is no use of FORCED command but a configure setting has ReplyUsingHtml disabled,
1178 // OR the original mail has no HTML part.
1179 const KMime::Content *content = mOrigMsg->mainBodyPart( "text/html" );
1180 if( mQuotes == ReplyAsPlain ||
1181 ( mQuotes != ReplyAsHtml && !GlobalSettings::self()->replyUsingHtml() ) ||
1182 (!content || !content->hasContent() ) ) {
1183 htmlBody.clear();
1184 } else {
1185 htmlBody = makeValidHtml( htmlBody );
1187 addProcessedBodyToMessage( plainBody, htmlBody );
1190 QString TemplateParser::getPlainSignature() const
1192 const KPIMIdentities::Identity &identity =
1193 m_identityManager->identityForUoid( mIdentity );
1195 if ( identity.isNull() ) {
1196 return QString();
1199 KPIMIdentities::Signature signature =
1200 const_cast<KPIMIdentities::Identity &>( identity ).signature();
1202 if ( signature.type() == KPIMIdentities::Signature::Inlined &&
1203 signature.isInlinedHtml() ) {
1204 return signature.toPlainText();
1205 } else {
1206 return signature.rawText();
1209 // TODO If %SIGNATURE command is on, then override it with signature from
1210 // "KMail configure->General->identity->signature".
1211 // There should be no two signatures.
1212 QString TemplateParser::getHtmlSignature() const
1214 const KPIMIdentities::Identity &identity =
1215 m_identityManager->identityForUoid( mIdentity );
1216 if ( identity.isNull() ) {
1217 return QString();
1220 KPIMIdentities::Signature signature =
1221 const_cast<KPIMIdentities::Identity &>( identity ).signature();
1223 if ( !signature.isInlinedHtml() ) {
1224 Qt::escape( signature.rawText() );
1225 return signature.rawText().replace( QRegExp( "\n" ), "<br />" );
1227 return signature.rawText();
1230 void TemplateParser::addProcessedBodyToMessage( const QString &plainBody,
1231 const QString &htmlBody ) const
1233 // Get the attachments of the original mail
1234 MessageCore::AttachmentCollector ac;
1235 ac.collectAttachmentsFrom( mOrigMsg.get() );
1237 MessageCore::ImageCollector ic;
1238 ic.collectImagesFrom( mOrigMsg.get() );
1240 // Now, delete the old content and set the new content, which
1241 // is either only the new text or the new text with some attachments.
1242 KMime::Content::List parts = mMsg->contents();
1243 foreach ( KMime::Content *content, parts ) {
1244 mMsg->removeContent( content, true/*delete*/ );
1247 // Set To and CC from the template
1248 if ( !mTo.isEmpty() ) {
1249 mMsg->to()->fromUnicodeString( mMsg->to()->asUnicodeString() + ',' + mTo, "utf-8" );
1252 if ( !mCC.isEmpty() ) {
1253 mMsg->cc()->fromUnicodeString( mMsg->cc()->asUnicodeString() + ',' + mCC, "utf-8" );
1256 mMsg->contentType()->clear(); // to get rid of old boundary
1258 const QByteArray boundary = KMime::multiPartBoundary();
1259 KMime::Content *const mainTextPart =
1260 htmlBody.isEmpty() ?
1261 createPlainPartContent( plainBody ) :
1262 createMultipartAlternativeContent( plainBody, htmlBody );
1263 mainTextPart->assemble();
1265 KMime::Content *textPart = mainTextPart;
1266 if ( !ic.images().empty() ) {
1267 textPart = createMultipartRelated( ic, mainTextPart );
1268 textPart->assemble();
1271 // If we have some attachments, create a multipart/mixed mail and
1272 // add the normal body as well as the attachments
1273 KMime::Content *mainPart = textPart;
1274 if ( !ac.attachments().empty() && mMode == Forward ) {
1275 mainPart = createMultipartMixed( ac, textPart );
1276 mainPart->assemble();
1279 mMsg->setBody( mainPart->encodedBody() );
1280 mMsg->setHeader( mainPart->contentType() );
1281 mMsg->setHeader( mainPart->contentTransferEncoding() );
1282 mMsg->assemble();
1283 mMsg->parse();
1286 KMime::Content *TemplateParser::createMultipartMixed( const MessageCore::AttachmentCollector &ac,
1287 KMime::Content *textPart ) const
1289 KMime::Content *mixedPart = new KMime::Content( mMsg.get() );
1290 const QByteArray boundary = KMime::multiPartBoundary();
1291 mixedPart->contentType()->setMimeType( "multipart/mixed" );
1292 mixedPart->contentType()->setBoundary( boundary );
1293 mixedPart->contentTransferEncoding()->setEncoding( KMime::Headers::CE7Bit );
1294 mixedPart->addContent( textPart );
1296 int attachmentNumber = 1;
1297 foreach ( KMime::Content *attachment, ac.attachments() ) {
1298 mixedPart->addContent( attachment );
1299 // If the content type has no name or filename parameter, add one, since otherwise the name
1300 // would be empty in the attachment view of the composer, which looks confusing
1301 if ( attachment->contentType( false ) ) {
1302 if ( !attachment->contentType()->hasParameter( "name" ) &&
1303 !attachment->contentType()->hasParameter( "filename" ) ) {
1304 attachment->contentType()->setParameter(
1305 "name", i18nc( "@item:intext", "Attachment %1", attachmentNumber ) );
1308 attachmentNumber++;
1310 return mixedPart;
1313 KMime::Content *TemplateParser::createMultipartRelated( const MessageCore::ImageCollector &ic,
1314 KMime::Content *mainTextPart ) const
1316 KMime::Content *relatedPart = new KMime::Content( mMsg.get() );
1317 const QByteArray boundary = KMime::multiPartBoundary();
1318 relatedPart->contentType()->setMimeType( "multipart/related" );
1319 relatedPart->contentType()->setBoundary( boundary );
1320 relatedPart->contentTransferEncoding()->setEncoding( KMime::Headers::CE7Bit );
1321 relatedPart->addContent( mainTextPart );
1322 foreach ( KMime::Content *image, ic.images() ) {
1323 kWarning() << "Adding" << image->contentID() << "as an embedded image";
1324 relatedPart->addContent( image );
1326 return relatedPart;
1329 KMime::Content *TemplateParser::createPlainPartContent( const QString &plainBody ) const
1331 KMime::Content *textPart = new KMime::Content( mMsg.get() );
1332 textPart->contentType()->setMimeType( "text/plain" );
1333 QTextCodec *charset = selectCharset( m_charsets, plainBody );
1334 textPart->contentType()->setCharset( charset->name() );
1335 textPart->contentTransferEncoding()->setEncoding( KMime::Headers::CE8Bit );
1336 textPart->fromUnicodeString( plainBody );
1337 return textPart;
1340 KMime::Content *TemplateParser::createMultipartAlternativeContent( const QString &plainBody,
1341 const QString &htmlBody ) const
1343 KMime::Content *multipartAlternative = new KMime::Content( mMsg.get() );
1344 multipartAlternative->contentType()->setMimeType( "multipart/alternative" );
1345 const QByteArray boundary = KMime::multiPartBoundary();
1346 multipartAlternative->contentType()->setBoundary( boundary );
1348 KMime::Content *textPart = createPlainPartContent( plainBody );
1349 multipartAlternative->addContent( textPart );
1351 KMime::Content *htmlPart = new KMime::Content( mMsg.get() );
1352 htmlPart->contentType()->setMimeType( "text/html" );
1353 QTextCodec *charset = selectCharset( m_charsets, htmlBody );
1354 htmlPart->contentType()->setCharset( charset->name() );
1355 htmlPart->contentTransferEncoding()->setEncoding( KMime::Headers::CE8Bit );
1356 htmlPart->fromUnicodeString( htmlBody );
1357 multipartAlternative->addContent( htmlPart );
1359 return multipartAlternative;
1362 QString TemplateParser::findCustomTemplate( const QString &tmplName )
1364 #ifndef Q_OS_WINCE
1365 CTemplates t( tmplName );
1366 mTo = t.to();
1367 mCC = t.cC();
1368 const QString content = t.content();
1369 if ( !content.isEmpty() ) {
1370 return content;
1371 } else {
1372 return findTemplate();
1374 #else
1375 return findTemplate();
1376 #endif
1379 QString TemplateParser::findTemplate()
1381 // kDebug() << "Trying to find template for mode" << mode;
1383 QString tmpl;
1384 #ifndef Q_OS_WINCE
1386 #if 0
1387 if ( !mFolder.isValid() ) { // find folder message belongs to
1388 mFolder = mMsg->parentCollection();
1389 if ( !mFolder.isValid() ) {
1390 if ( mOrigMsg ) {
1391 mFolder = mOrigMsg->parentCollection();
1393 if ( !mFolder.isValid() ) {
1394 kDebug() << "Oops! No folder for message";
1398 #else
1399 kDebug() << "AKONADI PORT: Disabled code in " << Q_FUNC_INFO;
1400 #endif
1401 kDebug() << "Folder found:" << mFolder;
1402 if ( mFolder.isValid() ) { // only if a folder was found
1403 QString fid = QString::number( mFolder.id() );
1404 Templates fconf( fid );
1405 if ( fconf.useCustomTemplates() ) { // does folder use custom templates?
1406 switch( mMode ) {
1407 case NewMessage:
1408 tmpl = fconf.templateNewMessage();
1409 break;
1410 case Reply:
1411 tmpl = fconf.templateReply();
1412 break;
1413 case ReplyAll:
1414 tmpl = fconf.templateReplyAll();
1415 break;
1416 case Forward:
1417 tmpl = fconf.templateForward();
1418 break;
1419 default:
1420 kDebug() << "Unknown message mode:" << mMode;
1421 return QString();
1423 mQuoteString = fconf.quoteString();
1424 if ( !tmpl.isEmpty() ) {
1425 return tmpl; // use folder-specific template
1430 if ( !mIdentity ) { // find identity message belongs to
1431 kDebug() << "AKONADI PORT: verify Akonadi::Item() here " << Q_FUNC_INFO;
1433 mIdentity = identityUoid( mMsg );
1434 if ( !mIdentity && mOrigMsg ) {
1435 kDebug() << "AKONADI PORT: verify Akonadi::Item() here " << Q_FUNC_INFO;
1436 mIdentity = identityUoid( mOrigMsg );
1438 mIdentity = m_identityManager->identityForUoidOrDefault( mIdentity ).uoid();
1439 if ( !mIdentity ) {
1440 kDebug() << "Oops! No identity for message";
1443 kDebug() << "Identity found:" << mIdentity;
1445 QString iid;
1446 if ( mIdentity ) {
1447 iid = TemplatesConfiguration::configIdString( mIdentity ); // templates ID for that identity
1448 } else {
1449 iid = "IDENTITY_NO_IDENTITY"; // templates ID for no identity
1452 Templates iconf( iid );
1453 if ( iconf.useCustomTemplates() ) { // does identity use custom templates?
1454 switch( mMode ) {
1455 case NewMessage:
1456 tmpl = iconf.templateNewMessage();
1457 break;
1458 case Reply:
1459 tmpl = iconf.templateReply();
1460 break;
1461 case ReplyAll:
1462 tmpl = iconf.templateReplyAll();
1463 break;
1464 case Forward:
1465 tmpl = iconf.templateForward();
1466 break;
1467 default:
1468 kDebug() << "Unknown message mode:" << mMode;
1469 return QString();
1471 mQuoteString = iconf.quoteString();
1472 if ( !tmpl.isEmpty() ) {
1473 return tmpl; // use identity-specific template
1476 #endif
1478 switch( mMode ) { // use the global template
1479 case NewMessage:
1480 tmpl = GlobalSettings::self()->templateNewMessage();
1481 break;
1482 case Reply:
1483 tmpl = GlobalSettings::self()->templateReply();
1484 break;
1485 case ReplyAll:
1486 tmpl = GlobalSettings::self()->templateReplyAll();
1487 break;
1488 case Forward:
1489 tmpl = GlobalSettings::self()->templateForward();
1490 break;
1491 default:
1492 kDebug() << "Unknown message mode:" << mMode;
1493 return QString();
1496 mQuoteString = GlobalSettings::self()->quoteString();
1497 return tmpl;
1500 QString TemplateParser::pipe( const QString &cmd, const QString &buf )
1502 KProcess process;
1503 bool success;
1505 process.setOutputChannelMode( KProcess::SeparateChannels );
1506 process.setShellCommand( cmd );
1507 process.start();
1508 if ( process.waitForStarted( PipeTimeout ) ) {
1509 bool finished = false;
1510 if ( !buf.isEmpty() ) {
1511 process.write( buf.toLatin1() );
1513 if ( buf.isEmpty() || process.waitForBytesWritten( PipeTimeout ) ) {
1514 if ( !buf.isEmpty() ) {
1515 process.closeWriteChannel();
1517 if ( process.waitForFinished( PipeTimeout ) ) {
1518 success = ( process.exitStatus() == QProcess::NormalExit );
1519 finished = true;
1520 } else {
1521 finished = false;
1522 success = false;
1524 } else {
1525 success = false;
1526 finished = false;
1529 // The process has started, but did not finish in time. Kill it.
1530 if ( !finished ) {
1531 process.kill();
1533 } else {
1534 success = false;
1537 if ( !success && mDebug ) {
1538 KMessageBox::error(
1540 i18nc( "@info",
1541 "Pipe command <command>%1</command> failed.", cmd ) );
1544 if ( success ) {
1545 return process.readAllStandardOutput();
1546 } else {
1547 return QString();
1551 void TemplateParser::setWordWrap( bool wrap, int wrapColWidth )
1553 mWrap = wrap;
1554 mColWrap = wrapColWidth;
1557 QString TemplateParser::plainMessageText( bool aStripSignature,
1558 AllowSelection isSelectionAllowed ) const
1560 if ( !mSelection.isEmpty() && ( isSelectionAllowed == SelectionAllowed ) ) {
1561 return mSelection;
1564 if ( !mOrigMsg ) {
1565 return QString();
1568 QString result = mOtp->plainTextContent();
1570 if ( result.isEmpty() ) { //HTML-only mails
1571 result = mOtp->convertedTextContent();
1574 if ( aStripSignature ) {
1575 result = MessageCore::StringUtil::stripSignature( result );
1578 return result;
1581 QString TemplateParser::htmlMessageText( bool aStripSignature, AllowSelection isSelectionAllowed )
1583 if( !mSelection.isEmpty() && ( isSelectionAllowed == SelectionAllowed ) ) {
1584 //TODO implement mSelection for HTML
1585 return mSelection;
1588 QString htmlElement = mOtp->htmlContent();
1590 if ( htmlElement.isEmpty() ) { //plain mails only
1591 htmlElement = mOtp->convertedHtmlContent();
1594 QWebPage page;
1595 page.settings()->setAttribute( QWebSettings::JavascriptEnabled, false );
1596 page.settings()->setAttribute( QWebSettings::JavaEnabled, false );
1597 page.settings()->setAttribute( QWebSettings::PluginsEnabled, false );
1598 page.settings()->setAttribute( QWebSettings::AutoLoadImages, false );
1600 page.currentFrame()->setHtml( htmlElement );
1602 //TODO to be tested/verified if this is not an issue
1603 page.settings()->setAttribute( QWebSettings::JavascriptEnabled, true );
1604 const QString bodyElement = page.currentFrame()->evaluateJavaScript(
1605 "document.getElementsByTagName('body')[0].innerHTML" ).toString();
1607 mHeadElement = page.currentFrame()->evaluateJavaScript(
1608 "document.getElementsByTagName('head')[0].innerHTML" ).toString();
1610 page.settings()->setAttribute( QWebSettings::JavascriptEnabled, false );
1612 if( !bodyElement.isEmpty() ) {
1613 if ( aStripSignature ) {
1614 //FIXME strip signature works partially for HTML mails
1615 return MessageCore::StringUtil::stripSignature( bodyElement );
1617 return bodyElement;
1620 if ( aStripSignature ) {
1621 //FIXME strip signature works partially for HTML mails
1622 return MessageCore::StringUtil::stripSignature( htmlElement );
1624 return htmlElement;
1627 QString TemplateParser::quotedPlainText( const QString &selection ) const
1629 QString content = selection;
1630 // Remove blank lines at the beginning:
1631 const int firstNonWS = content.indexOf( QRegExp( "\\S" ) );
1632 const int lineStart = content.lastIndexOf( '\n', firstNonWS );
1633 if ( lineStart >= 0 ) {
1634 content.remove( 0, static_cast<unsigned int>( lineStart ) );
1637 const QString indentStr =
1638 MessageCore::StringUtil::formatString( mQuoteString, mOrigMsg->from()->asUnicodeString() );
1639 #ifndef Q_OS_WINCE
1640 if ( GlobalSettings::self()->smartQuote() && mWrap ) {
1641 content = MessageCore::StringUtil::smartQuote( content, mColWrap - indentStr.length() );
1643 #endif
1644 content.replace( '\n', '\n' + indentStr );
1645 content.prepend( indentStr );
1646 content += '\n';
1648 return content;
1651 QString TemplateParser::quotedHtmlText( const QString &selection ) const
1653 QString content = selection;
1654 //TODO 1) look for all the variations of <br> and remove the blank lines
1655 //2) implement vertical bar for quoted HTML mail.
1656 //3) After vertical bar is implemented, If a user wants to edit quoted message,
1657 // then the <blockquote> tags below should open and close as when required.
1659 //Add blockquote tag, so that quoted message can be differentiated from normal message
1660 content = "<blockquote>" + content + "</blockquote>";
1661 return content;
1664 uint TemplateParser::identityUoid( const KMime::Message::Ptr &msg ) const
1666 QString idString;
1667 if ( msg->headerByType( "X-KMail-Identity" ) ) {
1668 idString = msg->headerByType( "X-KMail-Identity" )->asUnicodeString().trimmed();
1670 bool ok = false;
1671 int id = idString.toUInt( &ok );
1673 if ( !ok || id == 0 ) {
1674 id = m_identityManager->identityForAddress(
1675 msg->to()->asUnicodeString() + ", " + msg->cc()->asUnicodeString() ).uoid();
1678 return id;
1681 bool TemplateParser::isHtmlSignature() const
1683 const KPIMIdentities::Identity &identity =
1684 m_identityManager->identityForUoid( mIdentity );
1686 if ( identity.isNull() ) {
1687 return false;
1690 const KPIMIdentities::Signature signature =
1691 const_cast<KPIMIdentities::Identity &>( identity ).signature();
1693 return signature.isInlinedHtml();
1696 QString TemplateParser::plainToHtml( const QString &body ) const
1698 QString str = body;
1699 str = Qt::escape( str );
1700 str.replace( QRegExp( "\n" ), "<br />\n" );
1701 return str;
1704 //TODO implement this function using a DOM tree parser
1705 QString TemplateParser::makeValidHtml( QString &body )
1707 QRegExp regEx;
1708 regEx.setMinimal( true );
1709 regEx.setPattern( "<html.*>" );
1711 if ( !body.isEmpty() && !body.contains( regEx ) ) {
1712 regEx.setPattern( "<body.*>" );
1713 if ( !body.contains( regEx ) ) {
1714 body = "<body>" + body + "<br/></body>";
1716 regEx.setPattern( "<head.*>" );
1717 if ( !body.contains( regEx ) ) {
1718 body = "<head>" + mHeadElement +"</head>" + body;
1720 body = "<html>" + body + "</html>";
1722 return body;
1727 #include "templateparser.moc"