// kmreaderwin.cpp // Author: Markus Wuebben #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // khtml headers #include #include // So that we can get rid of the frames #include #include #include #include #include #include "kmversion.h" #include "kmglobal.h" #include "kmmainwin.h" #include "kbusyptr.h" #include "kfileio.h" #include "kmfolder.h" #include "kmmessage.h" #include "kmmsgpart.h" #include "kmmsgpartdlg.h" #include "kmreaderwin.h" #include "partNode.h" #include "linklocator.h" // for the MIME structure viewer (khz): #include "kmmimeparttree.h" // for selection //#include //#include //#include // X headers... #undef Never #undef Always //--- Sven's save attachments to /tmp start --- #include #include #include // for access and getpid #include //--- Sven's save attachments to /tmp end --- // for the click on attachment stuff (dnaber): #include // Do the tmp stuff correctly - thanks to Harri Porten for // reminding me (sven) #include "vcard.h" #include "kmdisplayvcard.h" #include #include #ifdef HAVE_PATHS_H #include #endif // INTERIM SOLUTION variable // // TO BE REMOVED ONCE AUTOMATIC PLUG-IN DETECTION IS FULLY WORKING // static CryptPlugWrapper* useThisCryptPlug = 0; // this function will be replaced once KMime is alive (khz, 04.05.2001) void KMReaderWin::insertAndParseNewChildNode( partNode& node, const char* content, const char* cntDesc ) { DwBodyPart* myBody = new DwBodyPart( DwString( content ), node.dwPart() ); myBody->Parse(); if( myBody->hasHeaders() ) { DwText& desc = myBody->Headers().ContentDescription(); desc.FromString( cntDesc ); desc.SetModified(); //desc.Assemble(); myBody->Headers().Parse(); } node.setFirstChild(new partNode(false, myBody))->buildObjectTree( false ); if( node.mimePartTreeItem() ) { kdDebug(5006) << "\n -----> Inserting items into MimePartTree\n" << endl; node.mChild->fillMimePartTree( node.mimePartTreeItem(), 0, "", // cntDesc, "", // mainCntTypeStr, "", // cntEnc, "" ); // cntSize ); kdDebug(5006) << "\n <----- Finished inserting items into MimePartTree\n" << endl; } else { kdDebug(5006) << "\n ------ Sorry, node.mimePartTreeItem() returns ZERO so" << "\n we cannot insert new lines into MimePartTree. :-(\n" << endl; } parseObjectTree( node.mChild );// showOneMimePart, keepEncryptions, includeSignatures ); } // this function will be replaced once KMime is alive (khz, 29.11.2001) void KMReaderWin::parseObjectTree( partNode* node, bool showOneMimePart, bool keepEncryptions, bool includeSignatures ) { kdDebug(5006) << "\n**\n** KMReaderWin::parseObjectTree( " << (node ? "node OK, " : "no node, ") << "showOneMimePart: " << (showOneMimePart ? "TRUE" : "FALSE") << " ) **\n**" << endl; if( showOneMimePart ) { // clear the viewer mViewer->view()->setUpdatesEnabled( false ); mViewer->view()->viewport()->setUpdatesEnabled( false ); static_cast(mViewer->widget())->ensureVisible(0,0); if (mHtmlTimer.isActive()) { mHtmlTimer.stop(); mViewer->end(); } mHtmlQueue.clear(); mColorBar->hide(); // start the new viewer content mViewer->begin( KURL( "file:/" ) ); mViewer->write("write(" background=\"file://" + mBackingPixmapStr + "\""); mViewer->write(">"); } if(node && (showOneMimePart || (mShowCompleteMessage && !node->mRoot ))) { if( showOneMimePart ) { // set this node and all it's children and their children to 'not yet processed' node->mWasProcessed = false; if( node->mChild ) node->mChild->setProcessed( false ); } else // set this node and all it's siblings and all it's childrens to 'not yet processed' node->setProcessed( false ); } bool isImage = false; bool isInlineSigned = false; bool isInlineEncrypted = false; if( node ) { partNode* curNode = node; /* // process decrypting (or signature verifying, resp.) // via CRYPTPLUG if( mCryptPlugList && !curNode->mWasProcessed ) { partNode* signedDataPart = 0; partNode* signaturePart = 0; partNode* versionPart = 0; partNode* encryptedDataPart = 0; CryptPlugWrapper* plugForSignatureVerifying = findPlugForSignatureVerifying( signedDataPart, signaturePart ); CryptPlugWrapper* plugForDecrypting = findPlugForDecrypting( versionPart, encryptedDataPart ); if( plugForSignatureVerifying ) { // Set the signature node to done to prevent it from being processed // by parseObjectTree( data ) called from writeOpaqueOrMultipartSignedData(). signaturePart->setProcessed( true ); writeOpaqueOrMultipartSignedData( *plugForSignatureVerifying, *signedDataPart, *signaturePart ); signedDataPart->setProcessed( true ); bDone = true; } if( plugForDecrypting ) { QCString decryptedData; if( okDecryptMIME( *plugForDecrypting, versionPart, *encryptedDataPart, decryptedData ) ) { DwBodyPart* myBody = new DwBodyPart( DwString( decryptedData ), encryptedDataPart->dwPart() ); myBody->Parse(); partNode myBodyNode( true, myBody ); myBodyNode.buildObjectTree( false ); parseObjectTree( &myBodyNode ); } else { if( versionPart ) parseObjectTree( versionPart ); writeHTMLStr("
"); writeHTMLStr(mCodec->toUnicode( decryptedData )); } if( versionPart ) versionPart->setProcessed( true ); encryptedDataPart->setProcessed( true ); bDone = true; } } */ // process all mime parts that are not covered by one of the CRYPTPLUGs if( !curNode->mWasProcessed ) { bool bDone = false; switch( curNode->type() ){ case DwMime::kTypeText: { kdDebug(5006) << "* text *" << endl; switch( curNode->subType() ){ case DwMime::kSubtypeHtml: { kdDebug(5006) << "html, attachmentstyle = " << mAttachmentStyle << endl; QCString cstr( curNode->msgPart().bodyDecoded() ); if( mAttachmentStyle != IconicAttmnt || !curNode->isAttachment() ) { if( htmlMail() ) { // ---Sven's strip and from end of attachment start- // We must fo this, or else we will see only 1st inlined html // attachment. It is IMHO enough to search only for and // put \0 there. int i = cstr.findRev("", -1, false); //case insensitive if( 0 <= i ) cstr.truncate(i); else // just in case - search for { i = cstr.findRev("", -1, false); //case insensitive if( 0 <= i ) cstr.truncate(i); } // ---Sven's strip and from end of attachment end- } else { writeHTMLStr(QString("
") .arg( cCBhtml.name() ) ); writeHTMLStr(i18n("Note: This is a HTML message. For " "security reasons, only the raw HTML code " "is shown. If you trust the sender of this " "message then you can activate formatted " //"HTML display by enabling Prefer HTML " //"to Plain Text in the Folder " //"menu.")); "HTML display for this message by clicking " "here.")); writeHTMLStr( "


"); } writeHTMLStr(mCodec->toUnicode( htmlMail() ? cstr : KMMessage::html2source( cstr ))); bDone = true; } break; } case DwMime::kSubtypeXVCard: { kdDebug(5006) << "v-card" << endl; // do nothing: X-VCard is handled in parseMsg(KMMessage* aMsg) // _before_ calling parseObjectTree() bDone = true; } break; // Every 'Text' type that is not 'Html' or 'V-Card' // is processed like 'Plain' text: case DwMime::kSubtypeRichtext: kdDebug(5006) << "rich text" << endl; case DwMime::kSubtypeEnriched: kdDebug(5006) << "enriched " << endl; case DwMime::kSubtypePlain: kdDebug(5006) << "plain " << endl; default: { kdDebug(5006) << "default " << endl; if( mAttachmentStyle != IconicAttmnt || !curNode->isAttachment() ) { writeBodyStr(curNode->msgPart().bodyDecoded().data(), mCodec, &isInlineSigned, &isInlineEncrypted); bDone = true; } } break; } } break; case DwMime::kTypeMultipart: { kdDebug(5006) << "* multipart *" << endl; switch( curNode->subType() ){ case DwMime::kSubtypeMixed: { kdDebug(5006) << "mixed" << endl; if( curNode->mChild ) parseObjectTree( curNode->mChild, showOneMimePart, keepEncryptions, includeSignatures ); bDone = true; } break; case DwMime::kSubtypeAlternative: { kdDebug(5006) << "alternative" << endl; if( curNode->mChild ) { partNode* dataHtml = curNode->mChild->findType( DwMime::kTypeText, DwMime::kSubtypeHtml, false, true ); partNode* dataPlain = curNode->mChild->findType( DwMime::kTypeText, DwMime::kSubtypePlain, false, true ); if( htmlMail() && dataHtml ) { if( dataPlain ) dataPlain->mWasProcessed = true; parseObjectTree( dataHtml, showOneMimePart, keepEncryptions, includeSignatures ); } else if( !htmlMail() && dataPlain ) { if( dataHtml ) dataHtml->mWasProcessed = true; parseObjectTree( dataPlain, showOneMimePart, keepEncryptions, includeSignatures ); } else parseObjectTree( curNode->mChild, showOneMimePart, keepEncryptions, includeSignatures ); bDone = true; } } break; case DwMime::kSubtypeDigest: { kdDebug(5006) << "digest" << endl; } break; case DwMime::kSubtypeParallel: { kdDebug(5006) << "parallel" << endl; } break; case DwMime::kSubtypeSigned: { kdDebug(5006) << "signed" << endl; CryptPlugWrapper* oldUseThisCryptPlug = useThisCryptPlug; /* ATTENTION: We currently do _not_ support "multipart/signed" with _multiple_ signatures. Instead we expect to find two objects: one object containing the signed data and another object containing exactly one signature, this is determined by looking for an "application/pgp-signature" object. */ if( !curNode->mChild ) kdDebug(5006) << " SORRY, signed has NO children" << endl; else { kdDebug(5006) << " signed has children" << endl; /* ATTENTION: This code is to be replaced by the new 'auto-detect' feature. -------------------------------------- */ partNode* data = 0; partNode* sign; sign = curNode->mChild->findType( DwMime::kTypeApplication, DwMime::kSubtypePgpSignature, false, true ); if( sign ) { kdDebug(5006) << " OpenPGP signature found" << endl; data = curNode->mChild->findTypeNot( DwMime::kTypeApplication, DwMime::kSubtypePgpSignature, false, true ); // INTERIM SOLUTION // // TO BE REMOVED ONCE AUTOMATIC PLUG-IN DETECTION IS FULLY WORKING // if(data){ CryptPlugWrapper* current; QPtrListIterator it( *mCryptPlugList ); while( ( current = it.current() ) ) { ++it; if( 0 <= current->libName().find( "openpgp", 0, false ) ) { useThisCryptPlug = current; //kdDebug(5006) << "\n\n\ngefunden1\n\n\n" << endl; break; } } } } else { sign = curNode->mChild->findType( DwMime::kTypeApplication, DwMime::kSubtypePkcs7Signature, false, true ); if( sign ) { kdDebug(5006) << " S/MIME signature found" << endl; data = curNode->mChild->findTypeNot( DwMime::kTypeApplication, DwMime::kSubtypePkcs7Signature, false, true ); // INTERIM SOLUTION // // TO BE REMOVED ONCE AUTOMATIC PLUG-IN DETECTION IS FULLY WORKING // if(data){ CryptPlugWrapper* current; QPtrListIterator it( *mCryptPlugList ); while( ( current = it.current() ) ) { ++it; if( 0 <= current->libName().find( "smime", 0, false ) ) { useThisCryptPlug = current; //kdDebug(5006) << "\n\n\ngefunden2\n\n\n" << endl; break; } } } } else { kdDebug(5006) << " Sorry, *neither* OpenPGP *nor* S/MIME signature could be found!\n\n" << endl; } } /* --------------------------------------------------------------------------------------------------------------- */ if( sign && data ) { kdDebug(5006) << " signed has data + signature" << endl; curNode->setSigned( true ); } if( !includeSignatures ) { if( !data ) data = curNode->mChild; QCString cstr( data->msgPart().bodyDecoded() ); writeBodyStr(cstr, mCodec, &isInlineSigned, &isInlineEncrypted); bDone = true; } else if( sign && data ) { // Set the signature node to done to prevent it from being processed // by parseObjectTree( data ) called from writeOpaqueOrMultipartSignedData(). sign->mWasProcessed = true; writeOpaqueOrMultipartSignedData( data, *sign ); bDone = true; } } useThisCryptPlug = oldUseThisCryptPlug; } break; case DwMime::kSubtypeEncrypted: { kdDebug(5006) << "encrypted" << endl; CryptPlugWrapper* oldUseThisCryptPlug = useThisCryptPlug; if( keepEncryptions ) { curNode->setEncrypted( true ); QCString cstr( curNode->msgPart().bodyDecoded() ); writeBodyStr(cstr, mCodec, &isInlineSigned, &isInlineEncrypted); bDone = true; } else if( curNode->mChild ) { /* ATTENTION: This code is to be replaced by the new 'auto-detect' feature. -------------------------------------- */ partNode* data = curNode->mChild->findType( DwMime::kTypeApplication, DwMime::kSubtypeOctetStream, false, true ); // INTERIM SOLUTION // // TO BE REMOVED ONCE AUTOMATIC PLUG-IN DETECTION IS FULLY WORKING // if(data){ CryptPlugWrapper* current; QPtrListIterator it( *mCryptPlugList ); while( ( current = it.current() ) ) { ++it; if( 0 <= current->libName().find( "openpgp", 0, false ) ) { useThisCryptPlug = current; //kdDebug(5006) << "\n\n\ngefunden3\n\n\n" << endl; break; } } } if( !data ) { data = curNode->mChild->findType( DwMime::kTypeApplication, DwMime::kSubtypePkcs7Mime, false, true ); // INTERIM SOLUTION // // TO BE REMOVED ONCE AUTOMATIC PLUG-IN DETECTION IS FULLY WORKING // if(data){ CryptPlugWrapper* current; QPtrListIterator it( *mCryptPlugList ); while( ( current = it.current() ) ) { ++it; if( 0 <= current->libName().find( "smime", 0, false ) ) { //kdDebug(5006) << "\n\n\ngefunden4\n\n\n" << endl; useThisCryptPlug = current; break; } } } } /* --------------------------------------------------------------------------------------------------------------- */ if( data ) { curNode->setEncrypted( true ); QCString decryptedData; if( okDecryptMIME( *data, decryptedData ) ) { DwBodyPart* myBody = new DwBodyPart(DwString( decryptedData ), data->dwPart()); myBody->Parse(); partNode myBodyNode( true, myBody ); myBodyNode.buildObjectTree( false ); parseObjectTree( &myBodyNode, showOneMimePart, keepEncryptions, includeSignatures ); } else { writeHTMLStr(mCodec->toUnicode( decryptedData )); } data->mWasProcessed = true; // Set the data node to done to prevent it from being processed bDone = true; } } useThisCryptPlug = oldUseThisCryptPlug; } break; default : { kdDebug(5006) << "( unknown subtype )" << endl; } break; } // Multipart object not processed yet? Just parse the children! if( !bDone ){ if( curNode && curNode->mChild ) { parseObjectTree( curNode->mChild, showOneMimePart, keepEncryptions, includeSignatures ); bDone = true; } } } break; case DwMime::kTypeMessage: { kdDebug(5006) << "* message *" << endl; switch( curNode->subType() ){ case DwMime::kSubtypeRfc822: { kdDebug(5006) << "RfC 822" << endl; } break; } } break; /* case DwMime::kType..WhatTheHellIsThis: { switch( curNode->subType() ){ case DwMime::kSubtypePartial: { } break; case DwMime::kSubtypeExternalBody: { } break; } } break; */ case DwMime::kTypeApplication: { kdDebug(5006) << "* application *" << endl; switch( curNode->subType() ){ case DwMime::kSubtypePostscript: { kdDebug(5006) << "postscript" << endl; } break; case DwMime::kSubtypeOctetStream: { kdDebug(5006) << "octet stream" << endl; CryptPlugWrapper* oldUseThisCryptPlug = useThisCryptPlug; if( curNode->mRoot && DwMime::kTypeMultipart == curNode->mRoot->type() && DwMime::kSubtypeEncrypted == curNode->mRoot->subType() ) { curNode->setEncrypted( true ); if( keepEncryptions ) { QCString cstr( curNode->msgPart().bodyDecoded() ); writeBodyStr(cstr, mCodec, &isInlineSigned, &isInlineEncrypted); bDone = true; } else { // INTERIM SOLUTION // // TO BE REMOVED ONCE AUTOMATIC PLUG-IN DETECTION IS FULLY WORKING // CryptPlugWrapper* current; QPtrListIterator it( *mCryptPlugList ); while( ( current = it.current() ) ) { ++it; if( 0 <= current->libName().find( "openpgp", 0, false ) ) { useThisCryptPlug = current; //kdDebug(5006) << "\n\n\ngefunden5\n\n\n" << endl; break; } } QCString decryptedData; if( okDecryptMIME( *curNode, decryptedData ) ) { DwBodyPart* myBody = new DwBodyPart(DwString( decryptedData ), curNode->dwPart()); myBody->Parse(); partNode myBodyNode( true, myBody ); myBodyNode.buildObjectTree( false ); parseObjectTree( &myBodyNode, showOneMimePart, keepEncryptions, includeSignatures ); } else { writeHTMLStr(mCodec->toUnicode( decryptedData )); } bDone = true; } } useThisCryptPlug = oldUseThisCryptPlug; } break; case DwMime::kSubtypePgpEncrypted: { kdDebug(5006) << "pgp encrypted" << endl; } break; case DwMime::kSubtypePgpSignature: { kdDebug(5006) << "pgp signed" << endl; } break; case DwMime::kSubtypePkcs7Mime: { kdDebug(5006) << "pkcs7 mime" << endl; if( curNode->dwPart() && curNode->dwPart()->hasHeaders() ) { CryptPlugWrapper* oldUseThisCryptPlug = useThisCryptPlug; /* ATTENTION: This code is to be replaced by the new 'auto-detect' feature. -------------------------------------- */ // INTERIM SOLUTION // // TO BE REMOVED ONCE AUTOMATIC PLUG-IN DETECTION IS FULLY WORKING // CryptPlugWrapper* current; QPtrListIterator it( *mCryptPlugList ); while( ( current = it.current() ) ) { ++it; if( 0 <= current->libName().find( "smime", 0, false ) ) { useThisCryptPlug = current; //kdDebug(5006) << "\n\n\ngefunden6\n\n\n" << endl; break; } } DwHeaders& headers( curNode->dwPart()->Headers() ); QCString ctypStr( headers.ContentType().AsString().c_str() ); bool isSigned = 0 <= ctypStr.find("smime-type=signed-data", 0, false); bool isEncrypted = 0 <= ctypStr.find("smime-type=enveloped-data", 0, false); // we call signature verification // if we either *know* that it is signed mail or // if there is *neither* signed *nor* encrypted parameter if( !isEncrypted ) { if( isSigned ) kdDebug(5006) << "pkcs7 mime == S/MIME TYPE: opaque signed data" << endl; else kdDebug(5006) << "pkcs7 mime - type unknown - opaque signed data ?" << endl; if( writeOpaqueOrMultipartSignedData( 0, *curNode ) && !isSigned ) { kdDebug(5006) << "pkcs7 mime - signature found - opaque signed data !" << endl; isSigned = true; } if( isSigned ) curNode->setSigned( true ); } // we call decryption function // if it turned out that this is not signed mail if( !isSigned ) { if( isEncrypted ) kdDebug(5006) << "pkcs7 mime == S/MIME TYPE: enveloped (encrypted) data" << endl; else kdDebug(5006) << "pkcs7 mime - type unknown - enveloped (encrypted) data ?" << endl; if( curNode->mChild ) { kdDebug(5006) << "\n-----> Calling parseObjectTree( curNode->mChild )\n" << endl; parseObjectTree( curNode->mChild ); kdDebug(5006) << "\n<----- Returning from parseObjectTree( curNode->mChild )\n" << endl; } else { kdDebug(5006) << "\n-----> Initially processing encrypted data\n" << endl; QCString decryptedData; if( okDecryptMIME( *curNode, decryptedData ) ) { isEncrypted = true; insertAndParseNewChildNode( *curNode, &*decryptedData, "encrypted data" ); } else { writeHTMLStr(mCodec->toUnicode( decryptedData )); } kdDebug(5006) << "\n<----- Returning from initially processing encrypted data\n" << endl; } if( isEncrypted ) curNode->setEncrypted( true ); } if( isSigned || isEncrypted ) bDone = true; useThisCryptPlug = oldUseThisCryptPlug; } } break; } } break; case DwMime::kTypeImage: { kdDebug(5006) << "* image *" << endl; switch( curNode->subType() ){ case DwMime::kSubtypeJpeg: { kdDebug(5006) << "JPEG" << endl; } break; case DwMime::kSubtypeGif: { kdDebug(5006) << "GIF" << endl; } break; } isImage = true; } break; case DwMime::kTypeAudio: { kdDebug(5006) << "* audio *" << endl; switch( curNode->subType() ){ case DwMime::kSubtypeBasic: { kdDebug(5006) << "basic" << endl; } break; } // We allways show audio as icon. writePartIcon(&curNode->msgPart(), curNode->nodeId()); bDone = true; } break; case DwMime::kTypeVideo: { kdDebug(5006) << "* video *" << endl; switch( curNode->subType() ){ case DwMime::kSubtypeMpeg: { kdDebug(5006) << "mpeg" << endl; } break; } } break; case DwMime::kTypeModel: { kdDebug(5006) << "* model *" << endl; // what the hell is "Content-Type: model/.." ? } break; } if( !bDone ) { bool asIcon = true; switch (mAttachmentStyle) { case IconicAttmnt: asIcon = TRUE; break; case InlineAttmnt: asIcon = FALSE; break; case SmartAttmnt: asIcon = ( curNode->msgPart().contentDisposition().find("inline") < 0 ); } if( asIcon ) { if( isImage ) inlineImage = true; writePartIcon(&curNode->msgPart(), curNode->nodeId()); if( isImage ) inlineImage = false; } else { QCString cstr( curNode->msgPart().bodyDecoded() ); writeBodyStr(cstr, mCodec, &isInlineSigned, &isInlineEncrypted); } } curNode->mWasProcessed = true; } // parse the siblings (children are parsed in the 'multipart' case terms) if( !showOneMimePart && curNode && curNode->mNext ) parseObjectTree( curNode->mNext, showOneMimePart, keepEncryptions, includeSignatures ); // adjust signed/encrypted flags if inline PGP was found if( isInlineSigned ) curNode->setSigned( true ); if( isInlineEncrypted ) curNode->setEncrypted( true ); } if( showOneMimePart ) { mViewer->write(""); sendNextHtmlChunk(); /*mViewer->view()->viewport()->setUpdatesEnabled( true ); mViewer->view()->setUpdatesEnabled( true ); mViewer->view()->viewport()->repaint( false );*/ } } /* =========================================================================== E N D O F T E M P O R A R Y M I M E C O D E =========================================================================== */ const int KMReaderWin::delay = 150; //----------------------------------------------------------------------------- KMReaderWin::KMReaderWin(CryptPlugWrapperList *cryptPlugList, KMMimePartTree* mimePartTree, int* showMIMETreeMode, QWidget *aParent, const char *aName, int aFlags) : KMReaderWinInherited(aParent, aName, aFlags | Qt::WDestructiveClose), mShowCompleteMessage( false ), mMimePartTree( mimePartTree ), mShowMIMETreeMode( showMIMETreeMode ), mCryptPlugList( cryptPlugList ), mRootNode( 0 ) { mAutoDelete = false; mLastSerNum = 0; mMsg = 0; mMsgBuf = 0; mMsgBufMD5 = ""; mMsgBufSize = -1; mMsgDisplay = true; mPrinting = false; mShowColorbar = false; initHtmlWidget(); readConfig(); mHtmlOverride = false; mUseFixedFont = false; connect( &updateReaderWinTimer, SIGNAL(timeout()), this, SLOT(updateReaderWin()) ); connect( &mResizeTimer, SIGNAL(timeout()), this, SLOT(slotDelayedResize()) ); connect( &mHtmlTimer, SIGNAL(timeout()), this, SLOT(sendNextHtmlChunk()) ); mCodec = 0; mAutoDetectEncoding = true; } //----------------------------------------------------------------------------- KMReaderWin::~KMReaderWin() { delete mViewer; //hack to prevent segfault on exit if (mAutoDelete) delete mMsg; if (mRootNode) delete mRootNode; removeTempFiles(); } //----------------------------------------------------------------------------- void KMReaderWin::setMimePartTree( KMMimePartTree* mimePartTree ) { mMimePartTree = mimePartTree; } //----------------------------------------------------------------------------- void KMReaderWin::removeTempFiles() { for (QStringList::Iterator it = mTempFiles.begin(); it != mTempFiles.end(); it++) { QFile::remove(*it); } mTempFiles.clear(); for (QStringList::Iterator it = mTempDirs.begin(); it != mTempDirs.end(); it++) { QDir(*it).rmdir(*it); } mTempDirs.clear(); } //----------------------------------------------------------------------------- bool KMReaderWin::event(QEvent *e) { if (e->type() == QEvent::ApplicationPaletteChange) { readColorConfig(); if (mMsg) { mMsg->readConfig(); setMsg(mMsg, true); // Force update } return true; } return KMReaderWinInherited::event(e); } //----------------------------------------------------------------------------- void KMReaderWin::readColorConfig(void) { KConfig *config = kapp->config(); KConfigGroupSaver saver(config, "Reader"); c1 = QColor(kapp->palette().active().text()); c2 = KGlobalSettings::linkColor(); c3 = KGlobalSettings::visitedLinkColor(); c4 = QColor(kapp->palette().active().base()); // The default colors are also defined in configuredialog.cpp cPgpEncrH = QColor( 0x00, 0x80, 0xFF ); // light blue cPgpOk1H = QColor( 0x40, 0xFF, 0x40 ); // light green cPgpOk0H = QColor( 0xFF, 0xFF, 0x40 ); // light yellow cPgpWarnH = QColor( 0xFF, 0xFF, 0x40 ); // light yellow cPgpErrH = QColor( 0xFF, 0x00, 0x00 ); // red cCBpgp = QColor( 0x80, 0xFF, 0x80 ); // very light green cCBplain = QColor( 0xFF, 0xFF, 0x80 ); // very light yellow cCBhtml = QColor( 0xFF, 0x40, 0x40 ); // light red if (!config->readBoolEntry("defaultColors",TRUE)) { c1 = config->readColorEntry("ForegroundColor",&c1); c2 = config->readColorEntry("LinkColor",&c2); c3 = config->readColorEntry("FollowedColor",&c3); c4 = config->readColorEntry("BackgroundColor",&c4); cPgpEncrH = config->readColorEntry( "PGPMessageEncr", &cPgpEncrH ); cPgpOk1H = config->readColorEntry( "PGPMessageOkKeyOk", &cPgpOk1H ); cPgpOk0H = config->readColorEntry( "PGPMessageOkKeyBad", &cPgpOk0H ); cPgpWarnH = config->readColorEntry( "PGPMessageWarn", &cPgpWarnH ); cPgpErrH = config->readColorEntry( "PGPMessageErr", &cPgpErrH ); cCBpgp = config->readColorEntry( "ColorbarPGP", &cCBpgp ); cCBplain = config->readColorEntry( "ColorbarPlain", &cCBplain ); cCBhtml = config->readColorEntry( "ColorbarHTML", &cCBhtml ); } // determine the frame and body color for PGP messages from the header color // if the header color equals the background color then the other colors are // also set to the background color (-> old style PGP message viewing) // else // the brightness of the frame is set to 4/5 of the brightness of the header // the saturation of the body is set to 1/8 of the saturation of the header int h,s,v; if ( cPgpOk1H == c4 ) { // header color == background color? cPgpOk1F = c4; cPgpOk1B = c4; } else { cPgpOk1H.hsv( &h, &s, &v ); cPgpOk1F.setHsv( h, s, v*4/5 ); cPgpOk1B.setHsv( h, s/8, v ); } if ( cPgpOk0H == c4 ) { // header color == background color? cPgpOk0F = c4; cPgpOk0B = c4; } else { cPgpOk0H.hsv( &h, &s, &v ); cPgpOk0F.setHsv( h, s, v*4/5 ); cPgpOk0B.setHsv( h, s/8, v ); } if ( cPgpWarnH == c4 ) { // header color == background color? cPgpWarnF = c4; cPgpWarnB = c4; } else { cPgpWarnH.hsv( &h, &s, &v ); cPgpWarnF.setHsv( h, s, v*4/5 ); cPgpWarnB.setHsv( h, s/8, v ); } if ( cPgpErrH == c4 ) { // header color == background color? cPgpErrF = c4; cPgpErrB = c4; } else { cPgpErrH.hsv( &h, &s, &v ); cPgpErrF.setHsv( h, s, v*4/5 ); cPgpErrB.setHsv( h, s/8, v ); } if ( cPgpEncrH == c4 ) { // header color == background color? cPgpEncrF = c4; cPgpEncrB = c4; } else { cPgpEncrH.hsv( &h, &s, &v ); cPgpEncrF.setHsv( h, s, v*4/5 ); cPgpEncrB.setHsv( h, s/8, v ); } mRecyleQouteColors = config->readBoolEntry( "RecycleQuoteColors", false ); // // Prepare the quoted fonts // mQuoteFontTag[0] = quoteFontTag(0); mQuoteFontTag[1] = quoteFontTag(1); mQuoteFontTag[2] = quoteFontTag(2); } //----------------------------------------------------------------------------- void KMReaderWin::readConfig(void) { KConfig *config = kapp->config(); QString encoding; { // block defines the lifetime of KConfigGroupSaver KConfigGroupSaver saver(config, "Pixmaps"); mBackingPixmapOn = FALSE; mBackingPixmapStr = config->readEntry("Readerwin",""); if (mBackingPixmapStr != "") mBackingPixmapOn = TRUE; } { KConfigGroupSaver saver(config, "Reader"); mHtmlMail = config->readBoolEntry( "htmlMail", false ); mAtmInline = config->readNumEntry("attach-inline", 100); mHeaderStyle = (HeaderStyle)config->readNumEntry("hdr-style", HdrFancy); mAttachmentStyle = (AttachmentStyle)config->readNumEntry("attmnt-style", SmartAttmnt); mLoadExternal = config->readBoolEntry( "htmlLoadExternal", false ); mViewer->setOnlyLocalReferences( !mLoadExternal ); // if the user uses OpenPGP then the color bar defaults to enabled // else it defaults to disabled if( Kpgp::Module::getKpgp()->usePGP() ) mShowColorbar = true; else mShowColorbar = false; mShowColorbar = config->readBoolEntry( "showColorbar", mShowColorbar ); // if the value defaults to enabled and KMail (with color bar) is used for // the first time the config dialog doesn't know this if we don't save the // value now config->writeEntry( "showColorbar", mShowColorbar ); } { KConfigGroupSaver saver(config, "Fonts"); mUnicodeFont = config->readBoolEntry("unicodeFont",FALSE); mBodyFont = KGlobalSettings::generalFont(); mFixedFont = KGlobalSettings::fixedFont(); if (!config->readBoolEntry("defaultFonts",TRUE)) { mBodyFont = config->readFontEntry((mPrinting) ? "print-font" : "body-font", &mBodyFont); mFixedFont = config->readFontEntry("fixed-font", &mFixedFont); fntSize = mBodyFont.pointSize(); mBodyFamily = mBodyFont.family(); } else { setFont(KGlobalSettings::generalFont()); fntSize = KGlobalSettings::generalFont().pointSize(); mBodyFamily = KGlobalSettings::generalFont().family(); } mViewer->setStandardFont(mBodyFamily); } readColorConfig(); if (mMsg) { update(); mMsg->readConfig(); } } //----------------------------------------------------------------------------- void KMReaderWin::writeConfig(bool aWithSync) { KConfig *config = kapp->config(); KConfigGroupSaver saver(config, "Reader"); config->writeEntry("attach-inline", mAtmInline); config->writeEntry("hdr-style", (int)mHeaderStyle); config->writeEntry("attmnt-style",(int)mAttachmentStyle); if (aWithSync) config->sync(); } //----------------------------------------------------------------------------- QString KMReaderWin::quoteFontTag( int quoteLevel ) { KConfig *config = kapp->config(); QColor color; { // block defines the lifetime of KConfigGroupSaver KConfigGroupSaver saver(config, "Reader"); if( config->readBoolEntry( "defaultColors", true ) == true ) { color = QColor(kapp->palette().active().text()); } else { if( quoteLevel == 0 ) { QColor defaultColor( 0x00, 0x80, 0x00 ); color = config->readColorEntry( "QuotedText1", &defaultColor ); } else if( quoteLevel == 1 ) { QColor defaultColor( 0x00, 0x70, 0x00 ); color = config->readColorEntry( "QuotedText2", &defaultColor ); } else if( quoteLevel == 2 ) { QColor defaultColor( 0x00, 0x60, 0x00 ); color = config->readColorEntry( "QuotedText3", &defaultColor ); } else color = QColor(kapp->palette().active().base()); } } QFont font; { KConfigGroupSaver saver(config, "Fonts"); if( config->readBoolEntry( "defaultFonts", true ) == true ) { font = KGlobalSettings::generalFont(); font.setItalic(true); } else { const QFont defaultFont = QFont("helvetica"); if( quoteLevel == 0 ) font = config->readFontEntry( "quote1-font", &defaultFont ); else if( quoteLevel == 1 ) font = config->readFontEntry( "quote2-font", &defaultFont ); else if( quoteLevel == 2 ) font = config->readFontEntry( "quote3-font", &defaultFont ); else { font = KGlobalSettings::generalFont(); font.setItalic(true); } } } QString style; if( mPrinting ) style = "color:#000000;"; else style = QString( "color:%1;" ).arg( color.name() ); if( font.italic() ) style += "font-style:italic;"; if( font.bold() ) style += "font-weight:bold;"; return QString( "
" ).arg( style ); } //----------------------------------------------------------------------------- void KMReaderWin::initHtmlWidget(void) { mBox = new QHBox(this); mColorBar = new QLabel(" ", mBox); mColorBar->setAlignment(Qt::AlignHCenter | Qt::AlignTop); mColorBar->setEraseColor( mPrinting ? QColor( "white" ) : c4 ); if ( !mShowColorbar ) mColorBar->hide(); mViewer = new KHTMLPart(mBox, "khtml"); mViewer->widget()->setFocusPolicy(WheelFocus); // Let's better be paranoid and disable plugins (it defaults to enabled): mViewer->setPluginsEnabled(false); mViewer->setJScriptEnabled(false); // just make this explicit mViewer->setJavaEnabled(false); // just make this explicit mViewer->setMetaRefreshEnabled(false); mViewer->setURLCursor(KCursor::handCursor()); // Espen 2000-05-14: Getting rid of thick ugly frames mViewer->view()->setLineWidth(0); connect(mViewer->browserExtension(), SIGNAL(openURLRequest(const KURL &, const KParts::URLArgs &)),this, SLOT(slotUrlOpen(const KURL &, const KParts::URLArgs &))); connect(mViewer->browserExtension(), SIGNAL(createNewWindow(const KURL &, const KParts::URLArgs &)),this, SLOT(slotUrlOpen(const KURL &, const KParts::URLArgs &))); connect(mViewer,SIGNAL(onURL(const QString &)),this, SLOT(slotUrlOn(const QString &))); connect(mViewer,SIGNAL(popupMenu(const QString &, const QPoint &)), SLOT(slotUrlPopup(const QString &, const QPoint &))); } //----------------------------------------------------------------------------- void KMReaderWin::setHeaderStyle(KMReaderWin::HeaderStyle aHeaderStyle) { mHeaderStyle = aHeaderStyle; update(true); writeConfig(true); // added this so we can forward w/ full headers } //----------------------------------------------------------------------------- void KMReaderWin::setAttachmentStyle(int aAttachmentStyle) { mAttachmentStyle = (AttachmentStyle)aAttachmentStyle; update(true); } //----------------------------------------------------------------------------- void KMReaderWin::setCodec(QTextCodec *codec) { mCodec = codec; if(!codec) { mAutoDetectEncoding = true; update(true); return; } mAutoDetectEncoding = false; update(true); } //----------------------------------------------------------------------------- void KMReaderWin::setInlineAttach(int aAtmInline) { mAtmInline = aAtmInline; update(true); } //----------------------------------------------------------------------------- void KMReaderWin::setMsg(KMMessage* aMsg, bool force) { if (aMsg) kdDebug(5006) << "(" << aMsg->getMsgSerNum() << ") " << aMsg->subject() << " " << aMsg->fromStrip() << endl; // If not forced and there is aMsg and aMsg is same as mMsg then return if (!force && aMsg && mLastSerNum != 0 && aMsg->getMsgSerNum() == mLastSerNum) return; kdDebug(5006) << "set Msg, force = " << force << endl; // connect to the updates if we have hancy headers mMsg = aMsg; mLastSerNum = (aMsg) ? aMsg->getMsgSerNum() : 0; if (mMsg) { mMsg->setCodec(mCodec); mMsg->setDecodeHTML(htmlMail()); } // Avoid flicker, somewhat of a cludge if (force) { // stop the timer to avoid calling updateReaderWin twice updateReaderWinTimer.stop(); mMsgBuf = 0; updateReaderWin(); } else if (updateReaderWinTimer.isActive()) updateReaderWinTimer.changeInterval( delay ); else updateReaderWinTimer.start( 0, TRUE ); } //----------------------------------------------------------------------------- void KMReaderWin::clearCache() { updateReaderWinTimer.stop(); mMsg = 0; } // enter items for the "new features" list here, so the main body of // the welcome page can be left untouched (probably much easier for // the translators). Note that the
  • ...
  • tags are added // automatically below: static const char * const kmailNewFeatures[] = { I18N_NOOP("PGP/MIME (RFC 3156) support for GnuPG users"), I18N_NOOP("S/MIME support"), I18N_NOOP("Optional MIME tree viewer, allowing direct access to all " "body parts (attachments)"), I18N_NOOP("Optional total/unread columns in the folder view"), I18N_NOOP("Custom folder icons"), I18N_NOOP("Custom date format"), I18N_NOOP("Reorganized menu bar looks more like other KDE applications"), I18N_NOOP("Default identity can now be renamed"), }; static const int numKMailNewFeatures = sizeof kmailNewFeatures / sizeof *kmailNewFeatures; //----------------------------------------------------------------------------- void KMReaderWin::displayAboutPage() { mColorBar->hide(); mMsgDisplay = FALSE; QString location = locate("data", "kmail/about/main.html"); QString content = kFileToString(location); mViewer->begin(location); QString info = i18n("%1: KMail version; %2: help:// URL; %3: homepage URL; " "%4: prior KMail version; %5: prior KDE version; " "%6: generated list of new features; " "%7: First-time user text (only shown on first start)" "--- end of comment ---", "

    Welcome to KMail %1

    KMail is the email client for the K " "Desktop Environment. It is designed to be fully compatible with " "Internet mailing standards including MIME, SMTP, POP3 and IMAP." "

    \n" "
    • KMail has many powerful features which are described in the " "documentation
    • \n" "
    • The KMail homepage offers information about " "new versions of KMail
    \n" "

    Some of the new features in this release of KMail include " "(compared to KMail %4, which is part of KDE %5):

    \n" "
      \n%6
    \n" "%7\n" "

    We hope that you will enjoy KMail.

    \n" "

    Thank you,

    \n" "

        The KMail Team

    ") .arg(KMAIL_VERSION) // KMail version .arg("help:/kmail/index.html") // KMail help:// URL .arg("http://kmail.kde.org/") // KMail homepage URL .arg("1.4").arg("3.0"); // prior KMail and KDE version QString featureItems; for ( int i = 0 ; i < numKMailNewFeatures ; i++ ) featureItems += i18n("
  • %1
  • \n").arg( i18n( kmailNewFeatures[i] ) ); info = info.arg( featureItems ); if( kernel->firstStart() ) { info = info.arg( i18n("

    Please take a moment to fill in the KMail " "configuration panel at Settings->Configure " "KMail.\n" "You need to create at least a default identity and " "an incoming as well as outgoing mail account." "

    \n") ); } else { info = info.arg( QString::null ); } mViewer->write(content.arg(pointsToPixel(fntSize), 0, 'f', 5).arg(info)); mViewer->end(); } //----------------------------------------------------------------------------- void KMReaderWin::updateReaderWin() { /* if (mMsgBuf && mMsg && !mMsg->msgIdMD5().isEmpty() && (mMsgBufMD5 == mMsg->msgIdMD5()) && ((unsigned)mMsgBufSize == mMsg->msgSize())) return; */ mMsgBuf = mMsg; if (mMsgBuf) { mMsgBufMD5 = mMsgBuf->msgIdMD5(); mMsgBufSize = mMsgBuf->msgSize(); } else { mMsgBufMD5 = ""; mMsgBufSize = -1; } /* if (mMsg && !mMsg->msgIdMD5().isEmpty()) updateReaderWinTimer.start( delay, TRUE ); */ if (!mMsgDisplay) return; mViewer->view()->setUpdatesEnabled( false ); mViewer->view()->viewport()->setUpdatesEnabled( false ); static_cast(mViewer->widget())->ensureVisible(0,0); if (mHtmlTimer.isActive()) { mHtmlTimer.stop(); mViewer->end(); } mHtmlQueue.clear(); if (mMsg) { if ( mShowColorbar ) mColorBar->show(); else mColorBar->hide(); parseMsg(); } else { mColorBar->hide(); mViewer->begin( KURL( "file:/" ) ); mViewer->write("\nwrite(" background=\"file://" + mBackingPixmapStr + "\""); mViewer->write(">"); mViewer->end(); mViewer->view()->viewport()->setUpdatesEnabled( true ); mViewer->view()->setUpdatesEnabled( true ); mViewer->view()->viewport()->repaint( false ); if( mMimePartTree ) mMimePartTree->clear(); } } //----------------------------------------------------------------------------- void KMReaderWin::queueHtml(const QString &aStr) { uint pos = 0; while (aStr.length() > pos) { mHtmlQueue += aStr.mid(pos, 16384); pos += 16384; } } //----------------------------------------------------------------------------- void KMReaderWin::sendNextHtmlChunk() { QStringList::Iterator it = mHtmlQueue.begin(); if (it == mHtmlQueue.end()) { mViewer->end(); mViewer->view()->viewport()->setUpdatesEnabled( true ); mViewer->view()->setUpdatesEnabled( true ); mViewer->view()->viewport()->repaint( false ); return; } mViewer->write(*it); mHtmlQueue.remove(it); mHtmlTimer.start(0, TRUE); } //----------------------------------------------------------------------------- double KMReaderWin::pointsToPixel(int pointSize) { QPaintDeviceMetrics pdm(mViewer->view()); double pixelSize = pointSize; return pixelSize * pdm.logicalDpiY() / 72; } //----------------------------------------------------------------------------- void KMReaderWin::parseMsg(void) { if(mMsg == NULL) return; if( mMimePartTree ) mMimePartTree->clear(); QString type = mMsg->typeStr().lower(); bool isMultipart = (type.find("multipart/") != -1); if( mMimePartTree && mShowMIMETreeMode && (1 == *mShowMIMETreeMode) ) { if( isMultipart ) mMimePartTree->show(); else mMimePartTree->hide(); } QString bkgrdStr = ""; if (mBackingPixmapOn) bkgrdStr = " background=\"file://" + mBackingPixmapStr + "\""; mViewer->begin( KURL( "file:/" ) ); if (mAutoDetectEncoding) { mCodec = 0; QCString encoding; if (type.find("text/") != -1) encoding = mMsg->charset(); else if ( isMultipart ) { if (mMsg->numBodyParts() > 0) { KMMessagePart msgPart; mMsg->bodyPart(0, &msgPart); encoding = msgPart.charset(); } } if (encoding.isEmpty()) encoding = kernel->networkCodec()->name(); mCodec = KMMsgBase::codecForName(encoding); } if (!mCodec) mCodec = QTextCodec::codecForName("iso8859-1"); mMsg->setCodec(mCodec); QColorGroup cg = kapp->palette().active(); queueHtml("\n" "" + // TODO: move these to stylesheet, too: ((mPrinting) ? QString("") : QString("" )); if (!parent()) setCaption(mMsg->subject()); parseMsg(mMsg); queueHtml(""); sendNextHtmlChunk(); } void KMReaderWin::showMessageAndSetData( const QString& txt0, const QString& txt1, const QString& txt2a, const QString& txt2b, QCString& data ) { data = "

    "; data += txt0.utf8(); data += "


    "; data += i18n("reason:").utf8(); data += "
        "; data += txt1.utf8(); data += "
    "; data += i18n("proposal:").utf8(); data += "
        "; data += txt2a.utf8(); data += "
        "; data += txt2b.utf8(); data += "
    "; KMessageBox::sorry(this, txt0+"\n\n"+txt1+"\n\n"+txt2a+"\n"+txt2b); } bool KMReaderWin::writeOpaqueOrMultipartSignedData( partNode* data, partNode& sign ) { bool bIsOpaqueSigned = false; if( data ) parseObjectTree( data ); CryptPlugWrapper* cryptPlug = useThisCryptPlug ? useThisCryptPlug : mCryptPlugList->active(); if( cryptPlug ) { if( data ) kdDebug(5006) << "\nKMReaderWin::writeOpaqueOrMultipartSignedData: processing SMIME Signed data" << endl; else kdDebug(5006) << "\nKMReaderWin::writeOpaqueOrMultipartSignedData: processing Opaque Signed data" << endl; kdDebug(5006) << "\nKMReaderWin::writeOpaqueOrMultipartSignedData: going to call CRYPTPLUG " << cryptPlug->libName() << endl; bool oldCryptPlugActiveFlag; if( !cryptPlug->active() ) { if( !cryptPlug->initStatus( 0 ) == CryptPlugWrapper::InitStatus_Ok ) { KMessageBox::sorry(this, i18n("Crypto Plug-In \"%s" "\" is not initialized.\n" "Please specify the Plug-In using the 'Settings/Configure KMail / Plug-In' dialog.").arg(cryptPlug->libName())); return false; } oldCryptPlugActiveFlag = cryptPlug->active(); cryptPlug->setActive( true ); } else oldCryptPlugActiveFlag = true; QCString cleartext; char* new_cleartext; if( data ) cleartext = data->dwPart()->AsString().c_str(); else new_cleartext = 0; // the following code runs only for S/MIME if( data && (0 <= cryptPlug->libName().find( "smime", 0, false )) ) { // replace simple LFs by CRLSs // according to RfC 2633, 3.1.1 Canonicalization int posLF = cleartext.find( '\n' ); if( ( 0 < posLF ) && ( '\r' != cleartext[posLF - 1] ) ) { kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl; cleartext = KMMessage::lf2crlf( cleartext ); kdDebug(5006) << " done." << endl; } } // #define KHZ_TEST #ifdef KHZ_TEST QFile fileD( "testdat_xx1" ); if( fileD.open( IO_WriteOnly ) ) { if( data ) { QDataStream ds( &fileD ); ds.writeRawBytes( cleartext, cleartext.length() ); } fileD.close(); // If data is 0 we just create a zero length file. } #endif QByteArray signaturetext( sign.msgPart().bodyDecodedBinary() ); QCString signatureStr( signaturetext ); bool signatureIsBinary = (-1 == signatureStr.find("BEGIN SIGNED MESSAGE", 0, false) ); int signatureLen = signaturetext.size(); #ifdef KHZ_TEST QFile fileS( "testdat_xx1.sig" ); if( fileS.open( IO_WriteOnly ) ) { QDataStream ds( &fileS ); ds.writeRawBytes( signaturetext, signaturetext.size() ); fileS.close(); } #endif QCString deb; deb = "\n\nS I G N A T U R E = "; if( signatureIsBinary ) deb += "[binary data]"; else { deb += "\""; deb += signaturetext; deb += "\""; } deb += "\n\nC O N T E N T = \""; deb += cleartext; deb += "\" <-- E N D O F C O N T E N T\n\n"; kdDebug(5006) << deb << endl; struct CryptPlugWrapper::SignatureMetaData sigMeta; sigMeta.status = 0; sigMeta.extended_info_count = 0; sigMeta.nota_xml = 0; // PENDING(khz) Should we distinguish between an invalid signature // and the incapability of the plugin to verify the signature? const char* cleartextP = cleartext; bool bSignatureOk = cryptPlug->hasFeature( Feature_VerifySignatures ) && cryptPlug->checkMessageSignature( data ? const_cast(&cleartextP) : &new_cleartext, signaturetext, signatureIsBinary, signatureLen, &sigMeta ); kdDebug(5006) << "\nKMReaderWin::writeOpaqueOrMultipartSignedData: returned from CRYPTPLUG" << endl; QString txt; QString unknown( i18n("(unknown)") ); if( !data ){ if( new_cleartext ) { bIsOpaqueSigned = true; deb = "\n\nN E W C O N T E N T = \""; deb += new_cleartext; deb += "\" <-- E N D O F N E W C O N T E N T\n\n"; kdDebug(5006) << deb << endl; insertAndParseNewChildNode( sign, new_cleartext, "opaqued signed data" ); delete new_cleartext; } else { txt = "

    "; txt.append( i18n( "The crypto engine returned no cleartext data!" ) ); txt.append( "

    " ); txt.append( "
     
    " ); txt.append( i18n( "Status: " ).local8Bit() ); if( sigMeta.status && 0 < strlen(sigMeta.status) ) { txt.append( "" ); txt.append( sigMeta.status ); txt.append( "" ); } else txt.append( unknown ); queueHtml(txt); } } if( bSignatureOk ) { txt = "
    "; txt.append( i18n( "Signature is OK." ).local8Bit() ); txt.append( "
     
    " ); txt.append( i18n( "Status: " ).local8Bit() ); if( sigMeta.status && 0 < strlen(sigMeta.status) ) { txt.append( "" ); txt.append( sigMeta.status ); txt.append( "" ); } else txt.append( unknown ); txt.append( "
     
    " ); txt.append( i18n( "Signature key information:" ).local8Bit() ); txt.append( "
    " ); if( 0 < sigMeta.extended_info_count ) { txt.append( "" ); for( int i=0; i" ); txt.append( QString( "").arg( ext.status_text ).latin1() ); txt.append( QString( "").arg( ext.fingerprint ).latin1() ); } txt.append( "
    " ); txt.append( i18n( "created" ).local8Bit() ); txt.append( "" ); txt.append( i18n( "status" ).local8Bit() ); txt.append( "" ); txt.append( i18n( "fingerprint" ).local8Bit() ); txt.append( "
    %1").arg( ext.creation_time ).latin1() ); // ist: txt.append( "
    ?%1%1
    " ); } else txt.append( unknown ); txt.append( "
    Notation (XML):
    " ); if( sigMeta.nota_xml && 0 < sigMeta.nota_xml ) { txt.append( "
    " );
            txt.append( sigMeta.nota_xml );
            txt.append( "
    " ); } else txt.append( unknown ); cryptPlug->freeSignatureMetaData( &sigMeta ); queueHtml(txt); } else { txt = "

    "; txt.append( i18n( "Signature could not be verified!" ) ); txt.append( "

    " ); txt.append( "
     
    " ); txt.append( i18n( "Status: " ).local8Bit() ); if( sigMeta.status && 0 < strlen(sigMeta.status) ) { txt.append( "" ); txt.append( sigMeta.status ); txt.append( "" ); } else txt.append( unknown ); queueHtml(txt); } cryptPlug->setActive( oldCryptPlugActiveFlag ); } else { KMessageBox::information(this, i18n("problem: No Crypto Plug-Ins found.\n" "Please specify a Plug-In using the 'Settings/Configure KMail / Plug-In' dialog."), QString::null, "cryptoPluginBox"); queueHtml(i18n("

    Signature could *not* be verified !


    " "reason:
        No Crypto Plug-Ins found.
    " "proposal:
        Please specify a Plug-In by invoking
        the " "'Settings/Configure KMail / Plug-In' dialog!
    ")); } kdDebug(5006) << "\nKMReaderWin::writeOpaqueOrMultipartSignedData: done, returning " << ( bIsOpaqueSigned ? "TRUE" : "FALSE" ) << endl; return bIsOpaqueSigned; } bool KMReaderWin::okDecryptMIME( partNode& data, QCString& decryptedData ) { const QString errorContentCouldNotBeDecrypted( i18n("Content could *not* be decrypted.") ); bool bDecryptionOk = false; CryptPlugWrapper* cryptPlug = useThisCryptPlug ? useThisCryptPlug : mCryptPlugList->active(); if( cryptPlug ) { QByteArray ciphertext( data.msgPart().bodyDecodedBinary() ); QCString cipherStr( ciphertext ); bool cipherIsBinary = (-1 == cipherStr.find("BEGIN ENCRYPTED MESSAGE", 0, false) ); int cipherLen = ciphertext.size(); #ifdef KHZ_TEST QFile fileC( "testdat_xx1.encrypted" ); if( fileC.open( IO_WriteOnly ) ) { QDataStream dc( &fileC ); dc.writeRawBytes( ciphertext, ciphertext.size() ); fileC.close(); } #endif QCString deb; deb = "\n\nE N C R Y P T E D D A T A = "; if( cipherIsBinary ) deb += "[binary data]"; else { deb += "\""; deb += ciphertext; deb += "\""; } deb += "\n\n"; kdDebug(5006) << deb << endl; char* cleartext = 0; const char* certificate = 0; if( ! cryptPlug->hasFeature( Feature_DecryptMessages ) ) { showMessageAndSetData( errorContentCouldNotBeDecrypted, i18n("Crypto Plug-In %1 can not decrypt any messages.").arg(cryptPlug->libName()), i18n("Please specify a *matching* Plug-In by invoking"), i18n("the 'Settings/Configure KMail / Plug-In' dialog!"), decryptedData ); } else { kdDebug() << "\nKMReaderWin::decryptMIME: going to call CRYPTPLUG " << cryptPlug->libName() << endl; bDecryptionOk = cryptPlug->decryptMessage( ciphertext, cipherIsBinary, cipherLen, &cleartext, certificate ); kdDebug() << "\nKMReaderWin::decryptMIME: returned from CRYPTPLUG" << endl; if( bDecryptionOk ) decryptedData = cleartext; else { showMessageAndSetData( errorContentCouldNotBeDecrypted, i18n("Crypto Plug-In %1 could not decrypt the data.").arg(cryptPlug->libName()), i18n("Make sure the Plug-In is installed properly and check your"), i18n("specifications made in the 'Settings/Configure KMail / Plug-In' dialog!"), decryptedData ); } } delete cleartext; } else { showMessageAndSetData( errorContentCouldNotBeDecrypted, i18n("No Crypto Plug-In settings found.").utf8(), i18n("Please specify a Plug-In by invoking").utf8(), i18n("the 'Settings/Configure KMail / Plug-In' dialog!").utf8(), decryptedData ); } return bDecryptionOk; } //----------------------------------------------------------------------------- void KMReaderWin::parseMsg(KMMessage* aMsg) { removeTempFiles(); KMMessagePart msgPart; int numParts; QCString type, subtype, contDisp; QByteArray str; assert(aMsg!=NULL); type = aMsg->typeStr(); numParts = aMsg->numBodyParts(); int mainType = aMsg->type(); int mainSubType = aMsg->subtype(); QString mainCntTypeStr; if( (DwMime::kTypeNull == mainType) || (DwMime::kTypeUnknown == mainType) ){ mainType = DwMime::kTypeText; mainSubType = DwMime::kSubtypePlain; mainCntTypeStr = "text/plain"; } else { mainCntTypeStr = aMsg->typeStr(); int scpos = mainCntTypeStr.find(';'); if( -1 < scpos) mainCntTypeStr.truncate( scpos ); } assert( aMsg != NULL ); VCard *vc = 0; bool hasVCard = false; // make sure we have a valid CryptPlugList bool tmpPlugList = !mCryptPlugList; if( tmpPlugList ) { mCryptPlugList = new CryptPlugWrapperList(); KConfig *config = KGlobal::config(); mCryptPlugList->loadFromConfig( config ); } // store message body in mRootNode if *no* body parts found // (please read the comment below before crying about me) :-) DwBodyPart* mainBody = 0; DwBodyPart* firstBodyPart = aMsg->getFirstDwBodyPart(); if( !firstBodyPart ) { // ATTENTION: This definitely /should/ be optimized. // Copying the message text into a new body part // surely is not the most efficient way to go. // I decided to do so for being able to get a // solution working for old style (== non MIME) // mails without spending much time on implementing. // During code revisal when switching to KMime // all this will probably disappear anyway (or it // will be optimized, resp.). (khz, 6.12.2001) kdDebug() << "*no* first body part found, creating one from Message" << endl; mainBody = new DwBodyPart(aMsg->asDwString(), 0); mainBody->Parse(); } if( mRootNode ) delete mRootNode; mRootNode = new partNode( mainBody ? mainBody : 0, mainType, mainSubType ); QString cntDesc, cntSize, cntEnc; cntDesc = aMsg->subject(); if( cntDesc.isEmpty() ) cntDesc = i18n("( body part )"); cntSize = QString::number( aMsg->msgSize() ); if( aMsg->contentTransferEncodingStr().isEmpty() ) cntEnc = "7bit"; else cntEnc = aMsg->contentTransferEncodingStr(); if( firstBodyPart ) { kdDebug(5006) << "\n -----> First body part *was* found, filling the Mime Part Tree" << endl; // store pointers to the MIME objects in our fast access tree partNode* curNode = mRootNode->setFirstChild( new partNode(firstBodyPart) ); curNode->buildObjectTree(); // fill the MIME part tree viewer if( mMimePartTree ) mRootNode->fillMimePartTree( 0, mMimePartTree, cntDesc, mainCntTypeStr, cntEnc, cntSize ); } else if( mMimePartTree ) { kdDebug(5006) << "\n -----> Inserting Root Node into the Mime Part Tree" << endl; mRootNode->fillMimePartTree( 0, mMimePartTree, cntDesc, mainCntTypeStr, cntEnc, cntSize ); /*new KMMimePartTreeItem( *mMimePartTree, mRootNode, cntDesc, mainCntTypeStr, cntEnc, cntSize );*/ kdDebug(5006) << "\n <----- Finished inserting Root Node into Mime Part Tree" << endl; } else { kdDebug(5006) << "\n ------ Sorry, no Mime Part Tree - can NOT insert Root Node!" << endl; } partNode* vCardNode = mRootNode->findType( DwMime::kTypeText, DwMime::kSubtypeXVCard ); if( vCardNode ) { int vcerr; QTextCodec *atmCodec = (mAutoDetectEncoding) ? KMMsgBase::codecForName(vCardNode->msgPart().charset()) : mCodec; if (!atmCodec) atmCodec = mCodec; vc = VCard::parseVCard(atmCodec->toUnicode( vCardNode->msgPart().bodyDecoded() ), &vcerr); if( vc ) { delete vc; kdDebug(5006) << "FOUND A VALID VCARD" << endl; hasVCard = true; writePartIcon(&vCardNode->msgPart(), aMsg->partNumber(vCardNode->dwPart()), TRUE ); } } queueHtml("
    " + (writeMsgHeader(hasVCard)) + "

    "); // show message content parseObjectTree( mRootNode ); // store encrypted/signed status information in the KMMessage // - this can only be done *after* calling parseObjectTree() aMsg->setEncryptionState( mRootNode->overallEncryptionState() ); aMsg->setSignatureState( mRootNode->overallSignatureState() ); // remove temp. Body Part that was created for a single part mail if( mainBody ) delete mainBody; // remove temp. CryptPlugList if( tmpPlugList ) delete mCryptPlugList; /* old code *before* AEGYPTEN_BRANCH merger: if (numParts > 0) { // ---sven: handle multipart/alternative start --- // This is for multipart/alternative messages WITHOUT attachments // main header has type=multipart/alternative and one attachment is // text/html if (type.find("multipart/alternative") != -1 && numParts == 2) { kdDebug(5006) << "Alternative message, type: " << type << endl; //Now: Only two attachments one of them is html for (i=0; i<2; i++) // count parts... { aMsg->bodyPart(i, &msgPart); // set part... subtype = msgPart.subtypeStr(); // get subtype... if (htmlMail() && qstricmp(subtype, "html")==0) // is it html? { // yes... str = msgPart.bodyDecoded(); // decode it... //mViewer->write(mCodec->toUnicode(str.data())); // write it... writeHTMLStr(mCodec->toUnicode(str.data())); return; // return, finshed. } else if (!htmlMail() && (qstricmp(subtype, "plain")==0)) // wasn't html show only if { // support for html is turned off str = msgPart.bodyDecoded(); // decode it... writeBodyStr(str.data(), mCodec); return; } } // if we are here we didnt find any html part. Handle it normaly then } // This works only for alternative msgs without attachments. Alternative // messages with attachments are broken with or without this. No need // to bother with strib or here, because if any part // follows this will not be shown correctly. You'll still be able to read the // main message and deal with attachments. Nothing I can do now :-( // ---sven: handle multipart/alternative end --- for (i=0; ibodyPart(i, &msgPart); type = msgPart.typeStr(); subtype = msgPart.subtypeStr(); contDisp = msgPart.contentDisposition(); kdDebug(5006) << "type: " << type.data() << endl; kdDebug(5006) << "subtye: " << subtype.data() << endl; kdDebug(5006) << "contDisp " << contDisp.data() << endl; if (i <= 0) asIcon = FALSE; else switch (mAttachmentStyle) { case IconicAttmnt: asIcon=TRUE; break; case InlineAttmnt: asIcon=FALSE; break; case SmartAttmnt: asIcon=(contDisp.find("inline")<0); } if (!asIcon) { // if (i<=0 || qstricmp(type, "text")==0)//||qstricmp(type, "message")==0) // if (qstricmp(type, "text")==0)//||qstricmp(type, "message")==0) if ((type == "") || (qstricmp(type, "text")==0)) { QCString cstr(msgPart.bodyDecoded()); if (i>0) queueHtml("


    "); QTextCodec *atmCodec = (mAutoDetectEncoding) ? KMMsgBase::codecForName(msgPart.charset()) : mCodec; if (!atmCodec) atmCodec = mCodec; if (htmlMail() && (qstricmp(subtype, "html")==0)) { // ---Sven's strip and from end of attachment start- // We must fo this, or else we will see only 1st inlined html attachment // It is IMHO enough to search only for and put \0 there. int i; i = cstr.findRev("", -1, false); //case insensitive if (i>0) cstr.truncate(i); else // just in case - search for { i = cstr.findRev("", -1, false); //case insensitive if (i>0) cstr.truncate(i); } // ---Sven's strip and from end of attachment end- //mViewer->write(atmCodec->toUnicode(cstr.data())); writeHTMLStr(atmCodec->toUnicode(cstr.data())); } else writeBodyStr(cstr, atmCodec); } // ---Sven's view smart or inline image attachments in kmail start--- else if (qstricmp(type, "image")==0) { inlineImage=true; writePartIcon(&msgPart, i); inlineImage=false; } // ---Sven's view smart or inline image attachments in kmail end--- else asIcon = TRUE; } if (asIcon) { writePartIcon(&msgPart, i); } } } else // if numBodyParts <= 0 { if (htmlMail() && ((type == "text/html") || (type.find("text/html") != -1))) //mViewer->write(mCodec->toUnicode(aMsg->bodyDecoded().data())); writeHTMLStr(mCodec->toUnicode(aMsg->bodyDecoded().data())); else writeBodyStr(aMsg->bodyDecoded(), mCodec); } */ } //----------------------------------------------------------------------------- QString KMReaderWin::writeMsgHeader(bool hasVCard) { QString vcname; // The direction of the header is determined according to the direction // of the application layout. QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" ); QString headerStr = QString("
    ").arg(dir); // However, the direction of the message subject within the header is // determined according to the contents of the subject itself. Since // the "Re:" and "Fwd:" prefixes would always cause the subject to be // considered left-to-right, they are ignored when determining its // direction. TODO: Implement this using the custom prefixes. QString subjectDir; if (!mMsg->subject().isEmpty()) { subjectDir = (KMMsgBase::skipKeyword(mMsg->subject()) .isRightToLeft()) ? "rtl" : "ltr"; } else subjectDir = i18n("No Subject").isRightToLeft() ? "rtl" : "ltr"; if (hasVCard) vcname = mTempFiles.last(); switch (mHeaderStyle) { case HdrBrief: headerStr += QString("
    " + strToHtml(mMsg->subject()) + "  (" + KMMessage::emailAddrAsAnchor(mMsg->from(),TRUE) + ", ") .arg(subjectDir); if (!mMsg->cc().isEmpty()) { headerStr.append(i18n("Cc: ")+ KMMessage::emailAddrAsAnchor(mMsg->cc(),TRUE) + ", "); } headerStr.append(" "+strToHtml(mMsg->dateShortStr()) + ")"); if (hasVCard) { headerStr.append("  "+i18n("[vCard]")+""); } headerStr.append("
    "); break; case HdrStandard: headerStr += QString("
    " + strToHtml(mMsg->subject()) + "
    ") .arg(subjectDir); headerStr.append(i18n("From: ") + KMMessage::emailAddrAsAnchor(mMsg->from(),FALSE)); if (hasVCard) { headerStr.append("  "+i18n("[vCard]")+""); } headerStr.append("
    "); headerStr.append(i18n("To: ") + KMMessage::emailAddrAsAnchor(mMsg->to(),FALSE) + "
    "); if (!mMsg->cc().isEmpty()) headerStr.append(i18n("Cc: ")+ KMMessage::emailAddrAsAnchor(mMsg->cc(),FALSE) + "
    "); break; case HdrFancy: { // the subject line and box below for details headerStr += QString("
    " "%2
    " "
    " "") .arg(subjectDir) .arg(mMsg->subject().isEmpty()? i18n("No Subject") : strToHtml(mMsg->subject())); // from line headerStr.append(QString("") .arg(i18n("From: ")) .arg(KMMessage::emailAddrAsAnchor(mMsg->from(),FALSE)) .arg(hasVCard ? "  "+i18n("[vCard]")+"" : "") .arg((mMsg->headerField("Organization").isEmpty()) ? "" : "  (" + strToHtml(mMsg->headerField("Organization")) + ")")); // to line headerStr.append(QString("") .arg(i18n("To: ")) .arg(KMMessage::emailAddrAsAnchor(mMsg->to(),FALSE))); // cc line, if any if (!mMsg->cc().isEmpty()) { headerStr.append(QString("") .arg(i18n("Cc: ")) .arg(KMMessage::emailAddrAsAnchor(mMsg->cc(),FALSE))); } // the date headerStr.append(QString("") .arg(i18n("Date: ")) .arg(mMsg->dateStr().isRightToLeft() ? "rtl" : "ltr") .arg(strToHtml(mMsg->dateStr()))); headerStr.append("
    %1%2%3%4
    %1%2
    %1%2
    %1%3
    "); break; } case HdrLong: headerStr += QString("
    " + strToHtml(mMsg->subject()) + "
    ") .arg(subjectDir); headerStr.append(i18n("Date: ") + strToHtml(mMsg->dateStr())+"
    "); headerStr.append(i18n("From: ") + KMMessage::emailAddrAsAnchor(mMsg->from(),FALSE)); if (hasVCard) { headerStr.append("  "+i18n("[vCard]")+""); } if (!mMsg->headerField("Organization").isEmpty()) { headerStr.append("  (" + strToHtml(mMsg->headerField("Organization")) + ")"); } headerStr.append("
    "); headerStr.append(i18n("To: ")+ KMMessage::emailAddrAsAnchor(mMsg->to(),FALSE) + "
    "); if (!mMsg->cc().isEmpty()) { headerStr.append(i18n("Cc: ")+ KMMessage::emailAddrAsAnchor(mMsg->cc(),FALSE) + "
    "); } if (!mMsg->bcc().isEmpty()) { headerStr.append(i18n("Bcc: ")+ KMMessage::emailAddrAsAnchor(mMsg->bcc(),FALSE) + "
    "); } if (!mMsg->replyTo().isEmpty()) { headerStr.append(i18n("Reply to: ")+ KMMessage::emailAddrAsAnchor(mMsg->replyTo(),FALSE) + "
    "); } break; case HdrAll: // we force the direction to ltr here, even in a arabic/hebrew UI, // as the headers are almost all Latin1 headerStr += "
    "; headerStr += strToHtml(mMsg->headerAsString(), true); if (hasVCard) { headerStr.append("
    "+i18n("[vCard]")+""); } headerStr += "
    "; break; default: kdDebug(5006) << "Unsupported header style " << mHeaderStyle << endl; } headerStr += "
    "; return headerStr; } //----------------------------------------------------------------------------- void KMReaderWin::writeBodyStr( const QCString aStr, QTextCodec *aCodec, bool* flagSigned, bool* flagEncrypted ) { QString line, htmlStr; QString signClass; bool goodSignature = false; Kpgp::Module* pgp = Kpgp::Module::getKpgp(); assert(pgp != NULL); bool isPgpMessage = false; // true if the message contains at least one // PGP MESSAGE or one PGP SIGNED MESSAGE block QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" ); QString headerStr = QString("
    ").arg(dir); QPtrList pgpBlocks; QStrList nonPgpBlocks; if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) ) { bool isEncrypted = false, isSigned = false; QString signer; QPtrListIterator pbit( pgpBlocks ); QStrListIterator npbit( nonPgpBlocks ); for( ; *pbit != 0; ++pbit, ++npbit ) { // insert the next Non-OpenPGP block QCString str( *npbit ); if( !str.isEmpty() ) htmlStr += quotedHTML( aCodec->toUnicode( str ) ); //htmlStr += "
    "; Kpgp::Block* block = *pbit; if( ( block->type() == Kpgp::PgpMessageBlock ) || ( block->type() == Kpgp::ClearsignedBlock ) ) { isPgpMessage = true; if( block->type() == Kpgp::PgpMessageBlock ) { emit noDrag(); // try to decrypt this OpenPGP block bool couldDecrypt = block->decrypt(); isEncrypted = block->isEncrypted(); if( isEncrypted ) { htmlStr += "" "
    "; if( couldDecrypt ) htmlStr += i18n("Encrypted message"); else htmlStr += QString("%1
    %2") .arg(i18n("Cannot decrypt message:")) .arg(pgp->lastErrorMsg()); htmlStr += "
    "; } } else { // try to verify this OpenPGP block block->verify(); } isSigned = block->isSigned(); if( isSigned ) { signer = block->signatureUserId(); if( signer.isEmpty() ) { signClass = "signWarn"; htmlStr += "" "
    "; htmlStr += i18n( "Message was signed with unknown key 0x%1." ) .arg( block->signatureKeyId() ); htmlStr += "
    "; htmlStr += i18n( "The validity of the signature can't be " "verified." ); htmlStr += "
    "; } else { goodSignature = block->goodSignature(); QCString keyId = block->signatureKeyId(); Kpgp::Validity keyTrust; if( !keyId.isEmpty() ) keyTrust = pgp->keyTrust( keyId ); else // This is needed for the PGP 6 support because PGP 6 doesn't // print the key id of the signing key if the key is known. keyTrust = pgp->keyTrust( signer ); // HTMLize the signer's user id and create mailto: link signer.replace( QRegExp("&"), "&" ); signer.replace( QRegExp("<"), "<" ); signer.replace( QRegExp(">"), ">" ); signer.replace( QRegExp("\""), """ ); signer = "" + signer + ""; if( goodSignature ) { if( keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL ) signClass = "signOkKeyBad"; else signClass = "signOkKeyOk"; htmlStr += "" "" "
    "; if( !keyId.isEmpty() ) htmlStr += i18n( "Message was signed by %1 (Key ID: 0x%2)." ) .arg( signer ) .arg( keyId ); else htmlStr += i18n( "Message was signed by %1." ).arg( signer ); htmlStr += "
    "; switch( keyTrust ) { case Kpgp::KPGP_VALIDITY_UNKNOWN: htmlStr += i18n( "The signature is valid, but the key's " "validity is unknown." ); break; case Kpgp::KPGP_VALIDITY_MARGINAL: htmlStr += i18n( "The signature is valid and the key is " "marginally trusted." ); break; case Kpgp::KPGP_VALIDITY_FULL: htmlStr += i18n( "The signature is valid and the key is " "fully trusted." ); break; case Kpgp::KPGP_VALIDITY_ULTIMATE: htmlStr += i18n( "The signature is valid and the key is " "ultimately trusted." ); break; default: htmlStr += i18n( "The signature is valid, but the key is " "untrusted." ); } htmlStr += "
    "; } else { signClass = "signErr"; htmlStr += "" "" ""; htmlStr += "
    "; if( !keyId.isEmpty() ) htmlStr += i18n( "Message was signed by %1 (Key ID: 0x%2)." ) .arg( signer ) .arg( keyId ); else htmlStr += i18n( "Message was signed by %1." ).arg( signer ); htmlStr += "
    "; htmlStr += i18n("Warning: The signature is bad."); htmlStr += "
    "; } } } htmlStr += quotedHTML( aCodec->toUnicode( block->text() ) ); if( isSigned ) { htmlStr += "
    " + i18n( "End of signed message" ) + "
    "; if( flagSigned ) *flagSigned = true; } if( isEncrypted ) { htmlStr += "
    " + i18n( "End of encrypted message" ) + "
    "; if( flagEncrypted ) *flagEncrypted = true; } } else // block is neither message block nor clearsigned block htmlStr += quotedHTML( aCodec->toUnicode( block->text() ) ); } // add the last Non-OpenPGP block QCString str( nonPgpBlocks.last() ); if( !str.isEmpty() ) htmlStr += quotedHTML( aCodec->toUnicode( str ) ); } else htmlStr = quotedHTML( aCodec->toUnicode( aStr ) ); if( isPgpMessage ) { mColorBar->setEraseColor( cCBpgp ); mColorBar->setText(i18n("\nP\nG\nP\n \nM\ne\ns\ns\na\ng\ne")); } else { mColorBar->setEraseColor( cCBplain ); mColorBar->setText(i18n("\nN\no\n \nP\nG\nP\n \nM\ne\ns\ns\na\ng\ne")); } queueHtml(htmlStr); } //----------------------------------------------------------------------------- void KMReaderWin::writeHTMLStr(const QString& aStr) { mColorBar->setEraseColor( cCBhtml ); mColorBar->setText(i18n("\nH\nT\nM\nL\n \nM\ne\ns\ns\na\ng\ne")); queueHtml(aStr); } //----------------------------------------------------------------------------- QString KMReaderWin::quotedHTML(const QString& s) { QString htmlStr, line; QString normalStartTag; const QString normalEndTag = ""; const QString quoteEnd = ""; unsigned int pos, beg; unsigned int length = s.length(); enum { First, New, Mid } paragState = First; bool rightToLeft = false; QString style; if( mBodyFont.bold() ) style += "font-weight:bold;"; if( mBodyFont.italic() ) style += "font-style:italic;"; if( style.isEmpty() ) normalStartTag = "
    "; else normalStartTag = QString("
    ").arg( style ); // skip leading empty lines for( pos = 0; pos < length && s[pos] <= ' '; pos++ ); while (pos > 0 && (s[pos-1] == ' ' || s[pos-1] == '\t')) pos--; beg = pos; int currQuoteLevel = -1; while (beg': case '|': actQuoteLevel++; break; case '\r': break; default: finish = TRUE; break; } } /* for() */ if ( (paragState != Mid && finish) || (actQuoteLevel != currQuoteLevel) ) { /* finish last quotelevel */ if (currQuoteLevel == -1) htmlStr.append( normalEndTag ); else htmlStr.append( quoteEnd ); if ( paragState == New ) htmlStr += "
    "; /* start new quotelevel */ currQuoteLevel = actQuoteLevel; if (actQuoteLevel == -1) htmlStr += normalStartTag; else htmlStr += mQuoteFontTag[currQuoteLevel%3]; paragState = Mid; } if ( !finish && paragState == Mid ) paragState = New; rightToLeft = line.isRightToLeft(); line = strToHtml(line, TRUE); htmlStr+= QString("
    " ).arg(rightToLeft ? "rtl" : "ltr"); htmlStr+= (line); htmlStr+= QString("
    "); } /* while() */ /* really finish the last quotelevel */ if (currQuoteLevel == -1) htmlStr.append( normalEndTag ); else htmlStr.append( quoteEnd ); //kdDebug(5006) << "KMReaderWin::quotedHTML:\n" // << "========================================\n" // << htmlStr // << "\n======================================\n"; return htmlStr; } //----------------------------------------------------------------------------- void KMReaderWin::writePartIcon(KMMessagePart* aMsgPart, int aPartNum, bool quiet) { QString iconName, href, label, comment, contDisp; QString fileName; if(aMsgPart == NULL) { kdDebug(5006) << "writePartIcon: aMsgPart == NULL\n" << endl; return; } kdDebug(5006) << "writePartIcon: PartNum: " << aPartNum << endl; comment = aMsgPart->contentDescription(); comment.replace(QRegExp("&"), "&"); comment.replace(QRegExp("<"), "<"); comment.replace(QRegExp(">"), ">"); fileName = aMsgPart->fileName(); if (fileName.isEmpty()) fileName = aMsgPart->name(); label = fileName; /* HTMLize label */ label.replace(QRegExp("&"), "&"); label.replace(QRegExp("<"), "<"); label.replace(QRegExp(">"), ">"); //--- Sven's save attachments to /tmp start --- KTempFile *tempFile = new KTempFile(QString::null, "." + QString::number(aPartNum)); tempFile->setAutoDelete(true); QString fname = tempFile->name(); delete tempFile; bool ok = true; if (access(QFile::encodeName(fname), W_OK) != 0) // Not there or not writable if (mkdir(QFile::encodeName(fname), 0) != 0 || chmod (QFile::encodeName(fname), S_IRWXU) != 0) ok = false; //failed create if (ok) { mTempDirs.append(fname); fileName.replace(QRegExp("[/\"\']"),""); if (fileName.isEmpty()) fileName = "unnamed"; fname += "/" + fileName; if (!kByteArrayToFile(aMsgPart->bodyDecodedBinary(), fname, false, false, false)) ok = false; mTempFiles.append(fname); } if (ok) { href = QString("file:")+fname; //debug ("Wrote attachment to %s", href.data()); } else { //--- Sven's save attachments to /tmp end --- href = QString("part://%1").arg(aPartNum+1); } // sven: for viewing images inline if (inlineImage) iconName = href; else iconName = aMsgPart->iconName(); if (iconName.right(14)=="mime_empty.png") { aMsgPart->magicSetType(); iconName = aMsgPart->iconName(); } if (!quiet) queueHtml("
    " + label + "
    " + comment + "
    "); } //----------------------------------------------------------------------------- QString KMReaderWin::strToHtml(const QString &aStr, bool aPreserveBlanks) const { return LinkLocator::convertToHtml(aStr, aPreserveBlanks); } //----------------------------------------------------------------------------- void KMReaderWin::printMsg(void) { if (!mMsg) return; if (mPrinting) mViewer->view()->print(); else { KMReaderWin printWin; printWin.setPrinting(TRUE); printWin.readConfig(); printWin.setMsg(mMsg, TRUE); printWin.printMsg(); } } //----------------------------------------------------------------------------- int KMReaderWin::msgPartFromUrl(const KURL &aUrl) { if (aUrl.isEmpty() || !mMsg) return -1; if (!aUrl.isLocalFile()) return -1; QString path = aUrl.path(); uint right = path.findRev('/'); uint left = path.findRev('.', right); bool ok; int res = path.mid(left + 1, right - left - 1).toInt(&ok); return (ok) ? res : -1; } //----------------------------------------------------------------------------- void KMReaderWin::resizeEvent(QResizeEvent *) { if( !mResizeTimer.isActive() ) { // // Combine all resize operations that are requested as long a // the timer runs. // mResizeTimer.start( 100, true ); } } //----------------------------------------------------------------------------- void KMReaderWin::slotDelayedResize() { //mViewer->widget()->setGeometry(0, 0, width(), height()); mBox->setGeometry(0, 0, width(), height()); } //----------------------------------------------------------------------------- void KMReaderWin::closeEvent(QCloseEvent *e) { KMReaderWinInherited::closeEvent(e); writeConfig(); } //----------------------------------------------------------------------------- void KMReaderWin::slotUrlOn(const QString &aUrl) { bool bOk = false; KURL url(aUrl); int id = msgPartFromUrl(url); if (id > 0) { // KMMessagePart msgPart; // mMsg->bodyPart(id, &msgPart); partNode* node = mRootNode ? mRootNode->findId( id ) : 0; if( node ) { KMMessagePart& msgPart = node->msgPart(); QString str = msgPart.fileName(); if (str.isEmpty()) str = msgPart.name(); emit statusMsg(i18n("Attachment: ") + str); bOk = true; } } else if( aUrl == "kmail:showHTML" ) { emit statusMsg( i18n("Turn on HTML rendering for this message.") ); bOk = true; } if( !bOk ) emit statusMsg(aUrl); } //----------------------------------------------------------------------------- void KMReaderWin::slotUrlOpen(const KURL &aUrl, const KParts::URLArgs &) { if (!aUrl.hasHost() && aUrl.path() == "/" && aUrl.hasRef()) { if (!mViewer->gotoAnchor(aUrl.ref())) static_cast(mViewer->widget())->ensureVisible(0,0); return; } int id = msgPartFromUrl(aUrl); if (id > 0) { // clicked onto an attachment mAtmCurrent = id; mAtmCurrentName = aUrl.path(); slotAtmOpen(); } else { // if (aUrl.protocol().isEmpty() || (aUrl.protocol() == "file")) // return; emit urlClicked(aUrl,/* aButton*/LeftButton); //### FIXME: add button to URLArgs! } } //----------------------------------------------------------------------------- void KMReaderWin::slotUrlPopup(const QString &aUrl, const QPoint& aPos) { if (!mMsg) return; KURL url( aUrl ); int id = msgPartFromUrl(url); if (id <= 0) { emit popupMenu(*mMsg, url, aPos); } else { // Attachment popup mAtmCurrent = id; mAtmCurrentName = url.path(); KPopupMenu *menu = new KPopupMenu(); menu->insertItem(i18n("Open..."), this, SLOT(slotAtmOpen())); menu->insertItem(i18n("Open With..."), this, SLOT(slotAtmOpenWith())); menu->insertItem(i18n("View..."), this, SLOT(slotAtmView())); menu->insertItem(i18n("Save As..."), this, SLOT(slotAtmSave())); menu->insertItem(i18n("Properties..."), this, SLOT(slotAtmProperties())); menu->exec(aPos,0); delete menu; } } //----------------------------------------------------------------------------- void KMReaderWin::slotFind() { //dnaber: KAction *act = mViewer->actionCollection()->action("find"); if( act ) act->activate(); } //----------------------------------------------------------------------------- void KMReaderWin::slotToggleFixedFont() { mUseFixedFont = !mUseFixedFont; mBodyFamily = (mUseFixedFont) ? mFixedFont.family() : mBodyFont.family(); fntSize = (mUseFixedFont) ? mFixedFont.pointSize() : mBodyFont.pointSize(); mViewer->setStandardFont(mBodyFamily); update(true); } //----------------------------------------------------------------------------- void KMReaderWin::atmViewMsg(KMMessagePart* aMsgPart) { KMMessage* msg = new KMMessage; assert(aMsgPart!=NULL); msg->fromString(aMsgPart->bodyDecoded()); emit showAtmMsg(msg); } //----------------------------------------------------------------------------- void KMReaderWin::atmView(KMReaderWin* aReaderWin, KMMessagePart* aMsgPart, bool aHTML, const QString& aFileName, const QString& pname, QTextCodec *codec) { QString str; if (aReaderWin && qstricmp(aMsgPart->typeStr(), "message")==0) { aReaderWin->atmViewMsg(aMsgPart); return; } kernel->kbp()->busy(); { KMReaderWin* win = new KMReaderWin; //new reader if (qstricmp(aMsgPart->typeStr(), "message")==0) { // if called from compose win KMMessage* msg = new KMMessage; assert(aMsgPart!=NULL); msg->fromString(aMsgPart->bodyDecoded()); win->setCaption(msg->subject()); win->setMsg(msg, true); win->show(); } else if (qstricmp(aMsgPart->typeStr(), "text")==0) { if (qstricmp(aMsgPart->subtypeStr(), "x-vcard") == 0) { KMDisplayVCard *vcdlg; int vcerr; VCard *vc = VCard::parseVCard(codec->toUnicode(aMsgPart ->bodyDecoded()), &vcerr); if (!vc) { QString errstring = i18n("Error reading in vCard:\n"); errstring += VCard::getError(vcerr); KMessageBox::error(NULL, errstring, i18n("vCard error")); return; } vcdlg = new KMDisplayVCard(vc); kernel->kbp()->idle(); vcdlg->show(); return; } win->readConfig(); if ( codec ) win->setCodec( codec ); else win->setCodec( KGlobal::charsets()->codecForName( "iso8859-1" ) ); win->mViewer->begin( KURL( "file:/" ) ); win->queueHtml("\n" "c1.name()) + QString(" bgcolor=\"%1\"").arg(win->c4.name()) + ">" ); QCString str = aMsgPart->bodyDecoded(); if (aHTML && (qstricmp(aMsgPart->subtypeStr(), "html")==0)) // HTML //win->mViewer->write(win->codec()->toUnicode(str)); win->writeHTMLStr(win->codec()->toUnicode(str)); else // plain text win->writeBodyStr(str, win->codec()); win->queueHtml(""); win->sendNextHtmlChunk(); win->setCaption(i18n("View Attachment: ") + pname); win->show(); } else if (qstricmp(aMsgPart->typeStr(), "image")==0) { if (aFileName.isEmpty()) return; // prevent crash // Open the window with a size so the image fits in (if possible): QImageIO *iio = new QImageIO(); iio->setFileName(aFileName); if( iio->read() ) { QImage img = iio->image(); int scnum = QApplication::desktop()->screenNumber(win); if( img.width() > 50 && img.width() > 50 // avoid super small windows && img.width() < QApplication::desktop()->screen(scnum)->width() // avoid super large windows && img.height() < QApplication::desktop()->screen(scnum)->height() ) { win->resize(img.width()+10, img.height()+10); } } // Just write the img tag to HTML: win->mViewer->begin( KURL( "file:/" ) ); win->mViewer->write("\n"); QString linkName = QString("").arg(aFileName); win->mViewer->write(linkName); win->mViewer->write(""); win->mViewer->end(); win->setCaption(i18n("View Attachment: ") + pname); win->show(); } else { QMultiLineEdit *medit = new QMultiLineEdit(); QString str = aMsgPart->bodyDecoded(); // A QString cannot handle binary data. So if it's shorter than the // attachment, we assume the attachment is binary: if( str.length() < (unsigned) aMsgPart->decodedSize() ) { str += i18n("\n[KMail: Attachment contains binary data. Trying to show first %1 characters.]").arg(str.length()); } medit->setText(str); medit->setReadOnly(true); medit->resize(500, 550); medit->show(); } } // ---Sven's view text, html and image attachments in html widget end --- kernel->kbp()->idle(); } //----------------------------------------------------------------------------- void KMReaderWin::slotAtmView() { // KMMessagePart msgPart; // mMsg->bodyPart(mAtmCurrent, &msgPart); partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0; if( node ) { KMMessagePart& msgPart = node->msgPart(); QString pname = msgPart.fileName(); if (pname.isEmpty()) pname=msgPart.name(); if (pname.isEmpty()) pname=msgPart.contentDescription(); if (pname.isEmpty()) pname="unnamed"; // image Attachment is saved already QTextCodec *atmCodec = (mAutoDetectEncoding) ? KMMsgBase::codecForName(msgPart.charset()) : mCodec; if (!atmCodec) atmCodec = mCodec; atmView(this, &msgPart, htmlMail(), mAtmCurrentName, pname, atmCodec); } } //----------------------------------------------------------------------------- void KMReaderWin::slotAtmOpen() { QString str, pname, cmd, fileName; // KMMessagePart msgPart; // mMsg->bodyPart(mAtmCurrent, &msgPart); partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0; if( !node ) return; KMMessagePart& msgPart = node->msgPart(); if (qstricmp(msgPart.typeStr(), "message")==0) { atmViewMsg(&msgPart); return; } if (qstricmp(msgPart.typeStr(), "text") == 0) { if (qstricmp(msgPart.subtypeStr(), "x-vcard") == 0) { KMDisplayVCard *vcdlg; int vcerr; QTextCodec *atmCodec = (mAutoDetectEncoding) ? KMMsgBase::codecForName(msgPart.charset()) : mCodec; if (!atmCodec) atmCodec = mCodec; VCard *vc = VCard::parseVCard(atmCodec->toUnicode(msgPart .bodyDecoded()), &vcerr); if (!vc) { QString errstring = i18n("Error reading in vCard:\n"); errstring += VCard::getError(vcerr); KMessageBox::error(this, errstring, i18n("vCard error")); return; } vcdlg = new KMDisplayVCard(vc); vcdlg->show(); return; } } // What to do when user clicks on an attachment --dnaber, 2000-06-01 // TODO: show full path for Service, not only name QString mimetype = KMimeType::findByURL(KURL(mAtmCurrentName))->name(); KService::Ptr offer = KServiceTypeProfile::preferredService(mimetype, "Application"); QString question; QString open_text = i18n("&Open"); QString filenameText = msgPart.fileName(); if (filenameText.isEmpty()) filenameText = msgPart.name(); if ( offer ) { question = i18n("Open attachment '%1' using '%2'?").arg(filenameText) .arg(offer->name()); } else { question = i18n("Open attachment '%1'?").arg(filenameText); open_text = i18n("&Open With..."); } question += i18n("\n\nNote that opening an attachment may compromise your system's security!"); // TODO: buttons don't have the correct order, but "Save" should be default int choice = KMessageBox::warningYesNoCancel(this, question, i18n("Open Attachment?"), i18n("&Save to Disk"), open_text); if( choice == KMessageBox::Yes ) { // Save slotAtmSave(); } else if( choice == KMessageBox::No ) { // Open if ( offer ) { // There's a default service for this kind of file - use it KURL::List lst; KURL url; url.setPath(mAtmCurrentName); lst.append(url); KRun::run(*offer, lst); } else { // There's no know service that handles this type of file, so open // the "Open with..." dialog. KURL::List lst; KURL url; url.setPath(mAtmCurrentName); lst.append(url); KRun::displayOpenWithDialog(lst); } } else { // Cancel kdDebug(5006) << "Canceled opening attachment" << endl; } } //----------------------------------------------------------------------------- void KMReaderWin::slotAtmOpenWith() { // It makes sense to have an extra "Open with..." entry in the menu // so the user can change filetype associations. // KMMessagePart msgPart; // mMsg->bodyPart(mAtmCurrent, &msgPart); /* partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0; if( node ) { KMMessagePart& msgPart = node->msgPart(); */ KURL::List lst; KURL url; url.setPath(mAtmCurrentName); lst.append(url); KRun::displayOpenWithDialog(lst); /* }*/ } //----------------------------------------------------------------------------- void KMReaderWin::slotAtmSave() { QString fileName; fileName = mSaveAttachDir; // KMMessagePart msgPart; // mMsg->bodyPart(mAtmCurrent, &msgPart); partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0; if( node ) { KMMessagePart& msgPart = node->msgPart(); if (!msgPart.fileName().isEmpty()) fileName.append(msgPart.fileName()); else fileName.append(msgPart.name()); KURL url = KFileDialog::getSaveURL( fileName, QString::null, this ); if( url.isEmpty() ) return; mSaveAttachDir = url.directory() + "/"; kernel->byteArrayToRemoteFile(msgPart.bodyDecodedBinary(), url); } } //----------------------------------------------------------------------------- void KMReaderWin::slotAtmProperties() { KMMsgPartDialogCompat dlg(0,TRUE); kernel->kbp()->busy(); // KMMessagePart msgPart; // mMsg->bodyPart(mAtmCurrent, &msgPart); partNode* node = mRootNode ? mRootNode->findId( mAtmCurrent ) : 0; if( node ) { KMMessagePart& msgPart = node->msgPart(); dlg.setMsgPart(&msgPart); kernel->kbp()->idle(); dlg.exec(); } } //----------------------------------------------------------------------------- void KMReaderWin::slotScrollUp() { static_cast(mViewer->widget())->scrollBy(0, -10); } //----------------------------------------------------------------------------- void KMReaderWin::slotScrollDown() { static_cast(mViewer->widget())->scrollBy(0, 10); } bool KMReaderWin::atBottom() const { const QScrollView *view = static_cast(mViewer->widget()); return view->contentsY() + view->visibleHeight() >= view->contentsHeight(); } //----------------------------------------------------------------------------- void KMReaderWin::slotJumpDown() { QScrollView *view = static_cast(mViewer->widget()); int offs = (view->clipper()->height() < 30) ? view->clipper()->height() : 30; view->scrollBy( 0, view->clipper()->height() - offs ); } //----------------------------------------------------------------------------- void KMReaderWin::slotScrollPrior() { static_cast(mViewer->widget())->scrollBy(0, -(int)(height()*0.8)); } //----------------------------------------------------------------------------- void KMReaderWin::slotScrollNext() { static_cast(mViewer->widget())->scrollBy(0, (int)(height()*0.8)); } //----------------------------------------------------------------------------- void KMReaderWin::slotDocumentChanged() { } //----------------------------------------------------------------------------- void KMReaderWin::slotTextSelected(bool) { QString temp = mViewer->selectedText(); kapp->clipboard()->setText(temp); } //----------------------------------------------------------------------------- void KMReaderWin::selectAll() { mViewer->selectAll(); } //----------------------------------------------------------------------------- QString KMReaderWin::copyText() { QString temp = mViewer->selectedText(); return temp; } //----------------------------------------------------------------------------- void KMReaderWin::slotDocumentDone() { // mSbVert->setValue(0); } //----------------------------------------------------------------------------- void KMReaderWin::setHtmlOverride(bool override) { mHtmlOverride = override; if (mMsg) mMsg->setDecodeHTML(htmlMail()); } //----------------------------------------------------------------------------- bool KMReaderWin::htmlMail() { return ((mHtmlMail && !mHtmlOverride) || (!mHtmlMail && mHtmlOverride)); } //----------------------------------------------------------------------------- #include "kmreaderwin.moc"