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( "OADDRESSEESADDR" ) ) {
00397         kdDebug() << "Command: OADDRESSEESADDR";
00398         i += strlen( "OADDRESSEESADDR" );
00399         const QString to = mOrigMsg->to();
00400         const QString cc = mOrigMsg->cc();
00401         if ( !to.isEmpty() )
00402           body.append( i18n( "To:" ) + ' ' + to );
00403         if ( !to.isEmpty() && !cc.isEmpty() )
00404           body.append( '\n' );
00405         if ( !cc.isEmpty() )
00406           body.append( i18n( "CC:" ) + ' ' +  cc );
00407 
00408       } else if ( cmd.startsWith( "CCADDR" ) ) {
00409         kdDebug() << "Command: CCADDR" << endl;
00410         i += strlen( "CCADDR" );
00411         QString str = mMsg->cc();
00412         body.append( str );
00413 
00414       } else if ( cmd.startsWith( "CCNAME" ) ) {
00415         kdDebug() << "Command: CCNAME" << endl;
00416         i += strlen( "CCNAME" );
00417         QString str = mMsg->ccStrip();
00418         body.append( str );
00419 
00420       } else if ( cmd.startsWith( "CCFNAME" ) ) {
00421         kdDebug() << "Command: CCFNAME" << endl;
00422         i += strlen( "CCFNAME" );
00423         QString str = mMsg->ccStrip();
00424         body.append( getFName( str ) );
00425 
00426       } else if ( cmd.startsWith( "CCLNAME" ) ) {
00427         kdDebug() << "Command: CCLNAME" << endl;
00428         i += strlen( "CCLNAME" );
00429         QString str = mMsg->ccStrip();
00430         body.append( getLName( str ) );
00431 
00432       } else if ( cmd.startsWith( "TOADDR" ) ) {
00433         kdDebug() << "Command: TOADDR" << endl;
00434         i += strlen( "TOADDR" );
00435         QString str = mMsg->to();
00436         body.append( str );
00437 
00438       } else if ( cmd.startsWith( "TONAME" ) ) {
00439         kdDebug() << "Command: TONAME" << endl;
00440         i += strlen( "TONAME" );
00441         QString str = mMsg->toStrip();
00442         body.append( str );
00443 
00444       } else if ( cmd.startsWith( "TOFNAME" ) ) {
00445         kdDebug() << "Command: TOFNAME" << endl;
00446         i += strlen( "TOFNAME" );
00447         QString str = mMsg->toStrip();
00448         body.append( getFName( str ) );
00449 
00450       } else if ( cmd.startsWith( "TOLNAME" ) ) {
00451         kdDebug() << "Command: TOLNAME" << endl;
00452         i += strlen( "TOLNAME" );
00453         QString str = mMsg->toStrip();
00454         body.append( getLName( str ) );
00455 
00456       } else if ( cmd.startsWith( "TOLIST" ) ) {
00457         kdDebug() << "Command: TOLIST" << endl;
00458         i += strlen( "TOLIST" );
00459         QString str = mMsg->to();
00460         body.append( str );
00461 
00462       } else if ( cmd.startsWith( "FROMADDR" ) ) {
00463         kdDebug() << "Command: FROMADDR" << endl;
00464         i += strlen( "FROMADDR" );
00465         QString str = mMsg->from();
00466         body.append( str );
00467 
00468       } else if ( cmd.startsWith( "FROMNAME" ) ) {
00469         kdDebug() << "Command: FROMNAME" << endl;
00470         i += strlen( "FROMNAME" );
00471         QString str = mMsg->fromStrip();
00472         body.append( str );
00473 
00474       } else if ( cmd.startsWith( "FROMFNAME" ) ) {
00475         kdDebug() << "Command: FROMFNAME" << endl;
00476         i += strlen( "FROMFNAME" );
00477         QString str = mMsg->fromStrip();
00478         body.append( getFName( str ) );
00479 
00480       } else if ( cmd.startsWith( "FROMLNAME" ) ) {
00481         kdDebug() << "Command: FROMLNAME" << endl;
00482         i += strlen( "FROMLNAME" );
00483         QString str = mMsg->fromStrip();
00484         body.append( getLName( str ) );
00485 
00486       } else if ( cmd.startsWith( "FULLSUBJECT" ) ) {
00487         kdDebug() << "Command: FULLSUBJECT" << endl;
00488         i += strlen( "FULLSUBJECT" );
00489         QString str = mMsg->subject();
00490         body.append( str );
00491 
00492       } else if ( cmd.startsWith( "FULLSUBJ" ) ) {
00493         kdDebug() << "Command: FULLSUBJ" << endl;
00494         i += strlen( "FULLSUBJ" );
00495         QString str = mMsg->subject();
00496         body.append( str );
00497 
00498       } else if ( cmd.startsWith( "MSGID" ) ) {
00499         kdDebug() << "Command: MSGID" << endl;
00500         i += strlen( "MSGID" );
00501         QString str = mMsg->id();
00502         body.append( str );
00503 
00504       } else if ( cmd.startsWith( "OHEADER=" ) ) {
00505         // insert specified content of header from original message
00506         kdDebug() << "Command: OHEADER=" << endl;
00507         QString q;
00508         int len = parseQuotes( "OHEADER=", cmd, q );
00509         i += len;
00510         if ( mOrigMsg ) {
00511           QString hdr = q;
00512           QString str = mOrigMsg->headerFields(hdr.local8Bit() ).join( ", " );
00513           body.append( str );
00514         }
00515 
00516       } else if ( cmd.startsWith( "HEADER=" ) ) {
00517         // insert specified content of header from current message
00518         kdDebug() << "Command: HEADER=" << endl;
00519         QString q;
00520         int len = parseQuotes( "HEADER=", cmd, q );
00521         i += len;
00522         QString hdr = q;
00523         QString str = mMsg->headerFields(hdr.local8Bit() ).join( ", " );
00524         body.append( str );
00525 
00526       } else if ( cmd.startsWith( "HEADER( " ) ) {
00527         // insert specified content of header from current message
00528         kdDebug() << "Command: HEADER( " << endl;
00529         QRegExp re = QRegExp( "^HEADER\\((.+)\\)" );
00530         re.setMinimal( true );
00531         int res = re.search( cmd );
00532         if ( res != 0 ) {
00533           // something wrong
00534           i += strlen( "HEADER( " );
00535         } else {
00536           i += re.matchedLength();
00537           QString hdr = re.cap( 1 );
00538           QString str = mMsg->headerFields( hdr.local8Bit() ).join( ", " );
00539           body.append( str );
00540         }
00541 
00542       } else if ( cmd.startsWith( "OCCADDR" ) ) {
00543         kdDebug() << "Command: OCCADDR" << endl;
00544         i += strlen( "OCCADDR" );
00545         if ( mOrigMsg ) {
00546           QString str = mOrigMsg->cc();
00547           body.append( str );
00548         }
00549 
00550       } else if ( cmd.startsWith( "OCCNAME" ) ) {
00551         kdDebug() << "Command: OCCNAME" << endl;
00552         i += strlen( "OCCNAME" );
00553         if ( mOrigMsg ) {
00554           QString str = mOrigMsg->ccStrip();
00555           body.append( str );
00556         }
00557 
00558       } else if ( cmd.startsWith( "OCCFNAME" ) ) {
00559         kdDebug() << "Command: OCCFNAME" << endl;
00560         i += strlen( "OCCFNAME" );
00561         if ( mOrigMsg ) {
00562           QString str = mOrigMsg->ccStrip();
00563           body.append( getFName( str ) );
00564         }
00565 
00566       } else if ( cmd.startsWith( "OCCLNAME" ) ) {
00567         kdDebug() << "Command: OCCLNAME" << endl;
00568         i += strlen( "OCCLNAME" );
00569         if ( mOrigMsg ) {
00570           QString str = mOrigMsg->ccStrip();
00571           body.append( getLName( str ) );
00572         }
00573 
00574       } else if ( cmd.startsWith( "OTOADDR" ) ) {
00575         kdDebug() << "Command: OTOADDR" << endl;
00576         i += strlen( "OTOADDR" );
00577         if ( mOrigMsg ) {
00578           QString str = mOrigMsg->to();
00579           body.append( str );
00580         }
00581 
00582       } else if ( cmd.startsWith( "OTONAME" ) ) {
00583         kdDebug() << "Command: OTONAME" << endl;
00584         i += strlen( "OTONAME" );
00585         if ( mOrigMsg ) {
00586           QString str = mOrigMsg->toStrip();
00587           body.append( str );
00588         }
00589 
00590       } else if ( cmd.startsWith( "OTOFNAME" ) ) {
00591         kdDebug() << "Command: OTOFNAME" << endl;
00592         i += strlen( "OTOFNAME" );
00593         if ( mOrigMsg ) {
00594           QString str = mOrigMsg->toStrip();
00595           body.append( getFName( str ) );
00596         }
00597 
00598       } else if ( cmd.startsWith( "OTOLNAME" ) ) {
00599         kdDebug() << "Command: OTOLNAME" << endl;
00600         i += strlen( "OTOLNAME" );
00601         if ( mOrigMsg ) {
00602           QString str = mOrigMsg->toStrip();
00603           body.append( getLName( str ) );
00604         }
00605 
00606       } else if ( cmd.startsWith( "OTOLIST" ) ) {
00607         kdDebug() << "Command: OTOLIST" << endl;
00608         i += strlen( "OTOLIST" );
00609         if ( mOrigMsg ) {
00610           QString str = mOrigMsg->to();
00611           body.append( str );
00612         }
00613 
00614       } else if ( cmd.startsWith( "OTO" ) ) {
00615         kdDebug() << "Command: OTO" << endl;
00616         i += strlen( "OTO" );
00617         if ( mOrigMsg ) {
00618           QString str = mOrigMsg->to();
00619           body.append( str );
00620         }
00621 
00622       } else if ( cmd.startsWith( "OFROMADDR" ) ) {
00623         kdDebug() << "Command: OFROMADDR" << endl;
00624         i += strlen( "OFROMADDR" );
00625         if ( mOrigMsg ) {
00626           QString str = mOrigMsg->from();
00627           body.append( str );
00628         }
00629 
00630       } else if ( cmd.startsWith( "OFROMNAME" ) ) {
00631         kdDebug() << "Command: OFROMNAME" << endl;
00632         i += strlen( "OFROMNAME" );
00633         if ( mOrigMsg ) {
00634           QString str = mOrigMsg->fromStrip();
00635           body.append( str );
00636         }
00637 
00638       } else if ( cmd.startsWith( "OFROMFNAME" ) ) {
00639         kdDebug() << "Command: OFROMFNAME" << endl;
00640         i += strlen( "OFROMFNAME" );
00641         if ( mOrigMsg ) {
00642           QString str = mOrigMsg->fromStrip();
00643           body.append( getFName( str ) );
00644         }
00645 
00646       } else if ( cmd.startsWith( "OFROMLNAME" ) ) {
00647         kdDebug() << "Command: OFROMLNAME" << endl;
00648         i += strlen( "OFROMLNAME" );
00649         if ( mOrigMsg ) {
00650           QString str = mOrigMsg->fromStrip();
00651           body.append( getLName( str ) );
00652         }
00653 
00654       } else if ( cmd.startsWith( "OFULLSUBJECT" ) ) {
00655         kdDebug() << "Command: OFULLSUBJECT" << endl;
00656         i += strlen( "OFULLSUBJECT" );
00657         if ( mOrigMsg ) {
00658           QString str = mOrigMsg->subject();
00659           body.append( str );
00660         }
00661 
00662       } else if ( cmd.startsWith( "OFULLSUBJ" ) ) {
00663         kdDebug() << "Command: OFULLSUBJ" << endl;
00664         i += strlen( "OFULLSUBJ" );
00665         if ( mOrigMsg ) {
00666           QString str = mOrigMsg->subject();
00667           body.append( str );
00668         }
00669 
00670       } else if ( cmd.startsWith( "OMSGID" ) ) {
00671         kdDebug() << "Command: OMSGID" << endl;
00672         i += strlen( "OMSGID" );
00673         if ( mOrigMsg ) {
00674           QString str = mOrigMsg->id();
00675           body.append( str );
00676         }
00677 
00678       } else if ( cmd.startsWith( "DATEEN" ) ) {
00679         kdDebug() << "Command: DATEEN" << endl;
00680         i += strlen( "DATEEN" );
00681         QDateTime date = QDateTime::currentDateTime();
00682         KLocale locale( "C" );
00683         QString str = locale.formatDate( date.date(), false );
00684         body.append( str );
00685 
00686       } else if ( cmd.startsWith( "DATESHORT" ) ) {
00687         kdDebug() << "Command: DATESHORT" << endl;
00688         i += strlen( "DATESHORT" );
00689         QDateTime date = QDateTime::currentDateTime();
00690         QString str = KGlobal::locale()->formatDate( date.date(), true );
00691         body.append( str );
00692 
00693       } else if ( cmd.startsWith( "DATE" ) ) {
00694         kdDebug() << "Command: DATE" << endl;
00695         i += strlen( "DATE" );
00696         QDateTime date = QDateTime::currentDateTime();
00697         QString str = KGlobal::locale()->formatDate( date.date(), false );
00698         body.append( str );
00699 
00700       } else if ( cmd.startsWith( "DOW" ) ) {
00701         kdDebug() << "Command: DOW" << endl;
00702         i += strlen( "DOW" );
00703         QDateTime date = QDateTime::currentDateTime();
00704         QString str = KGlobal::locale()->calendar()->weekDayName( date.date(), false );
00705         body.append( str );
00706 
00707       } else if ( cmd.startsWith( "TIMELONGEN" ) ) {
00708         kdDebug() << "Command: TIMELONGEN" << endl;
00709         i += strlen( "TIMELONGEN" );
00710         QDateTime date = QDateTime::currentDateTime();
00711         KLocale locale( "C");
00712         QString str = locale.formatTime( date.time(), true );
00713         body.append( str );
00714 
00715       } else if ( cmd.startsWith( "TIMELONG" ) ) {
00716         kdDebug() << "Command: TIMELONG" << endl;
00717         i += strlen( "TIMELONG" );
00718         QDateTime date = QDateTime::currentDateTime();
00719         QString str = KGlobal::locale()->formatTime( date.time(), true );
00720         body.append( str );
00721 
00722       } else if ( cmd.startsWith( "TIME" ) ) {
00723         kdDebug() << "Command: TIME" << endl;
00724         i += strlen( "TIME" );
00725         QDateTime date = QDateTime::currentDateTime();
00726         QString str = KGlobal::locale()->formatTime( date.time(), false );
00727         body.append( str );
00728 
00729       } else if ( cmd.startsWith( "ODATEEN" ) ) {
00730         kdDebug() << "Command: ODATEEN" << endl;
00731         i += strlen( "ODATEEN" );
00732         if ( mOrigMsg ) {
00733           QDateTime date;
00734           date.setTime_t( mOrigMsg->date() );
00735           KLocale locale( "C");
00736           QString str = locale.formatDate( date.date(), false );
00737           body.append( str );
00738         }
00739 
00740       } else if ( cmd.startsWith( "ODATESHORT") ) {
00741         kdDebug() << "Command: ODATESHORT" << endl;
00742         i += strlen( "ODATESHORT");
00743         if ( mOrigMsg ) {
00744           QDateTime date;
00745           date.setTime_t( mOrigMsg->date() );
00746           QString str = KGlobal::locale()->formatDate( date.date(), true );
00747           body.append( str );
00748         }
00749 
00750       } else if ( cmd.startsWith( "ODATE") ) {
00751         kdDebug() << "Command: ODATE" << endl;
00752         i += strlen( "ODATE");
00753         if ( mOrigMsg ) {
00754           QDateTime date;
00755           date.setTime_t( mOrigMsg->date() );
00756           QString str = KGlobal::locale()->formatDate( date.date(), false );
00757           body.append( str );
00758         }
00759 
00760       } else if ( cmd.startsWith( "ODOW") ) {
00761         kdDebug() << "Command: ODOW" << endl;
00762         i += strlen( "ODOW");
00763         if ( mOrigMsg ) {
00764           QDateTime date;
00765           date.setTime_t( mOrigMsg->date() );
00766           QString str = KGlobal::locale()->calendar()->weekDayName( date.date(), false );
00767           body.append( str );
00768         }
00769 
00770       } else if ( cmd.startsWith( "OTIMELONGEN") ) {
00771         kdDebug() << "Command: OTIMELONGEN" << endl;
00772         i += strlen( "OTIMELONGEN");
00773         if ( mOrigMsg ) {
00774           QDateTime date;
00775           date.setTime_t( mOrigMsg->date() );
00776           KLocale locale( "C");
00777           QString str = locale.formatTime( date.time(), true );
00778           body.append( str );
00779         }
00780 
00781       } else if ( cmd.startsWith( "OTIMELONG") ) {
00782         kdDebug() << "Command: OTIMELONG" << endl;
00783         i += strlen( "OTIMELONG");
00784         if ( mOrigMsg ) {
00785           QDateTime date;
00786           date.setTime_t( mOrigMsg->date() );
00787           QString str = KGlobal::locale()->formatTime( date.time(), true );
00788           body.append( str );
00789         }
00790 
00791       } else if ( cmd.startsWith( "OTIME") ) {
00792         kdDebug() << "Command: OTIME" << endl;
00793         i += strlen( "OTIME");
00794         if ( mOrigMsg ) {
00795           QDateTime date;
00796           date.setTime_t( mOrigMsg->date() );
00797           QString str = KGlobal::locale()->formatTime( date.time(), false );
00798           body.append( str );
00799         }
00800 
00801       } else if ( cmd.startsWith( "BLANK" ) ) {
00802         // do nothing
00803         kdDebug() << "Command: BLANK" << endl;
00804         i += strlen( "BLANK" );
00805 
00806       } else if ( cmd.startsWith( "NOP" ) ) {
00807         // do nothing
00808         kdDebug() << "Command: NOP" << endl;
00809         i += strlen( "NOP" );
00810 
00811       } else if ( cmd.startsWith( "CLEAR" ) ) {
00812         // clear body buffer; not too useful yet
00813         kdDebug() << "Command: CLEAR" << endl;
00814         i += strlen( "CLEAR" );
00815         body = "";
00816         mMsg->setCursorPos( 0 );
00817 
00818       } else if ( cmd.startsWith( "DEBUGOFF" ) ) {
00819         // turn off debug
00820         kdDebug() << "Command: DEBUGOFF" << endl;
00821         i += strlen( "DEBUGOFF" );
00822         mDebug = false;
00823 
00824       } else if ( cmd.startsWith( "DEBUG" ) ) {
00825         // turn on debug
00826         kdDebug() << "Command: DEBUG" << endl;
00827         i += strlen( "DEBUG" );
00828         mDebug = true;
00829 
00830       } else if ( cmd.startsWith( "CURSOR" ) ) {
00831         // turn on debug
00832         kdDebug() << "Command: CURSOR" << endl;
00833         i += strlen( "CURSOR" );
00834         mMsg->setCursorPos( body.length() );
00835 
00836       } else {
00837         // wrong command, do nothing
00838         body.append( c );
00839       }
00840 
00841     } else if ( dnl && ( c == '\n' || c == '\r') ) {
00842       // skip
00843       if ( ( c == '\n' && tmpl[i + 1] == '\r' ) ||
00844            ( c == '\r' && tmpl[i + 1] == '\n' ) ) {
00845         // skip one more
00846         i += 1;
00847       }
00848       dnl = false;
00849     } else {
00850       body.append( c );
00851     }
00852   }
00853 
00854   addProcessedBodyToMessage( body );
00855 }
00856 
00857 QString TemplateParser::messageText( bool allowSelectionOnly )
00858 {
00859   if ( !mSelection.isEmpty() && allowSelectionOnly )
00860     return mSelection;
00861 
00862   // No selection text, therefore we need to parse the object tree ourselves to get
00863   partNode *root = parsedObjectTree();
00864   return mOrigMsg->asPlainTextFromObjectTree( root, true, mAllowDecryption );
00865 }
00866 
00867 partNode* TemplateParser::parsedObjectTree()
00868 {
00869   if ( mOrigRoot )
00870     return mOrigRoot;
00871 
00872   mOrigRoot = partNode::fromMessage( mOrigMsg );
00873   KMail::ObjectTreeParser otp; // all defaults are ok
00874   otp.parseObjectTree( mOrigRoot );
00875   return mOrigRoot;
00876 }
00877 
00878 void TemplateParser::addProcessedBodyToMessage( const QString &body )
00879 {
00880   if ( mAppend ) {
00881 
00882     // ### What happens here if the body is multipart or in some way encoded?
00883     QCString msg_body = mMsg->body();
00884     msg_body.append( body.utf8() );
00885     mMsg->setBody( msg_body );
00886   }
00887   else {
00888 
00889     // Get the attachments of the original mail
00890     partNode *root = parsedObjectTree();
00891     KMail::AttachmentCollector ac;
00892     ac.collectAttachmentsFrom( root );
00893 
00894     // Now, delete the old content and set the new content, which
00895     // is either only the new text or the new text with some attachments.
00896     mMsg->deleteBodyParts();
00897 
00898     // Set To and CC from the template
00899     if ( mMode == Forward ) {
00900       if ( !mTo.isEmpty() ) {
00901         mMsg->setTo( mMsg->to() + ',' + mTo );
00902       }
00903       if ( !mCC.isEmpty() )
00904         mMsg->setCc( mMsg->cc() + ',' + mCC );
00905     }
00906 
00907     // If we have no attachment, simply create a text/plain part and
00908     // set the processed template text as the body
00909     if ( ac.attachments().empty() || mMode != Forward ) {
00910       mMsg->headers().ContentType().FromString( DwString() ); // to get rid of old boundary
00911       mMsg->headers().ContentType().Parse();
00912       mMsg->headers().ContentType().SetType( DwMime::kTypeText );
00913       mMsg->headers().ContentType().SetSubtype( DwMime::kSubtypePlain );
00914       mMsg->headers().Assemble();
00915       mMsg->setBodyFromUnicode( body );
00916       mMsg->assembleIfNeeded();
00917     }
00918 
00919     // If we have some attachments, create a multipart/mixed mail and
00920     // add the normal body as well as the attachments
00921     else
00922     {
00923       mMsg->headers().ContentType().SetType( DwMime::kTypeMultipart );
00924       mMsg->headers().ContentType().SetSubtype( DwMime::kSubtypeMixed );
00925       mMsg->headers().ContentType().CreateBoundary( 0 );
00926 
00927       KMMessagePart textPart;
00928       textPart.setBodyFromUnicode( body );
00929       mMsg->addDwBodyPart( mMsg->createDWBodyPart( &textPart ) );
00930       mMsg->assembleIfNeeded();
00931 
00932       for ( std::vector<partNode*>::const_iterator it = ac.attachments().begin();
00933             it != ac.attachments().end(); ++it ) {
00934 
00935         // When adding this body part, make sure to _not_ add the next bodypart
00936         // as well, which mimelib would do, therefore creating a mail with many
00937         // duplicate attachments (so many that KMail runs out of memory, in fact).
00938         // Body::AddBodyPart is very misleading here...
00939         ( *it )->dwPart()->SetNext( 0 );
00940 
00941         mMsg->addDwBodyPart( static_cast<DwBodyPart*>( ( *it )->dwPart()->Clone() ) );
00942         mMsg->assembleIfNeeded();
00943       }
00944     }
00945   }
00946 }
00947 
00948 QString TemplateParser::findCustomTemplate( const QString &tmplName )
00949 {
00950   CTemplates t( tmplName );
00951   mTo = t.to();
00952   mCC = t.cC();
00953   QString content = t.content();
00954   if ( !content.isEmpty() ) {
00955     return content;
00956   } else {
00957     return findTemplate();
00958   }
00959 }
00960 
00961 QString TemplateParser::findTemplate()
00962 {
00963   // import 'Phrases' if it not done yet
00964   if ( !GlobalSettings::self()->phrasesConverted() ) {
00965     TemplatesConfiguration::importFromPhrases();
00966   }
00967 
00968   // kdDebug() << "Trying to find template for mode " << mode << endl;
00969 
00970   QString tmpl;
00971 
00972   if ( !mFolder ) { // find folder message belongs to
00973     mFolder = mMsg->parent();
00974     if ( !mFolder ) {
00975       if ( mOrigMsg ) {
00976         mFolder = mOrigMsg->parent();
00977       }
00978       if ( !mFolder ) {
00979         kdDebug(5006) << "Oops! No folder for message" << endl;
00980       }
00981     }
00982   }
00983   kdDebug(5006) << "Folder found: " << mFolder << endl;
00984 
00985   if ( mFolder )  // only if a folder was found
00986   {
00987     QString fid = mFolder->idString();
00988     Templates fconf( fid );
00989     if ( fconf.useCustomTemplates() ) {   // does folder use custom templates?
00990       switch( mMode ) {
00991       case NewMessage:
00992         tmpl = fconf.templateNewMessage();
00993         break;
00994       case Reply:
00995         tmpl = fconf.templateReply();
00996         break;
00997       case ReplyAll:
00998         tmpl = fconf.templateReplyAll();
00999         break;
01000       case Forward:
01001         tmpl = fconf.templateForward();
01002         break;
01003       default:
01004         kdDebug(5006) << "Unknown message mode: " << mMode << endl;
01005         return "";
01006       }
01007       mQuoteString = fconf.quoteString();
01008       if ( !tmpl.isEmpty() ) {
01009         return tmpl;  // use folder-specific template
01010       }
01011     }
01012   }
01013 
01014   if ( !mIdentity ) { // find identity message belongs to
01015     mIdentity = mMsg->identityUoid();
01016     if ( !mIdentity && mOrigMsg ) {
01017       mIdentity = mOrigMsg->identityUoid();
01018     }
01019     mIdentity = kmkernel->identityManager()->identityForUoidOrDefault( mIdentity ).uoid();
01020     if ( !mIdentity ) {
01021       kdDebug(5006) << "Oops! No identity for message" << endl;
01022     }
01023   }
01024   kdDebug(5006) << "Identity found: " << mIdentity << endl;
01025 
01026   QString iid;
01027   if ( mIdentity ) {
01028     iid = QString("IDENTITY_%1").arg( mIdentity );  // templates ID for that identity
01029   }
01030   else {
01031     iid = "IDENTITY_NO_IDENTITY"; // templates ID for no identity
01032   }
01033 
01034   Templates iconf( iid );
01035   if ( iconf.useCustomTemplates() ) { // does identity use custom templates?
01036     switch( mMode ) {
01037     case NewMessage:
01038       tmpl = iconf.templateNewMessage();
01039       break;
01040     case Reply:
01041       tmpl = iconf.templateReply();
01042       break;
01043     case ReplyAll:
01044       tmpl = iconf.templateReplyAll();
01045       break;
01046     case Forward:
01047       tmpl = iconf.templateForward();
01048       break;
01049     default:
01050       kdDebug(5006) << "Unknown message mode: " << mMode << endl;
01051       return "";
01052     }
01053     mQuoteString = iconf.quoteString();
01054     if ( !tmpl.isEmpty() ) {
01055       return tmpl;  // use identity-specific template
01056     }
01057   }
01058 
01059   switch( mMode ) { // use the global template
01060   case NewMessage:
01061     tmpl = GlobalSettings::self()->templateNewMessage();
01062     break;
01063   case Reply:
01064     tmpl = GlobalSettings::self()->templateReply();
01065     break;
01066   case ReplyAll:
01067     tmpl = GlobalSettings::self()->templateReplyAll();
01068     break;
01069   case Forward:
01070     tmpl = GlobalSettings::self()->templateForward();
01071     break;
01072   default:
01073     kdDebug(5006) << "Unknown message mode: " << mMode << endl;
01074     return "";
01075   }
01076 
01077   mQuoteString = GlobalSettings::self()->quoteString();
01078   return tmpl;
01079 }
01080 
01081 QString TemplateParser::pipe( const QString &cmd, const QString &buf )
01082 {
01083   mPipeOut = "";
01084   mPipeErr = "";
01085   mPipeRc = 0;
01086 
01087   KProcess proc;
01088   QCString data = buf.local8Bit();
01089 
01090   // kdDebug() << "Command data: " << data << endl;
01091 
01092   proc << KShell::splitArgs( cmd, KShell::TildeExpand );
01093   proc.setUseShell( true );
01094   connect( &proc, SIGNAL( receivedStdout( KProcess *, char *, int ) ),
01095            this, SLOT( onReceivedStdout( KProcess *, char *, int ) ) );
01096   connect( &proc, SIGNAL( receivedStderr( KProcess *, char *, int ) ),
01097            this, SLOT( onReceivedStderr( KProcess *, char *, int ) ) );
01098   connect( &proc, SIGNAL( wroteStdin( KProcess * ) ),
01099            this, SLOT( onWroteStdin( KProcess * ) ) );
01100 
01101   if ( proc.start( KProcess::NotifyOnExit, KProcess::All ) ) {
01102 
01103     bool pipe_filled = proc.writeStdin( data, data.length() );
01104     if ( pipe_filled ) {
01105       proc.closeStdin();
01106 
01107       bool exited = proc.wait( PipeTimeout );
01108       if ( exited ) {
01109 
01110         if ( proc.normalExit() ) {
01111 
01112           mPipeRc = proc.exitStatus();
01113           if ( mPipeRc != 0 && mDebug ) {
01114             if ( mPipeErr.isEmpty() ) {
01115               KMessageBox::error( 0,
01116                                   i18n( "Pipe command exit with status %1: %2").
01117                                   arg( mPipeRc ).arg( cmd ) );
01118             } else {
01119               KMessageBox::detailedError( 0,
01120                                           i18n( "Pipe command exit with status %1: %2" ).
01121                                           arg( mPipeRc ).arg( cmd ), mPipeErr );
01122             }
01123           }
01124 
01125         } else {
01126 
01127           mPipeRc = -( proc.exitSignal() );
01128           if ( mPipeRc != 0 && mDebug ) {
01129             if ( mPipeErr.isEmpty() ) {
01130               KMessageBox::error( 0,
01131                                   i18n( "Pipe command killed by signal %1: %2" ).
01132                                   arg( -(mPipeRc) ).arg( cmd ) );
01133             } else {
01134               KMessageBox::detailedError( 0,
01135                                           i18n( "Pipe command killed by signal %1: %2" ).
01136                                           arg( -(mPipeRc) ).arg( cmd ), mPipeErr );
01137             }
01138           }
01139         }
01140 
01141       } else {
01142         // process does not exited after TemplateParser::PipeTimeout seconds, kill it
01143         proc.kill();
01144         proc.detach();
01145         if ( mDebug ) {
01146           KMessageBox::error( 0,
01147                               i18n( "Pipe command did not finish within %1 seconds: %2" ).
01148                               arg( PipeTimeout ).arg( cmd ) );
01149         }
01150       }
01151 
01152     } else {
01153       // can`t write to stdin of process
01154       proc.kill();
01155       proc.detach();
01156       if ( mDebug ) {
01157         if ( mPipeErr.isEmpty() ) {
01158           KMessageBox::error( 0,
01159                               i18n( "Cannot write to process stdin: %1" ).arg( cmd ) );
01160         } else {
01161           KMessageBox::detailedError( 0,
01162                                       i18n( "Cannot write to process stdin: %1" ).
01163                                       arg( cmd ), mPipeErr );
01164         }
01165       }
01166     }
01167 
01168   } else if ( mDebug ) {
01169     KMessageBox::error( 0,
01170                         i18n( "Cannot start pipe command from template: %1" ).
01171                         arg( cmd ) );
01172   }
01173 
01174   return mPipeOut;
01175 }
01176 
01177 void TemplateParser::onProcessExited( KProcess *proc )
01178 {
01179   Q_UNUSED( proc );
01180   // do nothing for now
01181 }
01182 
01183 void TemplateParser::onReceivedStdout( KProcess *proc, char *buffer, int buflen )
01184 {
01185   Q_UNUSED( proc );
01186   mPipeOut += QString::fromLocal8Bit( buffer, buflen );
01187 }
01188 
01189 void TemplateParser::onReceivedStderr( KProcess *proc, char *buffer, int buflen )
01190 {
01191   Q_UNUSED( proc );
01192   mPipeErr += QString::fromLocal8Bit( buffer, buflen );
01193 }
01194 
01195 void TemplateParser::onWroteStdin( KProcess *proc )
01196 {
01197   proc->closeStdin();
01198 }
01199 
01200 #include "templateparser.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys