tdeio_help.cpp
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 // assemble the local search paths 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 // this is kind of compat hack as we install our docs in en/ but the 00052 // default language is en_US 00053 for (TQStringList::Iterator it = langs.begin(); it != langs.end(); ++it) 00054 if ( *it == "en_US" ) 00055 *it = "en"; 00056 00057 // look up the different languages 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 // try to locate the file 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 // if we have a query, look if it contains an anchor 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; // use whatever is the target, most likely index.html 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 // Copied from tdeio_file to avoid redirects 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; // Finished 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 }