• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDocTools

kio_help.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2000 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
00003    Copyright (C) 2001 Stephan Kulow <coolo@kde.org>
00004    Copyright (C) 2003 Cornelius Schumacher <schumacher@kde.org>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later versio
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019    Boston, MA 02110-1301, USA.
00020 */
00021 
00022 
00023 #include <config.h>
00024 
00025 #include "kio_help.h"
00026 #include "xslt.h"
00027 #include "xslt_help.h"
00028 
00029 #include <kdebug.h>
00030 #include <kde_file.h>
00031 #include <kurl.h>
00032 #include <kglobal.h>
00033 #include <klocale.h>
00034 #include <kstandarddirs.h>
00035 #include <kcomponentdata.h>
00036 
00037 #include <QtCore/QDir>
00038 #include <QtCore/QFileInfo>
00039 #include <QtCore/QFile>
00040 #include <QtCore/QRegExp>
00041 #include <QtCore/QTextCodec>
00042 #include <QtGui/QTextDocument>
00043 
00044 
00045 #ifdef HAVE_SYS_TYPES_H
00046 # include <sys/types.h>
00047 #endif
00048 #ifdef HAVE_SYS_STAT_H
00049 # include <sys/stat.h>
00050 #endif
00051 
00052 #include <errno.h>
00053 #include <fcntl.h>
00054 #ifdef HAVE_STDIO_H
00055 # include <stdio.h>
00056 #endif
00057 #ifdef HAVE_STDLIB_H
00058 # include <stdlib.h>
00059 #endif
00060 
00061 #include <libxslt/xsltutils.h>
00062 #include <libxslt/transform.h>
00063 
00064 using namespace KIO;
00065 
00066 QString HelpProtocol::langLookup(const QString &fname)
00067 {
00068     QStringList search;
00069 
00070     // assemble the local search paths
00071     const QStringList localDoc = KGlobal::dirs()->resourceDirs("html");
00072 
00073     QStringList langs = KGlobal::locale()->languageList();
00074     langs.append( "en" );
00075     langs.removeAll( "C" );
00076 
00077     // this is kind of compat hack as we install our docs in en/ but the
00078     // default language is en_US
00079     for (QStringList::Iterator it = langs.begin(); it != langs.end(); ++it)
00080         if ( *it == "en_US" )
00081             *it = "en";
00082 
00083     // look up the different languages
00084     int ldCount = localDoc.count();
00085     for (int id=0; id < ldCount; id++)
00086     {
00087         QStringList::ConstIterator lang;
00088         for (lang = langs.constBegin(); lang != langs.constEnd(); ++lang)
00089             search.append(QString("%1%2/%3").arg(localDoc[id], *lang, fname));
00090     }
00091 
00092     // try to locate the file
00093     for (QStringList::ConstIterator it = search.constBegin(); it != search.constEnd(); ++it)
00094     {
00095         kDebug( 7119 ) << "Looking for help in: " << *it;
00096 
00097         QFileInfo info(*it);
00098         if (info.exists() && info.isFile() && info.isReadable())
00099             return *it;
00100 
00101         if ( ( *it ).endsWith( QLatin1String(".html") ) )
00102         {
00103             QString file = (*it).left((*it).lastIndexOf('/')) + "/index.docbook";
00104             kDebug( 7119 ) << "Looking for help in: " << file;
00105             info.setFile(file);
00106             if (info.exists() && info.isFile() && info.isReadable())
00107                 return *it;
00108         }
00109     }
00110 
00111 
00112     return QString();
00113 }
00114 
00115 
00116 QString HelpProtocol::lookupFile(const QString &fname,
00117                                  const QString &query, bool &redirect)
00118 {
00119     redirect = false;
00120 
00121     const QString path = fname;
00122 
00123     QString result = langLookup(path);
00124     if (result.isEmpty())
00125     {
00126         result = langLookup(path+"/index.html");
00127         if (!result.isEmpty())
00128     {
00129             KUrl red( "help:/" );
00130             red.setPath( path + "/index.html" );
00131             red.setQuery( query );
00132             redirection(red);
00133             kDebug( 7119 ) << "redirect to " << red.url();
00134             redirect = true;
00135     }
00136         else
00137         {
00138             const QString documentationNotFound = "khelpcenter/documentationnotfound/index.html";
00139             if (!langLookup(documentationNotFound).isEmpty())
00140             {
00141                 KUrl red;
00142                 red.setProtocol("help");
00143                 red.setPath(documentationNotFound);
00144                 red.setQuery(query);
00145                 redirection(red);
00146                 redirect = true;
00147             }
00148             else
00149             {
00150                 unicodeError( i18n("There is no documentation available for %1." , Qt::escape(path)) );
00151                 return QString();
00152             }
00153         }
00154     } else
00155         kDebug( 7119 ) << "result " << result;
00156 
00157     return result;
00158 }
00159 
00160 
00161 void HelpProtocol::unicodeError( const QString &t )
00162 {
00163 #ifdef Q_WS_WIN
00164    QString encoding = "UTF-8";
00165 #else
00166    QString encoding = QTextCodec::codecForLocale()->name();
00167 #endif   
00168    data(fromUnicode( QString(
00169         "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\"></head>\n"
00170         "%2</html>" ).arg( encoding, Qt::escape(t) ) ) );
00171      
00172 }
00173 
00174 HelpProtocol *slave = 0;
00175 
00176 HelpProtocol::HelpProtocol( bool ghelp, const QByteArray &pool, const QByteArray &app )
00177   : SlaveBase( ghelp ? "ghelp" : "help", pool, app ), mGhelp( ghelp )
00178 {
00179     slave = this;
00180 }
00181 
00182 void HelpProtocol::get( const KUrl& url )
00183 {
00184     kDebug( 7119 ) << "path=" << url.path()
00185                    << "query=" << url.query();
00186 
00187     bool redirect;
00188     QString doc = QDir::cleanPath(url.path());
00189     if (doc.contains("..")) {
00190         error( KIO::ERR_DOES_NOT_EXIST, url.url() );
00191         return;
00192     }
00193 
00194     if ( !mGhelp ) {
00195         if (!doc.startsWith('/'))
00196             doc = doc.prepend(QLatin1Char('/'));
00197 
00198         if (doc.endsWith('/'))
00199             doc += "index.html";
00200     }
00201 
00202     infoMessage(i18n("Looking up correct file"));
00203 
00204     if ( !mGhelp ) {
00205       doc = lookupFile(doc, url.query(), redirect);
00206 
00207       if (redirect)
00208       {
00209           finished();
00210           return;
00211       }
00212     }
00213 
00214     if (doc.isEmpty())
00215     {
00216         error( KIO::ERR_DOES_NOT_EXIST, url.url() );
00217         return;
00218     }
00219 
00220     mimeType("text/html");
00221     KUrl target;
00222     target.setPath(doc);
00223     if (url.hasHTMLRef())
00224         target.setHTMLRef(url.htmlRef());
00225 
00226     kDebug( 7119 ) << "target " << target.url();
00227 
00228     QString file = target.scheme() == "file" ? target.toLocalFile() : target.path();
00229     
00230     if ( mGhelp ) {
00231       if ( !file.endsWith( QLatin1String( ".xml" ) ) ) {
00232          get_file( target );
00233          return;
00234       }
00235     } else {
00236         QString docbook_file = file.left(file.lastIndexOf('/')) + "/index.docbook";
00237         if (!KStandardDirs::exists(file)) {
00238             file = docbook_file;
00239         } else {
00240             QFileInfo fi(file);
00241             if (fi.isDir()) {
00242                 file = file + "/index.docbook";
00243             } else {
00244                 if ( !file.endsWith( QLatin1String( ".html" ) ) || !compareTimeStamps( file, docbook_file ) ) {
00245                     get_file( target );
00246                     return;
00247                 } else
00248                     file = docbook_file;
00249             }
00250         }
00251     }
00252 
00253     infoMessage(i18n("Preparing document"));
00254 
00255     if ( mGhelp ) {
00256         QString xsl = "customization/kde-nochunk.xsl";
00257         mParsed = transform(file, KStandardDirs::locate("dtd", xsl));
00258 
00259         kDebug( 7119 ) << "parsed " << mParsed.length();
00260 
00261         if (mParsed.isEmpty()) {
00262             unicodeError( i18n( "The requested help file could not be parsed:<br />%1" ,  file ) );
00263         } else {
00264             int pos1 = mParsed.indexOf( "charset=" );
00265             if ( pos1 > 0 ) {
00266               int pos2 = mParsed.indexOf( '"', pos1 );
00267               if ( pos2 > 0 ) {
00268                 mParsed.replace( pos1, pos2 - pos1, "charset=UTF-8" );
00269               }
00270             }
00271             data( mParsed.toUtf8() );
00272         }
00273     } else {
00274 
00275         kDebug( 7119 ) << "look for cache for " << file;
00276 
00277         mParsed = lookForCache( file );
00278 
00279         kDebug( 7119 ) << "cached parsed " << mParsed.length();
00280 
00281         if ( mParsed.isEmpty() ) {
00282             mParsed = transform(file, KStandardDirs::locate("dtd", "customization/kde-chunk.xsl"));
00283             if ( !mParsed.isEmpty() ) {
00284                 infoMessage( i18n( "Saving to cache" ) );
00285 #ifdef Q_WS_WIN
00286                 QFileInfo fi(file);
00287                 // make sure filenames do not contain the base path, otherwise
00288                 // accessing user data from another location invalids cached files
00289                 // Accessing user data under a different path is possible
00290                 // when using usb sticks - this may affect unix/mac systems also
00291                 QString cache = '/' + fi.absolutePath().remove(KStandardDirs::installPath("html"),Qt::CaseInsensitive).replace('/','_') + '_' + fi.baseName() + '.';
00292 #else
00293                 QString cache = file.left( file.length() - 7 );
00294 #endif
00295                 saveToCache( mParsed, KStandardDirs::locateLocal( "cache",
00296                                                                   "kio_help" + cache +
00297                                                                   "cache.bz2" ) );
00298             }
00299         } else infoMessage( i18n( "Using cached version" ) );
00300 
00301         kDebug( 7119 ) << "parsed " << mParsed.length();
00302 
00303         if (mParsed.isEmpty()) {
00304             unicodeError( i18n( "The requested help file could not be parsed:<br />%1" ,  file ) );
00305         } else {
00306             QString query = url.query(), anchor;
00307 
00308             // if we have a query, look if it contains an anchor
00309             if (!query.isEmpty())
00310                 if (query.startsWith(QLatin1String("?anchor="))) {
00311                     anchor = query.mid(8).toLower();
00312 
00313                 KUrl redirURL(url);
00314 
00315                 redirURL.setQuery(QString());
00316                 redirURL.setHTMLRef(anchor);
00317                 redirection(redirURL);
00318                 finished();
00319                 return;
00320             }
00321             if (anchor.isEmpty() && url.hasHTMLRef())
00322             anchor = url.htmlRef();
00323 
00324             kDebug( 7119 ) << "anchor: " << anchor;
00325 
00326             if ( !anchor.isEmpty() )
00327             {
00328                 int index = 0;
00329                 while ( true ) {
00330                     index = mParsed.indexOf( QRegExp( "<a name=" ), index);
00331                     if ( index == -1 ) {
00332                         kDebug( 7119 ) << "no anchor\n";
00333                         break; // use whatever is the target, most likely index.html
00334                     }
00335 
00336                     if ( mParsed.mid( index, 11 + anchor.length() ).toLower() ==
00337                          QString( "<a name=\"%1\">" ).arg( anchor ) )
00338                     {
00339                         index = mParsed.lastIndexOf( "<FILENAME filename=", index ) +
00340                                  strlen( "<FILENAME filename=\"" );
00341                         QString filename=mParsed.mid( index, 2000 );
00342                         filename = filename.left( filename.indexOf( '\"' ) );
00343                         QString path = target.path();
00344                         path = path.left( path.lastIndexOf( '/' ) + 1) + filename;
00345                         target.setPath( path );
00346                         kDebug( 7119 ) << "anchor found in " << target.url();
00347                         break;
00348                     }
00349                     index++;
00350                 }
00351             }
00352             emitFile( target );
00353         }
00354     }
00355 
00356     finished();
00357 }
00358 
00359 void HelpProtocol::emitFile( const KUrl& url )
00360 {
00361     infoMessage(i18n("Looking up section"));
00362 
00363     QString filename = url.path().mid(url.path().lastIndexOf('/') + 1);
00364 
00365     int index = mParsed.indexOf(QString("<FILENAME filename=\"%1\"").arg(filename));
00366     if (index == -1) {
00367         if ( filename == "index.html" ) {
00368             data( fromUnicode( mParsed ) );
00369             return;
00370         }
00371 
00372         unicodeError( i18n("Could not find filename %1 in %2.", filename,  url.url() ) );
00373         return;
00374     }
00375 
00376     QString filedata = splitOut(mParsed, index);
00377     replaceCharsetHeader( filedata );
00378 
00379     data( fromUnicode( filedata ) );
00380     data( QByteArray() );
00381 }
00382 
00383 void HelpProtocol::mimetype( const KUrl &)
00384 {
00385     mimeType("text/html");
00386     finished();
00387 }
00388 
00389 // Copied from kio_file to avoid redirects
00390 
00391 #define MAX_IPC_SIZE (1024*32)
00392 
00393 void HelpProtocol::get_file( const KUrl& url )
00394 {
00395     kDebug( 7119 ) << "get_file " << url.url();
00396 
00397 #ifdef Q_WS_WIN
00398     QFile f( url.toLocalFile() );
00399     if ( !f.exists() ) {
00400         error( KIO::ERR_DOES_NOT_EXIST, url.url() );
00401         return;
00402     }
00403     if ( !f.open(QIODevice::ReadOnly) ) {
00404         error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
00405         return;
00406     }
00407     int processed_size = 0;
00408     totalSize( f.size() );
00409 
00410     QByteArray array;
00411     array.resize(MAX_IPC_SIZE);
00412 
00413     while( 1 )
00414     {
00415         qint64 n = f.read(array.data(),array.size());
00416         if (n == -1) {
00417             error( KIO::ERR_COULD_NOT_READ, url.path());
00418             f.close();
00419             return;
00420        }
00421        if (n == 0)
00422             break; // Finished
00423 
00424        data( array );
00425 
00426        processed_size += n;
00427        processedSize( processed_size );
00428     }
00429 
00430     data( QByteArray() );
00431     f.close();
00432 
00433     processedSize( f.size() );
00434     finished();
00435 #else
00436     QByteArray _path( QFile::encodeName(url.path()));
00437     KDE_struct_stat buff;
00438     if ( KDE_stat( _path.data(), &buff ) == -1 ) {
00439         if ( errno == EACCES )
00440            error( KIO::ERR_ACCESS_DENIED, url.url() );
00441         else
00442            error( KIO::ERR_DOES_NOT_EXIST, url.url() );
00443         return;
00444     }
00445 
00446     if ( S_ISDIR( buff.st_mode ) ) {
00447         error( KIO::ERR_IS_DIRECTORY, url.path() );
00448         return;
00449     }
00450     if ( S_ISFIFO( buff.st_mode ) || S_ISSOCK ( buff.st_mode ) ) {
00451         error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
00452         return;
00453     }
00454 
00455     int fd = KDE_open( _path.data(), O_RDONLY);
00456     if ( fd < 0 ) {
00457         error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
00458         return;
00459     }
00460 
00461     totalSize( buff.st_size );
00462     int processed_size = 0;
00463 
00464     char buffer[ MAX_IPC_SIZE ];
00465     QByteArray array;
00466 
00467     while( 1 )
00468     {
00469        int n = ::read( fd, buffer, MAX_IPC_SIZE );
00470        if (n == -1)
00471        {
00472           if (errno == EINTR)
00473               continue;
00474           error( KIO::ERR_COULD_NOT_READ, url.path());
00475           ::close(fd);
00476           return;
00477        }
00478        if (n == 0)
00479           break; // Finished
00480 
00481        array = array.fromRawData(buffer, n);
00482        data( array );
00483        array = array.fromRawData(buffer, n);
00484 
00485        processed_size += n;
00486        processedSize( processed_size );
00487     }
00488 
00489     data( QByteArray() );
00490 
00491     ::close( fd );
00492 
00493     processedSize( buff.st_size );
00494 
00495     finished();
00496 #endif
00497 }

KDocTools

Skip menu "KDocTools"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal