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