kmail

templateparser.cpp
1/*
2 * kmail: KDE mail client
3 * This file: Copyright (C) 2006 Dmitry Morozhnikov <dmiceman@mail.ru>
4 *
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.
9 *
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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 */
20
21#include <config.h>
22
23#include <tqstring.h>
24#include <tqdatetime.h>
25#include <tdelocale.h>
26#include <kcalendarsystem.h>
27#include <kmime_util.h>
28#include <tdeglobal.h>
29#include <tdeprocess.h>
30#include <tqregexp.h>
31#include <tqfile.h>
32#include <tdemessagebox.h>
33#include <kshell.h>
34#include <tqfileinfo.h>
35
36#include "kmmessage.h"
37#include "kmmsgbase.h"
38#include "kmfolder.h"
39#include "templatesconfiguration.h"
40#include "templatesconfiguration_kfg.h"
41#include "customtemplates_kfg.h"
42#include "globalsettings_base.h"
43#include "kmkernel.h"
44#include <libkpimidentities/identity.h>
45#include <libkpimidentities/identitymanager.h>
46#include "partNode.h"
47#include "attachmentcollector.h"
48#include "objecttreeparser.h"
49#include "util.h"
50
51#include "templateparser.h"
52#include <mimelib/bodypart.h>
53
54using namespace KMail;
55
56TemplateParser::TemplateParser( KMMessage *amsg, const Mode amode ) :
57 mMode( amode ), mFolder( 0 ), mIdentity( 0 ),
58 mAllowDecryption( false ),
59 mDebug( false ), mQuoteString( "> " ), mAppend( false ), mOrigRoot( 0 )
60{
61 mMsg = amsg;
62}
63
64void TemplateParser::setSelection( const TQString &selection )
65{
66 mSelection = selection;
67}
68
69void TemplateParser::setAllowDecryption( const bool allowDecryption )
70{
71 mAllowDecryption = allowDecryption;
72}
73
75{
76 // Only strip the signature when replying, it should be preserved when forwarding
77 return ( mMode == Reply || mMode == ReplyAll) && GlobalSettings::stripSignature();
78}
79
80TemplateParser::~TemplateParser()
81{
82 delete mOrigRoot;
83 mOrigRoot = 0;
84}
85
86int TemplateParser::parseQuotes( const TQString &prefix, const TQString &str,
87 TQString &quote ) const
88{
89 int pos = prefix.length();
90 int len;
91 int str_len = str.length();
92 TQChar qc = '"';
93 TQChar prev = 0;
94
95 pos++;
96 len = pos;
97
98 while ( pos < str_len ) {
99 TQChar c = str[pos];
100
101 pos++;
102 len++;
103
104 if ( prev ) {
105 quote.append( c );
106 prev = 0;
107 } else {
108 if ( c == '\\' ) {
109 prev = c;
110 } else if ( c == qc ) {
111 break;
112 } else {
113 quote.append( c );
114 }
115 }
116 }
117
118 return len;
119}
120
121TQString TemplateParser::getFName( const TQString &str )
122{
123 // simple logic:
124 // if there is ',' in name, than format is 'Last, First'
125 // else format is 'First Last'
126 // last resort -- return 'name' from 'name@domain'
127 int sep_pos;
128 TQString res;
129 if ( ( sep_pos = str.find( '@' ) ) > 0 ) {
130 int i;
131 for ( i = (sep_pos - 1); i >= 0; --i ) {
132 TQChar c = str[i];
133 if ( c.isLetterOrNumber() ) {
134 res.prepend( c );
135 } else {
136 break;
137 }
138 }
139 } else if ( ( sep_pos = str.find(',') ) > 0 ) {
140 unsigned int i;
141 bool begin = false;
142 for ( i = sep_pos; i < str.length(); ++i ) {
143 TQChar c = str[i];
144 if ( c.isLetterOrNumber() ) {
145 begin = true;
146 res.append( c );
147 } else if ( begin ) {
148 break;
149 }
150 }
151 } else {
152 unsigned int i;
153 for ( i = 0; i < str.length(); ++i ) {
154 TQChar c = str[i];
155 if ( c.isLetterOrNumber() ) {
156 res.append( c );
157 } else {
158 break;
159 }
160 }
161 }
162 return res;
163}
164
165TQString TemplateParser::getLName( const TQString &str )
166{
167 // simple logic:
168 // if there is ',' in name, than format is 'Last, First'
169 // else format is 'First Last'
170 int sep_pos;
171 TQString res;
172 if ( ( sep_pos = str.find(',') ) > 0 ) {
173 int i;
174 for ( i = sep_pos; i >= 0; --i ) {
175 TQChar c = str[i];
176 if ( c.isLetterOrNumber() ) {
177 res.prepend( c );
178 } else {
179 break;
180 }
181 }
182 } else {
183 if ( ( sep_pos = str.find( ' ' ) ) > 0 ) {
184 unsigned int i;
185 bool begin = false;
186 for ( i = sep_pos; i < str.length(); ++i ) {
187 TQChar c = str[i];
188 if ( c.isLetterOrNumber() ) {
189 begin = true;
190 res.append( c );
191 } else if ( begin ) {
192 break;
193 }
194 }
195 }
196 }
197 return res;
198}
199
200void TemplateParser::process( KMMessage *aorig_msg, KMFolder *afolder, bool append )
201{
202 mAppend = append;
203 mOrigMsg = aorig_msg;
204 mFolder = afolder;
205 TQString tmpl = findTemplate();
206 return processWithTemplate( tmpl );
207}
208
209void TemplateParser::process( const TQString &tmplName, KMMessage *aorig_msg,
210 KMFolder *afolder, bool append )
211{
212 mAppend = append;
213 mOrigMsg = aorig_msg;
214 mFolder = afolder;
215 TQString tmpl = findCustomTemplate( tmplName );
216 return processWithTemplate( tmpl );
217}
218
219void TemplateParser::processWithTemplate( const TQString &tmpl )
220{
221 TQString body;
222 int tmpl_len = tmpl.length();
223 bool dnl = false;
224 for ( int i = 0; i < tmpl_len; ++i ) {
225 TQChar c = tmpl[i];
226 // kdDebug() << "Next char: " << c << endl;
227 if ( c == '%' ) {
228 TQString cmd = tmpl.mid( i + 1 );
229
230 if ( cmd.startsWith( "-" ) ) {
231 // dnl
232 kdDebug() << "Command: -" << endl;
233 dnl = true;
234 i += 1;
235
236 } else if ( cmd.startsWith( "REM=" ) ) {
237 // comments
238 kdDebug() << "Command: REM=" << endl;
239 TQString q;
240 int len = parseQuotes( "REM=", cmd, q );
241 i += len;
242
243 } else if ( cmd.startsWith( "INSERT=" ) ) {
244 // insert content of specified file as is
245 kdDebug() << "Command: INSERT=" << endl;
246 TQString q;
247 int len = parseQuotes( "INSERT=", cmd, q );
248 i += len;
249 TQString path = KShell::tildeExpand( q );
250 TQFileInfo finfo( path );
251 if (finfo.isRelative() ) {
252 path = KShell::homeDir( "" );
253 path += '/';
254 path += q;
255 }
256 TQFile file( path );
257 if ( file.open( IO_ReadOnly ) ) {
258 TQByteArray content = file.readAll();
259 TQString str = TQString::fromLocal8Bit( content, content.size() );
260 body.append( str );
261 } else if ( mDebug ) {
262 KMessageBox::error( 0,
263 i18n( "Cannot insert content from file %1: %2" ).
264 arg( path ).arg( file.errorString() ) );
265 }
266
267 } else if ( cmd.startsWith( "SYSTEM=" ) ) {
268 // insert content of specified file as is
269 kdDebug() << "Command: SYSTEM=" << endl;
270 TQString q;
271 int len = parseQuotes( "SYSTEM=", cmd, q );
272 i += len;
273 TQString pipe_cmd = q;
274 TQString str = pipe( pipe_cmd, "" );
275 body.append( str );
276
277 } else if ( cmd.startsWith( "PUT=" ) ) {
278 // insert content of specified file as is
279 kdDebug() << "Command: PUT=" << endl;
280 TQString q;
281 int len = parseQuotes( "PUT=", cmd, q );
282 i += len;
283 TQString path = KShell::tildeExpand( q );
284 TQFileInfo finfo( path );
285 if (finfo.isRelative() ) {
286 path = KShell::homeDir( "" );
287 path += '/';
288 path += q;
289 }
290 TQFile file( path );
291 if ( file.open( IO_ReadOnly ) ) {
292 TQByteArray content = file.readAll();
293 body.append( TQString::fromLocal8Bit( content, content.size() ) );
294 } else if ( mDebug ) {
295 KMessageBox::error( 0,
296 i18n( "Cannot insert content from file %1: %2").
297 arg( path ).arg(file.errorString() ));
298 }
299
300 } else if ( cmd.startsWith( "QUOTEPIPE=" ) ) {
301 // pipe message body throw command and insert it as quotation
302 kdDebug() << "Command: QUOTEPIPE=" << endl;
303 TQString q;
304 int len = parseQuotes( "QUOTEPIPE=", cmd, q );
305 i += len;
306 TQString pipe_cmd = q;
307 if ( mOrigMsg ) {
308 TQString str = pipe( pipe_cmd, messageText( false ) );
309 TQString quote = mOrigMsg->asQuotedString( "", mQuoteString, str,
310 shouldStripSignature(), mAllowDecryption );
311 body.append( quote );
312 }
313
314 } else if ( cmd.startsWith( "QUOTE" ) ) {
315 kdDebug() << "Command: QUOTE" << endl;
316 i += strlen( "QUOTE" );
317 if ( mOrigMsg ) {
318 TQString quote = mOrigMsg->asQuotedString( "", mQuoteString, messageText( true ),
319 shouldStripSignature(), mAllowDecryption );
320 body.append( quote );
321 }
322
323 } else if ( cmd.startsWith( "QHEADERS" ) ) {
324 kdDebug() << "Command: TQHEADERS" << endl;
325 i += strlen( "QHEADERS" );
326 if ( mOrigMsg ) {
327 TQString quote = mOrigMsg->asQuotedString( "", mQuoteString,
328 mOrigMsg->headerAsSendableString(),
329 false, false );
330 body.append( quote );
331 }
332
333 } else if ( cmd.startsWith( "HEADERS" ) ) {
334 kdDebug() << "Command: HEADERS" << endl;
335 i += strlen( "HEADERS" );
336 if ( mOrigMsg ) {
337 TQString str = mOrigMsg->headerAsSendableString();
338 body.append( str );
339 }
340
341 } else if ( cmd.startsWith( "TEXTPIPE=" ) ) {
342 // pipe message body throw command and insert it as is
343 kdDebug() << "Command: TEXTPIPE=" << endl;
344 TQString q;
345 int len = parseQuotes( "TEXTPIPE=", cmd, q );
346 i += len;
347 TQString pipe_cmd = q;
348 if ( mOrigMsg ) {
349 TQString str = pipe(pipe_cmd, messageText( false ) );
350 body.append( str );
351 }
352
353 } else if ( cmd.startsWith( "MSGPIPE=" ) ) {
354 // pipe full message throw command and insert result as is
355 kdDebug() << "Command: MSGPIPE=" << endl;
356 TQString q;
357 int len = parseQuotes( "MSGPIPE=", cmd, q );
358 i += len;
359 TQString pipe_cmd = q;
360 if ( mOrigMsg ) {
361 TQString str = pipe(pipe_cmd, mOrigMsg->asString() );
362 body.append( str );
363 }
364
365 } else if ( cmd.startsWith( "BODYPIPE=" ) ) {
366 // pipe message body generated so far throw command and insert result as is
367 kdDebug() << "Command: BODYPIPE=" << endl;
368 TQString q;
369 int len = parseQuotes( "BODYPIPE=", cmd, q );
370 i += len;
371 TQString pipe_cmd = q;
372 TQString str = pipe( pipe_cmd, body );
373 body.append( str );
374
375 } else if ( cmd.startsWith( "CLEARPIPE=" ) ) {
376 // pipe message body generated so far throw command and
377 // insert result as is replacing current body
378 kdDebug() << "Command: CLEARPIPE=" << endl;
379 TQString q;
380 int len = parseQuotes( "CLEARPIPE=", cmd, q );
381 i += len;
382 TQString pipe_cmd = q;
383 TQString str = pipe( pipe_cmd, body );
384 body = str;
385 mMsg->setCursorPos( 0 );
386
387 } else if ( cmd.startsWith( "TEXT" ) ) {
388 kdDebug() << "Command: TEXT" << endl;
389 i += strlen( "TEXT" );
390 if ( mOrigMsg ) {
391 TQString quote = messageText( false );
392 body.append( quote );
393 }
394
395 } else if ( cmd.startsWith( "OTEXTSIZE" ) ) {
396 kdDebug() << "Command: OTEXTSIZE" << endl;
397 i += strlen( "OTEXTSIZE" );
398 if ( mOrigMsg ) {
399 TQString str = TQString( "%1" ).arg( mOrigMsg->body().length() );
400 body.append( str );
401 }
402
403 } else if ( cmd.startsWith( "OTEXT" ) ) {
404 kdDebug() << "Command: OTEXT" << endl;
405 i += strlen( "OTEXT" );
406 if ( mOrigMsg ) {
407 TQString quote = messageText( false );
408 body.append( quote );
409 }
410
411 } else if ( cmd.startsWith( "OADDRESSEESADDR" ) ) {
412 kdDebug() << "Command: OADDRESSEESADDR" << endl;
413 i += strlen( "OADDRESSEESADDR" );
414 const TQString to = mOrigMsg->to();
415 const TQString cc = mOrigMsg->cc();
416 if ( !to.isEmpty() )
417 body.append( i18n( "To:" ) + ' ' + to );
418 if ( !to.isEmpty() && !cc.isEmpty() )
419 body.append( '\n' );
420 if ( !cc.isEmpty() )
421 body.append( i18n( "CC:" ) + ' ' + cc );
422
423 } else if ( cmd.startsWith( "CCADDR" ) ) {
424 kdDebug() << "Command: CCADDR" << endl;
425 i += strlen( "CCADDR" );
426 TQString str = mMsg->cc();
427 body.append( str );
428
429 } else if ( cmd.startsWith( "CCNAME" ) ) {
430 kdDebug() << "Command: CCNAME" << endl;
431 i += strlen( "CCNAME" );
432 TQString str = mMsg->ccStrip();
433 body.append( str );
434
435 } else if ( cmd.startsWith( "CCFNAME" ) ) {
436 kdDebug() << "Command: CCFNAME" << endl;
437 i += strlen( "CCFNAME" );
438 TQString str = mMsg->ccStrip();
439 body.append( getFName( str ) );
440
441 } else if ( cmd.startsWith( "CCLNAME" ) ) {
442 kdDebug() << "Command: CCLNAME" << endl;
443 i += strlen( "CCLNAME" );
444 TQString str = mMsg->ccStrip();
445 body.append( getLName( str ) );
446
447 } else if ( cmd.startsWith( "TOADDR" ) ) {
448 kdDebug() << "Command: TOADDR" << endl;
449 i += strlen( "TOADDR" );
450 TQString str = mMsg->to();
451 body.append( str );
452
453 } else if ( cmd.startsWith( "TONAME" ) ) {
454 kdDebug() << "Command: TONAME" << endl;
455 i += strlen( "TONAME" );
456 TQString str = mMsg->toStrip();
457 body.append( str );
458
459 } else if ( cmd.startsWith( "TOFNAME" ) ) {
460 kdDebug() << "Command: TOFNAME" << endl;
461 i += strlen( "TOFNAME" );
462 TQString str = mMsg->toStrip();
463 body.append( getFName( str ) );
464
465 } else if ( cmd.startsWith( "TOLNAME" ) ) {
466 kdDebug() << "Command: TOLNAME" << endl;
467 i += strlen( "TOLNAME" );
468 TQString str = mMsg->toStrip();
469 body.append( getLName( str ) );
470
471 } else if ( cmd.startsWith( "TOLIST" ) ) {
472 kdDebug() << "Command: TOLIST" << endl;
473 i += strlen( "TOLIST" );
474 TQString str = mMsg->to();
475 body.append( str );
476
477 } else if ( cmd.startsWith( "FROMADDR" ) ) {
478 kdDebug() << "Command: FROMADDR" << endl;
479 i += strlen( "FROMADDR" );
480 TQString str = mMsg->from();
481 body.append( str );
482
483 } else if ( cmd.startsWith( "FROMNAME" ) ) {
484 kdDebug() << "Command: FROMNAME" << endl;
485 i += strlen( "FROMNAME" );
486 TQString str = mMsg->fromStrip();
487 body.append( str );
488
489 } else if ( cmd.startsWith( "FROMFNAME" ) ) {
490 kdDebug() << "Command: FROMFNAME" << endl;
491 i += strlen( "FROMFNAME" );
492 TQString str = mMsg->fromStrip();
493 body.append( getFName( str ) );
494
495 } else if ( cmd.startsWith( "FROMLNAME" ) ) {
496 kdDebug() << "Command: FROMLNAME" << endl;
497 i += strlen( "FROMLNAME" );
498 TQString str = mMsg->fromStrip();
499 body.append( getLName( str ) );
500
501 } else if ( cmd.startsWith( "FULLSUBJECT" ) ) {
502 kdDebug() << "Command: FULLSUBJECT" << endl;
503 i += strlen( "FULLSUBJECT" );
504 TQString str = mMsg->subject();
505 body.append( str );
506
507 } else if ( cmd.startsWith( "FULLSUBJ" ) ) {
508 kdDebug() << "Command: FULLSUBJ" << endl;
509 i += strlen( "FULLSUBJ" );
510 TQString str = mMsg->subject();
511 body.append( str );
512
513 } else if ( cmd.startsWith( "MSGID" ) ) {
514 kdDebug() << "Command: MSGID" << endl;
515 i += strlen( "MSGID" );
516 TQString str = mMsg->id();
517 body.append( str );
518
519 } else if ( cmd.startsWith( "OHEADER=" ) ) {
520 // insert specified content of header from original message
521 kdDebug() << "Command: OHEADER=" << endl;
522 TQString q;
523 int len = parseQuotes( "OHEADER=", cmd, q );
524 i += len;
525 if ( mOrigMsg ) {
526 TQString hdr = q;
527 TQString str = mOrigMsg->headerFields(hdr.local8Bit() ).join( ", " );
528 body.append( str );
529 }
530
531 } else if ( cmd.startsWith( "HEADER=" ) ) {
532 // insert specified content of header from current message
533 kdDebug() << "Command: HEADER=" << endl;
534 TQString q;
535 int len = parseQuotes( "HEADER=", cmd, q );
536 i += len;
537 TQString hdr = q;
538 TQString str = mMsg->headerFields(hdr.local8Bit() ).join( ", " );
539 body.append( str );
540
541 } else if ( cmd.startsWith( "HEADER( " ) ) {
542 // insert specified content of header from current message
543 kdDebug() << "Command: HEADER( " << endl;
544 TQRegExp re = TQRegExp( "^HEADER\\((.+)\\)" );
545 re.setMinimal( true );
546 int res = re.search( cmd );
547 if ( res != 0 ) {
548 // something wrong
549 i += strlen( "HEADER( " );
550 } else {
551 i += re.matchedLength();
552 TQString hdr = re.cap( 1 );
553 TQString str = mMsg->headerFields( hdr.local8Bit() ).join( ", " );
554 body.append( str );
555 }
556
557 } else if ( cmd.startsWith( "OCCADDR" ) ) {
558 kdDebug() << "Command: OCCADDR" << endl;
559 i += strlen( "OCCADDR" );
560 if ( mOrigMsg ) {
561 TQString str = mOrigMsg->cc();
562 body.append( str );
563 }
564
565 } else if ( cmd.startsWith( "OCCNAME" ) ) {
566 kdDebug() << "Command: OCCNAME" << endl;
567 i += strlen( "OCCNAME" );
568 if ( mOrigMsg ) {
569 TQString str = mOrigMsg->ccStrip();
570 body.append( str );
571 }
572
573 } else if ( cmd.startsWith( "OCCFNAME" ) ) {
574 kdDebug() << "Command: OCCFNAME" << endl;
575 i += strlen( "OCCFNAME" );
576 if ( mOrigMsg ) {
577 TQString str = mOrigMsg->ccStrip();
578 body.append( getFName( str ) );
579 }
580
581 } else if ( cmd.startsWith( "OCCLNAME" ) ) {
582 kdDebug() << "Command: OCCLNAME" << endl;
583 i += strlen( "OCCLNAME" );
584 if ( mOrigMsg ) {
585 TQString str = mOrigMsg->ccStrip();
586 body.append( getLName( str ) );
587 }
588
589 } else if ( cmd.startsWith( "OTOADDR" ) ) {
590 kdDebug() << "Command: OTOADDR" << endl;
591 i += strlen( "OTOADDR" );
592 if ( mOrigMsg ) {
593 TQString str = mOrigMsg->to();
594 body.append( str );
595 }
596
597 } else if ( cmd.startsWith( "OTONAME" ) ) {
598 kdDebug() << "Command: OTONAME" << endl;
599 i += strlen( "OTONAME" );
600 if ( mOrigMsg ) {
601 TQString str = mOrigMsg->toStrip();
602 body.append( str );
603 }
604
605 } else if ( cmd.startsWith( "OTOFNAME" ) ) {
606 kdDebug() << "Command: OTOFNAME" << endl;
607 i += strlen( "OTOFNAME" );
608 if ( mOrigMsg ) {
609 TQString str = mOrigMsg->toStrip();
610 body.append( getFName( str ) );
611 }
612
613 } else if ( cmd.startsWith( "OTOLNAME" ) ) {
614 kdDebug() << "Command: OTOLNAME" << endl;
615 i += strlen( "OTOLNAME" );
616 if ( mOrigMsg ) {
617 TQString str = mOrigMsg->toStrip();
618 body.append( getLName( str ) );
619 }
620
621 } else if ( cmd.startsWith( "OTOLIST" ) ) {
622 kdDebug() << "Command: OTOLIST" << endl;
623 i += strlen( "OTOLIST" );
624 if ( mOrigMsg ) {
625 TQString str = mOrigMsg->to();
626 body.append( str );
627 }
628
629 } else if ( cmd.startsWith( "OTO" ) ) {
630 kdDebug() << "Command: OTO" << endl;
631 i += strlen( "OTO" );
632 if ( mOrigMsg ) {
633 TQString str = mOrigMsg->to();
634 body.append( str );
635 }
636
637 } else if ( cmd.startsWith( "OFROMADDR" ) ) {
638 kdDebug() << "Command: OFROMADDR" << endl;
639 i += strlen( "OFROMADDR" );
640 if ( mOrigMsg ) {
641 TQString str = mOrigMsg->from();
642 body.append( str );
643 }
644
645 } else if ( cmd.startsWith( "OFROMNAME" ) ) {
646 kdDebug() << "Command: OFROMNAME" << endl;
647 i += strlen( "OFROMNAME" );
648 if ( mOrigMsg ) {
649 TQString str = mOrigMsg->fromStrip();
650 body.append( str );
651 }
652
653 } else if ( cmd.startsWith( "OFROMFNAME" ) ) {
654 kdDebug() << "Command: OFROMFNAME" << endl;
655 i += strlen( "OFROMFNAME" );
656 if ( mOrigMsg ) {
657 TQString str = mOrigMsg->fromStrip();
658 body.append( getFName( str ) );
659 }
660
661 } else if ( cmd.startsWith( "OFROMLNAME" ) ) {
662 kdDebug() << "Command: OFROMLNAME" << endl;
663 i += strlen( "OFROMLNAME" );
664 if ( mOrigMsg ) {
665 TQString str = mOrigMsg->fromStrip();
666 body.append( getLName( str ) );
667 }
668
669 } else if ( cmd.startsWith( "OFULLSUBJECT" ) ) {
670 kdDebug() << "Command: OFULLSUBJECT" << endl;
671 i += strlen( "OFULLSUBJECT" );
672 if ( mOrigMsg ) {
673 TQString str = mOrigMsg->subject();
674 body.append( str );
675 }
676
677 } else if ( cmd.startsWith( "OFULLSUBJ" ) ) {
678 kdDebug() << "Command: OFULLSUBJ" << endl;
679 i += strlen( "OFULLSUBJ" );
680 if ( mOrigMsg ) {
681 TQString str = mOrigMsg->subject();
682 body.append( str );
683 }
684
685 } else if ( cmd.startsWith( "OMSGID" ) ) {
686 kdDebug() << "Command: OMSGID" << endl;
687 i += strlen( "OMSGID" );
688 if ( mOrigMsg ) {
689 TQString str = mOrigMsg->id();
690 body.append( str );
691 }
692
693 } else if ( cmd.startsWith( "DATEEN" ) ) {
694 kdDebug() << "Command: DATEEN" << endl;
695 i += strlen( "DATEEN" );
696 TQDateTime date = TQDateTime::currentDateTime();
697 TDELocale locale( "C" );
698 TQString str = locale.formatDate( date.date(), false );
699 body.append( str );
700
701 } else if ( cmd.startsWith( "DATESHORT" ) ) {
702 kdDebug() << "Command: DATESHORT" << endl;
703 i += strlen( "DATESHORT" );
704 TQDateTime date = TQDateTime::currentDateTime();
705 TQString str = TDEGlobal::locale()->formatDate( date.date(), true );
706 body.append( str );
707
708 } else if ( cmd.startsWith( "DATE" ) ) {
709 kdDebug() << "Command: DATE" << endl;
710 i += strlen( "DATE" );
711 TQDateTime date = TQDateTime::currentDateTime();
712 TQString str = TDEGlobal::locale()->formatDate( date.date(), false );
713 body.append( str );
714
715 } else if ( cmd.startsWith( "DOW" ) ) {
716 kdDebug() << "Command: DOW" << endl;
717 i += strlen( "DOW" );
718 TQDateTime date = TQDateTime::currentDateTime();
719 TQString str = TDEGlobal::locale()->calendar()->weekDayName( date.date(), false );
720 body.append( str );
721
722 } else if ( cmd.startsWith( "TIMELONGEN" ) ) {
723 kdDebug() << "Command: TIMELONGEN" << endl;
724 i += strlen( "TIMELONGEN" );
725 TQDateTime date = TQDateTime::currentDateTime();
726 TDELocale locale( "C");
727 TQString str = locale.formatTime( date.time(), true );
728 body.append( str );
729
730 } else if ( cmd.startsWith( "TIMELONG" ) ) {
731 kdDebug() << "Command: TIMELONG" << endl;
732 i += strlen( "TIMELONG" );
733 TQDateTime date = TQDateTime::currentDateTime();
734 TQString str = TDEGlobal::locale()->formatTime( date.time(), true );
735 body.append( str );
736
737 } else if ( cmd.startsWith( "TIME" ) ) {
738 kdDebug() << "Command: TIME" << endl;
739 i += strlen( "TIME" );
740 TQDateTime date = TQDateTime::currentDateTime();
741 TQString str = TDEGlobal::locale()->formatTime( date.time(), false );
742 body.append( str );
743
744 } else if ( cmd.startsWith( "ODATEEN" ) ) {
745 kdDebug() << "Command: ODATEEN" << endl;
746 i += strlen( "ODATEEN" );
747 if ( mOrigMsg ) {
748 TQDateTime date;
749 date.setTime_t( mOrigMsg->date() );
750 TDELocale locale( "C");
751 TQString str = locale.formatDate( date.date(), false );
752 body.append( str );
753 }
754
755 } else if ( cmd.startsWith( "ODATESHORT") ) {
756 kdDebug() << "Command: ODATESHORT" << endl;
757 i += strlen( "ODATESHORT");
758 if ( mOrigMsg ) {
759 TQDateTime date;
760 date.setTime_t( mOrigMsg->date() );
761 TQString str = TDEGlobal::locale()->formatDate( date.date(), true );
762 body.append( str );
763 }
764
765 } else if ( cmd.startsWith( "ODATE") ) {
766 kdDebug() << "Command: ODATE" << endl;
767 i += strlen( "ODATE");
768 if ( mOrigMsg ) {
769 TQDateTime date;
770 date.setTime_t( mOrigMsg->date() );
771 TQString str = TDEGlobal::locale()->formatDate( date.date(), false );
772 body.append( str );
773 }
774
775 } else if ( cmd.startsWith( "ODOW") ) {
776 kdDebug() << "Command: ODOW" << endl;
777 i += strlen( "ODOW");
778 if ( mOrigMsg ) {
779 TQDateTime date;
780 date.setTime_t( mOrigMsg->date() );
781 TQString str = TDEGlobal::locale()->calendar()->weekDayName( date.date(), false );
782 body.append( str );
783 }
784
785 } else if ( cmd.startsWith( "OTIMELONGEN") ) {
786 kdDebug() << "Command: OTIMELONGEN" << endl;
787 i += strlen( "OTIMELONGEN");
788 if ( mOrigMsg ) {
789 TQDateTime date;
790 date.setTime_t( mOrigMsg->date() );
791 TDELocale locale( "C");
792 TQString str = locale.formatTime( date.time(), true );
793 body.append( str );
794 }
795
796 } else if ( cmd.startsWith( "OTIMELONG") ) {
797 kdDebug() << "Command: OTIMELONG" << endl;
798 i += strlen( "OTIMELONG");
799 if ( mOrigMsg ) {
800 TQDateTime date;
801 date.setTime_t( mOrigMsg->date() );
802 TQString str = TDEGlobal::locale()->formatTime( date.time(), true );
803 body.append( str );
804 }
805
806 } else if ( cmd.startsWith( "OTIME") ) {
807 kdDebug() << "Command: OTIME" << endl;
808 i += strlen( "OTIME");
809 if ( mOrigMsg ) {
810 TQDateTime date;
811 date.setTime_t( mOrigMsg->date() );
812 TQString str = TDEGlobal::locale()->formatTime( date.time(), false );
813 body.append( str );
814 }
815
816 } else if ( cmd.startsWith( "BLANK" ) ) {
817 // do nothing
818 kdDebug() << "Command: BLANK" << endl;
819 i += strlen( "BLANK" );
820
821 } else if ( cmd.startsWith( "NOP" ) ) {
822 // do nothing
823 kdDebug() << "Command: NOP" << endl;
824 i += strlen( "NOP" );
825
826 } else if ( cmd.startsWith( "CLEAR" ) ) {
827 // clear body buffer; not too useful yet
828 kdDebug() << "Command: CLEAR" << endl;
829 i += strlen( "CLEAR" );
830 body = "";
831 mMsg->setCursorPos( 0 );
832
833 } else if ( cmd.startsWith( "DEBUGOFF" ) ) {
834 // turn off debug
835 kdDebug() << "Command: DEBUGOFF" << endl;
836 i += strlen( "DEBUGOFF" );
837 mDebug = false;
838
839 } else if ( cmd.startsWith( "DEBUG" ) ) {
840 // turn on debug
841 kdDebug() << "Command: DEBUG" << endl;
842 i += strlen( "DEBUG" );
843 mDebug = true;
844
845 } else if ( cmd.startsWith( "CURSOR" ) ) {
846 // turn on debug
847 kdDebug() << "Command: CURSOR" << endl;
848 i += strlen( "CURSOR" );
849 mMsg->setCursorPos( body.length() );
850
851 } else {
852 // wrong command, do nothing
853 body.append( c );
854 }
855
856 } else if ( dnl && ( c == '\n' || c == '\r') ) {
857 // skip
858 if ( ( c == '\n' && tmpl[i + 1] == '\r' ) ||
859 ( c == '\r' && tmpl[i + 1] == '\n' ) ) {
860 // skip one more
861 i += 1;
862 }
863 dnl = false;
864 } else {
865 body.append( c );
866 }
867 }
868
870}
871
872TQString TemplateParser::messageText( bool allowSelectionOnly )
873{
874 if ( !mSelection.isEmpty() && allowSelectionOnly )
875 return mSelection;
876
877 // No selection text, therefore we need to parse the object tree ourselves to get
878 partNode *root = parsedObjectTree();
879 return mOrigMsg->asPlainTextFromObjectTree( root, shouldStripSignature(), mAllowDecryption );
880}
881
883{
884 if ( mOrigRoot )
885 return mOrigRoot;
886
887 mOrigRoot = partNode::fromMessage( mOrigMsg );
888 ObjectTreeParser otp; // all defaults are ok
889 otp.parseObjectTree( mOrigRoot );
890 return mOrigRoot;
891}
892
894{
895 if ( mAppend ) {
896
897 // ### What happens here if the body is multipart or in some way encoded?
898 TQCString msg_body = mMsg->body();
899 msg_body.append( body.utf8() );
900 mMsg->setBody( msg_body );
901 }
902 else {
903
904 // Get the attachments of the original mail
905 partNode *root = parsedObjectTree();
906 AttachmentCollector ac;
907 ac.collectAttachmentsFrom( root );
908
909 // Now, delete the old content and set the new content, which
910 // is either only the new text or the new text with some attachments.
911 mMsg->deleteBodyParts();
912
913 // Set To and CC from the template
914 if ( mMode == Forward ) {
915 if ( !mTo.isEmpty() ) {
916 mMsg->setTo( mMsg->to() + ',' + mTo );
917 }
918 if ( !mCC.isEmpty() )
919 mMsg->setCc( mMsg->cc() + ',' + mCC );
920 }
921
922 // If we have no attachment, simply create a text/plain part and
923 // set the processed template text as the body
924 if ( ac.attachments().empty() || mMode != Forward ) {
925 mMsg->headers().ContentType().FromString( DwString() ); // to get rid of old boundary
926 mMsg->headers().ContentType().Parse();
927 mMsg->headers().ContentType().SetType( DwMime::kTypeText );
928 mMsg->headers().ContentType().SetSubtype( DwMime::kSubtypePlain );
929 mMsg->headers().Assemble();
930 mMsg->setBodyFromUnicode( body );
931 mMsg->assembleIfNeeded();
932 }
933
934 // If we have some attachments, create a multipart/mixed mail and
935 // add the normal body as well as the attachments
936 else
937 {
938 mMsg->headers().ContentType().SetType( DwMime::kTypeMultipart );
939 mMsg->headers().ContentType().SetSubtype( DwMime::kSubtypeMixed );
940 mMsg->headers().ContentType().CreateBoundary( 0 );
941
942 KMMessagePart textPart;
943 textPart.setBodyFromUnicode( body );
944 mMsg->addDwBodyPart( mMsg->createDWBodyPart( &textPart ) );
945 mMsg->assembleIfNeeded();
946
947 int attachmentNumber = 1;
948 for ( std::vector<partNode*>::const_iterator it = ac.attachments().begin();
949 it != ac.attachments().end(); ++it, attachmentNumber++ ) {
950
951 // When adding this body part, make sure to _not_ add the next bodypart
952 // as well, which mimelib would do, therefore creating a mail with many
953 // duplicate attachments (so many that KMail runs out of memory, in fact).
954 // Body::AddBodyPart is very misleading here...
955 ( *it )->dwPart()->SetNext( 0 );
956
957 DwBodyPart *cloned = static_cast<DwBodyPart*>( ( *it )->dwPart()->Clone() );
958
959 // If the content type has no name or filename parameter, add one, since otherwise the name
960 // would be empty in the attachment view of the composer, which looks confusing
961 if ( cloned->Headers().HasContentType() ) {
962 DwMediaType &ct = cloned->Headers().ContentType();
963
964 // Converting to a string here, since DwMediaType does not have a HasParameter() function
965 TQString ctStr = ct.AsString().c_str();
966 if ( !ctStr.lower().contains( "name=" ) && !ctStr.lower().contains( "filename=" ) ) {
967 DwParameter *nameParameter = new DwParameter;
968 nameParameter->SetAttribute( "name" );
969 nameParameter->SetValue( Util::dwString( KMMsgBase::encodeRFC2231StringAutoDetectCharset(
970 i18n( "Attachment %1" ).arg( attachmentNumber ) ) ) );
971 ct.AddParameter( nameParameter );
972 }
973 }
974
975 mMsg->addDwBodyPart( cloned );
976 mMsg->assembleIfNeeded();
977 }
978 }
979 }
980}
981
982TQString TemplateParser::findCustomTemplate( const TQString &tmplName )
983{
984 CTemplates t( tmplName );
985 mTo = t.to();
986 mCC = t.cC();
987 TQString content = t.content();
988 if ( !content.isEmpty() ) {
989 return content;
990 } else {
991 return findTemplate();
992 }
993}
994
996{
997 // import 'Phrases' if it not done yet
998 if ( !GlobalSettings::self()->phrasesConverted() ) {
999 TemplatesConfiguration::importFromPhrases();
1000 }
1001
1002 // kdDebug() << "Trying to find template for mode " << mode << endl;
1003
1004 TQString tmpl;
1005
1006 if ( !mFolder ) { // find folder message belongs to
1007 mFolder = mMsg->parent();
1008 if ( !mFolder ) {
1009 if ( mOrigMsg ) {
1010 mFolder = mOrigMsg->parent();
1011 }
1012 if ( !mFolder ) {
1013 kdDebug(5006) << "Oops! No folder for message" << endl;
1014 }
1015 }
1016 }
1017 kdDebug(5006) << "Folder found: " << mFolder << endl;
1018
1019 if ( mFolder ) // only if a folder was found
1020 {
1021 TQString fid = mFolder->idString();
1022 Templates fconf( fid );
1023 if ( fconf.useCustomTemplates() ) { // does folder use custom templates?
1024 switch( mMode ) {
1025 case NewMessage:
1026 tmpl = fconf.templateNewMessage();
1027 break;
1028 case Reply:
1029 tmpl = fconf.templateReply();
1030 break;
1031 case ReplyAll:
1032 tmpl = fconf.templateReplyAll();
1033 break;
1034 case Forward:
1035 tmpl = fconf.templateForward();
1036 break;
1037 default:
1038 kdDebug(5006) << "Unknown message mode: " << mMode << endl;
1039 return "";
1040 }
1041 mQuoteString = fconf.quoteString();
1042 if ( !tmpl.isEmpty() ) {
1043 return tmpl; // use folder-specific template
1044 }
1045 }
1046 }
1047
1048 if ( !mIdentity ) { // find identity message belongs to
1049 mIdentity = mMsg->identityUoid();
1050 if ( !mIdentity && mOrigMsg ) {
1051 mIdentity = mOrigMsg->identityUoid();
1052 }
1053 mIdentity = kmkernel->identityManager()->identityForUoidOrDefault( mIdentity ).uoid();
1054 if ( !mIdentity ) {
1055 kdDebug(5006) << "Oops! No identity for message" << endl;
1056 }
1057 }
1058 kdDebug(5006) << "Identity found: " << mIdentity << endl;
1059
1060 TQString iid;
1061 if ( mIdentity ) {
1062 iid = TQString("IDENTITY_%1").arg( mIdentity ); // templates ID for that identity
1063 }
1064 else {
1065 iid = "IDENTITY_NO_IDENTITY"; // templates ID for no identity
1066 }
1067
1068 Templates iconf( iid );
1069 if ( iconf.useCustomTemplates() ) { // does identity use custom templates?
1070 switch( mMode ) {
1071 case NewMessage:
1072 tmpl = iconf.templateNewMessage();
1073 break;
1074 case Reply:
1075 tmpl = iconf.templateReply();
1076 break;
1077 case ReplyAll:
1078 tmpl = iconf.templateReplyAll();
1079 break;
1080 case Forward:
1081 tmpl = iconf.templateForward();
1082 break;
1083 default:
1084 kdDebug(5006) << "Unknown message mode: " << mMode << endl;
1085 return "";
1086 }
1087 mQuoteString = iconf.quoteString();
1088 if ( !tmpl.isEmpty() ) {
1089 return tmpl; // use identity-specific template
1090 }
1091 }
1092
1093 switch( mMode ) { // use the global template
1094 case NewMessage:
1095 tmpl = GlobalSettings::self()->templateNewMessage();
1096 break;
1097 case Reply:
1098 tmpl = GlobalSettings::self()->templateReply();
1099 break;
1100 case ReplyAll:
1101 tmpl = GlobalSettings::self()->templateReplyAll();
1102 break;
1103 case Forward:
1104 tmpl = GlobalSettings::self()->templateForward();
1105 break;
1106 default:
1107 kdDebug(5006) << "Unknown message mode: " << mMode << endl;
1108 return "";
1109 }
1110
1111 mQuoteString = GlobalSettings::self()->quoteString();
1112 return tmpl;
1113}
1114
1115TQString TemplateParser::pipe( const TQString &cmd, const TQString &buf )
1116{
1117 mPipeOut = "";
1118 mPipeErr = "";
1119 mPipeRc = 0;
1120
1121 TDEProcess proc;
1122 TQCString data = buf.local8Bit();
1123
1124 // kdDebug() << "Command data: " << data << endl;
1125
1126 proc << KShell::splitArgs( cmd, KShell::TildeExpand );
1127 proc.setUseShell( true );
1128 connect( &proc, TQ_SIGNAL( receivedStdout( TDEProcess *, char *, int ) ),
1129 this, TQ_SLOT( onReceivedStdout( TDEProcess *, char *, int ) ) );
1130 connect( &proc, TQ_SIGNAL( receivedStderr( TDEProcess *, char *, int ) ),
1131 this, TQ_SLOT( onReceivedStderr( TDEProcess *, char *, int ) ) );
1132 connect( &proc, TQ_SIGNAL( wroteStdin( TDEProcess * ) ),
1133 this, TQ_SLOT( onWroteStdin( TDEProcess * ) ) );
1134
1135 if ( proc.start( TDEProcess::NotifyOnExit, TDEProcess::All ) ) {
1136
1137 bool pipe_filled = proc.writeStdin( data, data.length() );
1138 if ( pipe_filled ) {
1139 proc.closeStdin();
1140
1141 bool exited = proc.wait( PipeTimeout );
1142 if ( exited ) {
1143
1144 if ( proc.normalExit() ) {
1145
1146 mPipeRc = proc.exitStatus();
1147 if ( mPipeRc != 0 && mDebug ) {
1148 if ( mPipeErr.isEmpty() ) {
1149 KMessageBox::error( 0,
1150 i18n( "Pipe command exit with status %1: %2").
1151 arg( mPipeRc ).arg( cmd ) );
1152 } else {
1153 KMessageBox::detailedError( 0,
1154 i18n( "Pipe command exit with status %1: %2" ).
1155 arg( mPipeRc ).arg( cmd ), mPipeErr );
1156 }
1157 }
1158
1159 } else {
1160
1161 mPipeRc = -( proc.exitSignal() );
1162 if ( mPipeRc != 0 && mDebug ) {
1163 if ( mPipeErr.isEmpty() ) {
1164 KMessageBox::error( 0,
1165 i18n( "Pipe command killed by signal %1: %2" ).
1166 arg( -(mPipeRc) ).arg( cmd ) );
1167 } else {
1168 KMessageBox::detailedError( 0,
1169 i18n( "Pipe command killed by signal %1: %2" ).
1170 arg( -(mPipeRc) ).arg( cmd ), mPipeErr );
1171 }
1172 }
1173 }
1174
1175 } else {
1176 // process does not exited after TemplateParser::PipeTimeout seconds, kill it
1177 proc.kill();
1178 proc.detach();
1179 if ( mDebug ) {
1180 KMessageBox::error( 0,
1181 i18n( "Pipe command did not finish within %1 seconds: %2" ).
1182 arg( PipeTimeout ).arg( cmd ) );
1183 }
1184 }
1185
1186 } else {
1187 // can`t write to stdin of process
1188 proc.kill();
1189 proc.detach();
1190 if ( mDebug ) {
1191 if ( mPipeErr.isEmpty() ) {
1192 KMessageBox::error( 0,
1193 i18n( "Cannot write to process stdin: %1" ).arg( cmd ) );
1194 } else {
1195 KMessageBox::detailedError( 0,
1196 i18n( "Cannot write to process stdin: %1" ).
1197 arg( cmd ), mPipeErr );
1198 }
1199 }
1200 }
1201
1202 } else if ( mDebug ) {
1203 KMessageBox::error( 0,
1204 i18n( "Cannot start pipe command from template: %1" ).
1205 arg( cmd ) );
1206 }
1207
1208 return mPipeOut;
1209}
1210
1211void TemplateParser::onProcessExited( TDEProcess *proc )
1212{
1213 Q_UNUSED( proc );
1214 // do nothing for now
1215}
1216
1217void TemplateParser::onReceivedStdout( TDEProcess *proc, char *buffer, int buflen )
1218{
1219 Q_UNUSED( proc );
1220 mPipeOut += TQString::fromLocal8Bit( buffer, buflen );
1221}
1222
1223void TemplateParser::onReceivedStderr( TDEProcess *proc, char *buffer, int buflen )
1224{
1225 Q_UNUSED( proc );
1226 mPipeErr += TQString::fromLocal8Bit( buffer, buflen );
1227}
1228
1229void TemplateParser::onWroteStdin( TDEProcess *proc )
1230{
1231 proc->closeStdin();
1232}
1233
1234#include "templateparser.moc"
Mail folder.
Definition: kmfolder.h:69
TQString idString() const
Returns a string that can be used to identify this folder.
Definition: kmfolder.cpp:705
This is a Mime Message.
Definition: kmmessage.h:68
uint identityUoid() const
Definition: kmmessage.cpp:1727
void setBody(const TQCString &aStr)
Set the message body.
Definition: kmmessage.cpp:2774
void setBodyFromUnicode(const TQString &str, DwEntity *entity=0)
Sets this body's content to str.
Definition: kmmessage.cpp:4440
TQCString body() const
Get the message body.
Definition: kmmessage.cpp:2570
TQString from() const
Get or set the 'From' header field.
Definition: kmmessage.cpp:2015
TQString asQuotedString(const TQString &headerStr, const TQString &indentStr, const TQString &selection=TQString(), bool aStripSignature=true, bool allowDecryption=true) const
Returns message body with quoting header and indented by the given indentation string.
Definition: kmmessage.cpp:835
TQString to() const
Get or set the 'To' header field.
Definition: kmmessage.cpp:1894
TQString subject() const
Get or set the 'Subject' header field.
Definition: kmmessage.cpp:2049
TQCString headerAsSendableString() const
Return the message header with the headers that should not be sent stripped off.
Definition: kmmessage.cpp:327
void addDwBodyPart(DwBodyPart *aDwPart)
Append a DwBodyPart to the message.
Definition: kmmessage.cpp:3348
TQStringList headerFields(const TQCString &name) const
Returns a list of the values of all header fields with the given name.
Definition: kmmessage.cpp:2302
TQString asPlainTextFromObjectTree(partNode *root, bool stripSignature, bool allowDecryption) const
Same as asPlainText(), only that this method expects an already parsed object tree as paramter.
Definition: kmmessage.cpp:740
TQCString asString() const
Return the entire message contents as a string.
Definition: kmmessage.cpp:314
TQString cc() const
Get or set the 'Cc' header field.
Definition: kmmessage.cpp:1940
TQCString id() const
Returns the message ID, useful for followups.
Definition: kmmessage.cpp:208
DwHeaders & headers() const
get the DwHeaders (make sure to call setNeedsAssembly() function after directly modyfying internal da...
Definition: kmmessage.cpp:2546
void assembleIfNeeded()
Assemble the internal message.
Definition: kmmessage.cpp:2559
void deleteBodyParts()
Delete all body parts.
Definition: kmmessage.cpp:3174
DwBodyPart * createDWBodyPart(const KMMessagePart *aPart)
Compose a DwBodyPart (needed for adding a part to the message).
Definition: kmmessage.cpp:3218
void setCursorPos(int pos)
Set cursor position as offset from message start.
Definition: kmmessage.h:927
void setAllowDecryption(const bool allowDecryption)
Sets whether the template parser is allowed to decrypt the original message when needing its message ...
virtual TQString findCustomTemplate(const TQString &tmpl)
Finds the template with the given name.
void setSelection(const TQString &selection)
Sets the selection.
bool shouldStripSignature() const
Determines whether the signature should be stripped when getting the text of the original message,...
virtual TQString findTemplate()
This finds the template to use.
partNode * parsedObjectTree()
Returns the parsed object tree of the original message.
TQString messageText(bool allowSelectionOnly)
If there was a text selection set in the constructor, that will be returned.
void addProcessedBodyToMessage(const TQString &body)
Called by processWithTemplate().
void append(TQByteArray &that, const TQByteArray &str)
Append a bytearray to a bytearray.
Definition: util.cpp:144
folderdiaquotatab.h
Definition: aboutdata.cpp:40