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