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