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 <kprocess.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 
54 using namespace KMail;
55 
56 TemplateParser::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 
64 void TemplateParser::setSelection( const TQString &selection )
65 {
66  mSelection = selection;
67 }
68 
69 void 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 
80 TemplateParser::~TemplateParser()
81 {
82  delete mOrigRoot;
83  mOrigRoot = 0;
84 }
85 
86 int 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 
121 TQString 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 
165 TQString 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 
200 void 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 
209 void 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 
219 void 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 
869  addProcessedBodyToMessage( body );
870 }
871 
872 TQString 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 
893 void TemplateParser::addProcessedBodyToMessage( const TQString &body )
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 
982 TQString 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 
1115 TQString 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 
1211 void TemplateParser::onProcessExited( TDEProcess *proc )
1212 {
1213  Q_UNUSED( proc );
1214  // do nothing for now
1215 }
1216 
1217 void TemplateParser::onReceivedStdout( TDEProcess *proc, char *buffer, int buflen )
1218 {
1219  Q_UNUSED( proc );
1220  mPipeOut += TQString::fromLocal8Bit( buffer, buflen );
1221 }
1222 
1223 void TemplateParser::onReceivedStderr( TDEProcess *proc, char *buffer, int buflen )
1224 {
1225  Q_UNUSED( proc );
1226  mPipeErr += TQString::fromLocal8Bit( buffer, buflen );
1227 }
1228 
1229 void TemplateParser::onWroteStdin( TDEProcess *proc )
1230 {
1231  proc->closeStdin();
1232 }
1233 
1234 #include "templateparser.moc"
bool shouldStripSignature() const
Determines whether the signature should be stripped when getting the text of the original message,...
void setSelection(const TQString &selection)
Sets the selection.
DwString dwString(const TQCString &str)
Construct a DwString from a TQCString.
Definition: util.cpp:130
virtual TQString findTemplate()
This finds the template to use.
void append(TQByteArray &that, const TQByteArray &str)
Append a bytearray to a bytearray.
Definition: util.cpp:144
void addProcessedBodyToMessage(const TQString &body)
Called by processWithTemplate().
virtual TQString findCustomTemplate(const TQString &tmpl)
Finds the template with the given name.
This is a Mime Message.
Definition: kmmessage.h:67
folderdiaquotatab.h
Definition: aboutdata.cpp:40
void setAllowDecryption(const bool allowDecryption)
Sets whether the template parser is allowed to decrypt the original message when needing its message ...
Mail folder.
Definition: kmfolder.h:68
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.