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