TDE core libraries
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.

tdeio_help.cpp 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. #include <config.h>
  2. #ifdef HAVE_SYS_TYPES_H
  3. # include <sys/types.h>
  4. #endif
  5. #ifdef HAVE_SYS_STAT_H
  6. # include <sys/stat.h>
  7. #endif
  8. #include <errno.h>
  9. #include <fcntl.h>
  10. #ifdef HAVE_STDIO_H
  11. # include <stdio.h>
  12. #endif
  13. #ifdef HAVE_STDLIB_H
  14. # include <stdlib.h>
  15. #endif
  16. #include <tqvaluelist.h>
  17. #include <tqfileinfo.h>
  18. #include <tqfile.h>
  19. #include <tqtextstream.h>
  20. #include <tqregexp.h>
  21. #include <tqtextcodec.h>
  22. #include <kdebug.h>
  23. #include <kurl.h>
  24. #include <tdeglobal.h>
  25. #include <tdelocale.h>
  26. #include <kstandarddirs.h>
  27. #include <kinstance.h>
  28. #include "tdeio_help.h"
  29. #include <libxslt/xsltutils.h>
  30. #include <libxslt/transform.h>
  31. #include "xslt.h"
  32. using namespace TDEIO;
  33. TQString HelpProtocol::langLookup(const TQString& fname)
  34. {
  35. TQStringList search;
  36. // assemble the local search paths
  37. const TQStringList localDoc = TDEGlobal::dirs()->resourceDirs("html") + TDEGlobal::dirs()->resourceDirs("html-bundle");
  38. TQStringList langs = TDEGlobal::locale()->languageList();
  39. langs.append( "en" );
  40. langs.remove( "C" );
  41. // this is kind of compat hack as we install our docs in en/ but the
  42. // default language is en_US
  43. for (TQStringList::Iterator it = langs.begin(); it != langs.end(); ++it)
  44. if ( *it == "en_US" )
  45. *it = "en";
  46. // look up the different languages
  47. int ldCount = localDoc.count();
  48. for (int id=0; id < ldCount; id++)
  49. {
  50. TQStringList::ConstIterator lang;
  51. for (lang = langs.begin(); lang != langs.end(); ++lang)
  52. search.append(TQString("%1%2/%3").arg(localDoc[id], *lang, fname));
  53. }
  54. // try to locate the file
  55. TQStringList::Iterator it;
  56. for (it = search.begin(); it != search.end(); ++it)
  57. {
  58. kdDebug( 7119 ) << "Looking for help in: " << *it << endl;
  59. TQFileInfo info(*it);
  60. if (info.exists() && info.isFile() && info.isReadable())
  61. return *it;
  62. if ( ( *it ).right( 5 ) == ".html" )
  63. {
  64. TQString file = (*it).left((*it).findRev('/')) + "/index.docbook";
  65. kdDebug( 7119 ) << "Looking for help in: " << file << endl;
  66. info.setFile(file);
  67. if (info.exists() && info.isFile() && info.isReadable())
  68. return *it;
  69. }
  70. }
  71. return TQString::null;
  72. }
  73. TQString HelpProtocol::lookupFile(const TQString &fname,
  74. const TQString &query, bool &redirect)
  75. {
  76. redirect = false;
  77. TQString path, result;
  78. path = fname;
  79. result = langLookup(path);
  80. if (result.isEmpty())
  81. {
  82. result = langLookup(path+"/index.html");
  83. if (!result.isEmpty())
  84. {
  85. KURL red( "help:/" );
  86. red.setPath( path + "/index.html" );
  87. red.setQuery( query );
  88. redirection(red);
  89. kdDebug( 7119 ) << "redirect to " << red.url() << endl;
  90. redirect = true;
  91. }
  92. else
  93. {
  94. unicodeError( i18n("There is no documentation available for %1." ).arg(path) );
  95. finished();
  96. return TQString::null;
  97. }
  98. } else
  99. kdDebug( 7119 ) << "result " << result << endl;
  100. return result;
  101. }
  102. void HelpProtocol::unicodeError( const TQString &t )
  103. {
  104. data(fromUnicode( TQString(
  105. "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\"></head>\n"
  106. "%2</html>" ).arg( TQTextCodec::codecForLocale()->name() ).arg( t ) ) );
  107. }
  108. HelpProtocol *slave = 0;
  109. HelpProtocol::HelpProtocol( bool ghelp, const TQCString &pool, const TQCString &app )
  110. : SlaveBase( ghelp ? "ghelp" : "help", pool, app ), mGhelp( ghelp )
  111. {
  112. slave = this;
  113. }
  114. void HelpProtocol::get( const KURL& url )
  115. {
  116. kdDebug( 7119 ) << "get: path=" << url.path()
  117. << " query=" << url.query() << endl;
  118. bool redirect;
  119. TQString doc;
  120. doc = url.path();
  121. if ( !mGhelp ) {
  122. if (doc.at(0) != '/')
  123. doc = doc.prepend('/');
  124. if (doc.at(doc.length() - 1) == '/')
  125. doc += "index.html";
  126. }
  127. infoMessage(i18n("Looking up correct file"));
  128. if ( !mGhelp ) {
  129. doc = lookupFile(doc, url.query(), redirect);
  130. if (redirect)
  131. {
  132. finished();
  133. return;
  134. }
  135. }
  136. if (doc.isEmpty())
  137. {
  138. error( TDEIO::ERR_DOES_NOT_EXIST, url.url() );
  139. return;
  140. }
  141. mimeType("text/html");
  142. KURL target;
  143. target.setPath(doc);
  144. if (url.hasHTMLRef())
  145. target.setHTMLRef(url.htmlRef());
  146. kdDebug( 7119 ) << "target " << target.url() << endl;
  147. TQString file = target.path();
  148. if ( mGhelp ) {
  149. if ( file.right( 4 ) != ".xml" ) {
  150. get_file( target );
  151. return;
  152. }
  153. } else {
  154. TQString docbook_file = file.left(file.findRev('/')) + "/index.docbook";
  155. if (!TDEStandardDirs::exists(file)) {
  156. file = docbook_file;
  157. } else {
  158. TQFileInfo fi(file);
  159. if (fi.isDir()) {
  160. file = file + "/index.docbook";
  161. } else {
  162. if ( file.right( 5 ) != ".html" || !compareTimeStamps( file, docbook_file ) ) {
  163. get_file( target );
  164. return;
  165. } else
  166. file = docbook_file;
  167. }
  168. }
  169. }
  170. infoMessage(i18n("Preparing document"));
  171. if ( mGhelp ) {
  172. TQString xsl = "customization/kde-nochunk.xsl";
  173. mParsed = transform(file, locate("dtd", xsl));
  174. kdDebug( 7119 ) << "parsed " << mParsed.length() << endl;
  175. if (mParsed.isEmpty()) {
  176. unicodeError( i18n( "The requested help file could not be parsed:<br>%1" ).arg( file ) );
  177. } else {
  178. int pos1 = mParsed.find( "charset=" );
  179. if ( pos1 > 0 ) {
  180. int pos2 = mParsed.find( '"', pos1 );
  181. if ( pos2 > 0 ) {
  182. mParsed.replace( pos1, pos2 - pos1, "charset=UTF-8" );
  183. }
  184. }
  185. data( mParsed.utf8() );
  186. }
  187. } else {
  188. kdDebug( 7119 ) << "look for cache for " << file << endl;
  189. mParsed = lookForCache( file );
  190. kdDebug( 7119 ) << "cached parsed " << mParsed.length() << endl;
  191. if ( mParsed.isEmpty() ) {
  192. mParsed = transform(file, locate("dtd", "customization/kde-chunk.xsl"));
  193. if ( !mParsed.isEmpty() ) {
  194. infoMessage( i18n( "Saving to cache" ) );
  195. TQString cache = file.left( file.length() - 7 );
  196. saveToCache( mParsed, locateLocal( "cache",
  197. "tdeio_help" + cache +
  198. "cache.bz2" ) );
  199. }
  200. } else infoMessage( i18n( "Using cached version" ) );
  201. kdDebug( 7119 ) << "parsed " << mParsed.length() << endl;
  202. if (mParsed.isEmpty()) {
  203. unicodeError( i18n( "The requested help file could not be parsed:<br>%1" ).arg( file ) );
  204. } else {
  205. TQString query = url.query(), anchor;
  206. // if we have a query, look if it contains an anchor
  207. if (!query.isEmpty())
  208. if (query.left(8) == "?anchor=") {
  209. anchor = query.mid(8).lower();
  210. KURL redirURL(url);
  211. redirURL.setQuery(TQString::null);
  212. redirURL.setHTMLRef(anchor);
  213. redirection(redirURL);
  214. finished();
  215. return;
  216. }
  217. if (anchor.isEmpty() && url.hasHTMLRef())
  218. anchor = url.htmlRef();
  219. kdDebug( 7119 ) << "anchor: " << anchor << endl;
  220. if ( !anchor.isEmpty() )
  221. {
  222. int index = 0;
  223. while ( true ) {
  224. index = mParsed.find( TQRegExp( "<a name=" ), index);
  225. if ( index == -1 ) {
  226. kdDebug( 7119 ) << "no anchor\n";
  227. break; // use whatever is the target, most likely index.html
  228. }
  229. if ( mParsed.mid( index, 11 + anchor.length() ).lower() ==
  230. TQString( "<a name=\"%1\">" ).arg( anchor ) )
  231. {
  232. index = mParsed.findRev( "<FILENAME filename=", index ) +
  233. strlen( "<FILENAME filename=\"" );
  234. TQString filename=mParsed.mid( index, 2000 );
  235. filename = filename.left( filename.find( '\"' ) );
  236. TQString path = target.path();
  237. path = path.left( path.findRev( '/' ) + 1) + filename;
  238. kdDebug( 7119 ) << "anchor found in " << path <<endl;
  239. target.setPath( path );
  240. break;
  241. }
  242. index++;
  243. }
  244. }
  245. emitFile( target );
  246. }
  247. }
  248. finished();
  249. }
  250. void HelpProtocol::emitFile( const KURL& url )
  251. {
  252. infoMessage(i18n("Looking up section"));
  253. TQString filename = url.path().mid(url.path().findRev('/') + 1);
  254. int index = mParsed.find(TQString("<FILENAME filename=\"%1\"").arg(filename));
  255. if (index == -1) {
  256. if ( filename == "index.html" ) {
  257. data( fromUnicode( mParsed ) );
  258. return;
  259. }
  260. unicodeError( i18n("Could not find filename %1 in %2.").arg(filename).arg( url.url() ) );
  261. return;
  262. }
  263. TQString filedata = splitOut(mParsed, index);
  264. replaceCharsetHeader( filedata );
  265. data( fromUnicode( filedata ) );
  266. data( TQByteArray() );
  267. }
  268. void HelpProtocol::mimetype( const KURL &)
  269. {
  270. mimeType("text/html");
  271. finished();
  272. }
  273. // Copied from tdeio_file to avoid redirects
  274. #define MAX_IPC_SIZE (1024*32)
  275. void HelpProtocol::get_file( const KURL& url )
  276. {
  277. kdDebug( 7119 ) << "get_file " << url.url() << endl;
  278. TQCString _path( TQFile::encodeName(url.path()));
  279. struct stat buff;
  280. if ( ::stat( _path.data(), &buff ) == -1 ) {
  281. if ( errno == EACCES )
  282. error( TDEIO::ERR_ACCESS_DENIED, url.path() );
  283. else
  284. error( TDEIO::ERR_DOES_NOT_EXIST, url.path() );
  285. return;
  286. }
  287. if ( S_ISDIR( buff.st_mode ) ) {
  288. error( TDEIO::ERR_IS_DIRECTORY, url.path() );
  289. return;
  290. }
  291. if ( S_ISFIFO( buff.st_mode ) || S_ISSOCK ( buff.st_mode ) ) {
  292. error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
  293. return;
  294. }
  295. int fd = open( _path.data(), O_RDONLY);
  296. if ( fd < 0 ) {
  297. error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
  298. return;
  299. }
  300. totalSize( buff.st_size );
  301. int processed_size = 0;
  302. char buffer[ MAX_IPC_SIZE ];
  303. TQByteArray array;
  304. while( 1 )
  305. {
  306. int n = ::read( fd, buffer, MAX_IPC_SIZE );
  307. if (n == -1)
  308. {
  309. if (errno == EINTR)
  310. continue;
  311. error( TDEIO::ERR_COULD_NOT_READ, url.path());
  312. close(fd);
  313. return;
  314. }
  315. if (n == 0)
  316. break; // Finished
  317. array.setRawData(buffer, n);
  318. data( array );
  319. array.resetRawData(buffer, n);
  320. processed_size += n;
  321. processedSize( processed_size );
  322. }
  323. data( TQByteArray() );
  324. close( fd );
  325. processedSize( buff.st_size );
  326. finished();
  327. }