You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
405 lines
11 KiB
405 lines
11 KiB
#include <config.h>
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
# include <sys/types.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_STAT_H
|
|
# include <sys/stat.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#ifdef HAVE_STDIO_H
|
|
# include <stdio.h>
|
|
#endif
|
|
#ifdef HAVE_STDLIB_H
|
|
# include <stdlib.h>
|
|
#endif
|
|
|
|
#include <tqvaluelist.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqfile.h>
|
|
#include <tqtextstream.h>
|
|
#include <tqregexp.h>
|
|
#include <tqtextcodec.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <kurl.h>
|
|
#include <kglobal.h>
|
|
#include <klocale.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kinstance.h>
|
|
|
|
#include "kio_help.h"
|
|
#include <libxslt/xsltutils.h>
|
|
#include <libxslt/transform.h>
|
|
#include "xslt.h"
|
|
|
|
using namespace TDEIO;
|
|
|
|
TQString HelpProtocol::langLookup(const TQString& fname)
|
|
{
|
|
TQStringList search;
|
|
|
|
// assemble the local search paths
|
|
const TQStringList localDoc = TDEGlobal::dirs()->resourceDirs("html") + TDEGlobal::dirs()->resourceDirs("html-bundle");
|
|
|
|
TQStringList langs = TDEGlobal::locale()->languageList();
|
|
langs.append( "en" );
|
|
langs.remove( "C" );
|
|
|
|
// this is kind of compat hack as we install our docs in en/ but the
|
|
// default language is en_US
|
|
for (TQStringList::Iterator it = langs.begin(); it != langs.end(); ++it)
|
|
if ( *it == "en_US" )
|
|
*it = "en";
|
|
|
|
// look up the different languages
|
|
int ldCount = localDoc.count();
|
|
for (int id=0; id < ldCount; id++)
|
|
{
|
|
TQStringList::ConstIterator lang;
|
|
for (lang = langs.begin(); lang != langs.end(); ++lang)
|
|
search.append(TQString("%1%2/%3").arg(localDoc[id], *lang, fname));
|
|
}
|
|
|
|
// try to locate the file
|
|
TQStringList::Iterator it;
|
|
for (it = search.begin(); it != search.end(); ++it)
|
|
{
|
|
kdDebug( 7119 ) << "Looking for help in: " << *it << endl;
|
|
|
|
TQFileInfo info(*it);
|
|
if (info.exists() && info.isFile() && info.isReadable())
|
|
return *it;
|
|
|
|
if ( ( *it ).right( 5 ) == ".html" )
|
|
{
|
|
TQString file = (*it).left((*it).findRev('/')) + "/index.docbook";
|
|
kdDebug( 7119 ) << "Looking for help in: " << file << endl;
|
|
info.setFile(file);
|
|
if (info.exists() && info.isFile() && info.isReadable())
|
|
return *it;
|
|
}
|
|
}
|
|
|
|
|
|
return TQString::null;
|
|
}
|
|
|
|
|
|
TQString HelpProtocol::lookupFile(const TQString &fname,
|
|
const TQString &query, bool &redirect)
|
|
{
|
|
redirect = false;
|
|
|
|
TQString path, result;
|
|
|
|
path = fname;
|
|
|
|
result = langLookup(path);
|
|
if (result.isEmpty())
|
|
{
|
|
result = langLookup(path+"/index.html");
|
|
if (!result.isEmpty())
|
|
{
|
|
KURL red( "help:/" );
|
|
red.setPath( path + "/index.html" );
|
|
red.setQuery( query );
|
|
redirection(red);
|
|
kdDebug( 7119 ) << "redirect to " << red.url() << endl;
|
|
redirect = true;
|
|
}
|
|
else
|
|
{
|
|
unicodeError( i18n("There is no documentation available for %1." ).arg(path) );
|
|
finished();
|
|
return TQString::null;
|
|
}
|
|
} else
|
|
kdDebug( 7119 ) << "result " << result << endl;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
void HelpProtocol::unicodeError( const TQString &t )
|
|
{
|
|
data(fromUnicode( TQString(
|
|
"<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\"></head>\n"
|
|
"%2</html>" ).arg( TQTextCodec::codecForLocale()->name() ).arg( t ) ) );
|
|
}
|
|
|
|
HelpProtocol *slave = 0;
|
|
|
|
HelpProtocol::HelpProtocol( bool ghelp, const TQCString &pool, const TQCString &app )
|
|
: SlaveBase( ghelp ? "ghelp" : "help", pool, app ), mGhelp( ghelp )
|
|
{
|
|
slave = this;
|
|
}
|
|
|
|
void HelpProtocol::get( const KURL& url )
|
|
{
|
|
kdDebug( 7119 ) << "get: path=" << url.path()
|
|
<< " query=" << url.query() << endl;
|
|
|
|
bool redirect;
|
|
TQString doc;
|
|
doc = url.path();
|
|
|
|
if ( !mGhelp ) {
|
|
if (doc.at(0) != '/')
|
|
doc = doc.prepend('/');
|
|
|
|
if (doc.at(doc.length() - 1) == '/')
|
|
doc += "index.html";
|
|
}
|
|
|
|
infoMessage(i18n("Looking up correct file"));
|
|
|
|
if ( !mGhelp ) {
|
|
doc = lookupFile(doc, url.query(), redirect);
|
|
|
|
if (redirect)
|
|
{
|
|
finished();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (doc.isEmpty())
|
|
{
|
|
error( TDEIO::ERR_DOES_NOT_EXIST, url.url() );
|
|
return;
|
|
}
|
|
|
|
mimeType("text/html");
|
|
KURL target;
|
|
target.setPath(doc);
|
|
if (url.hasHTMLRef())
|
|
target.setHTMLRef(url.htmlRef());
|
|
|
|
kdDebug( 7119 ) << "target " << target.url() << endl;
|
|
|
|
TQString file = target.path();
|
|
|
|
if ( mGhelp ) {
|
|
if ( file.right( 4 ) != ".xml" ) {
|
|
get_file( target );
|
|
return;
|
|
}
|
|
} else {
|
|
TQString docbook_file = file.left(file.findRev('/')) + "/index.docbook";
|
|
if (!KStandardDirs::exists(file)) {
|
|
file = docbook_file;
|
|
} else {
|
|
TQFileInfo fi(file);
|
|
if (fi.isDir()) {
|
|
file = file + "/index.docbook";
|
|
} else {
|
|
if ( file.right( 5 ) != ".html" || !compareTimeStamps( file, docbook_file ) ) {
|
|
get_file( target );
|
|
return;
|
|
} else
|
|
file = docbook_file;
|
|
}
|
|
}
|
|
}
|
|
|
|
infoMessage(i18n("Preparing document"));
|
|
|
|
if ( mGhelp ) {
|
|
TQString xsl = "customization/kde-nochunk.xsl";
|
|
mParsed = transform(file, locate("dtd", xsl));
|
|
|
|
kdDebug( 7119 ) << "parsed " << mParsed.length() << endl;
|
|
|
|
if (mParsed.isEmpty()) {
|
|
unicodeError( i18n( "The requested help file could not be parsed:<br>%1" ).arg( file ) );
|
|
} else {
|
|
int pos1 = mParsed.find( "charset=" );
|
|
if ( pos1 > 0 ) {
|
|
int pos2 = mParsed.find( '"', pos1 );
|
|
if ( pos2 > 0 ) {
|
|
mParsed.replace( pos1, pos2 - pos1, "charset=UTF-8" );
|
|
}
|
|
}
|
|
data( mParsed.utf8() );
|
|
}
|
|
} else {
|
|
|
|
kdDebug( 7119 ) << "look for cache for " << file << endl;
|
|
|
|
mParsed = lookForCache( file );
|
|
|
|
kdDebug( 7119 ) << "cached parsed " << mParsed.length() << endl;
|
|
|
|
if ( mParsed.isEmpty() ) {
|
|
mParsed = transform(file, locate("dtd", "customization/kde-chunk.xsl"));
|
|
if ( !mParsed.isEmpty() ) {
|
|
infoMessage( i18n( "Saving to cache" ) );
|
|
TQString cache = file.left( file.length() - 7 );
|
|
saveToCache( mParsed, locateLocal( "cache",
|
|
"kio_help" + cache +
|
|
"cache.bz2" ) );
|
|
}
|
|
} else infoMessage( i18n( "Using cached version" ) );
|
|
|
|
kdDebug( 7119 ) << "parsed " << mParsed.length() << endl;
|
|
|
|
if (mParsed.isEmpty()) {
|
|
unicodeError( i18n( "The requested help file could not be parsed:<br>%1" ).arg( file ) );
|
|
} else {
|
|
TQString query = url.query(), anchor;
|
|
|
|
// if we have a query, look if it contains an anchor
|
|
if (!query.isEmpty())
|
|
if (query.left(8) == "?anchor=") {
|
|
anchor = query.mid(8).lower();
|
|
|
|
KURL redirURL(url);
|
|
|
|
redirURL.setQuery(TQString::null);
|
|
redirURL.setHTMLRef(anchor);
|
|
redirection(redirURL);
|
|
finished();
|
|
return;
|
|
}
|
|
if (anchor.isEmpty() && url.hasHTMLRef())
|
|
anchor = url.htmlRef();
|
|
|
|
kdDebug( 7119 ) << "anchor: " << anchor << endl;
|
|
|
|
if ( !anchor.isEmpty() )
|
|
{
|
|
int index = 0;
|
|
while ( true ) {
|
|
index = mParsed.find( TQRegExp( "<a name=" ), index);
|
|
if ( index == -1 ) {
|
|
kdDebug( 7119 ) << "no anchor\n";
|
|
break; // use whatever is the target, most likely index.html
|
|
}
|
|
|
|
if ( mParsed.mid( index, 11 + anchor.length() ).lower() ==
|
|
TQString( "<a name=\"%1\">" ).arg( anchor ) )
|
|
{
|
|
index = mParsed.findRev( "<FILENAME filename=", index ) +
|
|
strlen( "<FILENAME filename=\"" );
|
|
TQString filename=mParsed.mid( index, 2000 );
|
|
filename = filename.left( filename.find( '\"' ) );
|
|
TQString path = target.path();
|
|
path = path.left( path.findRev( '/' ) + 1) + filename;
|
|
kdDebug( 7119 ) << "anchor found in " << path <<endl;
|
|
target.setPath( path );
|
|
break;
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
emitFile( target );
|
|
}
|
|
}
|
|
|
|
finished();
|
|
}
|
|
|
|
void HelpProtocol::emitFile( const KURL& url )
|
|
{
|
|
infoMessage(i18n("Looking up section"));
|
|
|
|
TQString filename = url.path().mid(url.path().findRev('/') + 1);
|
|
|
|
int index = mParsed.find(TQString("<FILENAME filename=\"%1\"").arg(filename));
|
|
if (index == -1) {
|
|
if ( filename == "index.html" ) {
|
|
data( fromUnicode( mParsed ) );
|
|
return;
|
|
}
|
|
|
|
unicodeError( i18n("Could not find filename %1 in %2.").arg(filename).arg( url.url() ) );
|
|
return;
|
|
}
|
|
|
|
TQString filedata = splitOut(mParsed, index);
|
|
replaceCharsetHeader( filedata );
|
|
|
|
data( fromUnicode( filedata ) );
|
|
data( TQByteArray() );
|
|
}
|
|
|
|
void HelpProtocol::mimetype( const KURL &)
|
|
{
|
|
mimeType("text/html");
|
|
finished();
|
|
}
|
|
|
|
// Copied from kio_file to avoid redirects
|
|
|
|
#define MAX_IPC_SIZE (1024*32)
|
|
|
|
void HelpProtocol::get_file( const KURL& url )
|
|
{
|
|
kdDebug( 7119 ) << "get_file " << url.url() << endl;
|
|
|
|
TQCString _path( TQFile::encodeName(url.path()));
|
|
struct stat buff;
|
|
if ( ::stat( _path.data(), &buff ) == -1 ) {
|
|
if ( errno == EACCES )
|
|
error( TDEIO::ERR_ACCESS_DENIED, url.path() );
|
|
else
|
|
error( TDEIO::ERR_DOES_NOT_EXIST, url.path() );
|
|
return;
|
|
}
|
|
|
|
if ( S_ISDIR( buff.st_mode ) ) {
|
|
error( TDEIO::ERR_IS_DIRECTORY, url.path() );
|
|
return;
|
|
}
|
|
if ( S_ISFIFO( buff.st_mode ) || S_ISSOCK ( buff.st_mode ) ) {
|
|
error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
|
|
return;
|
|
}
|
|
|
|
int fd = open( _path.data(), O_RDONLY);
|
|
if ( fd < 0 ) {
|
|
error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
|
|
return;
|
|
}
|
|
|
|
totalSize( buff.st_size );
|
|
int processed_size = 0;
|
|
|
|
char buffer[ MAX_IPC_SIZE ];
|
|
TQByteArray array;
|
|
|
|
while( 1 )
|
|
{
|
|
int n = ::read( fd, buffer, MAX_IPC_SIZE );
|
|
if (n == -1)
|
|
{
|
|
if (errno == EINTR)
|
|
continue;
|
|
error( TDEIO::ERR_COULD_NOT_READ, url.path());
|
|
close(fd);
|
|
return;
|
|
}
|
|
if (n == 0)
|
|
break; // Finished
|
|
|
|
array.setRawData(buffer, n);
|
|
data( array );
|
|
array.resetRawData(buffer, n);
|
|
|
|
processed_size += n;
|
|
processedSize( processed_size );
|
|
}
|
|
|
|
data( TQByteArray() );
|
|
|
|
close( fd );
|
|
|
|
processedSize( buff.st_size );
|
|
|
|
finished();
|
|
}
|