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.
27 #ifdef HAVE_SYS_TYPES_H
28 # include <sys/types.h>
30 #ifdef HAVE_SYS_STAT_H
31 # include <sys/stat.h>
43 #include <QtCore/QFileInfo>
44 #include <QtCore/QFile>
45 #include <QtCore/QTextStream>
46 #include <QtCore/QRegExp>
47 #include <QtCore/QTextCodec>
48 #include <QtGui/QTextDocument>
55 #include <kstandarddirs.h>
56 #include <kcomponentdata.h>
58 #include <libxslt/xsltutils.h>
59 #include <libxslt/transform.h>
64 QString
HelpProtocol::langLookup(const QString
&fname
)
68 // assemble the local search paths
69 const QStringList localDoc
= KGlobal::dirs()->resourceDirs("html");
71 QStringList langs
= KGlobal::locale()->languageList();
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
)
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
;
96 if (info
.exists() && info
.isFile() && info
.isReadable())
99 if ( ( *it
).endsWith( ".html" ) )
101 QString file
= (*it
).left((*it
).lastIndexOf('/')) + "/index.docbook";
102 kDebug( 7119 ) << "Looking for help in: " << file
;
104 if (info
.exists() && info
.isFile() && info
.isReadable())
114 QString
HelpProtocol::lookupFile(const QString
&fname
,
115 const QString
&query
, bool &redirect
)
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
);
131 kDebug( 7119 ) << "redirect to " << red
.url();
136 unicodeError( i18n("There is no documentation available for %1." , Qt::escape(path
)) );
140 kDebug( 7119 ) << "result " << 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
)
161 void HelpProtocol::get( const KUrl
& url
)
163 kDebug( 7119 ) << "get: path=" << url
.path()
164 << " query=" << url
.query() << endl
;
171 if (doc
.at(0) != '/')
172 doc
= doc
.prepend(QLatin1Char('/'));
174 if (doc
.at(doc
.length() - 1) == '/')
178 infoMessage(i18n("Looking up correct file"));
181 doc
= lookupFile(doc
, url
.query(), redirect
);
192 error( KIO::ERR_DOES_NOT_EXIST
, url
.url() );
196 mimeType("text/html");
199 if (url
.hasHTMLRef())
200 target
.setHTMLRef(url
.htmlRef());
202 kDebug( 7119 ) << "target " << target
.url();
204 QString file
= target
.path();
207 if ( !file
.endsWith( QLatin1String( ".xml" ) ) ) {
212 QString docbook_file
= file
.left(file
.lastIndexOf('/')) + "/index.docbook";
213 if (!KStandardDirs::exists(file
)) {
218 file
= file
+ "/index.docbook";
220 if ( !file
.endsWith( QLatin1String( ".html" ) ) || !compareTimeStamps( file
, docbook_file
) ) {
229 infoMessage(i18n("Preparing document"));
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
) );
240 int pos1
= mParsed
.indexOf( "charset=" );
242 int pos2
= mParsed
.indexOf( '"', pos1
);
244 mParsed
.replace( pos1
, pos2
- pos1
, "charset=UTF-8" );
247 data( mParsed
.toUtf8() );
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" ) );
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() + '.';
269 QString cache
= file
.left( file
.length() - 7 );
271 saveToCache( mParsed
, KStandardDirs::locateLocal( "cache",
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
) );
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();
291 redirURL
.setQuery(QString());
292 redirURL
.setHTMLRef(anchor
);
293 redirection(redirURL
);
297 if (anchor
.isEmpty() && url
.hasHTMLRef())
298 anchor
= url
.htmlRef();
300 kDebug( 7119 ) << "anchor: " << anchor
;
302 if ( !anchor
.isEmpty() )
306 index
= mParsed
.indexOf( QRegExp( "<a name=" ), index
);
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
);
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
));
343 if ( filename
== "index.html" ) {
344 data( fromUnicode( mParsed
) );
348 unicodeError( i18n("Could not find filename %1 in %2.", filename
, url
.url() ) );
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");
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();
374 QFile
f( url
.path() );
376 error( KIO::ERR_DOES_NOT_EXIST
, url
.url() );
379 if ( !f
.open(QIODevice::ReadOnly
) ) {
380 error( KIO::ERR_CANNOT_OPEN_FOR_READING
, url
.path() );
383 int processed_size
= 0;
384 totalSize( f
.size() );
387 array
.resize(MAX_IPC_SIZE
);
391 qint64 n
= f
.read(array
.data(),array
.size());
393 error( KIO::ERR_COULD_NOT_READ
, url
.path());
403 processedSize( processed_size
);
406 data( QByteArray() );
409 processedSize( f
.size() );
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() );
418 error( KIO::ERR_DOES_NOT_EXIST
, url
.url() );
422 if ( S_ISDIR( buff
.st_mode
) ) {
423 error( KIO::ERR_IS_DIRECTORY
, url
.path() );
426 if ( S_ISFIFO( buff
.st_mode
) || S_ISSOCK ( buff
.st_mode
) ) {
427 error( KIO::ERR_CANNOT_OPEN_FOR_READING
, url
.path() );
431 int fd
= KDE_open( _path
.data(), O_RDONLY
);
433 error( KIO::ERR_CANNOT_OPEN_FOR_READING
, url
.path() );
437 totalSize( buff
.st_size
);
438 int processed_size
= 0;
440 char buffer
[ MAX_IPC_SIZE
];
445 int n
= ::read( fd
, buffer
, MAX_IPC_SIZE
);
450 error( KIO::ERR_COULD_NOT_READ
, url
.path());
457 array
= array
.fromRawData(buffer
, n
);
459 array
= array
.fromRawData(buffer
, n
);
462 processedSize( processed_size
);
465 data( QByteArray() );
469 processedSize( buff
.st_size
);