/* -*- mode: C++; c-file-style: "gnu" -*- * kmail: KDE mail client * This file: Copyright (C) 2006 Dmitry Morozhnikov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kmmessage.h" #include "kmmsgbase.h" #include "kmfolder.h" #include "templatesconfiguration.h" #include "templatesconfiguration_kfg.h" #include "customtemplates_kfg.h" #include "globalsettings_base.h" #include "kmkernel.h" #include #include #include "partNode.h" #include "attachmentcollector.h" #include "objecttreeparser.h" #include "util.h" #include "templateparser.h" #include using namespace KMail; TemplateParser::TemplateParser( KMMessage *amsg, const Mode amode ) : mMode( amode ), mFolder( 0 ), mIdentity( 0 ), mAllowDecryption( false ), mDebug( false ), mQuoteString( "> " ), mAppend( false ), mOrigRoot( 0 ) { mMsg = amsg; } void TemplateParser::setSelection( const TQString &selection ) { mSelection = selection; } void TemplateParser::setAllowDecryption( const bool allowDecryption ) { mAllowDecryption = allowDecryption; } bool TemplateParser::shouldStripSignature() const { // Only strip the signature when replying, it should be preserved when forwarding return ( mMode == Reply || mMode == ReplyAll) && GlobalSettings::stripSignature(); } TemplateParser::~TemplateParser() { delete mOrigRoot; mOrigRoot = 0; } int TemplateParser::parseQuotes( const TQString &prefix, const TQString &str, TQString "e ) const { int pos = prefix.length(); int len; int str_len = str.length(); TQChar qc = '"'; TQChar prev = 0; pos++; len = pos; while ( pos < str_len ) { TQChar c = str[pos]; pos++; len++; if ( prev ) { quote.append( c ); prev = 0; } else { if ( c == '\\' ) { prev = c; } else if ( c == qc ) { break; } else { quote.append( c ); } } } return len; } TQString TemplateParser::getFName( const TQString &str ) { // simple logic: // if there is ',' in name, than format is 'Last, First' // else format is 'First Last' // last resort -- return 'name' from 'name@domain' int sep_pos; TQString res; if ( ( sep_pos = str.find( '@' ) ) > 0 ) { int i; for ( i = (sep_pos - 1); i >= 0; --i ) { TQChar c = str[i]; if ( c.isLetterOrNumber() ) { res.prepend( c ); } else { break; } } } else if ( ( sep_pos = str.find(',') ) > 0 ) { unsigned int i; bool begin = false; for ( i = sep_pos; i < str.length(); ++i ) { TQChar c = str[i]; if ( c.isLetterOrNumber() ) { begin = true; res.append( c ); } else if ( begin ) { break; } } } else { unsigned int i; for ( i = 0; i < str.length(); ++i ) { TQChar c = str[i]; if ( c.isLetterOrNumber() ) { res.append( c ); } else { break; } } } return res; } TQString TemplateParser::getLName( const TQString &str ) { // simple logic: // if there is ',' in name, than format is 'Last, First' // else format is 'First Last' int sep_pos; TQString res; if ( ( sep_pos = str.find(',') ) > 0 ) { int i; for ( i = sep_pos; i >= 0; --i ) { TQChar c = str[i]; if ( c.isLetterOrNumber() ) { res.prepend( c ); } else { break; } } } else { if ( ( sep_pos = str.find( ' ' ) ) > 0 ) { unsigned int i; bool begin = false; for ( i = sep_pos; i < str.length(); ++i ) { TQChar c = str[i]; if ( c.isLetterOrNumber() ) { begin = true; res.append( c ); } else if ( begin ) { break; } } } } return res; } void TemplateParser::process( KMMessage *aorig_msg, KMFolder *afolder, bool append ) { mAppend = append; mOrigMsg = aorig_msg; mFolder = afolder; TQString tmpl = findTemplate(); return processWithTemplate( tmpl ); } void TemplateParser::process( const TQString &tmplName, KMMessage *aorig_msg, KMFolder *afolder, bool append ) { mAppend = append; mOrigMsg = aorig_msg; mFolder = afolder; TQString tmpl = findCustomTemplate( tmplName ); return processWithTemplate( tmpl ); } void TemplateParser::processWithTemplate( const TQString &tmpl ) { TQString body; int tmpl_len = tmpl.length(); bool dnl = false; for ( int i = 0; i < tmpl_len; ++i ) { TQChar c = tmpl[i]; // kdDebug() << "Next char: " << c << endl; if ( c == '%' ) { TQString cmd = tmpl.mid( i + 1 ); if ( cmd.startsWith( "-" ) ) { // dnl kdDebug() << "Command: -" << endl; dnl = true; i += 1; } else if ( cmd.startsWith( "REM=" ) ) { // comments kdDebug() << "Command: REM=" << endl; TQString q; int len = parseQuotes( "REM=", cmd, q ); i += len; } else if ( cmd.startsWith( "INSERT=" ) ) { // insert content of specified file as is kdDebug() << "Command: INSERT=" << endl; TQString q; int len = parseQuotes( "INSERT=", cmd, q ); i += len; TQString path = KShell::tildeExpand( q ); TQFileInfo finfo( path ); if (finfo.isRelative() ) { path = KShell::homeDir( "" ); path += '/'; path += q; } TQFile file( path ); if ( file.open( IO_ReadOnly ) ) { TQByteArray content = file.readAll(); TQString str = TQString::fromLocal8Bit( content, content.size() ); body.append( str ); } else if ( mDebug ) { KMessageBox::error( 0, i18n( "Cannot insert content from file %1: %2" ). arg( path ).arg( file.errorString() ) ); } } else if ( cmd.startsWith( "SYSTEM=" ) ) { // insert content of specified file as is kdDebug() << "Command: SYSTEM=" << endl; TQString q; int len = parseQuotes( "SYSTEM=", cmd, q ); i += len; TQString pipe_cmd = q; TQString str = pipe( pipe_cmd, "" ); body.append( str ); } else if ( cmd.startsWith( "PUT=" ) ) { // insert content of specified file as is kdDebug() << "Command: PUT=" << endl; TQString q; int len = parseQuotes( "PUT=", cmd, q ); i += len; TQString path = KShell::tildeExpand( q ); TQFileInfo finfo( path ); if (finfo.isRelative() ) { path = KShell::homeDir( "" ); path += '/'; path += q; } TQFile file( path ); if ( file.open( IO_ReadOnly ) ) { TQByteArray content = file.readAll(); body.append( TQString::fromLocal8Bit( content, content.size() ) ); } else if ( mDebug ) { KMessageBox::error( 0, i18n( "Cannot insert content from file %1: %2"). arg( path ).arg(file.errorString() )); } } else if ( cmd.startsWith( "QUOTEPIPE=" ) ) { // pipe message body throw command and insert it as quotation kdDebug() << "Command: QUOTEPIPE=" << endl; TQString q; int len = parseQuotes( "QUOTEPIPE=", cmd, q ); i += len; TQString pipe_cmd = q; if ( mOrigMsg ) { TQString str = pipe( pipe_cmd, messageText( false ) ); TQString quote = mOrigMsg->asQuotedString( "", mQuoteString, str, shouldStripSignature(), mAllowDecryption ); body.append( quote ); } } else if ( cmd.startsWith( "QUOTE" ) ) { kdDebug() << "Command: QUOTE" << endl; i += strlen( "QUOTE" ); if ( mOrigMsg ) { TQString quote = mOrigMsg->asQuotedString( "", mQuoteString, messageText( true ), shouldStripSignature(), mAllowDecryption ); body.append( quote ); } } else if ( cmd.startsWith( "QHEADERS" ) ) { kdDebug() << "Command: TQHEADERS" << endl; i += strlen( "QHEADERS" ); if ( mOrigMsg ) { TQString quote = mOrigMsg->asQuotedString( "", mQuoteString, mOrigMsg->headerAsSendableString(), false, false ); body.append( quote ); } } else if ( cmd.startsWith( "HEADERS" ) ) { kdDebug() << "Command: HEADERS" << endl; i += strlen( "HEADERS" ); if ( mOrigMsg ) { TQString str = mOrigMsg->headerAsSendableString(); body.append( str ); } } else if ( cmd.startsWith( "TEXTPIPE=" ) ) { // pipe message body throw command and insert it as is kdDebug() << "Command: TEXTPIPE=" << endl; TQString q; int len = parseQuotes( "TEXTPIPE=", cmd, q ); i += len; TQString pipe_cmd = q; if ( mOrigMsg ) { TQString str = pipe(pipe_cmd, messageText( false ) ); body.append( str ); } } else if ( cmd.startsWith( "MSGPIPE=" ) ) { // pipe full message throw command and insert result as is kdDebug() << "Command: MSGPIPE=" << endl; TQString q; int len = parseQuotes( "MSGPIPE=", cmd, q ); i += len; TQString pipe_cmd = q; if ( mOrigMsg ) { TQString str = pipe(pipe_cmd, mOrigMsg->asString() ); body.append( str ); } } else if ( cmd.startsWith( "BODYPIPE=" ) ) { // pipe message body generated so far throw command and insert result as is kdDebug() << "Command: BODYPIPE=" << endl; TQString q; int len = parseQuotes( "BODYPIPE=", cmd, q ); i += len; TQString pipe_cmd = q; TQString str = pipe( pipe_cmd, body ); body.append( str ); } else if ( cmd.startsWith( "CLEARPIPE=" ) ) { // pipe message body generated so far throw command and // insert result as is replacing current body kdDebug() << "Command: CLEARPIPE=" << endl; TQString q; int len = parseQuotes( "CLEARPIPE=", cmd, q ); i += len; TQString pipe_cmd = q; TQString str = pipe( pipe_cmd, body ); body = str; mMsg->setCursorPos( 0 ); } else if ( cmd.startsWith( "TEXT" ) ) { kdDebug() << "Command: TEXT" << endl; i += strlen( "TEXT" ); if ( mOrigMsg ) { TQString quote = messageText( false ); body.append( quote ); } } else if ( cmd.startsWith( "OTEXTSIZE" ) ) { kdDebug() << "Command: OTEXTSIZE" << endl; i += strlen( "OTEXTSIZE" ); if ( mOrigMsg ) { TQString str = TQString( "%1" ).arg( mOrigMsg->body().length() ); body.append( str ); } } else if ( cmd.startsWith( "OTEXT" ) ) { kdDebug() << "Command: OTEXT" << endl; i += strlen( "OTEXT" ); if ( mOrigMsg ) { TQString quote = messageText( false ); body.append( quote ); } } else if ( cmd.startsWith( "OADDRESSEESADDR" ) ) { kdDebug() << "Command: OADDRESSEESADDR" << endl; i += strlen( "OADDRESSEESADDR" ); const TQString to = mOrigMsg->to(); const TQString cc = mOrigMsg->cc(); if ( !to.isEmpty() ) body.append( i18n( "To:" ) + ' ' + to ); if ( !to.isEmpty() && !cc.isEmpty() ) body.append( '\n' ); if ( !cc.isEmpty() ) body.append( i18n( "CC:" ) + ' ' + cc ); } else if ( cmd.startsWith( "CCADDR" ) ) { kdDebug() << "Command: CCADDR" << endl; i += strlen( "CCADDR" ); TQString str = mMsg->cc(); body.append( str ); } else if ( cmd.startsWith( "CCNAME" ) ) { kdDebug() << "Command: CCNAME" << endl; i += strlen( "CCNAME" ); TQString str = mMsg->ccStrip(); body.append( str ); } else if ( cmd.startsWith( "CCFNAME" ) ) { kdDebug() << "Command: CCFNAME" << endl; i += strlen( "CCFNAME" ); TQString str = mMsg->ccStrip(); body.append( getFName( str ) ); } else if ( cmd.startsWith( "CCLNAME" ) ) { kdDebug() << "Command: CCLNAME" << endl; i += strlen( "CCLNAME" ); TQString str = mMsg->ccStrip(); body.append( getLName( str ) ); } else if ( cmd.startsWith( "TOADDR" ) ) { kdDebug() << "Command: TOADDR" << endl; i += strlen( "TOADDR" ); TQString str = mMsg->to(); body.append( str ); } else if ( cmd.startsWith( "TONAME" ) ) { kdDebug() << "Command: TONAME" << endl; i += strlen( "TONAME" ); TQString str = mMsg->toStrip(); body.append( str ); } else if ( cmd.startsWith( "TOFNAME" ) ) { kdDebug() << "Command: TOFNAME" << endl; i += strlen( "TOFNAME" ); TQString str = mMsg->toStrip(); body.append( getFName( str ) ); } else if ( cmd.startsWith( "TOLNAME" ) ) { kdDebug() << "Command: TOLNAME" << endl; i += strlen( "TOLNAME" ); TQString str = mMsg->toStrip(); body.append( getLName( str ) ); } else if ( cmd.startsWith( "TOLIST" ) ) { kdDebug() << "Command: TOLIST" << endl; i += strlen( "TOLIST" ); TQString str = mMsg->to(); body.append( str ); } else if ( cmd.startsWith( "FROMADDR" ) ) { kdDebug() << "Command: FROMADDR" << endl; i += strlen( "FROMADDR" ); TQString str = mMsg->from(); body.append( str ); } else if ( cmd.startsWith( "FROMNAME" ) ) { kdDebug() << "Command: FROMNAME" << endl; i += strlen( "FROMNAME" ); TQString str = mMsg->fromStrip(); body.append( str ); } else if ( cmd.startsWith( "FROMFNAME" ) ) { kdDebug() << "Command: FROMFNAME" << endl; i += strlen( "FROMFNAME" ); TQString str = mMsg->fromStrip(); body.append( getFName( str ) ); } else if ( cmd.startsWith( "FROMLNAME" ) ) { kdDebug() << "Command: FROMLNAME" << endl; i += strlen( "FROMLNAME" ); TQString str = mMsg->fromStrip(); body.append( getLName( str ) ); } else if ( cmd.startsWith( "FULLSUBJECT" ) ) { kdDebug() << "Command: FULLSUBJECT" << endl; i += strlen( "FULLSUBJECT" ); TQString str = mMsg->subject(); body.append( str ); } else if ( cmd.startsWith( "FULLSUBJ" ) ) { kdDebug() << "Command: FULLSUBJ" << endl; i += strlen( "FULLSUBJ" ); TQString str = mMsg->subject(); body.append( str ); } else if ( cmd.startsWith( "MSGID" ) ) { kdDebug() << "Command: MSGID" << endl; i += strlen( "MSGID" ); TQString str = mMsg->id(); body.append( str ); } else if ( cmd.startsWith( "OHEADER=" ) ) { // insert specified content of header from original message kdDebug() << "Command: OHEADER=" << endl; TQString q; int len = parseQuotes( "OHEADER=", cmd, q ); i += len; if ( mOrigMsg ) { TQString hdr = q; TQString str = mOrigMsg->headerFields(hdr.local8Bit() ).join( ", " ); body.append( str ); } } else if ( cmd.startsWith( "HEADER=" ) ) { // insert specified content of header from current message kdDebug() << "Command: HEADER=" << endl; TQString q; int len = parseQuotes( "HEADER=", cmd, q ); i += len; TQString hdr = q; TQString str = mMsg->headerFields(hdr.local8Bit() ).join( ", " ); body.append( str ); } else if ( cmd.startsWith( "HEADER( " ) ) { // insert specified content of header from current message kdDebug() << "Command: HEADER( " << endl; TQRegExp re = TQRegExp( "^HEADER\\((.+)\\)" ); re.setMinimal( true ); int res = re.search( cmd ); if ( res != 0 ) { // something wrong i += strlen( "HEADER( " ); } else { i += re.matchedLength(); TQString hdr = re.cap( 1 ); TQString str = mMsg->headerFields( hdr.local8Bit() ).join( ", " ); body.append( str ); } } else if ( cmd.startsWith( "OCCADDR" ) ) { kdDebug() << "Command: OCCADDR" << endl; i += strlen( "OCCADDR" ); if ( mOrigMsg ) { TQString str = mOrigMsg->cc(); body.append( str ); } } else if ( cmd.startsWith( "OCCNAME" ) ) { kdDebug() << "Command: OCCNAME" << endl; i += strlen( "OCCNAME" ); if ( mOrigMsg ) { TQString str = mOrigMsg->ccStrip(); body.append( str ); } } else if ( cmd.startsWith( "OCCFNAME" ) ) { kdDebug() << "Command: OCCFNAME" << endl; i += strlen( "OCCFNAME" ); if ( mOrigMsg ) { TQString str = mOrigMsg->ccStrip(); body.append( getFName( str ) ); } } else if ( cmd.startsWith( "OCCLNAME" ) ) { kdDebug() << "Command: OCCLNAME" << endl; i += strlen( "OCCLNAME" ); if ( mOrigMsg ) { TQString str = mOrigMsg->ccStrip(); body.append( getLName( str ) ); } } else if ( cmd.startsWith( "OTOADDR" ) ) { kdDebug() << "Command: OTOADDR" << endl; i += strlen( "OTOADDR" ); if ( mOrigMsg ) { TQString str = mOrigMsg->to(); body.append( str ); } } else if ( cmd.startsWith( "OTONAME" ) ) { kdDebug() << "Command: OTONAME" << endl; i += strlen( "OTONAME" ); if ( mOrigMsg ) { TQString str = mOrigMsg->toStrip(); body.append( str ); } } else if ( cmd.startsWith( "OTOFNAME" ) ) { kdDebug() << "Command: OTOFNAME" << endl; i += strlen( "OTOFNAME" ); if ( mOrigMsg ) { TQString str = mOrigMsg->toStrip(); body.append( getFName( str ) ); } } else if ( cmd.startsWith( "OTOLNAME" ) ) { kdDebug() << "Command: OTOLNAME" << endl; i += strlen( "OTOLNAME" ); if ( mOrigMsg ) { TQString str = mOrigMsg->toStrip(); body.append( getLName( str ) ); } } else if ( cmd.startsWith( "OTOLIST" ) ) { kdDebug() << "Command: OTOLIST" << endl; i += strlen( "OTOLIST" ); if ( mOrigMsg ) { TQString str = mOrigMsg->to(); body.append( str ); } } else if ( cmd.startsWith( "OTO" ) ) { kdDebug() << "Command: OTO" << endl; i += strlen( "OTO" ); if ( mOrigMsg ) { TQString str = mOrigMsg->to(); body.append( str ); } } else if ( cmd.startsWith( "OFROMADDR" ) ) { kdDebug() << "Command: OFROMADDR" << endl; i += strlen( "OFROMADDR" ); if ( mOrigMsg ) { TQString str = mOrigMsg->from(); body.append( str ); } } else if ( cmd.startsWith( "OFROMNAME" ) ) { kdDebug() << "Command: OFROMNAME" << endl; i += strlen( "OFROMNAME" ); if ( mOrigMsg ) { TQString str = mOrigMsg->fromStrip(); body.append( str ); } } else if ( cmd.startsWith( "OFROMFNAME" ) ) { kdDebug() << "Command: OFROMFNAME" << endl; i += strlen( "OFROMFNAME" ); if ( mOrigMsg ) { TQString str = mOrigMsg->fromStrip(); body.append( getFName( str ) ); } } else if ( cmd.startsWith( "OFROMLNAME" ) ) { kdDebug() << "Command: OFROMLNAME" << endl; i += strlen( "OFROMLNAME" ); if ( mOrigMsg ) { TQString str = mOrigMsg->fromStrip(); body.append( getLName( str ) ); } } else if ( cmd.startsWith( "OFULLSUBJECT" ) ) { kdDebug() << "Command: OFULLSUBJECT" << endl; i += strlen( "OFULLSUBJECT" ); if ( mOrigMsg ) { TQString str = mOrigMsg->subject(); body.append( str ); } } else if ( cmd.startsWith( "OFULLSUBJ" ) ) { kdDebug() << "Command: OFULLSUBJ" << endl; i += strlen( "OFULLSUBJ" ); if ( mOrigMsg ) { TQString str = mOrigMsg->subject(); body.append( str ); } } else if ( cmd.startsWith( "OMSGID" ) ) { kdDebug() << "Command: OMSGID" << endl; i += strlen( "OMSGID" ); if ( mOrigMsg ) { TQString str = mOrigMsg->id(); body.append( str ); } } else if ( cmd.startsWith( "DATEEN" ) ) { kdDebug() << "Command: DATEEN" << endl; i += strlen( "DATEEN" ); TQDateTime date = TQDateTime::currentDateTime(); TDELocale locale( "C" ); TQString str = locale.formatDate( date.date(), false ); body.append( str ); } else if ( cmd.startsWith( "DATESHORT" ) ) { kdDebug() << "Command: DATESHORT" << endl; i += strlen( "DATESHORT" ); TQDateTime date = TQDateTime::currentDateTime(); TQString str = TDEGlobal::locale()->formatDate( date.date(), true ); body.append( str ); } else if ( cmd.startsWith( "DATE" ) ) { kdDebug() << "Command: DATE" << endl; i += strlen( "DATE" ); TQDateTime date = TQDateTime::currentDateTime(); TQString str = TDEGlobal::locale()->formatDate( date.date(), false ); body.append( str ); } else if ( cmd.startsWith( "DOW" ) ) { kdDebug() << "Command: DOW" << endl; i += strlen( "DOW" ); TQDateTime date = TQDateTime::currentDateTime(); TQString str = TDEGlobal::locale()->calendar()->weekDayName( date.date(), false ); body.append( str ); } else if ( cmd.startsWith( "TIMELONGEN" ) ) { kdDebug() << "Command: TIMELONGEN" << endl; i += strlen( "TIMELONGEN" ); TQDateTime date = TQDateTime::currentDateTime(); TDELocale locale( "C"); TQString str = locale.formatTime( date.time(), true ); body.append( str ); } else if ( cmd.startsWith( "TIMELONG" ) ) { kdDebug() << "Command: TIMELONG" << endl; i += strlen( "TIMELONG" ); TQDateTime date = TQDateTime::currentDateTime(); TQString str = TDEGlobal::locale()->formatTime( date.time(), true ); body.append( str ); } else if ( cmd.startsWith( "TIME" ) ) { kdDebug() << "Command: TIME" << endl; i += strlen( "TIME" ); TQDateTime date = TQDateTime::currentDateTime(); TQString str = TDEGlobal::locale()->formatTime( date.time(), false ); body.append( str ); } else if ( cmd.startsWith( "ODATEEN" ) ) { kdDebug() << "Command: ODATEEN" << endl; i += strlen( "ODATEEN" ); if ( mOrigMsg ) { TQDateTime date; date.setTime_t( mOrigMsg->date() ); TDELocale locale( "C"); TQString str = locale.formatDate( date.date(), false ); body.append( str ); } } else if ( cmd.startsWith( "ODATESHORT") ) { kdDebug() << "Command: ODATESHORT" << endl; i += strlen( "ODATESHORT"); if ( mOrigMsg ) { TQDateTime date; date.setTime_t( mOrigMsg->date() ); TQString str = TDEGlobal::locale()->formatDate( date.date(), true ); body.append( str ); } } else if ( cmd.startsWith( "ODATE") ) { kdDebug() << "Command: ODATE" << endl; i += strlen( "ODATE"); if ( mOrigMsg ) { TQDateTime date; date.setTime_t( mOrigMsg->date() ); TQString str = TDEGlobal::locale()->formatDate( date.date(), false ); body.append( str ); } } else if ( cmd.startsWith( "ODOW") ) { kdDebug() << "Command: ODOW" << endl; i += strlen( "ODOW"); if ( mOrigMsg ) { TQDateTime date; date.setTime_t( mOrigMsg->date() ); TQString str = TDEGlobal::locale()->calendar()->weekDayName( date.date(), false ); body.append( str ); } } else if ( cmd.startsWith( "OTIMELONGEN") ) { kdDebug() << "Command: OTIMELONGEN" << endl; i += strlen( "OTIMELONGEN"); if ( mOrigMsg ) { TQDateTime date; date.setTime_t( mOrigMsg->date() ); TDELocale locale( "C"); TQString str = locale.formatTime( date.time(), true ); body.append( str ); } } else if ( cmd.startsWith( "OTIMELONG") ) { kdDebug() << "Command: OTIMELONG" << endl; i += strlen( "OTIMELONG"); if ( mOrigMsg ) { TQDateTime date; date.setTime_t( mOrigMsg->date() ); TQString str = TDEGlobal::locale()->formatTime( date.time(), true ); body.append( str ); } } else if ( cmd.startsWith( "OTIME") ) { kdDebug() << "Command: OTIME" << endl; i += strlen( "OTIME"); if ( mOrigMsg ) { TQDateTime date; date.setTime_t( mOrigMsg->date() ); TQString str = TDEGlobal::locale()->formatTime( date.time(), false ); body.append( str ); } } else if ( cmd.startsWith( "BLANK" ) ) { // do nothing kdDebug() << "Command: BLANK" << endl; i += strlen( "BLANK" ); } else if ( cmd.startsWith( "NOP" ) ) { // do nothing kdDebug() << "Command: NOP" << endl; i += strlen( "NOP" ); } else if ( cmd.startsWith( "CLEAR" ) ) { // clear body buffer; not too useful yet kdDebug() << "Command: CLEAR" << endl; i += strlen( "CLEAR" ); body = ""; mMsg->setCursorPos( 0 ); } else if ( cmd.startsWith( "DEBUGOFF" ) ) { // turn off debug kdDebug() << "Command: DEBUGOFF" << endl; i += strlen( "DEBUGOFF" ); mDebug = false; } else if ( cmd.startsWith( "DEBUG" ) ) { // turn on debug kdDebug() << "Command: DEBUG" << endl; i += strlen( "DEBUG" ); mDebug = true; } else if ( cmd.startsWith( "CURSOR" ) ) { // turn on debug kdDebug() << "Command: CURSOR" << endl; i += strlen( "CURSOR" ); mMsg->setCursorPos( body.length() ); } else { // wrong command, do nothing body.append( c ); } } else if ( dnl && ( c == '\n' || c == '\r') ) { // skip if ( ( c == '\n' && tmpl[i + 1] == '\r' ) || ( c == '\r' && tmpl[i + 1] == '\n' ) ) { // skip one more i += 1; } dnl = false; } else { body.append( c ); } } addProcessedBodyToMessage( body ); } TQString TemplateParser::messageText( bool allowSelectionOnly ) { if ( !mSelection.isEmpty() && allowSelectionOnly ) return mSelection; // No selection text, therefore we need to parse the object tree ourselves to get partNode *root = parsedObjectTree(); return mOrigMsg->asPlainTextFromObjectTree( root, shouldStripSignature(), mAllowDecryption ); } partNode* TemplateParser::parsedObjectTree() { if ( mOrigRoot ) return mOrigRoot; mOrigRoot = partNode::fromMessage( mOrigMsg ); ObjectTreeParser otp; // all defaults are ok otp.parseObjectTree( mOrigRoot ); return mOrigRoot; } void TemplateParser::addProcessedBodyToMessage( const TQString &body ) { if ( mAppend ) { // ### What happens here if the body is multipart or in some way encoded? TQCString msg_body = mMsg->body(); msg_body.append( body.utf8() ); mMsg->setBody( msg_body ); } else { // Get the attachments of the original mail partNode *root = parsedObjectTree(); AttachmentCollector ac; ac.collectAttachmentsFrom( root ); // Now, delete the old content and set the new content, which // is either only the new text or the new text with some attachments. mMsg->deleteBodyParts(); // Set To and CC from the template if ( mMode == Forward ) { if ( !mTo.isEmpty() ) { mMsg->setTo( mMsg->to() + ',' + mTo ); } if ( !mCC.isEmpty() ) mMsg->setCc( mMsg->cc() + ',' + mCC ); } // If we have no attachment, simply create a text/plain part and // set the processed template text as the body if ( ac.attachments().empty() || mMode != Forward ) { mMsg->headers().ContentType().FromString( DwString() ); // to get rid of old boundary mMsg->headers().ContentType().Parse(); mMsg->headers().ContentType().SetType( DwMime::kTypeText ); mMsg->headers().ContentType().SetSubtype( DwMime::kSubtypePlain ); mMsg->headers().Assemble(); mMsg->setBodyFromUnicode( body ); mMsg->assembleIfNeeded(); } // If we have some attachments, create a multipart/mixed mail and // add the normal body as well as the attachments else { mMsg->headers().ContentType().SetType( DwMime::kTypeMultipart ); mMsg->headers().ContentType().SetSubtype( DwMime::kSubtypeMixed ); mMsg->headers().ContentType().CreateBoundary( 0 ); KMMessagePart textPart; textPart.setBodyFromUnicode( body ); mMsg->addDwBodyPart( mMsg->createDWBodyPart( &textPart ) ); mMsg->assembleIfNeeded(); int attachmentNumber = 1; for ( std::vector::const_iterator it = ac.attachments().begin(); it != ac.attachments().end(); ++it, attachmentNumber++ ) { // When adding this body part, make sure to _not_ add the next bodypart // as well, which mimelib would do, therefore creating a mail with many // duplicate attachments (so many that KMail runs out of memory, in fact). // Body::AddBodyPart is very misleading here... ( *it )->dwPart()->SetNext( 0 ); DwBodyPart *cloned = static_cast( ( *it )->dwPart()->Clone() ); // If the content type has no name or filename parameter, add one, since otherwise the name // would be empty in the attachment view of the composer, which looks confusing if ( cloned->Headers().HasContentType() ) { DwMediaType &ct = cloned->Headers().ContentType(); // Converting to a string here, since DwMediaType does not have a HasParameter() function TQString ctStr = ct.AsString().c_str(); if ( !ctStr.lower().contains( "name=" ) && !ctStr.lower().contains( "filename=" ) ) { DwParameter *nameParameter = new DwParameter; nameParameter->SetAttribute( "name" ); nameParameter->SetValue( Util::dwString( KMMsgBase::encodeRFC2231StringAutoDetectCharset( i18n( "Attachment %1" ).arg( attachmentNumber ) ) ) ); ct.AddParameter( nameParameter ); } } mMsg->addDwBodyPart( cloned ); mMsg->assembleIfNeeded(); } } } } TQString TemplateParser::findCustomTemplate( const TQString &tmplName ) { CTemplates t( tmplName ); mTo = t.to(); mCC = t.cC(); TQString content = t.content(); if ( !content.isEmpty() ) { return content; } else { return findTemplate(); } } TQString TemplateParser::findTemplate() { // import 'Phrases' if it not done yet if ( !GlobalSettings::self()->phrasesConverted() ) { TemplatesConfiguration::importFromPhrases(); } // kdDebug() << "Trying to find template for mode " << mode << endl; TQString tmpl; if ( !mFolder ) { // find folder message belongs to mFolder = mMsg->parent(); if ( !mFolder ) { if ( mOrigMsg ) { mFolder = mOrigMsg->parent(); } if ( !mFolder ) { kdDebug(5006) << "Oops! No folder for message" << endl; } } } kdDebug(5006) << "Folder found: " << mFolder << endl; if ( mFolder ) // only if a folder was found { TQString fid = mFolder->idString(); Templates fconf( fid ); if ( fconf.useCustomTemplates() ) { // does folder use custom templates? switch( mMode ) { case NewMessage: tmpl = fconf.templateNewMessage(); break; case Reply: tmpl = fconf.templateReply(); break; case ReplyAll: tmpl = fconf.templateReplyAll(); break; case Forward: tmpl = fconf.templateForward(); break; default: kdDebug(5006) << "Unknown message mode: " << mMode << endl; return ""; } mQuoteString = fconf.quoteString(); if ( !tmpl.isEmpty() ) { return tmpl; // use folder-specific template } } } if ( !mIdentity ) { // find identity message belongs to mIdentity = mMsg->identityUoid(); if ( !mIdentity && mOrigMsg ) { mIdentity = mOrigMsg->identityUoid(); } mIdentity = kmkernel->identityManager()->identityForUoidOrDefault( mIdentity ).uoid(); if ( !mIdentity ) { kdDebug(5006) << "Oops! No identity for message" << endl; } } kdDebug(5006) << "Identity found: " << mIdentity << endl; TQString iid; if ( mIdentity ) { iid = TQString("IDENTITY_%1").arg( mIdentity ); // templates ID for that identity } else { iid = "IDENTITY_NO_IDENTITY"; // templates ID for no identity } Templates iconf( iid ); if ( iconf.useCustomTemplates() ) { // does identity use custom templates? switch( mMode ) { case NewMessage: tmpl = iconf.templateNewMessage(); break; case Reply: tmpl = iconf.templateReply(); break; case ReplyAll: tmpl = iconf.templateReplyAll(); break; case Forward: tmpl = iconf.templateForward(); break; default: kdDebug(5006) << "Unknown message mode: " << mMode << endl; return ""; } mQuoteString = iconf.quoteString(); if ( !tmpl.isEmpty() ) { return tmpl; // use identity-specific template } } switch( mMode ) { // use the global template case NewMessage: tmpl = GlobalSettings::self()->templateNewMessage(); break; case Reply: tmpl = GlobalSettings::self()->templateReply(); break; case ReplyAll: tmpl = GlobalSettings::self()->templateReplyAll(); break; case Forward: tmpl = GlobalSettings::self()->templateForward(); break; default: kdDebug(5006) << "Unknown message mode: " << mMode << endl; return ""; } mQuoteString = GlobalSettings::self()->quoteString(); return tmpl; } TQString TemplateParser::pipe( const TQString &cmd, const TQString &buf ) { mPipeOut = ""; mPipeErr = ""; mPipeRc = 0; TDEProcess proc; TQCString data = buf.local8Bit(); // kdDebug() << "Command data: " << data << endl; proc << KShell::splitArgs( cmd, KShell::TildeExpand ); proc.setUseShell( true ); connect( &proc, TQT_SIGNAL( receivedStdout( TDEProcess *, char *, int ) ), this, TQT_SLOT( onReceivedStdout( TDEProcess *, char *, int ) ) ); connect( &proc, TQT_SIGNAL( receivedStderr( TDEProcess *, char *, int ) ), this, TQT_SLOT( onReceivedStderr( TDEProcess *, char *, int ) ) ); connect( &proc, TQT_SIGNAL( wroteStdin( TDEProcess * ) ), this, TQT_SLOT( onWroteStdin( TDEProcess * ) ) ); if ( proc.start( TDEProcess::NotifyOnExit, TDEProcess::All ) ) { bool pipe_filled = proc.writeStdin( data, data.length() ); if ( pipe_filled ) { proc.closeStdin(); bool exited = proc.wait( PipeTimeout ); if ( exited ) { if ( proc.normalExit() ) { mPipeRc = proc.exitStatus(); if ( mPipeRc != 0 && mDebug ) { if ( mPipeErr.isEmpty() ) { KMessageBox::error( 0, i18n( "Pipe command exit with status %1: %2"). arg( mPipeRc ).arg( cmd ) ); } else { KMessageBox::detailedError( 0, i18n( "Pipe command exit with status %1: %2" ). arg( mPipeRc ).arg( cmd ), mPipeErr ); } } } else { mPipeRc = -( proc.exitSignal() ); if ( mPipeRc != 0 && mDebug ) { if ( mPipeErr.isEmpty() ) { KMessageBox::error( 0, i18n( "Pipe command killed by signal %1: %2" ). arg( -(mPipeRc) ).arg( cmd ) ); } else { KMessageBox::detailedError( 0, i18n( "Pipe command killed by signal %1: %2" ). arg( -(mPipeRc) ).arg( cmd ), mPipeErr ); } } } } else { // process does not exited after TemplateParser::PipeTimeout seconds, kill it proc.kill(); proc.detach(); if ( mDebug ) { KMessageBox::error( 0, i18n( "Pipe command did not finish within %1 seconds: %2" ). arg( PipeTimeout ).arg( cmd ) ); } } } else { // can`t write to stdin of process proc.kill(); proc.detach(); if ( mDebug ) { if ( mPipeErr.isEmpty() ) { KMessageBox::error( 0, i18n( "Cannot write to process stdin: %1" ).arg( cmd ) ); } else { KMessageBox::detailedError( 0, i18n( "Cannot write to process stdin: %1" ). arg( cmd ), mPipeErr ); } } } } else if ( mDebug ) { KMessageBox::error( 0, i18n( "Cannot start pipe command from template: %1" ). arg( cmd ) ); } return mPipeOut; } void TemplateParser::onProcessExited( TDEProcess *proc ) { Q_UNUSED( proc ); // do nothing for now } void TemplateParser::onReceivedStdout( TDEProcess *proc, char *buffer, int buflen ) { Q_UNUSED( proc ); mPipeOut += TQString::fromLocal8Bit( buffer, buflen ); } void TemplateParser::onReceivedStderr( TDEProcess *proc, char *buffer, int buflen ) { Q_UNUSED( proc ); mPipeErr += TQString::fromLocal8Bit( buffer, buflen ); } void TemplateParser::onWroteStdin( TDEProcess *proc ) { proc->closeStdin(); } #include "templateparser.moc"