fix logic
[personal-kdelibs.git] / kdoctools / kio_help.cpp
blob489257fcd1d06fbf74bac9940219f315df352139
1 /* This file is part of the KDE libraries
2 Copyright (C) 2000 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
3 Copyright (C) 2001 Stephan Kulow <coolo@kde.org>
4 Copyright (C) 2003 Cornelius Schumacher <schumacher@kde.org>
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later versio
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
23 #include "kio_help.h"
25 #include <config.h>
27 #ifdef HAVE_SYS_TYPES_H
28 # include <sys/types.h>
29 #endif
30 #ifdef HAVE_SYS_STAT_H
31 # include <sys/stat.h>
32 #endif
34 #include <errno.h>
35 #include <fcntl.h>
36 #ifdef HAVE_STDIO_H
37 # include <stdio.h>
38 #endif
39 #ifdef HAVE_STDLIB_H
40 # include <stdlib.h>
41 #endif
43 #include <QtCore/QFileInfo>
44 #include <QtCore/QFile>
45 #include <QtCore/QTextStream>
46 #include <QtCore/QRegExp>
47 #include <QtCore/QTextCodec>
48 #include <QtGui/QTextDocument>
50 #include <kdebug.h>
51 #include <kde_file.h>
52 #include <kurl.h>
53 #include <kglobal.h>
54 #include <klocale.h>
55 #include <kstandarddirs.h>
56 #include <kcomponentdata.h>
58 #include <libxslt/xsltutils.h>
59 #include <libxslt/transform.h>
60 #include "xslt.h"
62 using namespace KIO;
64 QString HelpProtocol::langLookup(const QString &fname)
66 QStringList search;
68 // assemble the local search paths
69 const QStringList localDoc = KGlobal::dirs()->resourceDirs("html");
71 QStringList langs = KGlobal::locale()->languageList();
72 langs.append( "en" );
73 langs.removeAll( "C" );
75 // this is kind of compat hack as we install our docs in en/ but the
76 // default language is en_US
77 for (QStringList::Iterator it = langs.begin(); it != langs.end(); ++it)
78 if ( *it == "en_US" )
79 *it = "en";
81 // look up the different languages
82 int ldCount = localDoc.count();
83 for (int id=0; id < ldCount; id++)
85 QStringList::ConstIterator lang;
86 for (lang = langs.constBegin(); lang != langs.constEnd(); ++lang)
87 search.append(QString("%1%2/%3").arg(localDoc[id], *lang, fname));
90 // try to locate the file
91 for (QStringList::ConstIterator it = search.constBegin(); it != search.constEnd(); ++it)
93 kDebug( 7119 ) << "Looking for help in: " << *it;
95 QFileInfo info(*it);
96 if (info.exists() && info.isFile() && info.isReadable())
97 return *it;
99 if ( ( *it ).endsWith( ".html" ) )
101 QString file = (*it).left((*it).lastIndexOf('/')) + "/index.docbook";
102 kDebug( 7119 ) << "Looking for help in: " << file;
103 info.setFile(file);
104 if (info.exists() && info.isFile() && info.isReadable())
105 return *it;
110 return QString();
114 QString HelpProtocol::lookupFile(const QString &fname,
115 const QString &query, bool &redirect)
117 redirect = false;
119 const QString path = fname;
121 QString result = langLookup(path);
122 if (result.isEmpty())
124 result = langLookup(path+"/index.html");
125 if (!result.isEmpty())
127 KUrl red( "help:/" );
128 red.setPath( path + "/index.html" );
129 red.setQuery( query );
130 redirection(red);
131 kDebug( 7119 ) << "redirect to " << red.url();
132 redirect = true;
134 else
136 unicodeError( i18n("There is no documentation available for %1." , Qt::escape(path)) );
137 return QString();
139 } else
140 kDebug( 7119 ) << "result " << result;
142 return result;
146 void HelpProtocol::unicodeError( const QString &t )
148 data(fromUnicode( QString(
149 "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\"></head>\n"
150 "%2</html>" ).arg( QString( QTextCodec::codecForLocale()->name() ), t ) ) );
153 HelpProtocol *slave = 0;
155 HelpProtocol::HelpProtocol( bool ghelp, const QByteArray &pool, const QByteArray &app )
156 : SlaveBase( ghelp ? "ghelp" : "help", pool, app ), mGhelp( ghelp )
158 slave = this;
161 void HelpProtocol::get( const KUrl& url )
163 kDebug( 7119 ) << "get: path=" << url.path()
164 << " query=" << url.query() << endl;
166 bool redirect;
167 QString doc;
168 doc = url.path();
170 if ( !mGhelp ) {
171 if (doc.at(0) != '/')
172 doc = doc.prepend(QLatin1Char('/'));
174 if (doc.at(doc.length() - 1) == '/')
175 doc += "index.html";
178 infoMessage(i18n("Looking up correct file"));
180 if ( !mGhelp ) {
181 doc = lookupFile(doc, url.query(), redirect);
183 if (redirect)
185 finished();
186 return;
190 if (doc.isEmpty())
192 error( KIO::ERR_DOES_NOT_EXIST, url.url() );
193 return;
196 mimeType("text/html");
197 KUrl target;
198 target.setPath(doc);
199 if (url.hasHTMLRef())
200 target.setHTMLRef(url.htmlRef());
202 kDebug( 7119 ) << "target " << target.url();
204 QString file = target.path();
206 if ( mGhelp ) {
207 if ( !file.endsWith( QLatin1String( ".xml" ) ) ) {
208 get_file( target );
209 return;
211 } else {
212 QString docbook_file = file.left(file.lastIndexOf('/')) + "/index.docbook";
213 if (!KStandardDirs::exists(file)) {
214 file = docbook_file;
215 } else {
216 QFileInfo fi(file);
217 if (fi.isDir()) {
218 file = file + "/index.docbook";
219 } else {
220 if ( !file.endsWith( QLatin1String( ".html" ) ) || !compareTimeStamps( file, docbook_file ) ) {
221 get_file( target );
222 return;
223 } else
224 file = docbook_file;
229 infoMessage(i18n("Preparing document"));
231 if ( mGhelp ) {
232 QString xsl = "customization/kde-nochunk.xsl";
233 mParsed = transform(file, KStandardDirs::locate("dtd", xsl));
235 kDebug( 7119 ) << "parsed " << mParsed.length();
237 if (mParsed.isEmpty()) {
238 unicodeError( i18n( "The requested help file could not be parsed:<br />%1" , file ) );
239 } else {
240 int pos1 = mParsed.indexOf( "charset=" );
241 if ( pos1 > 0 ) {
242 int pos2 = mParsed.indexOf( '"', pos1 );
243 if ( pos2 > 0 ) {
244 mParsed.replace( pos1, pos2 - pos1, "charset=UTF-8" );
247 data( mParsed.toUtf8() );
249 } else {
251 kDebug( 7119 ) << "look for cache for " << file;
253 mParsed = lookForCache( file );
255 kDebug( 7119 ) << "cached parsed " << mParsed.length();
257 if ( mParsed.isEmpty() ) {
258 mParsed = transform(file, KStandardDirs::locate("dtd", "customization/kde-chunk.xsl"));
259 if ( !mParsed.isEmpty() ) {
260 infoMessage( i18n( "Saving to cache" ) );
261 #ifdef Q_WS_WIN
262 QFileInfo fi(file);
263 // make sure filenames do not contain the base path, otherwise
264 // accessing user data from another location invalids cached files
265 // Accessing user data under a different path is possible
266 // when using usb sticks - this may affect unix/mac systems also
267 QString cache = '/' + fi.absolutePath().remove(KStandardDirs::installPath("html"),Qt::CaseInsensitive).replace('/','_') + '_' + fi.baseName() + '.';
268 #else
269 QString cache = file.left( file.length() - 7 );
270 #endif
271 saveToCache( mParsed, KStandardDirs::locateLocal( "cache",
272 "kio_help" + cache +
273 "cache.bz2" ) );
275 } else infoMessage( i18n( "Using cached version" ) );
277 kDebug( 7119 ) << "parsed " << mParsed.length();
279 if (mParsed.isEmpty()) {
280 unicodeError( i18n( "The requested help file could not be parsed:<br />%1" , file ) );
281 } else {
282 QString query = url.query(), anchor;
284 // if we have a query, look if it contains an anchor
285 if (!query.isEmpty())
286 if (query.startsWith("?anchor=")) {
287 anchor = query.mid(8).toLower();
289 KUrl redirURL(url);
291 redirURL.setQuery(QString());
292 redirURL.setHTMLRef(anchor);
293 redirection(redirURL);
294 finished();
295 return;
297 if (anchor.isEmpty() && url.hasHTMLRef())
298 anchor = url.htmlRef();
300 kDebug( 7119 ) << "anchor: " << anchor;
302 if ( !anchor.isEmpty() )
304 int index = 0;
305 while ( true ) {
306 index = mParsed.indexOf( QRegExp( "<a name=" ), index);
307 if ( index == -1 ) {
308 kDebug( 7119 ) << "no anchor\n";
309 break; // use whatever is the target, most likely index.html
312 if ( mParsed.mid( index, 11 + anchor.length() ).toLower() ==
313 QString( "<a name=\"%1\">" ).arg( anchor ) )
315 index = mParsed.lastIndexOf( "<FILENAME filename=", index ) +
316 strlen( "<FILENAME filename=\"" );
317 QString filename=mParsed.mid( index, 2000 );
318 filename = filename.left( filename.indexOf( '\"' ) );
319 QString path = target.path();
320 path = path.left( path.lastIndexOf( '/' ) + 1) + filename;
321 kDebug( 7119 ) << "anchor found in " << path;
322 target.setPath( path );
323 break;
325 index++;
328 emitFile( target );
332 finished();
335 void HelpProtocol::emitFile( const KUrl& url )
337 infoMessage(i18n("Looking up section"));
339 QString filename = url.path().mid(url.path().lastIndexOf('/') + 1);
341 int index = mParsed.indexOf(QString("<FILENAME filename=\"%1\"").arg(filename));
342 if (index == -1) {
343 if ( filename == "index.html" ) {
344 data( fromUnicode( mParsed ) );
345 return;
348 unicodeError( i18n("Could not find filename %1 in %2.", filename, url.url() ) );
349 return;
352 QString filedata = splitOut(mParsed, index);
353 replaceCharsetHeader( filedata );
355 data( fromUnicode( filedata ) );
356 data( QByteArray() );
359 void HelpProtocol::mimetype( const KUrl &)
361 mimeType("text/html");
362 finished();
365 // Copied from kio_file to avoid redirects
367 #define MAX_IPC_SIZE (1024*32)
369 void HelpProtocol::get_file( const KUrl& url )
371 kDebug( 7119 ) << "get_file " << url.url();
373 #ifdef Q_WS_WIN
374 QFile f( url.path() );
375 if ( !f.exists() ) {
376 error( KIO::ERR_DOES_NOT_EXIST, url.url() );
377 return;
379 if ( !f.open(QIODevice::ReadOnly) ) {
380 error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
381 return;
383 int processed_size = 0;
384 totalSize( f.size() );
386 QByteArray array;
387 array.resize(MAX_IPC_SIZE);
389 while( 1 )
391 qint64 n = f.read(array.data(),array.size());
392 if (n == -1) {
393 error( KIO::ERR_COULD_NOT_READ, url.path());
394 f.close();
395 return;
397 if (n == 0)
398 break; // Finished
400 data( array );
402 processed_size += n;
403 processedSize( processed_size );
406 data( QByteArray() );
407 f.close();
409 processedSize( f.size() );
410 finished();
411 #else
412 QByteArray _path( QFile::encodeName(url.path()));
413 KDE_struct_stat buff;
414 if ( KDE_stat( _path.data(), &buff ) == -1 ) {
415 if ( errno == EACCES )
416 error( KIO::ERR_ACCESS_DENIED, url.url() );
417 else
418 error( KIO::ERR_DOES_NOT_EXIST, url.url() );
419 return;
422 if ( S_ISDIR( buff.st_mode ) ) {
423 error( KIO::ERR_IS_DIRECTORY, url.path() );
424 return;
426 if ( S_ISFIFO( buff.st_mode ) || S_ISSOCK ( buff.st_mode ) ) {
427 error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
428 return;
431 int fd = KDE_open( _path.data(), O_RDONLY);
432 if ( fd < 0 ) {
433 error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
434 return;
437 totalSize( buff.st_size );
438 int processed_size = 0;
440 char buffer[ MAX_IPC_SIZE ];
441 QByteArray array;
443 while( 1 )
445 int n = ::read( fd, buffer, MAX_IPC_SIZE );
446 if (n == -1)
448 if (errno == EINTR)
449 continue;
450 error( KIO::ERR_COULD_NOT_READ, url.path());
451 ::close(fd);
452 return;
454 if (n == 0)
455 break; // Finished
457 array = array.fromRawData(buffer, n);
458 data( array );
459 array = array.fromRawData(buffer, n);
461 processed_size += n;
462 processedSize( processed_size );
465 data( QByteArray() );
467 ::close( fd );
469 processedSize( buff.st_size );
471 finished();
472 #endif