KDocTools
kio_help.cpp
Go to the documentation of this file.00001 #include <config.h>
00002
00003 #ifdef HAVE_SYS_TYPES_H
00004 # include <sys/types.h>
00005 #endif
00006 #ifdef HAVE_SYS_STAT_H
00007 # include <sys/stat.h>
00008 #endif
00009
00010 #include <errno.h>
00011 #include <fcntl.h>
00012 #ifdef HAVE_STDIO_H
00013 # include <stdio.h>
00014 #endif
00015 #ifdef HAVE_STDLIB_H
00016 # include <stdlib.h>
00017 #endif
00018
00019 #include <qvaluelist.h>
00020 #include <qfileinfo.h>
00021 #include <qfile.h>
00022 #include <qtextstream.h>
00023 #include <qregexp.h>
00024 #include <qtextcodec.h>
00025
00026 #include <kdebug.h>
00027 #include <kurl.h>
00028 #include <kglobal.h>
00029 #include <klocale.h>
00030 #include <kstandarddirs.h>
00031 #include <kinstance.h>
00032
00033 #include "kio_help.h"
00034 #include <libxslt/xsltutils.h>
00035 #include <libxslt/transform.h>
00036 #include "xslt.h"
00037
00038 using namespace KIO;
00039
00040 QString HelpProtocol::langLookup(const QString& fname)
00041 {
00042 QStringList search;
00043
00044
00045 const QStringList localDoc = KGlobal::dirs()->resourceDirs("html");
00046
00047 QStringList langs = KGlobal::locale()->languageList();
00048 langs.append( "en" );
00049 langs.remove( "C" );
00050
00051
00052
00053 for (QStringList::Iterator it = langs.begin(); it != langs.end(); ++it)
00054 if ( *it == "en_US" )
00055 *it = "en";
00056
00057
00058 int ldCount = localDoc.count();
00059 for (int id=0; id < ldCount; id++)
00060 {
00061 QStringList::ConstIterator lang;
00062 for (lang = langs.begin(); lang != langs.end(); ++lang)
00063 search.append(QString("%1%2/%3").arg(localDoc[id], *lang, fname));
00064 }
00065
00066
00067 QStringList::Iterator it;
00068 for (it = search.begin(); it != search.end(); ++it)
00069 {
00070 kdDebug( 7119 ) << "Looking for help in: " << *it << endl;
00071
00072 QFileInfo info(*it);
00073 if (info.exists() && info.isFile() && info.isReadable())
00074 return *it;
00075
00076 if ( ( *it ).right( 5 ) == ".html" )
00077 {
00078 QString file = (*it).left((*it).findRev('/')) + "/index.docbook";
00079 kdDebug( 7119 ) << "Looking for help in: " << file << endl;
00080 info.setFile(file);
00081 if (info.exists() && info.isFile() && info.isReadable())
00082 return *it;
00083 }
00084 }
00085
00086
00087 return QString::null;
00088 }
00089
00090
00091 QString HelpProtocol::lookupFile(const QString &fname,
00092 const QString &query, bool &redirect)
00093 {
00094 redirect = false;
00095
00096 QString path, result;
00097
00098 path = fname;
00099
00100 result = langLookup(path);
00101 if (result.isEmpty())
00102 {
00103 result = langLookup(path+"/index.html");
00104 if (!result.isEmpty())
00105 {
00106 KURL red( "help:/" );
00107 red.setPath( path + "/index.html" );
00108 red.setQuery( query );
00109 redirection(red);
00110 kdDebug( 7119 ) << "redirect to " << red.url() << endl;
00111 redirect = true;
00112 }
00113 else
00114 {
00115 unicodeError( i18n("There is no documentation available for %1." ).arg(path) );
00116 finished();
00117 return QString::null;
00118 }
00119 } else
00120 kdDebug( 7119 ) << "result " << result << endl;
00121
00122 return result;
00123 }
00124
00125
00126 void HelpProtocol::unicodeError( const QString &t )
00127 {
00128 data(fromUnicode( QString(
00129 "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\"></head>\n"
00130 "%2</html>" ).arg( QTextCodec::codecForLocale()->name() ).arg( t ) ) );
00131 }
00132
00133 HelpProtocol *slave = 0;
00134
00135 HelpProtocol::HelpProtocol( bool ghelp, const QCString &pool, const QCString &app )
00136 : SlaveBase( ghelp ? "ghelp" : "help", pool, app ), mGhelp( ghelp )
00137 {
00138 slave = this;
00139 }
00140
00141 void HelpProtocol::get( const KURL& url )
00142 {
00143 kdDebug( 7119 ) << "get: path=" << url.path()
00144 << " query=" << url.query() << endl;
00145
00146 bool redirect;
00147 QString doc;
00148 doc = url.path();
00149
00150 if ( !mGhelp ) {
00151 if (doc.at(0) != '/')
00152 doc = doc.prepend('/');
00153
00154 if (doc.at(doc.length() - 1) == '/')
00155 doc += "index.html";
00156 }
00157
00158 infoMessage(i18n("Looking up correct file"));
00159
00160 if ( !mGhelp ) {
00161 doc = lookupFile(doc, url.query(), redirect);
00162
00163 if (redirect)
00164 {
00165 finished();
00166 return;
00167 }
00168 }
00169
00170 if (doc.isEmpty())
00171 {
00172 error( KIO::ERR_DOES_NOT_EXIST, url.url() );
00173 return;
00174 }
00175
00176 mimeType("text/html");
00177 KURL target;
00178 target.setPath(doc);
00179 if (url.hasHTMLRef())
00180 target.setHTMLRef(url.htmlRef());
00181
00182 kdDebug( 7119 ) << "target " << target.url() << endl;
00183
00184 QString file = target.path();
00185
00186 if ( mGhelp ) {
00187 if ( file.right( 4 ) != ".xml" ) {
00188 get_file( target );
00189 return;
00190 }
00191 } else {
00192 QString docbook_file = file.left(file.findRev('/')) + "/index.docbook";
00193 if (!KStandardDirs::exists(file)) {
00194 file = docbook_file;
00195 } else {
00196 QFileInfo fi(file);
00197 if (fi.isDir()) {
00198 file = file + "/index.docbook";
00199 } else {
00200 if ( file.right( 5 ) != ".html" || !compareTimeStamps( file, docbook_file ) ) {
00201 get_file( target );
00202 return;
00203 } else
00204 file = docbook_file;
00205 }
00206 }
00207 }
00208
00209 infoMessage(i18n("Preparing document"));
00210
00211 if ( mGhelp ) {
00212 QString xsl = "customization/kde-nochunk.xsl";
00213 mParsed = transform(file, locate("dtd", xsl));
00214
00215 kdDebug( 7119 ) << "parsed " << mParsed.length() << endl;
00216
00217 if (mParsed.isEmpty()) {
00218 unicodeError( i18n( "The requested help file could not be parsed:<br>%1" ).arg( file ) );
00219 } else {
00220 int pos1 = mParsed.find( "charset=" );
00221 if ( pos1 > 0 ) {
00222 int pos2 = mParsed.find( '"', pos1 );
00223 if ( pos2 > 0 ) {
00224 mParsed.replace( pos1, pos2 - pos1, "charset=UTF-8" );
00225 }
00226 }
00227 data( mParsed.utf8() );
00228 }
00229 } else {
00230
00231 kdDebug( 7119 ) << "look for cache for " << file << endl;
00232
00233 mParsed = lookForCache( file );
00234
00235 kdDebug( 7119 ) << "cached parsed " << mParsed.length() << endl;
00236
00237 if ( mParsed.isEmpty() ) {
00238 mParsed = transform(file, locate("dtd", "customization/kde-chunk.xsl"));
00239 if ( !mParsed.isEmpty() ) {
00240 infoMessage( i18n( "Saving to cache" ) );
00241 QString cache = file.left( file.length() - 7 );
00242 saveToCache( mParsed, locateLocal( "cache",
00243 "kio_help" + cache +
00244 "cache.bz2" ) );
00245 }
00246 } else infoMessage( i18n( "Using cached version" ) );
00247
00248 kdDebug( 7119 ) << "parsed " << mParsed.length() << endl;
00249
00250 if (mParsed.isEmpty()) {
00251 unicodeError( i18n( "The requested help file could not be parsed:<br>%1" ).arg( file ) );
00252 } else {
00253 QString query = url.query(), anchor;
00254
00255
00256 if (!query.isEmpty())
00257 if (query.left(8) == "?anchor=") {
00258 anchor = query.mid(8).lower();
00259
00260 KURL redirURL(url);
00261
00262 redirURL.setQuery(QString::null);
00263 redirURL.setHTMLRef(anchor);
00264 redirection(redirURL);
00265 finished();
00266 return;
00267 }
00268 if (anchor.isEmpty() && url.hasHTMLRef())
00269 anchor = url.htmlRef();
00270
00271 kdDebug( 7119 ) << "anchor: " << anchor << endl;
00272
00273 if ( !anchor.isEmpty() )
00274 {
00275 int index = 0;
00276 while ( true ) {
00277 index = mParsed.find( QRegExp( "<a name=" ), index);
00278 if ( index == -1 ) {
00279 kdDebug( 7119 ) << "no anchor\n";
00280 break;
00281 }
00282
00283 if ( mParsed.mid( index, 11 + anchor.length() ).lower() ==
00284 QString( "<a name=\"%1\">" ).arg( anchor ) )
00285 {
00286 index = mParsed.findRev( "<FILENAME filename=", index ) +
00287 strlen( "<FILENAME filename=\"" );
00288 QString filename=mParsed.mid( index, 2000 );
00289 filename = filename.left( filename.find( '\"' ) );
00290 QString path = target.path();
00291 path = path.left( path.findRev( '/' ) + 1) + filename;
00292 kdDebug( 7119 ) << "anchor found in " << path <<endl;
00293 target.setPath( path );
00294 break;
00295 }
00296 index++;
00297 }
00298 }
00299 emitFile( target );
00300 }
00301 }
00302
00303 finished();
00304 }
00305
00306 void HelpProtocol::emitFile( const KURL& url )
00307 {
00308 infoMessage(i18n("Looking up section"));
00309
00310 QString filename = url.path().mid(url.path().findRev('/') + 1);
00311
00312 int index = mParsed.find(QString("<FILENAME filename=\"%1\"").arg(filename));
00313 if (index == -1) {
00314 if ( filename == "index.html" ) {
00315 data( fromUnicode( mParsed ) );
00316 return;
00317 }
00318
00319 unicodeError( i18n("Could not find filename %1 in %2.").arg(filename).arg( url.url() ) );
00320 return;
00321 }
00322
00323 QString filedata = splitOut(mParsed, index);
00324 replaceCharsetHeader( filedata );
00325
00326 data( fromUnicode( filedata ) );
00327 data( QByteArray() );
00328 }
00329
00330 void HelpProtocol::mimetype( const KURL &)
00331 {
00332 mimeType("text/html");
00333 finished();
00334 }
00335
00336
00337
00338 #define MAX_IPC_SIZE (1024*32)
00339
00340 void HelpProtocol::get_file( const KURL& url )
00341 {
00342 kdDebug( 7119 ) << "get_file " << url.url() << endl;
00343
00344 QCString _path( QFile::encodeName(url.path()));
00345 struct stat buff;
00346 if ( ::stat( _path.data(), &buff ) == -1 ) {
00347 if ( errno == EACCES )
00348 error( KIO::ERR_ACCESS_DENIED, url.path() );
00349 else
00350 error( KIO::ERR_DOES_NOT_EXIST, url.path() );
00351 return;
00352 }
00353
00354 if ( S_ISDIR( buff.st_mode ) ) {
00355 error( KIO::ERR_IS_DIRECTORY, url.path() );
00356 return;
00357 }
00358 if ( S_ISFIFO( buff.st_mode ) || S_ISSOCK ( buff.st_mode ) ) {
00359 error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
00360 return;
00361 }
00362
00363 int fd = open( _path.data(), O_RDONLY);
00364 if ( fd < 0 ) {
00365 error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
00366 return;
00367 }
00368
00369 totalSize( buff.st_size );
00370 int processed_size = 0;
00371
00372 char buffer[ MAX_IPC_SIZE ];
00373 QByteArray array;
00374
00375 while( 1 )
00376 {
00377 int n = ::read( fd, buffer, MAX_IPC_SIZE );
00378 if (n == -1)
00379 {
00380 if (errno == EINTR)
00381 continue;
00382 error( KIO::ERR_COULD_NOT_READ, url.path());
00383 close(fd);
00384 return;
00385 }
00386 if (n == 0)
00387 break;
00388
00389 array.setRawData(buffer, n);
00390 data( array );
00391 array.resetRawData(buffer, n);
00392
00393 processed_size += n;
00394 processedSize( processed_size );
00395 }
00396
00397 data( QByteArray() );
00398
00399 close( fd );
00400
00401 processedSize( buff.st_size );
00402
00403 finished();
00404 }