TDE personal information management applications
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3133 lines
121 KiB

  1. /* -*- mode: C++; c-file-style: "gnu" -*-
  2. objecttreeparser.cpp
  3. This file is part of KMail, the KDE mail client.
  4. Copyright (c) 2002-2004 Klarälvdalens Datakonsult AB
  5. Copyright (c) 2003 Marc Mutz <mutz@kde.org>
  6. KMail is free software; you can redistribute it and/or modify it
  7. under the terms of the GNU General Public License, version 2, as
  8. published by the Free Software Foundation.
  9. KMail is distributed in the hope that it will be useful, but
  10. WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. In addition, as a special exception, the copyright holders give
  17. permission to link the code of this program with any edition of
  18. the TQt library by Trolltech AS, Norway (or with modified versions
  19. of TQt that use the same license as TQt), and distribute linked
  20. combinations including the two. You must obey the GNU General
  21. Public License in all respects for all of the code used other than
  22. TQt. If you modify this file, you may extend this exception to
  23. your version of the file, but you are not obligated to do so. If
  24. you do not wish to do so, delete this exception statement from
  25. your version.
  26. */
  27. #include <config.h>
  28. // my header file
  29. #include "objecttreeparser.h"
  30. #include "objecttreeparser_p.h"
  31. // other KMail headers
  32. #include "kmkernel.h"
  33. #include "kmreaderwin.h"
  34. #include "partNode.h"
  35. #include <libtdepim/tdefileio.h>
  36. #include <libemailfunctions/email.h>
  37. #include "partmetadata.h"
  38. #include "attachmentstrategy.h"
  39. #include "interfaces/htmlwriter.h"
  40. #include "htmlstatusbar.h"
  41. #include "csshelper.h"
  42. #include "bodypartformatter.h"
  43. #include "bodypartformatterfactory.h"
  44. #include "partnodebodypart.h"
  45. #include "interfaces/bodypartformatter.h"
  46. #include "globalsettings.h"
  47. #include "util.h"
  48. #include "callback.h"
  49. // other module headers
  50. #include <mimelib/enum.h>
  51. #include <mimelib/bodypart.h>
  52. #include <mimelib/string.h>
  53. #include <mimelib/text.h>
  54. #include <kleo/specialjob.h>
  55. #include <kleo/cryptobackendfactory.h>
  56. #include <kleo/decryptverifyjob.h>
  57. #include <kleo/verifydetachedjob.h>
  58. #include <kleo/verifyopaquejob.h>
  59. #include <kleo/keylistjob.h>
  60. #include <kleo/importjob.h>
  61. #include <kleo/dn.h>
  62. #include <gpgmepp/importresult.h>
  63. #include <gpgmepp/decryptionresult.h>
  64. #include <gpgmepp/key.h>
  65. #include <gpgmepp/keylistresult.h>
  66. #include <gpgme.h>
  67. #include <kpgpblock.h>
  68. #include <kpgp.h>
  69. #include <linklocator.h>
  70. #include <ktnef/ktnefparser.h>
  71. #include <ktnef/ktnefmessage.h>
  72. #include <ktnef/ktnefattach.h>
  73. // other KDE headers
  74. #include <kdebug.h>
  75. #include <tdelocale.h>
  76. #include <kmimetype.h>
  77. #include <tdeglobal.h>
  78. #include <tdehtml_part.h>
  79. #include <tdetempfile.h>
  80. #include <kstandarddirs.h>
  81. #include <tdeapplication.h>
  82. #include <tdemessagebox.h>
  83. #include <kiconloader.h>
  84. #include <kmdcodec.h>
  85. // other TQt headers
  86. #include <tqtextcodec.h>
  87. #include <tqdir.h>
  88. #include <tqfile.h>
  89. #include <tqapplication.h>
  90. #include <tdestyle.h>
  91. #include <tqbuffer.h>
  92. #include <tqpixmap.h>
  93. #include <tqpainter.h>
  94. #include <tqregexp.h>
  95. // other headers
  96. #include <memory>
  97. #include <sstream>
  98. #include <sys/stat.h>
  99. #include <sys/types.h>
  100. #include <unistd.h>
  101. #include <cassert>
  102. #include "chiasmuskeyselector.h"
  103. namespace KMail {
  104. // A small class that eases temporary CryptPlugWrapper changes:
  105. class ObjectTreeParser::CryptoProtocolSaver {
  106. ObjectTreeParser * otp;
  107. const Kleo::CryptoBackend::Protocol * protocol;
  108. public:
  109. CryptoProtocolSaver( ObjectTreeParser * _otp, const Kleo::CryptoBackend::Protocol* _w )
  110. : otp( _otp ), protocol( _otp ? _otp->cryptoProtocol() : 0 )
  111. {
  112. if ( otp )
  113. otp->setCryptoProtocol( _w );
  114. }
  115. ~CryptoProtocolSaver() {
  116. if ( otp )
  117. otp->setCryptoProtocol( protocol );
  118. }
  119. };
  120. ObjectTreeParser::ObjectTreeParser( KMReaderWin * reader, const Kleo::CryptoBackend::Protocol * protocol,
  121. bool showOnlyOneMimePart, bool keepEncryptions,
  122. bool includeSignatures,
  123. const AttachmentStrategy * strategy,
  124. HtmlWriter * htmlWriter,
  125. CSSHelper * cssHelper )
  126. : mReader( reader ),
  127. mCryptoProtocol( protocol ),
  128. mShowOnlyOneMimePart( showOnlyOneMimePart ),
  129. mKeepEncryptions( keepEncryptions ),
  130. mIncludeSignatures( includeSignatures ),
  131. mHasPendingAsyncJobs( false ),
  132. mAllowAsync( false ),
  133. mShowRawToltecMail( false ),
  134. mAttachmentStrategy( strategy ),
  135. mHtmlWriter( htmlWriter ),
  136. mCSSHelper( cssHelper )
  137. {
  138. if ( !attachmentStrategy() )
  139. mAttachmentStrategy = reader ? reader->attachmentStrategy()
  140. : AttachmentStrategy::smart();
  141. if ( reader && !this->htmlWriter() )
  142. mHtmlWriter = reader->htmlWriter();
  143. if ( reader && !this->cssHelper() )
  144. mCSSHelper = reader->mCSSHelper;
  145. }
  146. ObjectTreeParser::ObjectTreeParser( const ObjectTreeParser & other )
  147. : mReader( other.mReader ),
  148. mCryptoProtocol( other.cryptoProtocol() ),
  149. mShowOnlyOneMimePart( other.showOnlyOneMimePart() ),
  150. mKeepEncryptions( other.keepEncryptions() ),
  151. mIncludeSignatures( other.includeSignatures() ),
  152. mHasPendingAsyncJobs( other.hasPendingAsyncJobs() ),
  153. mAllowAsync( other.allowAsync() ),
  154. mAttachmentStrategy( other.attachmentStrategy() ),
  155. mHtmlWriter( other.htmlWriter() ),
  156. mCSSHelper( other.cssHelper() )
  157. {
  158. }
  159. ObjectTreeParser::~ObjectTreeParser() {}
  160. void ObjectTreeParser::insertAndParseNewChildNode( partNode& startNode,
  161. const char* content,
  162. const char* cntDesc,
  163. bool append, bool addToTextualContent )
  164. {
  165. DwBodyPart* myBody = new DwBodyPart( DwString( content ), 0 );
  166. myBody->Parse();
  167. if ( ( !myBody->Body().FirstBodyPart() ||
  168. myBody->Body().AsString().length() == 0 ) &&
  169. startNode.dwPart() &&
  170. startNode.dwPart()->Body().Message() &&
  171. startNode.dwPart()->Body().Message()->Body().FirstBodyPart() )
  172. {
  173. // if encapsulated imap messages are loaded the content-string is not complete
  174. // so we need to keep the child dwparts
  175. myBody = new DwBodyPart( *(startNode.dwPart()->Body().Message()) );
  176. }
  177. if ( myBody->hasHeaders() ) {
  178. DwText& desc = myBody->Headers().ContentDescription();
  179. desc.FromString( cntDesc );
  180. desc.SetModified();
  181. myBody->Headers().Parse();
  182. }
  183. partNode* parentNode = &startNode;
  184. partNode* newNode = new partNode(false, myBody);
  185. // Build the object tree of the new node before setting the parent, as otherwise
  186. // buildObjectTree() would erronously modify the parents as well
  187. newNode->buildObjectTree( false );
  188. if ( append && parentNode->firstChild() ) {
  189. parentNode = parentNode->firstChild();
  190. while( parentNode->nextSibling() )
  191. parentNode = parentNode->nextSibling();
  192. parentNode->setNext( newNode );
  193. } else
  194. parentNode->setFirstChild( newNode );
  195. if ( startNode.mimePartTreeItem() ) {
  196. newNode->fillMimePartTree( startNode.mimePartTreeItem(), 0,
  197. TQString(), TQString(), TQString(), 0,
  198. append );
  199. } else {
  200. }
  201. ObjectTreeParser otp( mReader, cryptoProtocol() );
  202. otp.parseObjectTree( newNode );
  203. if ( addToTextualContent ) {
  204. mRawReplyString += otp.rawReplyString();
  205. mTextualContent += otp.textualContent();
  206. if ( !otp.textualContentCharset().isEmpty() )
  207. mTextualContentCharset = otp.textualContentCharset();
  208. }
  209. }
  210. //-----------------------------------------------------------------------------
  211. void ObjectTreeParser::parseObjectTree( partNode * node ) {
  212. //kdDebug(5006) << "ObjectTreeParser::parseObjectTree( "
  213. // << (node ? "node OK, " : "no node, ")
  214. // << "showOnlyOneMimePart: " << (showOnlyOneMimePart() ? "TRUE" : "FALSE")
  215. // << " )" << endl;
  216. if ( !node )
  217. return;
  218. // reset pending async jobs state (we'll rediscover pending jobs as we go)
  219. mHasPendingAsyncJobs = false;
  220. // reset "processed" flags for...
  221. if ( showOnlyOneMimePart() ) {
  222. // ... this node and all descendants
  223. node->setProcessed( false, false );
  224. if ( partNode * child = node->firstChild() )
  225. child->setProcessed( false, true );
  226. } else if ( mReader && !node->parentNode() ) {
  227. // ...this node and all it's siblings and descendants
  228. node->setProcessed( false, true );
  229. }
  230. for ( ; node ; node = node->nextSibling() ) {
  231. if ( node->processed() )
  232. continue;
  233. ProcessResult processResult;
  234. if ( mReader ) {
  235. htmlWriter()->queue( TQString::fromLatin1("<a name=\"att%1\"/>").arg( node->nodeId() ) );
  236. }
  237. if ( const Interface::BodyPartFormatter * formatter
  238. = BodyPartFormatterFactory::instance()->createFor( node->typeString(), node->subTypeString() ) ) {
  239. // Only use the external plugin if we have a reader. Otherwise, just do nothing for this
  240. // node.
  241. if ( mReader ) {
  242. PartNodeBodyPart part( *node, codecFor( node ) );
  243. // Set the default display strategy for this body part relying on the
  244. // identity of KMail::Interface::BodyPart::Display and AttachmentStrategy::Display
  245. part.setDefaultDisplay( (KMail::Interface::BodyPart::Display) attachmentStrategy()->defaultDisplay( node ) );
  246. writeAttachmentMarkHeader( node );
  247. node->setDisplayedEmbedded( true );
  248. Callback callback( mReader->message(), mReader );
  249. const Interface::BodyPartFormatter::Result result = formatter->format( &part, htmlWriter(), callback );
  250. writeAttachmentMarkFooter();
  251. switch ( result ) {
  252. case Interface::BodyPartFormatter::AsIcon:
  253. processResult.setNeverDisplayInline( true );
  254. // fall through:
  255. case Interface::BodyPartFormatter::Failed:
  256. defaultHandling( node, processResult );
  257. break;
  258. case Interface::BodyPartFormatter::Ok:
  259. case Interface::BodyPartFormatter::NeedContent:
  260. // FIXME: incomplete content handling
  261. ;
  262. }
  263. }
  264. } else {
  265. const BodyPartFormatter * bpf
  266. = BodyPartFormatter::createFor( node->type(), node->subType() );
  267. kdFatal( !bpf, 5006 ) << "THIS SHOULD NO LONGER HAPPEN ("
  268. << node->typeString() << '/' << node->subTypeString()
  269. << ')' << endl;
  270. writeAttachmentMarkHeader( node );
  271. if ( bpf && !bpf->process( this, node, processResult ) ) {
  272. defaultHandling( node, processResult );
  273. }
  274. writeAttachmentMarkFooter();
  275. }
  276. node->setProcessed( true, false );
  277. // adjust signed/encrypted flags if inline PGP was found
  278. processResult.adjustCryptoStatesOfNode( node );
  279. if ( showOnlyOneMimePart() )
  280. break;
  281. }
  282. }
  283. void ObjectTreeParser::defaultHandling( partNode * node, ProcessResult & result ) {
  284. // ### (mmutz) default handling should go into the respective
  285. // ### bodypartformatters.
  286. if ( !mReader )
  287. return;
  288. const AttachmentStrategy * as = attachmentStrategy();
  289. if ( as && as->defaultDisplay( node ) == AttachmentStrategy::None &&
  290. !showOnlyOneMimePart() &&
  291. node->parentNode() /* message is not an attachment */ ) {
  292. node->setDisplayedHidden( true );
  293. return;
  294. }
  295. bool asIcon = true;
  296. if ( showOnlyOneMimePart() )
  297. // ### (mmutz) this is wrong! If I click on an image part, I
  298. // want the equivalent of "view...", except for the extra
  299. // window!
  300. asIcon = !node->hasContentDispositionInline();
  301. else if ( !result.neverDisplayInline() )
  302. if ( as )
  303. asIcon = as->defaultDisplay( node ) == AttachmentStrategy::AsIcon;
  304. // neither image nor text -> show as icon
  305. if ( !result.isImage() && node->type() != DwMime::kTypeText )
  306. asIcon = true;
  307. // if the image is not complete do not try to show it inline
  308. if ( result.isImage() && !node->msgPart().isComplete() )
  309. asIcon = true;
  310. if ( asIcon ) {
  311. if ( !( as && as->defaultDisplay( node ) == AttachmentStrategy::None ) ||
  312. showOnlyOneMimePart() ) {
  313. writePartIcon( &node->msgPart(), node->nodeId() );
  314. }
  315. else {
  316. node->setDisplayedHidden( true );
  317. }
  318. } else if ( result.isImage() ) {
  319. node->setDisplayedEmbedded( true );
  320. writePartIcon( &node->msgPart(), node->nodeId(), true );
  321. }
  322. else {
  323. node->setDisplayedEmbedded( true );
  324. writeBodyString( node->msgPart().bodyDecoded(),
  325. node->trueFromAddress(),
  326. codecFor( node ), result, false );
  327. }
  328. // end of ###
  329. }
  330. void ProcessResult::adjustCryptoStatesOfNode( partNode * node ) const {
  331. if ( ( inlineSignatureState() != KMMsgNotSigned ) ||
  332. ( inlineEncryptionState() != KMMsgNotEncrypted ) ) {
  333. node->setSignatureState( inlineSignatureState() );
  334. node->setEncryptionState( inlineEncryptionState() );
  335. }
  336. }
  337. //////////////////
  338. //////////////////
  339. //////////////////
  340. static int signatureToStatus( const GpgME::Signature &sig )
  341. {
  342. switch ( sig.status().code() ) {
  343. case GPG_ERR_NO_ERROR:
  344. return GPGME_SIG_STAT_GOOD;
  345. case GPG_ERR_BAD_SIGNATURE:
  346. return GPGME_SIG_STAT_BAD;
  347. case GPG_ERR_NO_PUBKEY:
  348. return GPGME_SIG_STAT_NOKEY;
  349. case GPG_ERR_NO_DATA:
  350. return GPGME_SIG_STAT_NOSIG;
  351. case GPG_ERR_SIG_EXPIRED:
  352. return GPGME_SIG_STAT_GOOD_EXP;
  353. case GPG_ERR_KEY_EXPIRED:
  354. return GPGME_SIG_STAT_GOOD_EXPKEY;
  355. default:
  356. return GPGME_SIG_STAT_ERROR;
  357. }
  358. }
  359. bool ObjectTreeParser::writeOpaqueOrMultipartSignedData( partNode* data,
  360. partNode& sign,
  361. const TQString& fromAddress,
  362. bool doCheck,
  363. TQCString* cleartextData,
  364. const std::vector<GpgME::Signature> & paramSignatures,
  365. bool hideErrors )
  366. {
  367. bool bIsOpaqueSigned = false;
  368. enum { NO_PLUGIN, NOT_INITIALIZED, CANT_VERIFY_SIGNATURES }
  369. cryptPlugError = NO_PLUGIN;
  370. const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
  371. TQString cryptPlugLibName;
  372. TQString cryptPlugDisplayName;
  373. if ( cryptProto ) {
  374. cryptPlugLibName = cryptProto->name();
  375. cryptPlugDisplayName = cryptProto->displayName();
  376. }
  377. #ifndef NDEBUG
  378. if ( !doCheck ) {
  379. //kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: showing OpenPGP (Encrypted+Signed) data" << endl;
  380. }
  381. else {
  382. if ( data ) {
  383. //kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: processing Multipart Signed data" << endl;
  384. }
  385. else {
  386. //kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: processing Opaque Signed data" << endl;
  387. }
  388. }
  389. #endif
  390. if ( doCheck && cryptProto ) {
  391. //kdDebug(5006) << "ObjectTreeParser::writeOpaqueOrMultipartSignedData: going to call CRYPTPLUG "
  392. // << cryptPlugLibName << endl;
  393. }
  394. TQCString cleartext;
  395. TQByteArray signaturetext;
  396. if ( doCheck && cryptProto ) {
  397. if ( data ) {
  398. cleartext = KMail::Util::CString( data->dwPart()->AsString() );
  399. dumpToFile( "dat_01_reader_signedtext_before_canonicalization",
  400. cleartext.data(), cleartext.length() );
  401. // replace simple LFs by CRLSs
  402. // according to RfC 2633, 3.1.1 Canonicalization
  403. //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
  404. cleartext = Util::lf2crlf( cleartext );
  405. //kdDebug(5006) << " done." << endl;
  406. }
  407. dumpToFile( "dat_02_reader_signedtext_after_canonicalization",
  408. cleartext.data(), cleartext.length() );
  409. signaturetext = sign.msgPart().bodyDecodedBinary();
  410. dumpToFile( "dat_03_reader.sig", signaturetext.data(),
  411. signaturetext.size() );
  412. }
  413. std::vector<GpgME::Signature> signatures;
  414. if ( !doCheck )
  415. signatures = paramSignatures;
  416. PartMetaData messagePart;
  417. messagePart.isSigned = true;
  418. messagePart.technicalProblem = ( cryptProto == 0 );
  419. messagePart.isGoodSignature = false;
  420. messagePart.isEncrypted = false;
  421. messagePart.isDecryptable = false;
  422. messagePart.keyTrust = Kpgp::KPGP_VALIDITY_UNKNOWN;
  423. messagePart.status = i18n("Wrong Crypto Plug-In.");
  424. messagePart.status_code = GPGME_SIG_STAT_NONE;
  425. GpgME::Key key;
  426. if ( doCheck && cryptProto ) {
  427. GpgME::VerificationResult result;
  428. if ( data ) { // detached
  429. const VerifyDetachedBodyPartMemento * m
  430. = dynamic_cast<VerifyDetachedBodyPartMemento*>( sign.bodyPartMemento( "verifydetached" ) );
  431. if ( !m ) {
  432. Kleo::VerifyDetachedJob * job = cryptProto->verifyDetachedJob();
  433. if ( !job ) {
  434. cryptPlugError = CANT_VERIFY_SIGNATURES;
  435. // PENDING(marc) cryptProto = 0 here?
  436. } else {
  437. TQByteArray plainData = cleartext;
  438. plainData.resize( cleartext.size() - 1 );
  439. VerifyDetachedBodyPartMemento * newM
  440. = new VerifyDetachedBodyPartMemento( job, cryptProto->keyListJob(), signaturetext, plainData );
  441. if ( allowAsync() ) {
  442. if ( newM->start() ) {
  443. messagePart.inProgress = true;
  444. mHasPendingAsyncJobs = true;
  445. } else {
  446. m = newM;
  447. }
  448. } else {
  449. newM->exec();
  450. m = newM;
  451. }
  452. sign.setBodyPartMemento( "verifydetached", newM );
  453. }
  454. } else if ( m->isRunning() ) {
  455. messagePart.inProgress = true;
  456. mHasPendingAsyncJobs = true;
  457. m = 0;
  458. }
  459. if ( m ) {
  460. result = m->verifyResult();
  461. messagePart.auditLogError = m->auditLogError();
  462. messagePart.auditLog = m->auditLogAsHtml();
  463. key = m->signingKey();
  464. }
  465. } else { // opaque
  466. const VerifyOpaqueBodyPartMemento * m
  467. = dynamic_cast<VerifyOpaqueBodyPartMemento*>( sign.bodyPartMemento( "verifyopaque" ) );
  468. if ( !m ) {
  469. Kleo::VerifyOpaqueJob * job = cryptProto->verifyOpaqueJob();
  470. if ( !job ) {
  471. cryptPlugError = CANT_VERIFY_SIGNATURES;
  472. // PENDING(marc) cryptProto = 0 here?
  473. } else {
  474. VerifyOpaqueBodyPartMemento * newM
  475. = new VerifyOpaqueBodyPartMemento( job, cryptProto->keyListJob(), signaturetext );
  476. if ( allowAsync() ) {
  477. if ( newM->start() ) {
  478. messagePart.inProgress = true;
  479. mHasPendingAsyncJobs = true;
  480. } else {
  481. m = newM;
  482. }
  483. } else {
  484. newM->exec();
  485. m = newM;
  486. }
  487. sign.setBodyPartMemento( "verifyopaque", newM );
  488. }
  489. } else if ( m->isRunning() ) {
  490. messagePart.inProgress = true;
  491. mHasPendingAsyncJobs = true;
  492. m = 0;
  493. }
  494. if ( m ) {
  495. result = m->verifyResult();
  496. const TQByteArray & plainData = m->plainText();
  497. cleartext = TQCString( plainData.data(), plainData.size() + 1 );
  498. messagePart.auditLogError = m->auditLogError();
  499. messagePart.auditLog = m->auditLogAsHtml();
  500. key = m->signingKey();
  501. }
  502. }
  503. std::stringstream ss;
  504. ss << result;
  505. //kdDebug(5006) << ss.str().c_str() << endl;
  506. signatures = result.signatures();
  507. }
  508. if ( doCheck ) {
  509. //kdDebug(5006) << "\nObjectTreeParser::writeOpaqueOrMultipartSignedData: returned from CRYPTPLUG" << endl;
  510. }
  511. // ### only one signature supported
  512. if ( signatures.size() > 0 ) {
  513. //kdDebug(5006) << "\nObjectTreeParser::writeOpaqueOrMultipartSignedData: found signature" << endl;
  514. GpgME::Signature signature = signatures[0];
  515. messagePart.status_code = signatureToStatus( signature );
  516. messagePart.status = TQString::fromUtf8( signature.status().asString() );
  517. for ( uint i = 1; i < signatures.size(); ++i ) {
  518. if ( signatureToStatus( signatures[i] ) != messagePart.status_code ) {
  519. messagePart.status_code = GPGME_SIG_STAT_DIFF;
  520. messagePart.status = i18n("Different results for signatures");
  521. }
  522. }
  523. if ( messagePart.status_code & GPGME_SIG_STAT_GOOD )
  524. messagePart.isGoodSignature = true;
  525. // save extended signature status flags
  526. messagePart.sigSummary = signature.summary();
  527. if ( key.keyID() )
  528. messagePart.keyId = key.keyID();
  529. if ( messagePart.keyId.isEmpty() )
  530. messagePart.keyId = signature.fingerprint();
  531. // ### Ugh. We depend on two enums being in sync:
  532. messagePart.keyTrust = (Kpgp::Validity)signature.validity();
  533. if ( key.numUserIDs() > 0 && key.userID( 0 ).id() )
  534. messagePart.signer = Kleo::DN( key.userID( 0 ).id() ).prettyDN();
  535. for ( uint iMail = 0; iMail < key.numUserIDs(); ++iMail ) {
  536. // The following if /should/ always result in TRUE but we
  537. // won't trust implicitely the plugin that gave us these data.
  538. if ( key.userID( iMail ).email() ) {
  539. TQString email = TQString::fromUtf8( key.userID( iMail ).email() );
  540. // ### work around gpgme 0.3.x / cryptplug bug where the
  541. // ### email addresses are specified as angle-addr, not addr-spec:
  542. if ( email.startsWith( "<" ) && email.endsWith( ">" ) )
  543. email = email.mid( 1, email.length() - 2 );
  544. if ( !email.isEmpty() )
  545. messagePart.signerMailAddresses.append( email );
  546. }
  547. }
  548. if ( signature.creationTime() )
  549. messagePart.creationTime.setTime_t( signature.creationTime() );
  550. else
  551. messagePart.creationTime = TQDateTime();
  552. if ( messagePart.signer.isEmpty() ) {
  553. if ( key.numUserIDs() > 0 && key.userID( 0 ).name() )
  554. messagePart.signer = Kleo::DN( key.userID( 0 ).name() ).prettyDN();
  555. if ( !messagePart.signerMailAddresses.empty() ) {
  556. if ( messagePart.signer.isEmpty() )
  557. messagePart.signer = messagePart.signerMailAddresses.front();
  558. else
  559. messagePart.signer += " <" + messagePart.signerMailAddresses.front() + '>';
  560. }
  561. }
  562. //kdDebug(5006) << "\n key id: " << messagePart.keyId
  563. // << "\n key trust: " << messagePart.keyTrust
  564. // << "\n signer: " << messagePart.signer << endl;
  565. } else {
  566. messagePart.creationTime = TQDateTime();
  567. }
  568. if ( !doCheck || !data ){
  569. if ( cleartextData || !cleartext.isEmpty() ) {
  570. if ( mReader )
  571. htmlWriter()->queue( writeSigstatHeader( messagePart,
  572. cryptProto,
  573. fromAddress ) );
  574. bIsOpaqueSigned = true;
  575. CryptoProtocolSaver cpws( this, cryptProto );
  576. insertAndParseNewChildNode( sign, doCheck ? cleartext.data() : cleartextData->data(),
  577. "opaqued signed data" );
  578. if ( mReader )
  579. htmlWriter()->queue( writeSigstatFooter( messagePart ) );
  580. }
  581. else if ( !hideErrors ) {
  582. TQString txt;
  583. txt = "<hr><b><h2>";
  584. txt.append( i18n( "The crypto engine returned no cleartext data." ) );
  585. txt.append( "</h2></b>" );
  586. txt.append( "<br>&nbsp;<br>" );
  587. txt.append( i18n( "Status: " ) );
  588. if ( !messagePart.status.isEmpty() ) {
  589. txt.append( "<i>" );
  590. txt.append( messagePart.status );
  591. txt.append( "</i>" );
  592. }
  593. else
  594. txt.append( i18n("(unknown)") );
  595. if ( mReader )
  596. htmlWriter()->queue(txt);
  597. }
  598. }
  599. else {
  600. if ( mReader ) {
  601. if ( !cryptProto ) {
  602. TQString errorMsg;
  603. switch ( cryptPlugError ) {
  604. case NOT_INITIALIZED:
  605. errorMsg = i18n( "Crypto plug-in \"%1\" is not initialized." )
  606. .arg( cryptPlugLibName );
  607. break;
  608. case CANT_VERIFY_SIGNATURES:
  609. errorMsg = i18n( "Crypto plug-in \"%1\" cannot verify signatures." )
  610. .arg( cryptPlugLibName );
  611. break;
  612. case NO_PLUGIN:
  613. if ( cryptPlugDisplayName.isEmpty() )
  614. errorMsg = i18n( "No appropriate crypto plug-in was found." );
  615. else
  616. errorMsg = i18n( "%1 is either 'OpenPGP' or 'S/MIME'",
  617. "No %1 plug-in was found." )
  618. .arg( cryptPlugDisplayName );
  619. break;
  620. }
  621. messagePart.errorText = i18n( "The message is signed, but the "
  622. "validity of the signature cannot be "
  623. "verified.<br />"
  624. "Reason: %1" )
  625. .arg( errorMsg );
  626. }
  627. if ( mReader )
  628. htmlWriter()->queue( writeSigstatHeader( messagePart,
  629. cryptProto,
  630. fromAddress ) );
  631. }
  632. ObjectTreeParser otp( mReader, cryptProto, true );
  633. otp.parseObjectTree( data );
  634. mRawReplyString += otp.rawReplyString();
  635. mTextualContent += otp.textualContent();
  636. if ( !otp.textualContentCharset().isEmpty() )
  637. mTextualContentCharset = otp.textualContentCharset();
  638. if ( mReader )
  639. htmlWriter()->queue( writeSigstatFooter( messagePart ) );
  640. }
  641. //kdDebug(5006) << "\nObjectTreeParser::writeOpaqueOrMultipartSignedData: done, returning "
  642. // << ( bIsOpaqueSigned ? "TRUE" : "FALSE" ) << endl;
  643. return bIsOpaqueSigned;
  644. }
  645. void ObjectTreeParser::writeDecryptionInProgressBlock() {
  646. assert( mReader );
  647. // PENDING(marc) find an animated icon here:
  648. //const TQString iconName = TDEGlobal::instance()->iconLoader()->iconPath( "decrypted", TDEIcon::Small );
  649. const TQString decryptedData = i18n("Encrypted data not shown");
  650. PartMetaData messagePart;
  651. messagePart.isDecryptable = true;
  652. messagePart.isEncrypted = true;
  653. messagePart.isSigned = false;
  654. messagePart.inProgress = true;
  655. htmlWriter()->queue( writeSigstatHeader( messagePart,
  656. cryptoProtocol(),
  657. TQString() ) );
  658. //htmlWriter()->queue( decryptedData );
  659. htmlWriter()->queue( writeSigstatFooter( messagePart ) );
  660. }
  661. void ObjectTreeParser::writeDeferredDecryptionBlock() {
  662. assert( mReader );
  663. const TQString iconName = TDEGlobal::instance()->iconLoader()->iconPath( "decrypted", TDEIcon::Small );
  664. const TQString decryptedData =
  665. "<div style=\"font-size:large; text-align:center;padding-top:20pt;\">" +
  666. i18n("This message is encrypted.") +
  667. "</div>"
  668. "<div style=\"text-align:center; padding-bottom:20pt;\">"
  669. "<a href=\"kmail:decryptMessage\">"
  670. "<img src=\"" + iconName + "\"/>" +
  671. i18n("Decrypt Message") +
  672. "</a></div>";
  673. PartMetaData messagePart;
  674. messagePart.isDecryptable = true;
  675. messagePart.isEncrypted = true;
  676. messagePart.isSigned = false;
  677. mRawReplyString += decryptedData.utf8();
  678. htmlWriter()->queue( writeSigstatHeader( messagePart,
  679. cryptoProtocol(),
  680. TQString() ) );
  681. htmlWriter()->queue( decryptedData );
  682. htmlWriter()->queue( writeSigstatFooter( messagePart ) );
  683. }
  684. bool ObjectTreeParser::okDecryptMIME( partNode& data,
  685. TQCString& decryptedData,
  686. bool& signatureFound,
  687. std::vector<GpgME::Signature> &signatures,
  688. bool showWarning,
  689. bool& passphraseError,
  690. bool& actuallyEncrypted,
  691. bool& decryptionStarted,
  692. TQString& aErrorText,
  693. GpgME::Error & auditLogError,
  694. TQString& auditLog )
  695. {
  696. passphraseError = false;
  697. decryptionStarted = false;
  698. aErrorText = TQString();
  699. auditLogError = GpgME::Error();
  700. auditLog = TQString();
  701. bool bDecryptionOk = false;
  702. enum { NO_PLUGIN, NOT_INITIALIZED, CANT_DECRYPT }
  703. cryptPlugError = NO_PLUGIN;
  704. const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
  705. TQString cryptPlugLibName;
  706. if ( cryptProto )
  707. cryptPlugLibName = cryptProto->name();
  708. assert( !mReader || mReader->decryptMessage() );
  709. if ( cryptProto && !kmkernel->contextMenuShown() ) {
  710. TQByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
  711. #ifdef MARCS_DEBUG
  712. TQCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
  713. bool cipherIsBinary = (-1 == cipherStr.find("BEGIN ENCRYPTED MESSAGE", 0, false) ) &&
  714. (-1 == cipherStr.find("BEGIN PGP ENCRYPTED MESSAGE", 0, false) ) &&
  715. (-1 == cipherStr.find("BEGIN PGP MESSAGE", 0, false) );
  716. dumpToFile( "dat_04_reader.encrypted", ciphertext.data(), ciphertext.size() );
  717. TQCString deb;
  718. deb = "\n\nE N C R Y P T E D D A T A = ";
  719. if ( cipherIsBinary )
  720. deb += "[binary data]";
  721. else {
  722. deb += "\"";
  723. deb += cipherStr;
  724. deb += "\"";
  725. }
  726. deb += "\n\n";
  727. kdDebug(5006) << deb << endl;
  728. #endif
  729. //kdDebug(5006) << "ObjectTreeParser::decryptMIME: going to call CRYPTPLUG "
  730. // << cryptPlugLibName << endl;
  731. if ( mReader )
  732. emit mReader->noDrag(); // in case pineentry pops up, don't let kmheaders start a drag afterwards
  733. // Check whether the memento contains a result from last time:
  734. const DecryptVerifyBodyPartMemento * m
  735. = dynamic_cast<DecryptVerifyBodyPartMemento*>( data.bodyPartMemento( "decryptverify" ) );
  736. if ( !m ) {
  737. Kleo::DecryptVerifyJob * job = cryptProto->decryptVerifyJob();
  738. if ( !job ) {
  739. cryptPlugError = CANT_DECRYPT;
  740. cryptProto = 0;
  741. } else {
  742. DecryptVerifyBodyPartMemento * newM
  743. = new DecryptVerifyBodyPartMemento( job, ciphertext );
  744. if ( allowAsync() ) {
  745. if ( newM->start() ) {
  746. decryptionStarted = true;
  747. mHasPendingAsyncJobs = true;
  748. } else {
  749. m = newM;
  750. }
  751. } else {
  752. newM->exec();
  753. m = newM;
  754. }
  755. data.setBodyPartMemento( "decryptverify", newM );
  756. }
  757. } else if ( m->isRunning() ) {
  758. decryptionStarted = true;
  759. mHasPendingAsyncJobs = true;
  760. m = 0;
  761. }
  762. if ( m ) {
  763. const TQByteArray & plainText = m->plainText();
  764. const GpgME::DecryptionResult & decryptResult = m->decryptResult();
  765. const GpgME::VerificationResult & verifyResult = m->verifyResult();
  766. std::stringstream ss;
  767. ss << decryptResult << '\n' << verifyResult;
  768. //kdDebug(5006) << ss.str().c_str() << endl;
  769. signatureFound = verifyResult.signatures().size() > 0;
  770. signatures = verifyResult.signatures();
  771. bDecryptionOk = !decryptResult.error();
  772. passphraseError = decryptResult.error().isCanceled()
  773. || decryptResult.error().code() == GPG_ERR_NO_SECKEY;
  774. actuallyEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA;
  775. aErrorText = TQString::fromLocal8Bit( decryptResult.error().asString() );
  776. auditLogError = m->auditLogError();
  777. auditLog = m->auditLogAsHtml();
  778. //kdDebug(5006) << "ObjectTreeParser::decryptMIME: returned from CRYPTPLUG"
  779. // << endl;
  780. if ( bDecryptionOk )
  781. decryptedData = TQCString( plainText.data(), plainText.size() + 1 );
  782. else if ( mReader && showWarning ) {
  783. decryptedData = "<div style=\"font-size:x-large; text-align:center;"
  784. "padding:20pt;\">"
  785. + i18n("Encrypted data not shown.").utf8()
  786. + "</div>";
  787. if ( !passphraseError )
  788. aErrorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.")
  789. .arg( cryptPlugLibName )
  790. + "<br />"
  791. + i18n("Error: %1").arg( aErrorText );
  792. }
  793. }
  794. }
  795. if ( !cryptProto ) {
  796. decryptedData = "<div style=\"text-align:center; padding:20pt;\">"
  797. + i18n("Encrypted data not shown.").utf8()
  798. + "</div>";
  799. switch ( cryptPlugError ) {
  800. case NOT_INITIALIZED:
  801. aErrorText = i18n( "Crypto plug-in \"%1\" is not initialized." )
  802. .arg( cryptPlugLibName );
  803. break;
  804. case CANT_DECRYPT:
  805. aErrorText = i18n( "Crypto plug-in \"%1\" cannot decrypt messages." )
  806. .arg( cryptPlugLibName );
  807. break;
  808. case NO_PLUGIN:
  809. aErrorText = i18n( "No appropriate crypto plug-in was found." );
  810. break;
  811. }
  812. } else if ( kmkernel->contextMenuShown() ) {
  813. // ### Workaround for bug 56693 (kmail freeze with the complete desktop
  814. // ### while pinentry-qt appears)
  815. TQByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
  816. TQCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
  817. bool cipherIsBinary = (-1 == cipherStr.find("BEGIN ENCRYPTED MESSAGE", 0, false) ) &&
  818. (-1 == cipherStr.find("BEGIN PGP ENCRYPTED MESSAGE", 0, false) ) &&
  819. (-1 == cipherStr.find("BEGIN PGP MESSAGE", 0, false) );
  820. if ( !cipherIsBinary ) {
  821. decryptedData = cipherStr;
  822. }
  823. else {
  824. decryptedData = "<div style=\"font-size:x-large; text-align:center;"
  825. "padding:20pt;\">"
  826. + i18n("Encrypted data not shown.").utf8()
  827. + "</div>";
  828. }
  829. }
  830. dumpToFile( "dat_05_reader.decrypted", decryptedData.data(), decryptedData.size() );
  831. return bDecryptionOk;
  832. }
  833. //static
  834. bool ObjectTreeParser::containsExternalReferences( const TQCString & str )
  835. {
  836. TQRegExp httpRegExp("(\\\"|\\\'|url\\s*\\(\\s*)http[s]?:");
  837. int httpPos = str.find( httpRegExp, 0 );
  838. while ( httpPos >= 0 ) {
  839. // look backwards for "href"
  840. if ( httpPos > 5 ) {
  841. int hrefPos = str.findRev( "href", httpPos - 5, true );
  842. // if no 'href' is found or the distance between 'href' and '"http[s]:'
  843. // is larger than 7 (7 is the distance in 'href = "http[s]:') then
  844. // we assume that we have found an external reference
  845. if ( ( hrefPos == -1 ) || ( httpPos - hrefPos > 7 ) )
  846. return true;
  847. }
  848. // find next occurrence of "http: or "https:
  849. httpPos = str.find( httpRegExp, httpPos + 6 );
  850. }
  851. return false;
  852. }
  853. bool ObjectTreeParser::processTextHtmlSubtype( partNode * curNode, ProcessResult & ) {
  854. TQCString cstr( curNode->msgPart().bodyDecoded() );
  855. mRawReplyString = cstr;
  856. if ( curNode->isFirstTextPart() ) {
  857. mTextualContent += curNode->msgPart().bodyToUnicode();
  858. mTextualContentCharset = curNode->msgPart().charset();
  859. }
  860. if ( !mReader )
  861. return true;
  862. if ( curNode->isFirstTextPart() ||
  863. attachmentStrategy()->defaultDisplay( curNode ) == AttachmentStrategy::Inline ||
  864. showOnlyOneMimePart() )
  865. {
  866. if ( mReader->htmlMail() ) {
  867. curNode->setDisplayedEmbedded( true );
  868. // ---Sven's strip </BODY> and </HTML> from end of attachment start-
  869. // We must fo this, or else we will see only 1st inlined html
  870. // attachment. It is IMHO enough to search only for </BODY> and
  871. // put \0 there.
  872. int i = cstr.findRev("</body>", -1, false); //case insensitive
  873. if ( 0 <= i )
  874. cstr.truncate(i);
  875. else // just in case - search for </html>
  876. {
  877. i = cstr.findRev("</html>", -1, false); //case insensitive
  878. if ( 0 <= i ) cstr.truncate(i);
  879. }
  880. // ---Sven's strip </BODY> and </HTML> from end of attachment end-
  881. // Show the "external references" warning (with possibility to load
  882. // external references only if loading external references is disabled
  883. // and the HTML code contains obvious external references). For
  884. // messages where the external references are obfuscated the user won't
  885. // have an easy way to load them but that shouldn't be a problem
  886. // because only spam contains obfuscated external references.
  887. if ( !mReader->htmlLoadExternal() &&
  888. containsExternalReferences( cstr ) ) {
  889. htmlWriter()->queue( "<div class=\"htmlWarn\">\n" );
  890. htmlWriter()->queue( i18n("<b>Note:</b> This HTML message may contain external "
  891. "references to images etc. For security/privacy reasons "
  892. "external references are not loaded. If you trust the "
  893. "sender of this message then you can load the external "
  894. "references for this message "
  895. "<a href=\"kmail:loadExternal\">by clicking here</a>.") );
  896. htmlWriter()->queue( "</div><br><br>" );
  897. }
  898. } else {
  899. htmlWriter()->queue( "<div class=\"htmlWarn\">\n" );
  900. htmlWriter()->queue( i18n("<b>Note:</b> This is an HTML message. For "
  901. "security reasons, only the raw HTML code "
  902. "is shown. If you trust the sender of this "
  903. "message then you can activate formatted "
  904. "HTML display for this message "
  905. "<a href=\"kmail:showHTML\">by clicking here</a>.") );
  906. htmlWriter()->queue( "</div><br><br>" );
  907. }
  908. htmlWriter()->queue( codecFor( curNode )->toUnicode( mReader->htmlMail() ? cstr : KMMessage::html2source( cstr )));
  909. mReader->mColorBar->setHtmlMode();
  910. return true;
  911. }
  912. return false;
  913. }
  914. } // namespace KMail
  915. static bool isMailmanMessage( partNode * curNode ) {
  916. if ( !curNode->dwPart() || !curNode->dwPart()->hasHeaders() )
  917. return false;
  918. DwHeaders & headers = curNode->dwPart()->Headers();
  919. if ( headers.HasField("X-Mailman-Version") )
  920. return true;
  921. if ( headers.HasField("X-Mailer") &&
  922. 0 == TQCString( headers.FieldBody("X-Mailer").AsString().c_str() )
  923. .find("MAILMAN", 0, false) )
  924. return true;
  925. return false;
  926. }
  927. namespace KMail {
  928. bool ObjectTreeParser::processMailmanMessage( partNode * curNode ) {
  929. const TQCString cstr = curNode->msgPart().bodyDecoded();
  930. //###
  931. const TQCString delim1( "--__--__--\n\nMessage:");
  932. const TQCString delim2( "--__--__--\r\n\r\nMessage:");
  933. const TQCString delimZ2("--__--__--\n\n_____________");
  934. const TQCString delimZ1("--__--__--\r\n\r\n_____________");
  935. TQCString partStr, digestHeaderStr;
  936. int thisDelim = cstr.find(delim1.data(), 0, false);
  937. if ( thisDelim == -1 )
  938. thisDelim = cstr.find(delim2.data(), 0, false);
  939. if ( thisDelim == -1 ) {
  940. kdDebug(5006) << " Sorry: Old style Mailman message but no delimiter found." << endl;
  941. return false;
  942. }
  943. int nextDelim = cstr.find(delim1.data(), thisDelim+1, false);
  944. if ( -1 == nextDelim )
  945. nextDelim = cstr.find(delim2.data(), thisDelim+1, false);
  946. if ( -1 == nextDelim )
  947. nextDelim = cstr.find(delimZ1.data(), thisDelim+1, false);
  948. if ( -1 == nextDelim )
  949. nextDelim = cstr.find(delimZ2.data(), thisDelim+1, false);
  950. if ( nextDelim < 0)
  951. return false;
  952. //kdDebug(5006) << " processing old style Mailman digest" << endl;
  953. //if ( curNode->mRoot )
  954. // curNode = curNode->mRoot;
  955. // at least one message found: build a mime tree
  956. digestHeaderStr = "Content-Type=text/plain\nContent-Description=digest header\n\n";
  957. digestHeaderStr += cstr.mid( 0, thisDelim );
  958. insertAndParseNewChildNode( *curNode,
  959. &*digestHeaderStr,
  960. "Digest Header", true );
  961. //mReader->queueHtml("<br><hr><br>");
  962. // temporarily change curent node's Content-Type
  963. // to get our embedded RfC822 messages properly inserted
  964. curNode->setType( DwMime::kTypeMultipart );
  965. curNode->setSubType( DwMime::kSubtypeDigest );
  966. while( -1 < nextDelim ){
  967. int thisEoL = cstr.find("\nMessage:", thisDelim, false);
  968. if ( -1 < thisEoL )
  969. thisDelim = thisEoL+1;
  970. else{
  971. thisEoL = cstr.find("\n_____________", thisDelim, false);
  972. if ( -1 < thisEoL )
  973. thisDelim = thisEoL+1;
  974. }
  975. thisEoL = cstr.find('\n', thisDelim);
  976. if ( -1 < thisEoL )
  977. thisDelim = thisEoL+1;
  978. else
  979. thisDelim = thisDelim+1;
  980. //while( thisDelim < cstr.size() && '\n' == cstr[thisDelim] )
  981. // ++thisDelim;
  982. partStr = "Content-Type=message/rfc822\nContent-Description=embedded message\n";
  983. partStr += cstr.mid( thisDelim, nextDelim-thisDelim );
  984. TQCString subject("embedded message");
  985. TQCString subSearch("\nSubject:");
  986. int subPos = partStr.find(subSearch.data(), 0, false);
  987. if ( -1 < subPos ){
  988. subject = partStr.mid(subPos+subSearch.length());
  989. thisEoL = subject.find('\n');
  990. if ( -1 < thisEoL )
  991. subject.truncate( thisEoL );
  992. }
  993. //kdDebug(5006) << " embedded message found: \"" << subject << "\"" << endl;
  994. insertAndParseNewChildNode( *curNode,
  995. &*partStr,
  996. subject, true );
  997. //mReader->queueHtml("<br><hr><br>");
  998. thisDelim = nextDelim+1;
  999. nextDelim = cstr.find(delim1.data(), thisDelim, false);
  1000. if ( -1 == nextDelim )
  1001. nextDelim = cstr.find(delim2.data(), thisDelim, false);
  1002. if ( -1 == nextDelim )
  1003. nextDelim = cstr.find(delimZ1.data(), thisDelim, false);
  1004. if ( -1 == nextDelim )
  1005. nextDelim = cstr.find(delimZ2.data(), thisDelim, false);
  1006. }
  1007. // reset curent node's Content-Type
  1008. curNode->setType( DwMime::kTypeText );
  1009. curNode->setSubType( DwMime::kSubtypePlain );
  1010. int thisEoL = cstr.find("_____________", thisDelim);
  1011. if ( -1 < thisEoL ){
  1012. thisDelim = thisEoL;
  1013. thisEoL = cstr.find('\n', thisDelim);
  1014. if ( -1 < thisEoL )
  1015. thisDelim = thisEoL+1;
  1016. }
  1017. else
  1018. thisDelim = thisDelim+1;
  1019. partStr = "Content-Type=text/plain\nContent-Description=digest footer\n\n";
  1020. partStr += cstr.mid( thisDelim );
  1021. insertAndParseNewChildNode( *curNode,
  1022. &*partStr,
  1023. "Digest Footer", true );
  1024. return true;
  1025. }
  1026. bool ObjectTreeParser::processTextPlainSubtype( partNode * curNode, ProcessResult & result ) {
  1027. if ( !mReader ) {
  1028. mRawReplyString = curNode->msgPart().bodyDecoded();
  1029. if ( curNode->isFirstTextPart() ) {
  1030. mTextualContent += curNode->msgPart().bodyToUnicode();
  1031. mTextualContentCharset = curNode->msgPart().charset();
  1032. }
  1033. return true;
  1034. }
  1035. if ( !curNode->isFirstTextPart() &&
  1036. attachmentStrategy()->defaultDisplay( curNode ) != AttachmentStrategy::Inline &&
  1037. !showOnlyOneMimePart() )
  1038. return false;
  1039. mRawReplyString = curNode->msgPart().bodyDecoded();
  1040. if ( curNode->isFirstTextPart() ) {
  1041. mTextualContent += curNode->msgPart().bodyToUnicode();
  1042. mTextualContentCharset = curNode->msgPart().charset();
  1043. }
  1044. TQString label = curNode->msgPart().fileName().stripWhiteSpace();
  1045. if ( label.isEmpty() )
  1046. label = curNode->msgPart().name().stripWhiteSpace();
  1047. const bool bDrawFrame = !curNode->isFirstTextPart()
  1048. && !showOnlyOneMimePart()
  1049. && !label.isEmpty();
  1050. if ( bDrawFrame ) {
  1051. label = KMMessage::quoteHtmlChars( label, true );
  1052. const TQString comment =
  1053. KMMessage::quoteHtmlChars( curNode->msgPart().contentDescription(), true );
  1054. const TQString fileName =
  1055. mReader->writeMessagePartToTempFile( &curNode->msgPart(),
  1056. curNode->nodeId() );
  1057. const TQString dir = TQApplication::reverseLayout() ? "rtl" : "ltr" ;
  1058. TQString htmlStr = "<table cellspacing=\"1\" class=\"textAtm\">"
  1059. "<tr class=\"textAtmH\"><td dir=\"" + dir + "\">";
  1060. if ( !fileName.isEmpty() )
  1061. htmlStr += "<a href=\"" + curNode->asHREF( "body" ) + "\">"
  1062. + label + "</a>";
  1063. else
  1064. htmlStr += label;
  1065. if ( !comment.isEmpty() )
  1066. htmlStr += "<br>" + comment;
  1067. htmlStr += "</td></tr><tr class=\"textAtmB\"><td>";
  1068. htmlWriter()->queue( htmlStr );
  1069. }
  1070. // process old style not-multipart Mailman messages to
  1071. // enable verification of the embedded messages' signatures
  1072. if ( !isMailmanMessage( curNode ) ||
  1073. !processMailmanMessage( curNode ) ) {
  1074. writeBodyString( mRawReplyString, curNode->trueFromAddress(),
  1075. codecFor( curNode ), result, !bDrawFrame );
  1076. curNode->setDisplayedEmbedded( true );
  1077. }
  1078. if ( bDrawFrame )
  1079. htmlWriter()->queue( "</td></tr></table>" );
  1080. return true;
  1081. }
  1082. void ObjectTreeParser::stdChildHandling( partNode * child ) {
  1083. if ( !child )
  1084. return;
  1085. ObjectTreeParser otp( *this );
  1086. otp.setShowOnlyOneMimePart( false );
  1087. otp.parseObjectTree( child );
  1088. mRawReplyString += otp.rawReplyString();
  1089. mTextualContent += otp.textualContent();
  1090. if ( !otp.textualContentCharset().isEmpty() )
  1091. mTextualContentCharset = otp.textualContentCharset();
  1092. }
  1093. TQString ObjectTreeParser::defaultToltecReplacementText()
  1094. {
  1095. return i18n( "This message is a <i>Toltec</i> Groupware object, it can only be viewed with "
  1096. "Microsoft Outlook in combination with the Toltec connector." );
  1097. }
  1098. bool ObjectTreeParser::processToltecMail( partNode *node )
  1099. {
  1100. if ( !node || !mHtmlWriter || !GlobalSettings::self()->showToltecReplacementText() ||
  1101. !node->isToltecMessage() || mShowRawToltecMail )
  1102. return false;
  1103. htmlWriter()->queue( GlobalSettings::self()->toltecReplacementText() );
  1104. htmlWriter()->queue( "<br><br><a href=\"kmail:showRawToltecMail\">" +
  1105. i18n( "Show Raw Message" ) + "</a>" );
  1106. return true;
  1107. }
  1108. bool ObjectTreeParser::processMultiPartMixedSubtype( partNode * node, ProcessResult & ) {
  1109. if ( processToltecMail( node ) ) {
  1110. return true;
  1111. }
  1112. partNode * child = node->firstChild();
  1113. if ( !child )
  1114. return false;
  1115. // normal treatment of the parts in the mp/mixed container
  1116. stdChildHandling( child );
  1117. return true;
  1118. }
  1119. bool ObjectTreeParser::processMultiPartAlternativeSubtype( partNode * node, ProcessResult & ) {
  1120. partNode * child = node->firstChild();
  1121. if ( !child )
  1122. return false;
  1123. partNode * dataHtml = child->findType( DwMime::kTypeText,
  1124. DwMime::kSubtypeHtml, false, true );
  1125. partNode * dataPlain = child->findType( DwMime::kTypeText,
  1126. DwMime::kSubtypePlain, false, true );
  1127. if ( (mReader && mReader->htmlMail() && dataHtml) ||
  1128. (dataHtml && dataPlain && dataPlain->msgPart().body().isEmpty()) ) {
  1129. if ( dataPlain )
  1130. dataPlain->setProcessed( true, false );
  1131. stdChildHandling( dataHtml );
  1132. return true;
  1133. }
  1134. if ( !mReader || (!mReader->htmlMail() && dataPlain) ) {
  1135. if ( dataHtml )
  1136. dataHtml->setProcessed( true, false );
  1137. stdChildHandling( dataPlain );
  1138. return true;
  1139. }
  1140. stdChildHandling( child );
  1141. return true;
  1142. }
  1143. bool ObjectTreeParser::processMultiPartDigestSubtype( partNode * node, ProcessResult & result ) {
  1144. return processMultiPartMixedSubtype( node, result );
  1145. }
  1146. bool ObjectTreeParser::processMultiPartParallelSubtype( partNode * node, ProcessResult & result ) {
  1147. return processMultiPartMixedSubtype( node, result );
  1148. }
  1149. bool ObjectTreeParser::processMultiPartSignedSubtype( partNode * node, ProcessResult & ) {
  1150. if ( node->childCount() != 2 ) {
  1151. kdDebug(5006) << "mulitpart/signed must have exactly two child parts!" << endl
  1152. << "processing as multipart/mixed" << endl;
  1153. if ( node->firstChild() )
  1154. stdChildHandling( node->firstChild() );
  1155. return node->firstChild();
  1156. }
  1157. partNode * signedData = node->firstChild();
  1158. assert( signedData );
  1159. partNode * signature = signedData->nextSibling();
  1160. assert( signature );
  1161. signature->setProcessed( true, true );
  1162. if ( !includeSignatures() ) {
  1163. stdChildHandling( signedData );
  1164. return true;
  1165. }
  1166. // FIXME(marc) check here that the protocol parameter matches the
  1167. // mimetype of "signature" (not required by the RFC, but practised
  1168. // by all implementaions of security multiparts
  1169. const TQString contentType = node->contentTypeParameter( "protocol" ).lower();
  1170. const Kleo::CryptoBackend::Protocol *protocol = 0;
  1171. if ( contentType == "application/pkcs7-signature" || contentType == "application/x-pkcs7-signature" )
  1172. protocol = Kleo::CryptoBackendFactory::instance()->smime();
  1173. else if ( contentType == "application/pgp-signature" || contentType == "application/x-pgp-signature" )
  1174. protocol = Kleo::CryptoBackendFactory::instance()->openpgp();
  1175. if ( !protocol ) {
  1176. signature->setProcessed( true, true );
  1177. stdChildHandling( signedData );
  1178. return true;
  1179. }
  1180. CryptoProtocolSaver saver( this, protocol );
  1181. node->setSignatureState( KMMsgFullySigned );
  1182. writeOpaqueOrMultipartSignedData( signedData, *signature,
  1183. node->trueFromAddress() );
  1184. return true;
  1185. }
  1186. bool ObjectTreeParser::processMultiPartEncryptedSubtype( partNode * node, ProcessResult & result ) {
  1187. partNode * child = node->firstChild();
  1188. if ( !child )
  1189. return false;
  1190. if ( keepEncryptions() ) {
  1191. node->setEncryptionState( KMMsgFullyEncrypted );
  1192. const TQCString cstr = node->msgPart().bodyDecoded();
  1193. if ( mReader )
  1194. writeBodyString( cstr, node->trueFromAddress(),
  1195. codecFor( node ), result, false );
  1196. mRawReplyString += cstr;
  1197. return true;
  1198. }
  1199. const Kleo::CryptoBackend::Protocol * useThisCryptProto = 0;
  1200. /*
  1201. ATTENTION: This code is to be replaced by the new 'auto-detect' feature. --------------------------------------
  1202. */
  1203. partNode * data = child->findType( DwMime::kTypeApplication,
  1204. DwMime::kSubtypeOctetStream, false, true );
  1205. if ( data ) {
  1206. useThisCryptProto = Kleo::CryptoBackendFactory::instance()->openpgp();
  1207. }
  1208. if ( !data ) {
  1209. data = child->findType( DwMime::kTypeApplication,
  1210. DwMime::kSubtypePkcs7Mime, false, true );
  1211. if ( data ) {
  1212. useThisCryptProto = Kleo::CryptoBackendFactory::instance()->smime();
  1213. }
  1214. }
  1215. /*
  1216. ---------------------------------------------------------------------------------------------------------------
  1217. */
  1218. if ( !data ) {
  1219. stdChildHandling( child );
  1220. return true;
  1221. }
  1222. CryptoProtocolSaver cpws( this, useThisCryptProto );
  1223. if ( partNode * dataChild = data->firstChild() ) {
  1224. //kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl;
  1225. stdChildHandling( dataChild );
  1226. //kdDebug(5006) << "\n-----> Returning from parseObjectTree( curNode->mChild )\n" << endl;
  1227. return true;
  1228. }
  1229. node->setEncryptionState( KMMsgFullyEncrypted );
  1230. if ( mReader && !mReader->decryptMessage() ) {
  1231. writeDeferredDecryptionBlock();
  1232. data->setProcessed( true, false ); // Set the data node to done to prevent it from being processed
  1233. return true;
  1234. }
  1235. //kdDebug(5006) << "\n-----> Initially processing encrypted data\n" << endl;
  1236. PartMetaData messagePart;
  1237. TQCString decryptedData;
  1238. bool signatureFound;
  1239. std::vector<GpgME::Signature> signatures;
  1240. bool passphraseError;
  1241. bool actuallyEncrypted = true;
  1242. bool decryptionStarted;
  1243. bool bOkDecrypt = okDecryptMIME( *data,
  1244. decryptedData,
  1245. signatureFound,
  1246. signatures,
  1247. true,
  1248. passphraseError,
  1249. actuallyEncrypted,
  1250. decryptionStarted,
  1251. messagePart.errorText,
  1252. messagePart.auditLogError,
  1253. messagePart.auditLog );
  1254. if ( decryptionStarted ) {
  1255. writeDecryptionInProgressBlock();
  1256. return true;
  1257. }
  1258. // paint the frame
  1259. if ( mReader ) {
  1260. messagePart.isDecryptable = bOkDecrypt;
  1261. messagePart.isEncrypted = true;
  1262. messagePart.isSigned = false;
  1263. htmlWriter()->queue( writeSigstatHeader( messagePart,
  1264. cryptoProtocol(),
  1265. node->trueFromAddress() ) );
  1266. }
  1267. if ( bOkDecrypt ) {
  1268. // Note: Multipart/Encrypted might also be signed
  1269. // without encapsulating a nicely formatted
  1270. // ~~~~~~~ Multipart/Signed part.
  1271. // (see RFC 3156 --> 6.2)
  1272. // In this case we paint a _2nd_ frame inside the
  1273. // encryption frame, but we do _not_ show a respective
  1274. // encapsulated MIME part in the Mime Tree Viewer
  1275. // since we do want to show the _true_ structure of the
  1276. // message there - not the structure that the sender's
  1277. // MUA 'should' have sent. :-D (khz, 12.09.2002)
  1278. //
  1279. if ( signatureFound ) {
  1280. writeOpaqueOrMultipartSignedData( 0,
  1281. *node,
  1282. node->trueFromAddress(),
  1283. false,
  1284. &decryptedData,
  1285. signatures,
  1286. false );
  1287. node->setSignatureState( KMMsgFullySigned );
  1288. } else {
  1289. insertAndParseNewChildNode( *node,
  1290. &*decryptedData,
  1291. "encrypted data" );
  1292. }
  1293. } else {
  1294. mRawReplyString += decryptedData;
  1295. if ( mReader ) {
  1296. // print the error message that was returned in decryptedData
  1297. // (utf8-encoded)
  1298. htmlWriter()->queue( TQString::fromUtf8( decryptedData.data() ) );
  1299. }
  1300. }
  1301. if ( mReader )
  1302. htmlWriter()->queue( writeSigstatFooter( messagePart ) );
  1303. data->setProcessed( true, false ); // Set the data node to done to prevent it from being processed
  1304. return true;
  1305. }
  1306. bool ObjectTreeParser::processMessageRfc822Subtype( partNode * node, ProcessResult & ) {
  1307. if ( mReader
  1308. && !attachmentStrategy()->inlineNestedMessages()
  1309. && !showOnlyOneMimePart() )
  1310. return false;
  1311. if ( partNode * child = node->firstChild() ) {
  1312. //kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl;
  1313. ObjectTreeParser otp( mReader, cryptoProtocol() );
  1314. otp.parseObjectTree( child );
  1315. mRawReplyString += otp.rawReplyString();
  1316. mTextualContent += otp.textualContent();
  1317. if ( !otp.textualContentCharset().isEmpty() )
  1318. mTextualContentCharset = otp.textualContentCharset();
  1319. //kdDebug(5006) << "\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl;
  1320. return true;
  1321. }
  1322. //kdDebug(5006) << "\n-----> Initially processing data of embedded RfC 822 message\n" << endl;
  1323. // paint the frame
  1324. PartMetaData messagePart;
  1325. if ( mReader ) {
  1326. messagePart.isEncrypted = false;
  1327. messagePart.isSigned = false;
  1328. messagePart.isEncapsulatedRfc822Message = true;
  1329. TQString filename =
  1330. mReader->writeMessagePartToTempFile( &node->msgPart(),
  1331. node->nodeId() );
  1332. htmlWriter()->queue( writeSigstatHeader( messagePart,
  1333. cryptoProtocol(),
  1334. node->trueFromAddress(),
  1335. node ) );
  1336. }
  1337. TQCString rfc822messageStr( node->msgPart().bodyDecoded() );
  1338. // display the headers of the encapsulated message
  1339. DwMessage* rfc822DwMessage = new DwMessage(); // will be deleted by c'tor of rfc822headers
  1340. rfc822DwMessage->FromString( rfc822messageStr );
  1341. rfc822DwMessage->Parse();
  1342. KMMessage rfc822message( rfc822DwMessage );
  1343. node->setFromAddress( rfc822message.from() );
  1344. //kdDebug(5006) << "\n-----> Store RfC 822 message header \"From: " << rfc822message.from() << "\"\n" << endl;
  1345. if ( mReader )
  1346. htmlWriter()->queue( mReader->writeMsgHeader( &rfc822message ) );
  1347. //mReader->parseMsgHeader( &rfc822message );
  1348. // display the body of the encapsulated message
  1349. insertAndParseNewChildNode( *node,
  1350. &*rfc822messageStr,
  1351. "encapsulated message", false /*append*/,
  1352. false /*add to textual content*/ );
  1353. node->setDisplayedEmbedded( true );
  1354. if ( mReader )
  1355. htmlWriter()->queue( writeSigstatFooter( messagePart ) );
  1356. return true;
  1357. }
  1358. bool ObjectTreeParser::processApplicationOctetStreamSubtype( partNode * node, ProcessResult & result ) {
  1359. if ( partNode * child = node->firstChild() ) {
  1360. //kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl;
  1361. ObjectTreeParser otp( mReader, cryptoProtocol() );
  1362. otp.parseObjectTree( child );
  1363. mRawReplyString += otp.rawReplyString();
  1364. mTextualContent += otp.textualContent();
  1365. if ( !otp.textualContentCharset().isEmpty() )
  1366. mTextualContentCharset = otp.textualContentCharset();
  1367. //kdDebug(5006) << "\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl;
  1368. return true;
  1369. }
  1370. const Kleo::CryptoBackend::Protocol* oldUseThisCryptPlug = cryptoProtocol();
  1371. if ( node->parentNode()
  1372. && DwMime::kTypeMultipart == node->parentNode()->type()
  1373. && DwMime::kSubtypeEncrypted == node->parentNode()->subType() ) {
  1374. //kdDebug(5006) << "\n-----> Initially processing encrypted data\n" << endl;
  1375. node->setEncryptionState( KMMsgFullyEncrypted );
  1376. if ( keepEncryptions() ) {
  1377. const TQCString cstr = node->msgPart().bodyDecoded();
  1378. if ( mReader )
  1379. writeBodyString( cstr, node->trueFromAddress(),
  1380. codecFor( node ), result, false );
  1381. mRawReplyString += cstr;
  1382. } else if ( mReader && !mReader->decryptMessage() ) {
  1383. writeDeferredDecryptionBlock();
  1384. } else {
  1385. /*
  1386. ATTENTION: This code is to be replaced by the planned 'auto-detect' feature.
  1387. */
  1388. PartMetaData messagePart;
  1389. setCryptoProtocol( Kleo::CryptoBackendFactory::instance()->openpgp() );
  1390. TQCString decryptedData;
  1391. bool signatureFound;
  1392. std::vector<GpgME::Signature> signatures;
  1393. bool passphraseError;
  1394. bool actuallyEncrypted = true;
  1395. bool decryptionStarted;
  1396. bool bOkDecrypt = okDecryptMIME( *node,
  1397. decryptedData,
  1398. signatureFound,
  1399. signatures,
  1400. true,
  1401. passphraseError,
  1402. actuallyEncrypted,
  1403. decryptionStarted,
  1404. messagePart.errorText,
  1405. messagePart.auditLogError,
  1406. messagePart.auditLog );
  1407. if ( decryptionStarted ) {
  1408. writeDecryptionInProgressBlock();
  1409. return true;
  1410. }
  1411. // paint the frame
  1412. if ( mReader ) {
  1413. messagePart.isDecryptable = bOkDecrypt;
  1414. messagePart.isEncrypted = true;
  1415. messagePart.isSigned = false;
  1416. htmlWriter()->queue( writeSigstatHeader( messagePart,
  1417. cryptoProtocol(),
  1418. node->trueFromAddress() ) );
  1419. }
  1420. if ( bOkDecrypt ) {
  1421. // fixing the missing attachments bug #1090-b
  1422. insertAndParseNewChildNode( *node,
  1423. &*decryptedData,
  1424. "encrypted data" );
  1425. } else {
  1426. mRawReplyString += decryptedData;
  1427. if ( mReader ) {
  1428. // print the error message that was returned in decryptedData
  1429. // (utf8-encoded)
  1430. htmlWriter()->queue( TQString::fromUtf8( decryptedData.data() ) );
  1431. }
  1432. }
  1433. if ( mReader )
  1434. htmlWriter()->queue( writeSigstatFooter( messagePart ) );
  1435. }
  1436. return true;
  1437. }
  1438. setCryptoProtocol( oldUseThisCryptPlug );
  1439. return false;
  1440. }
  1441. bool ObjectTreeParser::processApplicationPkcs7MimeSubtype( partNode * node, ProcessResult & result ) {
  1442. if ( partNode * child = node->firstChild() ) {
  1443. //kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl;
  1444. ObjectTreeParser otp( mReader, cryptoProtocol() );
  1445. otp.parseObjectTree( child );
  1446. mRawReplyString += otp.rawReplyString();
  1447. mTextualContent += otp.textualContent();
  1448. if ( !otp.textualContentCharset().isEmpty() )
  1449. mTextualContentCharset = otp.textualContentCharset();
  1450. //kdDebug(5006) << "\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl;
  1451. return true;
  1452. }
  1453. //kdDebug(5006) << "\n-----> Initially processing signed and/or encrypted data\n" << endl;
  1454. if ( !node->dwPart() || !node->dwPart()->hasHeaders() )
  1455. return false;
  1456. const Kleo::CryptoBackend::Protocol * smimeCrypto = Kleo::CryptoBackendFactory::instance()->smime();
  1457. const TQString smimeType = node->contentTypeParameter("smime-type").lower();
  1458. if ( smimeType == "certs-only" ) {
  1459. result.setNeverDisplayInline( true );
  1460. if ( !smimeCrypto || !mReader )
  1461. return false;
  1462. const TDEConfigGroup reader( KMKernel::config(), "Reader" );
  1463. if ( !reader.readBoolEntry( "AutoImportKeys", false ) )
  1464. return false;
  1465. const TQByteArray certData = node->msgPart().bodyDecodedBinary();
  1466. const STD_NAMESPACE_PREFIX auto_ptr<Kleo::ImportJob> import( smimeCrypto->importJob() );
  1467. const GpgME::ImportResult res = import->exec( certData );
  1468. if ( res.error() ) {
  1469. htmlWriter()->queue( i18n( "Sorry, certificate could not be imported.<br>"
  1470. "Reason: %1").arg( TQString::fromLocal8Bit( res.error().asString() ) ) );
  1471. return true;
  1472. }
  1473. const int nImp = res.numImported();
  1474. const int nUnc = res.numUnchanged();
  1475. const int nSKImp = res.numSecretKeysImported();
  1476. const int nSKUnc = res.numSecretKeysUnchanged();
  1477. if ( !nImp && !nSKImp && !nUnc && !nSKUnc ) {
  1478. htmlWriter()->queue( i18n( "Sorry, no certificates were found in this message." ) );
  1479. return true;
  1480. }
  1481. TQString comment = "<b>" + i18n( "Certificate import status:" ) + "</b><br>&nbsp;<br>";
  1482. if ( nImp )
  1483. comment += i18n( "1 new certificate was imported.",
  1484. "%n new certificates were imported.", nImp ) + "<br>";
  1485. if ( nUnc )
  1486. comment += i18n( "1 certificate was unchanged.",
  1487. "%n certificates were unchanged.", nUnc ) + "<br>";
  1488. if ( nSKImp )
  1489. comment += i18n( "1 new secret key was imported.",
  1490. "%n new secret keys were imported.", nSKImp ) + "<br>";
  1491. if ( nSKUnc )
  1492. comment += i18n( "1 secret key was unchanged.",
  1493. "%n secret keys were unchanged.", nSKUnc ) + "<br>";
  1494. comment += "&nbsp;<br>";
  1495. htmlWriter()->queue( comment );
  1496. if ( !nImp && !nSKImp ) {
  1497. htmlWriter()->queue( "<hr>" );
  1498. return true;
  1499. }
  1500. const std::vector<GpgME::Import> imports = res.imports();
  1501. if ( imports.empty() ) {
  1502. htmlWriter()->queue( i18n( "Sorry, no details on certificate import available." ) + "<hr>" );
  1503. return true;
  1504. }
  1505. htmlWriter()->queue( "<b>" + i18n( "Certificate import details:" ) + "</b><br>" );
  1506. for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != imports.end() ; ++it ) {
  1507. if ( (*it).error() )
  1508. htmlWriter()->queue( i18n( "Failed: %1 (%2)" )
  1509. .arg( (*it).fingerprint(),
  1510. TQString::fromLocal8Bit( (*it).error().asString() ) ) );
  1511. else if ( (*it).status() & ~GpgME::Import::ContainedSecretKey ) {
  1512. if ( (*it).status() & GpgME::Import::ContainedSecretKey ) {
  1513. htmlWriter()->queue( i18n( "New or changed: %1 (secret key available)" ).arg( (*it).fingerprint() ) );
  1514. }
  1515. else {
  1516. htmlWriter()->queue( i18n( "New or changed: %1" ).arg( (*it).fingerprint() ) );
  1517. }
  1518. }
  1519. htmlWriter()->queue( "<br>" );
  1520. }
  1521. htmlWriter()->queue( "<hr>" );
  1522. return true;
  1523. }
  1524. if ( !smimeCrypto )
  1525. return false;
  1526. CryptoProtocolSaver cpws( this, smimeCrypto );
  1527. bool isSigned = smimeType == "signed-data";
  1528. bool isEncrypted = smimeType == "enveloped-data";
  1529. // Analyze "signTestNode" node to find/verify a signature.
  1530. // If zero this verification was successfully done after
  1531. // decrypting via recursion by insertAndParseNewChildNode().
  1532. partNode* signTestNode = isEncrypted ? 0 : node;
  1533. // We try decrypting the content
  1534. // if we either *know* that it is an encrypted message part
  1535. // or there is neither signed nor encrypted parameter.
  1536. if ( !isSigned ) {
  1537. if ( isEncrypted ) {
  1538. //kdDebug(5006) << "pkcs7 mime == S/MIME TYPE: enveloped (encrypted) data" << endl;
  1539. }
  1540. else {
  1541. //kdDebug(5006) << "pkcs7 mime - type unknown - enveloped (encrypted) data ?" << endl;
  1542. }
  1543. TQCString decryptedData;
  1544. PartMetaData messagePart;
  1545. messagePart.isEncrypted = true;
  1546. messagePart.isSigned = false;
  1547. bool signatureFound;
  1548. std::vector<GpgME::Signature> signatures;
  1549. bool passphraseError;
  1550. bool actuallyEncrypted = true;
  1551. bool decryptionStarted;
  1552. if ( mReader && !mReader->decryptMessage() ) {
  1553. writeDeferredDecryptionBlock();
  1554. isEncrypted = true;
  1555. signTestNode = 0; // PENDING(marc) to be abs. sure, we'd need to have to look at the content
  1556. } else {
  1557. const bool bOkDecrypt = okDecryptMIME( *node,
  1558. decryptedData,
  1559. signatureFound,
  1560. signatures,
  1561. false,
  1562. passphraseError,
  1563. actuallyEncrypted,
  1564. decryptionStarted,
  1565. messagePart.errorText,
  1566. messagePart.auditLogError,
  1567. messagePart.auditLog );
  1568. if ( decryptionStarted ) {
  1569. writeDecryptionInProgressBlock();
  1570. return true;
  1571. }
  1572. if ( bOkDecrypt ) {
  1573. //kdDebug(5006) << "pkcs7 mime - encryption found - enveloped (encrypted) data !" << endl;
  1574. isEncrypted = true;
  1575. node->setEncryptionState( KMMsgFullyEncrypted );
  1576. signTestNode = 0;
  1577. // paint the frame
  1578. messagePart.isDecryptable = true;
  1579. if ( mReader )
  1580. htmlWriter()->queue( writeSigstatHeader( messagePart,
  1581. cryptoProtocol(),
  1582. node->trueFromAddress() ) );
  1583. insertAndParseNewChildNode( *node,
  1584. &*decryptedData,
  1585. "encrypted data" );
  1586. if ( mReader )
  1587. htmlWriter()->queue( writeSigstatFooter( messagePart ) );
  1588. } else {
  1589. // decryption failed, which could be because the part was encrypted but
  1590. // decryption failed, or because we didn't know if it was encrypted, tried,
  1591. // and failed. If the message was not actually encrypted, we continue
  1592. // assuming it's signed
  1593. if ( passphraseError || ( smimeType.isEmpty() && actuallyEncrypted ) ) {
  1594. isEncrypted = true;
  1595. signTestNode = 0;
  1596. }
  1597. if ( isEncrypted ) {
  1598. //kdDebug(5006) << "pkcs7 mime - ERROR: COULD NOT DECRYPT enveloped data !" << endl;
  1599. // paint the frame
  1600. messagePart.isDecryptable = false;
  1601. if ( mReader ) {
  1602. htmlWriter()->queue( writeSigstatHeader( messagePart,
  1603. cryptoProtocol(),
  1604. node->trueFromAddress() ) );
  1605. assert( mReader->decryptMessage() ); // handled above
  1606. writePartIcon( &node->msgPart(), node->nodeId() );
  1607. htmlWriter()->queue( writeSigstatFooter( messagePart ) );
  1608. }
  1609. } else {
  1610. //kdDebug(5006) << "pkcs7 mime - NO encryption found" << endl;
  1611. }
  1612. }
  1613. }
  1614. if ( isEncrypted )
  1615. node->setEncryptionState( KMMsgFullyEncrypted );
  1616. }
  1617. // We now try signature verification if necessarry.
  1618. if ( signTestNode ) {
  1619. if ( isSigned ) {
  1620. //kdDebug(5006) << "pkcs7 mime == S/MIME TYPE: opaque signed data" << endl;
  1621. }
  1622. else {
  1623. //kdDebug(5006) << "pkcs7 mime - type unknown - opaque signed data ?" << endl;
  1624. }
  1625. bool sigFound = writeOpaqueOrMultipartSignedData( 0,
  1626. *signTestNode,
  1627. node->trueFromAddress(),
  1628. true,
  1629. 0,
  1630. std::vector<GpgME::Signature>(),
  1631. isEncrypted );
  1632. if ( sigFound ) {
  1633. if ( !isSigned ) {
  1634. //kdDebug(5006) << "pkcs7 mime - signature found - opaque signed data !" << endl;
  1635. isSigned = true;
  1636. }
  1637. signTestNode->setSignatureState( KMMsgFullySigned );
  1638. if ( signTestNode != node )
  1639. node->setSignatureState( KMMsgFullySigned );
  1640. } else {
  1641. //kdDebug(5006) << "pkcs7 mime - NO signature found :-(" << endl;
  1642. }
  1643. }
  1644. return isSigned || isEncrypted;
  1645. }
  1646. bool ObjectTreeParser::decryptChiasmus( const TQByteArray& data, TQByteArray& bodyDecoded, TQString& errorText )
  1647. {
  1648. const Kleo::CryptoBackend::Protocol * chiasmus =
  1649. Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
  1650. Q_ASSERT( chiasmus );
  1651. if ( !chiasmus )
  1652. return false;
  1653. const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob( "x-obtain-keys", TQStringVariantMap() ) );
  1654. if ( !listjob.get() ) {
  1655. errorText = i18n( "Chiasmus backend does not offer the "
  1656. "\"x-obtain-keys\" function. Please report this bug." );
  1657. return false;
  1658. }
  1659. if ( listjob->exec() ) {
  1660. errorText = i18n( "Chiasmus Backend Error" );
  1661. return false;
  1662. }
  1663. const TQVariant result = listjob->property( "result" );
  1664. if ( result.type() != TQVariant::StringList ) {
  1665. errorText = i18n( "Unexpected return value from Chiasmus backend: "
  1666. "The \"x-obtain-keys\" function did not return a "
  1667. "string list. Please report this bug." );
  1668. return false;
  1669. }
  1670. const TQStringList keys = result.toStringList();
  1671. if ( keys.empty() ) {
  1672. errorText = i18n( "No keys have been found. Please check that a "
  1673. "valid key path has been set in the Chiasmus "
  1674. "configuration." );
  1675. return false;
  1676. }
  1677. emit mReader->noDrag();
  1678. ChiasmusKeySelector selectorDlg( mReader, i18n( "Chiasmus Decryption Key Selection" ),
  1679. keys, GlobalSettings::chiasmusDecryptionKey(),
  1680. GlobalSettings::chiasmusDecryptionOptions() );
  1681. if ( selectorDlg.exec() != TQDialog::Accepted )
  1682. return false;
  1683. GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
  1684. GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
  1685. assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
  1686. const STD_NAMESPACE_PREFIX auto_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-decrypt", TQStringVariantMap() ) );
  1687. if ( !job.get() ) {
  1688. errorText = i18n( "Chiasmus backend does not offer the "
  1689. "\"x-decrypt\" function. Please report this bug." );
  1690. return false;
  1691. }
  1692. if ( !job->setProperty( "key", GlobalSettings::chiasmusDecryptionKey() ) ||
  1693. !job->setProperty( "options", GlobalSettings::chiasmusDecryptionOptions() ) ||
  1694. !job->setProperty( "input", data ) ) {
  1695. errorText = i18n( "The \"x-decrypt\" function does not accept "
  1696. "the expected parameters. Please report this bug." );
  1697. return false;
  1698. }
  1699. if ( job->exec() ) {
  1700. errorText = i18n( "Chiasmus Decryption Error" );
  1701. return false;
  1702. }
  1703. const TQVariant resultData = job->property( "result" );
  1704. if ( resultData.type() != TQVariant::ByteArray ) {
  1705. errorText = i18n( "Unexpected return value from Chiasmus backend: "
  1706. "The \"x-decrypt\" function did not return a "
  1707. "byte array. Please report this bug." );
  1708. return false;
  1709. }
  1710. bodyDecoded = resultData.toByteArray();
  1711. return true;
  1712. }
  1713. bool ObjectTreeParser::processApplicationChiasmusTextSubtype( partNode * curNode, ProcessResult & result )
  1714. {
  1715. if ( !mReader ) {
  1716. mRawReplyString = curNode->msgPart().bodyDecoded();
  1717. mTextualContent += curNode->msgPart().bodyToUnicode();
  1718. mTextualContentCharset = curNode->msgPart().charset();
  1719. return true;
  1720. }
  1721. TQByteArray decryptedBody;
  1722. TQString errorText;
  1723. const TQByteArray data = curNode->msgPart().bodyDecodedBinary();
  1724. bool bOkDecrypt = decryptChiasmus( data, decryptedBody, errorText );
  1725. PartMetaData messagePart;
  1726. messagePart.isDecryptable = bOkDecrypt;
  1727. messagePart.isEncrypted = true;
  1728. messagePart.isSigned = false;
  1729. messagePart.errorText = errorText;
  1730. if ( mReader )
  1731. htmlWriter()->queue( writeSigstatHeader( messagePart,
  1732. 0, //cryptPlugWrapper(),
  1733. curNode->trueFromAddress() ) );
  1734. const TQByteArray body = bOkDecrypt ? decryptedBody : data;
  1735. const TQString chiasmusCharset = curNode->contentTypeParameter("chiasmus-charset");
  1736. const TQTextCodec* aCodec = chiasmusCharset.isEmpty()
  1737. ? codecFor( curNode )
  1738. : KMMsgBase::codecForName( chiasmusCharset.ascii() );
  1739. htmlWriter()->queue( quotedHTML( aCodec->toUnicode( body ), false /*decorate*/ ) );
  1740. result.setInlineEncryptionState( KMMsgFullyEncrypted );
  1741. if ( mReader )
  1742. htmlWriter()->queue( writeSigstatFooter( messagePart ) );
  1743. return true;
  1744. }
  1745. bool ObjectTreeParser::processApplicationMsTnefSubtype( partNode *node, ProcessResult &result )
  1746. {
  1747. Q_UNUSED( result );
  1748. if ( !mReader )
  1749. return false;
  1750. const TQString fileName = mReader->writeMessagePartToTempFile( &node->msgPart(), node->nodeId() );
  1751. KTNEFParser parser;
  1752. if ( !parser.openFile( fileName ) || !parser.message()) {
  1753. kdDebug() << k_funcinfo << "Could not parse " << fileName << endl;
  1754. return false;
  1755. }
  1756. TQPtrList<KTNEFAttach> tnefatts = parser.message()->attachmentList();
  1757. if ( tnefatts.isEmpty() ) {
  1758. kdDebug() << k_funcinfo << "No attachments found in " << fileName << endl;
  1759. return false;
  1760. }
  1761. if ( !showOnlyOneMimePart() ) {
  1762. TQString label = node->msgPart().fileName().stripWhiteSpace();
  1763. if ( label.isEmpty() )
  1764. label = node->msgPart().name().stripWhiteSpace();
  1765. label = KMMessage::quoteHtmlChars( label, true );
  1766. const TQString comment = KMMessage::quoteHtmlChars( node->msgPart().contentDescription(), true );
  1767. const TQString dir = TQApplication::reverseLayout() ? "rtl" : "ltr" ;
  1768. TQString htmlStr = "<table cellspacing=\"1\" class=\"textAtm\">"
  1769. "<tr class=\"textAtmH\"><td dir=\"" + dir + "\">";
  1770. if ( !fileName.isEmpty() )
  1771. htmlStr += "<a href=\"" + node->asHREF( "body" ) + "\">"
  1772. + label + "</a>";
  1773. else
  1774. htmlStr += label;
  1775. if ( !comment.isEmpty() )
  1776. htmlStr += "<br>" + comment;
  1777. htmlStr += "</td></tr><tr class=\"textAtmB\"><td>";
  1778. htmlWriter()->queue( htmlStr );
  1779. }
  1780. for ( uint i = 0; i < tnefatts.count(); ++i ) {
  1781. KTNEFAttach *att = tnefatts.at( i );
  1782. TQString label = att->displayName();
  1783. if( label.isEmpty() )
  1784. label = att->name();
  1785. label = KMMessage::quoteHtmlChars( label, true );
  1786. TQString dir = mReader->createTempDir( "ktnef-" + TQString::number( i ) );
  1787. parser.extractFileTo( att->name(), dir );
  1788. mReader->mTempFiles.append( dir + TQDir::separator() + att->name() );
  1789. TQString href = "file:" + KURL::encode_string( dir + TQDir::separator() + att->name() );
  1790. KMimeType::Ptr mimeType = KMimeType::mimeType( att->mimeTag() );
  1791. TQString iconName = TDEGlobal::instance()->iconLoader()->iconPath( mimeType->icon( TQString(), false ), TDEIcon::Desktop );
  1792. htmlWriter()->queue( "<div><a href=\"" + href + "\"><img src=\"" +
  1793. iconName + "\" border=\"0\" style=\"max-width: 100%\">" + label +
  1794. "</a></div><br>" );
  1795. }
  1796. if ( !showOnlyOneMimePart() )
  1797. htmlWriter()->queue( "</td></tr></table>" );
  1798. return true;
  1799. }
  1800. void ObjectTreeParser::writeBodyString( const TQCString & bodyString,
  1801. const TQString & fromAddress,
  1802. const TQTextCodec * codec,
  1803. ProcessResult & result,
  1804. bool decorate ) {
  1805. assert( mReader ); assert( codec );
  1806. KMMsgSignatureState inlineSignatureState = result.inlineSignatureState();
  1807. KMMsgEncryptionState inlineEncryptionState = result.inlineEncryptionState();
  1808. writeBodyStr( bodyString, codec, fromAddress,
  1809. inlineSignatureState, inlineEncryptionState, decorate );
  1810. result.setInlineSignatureState( inlineSignatureState );
  1811. result.setInlineEncryptionState( inlineEncryptionState );
  1812. }
  1813. void ObjectTreeParser::writePartIcon( KMMessagePart * msgPart, int partNum, bool inlineImage ) {
  1814. if ( !mReader || !msgPart )
  1815. return;
  1816. TQString label = msgPart->fileName();
  1817. if( label.isEmpty() )
  1818. label = msgPart->name();
  1819. if( label.isEmpty() )
  1820. label = "unnamed";
  1821. label = KMMessage::quoteHtmlChars( label, true );
  1822. TQString comment = msgPart->contentDescription();
  1823. comment = KMMessage::quoteHtmlChars( comment, true );
  1824. if ( label == comment ) comment = TQString();
  1825. TQString fileName = mReader->writeMessagePartToTempFile( msgPart, partNum );
  1826. TQString href = TQString( "attachment:%1?place=body" ).arg( partNum );
  1827. TQString iconName;
  1828. if( inlineImage )
  1829. iconName = href;
  1830. else {
  1831. iconName = msgPart->iconName();
  1832. if( iconName.right( 14 ) == "mime_empty.png" ) {
  1833. msgPart->magicSetType();
  1834. iconName = msgPart->iconName();
  1835. }
  1836. }
  1837. TQCString contentId = msgPart->contentId();
  1838. if ( !contentId.isEmpty() ) {
  1839. htmlWriter()->embedPart( contentId, href );
  1840. }
  1841. if( inlineImage )
  1842. // show the filename of the image below the embedded image
  1843. htmlWriter()->queue( "<div><a href=\"" + href + "\">"
  1844. "<img src=\"" + fileName + "\" border=\"0\" style=\"max-width: 100%\"></a>"
  1845. "</div>"
  1846. "<div><a href=\"" + href + "\">" + label + "</a>"
  1847. "</div>"
  1848. "<div>" + comment + "</div><br>" );
  1849. else
  1850. // show the filename next to the icon
  1851. htmlWriter()->queue( "<div><a href=\"" + href + "\"><img src=\"" +
  1852. iconName + "\" border=\"0\" style=\"max-width: 100%\">" + label +
  1853. "</a></div>"
  1854. "<div>" + comment + "</div><br>" );
  1855. }
  1856. #define SIG_FRAME_COL_UNDEF 99
  1857. #define SIG_FRAME_COL_RED -1
  1858. #define SIG_FRAME_COL_YELLOW 0
  1859. #define SIG_FRAME_COL_GREEN 1
  1860. TQString ObjectTreeParser::sigStatusToString( const Kleo::CryptoBackend::Protocol* cryptProto,
  1861. int status_code,
  1862. GpgME::Signature::Summary summary,
  1863. int& frameColor,
  1864. bool& showKeyInfos )
  1865. {
  1866. // note: At the moment frameColor and showKeyInfos are
  1867. // used for CMS only but not for PGP signatures
  1868. // pending(khz): Implement usage of these for PGP sigs as well.
  1869. showKeyInfos = true;
  1870. TQString result;
  1871. if( cryptProto ) {
  1872. if( cryptProto == Kleo::CryptoBackendFactory::instance()->openpgp() ) {
  1873. // process enum according to it's definition to be read in
  1874. // GNU Privacy Guard CVS repository /gpgme/gpgme/gpgme.h
  1875. switch( status_code ) {
  1876. case 0: // GPGME_SIG_STAT_NONE
  1877. result = i18n("Error: Signature not verified");
  1878. break;
  1879. case 1: // GPGME_SIG_STAT_GOOD
  1880. result = i18n("Good signature");
  1881. break;
  1882. case 2: // GPGME_SIG_STAT_BAD
  1883. result = i18n("<b>Bad</b> signature");
  1884. break;
  1885. case 3: // GPGME_SIG_STAT_NOKEY
  1886. result = i18n("No public key to verify the signature");
  1887. break;
  1888. case 4: // GPGME_SIG_STAT_NOSIG
  1889. result = i18n("No signature found");
  1890. break;
  1891. case 5: // GPGME_SIG_STAT_ERROR
  1892. result = i18n("Error verifying the signature");
  1893. break;
  1894. case 6: // GPGME_SIG_STAT_DIFF
  1895. result = i18n("Different results for signatures");
  1896. break;
  1897. /* PENDING(khz) Verify exact meaning of the following values:
  1898. case 7: // GPGME_SIG_STAT_GOOD_EXP
  1899. return i18n("Signature certificate is expired");
  1900. break;
  1901. case 8: // GPGME_SIG_STAT_GOOD_EXPKEY
  1902. return i18n("One of the certificate's keys is expired");
  1903. break;
  1904. */
  1905. default:
  1906. result = ""; // do *not* return a default text here !
  1907. break;
  1908. }
  1909. }
  1910. else if ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() ) {
  1911. // process status bits according to SigStatus_...
  1912. // definitions in tdenetwork/libtdenetwork/cryptplug.h
  1913. if( summary == GpgME::Signature::None ) {
  1914. result = i18n("No status information available.");
  1915. frameColor = SIG_FRAME_COL_YELLOW;
  1916. showKeyInfos = false;
  1917. return result;
  1918. }
  1919. if( summary & GpgME::Signature::Valid ) {
  1920. result = i18n("Good signature.");
  1921. // Note:
  1922. // Here we are work differently than KMail did before!
  1923. //
  1924. // The GOOD case ( == sig matching and the complete
  1925. // certificate chain was verified and is valid today )
  1926. // by definition does *not* show any key
  1927. // information but just states that things are OK.
  1928. // (khz, according to LinuxTag 2002 meeting)
  1929. frameColor = SIG_FRAME_COL_GREEN;
  1930. showKeyInfos = false;
  1931. return result;
  1932. }
  1933. // we are still there? OK, let's test the different cases:
  1934. // we assume green, test for yellow or red (in this order!)
  1935. frameColor = SIG_FRAME_COL_GREEN;
  1936. TQString result2;
  1937. if( summary & GpgME::Signature::KeyExpired ){
  1938. // still is green!
  1939. result2 += i18n("One key has expired.");
  1940. }
  1941. if( summary & GpgME::Signature::SigExpired ){
  1942. // and still is green!
  1943. result2 += i18n("The signature has expired.");
  1944. }
  1945. // test for yellow:
  1946. if( summary & GpgME::Signature::KeyMissing ) {
  1947. result2 += i18n("Unable to verify: key missing.");
  1948. // if the signature certificate is missing
  1949. // we cannot show infos on it
  1950. showKeyInfos = false;
  1951. frameColor = SIG_FRAME_COL_YELLOW;
  1952. }
  1953. if( summary & GpgME::Signature::CrlMissing ){
  1954. result2 += i18n("CRL not available.");
  1955. frameColor = SIG_FRAME_COL_YELLOW;
  1956. }
  1957. if( summary & GpgME::Signature::CrlTooOld ){
  1958. result2 += i18n("Available CRL is too old.");
  1959. frameColor = SIG_FRAME_COL_YELLOW;
  1960. }
  1961. if( summary & GpgME::Signature::BadPolicy ){
  1962. result2 += i18n("A policy was not met.");
  1963. frameColor = SIG_FRAME_COL_YELLOW;
  1964. }
  1965. if( summary & GpgME::Signature::SysError ){
  1966. result2 += i18n("A system error occurred.");
  1967. // if a system error occurred
  1968. // we cannot trust any information
  1969. // that was given back by the plug-in
  1970. showKeyInfos = false;
  1971. frameColor = SIG_FRAME_COL_YELLOW;
  1972. }
  1973. // test for red:
  1974. if( summary & GpgME::Signature::KeyRevoked ){
  1975. // this is red!
  1976. result2 += i18n("One key has been revoked.");
  1977. frameColor = SIG_FRAME_COL_RED;
  1978. }
  1979. if( summary & GpgME::Signature::Red ) {
  1980. if( result2.isEmpty() )
  1981. // Note:
  1982. // Here we are work differently than KMail did before!
  1983. //
  1984. // The BAD case ( == sig *not* matching )
  1985. // by definition does *not* show any key
  1986. // information but just states that things are BAD.
  1987. //
  1988. // The reason for this: In this case ALL information
  1989. // might be falsificated, we can NOT trust the data
  1990. // in the body NOT the signature - so we don't show
  1991. // any key/signature information at all!
  1992. // (khz, according to LinuxTag 2002 meeting)
  1993. showKeyInfos = false;
  1994. frameColor = SIG_FRAME_COL_RED;
  1995. }
  1996. else
  1997. result = "";
  1998. if( SIG_FRAME_COL_GREEN == frameColor ) {
  1999. result = i18n("Good signature.");
  2000. } else if( SIG_FRAME_COL_RED == frameColor ) {
  2001. result = i18n("<b>Bad</b> signature.");
  2002. } else
  2003. result = "";
  2004. if( !result2.isEmpty() ) {
  2005. if( !result.isEmpty() )
  2006. result.append("<br />");
  2007. result.append( result2 );
  2008. }
  2009. }
  2010. /*
  2011. // add i18n support for 3rd party plug-ins here:
  2012. else if (0 <= cryptPlug->libName().find( "yetanotherpluginname", 0, false )) {
  2013. }
  2014. */
  2015. }
  2016. return result;
  2017. }
  2018. static TQString writeSimpleSigstatHeader( const PartMetaData &block )
  2019. {
  2020. TQString html;
  2021. html += "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td>";
  2022. if ( block.signClass == "signErr" ) {
  2023. html += i18n( "Invalid signature." );
  2024. } else if ( block.signClass == "signOkKeyBad" || block.signClass == "signWarn" ) {
  2025. html += i18n( "Not enough information to check signature validity." );
  2026. } else if ( block.signClass == "signOkKeyOk" ) {
  2027. TQString addr;
  2028. if ( !block.signerMailAddresses.isEmpty() )
  2029. addr = block.signerMailAddresses.first();
  2030. TQString name = addr;
  2031. if ( name.isEmpty() )
  2032. name = block.signer;
  2033. if ( addr.isEmpty() ) {
  2034. html += i18n( "Signature is valid." );
  2035. } else {
  2036. html += i18n( "Signed by <a href=\"mailto:%1\">%2</a>." ).arg( addr, name );
  2037. }
  2038. } else {
  2039. // should not happen
  2040. html += i18n( "Unknown signature state" );
  2041. }
  2042. html += "</td><td align=\"right\">";
  2043. html += "<a href=\"kmail:showSignatureDetails\">";
  2044. html += i18n( "Show Details" );
  2045. html += "</a></td></tr></table>";
  2046. return html;
  2047. }
  2048. static TQString beginVerboseSigstatHeader()
  2049. {
  2050. return "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td rowspan=\"2\">";
  2051. }
  2052. static TQString makeShowAuditLogLink( const GpgME::Error & err, const TQString & auditLog ) {
  2053. if ( const unsigned int code = err.code() ) {
  2054. if ( code == GPG_ERR_NOT_IMPLEMENTED ) {
  2055. //kdDebug(5006) << "makeShowAuditLogLink: not showing link (not implemented)" << endl;
  2056. return TQString();
  2057. } else if ( code == GPG_ERR_NO_DATA ) {
  2058. //kdDebug(5006) << "makeShowAuditLogLink: not showing link (not available)" << endl;
  2059. return i18n("No Audit Log available");
  2060. } else {
  2061. return i18n("Error Retrieving Audit Log: %1").arg( TQString::fromLocal8Bit( err.asString() ) );
  2062. }
  2063. }
  2064. if ( !auditLog.isEmpty() ) {
  2065. KURL url;
  2066. url.setProtocol( "kmail" );
  2067. url.setPath( "showAuditLog" );
  2068. url.addQueryItem( "log", auditLog );
  2069. return "<a href=\"" + url.htmlURL() + "\">" + i18n("The Audit Log is a detailed error log from the gnupg backend", "Show Audit Log") + "</a>";
  2070. }
  2071. return TQString();
  2072. }
  2073. static TQString endVerboseSigstatHeader( const PartMetaData & pmd )
  2074. {
  2075. TQString html;
  2076. html += "</td><td align=\"right\" valign=\"top\" nowrap=\"nowrap\">";
  2077. html += "<a href=\"kmail:hideSignatureDetails\">";
  2078. html += i18n( "Hide Details" );
  2079. html += "</a></td></tr>";
  2080. html += "<tr><td align=\"right\" valign=\"bottom\" nowrap=\"nowrap\">";
  2081. html += makeShowAuditLogLink( pmd.auditLogError, pmd.auditLog );
  2082. html += "</td></tr></table>";
  2083. return html;
  2084. }
  2085. TQString ObjectTreeParser::writeSigstatHeader( PartMetaData & block,
  2086. const Kleo::CryptoBackend::Protocol * cryptProto,
  2087. const TQString & fromAddress,
  2088. partNode *node )
  2089. {
  2090. const bool isSMIME = cryptProto && ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() );
  2091. TQString signer = block.signer;
  2092. TQString htmlStr, simpleHtmlStr;
  2093. TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
  2094. TQString cellPadding("cellpadding=\"1\"");
  2095. if( block.isEncapsulatedRfc822Message )
  2096. {
  2097. htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"rfc822\">"
  2098. "<tr class=\"rfc822H\"><td dir=\"" + dir + "\">";
  2099. if ( node )
  2100. htmlStr += "<a href=\"" + node->asHREF( "body" ) + "\">"
  2101. + i18n("Encapsulated message") + "</a>";
  2102. else
  2103. htmlStr += i18n("Encapsulated message");
  2104. htmlStr += "</td></tr><tr class=\"rfc822B\"><td>";
  2105. }
  2106. if( block.isEncrypted )
  2107. {
  2108. htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"encr\">"
  2109. "<tr class=\"encrH\"><td dir=\"" + dir + "\">";
  2110. if ( block.inProgress )
  2111. htmlStr += i18n("Please wait while the message is being decrypted...");
  2112. else if ( block.isDecryptable )
  2113. htmlStr += i18n("Encrypted message");
  2114. else {
  2115. htmlStr += i18n("Encrypted message (decryption not possible)");
  2116. if( !block.errorText.isEmpty() )
  2117. htmlStr += "<br />" + i18n("Reason: %1").arg( block.errorText );
  2118. }
  2119. htmlStr += "</td></tr><tr class=\"encrB\"><td>";
  2120. }
  2121. if ( block.isSigned && block.inProgress )
  2122. {
  2123. block.signClass = "signInProgress";
  2124. htmlStr += "<table cellspacing=\"1\" "+cellPadding+" class=\"signInProgress\">"
  2125. "<tr class=\"signInProgressH\"><td dir=\"" + dir + "\">";
  2126. htmlStr += i18n("Please wait while the signature is being verified...");
  2127. htmlStr += "</td></tr><tr class=\"signInProgressB\"><td>";
  2128. }
  2129. simpleHtmlStr = htmlStr;
  2130. if ( block.isSigned && !block.inProgress ) {
  2131. TQStringList& blockAddrs( block.signerMailAddresses );
  2132. // note: At the moment frameColor and showKeyInfos are
  2133. // used for CMS only but not for PGP signatures
  2134. // pending(khz): Implement usage of these for PGP sigs as well.
  2135. int frameColor = SIG_FRAME_COL_UNDEF;
  2136. bool showKeyInfos;
  2137. bool onlyShowKeyURL = false;
  2138. bool cannotCheckSignature = true;
  2139. TQString statusStr = sigStatusToString( cryptProto,
  2140. block.status_code,
  2141. block.sigSummary,
  2142. frameColor,
  2143. showKeyInfos );
  2144. // if needed fallback to english status text
  2145. // that was reported by the plugin
  2146. if( statusStr.isEmpty() )
  2147. statusStr = block.status;
  2148. if( block.technicalProblem )
  2149. frameColor = SIG_FRAME_COL_YELLOW;
  2150. switch( frameColor ){
  2151. case SIG_FRAME_COL_RED:
  2152. cannotCheckSignature = false;
  2153. break;
  2154. case SIG_FRAME_COL_YELLOW:
  2155. cannotCheckSignature = true;
  2156. break;
  2157. case SIG_FRAME_COL_GREEN:
  2158. cannotCheckSignature = false;
  2159. break;
  2160. }
  2161. // compose the string for displaying the key ID
  2162. // either as URL or not linked (for PGP)
  2163. // note: Once we can start PGP key manager programs
  2164. // from within KMail we could change this and
  2165. // always show the URL. (khz, 2002/06/27)
  2166. TQString startKeyHREF;
  2167. if( isSMIME )
  2168. startKeyHREF =
  2169. TQString("<a href=\"kmail:showCertificate#%1 ### %2 ### %3\">")
  2170. .arg( cryptProto->displayName(),
  2171. cryptProto->name(),
  2172. block.keyId );
  2173. TQString keyWithWithoutURL
  2174. = isSMIME
  2175. ? TQString("%1%2</a>")
  2176. .arg( startKeyHREF,
  2177. cannotCheckSignature ? i18n("[Details]") : ("0x" + block.keyId) )
  2178. : "0x" + TQString::fromUtf8( block.keyId );
  2179. // temporary hack: always show key infos!
  2180. showKeyInfos = true;
  2181. // Sorry for using 'black' as null color but .isValid()
  2182. // checking with TQColor default c'tor did not work for
  2183. // some reason.
  2184. if( isSMIME && (SIG_FRAME_COL_UNDEF != frameColor) ) {
  2185. // new frame settings for CMS:
  2186. // beautify the status string
  2187. if( !statusStr.isEmpty() ) {
  2188. statusStr.prepend("<i>");
  2189. statusStr.append( "</i>");
  2190. }
  2191. // special color handling: S/MIME uses only green/yellow/red.
  2192. switch( frameColor ) {
  2193. case SIG_FRAME_COL_RED:
  2194. block.signClass = "signErr";//"signCMSRed";
  2195. onlyShowKeyURL = true;
  2196. break;
  2197. case SIG_FRAME_COL_YELLOW:
  2198. if( block.technicalProblem )
  2199. block.signClass = "signWarn";
  2200. else
  2201. block.signClass = "signOkKeyBad";//"signCMSYellow";
  2202. break;
  2203. case SIG_FRAME_COL_GREEN:
  2204. block.signClass = "signOkKeyOk";//"signCMSGreen";
  2205. // extra hint for green case
  2206. // that email addresses in DN do not match fromAddress
  2207. TQString greenCaseWarning;
  2208. TQString msgFrom( KPIM::getEmailAddress(fromAddress) );
  2209. TQString certificate;
  2210. if( block.keyId.isEmpty() )
  2211. certificate = i18n("certificate");
  2212. else
  2213. certificate = startKeyHREF + i18n("certificate") + "</a>";
  2214. if( !blockAddrs.empty() ){
  2215. if( blockAddrs.grep(
  2216. msgFrom,
  2217. false ).isEmpty() ) {
  2218. greenCaseWarning =
  2219. "<u>" +
  2220. i18n("Warning:") +
  2221. "</u> " +
  2222. i18n("Sender's mail address is not stored "
  2223. "in the %1 used for signing.").arg(certificate) +
  2224. "<br />" +
  2225. i18n("sender: ") +
  2226. msgFrom +
  2227. "<br />" +
  2228. i18n("stored: ");
  2229. // We cannot use TQt's join() function here but
  2230. // have to join the addresses manually to
  2231. // extract the mail addresses (without '<''>')
  2232. // before including it into our string:
  2233. bool bStart = true;
  2234. for(TQStringList::ConstIterator it = blockAddrs.begin();
  2235. it != blockAddrs.end(); ++it ){
  2236. if( !bStart )
  2237. greenCaseWarning.append(", <br />&nbsp; &nbsp;");
  2238. bStart = false;
  2239. greenCaseWarning.append( KPIM::getEmailAddress(*it) );
  2240. }
  2241. }
  2242. } else {
  2243. greenCaseWarning =
  2244. "<u>" +
  2245. i18n("Warning:") +
  2246. "</u> " +
  2247. i18n("No mail address is stored in the %1 used for signing, "
  2248. "so we cannot compare it to the sender's address %2.")
  2249. .arg(certificate,msgFrom);
  2250. }
  2251. if( !greenCaseWarning.isEmpty() ) {
  2252. if( !statusStr.isEmpty() )
  2253. statusStr.append("<br />&nbsp;<br />");
  2254. statusStr.append( greenCaseWarning );
  2255. }
  2256. break;
  2257. }
  2258. TQString frame = "<table cellspacing=\"1\" "+cellPadding+" "
  2259. "class=\"" + block.signClass + "\">"
  2260. "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
  2261. htmlStr += frame + beginVerboseSigstatHeader();
  2262. simpleHtmlStr += frame;
  2263. simpleHtmlStr += writeSimpleSigstatHeader( block );
  2264. if( block.technicalProblem ) {
  2265. htmlStr += block.errorText;
  2266. }
  2267. else if( showKeyInfos ) {
  2268. if( cannotCheckSignature ) {
  2269. htmlStr += i18n( "Not enough information to check "
  2270. "signature. %1" )
  2271. .arg( keyWithWithoutURL );
  2272. }
  2273. else {
  2274. if (block.signer.isEmpty())
  2275. signer = "";
  2276. else {
  2277. if( !blockAddrs.empty() ){
  2278. TQString address = KMMessage::encodeMailtoUrl( blockAddrs.first() );
  2279. signer = "<a href=\"mailto:" + address + "\">" + signer + "</a>";
  2280. }
  2281. }
  2282. if( block.keyId.isEmpty() ) {
  2283. if( signer.isEmpty() || onlyShowKeyURL )
  2284. htmlStr += i18n( "Message was signed with unknown key." );
  2285. else
  2286. htmlStr += i18n( "Message was signed by %1." )
  2287. .arg( signer );
  2288. } else {
  2289. TQDateTime created = block.creationTime;
  2290. if( created.isValid() ) {
  2291. if( signer.isEmpty() ) {
  2292. if( onlyShowKeyURL )
  2293. htmlStr += i18n( "Message was signed with key %1." )
  2294. .arg( keyWithWithoutURL );
  2295. else
  2296. htmlStr += i18n( "Message was signed on %1 with key %2." )
  2297. .arg( TDEGlobal::locale()->formatDateTime( created ),
  2298. keyWithWithoutURL );
  2299. }
  2300. else {
  2301. if( onlyShowKeyURL )
  2302. htmlStr += i18n( "Message was signed with key %1." )
  2303. .arg( keyWithWithoutURL );
  2304. else
  2305. htmlStr += i18n( "Message was signed by %3 on %1 with key %2" )
  2306. .arg( TDEGlobal::locale()->formatDateTime( created ),
  2307. keyWithWithoutURL,
  2308. signer );
  2309. }
  2310. }
  2311. else {
  2312. if( signer.isEmpty() || onlyShowKeyURL )
  2313. htmlStr += i18n( "Message was signed with key %1." )
  2314. .arg( keyWithWithoutURL );
  2315. else
  2316. htmlStr += i18n( "Message was signed by %2 with key %1." )
  2317. .arg( keyWithWithoutURL,
  2318. signer );
  2319. }
  2320. }
  2321. }
  2322. htmlStr += "<br />";
  2323. if( !statusStr.isEmpty() ) {
  2324. htmlStr += "&nbsp;<br />";
  2325. htmlStr += i18n( "Status: " );
  2326. htmlStr += statusStr;
  2327. }
  2328. } else {
  2329. htmlStr += statusStr;
  2330. }
  2331. frame = "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
  2332. htmlStr += endVerboseSigstatHeader( block ) + frame;
  2333. simpleHtmlStr += frame;
  2334. } else {
  2335. // old frame settings for PGP:
  2336. if( block.signer.isEmpty() || block.technicalProblem ) {
  2337. block.signClass = "signWarn";
  2338. TQString frame = "<table cellspacing=\"1\" "+cellPadding+" "
  2339. "class=\"" + block.signClass + "\">"
  2340. "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
  2341. htmlStr += frame + beginVerboseSigstatHeader();
  2342. simpleHtmlStr += frame;
  2343. simpleHtmlStr += writeSimpleSigstatHeader( block );
  2344. if( block.technicalProblem ) {
  2345. htmlStr += block.errorText;
  2346. }
  2347. else {
  2348. if( !block.keyId.isEmpty() ) {
  2349. TQDateTime created = block.creationTime;
  2350. if ( created.isValid() )
  2351. htmlStr += i18n( "Message was signed on %1 with unknown key %2." )
  2352. .arg( TDEGlobal::locale()->formatDateTime( created ),
  2353. keyWithWithoutURL );
  2354. else
  2355. htmlStr += i18n( "Message was signed with unknown key %1." )
  2356. .arg( keyWithWithoutURL );
  2357. }
  2358. else
  2359. htmlStr += i18n( "Message was signed with unknown key." );
  2360. htmlStr += "<br />";
  2361. htmlStr += i18n( "The validity of the signature cannot be "
  2362. "verified." );
  2363. if( !statusStr.isEmpty() ) {
  2364. htmlStr += "<br />";
  2365. htmlStr += i18n( "Status: " );
  2366. htmlStr += "<i>";
  2367. htmlStr += statusStr;
  2368. htmlStr += "</i>";
  2369. }
  2370. }
  2371. frame = "</td></tr><tr class=\"" + block.signClass + "B\"><td>";
  2372. htmlStr += endVerboseSigstatHeader( block ) + frame;
  2373. simpleHtmlStr += frame;
  2374. }
  2375. else
  2376. {
  2377. // HTMLize the signer's user id and create mailto: link
  2378. signer = KMMessage::quoteHtmlChars( signer, true );
  2379. signer = "<a href=\"mailto:" + signer + "\">" + signer + "</a>";
  2380. if (block.isGoodSignature) {
  2381. if( block.keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
  2382. block.signClass = "signOkKeyBad";
  2383. else
  2384. block.signClass = "signOkKeyOk";
  2385. TQString frame = "<table cellspacing=\"1\" "+cellPadding+" "
  2386. "class=\"" + block.signClass + "\">"
  2387. "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
  2388. htmlStr += frame + beginVerboseSigstatHeader();
  2389. simpleHtmlStr += frame;
  2390. simpleHtmlStr += writeSimpleSigstatHeader( block );
  2391. if( !block.keyId.isEmpty() )
  2392. htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
  2393. .arg( keyWithWithoutURL,
  2394. signer );
  2395. else
  2396. htmlStr += i18n( "Message was signed by %1." ).arg( signer );
  2397. htmlStr += "<br />";
  2398. switch( block.keyTrust )
  2399. {
  2400. case Kpgp::KPGP_VALIDITY_UNKNOWN:
  2401. htmlStr += i18n( "The signature is valid, but the key's "
  2402. "validity is unknown." );
  2403. break;
  2404. case Kpgp::KPGP_VALIDITY_MARGINAL:
  2405. htmlStr += i18n( "The signature is valid and the key is "
  2406. "marginally trusted." );
  2407. break;
  2408. case Kpgp::KPGP_VALIDITY_FULL:
  2409. htmlStr += i18n( "The signature is valid and the key is "
  2410. "fully trusted." );
  2411. break;
  2412. case Kpgp::KPGP_VALIDITY_ULTIMATE:
  2413. htmlStr += i18n( "The signature is valid and the key is "
  2414. "ultimately trusted." );
  2415. break;
  2416. default:
  2417. htmlStr += i18n( "The signature is valid, but the key is "
  2418. "untrusted." );
  2419. }
  2420. frame = "</td></tr>"
  2421. "<tr class=\"" + block.signClass + "B\"><td>";
  2422. htmlStr += endVerboseSigstatHeader( block ) + frame;
  2423. simpleHtmlStr += frame;
  2424. }
  2425. else
  2426. {
  2427. block.signClass = "signErr";
  2428. TQString frame = "<table cellspacing=\"1\" "+cellPadding+" "
  2429. "class=\"" + block.signClass + "\">"
  2430. "<tr class=\"" + block.signClass + "H\"><td dir=\"" + dir + "\">";
  2431. htmlStr += frame + beginVerboseSigstatHeader();
  2432. simpleHtmlStr += frame;
  2433. simpleHtmlStr += writeSimpleSigstatHeader( block );
  2434. if( !block.keyId.isEmpty() )
  2435. htmlStr += i18n( "Message was signed by %2 (Key ID: %1)." )
  2436. .arg( keyWithWithoutURL,
  2437. signer );
  2438. else
  2439. htmlStr += i18n( "Message was signed by %1." ).arg( signer );
  2440. htmlStr += "<br />";
  2441. htmlStr += i18n("Warning: The signature is bad.");
  2442. frame = "</td></tr>"
  2443. "<tr class=\"" + block.signClass + "B\"><td>";
  2444. htmlStr += endVerboseSigstatHeader( block ) + frame;
  2445. simpleHtmlStr += frame;
  2446. }
  2447. }
  2448. }
  2449. }
  2450. if ( mReader->showSignatureDetails() )
  2451. return htmlStr;
  2452. return simpleHtmlStr;
  2453. }
  2454. TQString ObjectTreeParser::writeSigstatFooter( PartMetaData& block )
  2455. {
  2456. TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
  2457. TQString htmlStr;
  2458. if (block.isSigned) {
  2459. htmlStr += "</td></tr><tr class=\"" + block.signClass + "H\">";
  2460. htmlStr += "<td dir=\"" + dir + "\">" +
  2461. i18n( "End of signed message" ) +
  2462. "</td></tr></table>";
  2463. }
  2464. if (block.isEncrypted) {
  2465. htmlStr += "</td></tr><tr class=\"encrH\"><td dir=\"" + dir + "\">" +
  2466. i18n( "End of encrypted message" ) +
  2467. "</td></tr></table>";
  2468. }
  2469. if( block.isEncapsulatedRfc822Message )
  2470. {
  2471. htmlStr += "</td></tr><tr class=\"rfc822H\"><td dir=\"" + dir + "\">" +
  2472. i18n( "End of encapsulated message" ) +
  2473. "</td></tr></table>";
  2474. }
  2475. return htmlStr;
  2476. }
  2477. //-----------------------------------------------------------------------------
  2478. void ObjectTreeParser::writeAttachmentMarkHeader( partNode *node )
  2479. {
  2480. if ( !mReader )
  2481. return;
  2482. htmlWriter()->queue( TQString( "<div id=\"attachmentDiv%1\">\n" ).arg( node->nodeId() ) );
  2483. }
  2484. //-----------------------------------------------------------------------------
  2485. void ObjectTreeParser::writeAttachmentMarkFooter()
  2486. {
  2487. if ( !mReader )
  2488. return;
  2489. htmlWriter()->queue( TQString( "</div>" ) );
  2490. }
  2491. //-----------------------------------------------------------------------------
  2492. void ObjectTreeParser::writeBodyStr( const TQCString& aStr, const TQTextCodec *aCodec,
  2493. const TQString& fromAddress )
  2494. {
  2495. KMMsgSignatureState dummy1;
  2496. KMMsgEncryptionState dummy2;
  2497. writeBodyStr( aStr, aCodec, fromAddress, dummy1, dummy2, false );
  2498. }
  2499. //-----------------------------------------------------------------------------
  2500. void ObjectTreeParser::writeBodyStr( const TQCString& aStr, const TQTextCodec *aCodec,
  2501. const TQString& fromAddress,
  2502. KMMsgSignatureState& inlineSignatureState,
  2503. KMMsgEncryptionState& inlineEncryptionState,
  2504. bool decorate )
  2505. {
  2506. bool goodSignature = false;
  2507. Kpgp::Module* pgp = Kpgp::Module::getKpgp();
  2508. assert(pgp != 0);
  2509. bool isPgpMessage = false; // true if the message contains at least one
  2510. // PGP MESSAGE or one PGP SIGNED MESSAGE block
  2511. TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
  2512. TQString headerStr = TQString("<div dir=\"%1\">").arg(dir);
  2513. inlineSignatureState = KMMsgNotSigned;
  2514. inlineEncryptionState = KMMsgNotEncrypted;
  2515. TQPtrList<Kpgp::Block> pgpBlocks;
  2516. TQStrList nonPgpBlocks;
  2517. if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) )
  2518. {
  2519. bool isEncrypted = false, isSigned = false;
  2520. bool fullySignedOrEncrypted = true;
  2521. bool firstNonPgpBlock = true;
  2522. bool couldDecrypt = false;
  2523. TQString signer;
  2524. TQCString keyId;
  2525. TQString decryptionError;
  2526. Kpgp::Validity keyTrust = Kpgp::KPGP_VALIDITY_FULL;
  2527. TQPtrListIterator<Kpgp::Block> pbit( pgpBlocks );
  2528. TQStrListIterator npbit( nonPgpBlocks );
  2529. TQString htmlStr;
  2530. for( ; *pbit != 0; ++pbit, ++npbit )
  2531. {
  2532. // insert the next Non-OpenPGP block
  2533. TQCString str( *npbit );
  2534. if( !str.isEmpty() ) {
  2535. htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
  2536. //kdDebug( 5006 ) << "Non-empty Non-OpenPGP block found: '" << str
  2537. // << "'" << endl;
  2538. // treat messages with empty lines before the first clearsigned
  2539. // block as fully signed/encrypted
  2540. if( firstNonPgpBlock ) {
  2541. // check whether str only consists of \n
  2542. for( TQCString::ConstIterator c = str.begin(); *c; ++c ) {
  2543. if( *c != '\n' ) {
  2544. fullySignedOrEncrypted = false;
  2545. break;
  2546. }
  2547. }
  2548. }
  2549. else {
  2550. fullySignedOrEncrypted = false;
  2551. }
  2552. }
  2553. firstNonPgpBlock = false;
  2554. //htmlStr += "<br>";
  2555. Kpgp::Block* block = *pbit;
  2556. if( ( block->type() == Kpgp::PgpMessageBlock &&
  2557. // ### Workaround for bug 56693
  2558. !kmkernel->contextMenuShown() ) ||
  2559. ( block->type() == Kpgp::ClearsignedBlock ) )
  2560. {
  2561. isPgpMessage = true;
  2562. if( block->type() == Kpgp::PgpMessageBlock )
  2563. {
  2564. if ( mReader )
  2565. emit mReader->noDrag();
  2566. // try to decrypt this OpenPGP block
  2567. couldDecrypt = block->decrypt();
  2568. isEncrypted = block->isEncrypted();
  2569. if (!couldDecrypt) {
  2570. decryptionError = pgp->lastErrorMsg();
  2571. }
  2572. }
  2573. else
  2574. {
  2575. // try to verify this OpenPGP block
  2576. block->verify();
  2577. }
  2578. isSigned = block->isSigned();
  2579. if( isSigned )
  2580. {
  2581. keyId = block->signatureKeyId();
  2582. signer = block->signatureUserId();
  2583. if( !signer.isEmpty() )
  2584. {
  2585. goodSignature = block->goodSignature();
  2586. if( !keyId.isEmpty() ) {
  2587. keyTrust = pgp->keyTrust( keyId );
  2588. Kpgp::Key* key = pgp->publicKey( keyId );
  2589. if ( key ) {
  2590. // Use the user ID from the key because this one
  2591. // is charset safe.
  2592. signer = key->primaryUserID();
  2593. }
  2594. }
  2595. else
  2596. // This is needed for the PGP 6 support because PGP 6 doesn't
  2597. // print the key id of the signing key if the key is known.
  2598. keyTrust = pgp->keyTrust( signer );
  2599. }
  2600. }
  2601. if( isSigned )
  2602. inlineSignatureState = KMMsgPartiallySigned;
  2603. if( isEncrypted )
  2604. inlineEncryptionState = KMMsgPartiallyEncrypted;
  2605. PartMetaData messagePart;
  2606. messagePart.isSigned = isSigned;
  2607. messagePart.technicalProblem = false;
  2608. messagePart.isGoodSignature = goodSignature;
  2609. messagePart.isEncrypted = isEncrypted;
  2610. messagePart.isDecryptable = couldDecrypt;
  2611. messagePart.decryptionError = decryptionError;
  2612. messagePart.signer = signer;
  2613. messagePart.keyId = keyId;
  2614. messagePart.keyTrust = keyTrust;
  2615. htmlStr += writeSigstatHeader( messagePart, 0, fromAddress );
  2616. htmlStr += quotedHTML( aCodec->toUnicode( block->text() ), decorate );
  2617. htmlStr += writeSigstatFooter( messagePart );
  2618. }
  2619. else // block is neither message block nor clearsigned block
  2620. htmlStr += quotedHTML( aCodec->toUnicode( block->text() ),
  2621. decorate );
  2622. }
  2623. // add the last Non-OpenPGP block
  2624. TQCString str( nonPgpBlocks.last() );
  2625. if( !str.isEmpty() ) {
  2626. htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
  2627. // Even if the trailing Non-OpenPGP block isn't empty we still
  2628. // consider the message part fully signed/encrypted because else
  2629. // all inline signed mailing list messages would only be partially
  2630. // signed because of the footer which is often added by the mailing
  2631. // list software. IK, 2003-02-15
  2632. }
  2633. if( fullySignedOrEncrypted ) {
  2634. if( inlineSignatureState == KMMsgPartiallySigned )
  2635. inlineSignatureState = KMMsgFullySigned;
  2636. if( inlineEncryptionState == KMMsgPartiallyEncrypted )
  2637. inlineEncryptionState = KMMsgFullyEncrypted;
  2638. }
  2639. htmlWriter()->queue( htmlStr );
  2640. }
  2641. else
  2642. htmlWriter()->queue( quotedHTML( aCodec->toUnicode( aStr ), decorate ) );
  2643. }
  2644. TQString ObjectTreeParser::quotedHTML( const TQString& s, bool decorate )
  2645. {
  2646. assert( mReader );
  2647. assert( cssHelper() );
  2648. int convertFlags = LinkLocator::PreserveSpaces;
  2649. if ( decorate && GlobalSettings::self()->showEmoticons() ) {
  2650. convertFlags |= LinkLocator::ReplaceSmileys;
  2651. }
  2652. TQString htmlStr;
  2653. const TQString normalStartTag = cssHelper()->nonQuotedFontTag();
  2654. TQString quoteFontTag[3];
  2655. TQString deepQuoteFontTag[3];
  2656. for ( int i = 0 ; i < 3 ; ++i ) {
  2657. quoteFontTag[i] = cssHelper()->quoteFontTag( i );
  2658. deepQuoteFontTag[i] = cssHelper()->quoteFontTag( i+3 );
  2659. }
  2660. const TQString normalEndTag = "</div>";
  2661. const TQString quoteEnd = "</div>";
  2662. unsigned int pos, beg;
  2663. const unsigned int length = s.length();
  2664. // skip leading empty lines
  2665. for ( pos = 0; pos < length && s.at(pos) <= TQChar(' '); pos++ ) { ; }
  2666. while (pos > 0 && (s[pos-1] == ' ' || s[pos-1] == '\t')) pos--;
  2667. beg = pos;
  2668. int currQuoteLevel = -2; // -2 == no previous lines
  2669. bool curHidden = false; // no hide any block
  2670. while (beg<length)
  2671. {
  2672. TQString line;
  2673. /* search next occurrence of '\n' */
  2674. pos = s.find('\n', beg, FALSE);
  2675. if (pos == (unsigned int)(-1))
  2676. pos = length;
  2677. line = s.mid(beg,pos-beg);
  2678. beg = pos+1;
  2679. /* calculate line's current quoting depth */
  2680. int actQuoteLevel = -1;
  2681. if ( GlobalSettings::self()->showExpandQuotesMark() )
  2682. {
  2683. // Cache Icons
  2684. if ( mCollapseIcon.isEmpty() ) {
  2685. mCollapseIcon= LinkLocator::pngToDataUrl(
  2686. TDEGlobal::instance()->iconLoader()->iconPath( "quotecollapse",0 ));
  2687. }
  2688. if ( mExpandIcon.isEmpty() )
  2689. mExpandIcon= LinkLocator::pngToDataUrl(
  2690. TDEGlobal::instance()->iconLoader()->iconPath( "quoteexpand",0 ));
  2691. }
  2692. for (unsigned int p=0; p<line.length(); p++) {
  2693. switch (line[p].latin1()) {
  2694. case '>':
  2695. case '|':
  2696. actQuoteLevel++;
  2697. break;
  2698. case ' ': // spaces and tabs are allowed between the quote markers
  2699. case '\t':
  2700. case '\r':
  2701. break;
  2702. default: // stop quoting depth calculation
  2703. p = line.length();
  2704. break;
  2705. }
  2706. } /* for() */
  2707. bool actHidden = false;
  2708. TQString textExpand;
  2709. // This quoted line needs be hiden
  2710. if (GlobalSettings::self()->showExpandQuotesMark() && mReader->mLevelQuote >= 0
  2711. && mReader->mLevelQuote <= ( actQuoteLevel ) )
  2712. actHidden = true;
  2713. if ( actQuoteLevel != currQuoteLevel ) {
  2714. /* finish last quotelevel */
  2715. if (currQuoteLevel == -1)
  2716. htmlStr.append( normalEndTag );
  2717. else if ( currQuoteLevel >= 0 && !curHidden )
  2718. htmlStr.append( quoteEnd );
  2719. /* start new quotelevel */
  2720. if (actQuoteLevel == -1)
  2721. htmlStr += normalStartTag;
  2722. else
  2723. {
  2724. if ( GlobalSettings::self()->showExpandQuotesMark() )
  2725. {
  2726. if ( actHidden )
  2727. {
  2728. //only show the QuoteMark when is the first line of the level hidden
  2729. if ( !curHidden )
  2730. {
  2731. //Expand all quotes
  2732. htmlStr += "<div class=\"quotelevelmark\" >" ;
  2733. htmlStr += TQString( "<a href=\"kmail:levelquote?%1 \">"
  2734. "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
  2735. .arg(-1)
  2736. .arg( mExpandIcon );
  2737. htmlStr += "</div><br/>";
  2738. htmlStr += quoteEnd;
  2739. }
  2740. }else {
  2741. htmlStr += "<div class=\"quotelevelmark\" >" ;
  2742. htmlStr += TQString( "<a href=\"kmail:levelquote?%1 \">"
  2743. "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
  2744. .arg(actQuoteLevel)
  2745. .arg( mCollapseIcon);
  2746. htmlStr += "</div>";
  2747. if ( actQuoteLevel < 3 )
  2748. htmlStr += quoteFontTag[actQuoteLevel];
  2749. else
  2750. htmlStr += deepQuoteFontTag[actQuoteLevel%3];
  2751. }
  2752. } else
  2753. if ( actQuoteLevel < 3 )
  2754. htmlStr += quoteFontTag[actQuoteLevel];
  2755. else
  2756. htmlStr += deepQuoteFontTag[actQuoteLevel%3];
  2757. }
  2758. currQuoteLevel = actQuoteLevel;
  2759. }
  2760. curHidden = actHidden;
  2761. if ( !actHidden )
  2762. {
  2763. // don't write empty <div ...></div> blocks (they have zero height)
  2764. // ignore ^M DOS linebreaks
  2765. if( !line.replace('\015', "").isEmpty() )
  2766. {
  2767. htmlStr +=TQString( "<div dir=\"%1\">" ).arg( line.isRightToLeft() ? "rtl":"ltr" );
  2768. htmlStr += LinkLocator::convertToHtml( line, convertFlags );
  2769. htmlStr += TQString( "</div>" );
  2770. }
  2771. else
  2772. htmlStr += "<br>";
  2773. }
  2774. } /* while() */
  2775. /* really finish the last quotelevel */
  2776. if (currQuoteLevel == -1)
  2777. htmlStr.append( normalEndTag );
  2778. else
  2779. htmlStr.append( quoteEnd );
  2780. return htmlStr;
  2781. }
  2782. const TQTextCodec * ObjectTreeParser::codecFor( partNode * node ) const {
  2783. assert( node );
  2784. if ( mReader && mReader->overrideCodec() )
  2785. return mReader->overrideCodec();
  2786. return node->msgPart().codec();
  2787. }
  2788. #ifdef MARCS_DEBUG
  2789. void ObjectTreeParser::dumpToFile( const char * filename, const char * start,
  2790. size_t len ) {
  2791. assert( filename );
  2792. TQFile f( filename );
  2793. if ( f.open( IO_WriteOnly ) ) {
  2794. if ( start ) {
  2795. TQDataStream ds( &f );
  2796. ds.writeRawBytes( start, len );
  2797. }
  2798. f.close(); // If data is 0 we just create a zero length file.
  2799. }
  2800. }
  2801. #endif // !NDEBUG
  2802. } // namespace KMail