/* This file is part of the KDE libraries Copyright (C) 2000 Matthias Hoelzer-Kluepfel Copyright (C) 2001 Stephan Kulow Copyright (C) 2003 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later versio This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "kio_help.h" #include "xslt.h" #include "xslt_help.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #include #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #include #include using namespace KIO; QString HelpProtocol::langLookup(const QString &fname) { QStringList search; // assemble the local search paths const QStringList localDoc = KGlobal::dirs()->resourceDirs("html"); QStringList langs = KGlobal::locale()->languageList(); langs.append( "en" ); langs.removeAll( "C" ); // this is kind of compat hack as we install our docs in en/ but the // default language is en_US for (QStringList::Iterator it = langs.begin(); it != langs.end(); ++it) if ( *it == "en_US" ) *it = "en"; // look up the different languages int ldCount = localDoc.count(); for (int id=0; id < ldCount; id++) { QStringList::ConstIterator lang; for (lang = langs.constBegin(); lang != langs.constEnd(); ++lang) search.append(QString("%1%2/%3").arg(localDoc[id], *lang, fname)); } // try to locate the file for (QStringList::ConstIterator it = search.constBegin(); it != search.constEnd(); ++it) { kDebug( 7119 ) << "Looking for help in: " << *it; QFileInfo info(*it); if (info.exists() && info.isFile() && info.isReadable()) return *it; if ( ( *it ).endsWith( QLatin1String(".html") ) ) { QString file = (*it).left((*it).lastIndexOf('/')) + "/index.docbook"; kDebug( 7119 ) << "Looking for help in: " << file; info.setFile(file); if (info.exists() && info.isFile() && info.isReadable()) return *it; } } return QString(); } QString HelpProtocol::lookupFile(const QString &fname, const QString &query, bool &redirect) { redirect = false; const QString path = fname; QString result = langLookup(path); if (result.isEmpty()) { result = langLookup(path+"/index.html"); if (!result.isEmpty()) { KUrl red( "help:/" ); red.setPath( path + "/index.html" ); red.setQuery( query ); redirection(red); kDebug( 7119 ) << "redirect to " << red.url(); redirect = true; } else { const QString documentationNotFound = "khelpcenter/documentationnotfound/index.html"; if (!langLookup(documentationNotFound).isEmpty()) { KUrl red; red.setProtocol("help"); red.setPath(documentationNotFound); red.setQuery(query); redirection(red); redirect = true; } else { unicodeError( i18n("There is no documentation available for %1." , Qt::escape(path)) ); return QString(); } } } else kDebug( 7119 ) << "result " << result; return result; } void HelpProtocol::unicodeError( const QString &t ) { #ifdef Q_WS_WIN QString encoding = "UTF-8"; #else QString encoding = QTextCodec::codecForLocale()->name(); #endif data(fromUnicode( QString( "\n" "%2" ).arg( encoding, Qt::escape(t) ) ) ); } HelpProtocol *slave = 0; HelpProtocol::HelpProtocol( bool ghelp, const QByteArray &pool, const QByteArray &app ) : SlaveBase( ghelp ? "ghelp" : "help", pool, app ), mGhelp( ghelp ) { slave = this; } void HelpProtocol::get( const KUrl& url ) { kDebug( 7119 ) << "path=" << url.path() << "query=" << url.query(); bool redirect; QString doc = QDir::cleanPath(url.path()); if (doc.contains("..")) { error( KIO::ERR_DOES_NOT_EXIST, url.url() ); return; } if ( !mGhelp ) { if (!doc.startsWith('/')) doc = doc.prepend(QLatin1Char('/')); if (doc.endsWith('/')) doc += "index.html"; } infoMessage(i18n("Looking up correct file")); if ( !mGhelp ) { doc = lookupFile(doc, url.query(), redirect); if (redirect) { finished(); return; } } if (doc.isEmpty()) { error( KIO::ERR_DOES_NOT_EXIST, url.url() ); return; } mimeType("text/html"); KUrl target; target.setPath(doc); if (url.hasHTMLRef()) target.setHTMLRef(url.htmlRef()); kDebug( 7119 ) << "target " << target.url(); QString file = target.scheme() == "file" ? target.toLocalFile() : target.path(); if ( mGhelp ) { if ( !file.endsWith( QLatin1String( ".xml" ) ) ) { get_file( target ); return; } } else { QString docbook_file = file.left(file.lastIndexOf('/')) + "/index.docbook"; if (!KStandardDirs::exists(file)) { file = docbook_file; } else { QFileInfo fi(file); if (fi.isDir()) { file = file + "/index.docbook"; } else { if ( !file.endsWith( QLatin1String( ".html" ) ) || !compareTimeStamps( file, docbook_file ) ) { get_file( target ); return; } else file = docbook_file; } } } infoMessage(i18n("Preparing document")); if ( mGhelp ) { QString xsl = "customization/kde-nochunk.xsl"; mParsed = transform(file, KStandardDirs::locate("dtd", xsl)); kDebug( 7119 ) << "parsed " << mParsed.length(); if (mParsed.isEmpty()) { unicodeError( i18n( "The requested help file could not be parsed:
%1" , file ) ); } else { int pos1 = mParsed.indexOf( "charset=" ); if ( pos1 > 0 ) { int pos2 = mParsed.indexOf( '"', pos1 ); if ( pos2 > 0 ) { mParsed.replace( pos1, pos2 - pos1, "charset=UTF-8" ); } } data( mParsed.toUtf8() ); } } else { kDebug( 7119 ) << "look for cache for " << file; mParsed = lookForCache( file ); kDebug( 7119 ) << "cached parsed " << mParsed.length(); if ( mParsed.isEmpty() ) { mParsed = transform(file, KStandardDirs::locate("dtd", "customization/kde-chunk.xsl")); if ( !mParsed.isEmpty() ) { infoMessage( i18n( "Saving to cache" ) ); #ifdef Q_WS_WIN QFileInfo fi(file); // make sure filenames do not contain the base path, otherwise // accessing user data from another location invalids cached files // Accessing user data under a different path is possible // when using usb sticks - this may affect unix/mac systems also QString cache = '/' + fi.absolutePath().remove(KStandardDirs::installPath("html"),Qt::CaseInsensitive).replace('/','_') + '_' + fi.baseName() + '.'; #else QString cache = file.left( file.length() - 7 ); #endif saveToCache( mParsed, KStandardDirs::locateLocal( "cache", "kio_help" + cache + "cache.bz2" ) ); } } else infoMessage( i18n( "Using cached version" ) ); kDebug( 7119 ) << "parsed " << mParsed.length(); if (mParsed.isEmpty()) { unicodeError( i18n( "The requested help file could not be parsed:
%1" , file ) ); } else { QString query = url.query(), anchor; // if we have a query, look if it contains an anchor if (!query.isEmpty()) if (query.startsWith(QLatin1String("?anchor="))) { anchor = query.mid(8).toLower(); KUrl redirURL(url); redirURL.setQuery(QString()); redirURL.setHTMLRef(anchor); redirection(redirURL); finished(); return; } if (anchor.isEmpty() && url.hasHTMLRef()) anchor = url.htmlRef(); kDebug( 7119 ) << "anchor: " << anchor; if ( !anchor.isEmpty() ) { int index = 0; while ( true ) { index = mParsed.indexOf( QRegExp( "" ).arg( anchor ) ) { index = mParsed.lastIndexOf( "