tdelibs
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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  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, const TQString &query, bool &redirect)
  74. {
  75. redirect = false;
  76. TQString result = langLookup(fname);
  77. if (result.isEmpty())
  78. {
  79. result = langLookup(fname+"/index.html");
  80. if (!result.isEmpty())
  81. {
  82. KURL red("help:/");
  83. red.setPath( fname+"/index.html" );
  84. red.setQuery( query );
  85. redirection(red);
  86. kdDebug( 7119 ) << "redirect to " << red.url() << endl;
  87. redirect = true;
  88. }
  89. else
  90. {
  91. const TQString helpNotFound("khelpcenter/helpnotfound/index.html");
  92. result = langLookup(helpNotFound);
  93. if (!result.isEmpty())
  94. {
  95. KURL red("help:/");
  96. red.setPath(helpNotFound);
  97. red.setQuery(query);
  98. redirection(red);
  99. kdDebug( 7119 ) << "redirect to " << red.url() << endl;
  100. redirect = true;
  101. }
  102. else
  103. {
  104. unicodeError(i18n("Sorry, there is no documentation available at all for %1." ).arg(fname));
  105. finished();
  106. return TQString::null;
  107. }
  108. }
  109. } else
  110. kdDebug( 7119 ) << "result " << result << endl;
  111. return result;
  112. }
  113. void HelpProtocol::unicodeError( const TQString &t )
  114. {
  115. data(fromUnicode( TQString(
  116. "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\"></head>\n"
  117. "%2</html>" ).arg( TQTextCodec::codecForLocale()->name() ).arg( t ) ) );
  118. }
  119. HelpProtocol *slave = 0;
  120. HelpProtocol::HelpProtocol( bool ghelp, const TQCString &pool, const TQCString &app )
  121. : SlaveBase( ghelp ? "ghelp" : "help", pool, app ), mGhelp( ghelp )
  122. {
  123. slave = this;
  124. }
  125. void HelpProtocol::get( const KURL& url )
  126. {
  127. kdDebug( 7119 ) << "get: path=" << url.path()
  128. << " query=" << url.query() << endl;
  129. bool redirect;
  130. TQString doc;
  131. doc = url.path();
  132. if ( !mGhelp ) {
  133. if (doc.at(0) != '/')
  134. doc = doc.prepend('/');
  135. if (doc.at(doc.length() - 1) == '/')
  136. doc += "index.html";
  137. }
  138. infoMessage(i18n("Looking up correct file"));
  139. if ( !mGhelp ) {
  140. doc = lookupFile(doc, url.query(), redirect);
  141. if (redirect)
  142. {
  143. finished();
  144. return;
  145. }
  146. }
  147. if (doc.isEmpty())
  148. {
  149. error( TDEIO::ERR_DOES_NOT_EXIST, url.url() );
  150. return;
  151. }
  152. mimeType("text/html");
  153. KURL target;
  154. target.setPath(doc);
  155. if (url.hasHTMLRef())
  156. target.setHTMLRef(url.htmlRef());
  157. kdDebug( 7119 ) << "target " << target.url() << endl;
  158. TQString file = target.path();
  159. if ( mGhelp ) {
  160. if ( file.right( 4 ) != ".xml" ) {
  161. get_file( target );
  162. return;
  163. }
  164. } else {
  165. TQString docbook_file = file.left(file.findRev('/')) + "/index.docbook";
  166. if (!TDEStandardDirs::exists(file)) {
  167. file = docbook_file;
  168. } else {
  169. TQFileInfo fi(file);
  170. if (fi.isDir()) {
  171. file = file + "/index.docbook";
  172. } else {
  173. if ( file.right( 5 ) != ".html" || !compareTimeStamps( file, docbook_file ) ) {
  174. get_file( target );
  175. return;
  176. } else
  177. file = docbook_file;
  178. }
  179. }
  180. }
  181. infoMessage(i18n("Preparing document"));
  182. if ( mGhelp ) {
  183. TQString xsl = "customization/tde-nochunk.xsl";
  184. mParsed = transform(file, locate("dtd", xsl));
  185. kdDebug( 7119 ) << "parsed " << mParsed.length() << endl;
  186. if (mParsed.isEmpty()) {
  187. unicodeError( i18n( "The requested help file could not be parsed:<br>%1" ).arg( file ) );
  188. } else {
  189. int pos1 = mParsed.find( "charset=" );
  190. if ( pos1 > 0 ) {
  191. int pos2 = mParsed.find( '"', pos1 );
  192. if ( pos2 > 0 ) {
  193. mParsed.replace( pos1, pos2 - pos1, "charset=UTF-8" );
  194. }
  195. }
  196. data( mParsed.utf8() );
  197. }
  198. } else {
  199. kdDebug( 7119 ) << "look for cache for " << file << endl;
  200. mParsed = lookForCache( file );
  201. kdDebug( 7119 ) << "cached parsed " << mParsed.length() << endl;
  202. if ( mParsed.isEmpty() ) {
  203. mParsed = transform(file, locate("dtd", "customization/tde-chunk.xsl"));
  204. if ( !mParsed.isEmpty() ) {
  205. infoMessage( i18n( "Saving to cache" ) );
  206. TQString cache = file.left( file.length() - 7 );
  207. saveToCache( mParsed, locateLocal( "cache",
  208. "tdeio_help" + cache +
  209. "cache.bz2" ) );
  210. }
  211. } else infoMessage( i18n( "Using cached version" ) );
  212. kdDebug( 7119 ) << "parsed " << mParsed.length() << endl;
  213. if (mParsed.isEmpty()) {
  214. unicodeError( i18n( "The requested help file could not be parsed:<br>%1" ).arg( file ) );
  215. } else {
  216. TQString query = url.query(), anchor;
  217. // if we have a query, look if it contains an anchor
  218. if (!query.isEmpty())
  219. if (query.left(8) == "?anchor=") {
  220. anchor = query.mid(8).lower();
  221. KURL redirURL(url);
  222. redirURL.setQuery(TQString::null);
  223. redirURL.setHTMLRef(anchor);
  224. redirection(redirURL);
  225. finished();
  226. return;
  227. }
  228. if (anchor.isEmpty() && url.hasHTMLRef())
  229. anchor = url.htmlRef();
  230. kdDebug( 7119 ) << "anchor: " << anchor << endl;
  231. if ( !anchor.isEmpty() )
  232. {
  233. int index = 0;
  234. while ( true ) {
  235. index = mParsed.find( TQRegExp( "<a name=" ), index);
  236. if ( index == -1 ) {
  237. kdDebug( 7119 ) << "no anchor\n";
  238. break; // use whatever is the target, most likely index.html
  239. }
  240. if ( mParsed.mid( index, 11 + anchor.length() ).lower() ==
  241. TQString( "<a name=\"%1\">" ).arg( anchor ) )
  242. {
  243. index = mParsed.findRev( "<FILENAME filename=", index ) +
  244. strlen( "<FILENAME filename=\"" );
  245. TQString filename=mParsed.mid( index, 2000 );
  246. filename = filename.left( filename.find( '\"' ) );
  247. TQString path = target.path();
  248. path = path.left( path.findRev( '/' ) + 1) + filename;
  249. kdDebug( 7119 ) << "anchor found in " << path <<endl;
  250. target.setPath( path );
  251. break;
  252. }
  253. index++;
  254. }
  255. }
  256. emitFile( target );
  257. }
  258. }
  259. finished();
  260. }
  261. void HelpProtocol::emitFile( const KURL& url )
  262. {
  263. infoMessage(i18n("Looking up section"));
  264. TQString filename = url.path().mid(url.path().findRev('/') + 1);
  265. int index = mParsed.find(TQString("<FILENAME filename=\"%1\"").arg(filename));
  266. if (index == -1) {
  267. if ( filename == "index.html" ) {
  268. data( fromUnicode( mParsed ) );
  269. return;
  270. }
  271. unicodeError( i18n("Could not find filename %1 in %2.").arg(filename).arg( url.url() ) );
  272. return;
  273. }
  274. TQString filedata = splitOut(mParsed, index);
  275. replaceCharsetHeader( filedata );
  276. data( fromUnicode( filedata ) );
  277. data( TQByteArray() );
  278. }
  279. void HelpProtocol::mimetype( const KURL &)
  280. {
  281. mimeType("text/html");
  282. finished();
  283. }
  284. // Copied from tdeio_file to avoid redirects
  285. #define MAX_IPC_SIZE (1024*32)
  286. void HelpProtocol::get_file( const KURL& url )
  287. {
  288. kdDebug( 7119 ) << "get_file " << url.url() << endl;
  289. TQCString _path( TQFile::encodeName(url.path()));
  290. struct stat buff;
  291. if ( ::stat( _path.data(), &buff ) == -1 ) {
  292. if ( errno == EACCES )
  293. error( TDEIO::ERR_ACCESS_DENIED, url.path() );
  294. else
  295. error( TDEIO::ERR_DOES_NOT_EXIST, url.path() );
  296. return;
  297. }
  298. if ( S_ISDIR( buff.st_mode ) ) {
  299. error( TDEIO::ERR_IS_DIRECTORY, url.path() );
  300. return;
  301. }
  302. if ( S_ISFIFO( buff.st_mode ) || S_ISSOCK ( buff.st_mode ) ) {
  303. error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
  304. return;
  305. }
  306. int fd = open( _path.data(), O_RDONLY);
  307. if ( fd < 0 ) {
  308. error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.path() );
  309. return;
  310. }
  311. totalSize( buff.st_size );
  312. int processed_size = 0;
  313. char buffer[ MAX_IPC_SIZE ];
  314. TQByteArray array;
  315. while( 1 )
  316. {
  317. int n = ::read( fd, buffer, MAX_IPC_SIZE );
  318. if (n == -1)
  319. {
  320. if (errno == EINTR)
  321. continue;
  322. error( TDEIO::ERR_COULD_NOT_READ, url.path());
  323. close(fd);
  324. return;
  325. }
  326. if (n == 0)
  327. break; // Finished
  328. array.setRawData(buffer, n);
  329. data( array );
  330. array.resetRawData(buffer, n);
  331. processed_size += n;
  332. processedSize( processed_size );
  333. }
  334. data( TQByteArray() );
  335. close( fd );
  336. processedSize( buff.st_size );
  337. finished();
  338. }