kmail

templateparser.cpp
00001 /*   -*- mode: C++; c-file-style: "gnu" -*-
00002  *   kmail: KDE mail client
00003  *   This file: Copyright (C) 2006 Dmitry Morozhnikov <dmiceman@mail.ru>
00004  *
00005  *   This program is free software; you can redistribute it and/or modify
00006  *   it under the terms of the GNU General Public License as published by
00007  *   the Free Software Foundation; either version 2 of the License, or
00008  *   (at your option) any later version.
00009  *
00010  *   This program is distributed in the hope that it will be useful,
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *   GNU General Public License for more details.
00014  *
00015  *   You should have received a copy of the GNU General Public License
00016  *   along with this program; if not, write to the Free Software
00017  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018  *
00019  */
00020 
00021 #include <config.h>
00022 
00023 #include <tqstring.h>
00024 #include <tqdatetime.h>
00025 #include <klocale.h>
00026 #include <kcalendarsystem.h>
00027 #include <kmime_util.h>
00028 #include <kglobal.h>
00029 #include <kprocess.h>
00030 #include <tqregexp.h>
00031 #include <tqfile.h>
00032 #include <kmessagebox.h>
00033 #include <kshell.h>
00034 #include <tqfileinfo.h>
00035 
00036 #include "kmmessage.h"
00037 #include "kmmsgbase.h"
00038 #include "kmfolder.h"
00039 #include "templatesconfiguration.h"
00040 #include "templatesconfiguration_kfg.h"
00041 #include "customtemplates_kfg.h"
00042 #include "globalsettings_base.h"
00043 #include "kmkernel.h"
00044 #include <libkpimidentities/identity.h>
00045 #include <libkpimidentities/identitymanager.h>
00046 #include "partNode.h"
00047 #include "attachmentcollector.h"
00048 #include "objecttreeparser.h"
00049 #include "util.h"
00050 
00051 #include "templateparser.h"
00052 #include <mimelib/bodypart.h>
00053 
00054 using namespace KMail;
00055 
00056 TemplateParser::TemplateParser( KMMessage *amsg, const Mode amode ) :
00057   mMode( amode ), mFolder( 0 ), mIdentity( 0 ),
00058   mAllowDecryption( false ),
00059   mDebug( false ), mQuoteString( "> " ), mAppend( false ), mOrigRoot( 0 )
00060 {
00061   mMsg = amsg;
00062 }
00063 
00064 void TemplateParser::setSelection( const TQString &selection )
00065 {
00066   mSelection = selection;
00067 }
00068 
00069 void TemplateParser::setAllowDecryption( const bool allowDecryption )
00070 {
00071   mAllowDecryption = allowDecryption;
00072 }
00073 
00074 bool TemplateParser::shouldStripSignature() const
00075 {
00076   // Only strip the signature when replying, it should be preserved when forwarding
00077   return ( mMode == Reply || mMode == ReplyAll) && GlobalSettings::stripSignature();
00078 }
00079 
00080 TemplateParser::~TemplateParser()
00081 {
00082   delete mOrigRoot;
00083   mOrigRoot = 0;
00084 }
00085 
00086 int TemplateParser::parseQuotes( const TQString &prefix, const TQString &str,
00087                                  TQString &quote ) const
00088 {
00089   int pos = prefix.length();
00090   int len;
00091   int str_len = str.length();
00092   TQChar qc = '"';
00093   TQChar prev = 0;
00094 
00095   pos++;
00096   len = pos;
00097 
00098   while ( pos < str_len ) {
00099     TQChar c = str[pos];
00100 
00101     pos++;
00102     len++;
00103 
00104     if ( prev ) {
00105       quote.append( c );
00106       prev = 0;
00107     } else {
00108       if ( c == '\\' ) {
00109         prev = c;
00110       } else if ( c == qc ) {
00111         break;
00112       } else {
00113         quote.append( c );
00114       }
00115     }
00116   }
00117 
00118   return len;
00119 }
00120 
00121 TQString TemplateParser::getFName( const TQString &str )
00122 {
00123   // simple logic:
00124   // if there is ',' in name, than format is 'Last, First'
00125   // else format is 'First Last'
00126   // last resort -- return 'name' from 'name@domain'
00127   int sep_pos;
00128   TQString res;
00129   if ( ( sep_pos = str.find( '@' ) ) > 0 ) {
00130     int i;
00131     for ( i = (sep_pos - 1); i >= 0; --i ) {
00132       TQChar c = str[i];
00133       if ( c.isLetterOrNumber() ) {
00134         res.prepend( c );
00135       } else {
00136         break;
00137       }
00138     }
00139   } else if ( ( sep_pos = str.find(',') ) > 0 ) {
00140     unsigned int i;
00141     bool begin = false;
00142     for ( i = sep_pos; i < str.length(); ++i ) {
00143       TQChar c = str[i];
00144       if ( c.isLetterOrNumber() ) {
00145         begin = true;
00146         res.append( c );
00147       } else if ( begin ) {
00148         break;
00149       }
00150     }
00151   } else {
00152     unsigned int i;
00153     for ( i = 0; i < str.length(); ++i ) {
00154       TQChar c = str[i];
00155       if ( c.isLetterOrNumber() ) {
00156         res.append( c );
00157       } else {
00158         break;
00159       }
00160     }
00161   }
00162   return res;
00163 }
00164 
00165 TQString TemplateParser::getLName( const TQString &str )
00166 {
00167   // simple logic:
00168   // if there is ',' in name, than format is 'Last, First'
00169   // else format is 'First Last'
00170   int sep_pos;
00171   TQString res;
00172   if ( ( sep_pos = str.find(',') ) > 0 ) {
00173     int i;
00174     for ( i = sep_pos; i >= 0; --i ) {
00175       TQChar c = str[i];
00176       if ( c.isLetterOrNumber() ) {
00177         res.prepend( c );
00178       } else {
00179         break;
00180       }
00181     }
00182   } else {
00183     if ( ( sep_pos = str.find( ' ' ) ) > 0 ) {
00184       unsigned int i;
00185       bool begin = false;
00186       for ( i = sep_pos; i < str.length(); ++i ) {
00187         TQChar c = str[i];
00188         if ( c.isLetterOrNumber() ) {
00189           begin = true;
00190           res.append( c );
00191         } else if ( begin ) {
00192           break;
00193         }
00194       }
00195     }
00196   }
00197   return res;
00198 }
00199 
00200 void TemplateParser::process( KMMessage *aorig_msg, KMFolder *afolder, bool append )
00201 {
00202   mAppend = append;
00203   mOrigMsg = aorig_msg;
00204   mFolder = afolder;
00205   TQString tmpl = findTemplate();
00206   return processWithTemplate( tmpl );
00207 }
00208 
00209 void TemplateParser::process( const TQString &tmplName, KMMessage *aorig_msg,
00210                               KMFolder *afolder, bool append )
00211 {
00212   mAppend = append;
00213   mOrigMsg = aorig_msg;
00214   mFolder = afolder;
00215   TQString tmpl = findCustomTemplate( tmplName );
00216   return processWithTemplate( tmpl );
00217 }
00218 
00219 void TemplateParser::processWithTemplate( const TQString &tmpl )
00220 {
00221   TQString body;
00222   int tmpl_len = tmpl.length();
00223   bool dnl = false;
00224   for ( int i = 0; i < tmpl_len; ++i ) {
00225     TQChar c = tmpl[i];
00226     // kdDebug() << "Next char: " << c << endl;
00227     if ( c == '%' ) {
00228       TQString cmd = tmpl.mid( i + 1 );
00229 
00230       if ( cmd.startsWith( "-" ) ) {
00231         // dnl
00232         kdDebug() << "Command: -" << endl;
00233         dnl = true;
00234         i += 1;
00235 
00236       } else if ( cmd.startsWith( "REM=" ) ) {
00237         // comments
00238         kdDebug() << "Command: REM=" << endl;
00239         TQString q;
00240         int len = parseQuotes( "REM=", cmd, q );
00241         i += len;
00242 
00243       } else if ( cmd.startsWith( "INSERT=" ) ) {
00244         // insert content of specified file as is
00245         kdDebug() << "Command: INSERT=" << endl;
00246         TQString q;
00247         int len = parseQuotes( "INSERT=", cmd, q );
00248         i += len;
00249         TQString path = KShell::tildeExpand( q );
00250         TQFileInfo finfo( path );
00251         if (finfo.isRelative() ) {
00252           path = KShell::homeDir( "" );
00253           path += '/';
00254           path += q;
00255         }
00256         TQFile file( path );
00257         if ( file.open( IO_ReadOnly ) ) {
00258           TQByteArray content = file.readAll();
00259           TQString str = TQString::fromLocal8Bit( content, content.size() );
00260           body.append( str );
00261         } else if ( mDebug ) {
00262           KMessageBox::error( 0,
00263                               i18n( "Cannot insert content from file %1: %2" ).
00264                               arg( path ).arg( file.errorString() ) );
00265         }
00266 
00267       } else if ( cmd.startsWith( "SYSTEM=" ) ) {
00268         // insert content of specified file as is
00269         kdDebug() << "Command: SYSTEM=" << endl;
00270         TQString q;
00271         int len = parseQuotes( "SYSTEM=", cmd, q );
00272         i += len;
00273         TQString pipe_cmd = q;
00274         TQString str = pipe( pipe_cmd, "" );
00275         body.append( str );
00276 
00277       } else if ( cmd.startsWith( "PUT=" ) ) {
00278         // insert content of specified file as is
00279         kdDebug() << "Command: PUT=" << endl;
00280         TQString q;
00281         int len = parseQuotes( "PUT=", cmd, q );
00282         i += len;
00283         TQString path = KShell::tildeExpand( q );
00284         TQFileInfo finfo( path );
00285         if (finfo.isRelative() ) {
00286           path = KShell::homeDir( "" );
00287           path += '/';
00288           path += q;
00289         }
00290         TQFile file( path );
00291         if ( file.open( IO_ReadOnly ) ) {
00292           TQByteArray content = file.readAll();
00293           body.append( TQString::fromLocal8Bit( content, content.size() ) );
00294         } else if ( mDebug ) {
00295           KMessageBox::error( 0,
00296                               i18n( "Cannot insert content from file %1: %2").
00297                               arg( path ).arg(file.errorString() ));
00298         }
00299 
00300       } else if ( cmd.startsWith( "QUOTEPIPE=" ) ) {
00301         // pipe message body throw command and insert it as quotation
00302         kdDebug() << "Command: QUOTEPIPE=" << endl;
00303         TQString q;
00304         int len = parseQuotes( "QUOTEPIPE=", cmd, q );
00305         i += len;
00306         TQString pipe_cmd = q;
00307         if ( mOrigMsg ) {
00308           TQString str = pipe( pipe_cmd, messageText( false ) );
00309           TQString quote = mOrigMsg->asQuotedString( "", mQuoteString, str,
00310                                                     shouldStripSignature(), mAllowDecryption );
00311           body.append( quote );
00312         }
00313 
00314       } else if ( cmd.startsWith( "QUOTE" ) ) {
00315         kdDebug() << "Command: QUOTE" << endl;
00316         i += strlen( "QUOTE" );
00317         if ( mOrigMsg ) {
00318           TQString quote = mOrigMsg->asQuotedString( "", mQuoteString, messageText( true ),
00319                                                     shouldStripSignature(), mAllowDecryption );
00320           body.append( quote );
00321         }
00322 
00323       } else if ( cmd.startsWith( "QHEADERS" ) ) {
00324         kdDebug() << "Command: TQHEADERS" << endl;
00325         i += strlen( "QHEADERS" );
00326         if ( mOrigMsg ) {
00327           TQString quote = mOrigMsg->asQuotedString( "", mQuoteString,
00328                                                     mOrigMsg->headerAsSendableString(),
00329                                                     false, false );
00330           body.append( quote );
00331         }
00332 
00333       } else if ( cmd.startsWith( "HEADERS" ) ) {
00334         kdDebug() << "Command: HEADERS" << endl;
00335         i += strlen( "HEADERS" );
00336         if ( mOrigMsg ) {
00337           TQString str = mOrigMsg->headerAsSendableString();
00338           body.append( str );
00339         }
00340 
00341       } else if ( cmd.startsWith( "TEXTPIPE=" ) ) {
00342         // pipe message body throw command and insert it as is
00343         kdDebug() << "Command: TEXTPIPE=" << endl;
00344         TQString q;
00345         int len = parseQuotes( "TEXTPIPE=", cmd, q );
00346         i += len;
00347         TQString pipe_cmd = q;
00348         if ( mOrigMsg ) {
00349           TQString str = pipe(pipe_cmd, messageText( false ) );
00350           body.append( str );
00351         }
00352 
00353       } else if ( cmd.startsWith( "MSGPIPE=" ) ) {
00354         // pipe full message throw command and insert result as is
00355         kdDebug() << "Command: MSGPIPE=" << endl;
00356         TQString q;
00357         int len = parseQuotes( "MSGPIPE=", cmd, q );
00358         i += len;
00359         TQString pipe_cmd = q;
00360         if ( mOrigMsg ) {
00361           TQString str = pipe(pipe_cmd, mOrigMsg->asString() );
00362           body.append( str );
00363         }
00364 
00365       } else if ( cmd.startsWith( "BODYPIPE=" ) ) {
00366         // pipe message body generated so far throw command and insert result as is
00367         kdDebug() << "Command: BODYPIPE=" << endl;
00368         TQString q;
00369         int len = parseQuotes( "BODYPIPE=", cmd, q );
00370         i += len;
00371         TQString pipe_cmd = q;
00372         TQString str = pipe( pipe_cmd, body );
00373         body.append( str );
00374 
00375       } else if ( cmd.startsWith( "CLEARPIPE=" ) ) {
00376         // pipe message body generated so far throw command and
00377         // insert result as is replacing current body
00378         kdDebug() << "Command: CLEARPIPE=" << endl;
00379         TQString q;
00380         int len = parseQuotes( "CLEARPIPE=", cmd, q );
00381         i += len;
00382         TQString pipe_cmd = q;
00383         TQString str = pipe( pipe_cmd, body );
00384         body = str;
00385         mMsg->setCursorPos( 0 );
00386 
00387       } else if ( cmd.startsWith( "TEXT" ) ) {
00388         kdDebug() << "Command: TEXT" << endl;
00389         i += strlen( "TEXT" );
00390         if ( mOrigMsg ) {
00391           TQString quote = messageText( false );
00392           body.append( quote );
00393         }
00394 
00395       } else if ( cmd.startsWith( "OTEXTSIZE" ) ) {
00396         kdDebug() << "Command: OTEXTSIZE" << endl;
00397         i += strlen( "OTEXTSIZE" );
00398         if ( mOrigMsg ) {
00399           TQString str = TQString( "%1" ).arg( mOrigMsg->body().length() );
00400           body.append( str );
00401         }
00402 
00403       } else if ( cmd.startsWith( "OTEXT" ) ) {
00404         kdDebug() << "Command: OTEXT" << endl;
00405         i += strlen( "OTEXT" );
00406         if ( mOrigMsg ) {
00407           TQString quote = messageText( false );
00408           body.append( quote );
00409         }
00410 
00411       } else if ( cmd.startsWith( "OADDRESSEESADDR" ) ) {
00412         kdDebug() << "Command: OADDRESSEESADDR" << endl;
00413         i += strlen( "OADDRESSEESADDR" );
00414         const TQString to = mOrigMsg->to();
00415         const TQString cc = mOrigMsg->cc();
00416         if ( !to.isEmpty() )
00417           body.append( i18n( "To:" ) + ' ' + to );
00418         if ( !to.isEmpty() && !cc.isEmpty() )
00419           body.append( '\n' );
00420         if ( !cc.isEmpty() )
00421           body.append( i18n( "CC:" ) + ' ' +  cc );
00422 
00423       } else if ( cmd.startsWith( "CCADDR" ) ) {
00424         kdDebug() << "Command: CCADDR" << endl;
00425         i += strlen( "CCADDR" );
00426         TQString str = mMsg->cc();
00427         body.append( str );
00428 
00429       } else if ( cmd.startsWith( "CCNAME" ) ) {
00430         kdDebug() << "Command: CCNAME" << endl;
00431         i += strlen( "CCNAME" );
00432         TQString str = mMsg->ccStrip();
00433         body.append( str );
00434 
00435       } else if ( cmd.startsWith( "CCFNAME" ) ) {
00436         kdDebug() << "Command: CCFNAME" << endl;
00437         i += strlen( "CCFNAME" );
00438         TQString str = mMsg->ccStrip();
00439         body.append( getFName( str ) );
00440 
00441       } else if ( cmd.startsWith( "CCLNAME" ) ) {
00442         kdDebug() << "Command: CCLNAME" << endl;
00443         i += strlen( "CCLNAME" );
00444         TQString str = mMsg->ccStrip();
00445         body.append( getLName( str ) );
00446 
00447       } else if ( cmd.startsWith( "TOADDR" ) ) {
00448         kdDebug() << "Command: TOADDR" << endl;
00449         i += strlen( "TOADDR" );
00450         TQString str = mMsg->to();
00451         body.append( str );
00452 
00453       } else if ( cmd.startsWith( "TONAME" ) ) {
00454         kdDebug() << "Command: TONAME" << endl;
00455         i += strlen( "TONAME" );
00456         TQString str = mMsg->toStrip();
00457         body.append( str );
00458 
00459       } else if ( cmd.startsWith( "TOFNAME" ) ) {
00460         kdDebug() << "Command: TOFNAME" << endl;
00461         i += strlen( "TOFNAME" );
00462         TQString str = mMsg->toStrip();
00463         body.append( getFName( str ) );
00464 
00465       } else if ( cmd.startsWith( "TOLNAME" ) ) {
00466         kdDebug() << "Command: TOLNAME" << endl;
00467         i += strlen( "TOLNAME" );
00468         TQString str = mMsg->toStrip();
00469         body.append( getLName( str ) );
00470 
00471       } else if ( cmd.startsWith( "TOLIST" ) ) {
00472         kdDebug() << "Command: TOLIST" << endl;
00473         i += strlen( "TOLIST" );
00474         TQString str = mMsg->to();
00475         body.append( str );
00476 
00477       } else if ( cmd.startsWith( "FROMADDR" ) ) {
00478         kdDebug() << "Command: FROMADDR" << endl;
00479         i += strlen( "FROMADDR" );
00480         TQString str = mMsg->from();
00481         body.append( str );
00482 
00483       } else if ( cmd.startsWith( "FROMNAME" ) ) {
00484         kdDebug() << "Command: FROMNAME" << endl;
00485         i += strlen( "FROMNAME" );
00486         TQString str = mMsg->fromStrip();
00487         body.append( str );
00488 
00489       } else if ( cmd.startsWith( "FROMFNAME" ) ) {
00490         kdDebug() << "Command: FROMFNAME" << endl;
00491         i += strlen( "FROMFNAME" );
00492         TQString str = mMsg->fromStrip();
00493         body.append( getFName( str ) );
00494 
00495       } else if ( cmd.startsWith( "FROMLNAME" ) ) {
00496         kdDebug() << "Command: FROMLNAME" << endl;
00497         i += strlen( "FROMLNAME" );
00498         TQString str = mMsg->fromStrip();
00499         body.append( getLName( str ) );
00500 
00501       } else if ( cmd.startsWith( "FULLSUBJECT" ) ) {
00502         kdDebug() << "Command: FULLSUBJECT" << endl;
00503         i += strlen( "FULLSUBJECT" );
00504         TQString str = mMsg->subject();
00505         body.append( str );
00506 
00507       } else if ( cmd.startsWith( "FULLSUBJ" ) ) {
00508         kdDebug() << "Command: FULLSUBJ" << endl;
00509         i += strlen( "FULLSUBJ" );
00510         TQString str = mMsg->subject();
00511         body.append( str );
00512 
00513       } else if ( cmd.startsWith( "MSGID" ) ) {
00514         kdDebug() << "Command: MSGID" << endl;
00515         i += strlen( "MSGID" );
00516         TQString str = mMsg->id();
00517         body.append( str );
00518 
00519       } else if ( cmd.startsWith( "OHEADER=" ) ) {
00520         // insert specified content of header from original message
00521         kdDebug() << "Command: OHEADER=" << endl;
00522         TQString q;
00523         int len = parseQuotes( "OHEADER=", cmd, q );
00524         i += len;
00525         if ( mOrigMsg ) {
00526           TQString hdr = q;
00527           TQString str = mOrigMsg->headerFields(hdr.local8Bit() ).join( ", " );
00528           body.append( str );
00529         }
00530 
00531       } else if ( cmd.startsWith( "HEADER=" ) ) {
00532         // insert specified content of header from current message
00533         kdDebug() << "Command: HEADER=" << endl;
00534         TQString q;
00535         int len = parseQuotes( "HEADER=", cmd, q );
00536         i += len;
00537         TQString hdr = q;
00538         TQString str = mMsg->headerFields(hdr.local8Bit() ).join( ", " );
00539         body.append( str );
00540 
00541       } else if ( cmd.startsWith( "HEADER( " ) ) {
00542         // insert specified content of header from current message
00543         kdDebug() << "Command: HEADER( " << endl;
00544         TQRegExp re = TQRegExp( "^HEADER\\((.+)\\)" );
00545         re.setMinimal( true );
00546         int res = re.search( cmd );
00547         if ( res != 0 ) {
00548           // something wrong
00549           i += strlen( "HEADER( " );
00550         } else {
00551           i += re.matchedLength();
00552           TQString hdr = re.cap( 1 );
00553           TQString str = mMsg->headerFields( hdr.local8Bit() ).join( ", " );
00554           body.append( str );
00555         }
00556 
00557       } else if ( cmd.startsWith( "OCCADDR" ) ) {
00558         kdDebug() << "Command: OCCADDR" << endl;
00559         i += strlen( "OCCADDR" );
00560         if ( mOrigMsg ) {
00561           TQString str = mOrigMsg->cc();
00562           body.append( str );
00563         }
00564 
00565       } else if ( cmd.startsWith( "OCCNAME" ) ) {
00566         kdDebug() << "Command: OCCNAME" << endl;
00567         i += strlen( "OCCNAME" );
00568         if ( mOrigMsg ) {
00569           TQString str = mOrigMsg->ccStrip();
00570           body.append( str );
00571         }
00572 
00573       } else if ( cmd.startsWith( "OCCFNAME" ) ) {
00574         kdDebug() << "Command: OCCFNAME" << endl;
00575         i += strlen( "OCCFNAME" );
00576         if ( mOrigMsg ) {
00577           TQString str = mOrigMsg->ccStrip();
00578           body.append( getFName( str ) );
00579         }
00580 
00581       } else if ( cmd.startsWith( "OCCLNAME" ) ) {
00582         kdDebug() << "Command: OCCLNAME" << endl;
00583         i += strlen( "OCCLNAME" );
00584         if ( mOrigMsg ) {
00585           TQString str = mOrigMsg->ccStrip();
00586           body.append( getLName( str ) );
00587         }
00588 
00589       } else if ( cmd.startsWith( "OTOADDR" ) ) {
00590         kdDebug() << "Command: OTOADDR" << endl;
00591         i += strlen( "OTOADDR" );
00592         if ( mOrigMsg ) {
00593           TQString str = mOrigMsg->to();
00594           body.append( str );
00595         }
00596 
00597       } else if ( cmd.startsWith( "OTONAME" ) ) {
00598         kdDebug() << "Command: OTONAME" << endl;
00599         i += strlen( "OTONAME" );
00600         if ( mOrigMsg ) {
00601           TQString str = mOrigMsg->toStrip();
00602           body.append( str );
00603         }
00604 
00605       } else if ( cmd.startsWith( "OTOFNAME" ) ) {
00606         kdDebug() << "Command: OTOFNAME" << endl;
00607         i += strlen( "OTOFNAME" );
00608         if ( mOrigMsg ) {
00609           TQString str = mOrigMsg->toStrip();
00610           body.append( getFName( str ) );
00611         }
00612 
00613       } else if ( cmd.startsWith( "OTOLNAME" ) ) {
00614         kdDebug() << "Command: OTOLNAME" << endl;
00615         i += strlen( "OTOLNAME" );
00616         if ( mOrigMsg ) {
00617           TQString str = mOrigMsg->toStrip();
00618           body.append( getLName( str ) );
00619         }
00620 
00621       } else if ( cmd.startsWith( "OTOLIST" ) ) {
00622         kdDebug() << "Command: OTOLIST" << endl;
00623         i += strlen( "OTOLIST" );
00624         if ( mOrigMsg ) {
00625           TQString str = mOrigMsg->to();
00626           body.append( str );
00627         }
00628 
00629       } else if ( cmd.startsWith( "OTO" ) ) {
00630         kdDebug() << "Command: OTO" << endl;
00631         i += strlen( "OTO" );
00632         if ( mOrigMsg ) {
00633           TQString str = mOrigMsg->to();
00634           body.append( str );
00635         }
00636 
00637       } else if ( cmd.startsWith( "OFROMADDR" ) ) {
00638         kdDebug() << "Command: OFROMADDR" << endl;
00639         i += strlen( "OFROMADDR" );
00640         if ( mOrigMsg ) {
00641           TQString str = mOrigMsg->from();
00642           body.append( str );
00643         }
00644 
00645       } else if ( cmd.startsWith( "OFROMNAME" ) ) {
00646         kdDebug() << "Command: OFROMNAME" << endl;
00647         i += strlen( "OFROMNAME" );
00648         if ( mOrigMsg ) {
00649           TQString str = mOrigMsg->fromStrip();
00650           body.append( str );
00651         }
00652 
00653       } else if ( cmd.startsWith( "OFROMFNAME" ) ) {
00654         kdDebug() << "Command: OFROMFNAME" << endl;
00655         i += strlen( "OFROMFNAME" );
00656         if ( mOrigMsg ) {
00657           TQString str = mOrigMsg->fromStrip();
00658           body.append( getFName( str ) );
00659         }
00660 
00661       } else if ( cmd.startsWith( "OFROMLNAME" ) ) {
00662         kdDebug() << "Command: OFROMLNAME" << endl;
00663         i += strlen( "OFROMLNAME" );
00664         if ( mOrigMsg ) {
00665           TQString str = mOrigMsg->fromStrip();
00666           body.append( getLName( str ) );
00667         }
00668 
00669       } else if ( cmd.startsWith( "OFULLSUBJECT" ) ) {
00670         kdDebug() << "Command: OFULLSUBJECT" << endl;
00671         i += strlen( "OFULLSUBJECT" );
00672         if ( mOrigMsg ) {
00673           TQString str = mOrigMsg->subject();
00674           body.append( str );
00675         }
00676 
00677       } else if ( cmd.startsWith( "OFULLSUBJ" ) ) {
00678         kdDebug() << "Command: OFULLSUBJ" << endl;
00679         i += strlen( "OFULLSUBJ" );
00680         if ( mOrigMsg ) {
00681           TQString str = mOrigMsg->subject();
00682           body.append( str );
00683         }
00684 
00685       } else if ( cmd.startsWith( "OMSGID" ) ) {
00686         kdDebug() << "Command: OMSGID" << endl;
00687         i += strlen( "OMSGID" );
00688         if ( mOrigMsg ) {
00689           TQString str = mOrigMsg->id();
00690           body.append( str );
00691         }
00692 
00693       } else if ( cmd.startsWith( "DATEEN" ) ) {
00694         kdDebug() << "Command: DATEEN" << endl;
00695         i += strlen( "DATEEN" );
00696         TQDateTime date = TQDateTime::currentDateTime();
00697         KLocale locale( "C" );
00698         TQString str = locale.formatDate( date.date(), false );
00699         body.append( str );
00700 
00701       } else if ( cmd.startsWith( "DATESHORT" ) ) {
00702         kdDebug() << "Command: DATESHORT" << endl;
00703         i += strlen( "DATESHORT" );
00704         TQDateTime date = TQDateTime::currentDateTime();
00705         TQString str = KGlobal::locale()->formatDate( date.date(), true );
00706         body.append( str );
00707 
00708       } else if ( cmd.startsWith( "DATE" ) ) {
00709         kdDebug() << "Command: DATE" << endl;
00710         i += strlen( "DATE" );
00711         TQDateTime date = TQDateTime::currentDateTime();
00712         TQString str = KGlobal::locale()->formatDate( date.date(), false );
00713         body.append( str );
00714 
00715       } else if ( cmd.startsWith( "DOW" ) ) {
00716         kdDebug() << "Command: DOW" << endl;
00717         i += strlen( "DOW" );
00718         TQDateTime date = TQDateTime::currentDateTime();
00719         TQString str = KGlobal::locale()->calendar()->weekDayName( date.date(), false );
00720         body.append( str );
00721 
00722       } else if ( cmd.startsWith( "TIMELONGEN" ) ) {
00723         kdDebug() << "Command: TIMELONGEN" << endl;
00724         i += strlen( "TIMELONGEN" );
00725         TQDateTime date = TQDateTime::currentDateTime();
00726         KLocale locale( "C");
00727         TQString str = locale.formatTime( date.time(), true );
00728         body.append( str );
00729 
00730       } else if ( cmd.startsWith( "TIMELONG" ) ) {
00731         kdDebug() << "Command: TIMELONG" << endl;
00732         i += strlen( "TIMELONG" );
00733         TQDateTime date = TQDateTime::currentDateTime();
00734         TQString str = KGlobal::locale()->formatTime( date.time(), true );
00735         body.append( str );
00736 
00737       } else if ( cmd.startsWith( "TIME" ) ) {
00738         kdDebug() << "Command: TIME" << endl;
00739         i += strlen( "TIME" );
00740         TQDateTime date = TQDateTime::currentDateTime();
00741         TQString str = KGlobal::locale()->formatTime( date.time(), false );
00742         body.append( str );
00743 
00744       } else if ( cmd.startsWith( "ODATEEN" ) ) {
00745         kdDebug() << "Command: ODATEEN" << endl;
00746         i += strlen( "ODATEEN" );
00747         if ( mOrigMsg ) {
00748           TQDateTime date;
00749           date.setTime_t( mOrigMsg->date() );
00750           KLocale locale( "C");
00751           TQString str = locale.formatDate( date.date(), false );
00752           body.append( str );
00753         }
00754 
00755       } else if ( cmd.startsWith( "ODATESHORT") ) {
00756         kdDebug() << "Command: ODATESHORT" << endl;
00757         i += strlen( "ODATESHORT");
00758         if ( mOrigMsg ) {
00759           TQDateTime date;
00760           date.setTime_t( mOrigMsg->date() );
00761           TQString str = KGlobal::locale()->formatDate( date.date(), true );
00762           body.append( str );
00763         }
00764 
00765       } else if ( cmd.startsWith( "ODATE") ) {
00766         kdDebug() << "Command: ODATE" << endl;
00767         i += strlen( "ODATE");
00768         if ( mOrigMsg ) {
00769           TQDateTime date;
00770           date.setTime_t( mOrigMsg->date() );
00771           TQString str = KGlobal::locale()->formatDate( date.date(), false );
00772           body.append( str );
00773         }
00774 
00775       } else if ( cmd.startsWith( "ODOW") ) {
00776         kdDebug() << "Command: ODOW" << endl;
00777         i += strlen( "ODOW");
00778         if ( mOrigMsg ) {
00779           TQDateTime date;
00780           date.setTime_t( mOrigMsg->date() );
00781           TQString str = KGlobal::locale()->calendar()->weekDayName( date.date(), false );
00782           body.append( str );
00783         }
00784 
00785       } else if ( cmd.startsWith( "OTIMELONGEN") ) {
00786         kdDebug() << "Command: OTIMELONGEN" << endl;
00787         i += strlen( "OTIMELONGEN");
00788         if ( mOrigMsg ) {
00789           TQDateTime date;
00790           date.setTime_t( mOrigMsg->date() );
00791           KLocale locale( "C");
00792           TQString str = locale.formatTime( date.time(), true );
00793           body.append( str );
00794         }
00795 
00796       } else if ( cmd.startsWith( "OTIMELONG") ) {
00797         kdDebug() << "Command: OTIMELONG" << endl;
00798         i += strlen( "OTIMELONG");
00799         if ( mOrigMsg ) {
00800           TQDateTime date;
00801           date.setTime_t( mOrigMsg->date() );
00802           TQString str = KGlobal::locale()->formatTime( date.time(), true );
00803           body.append( str );
00804         }
00805 
00806       } else if ( cmd.startsWith( "OTIME") ) {
00807         kdDebug() << "Command: OTIME" << endl;
00808         i += strlen( "OTIME");
00809         if ( mOrigMsg ) {
00810           TQDateTime date;
00811           date.setTime_t( mOrigMsg->date() );
00812           TQString str = KGlobal::locale()->formatTime( date.time(), false );
00813           body.append( str );
00814         }
00815 
00816       } else if ( cmd.startsWith( "BLANK" ) ) {
00817         // do nothing
00818         kdDebug() << "Command: BLANK" << endl;
00819         i += strlen( "BLANK" );
00820 
00821       } else if ( cmd.startsWith( "NOP" ) ) {
00822         // do nothing
00823         kdDebug() << "Command: NOP" << endl;
00824         i += strlen( "NOP" );
00825 
00826       } else if ( cmd.startsWith( "CLEAR" ) ) {
00827         // clear body buffer; not too useful yet
00828         kdDebug() << "Command: CLEAR" << endl;
00829         i += strlen( "CLEAR" );
00830         body = "";
00831         mMsg->setCursorPos( 0 );
00832 
00833       } else if ( cmd.startsWith( "DEBUGOFF" ) ) {
00834         // turn off debug
00835         kdDebug() << "Command: DEBUGOFF" << endl;
00836         i += strlen( "DEBUGOFF" );
00837         mDebug = false;
00838 
00839       } else if ( cmd.startsWith( "DEBUG" ) ) {
00840         // turn on debug
00841         kdDebug() << "Command: DEBUG" << endl;
00842         i += strlen( "DEBUG" );
00843         mDebug = true;
00844 
00845       } else if ( cmd.startsWith( "CURSOR" ) ) {
00846         // turn on debug
00847         kdDebug() << "Command: CURSOR" << endl;
00848         i += strlen( "CURSOR" );
00849         mMsg->setCursorPos( body.length() );
00850 
00851       } else {
00852         // wrong command, do nothing
00853         body.append( c );
00854       }
00855 
00856     } else if ( dnl && ( c == '\n' || c == '\r') ) {
00857       // skip
00858       if ( ( c == '\n' && tmpl[i + 1] == '\r' ) ||
00859            ( c == '\r' && tmpl[i + 1] == '\n' ) ) {
00860         // skip one more
00861         i += 1;
00862       }
00863       dnl = false;
00864     } else {
00865       body.append( c );
00866     }
00867   }
00868 
00869   addProcessedBodyToMessage( body );
00870 }
00871 
00872 TQString TemplateParser::messageText( bool allowSelectionOnly )
00873 {
00874   if ( !mSelection.isEmpty() && allowSelectionOnly )
00875     return mSelection;
00876 
00877   // No selection text, therefore we need to parse the object tree ourselves to get
00878   partNode *root = parsedObjectTree();
00879   return mOrigMsg->asPlainTextFromObjectTree( root, shouldStripSignature(), mAllowDecryption );
00880 }
00881 
00882 partNode* TemplateParser::parsedObjectTree()
00883 {
00884   if ( mOrigRoot )
00885     return mOrigRoot;
00886 
00887   mOrigRoot = partNode::fromMessage( mOrigMsg );
00888   ObjectTreeParser otp; // all defaults are ok
00889   otp.parseObjectTree( mOrigRoot );
00890   return mOrigRoot;
00891 }
00892 
00893 void TemplateParser::addProcessedBodyToMessage( const TQString &body )
00894 {
00895   if ( mAppend ) {
00896 
00897     // ### What happens here if the body is multipart or in some way encoded?
00898     TQCString msg_body = mMsg->body();
00899     msg_body.append( body.utf8() );
00900     mMsg->setBody( msg_body );
00901   }
00902   else {
00903 
00904     // Get the attachments of the original mail
00905     partNode *root = parsedObjectTree();
00906     AttachmentCollector ac;
00907     ac.collectAttachmentsFrom( root );
00908 
00909     // Now, delete the old content and set the new content, which
00910     // is either only the new text or the new text with some attachments.
00911     mMsg->deleteBodyParts();
00912 
00913     // Set To and CC from the template
00914     if ( mMode == Forward ) {
00915       if ( !mTo.isEmpty() ) {
00916         mMsg->setTo( mMsg->to() + ',' + mTo );
00917       }
00918       if ( !mCC.isEmpty() )
00919         mMsg->setCc( mMsg->cc() + ',' + mCC );
00920     }
00921 
00922     // If we have no attachment, simply create a text/plain part and
00923     // set the processed template text as the body
00924     if ( ac.attachments().empty() || mMode != Forward ) {
00925       mMsg->headers().ContentType().FromString( DwString() ); // to get rid of old boundary
00926       mMsg->headers().ContentType().Parse();
00927       mMsg->headers().ContentType().SetType( DwMime::kTypeText );
00928       mMsg->headers().ContentType().SetSubtype( DwMime::kSubtypePlain );
00929       mMsg->headers().Assemble();
00930       mMsg->setBodyFromUnicode( body );
00931       mMsg->assembleIfNeeded();
00932     }
00933 
00934     // If we have some attachments, create a multipart/mixed mail and
00935     // add the normal body as well as the attachments
00936     else
00937     {
00938       mMsg->headers().ContentType().SetType( DwMime::kTypeMultipart );
00939       mMsg->headers().ContentType().SetSubtype( DwMime::kSubtypeMixed );
00940       mMsg->headers().ContentType().CreateBoundary( 0 );
00941 
00942       KMMessagePart textPart;
00943       textPart.setBodyFromUnicode( body );
00944       mMsg->addDwBodyPart( mMsg->createDWBodyPart( &textPart ) );
00945       mMsg->assembleIfNeeded();
00946 
00947       int attachmentNumber = 1;
00948       for ( std::vector<partNode*>::const_iterator it = ac.attachments().begin();
00949             it != ac.attachments().end(); ++it, attachmentNumber++ ) {
00950 
00951         // When adding this body part, make sure to _not_ add the next bodypart
00952         // as well, which mimelib would do, therefore creating a mail with many
00953         // duplicate attachments (so many that KMail runs out of memory, in fact).
00954         // Body::AddBodyPart is very misleading here...
00955         ( *it )->dwPart()->SetNext( 0 );
00956 
00957         DwBodyPart *cloned = static_cast<DwBodyPart*>( ( *it )->dwPart()->Clone() );
00958 
00959         // If the content type has no name or filename parameter, add one, since otherwise the name
00960         // would be empty in the attachment view of the composer, which looks confusing
00961         if ( cloned->Headers().HasContentType() ) {
00962           DwMediaType &ct = cloned->Headers().ContentType();
00963 
00964           // Converting to a string here, since DwMediaType does not have a HasParameter() function
00965           TQString ctStr = ct.AsString().c_str();
00966           if ( !ctStr.lower().contains( "name=" ) && !ctStr.lower().contains( "filename=" ) ) {
00967             DwParameter *nameParameter = new DwParameter;
00968             nameParameter->SetAttribute( "name" );
00969             nameParameter->SetValue( Util::dwString( KMMsgBase::encodeRFC2231StringAutoDetectCharset(
00970                        i18n( "Attachment %1" ).arg( attachmentNumber ) ) ) );
00971             ct.AddParameter( nameParameter );
00972           }
00973         }
00974 
00975         mMsg->addDwBodyPart( cloned );
00976         mMsg->assembleIfNeeded();
00977       }
00978     }
00979   }
00980 }
00981 
00982 TQString TemplateParser::findCustomTemplate( const TQString &tmplName )
00983 {
00984   CTemplates t( tmplName );
00985   mTo = t.to();
00986   mCC = t.cC();
00987   TQString content = t.content();
00988   if ( !content.isEmpty() ) {
00989     return content;
00990   } else {
00991     return findTemplate();
00992   }
00993 }
00994 
00995 TQString TemplateParser::findTemplate()
00996 {
00997   // import 'Phrases' if it not done yet
00998   if ( !GlobalSettings::self()->phrasesConverted() ) {
00999     TemplatesConfiguration::importFromPhrases();
01000   }
01001 
01002   // kdDebug() << "Trying to find template for mode " << mode << endl;
01003 
01004   TQString tmpl;
01005 
01006   if ( !mFolder ) { // find folder message belongs to
01007     mFolder = mMsg->parent();
01008     if ( !mFolder ) {
01009       if ( mOrigMsg ) {
01010         mFolder = mOrigMsg->parent();
01011       }
01012       if ( !mFolder ) {
01013         kdDebug(5006) << "Oops! No folder for message" << endl;
01014       }
01015     }
01016   }
01017   kdDebug(5006) << "Folder found: " << mFolder << endl;
01018 
01019   if ( mFolder )  // only if a folder was found
01020   {
01021     TQString fid = mFolder->idString();
01022     Templates fconf( fid );
01023     if ( fconf.useCustomTemplates() ) {   // does folder use custom templates?
01024       switch( mMode ) {
01025       case NewMessage:
01026         tmpl = fconf.templateNewMessage();
01027         break;
01028       case Reply:
01029         tmpl = fconf.templateReply();
01030         break;
01031       case ReplyAll:
01032         tmpl = fconf.templateReplyAll();
01033         break;
01034       case Forward:
01035         tmpl = fconf.templateForward();
01036         break;
01037       default:
01038         kdDebug(5006) << "Unknown message mode: " << mMode << endl;
01039         return "";
01040       }
01041       mQuoteString = fconf.quoteString();
01042       if ( !tmpl.isEmpty() ) {
01043         return tmpl;  // use folder-specific template
01044       }
01045     }
01046   }
01047 
01048   if ( !mIdentity ) { // find identity message belongs to
01049     mIdentity = mMsg->identityUoid();
01050     if ( !mIdentity && mOrigMsg ) {
01051       mIdentity = mOrigMsg->identityUoid();
01052     }
01053     mIdentity = kmkernel->identityManager()->identityForUoidOrDefault( mIdentity ).uoid();
01054     if ( !mIdentity ) {
01055       kdDebug(5006) << "Oops! No identity for message" << endl;
01056     }
01057   }
01058   kdDebug(5006) << "Identity found: " << mIdentity << endl;
01059 
01060   TQString iid;
01061   if ( mIdentity ) {
01062     iid = TQString("IDENTITY_%1").arg( mIdentity ); // templates ID for that identity
01063   }
01064   else {
01065     iid = "IDENTITY_NO_IDENTITY"; // templates ID for no identity
01066   }
01067 
01068   Templates iconf( iid );
01069   if ( iconf.useCustomTemplates() ) { // does identity use custom templates?
01070     switch( mMode ) {
01071     case NewMessage:
01072       tmpl = iconf.templateNewMessage();
01073       break;
01074     case Reply:
01075       tmpl = iconf.templateReply();
01076       break;
01077     case ReplyAll:
01078       tmpl = iconf.templateReplyAll();
01079       break;
01080     case Forward:
01081       tmpl = iconf.templateForward();
01082       break;
01083     default:
01084       kdDebug(5006) << "Unknown message mode: " << mMode << endl;
01085       return "";
01086     }
01087     mQuoteString = iconf.quoteString();
01088     if ( !tmpl.isEmpty() ) {
01089       return tmpl;  // use identity-specific template
01090     }
01091   }
01092 
01093   switch( mMode ) { // use the global template
01094   case NewMessage:
01095     tmpl = GlobalSettings::self()->templateNewMessage();
01096     break;
01097   case Reply:
01098     tmpl = GlobalSettings::self()->templateReply();
01099     break;
01100   case ReplyAll:
01101     tmpl = GlobalSettings::self()->templateReplyAll();
01102     break;
01103   case Forward:
01104     tmpl = GlobalSettings::self()->templateForward();
01105     break;
01106   default:
01107     kdDebug(5006) << "Unknown message mode: " << mMode << endl;
01108     return "";
01109   }
01110 
01111   mQuoteString = GlobalSettings::self()->quoteString();
01112   return tmpl;
01113 }
01114 
01115 TQString TemplateParser::pipe( const TQString &cmd, const TQString &buf )
01116 {
01117   mPipeOut = "";
01118   mPipeErr = "";
01119   mPipeRc = 0;
01120 
01121   KProcess proc;
01122   TQCString data = buf.local8Bit();
01123 
01124   // kdDebug() << "Command data: " << data << endl;
01125 
01126   proc << KShell::splitArgs( cmd, KShell::TildeExpand );
01127   proc.setUseShell( true );
01128   connect( &proc, TQT_SIGNAL( receivedStdout( KProcess *, char *, int ) ),
01129            this, TQT_SLOT( onReceivedStdout( KProcess *, char *, int ) ) );
01130   connect( &proc, TQT_SIGNAL( receivedStderr( KProcess *, char *, int ) ),
01131            this, TQT_SLOT( onReceivedStderr( KProcess *, char *, int ) ) );
01132   connect( &proc, TQT_SIGNAL( wroteStdin( KProcess * ) ),
01133            this, TQT_SLOT( onWroteStdin( KProcess * ) ) );
01134 
01135   if ( proc.start( KProcess::NotifyOnExit, KProcess::All ) ) {
01136 
01137     bool pipe_filled = proc.writeStdin( data, data.length() );
01138     if ( pipe_filled ) {
01139       proc.closeStdin();
01140 
01141       bool exited = proc.wait( PipeTimeout );
01142       if ( exited ) {
01143 
01144         if ( proc.normalExit() ) {
01145 
01146           mPipeRc = proc.exitStatus();
01147           if ( mPipeRc != 0 && mDebug ) {
01148             if ( mPipeErr.isEmpty() ) {
01149               KMessageBox::error( 0,
01150                                   i18n( "Pipe command exit with status %1: %2").
01151                                   arg( mPipeRc ).arg( cmd ) );
01152             } else {
01153               KMessageBox::detailedError( 0,
01154                                           i18n( "Pipe command exit with status %1: %2" ).
01155                                           arg( mPipeRc ).arg( cmd ), mPipeErr );
01156             }
01157           }
01158 
01159         } else {
01160 
01161           mPipeRc = -( proc.exitSignal() );
01162           if ( mPipeRc != 0 && mDebug ) {
01163             if ( mPipeErr.isEmpty() ) {
01164               KMessageBox::error( 0,
01165                                   i18n( "Pipe command killed by signal %1: %2" ).
01166                                   arg( -(mPipeRc) ).arg( cmd ) );
01167             } else {
01168               KMessageBox::detailedError( 0,
01169                                           i18n( "Pipe command killed by signal %1: %2" ).
01170                                           arg( -(mPipeRc) ).arg( cmd ), mPipeErr );
01171             }
01172           }
01173         }
01174 
01175       } else {
01176         // process does not exited after TemplateParser::PipeTimeout seconds, kill it
01177         proc.kill();
01178         proc.detach();
01179         if ( mDebug ) {
01180           KMessageBox::error( 0,
01181                               i18n( "Pipe command did not finish within %1 seconds: %2" ).
01182                               arg( PipeTimeout ).arg( cmd ) );
01183         }
01184       }
01185 
01186     } else {
01187       // can`t write to stdin of process
01188       proc.kill();
01189       proc.detach();
01190       if ( mDebug ) {
01191         if ( mPipeErr.isEmpty() ) {
01192           KMessageBox::error( 0,
01193                               i18n( "Cannot write to process stdin: %1" ).arg( cmd ) );
01194         } else {
01195           KMessageBox::detailedError( 0,
01196                                       i18n( "Cannot write to process stdin: %1" ).
01197                                       arg( cmd ), mPipeErr );
01198         }
01199       }
01200     }
01201 
01202   } else if ( mDebug ) {
01203     KMessageBox::error( 0,
01204                         i18n( "Cannot start pipe command from template: %1" ).
01205                         arg( cmd ) );
01206   }
01207 
01208   return mPipeOut;
01209 }
01210 
01211 void TemplateParser::onProcessExited( KProcess *proc )
01212 {
01213   Q_UNUSED( proc );
01214   // do nothing for now
01215 }
01216 
01217 void TemplateParser::onReceivedStdout( KProcess *proc, char *buffer, int buflen )
01218 {
01219   Q_UNUSED( proc );
01220   mPipeOut += TQString::fromLocal8Bit( buffer, buflen );
01221 }
01222 
01223 void TemplateParser::onReceivedStderr( KProcess *proc, char *buffer, int buflen )
01224 {
01225   Q_UNUSED( proc );
01226   mPipeErr += TQString::fromLocal8Bit( buffer, buflen );
01227 }
01228 
01229 void TemplateParser::onWroteStdin( KProcess *proc )
01230 {
01231   proc->closeStdin();
01232 }
01233 
01234 #include "templateparser.moc"