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