// 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 "kmversion.h" #include "kmglobal.h" #include "kbusyptr.h" #include "kfileio.h" #include "kmfolder.h" #include "kmmessage.h" #include "kmmsgpart.h" #include "kmmsgpartdlg.h" #include "kmreaderwin.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 const int KMReaderWin::delay = 150; //----------------------------------------------------------------------------- KMReaderWin::KMReaderWin(QWidget *aParent, const char *aName, int aFlags) :KMReaderWinInherited(aParent, aName, aFlags | Qt::WDestructiveClose) { mAutoDelete = false; mMsg = 0; mMsgBuf = 0; mMsgBufMD5 = ""; mMsgBufSize = -1; mVcnum = -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; removeTempFiles(); } //----------------------------------------------------------------------------- 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 str; if (mPrinting) str = QString(""); else str = QString("").arg( color.name() ); if( font.italic() ) { str += ""; } if( font.bold() ) { str += ""; } return( str ); } //----------------------------------------------------------------------------- void KMReaderWin::initHtmlWidget(void) { mBox = new QHBox(this); mColorBar = new QLabel(" ", mBox); mColorBar->setAlignment(Qt::AlignHCenter | Qt::AlignTop); mColorBar->setEraseColor( 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 && mMsg == aMsg) // return; kdDebug(5006) << "set Msg, force = " << force << endl; // connect to the updates if we have hancy headers mMsg = aMsg; 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("Maildir support"), I18N_NOOP("Distribution lists"), I18N_NOOP("SMTP authentication"), I18N_NOOP("SMTP over SSL/TLS"), I18N_NOOP("Pipelining for POP3 " "(faster mail download on slow responding networks)"), I18N_NOOP("Various improvements for IMAP"), I18N_NOOP("On-demand downloading or deleting without downloading " "of big mails on a POP3 server"), I18N_NOOP("Automatic configuration of POP3/IMAP/SMTP security features"), I18N_NOOP("Automatic encoding selection for outgoing mails"), I18N_NOOP("DIGEST-MD5 authentication"), I18N_NOOP("Per-identity sent-mail and drafts folders"), I18N_NOOP("Expiry of old messages"), I18N_NOOP("Hotkey to temporary switch to fixed width fonts"), I18N_NOOP("UTF-7 support"), I18N_NOOP("Automatic encryption using OpenPGP"), I18N_NOOP("Enhanced status reports for encrypted/signed messages"), }; 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.3").arg("2.2"); // 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 ); } } //----------------------------------------------------------------------------- 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; QString bkgrdStr = ""; if (mBackingPixmapOn) bkgrdStr = " background=\"file://" + mBackingPixmapStr + "\""; mViewer->begin( KURL( "file:/" ) ); QString type = mMsg->typeStr().lower(); if (mAutoDetectEncoding) { mCodec = 0; QCString encoding; if (type.find("text/") != -1) encoding = mMsg->charset(); else if (type.find("multipart/") != -1) { 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::parseMsg(KMMessage* aMsg) { removeTempFiles(); KMMessagePart msgPart; int i, numParts; QCString type, subtype, contDisp; QByteArray str; bool asIcon = false; inlineImage = false; VCard *vc; assert(aMsg!=NULL); type = aMsg->typeStr(); numParts = aMsg->numBodyParts(); // Hrm we have to iterate this twice with the current design. This // should really be fixed. (FIXME) mVcnum = -1; for (int j = 0; j < aMsg->numBodyParts(); j++) { aMsg->bodyPart(j, &msgPart); if (!qstricmp(msgPart.typeStr(), "text") && !qstricmp(msgPart.subtypeStr(), "x-vcard")) { int vcerr; QTextCodec *atmCodec = (mAutoDetectEncoding) ? KMMsgBase::codecForName(msgPart.charset()) : mCodec; if (!atmCodec) atmCodec = mCodec; vc = VCard::parseVCard(atmCodec->toUnicode(msgPart .bodyDecoded()), &vcerr); if (vc) { delete vc; kdDebug(5006) << "FOUND A VALID VCARD" << endl; mVcnum = j; writePartIcon(&msgPart, j, TRUE); break; } } } queueHtml("
    " + (writeMsgHeader()) + "
    "); 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() { QString vcname; QString dir = ( QApplication::reverseLayout() ? "RTL" : "LTR" ); QString headerStr = QString("
    ").arg(dir); if (mVcnum >= 0) vcname = mTempFiles.last(); switch (mHeaderStyle) { case HdrBrief: headerStr.append("" + strToHtml(mMsg->subject()) + "  (" + KMMessage::emailAddrAsAnchor(mMsg->from(),TRUE) + ", "); if (!mMsg->cc().isEmpty()) { headerStr.append(i18n("Cc: ")+ KMMessage::emailAddrAsAnchor(mMsg->cc(),TRUE) + ", "); } headerStr.append(" "+strToHtml(mMsg->dateShortStr()) + ")"); if (mVcnum >= 0) { headerStr.append("  "+i18n("[vCard]")+""); } headerStr.append("
    "); break; case HdrStandard: headerStr.append("" + strToHtml(mMsg->subject()) + "
    "); headerStr.append(i18n("From: ") + KMMessage::emailAddrAsAnchor(mMsg->from(),FALSE)); if (mVcnum >= 0) { 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("
    " "%5
    " "
    " "") .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((mVcnum < 0) ? "" : "  "+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(strToHtml(mMsg->dateStr()))); headerStr.append("
    %1%2%3%4
    %1%2
    %1%2
    %1%2
    "); break; } case HdrLong: headerStr.append("" + strToHtml(mMsg->subject()) + "
    "); headerStr.append(i18n("Date: ") + strToHtml(mMsg->dateStr())+"
    "); headerStr.append(i18n("From: ") + KMMessage::emailAddrAsAnchor(mMsg->from(),FALSE)); if (mVcnum >= 0) { 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 (mVcnum >= 0) { 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) { 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 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 += "" "" "
    "; 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( isEncrypted ) htmlStr += "
    " + i18n( "End of encrypted message" ) + "
    "; } 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; QString normalEndTag; QString quoteEnd(""); unsigned int pos, beg; unsigned int length = s.length(); enum { First, New, Mid } paragState = First; bool rightToLeft = false; if (mBodyFont.bold()) { normalStartTag += ""; normalEndTag += ""; } if (mBodyFont.italic()) { normalStartTag += ""; normalEndTag += ""; } // 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 ' ': case '\t': case '\r': break; default: finish = TRUE; break; } } /* for() */ if ( (paragState != Mid && finish) || (actQuoteLevel != currQuoteLevel) ) { if ( finish ) rightToLeft = line.isRightToLeft(); /* finish last quotelevel */ if (currQuoteLevel == -1) htmlStr.append( normalEndTag ); else htmlStr.append( quoteEnd ); if ( paragState == New ) htmlStr += ""; htmlStr += ( rightToLeft ? "
    " : "
    " ); /* start new quotelevel */ currQuoteLevel = actQuoteLevel; if (actQuoteLevel == -1) htmlStr += normalStartTag; else htmlStr += mQuoteFontTag[currQuoteLevel%3]; paragState = Mid; } if ( !finish && paragState == Mid ) paragState = New; line = strToHtml(line, TRUE); line.append("
    "); htmlStr.append(line); } /* while() */ /* really finish the last quotelevel */ if (currQuoteLevel == -1) htmlStr.append( normalEndTag ); else htmlStr.append( quoteEnd ); htmlStr += "
    "; 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 { QString str; QString result((QChar*)NULL, aStr.length() * 2); int pos; QChar ch; int i, x; bool startOfLine = true; for (pos = 0, x = 0; pos < (int)aStr.length(); pos++, x++) { ch = aStr[pos]; if (aPreserveBlanks) { if (ch==' ') { if (startOfLine) { result += " "; pos++, x++; startOfLine = false; } while (aStr[pos] == ' ') { result += " "; pos++, x++; if (aStr[pos] == ' ') { result += " "; pos++, x++; } } pos--, x--; continue; } else if (ch=='\t') { do { result += " "; x++; } while((x&7) != 0); x--; continue; } } if (ch=='\n') { result += "
    "; startOfLine = true; x = -1; continue; } startOfLine = false; if (ch=='&') result += "&"; else if (ch=='<') result += "<"; else if (ch=='>') result += ">"; else if ((ch=='h' && aStr.mid(pos, 7) == "http://") || (ch=='h' && aStr.mid(pos, 8) == "https://") || (ch=='f' && aStr.mid(pos, 6) == "ftp://") || (ch=='m' && aStr.mid(pos, 7) == "mailto:")) // note: no "file:" for security reasons { // handle cases like this: http://foobar.org/ for (i=0; pos < (int)aStr.length() && aStr[pos] > ' ' && aStr[pos] != '\"' && QString("<>()[]").find(aStr[pos]) == -1; i++, pos++) str[i] = aStr[pos]; pos--; while (i>0 && str[i-1].isPunct() && str[i-1]!='/' && str[i-1]!='#') { i--; pos--; } str.truncate(i); // don't create link if url is empty if( ( str == "http://" ) || ( str == "https://" ) || ( str == "ftp://" ) || ( str == "mailto" ) ) result += str; else result += "" + str + ""; } else if (ch=='@') { // the following characters are allowed in a dot-atom (RFC 2822): // a-z A-Z 0-9 . ! # $ % & ' * + - / = ? ^ _ ` { | } ~ const QString allowedSpecialChars = QString(".!#$%&'*+-/=?^_`{|}~"); QString iStr; int len; int atpos = pos; // determine the local part of the email address pos--; while (pos >= 0 && aStr[pos].unicode() < 128 && (aStr[pos].isLetterOrNumber() || allowedSpecialChars.find(aStr[pos]) != -1) && (atpos - pos) < 255) { pos--; } pos++; // we assume that an email address starts with a letter or a digit while (allowedSpecialChars.find(aStr[pos]) != -1) { pos++; } // len is the length of the local part len = atpos - pos; iStr = aStr.mid( pos, len + 1 ); kdDebug(5006) << "local part of email address is: \"" << iStr << "\"\n"; // remove the local part from the result (as '&'s have been expanded to // & we have to take care of the 4 additional characters per '&') result.truncate(result.length() - len - (iStr.contains('&')*4)); // determine the domain part of the email address pos = atpos + 1; while (pos < (int)aStr.length() && aStr[pos].unicode() < 128 && (aStr[pos].isLetterOrNumber() || allowedSpecialChars.find(aStr[pos]) != -1) && (pos - atpos + len) < 255) { pos++; } pos--; // we assume that an email address ends with a letter or a digit while (allowedSpecialChars.find(aStr[pos]) != -1) { pos--; } iStr += aStr.mid( atpos + 1, pos - atpos ); if (len > 0 && iStr[iStr.length() - 1] != '@') { iStr = "" + iStr + ""; } result += iStr; } else { result += ch; } } return result; } //----------------------------------------------------------------------------- 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) { KURL url(aUrl); int id = msgPartFromUrl(url); if (id < 0) { emit statusMsg(aUrl); } else { KMMessagePart msgPart; mMsg->bodyPart(id, &msgPart); QString str = msgPart.fileName(); if (str.isEmpty()) str = msgPart.name(); emit statusMsg(i18n("Attachment: ") + str); } } //----------------------------------------------------------------------------- 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(); if( img.width() > 50 && img.width() > 50 // avoid super small windows && img.width() < KApplication::desktop()->width() // avoid super large windows && img.height() < KApplication::desktop()->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); 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); 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); KURL::List lst; KURL url; url.setPath(mAtmCurrentName); lst.append(url); KRun::displayOpenWithDialog(lst); } //----------------------------------------------------------------------------- void KMReaderWin::slotAtmSave() { KMMessagePart msgPart; QString fileName; fileName = mSaveAttachDir; mMsg->bodyPart(mAtmCurrent, &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() { KMMessagePart msgPart; KMMsgPartDialogCompat dlg(0,TRUE); kernel->kbp()->busy(); mMsg->bodyPart(mAtmCurrent, &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"