1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_xmlhelp.hxx"
30 
31 #define WORKAROUND_98119
32 
33 #ifdef WORKAROUND_98119
34 #include "bufferedinputstream.hxx"
35 #endif
36 
37 #include <string.h>
38 #ifndef _VOS_DIAGNOSE_HXX_
39 #include <vos/diagnose.hxx>
40 #endif
41 #include <osl/thread.h>
42 #include <rtl/memory.h>
43 #include <osl/file.hxx>
44 #include <cppuhelper/weak.hxx>
45 #include <cppuhelper/queryinterface.hxx>
46 #include <comphelper/processfactory.hxx>
47 #include <rtl/uri.hxx>
48 #include <rtl/ustrbuf.hxx>
49 #include <libxslt/xslt.h>
50 #include <libxslt/transform.h>
51 #include <libxslt/xsltutils.h>
52 #include "db.hxx"
53 #include <com/sun/star/io/XActiveDataSink.hpp>
54 #include <com/sun/star/io/XInputStream.hpp>
55 #include <com/sun/star/io/XSeekable.hpp>
56 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
57 #include <com/sun/star/ucb/OpenMode.hpp>
58 #include <com/sun/star/ucb/XCommandProcessor.hpp>
59 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
60 #include <com/sun/star/ucb/XContentIdentifier.hpp>
61 #include <com/sun/star/ucb/XContentProvider.hpp>
62 #include <com/sun/star/ucb/XContentIdentifierFactory.hpp>
63 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
64 #include <com/sun/star/beans/XPropertySet.hpp>
65 
66 #include "urlparameter.hxx"
67 #include "databases.hxx"
68 
69 namespace chelp {
70 
71 	inline bool ascii_isDigit( sal_Unicode ch )
72 	{
73 		return ((ch >= 0x0030) && (ch <= 0x0039));
74 	}
75 
76 	inline bool ascii_isLetter( sal_Unicode ch )
77 	{
78 		return ( ( (ch >= 0x0041) && (ch <= 0x005A) ) ||
79 				 ( (ch >= 0x0061) && (ch <= 0x007A) ) );
80 	}
81 
82 	inline bool isLetterOrDigit( sal_Unicode ch )
83 	{
84 		return ascii_isLetter( ch ) || ascii_isDigit( ch );
85 	}
86 
87 }
88 
89 using namespace cppu;
90 using namespace com::sun::star::io;
91 using namespace com::sun::star::uno;
92 using namespace com::sun::star::lang;
93 using namespace com::sun::star::ucb;
94 using namespace com::sun::star::beans;
95 using namespace com::sun::star::container;
96 using namespace berkeleydbproxy;
97 using namespace chelp;
98 
99 
100 URLParameter::URLParameter( const rtl::OUString& aURL,
101 							Databases* pDatabases )
102 	throw( com::sun::star::ucb::IllegalIdentifierException )
103 	: m_pDatabases( pDatabases ),
104       m_aURL( aURL )
105 {
106 	init( false );
107 	parse();
108 }
109 
110 
111 bool URLParameter::isErrorDocument()
112 {
113 	bool bErrorDoc = false;
114 
115 	if( isFile() )
116 	{
117 		Reference< XHierarchicalNameAccess > xNA =
118 			m_pDatabases->findJarFileForPath( get_jar(), get_language(), get_path() );
119 		bErrorDoc = !xNA.is();
120 	}
121 
122 	return bErrorDoc;
123 }
124 
125 
126 rtl::OString URLParameter::getByName( const char* par )
127 {
128 	rtl::OUString val;
129 
130 	if( strcmp( par,"Program" ) == 0 )
131 		val = get_program();
132 	else if( strcmp( par,"Database" ) == 0 )
133 		val = get_module();
134 	else if( strcmp( par,"DatabasePar" ) == 0 )
135 		val = get_dbpar();
136 	else if( strcmp( par,"Id" ) == 0 )
137 		val = get_id();
138 	else if( strcmp( par,"Path" ) == 0 )
139 		val = get_path();
140 	else if( strcmp( par,"Language" ) == 0 )
141 		val = get_language();
142 	else if( strcmp( par,"System" ) == 0 )
143 		val = get_system();
144 	else if( strcmp( par,"HelpPrefix" ) == 0 )
145 		val = get_prefix();
146 
147 	return rtl::OString( val.getStr(),val.getLength(),RTL_TEXTENCODING_UTF8 );
148 }
149 
150 
151 rtl::OUString URLParameter::get_id()
152 {
153 	if( m_aId.compareToAscii("start") == 0 )
154 	{   // module is set
155 		StaticModuleInformation* inf =
156 			m_pDatabases->getStaticInformationForModule( get_module(),
157 														 get_language() );
158 		if( inf )
159 			m_aId = inf->get_id();
160 
161 		m_bStart = true;
162 	}
163 
164 	return m_aId;
165 }
166 
167 rtl::OUString URLParameter::get_tag()
168 {
169 	if( isFile() )
170 		return get_the_tag();
171 	else
172 		return m_aTag;
173 }
174 
175 
176 rtl::OUString URLParameter::get_title()
177 {
178 	if( isFile() )
179 		return get_the_title();
180 	else if( m_aModule.compareToAscii("") != 0 )
181 	{
182 		StaticModuleInformation* inf =
183 			m_pDatabases->getStaticInformationForModule( get_module(),
184 														 get_language() );
185 		if( inf )
186 			m_aTitle = inf->get_title();
187 	}
188 	else   // This must be the root
189 		m_aTitle = rtl::OUString::createFromAscii("root");
190 
191 	return m_aTitle;
192 }
193 
194 
195 rtl::OUString URLParameter::get_language()
196 {
197 	if( m_aLanguage.getLength() == 0 )
198 		return m_aDefaultLanguage;
199 
200 	return m_aLanguage;
201 }
202 
203 
204 rtl::OUString URLParameter::get_program()
205 {
206 	if( ! m_aProgram.getLength() )
207 	{
208 		StaticModuleInformation* inf =
209 			m_pDatabases->getStaticInformationForModule( get_module(),
210 														 get_language() );
211 		if( inf )
212 			m_aProgram = inf->get_program();
213 	}
214 	return m_aProgram;
215 }
216 
217 
218 void URLParameter::init( bool bDefaultLanguageIsInitialized )
219 {
220 	(void)bDefaultLanguageIsInitialized;
221 
222 	m_bBerkeleyRead = false;
223 	m_bStart = false;
224     m_bUseDB = true;
225 	m_nHitCount = 100;                // The default maximum hitcount
226 }
227 
228 
229 rtl::OUString URLParameter::get_the_tag()
230 {
231     if(m_bUseDB) {
232         if( ! m_bBerkeleyRead )
233             readBerkeley();
234 
235         m_bBerkeleyRead = true;
236 
237         return m_aTag;
238     }
239     else
240         return rtl::OUString();
241 }
242 
243 
244 
245 rtl::OUString URLParameter::get_the_path()
246 {
247     if(m_bUseDB) {
248         if( ! m_bBerkeleyRead )
249             readBerkeley();
250         m_bBerkeleyRead = true;
251 
252         return m_aPath;
253     }
254     else
255         return get_id();
256 }
257 
258 
259 
260 rtl::OUString URLParameter::get_the_title()
261 {
262     if(m_bUseDB) {
263         if( ! m_bBerkeleyRead )
264             readBerkeley();
265         m_bBerkeleyRead = true;
266 
267         return m_aTitle;
268     }
269     else
270         return rtl::OUString();
271 }
272 
273 
274 rtl::OUString URLParameter::get_the_jar()
275 {
276     if(m_bUseDB) {
277         if( ! m_bBerkeleyRead )
278             readBerkeley();
279         m_bBerkeleyRead = true;
280 
281         return m_aJar;
282     }
283     else
284         return get_module() + rtl::OUString::createFromAscii(".jar");
285 }
286 
287 
288 
289 
290 void URLParameter::readBerkeley()
291 {
292 	static rtl::OUString aQuestionMark( rtl::OUString::createFromAscii( "?" ) );
293 
294 	if( get_id().compareToAscii("") == 0 )
295 		return;
296 
297 	rtl::OUString aModule = get_module();
298 	rtl::OUString aLanguage = get_language();
299 
300 	DataBaseIterator aDbIt( *m_pDatabases, aModule, aLanguage, false );
301 	bool bSuccess = false;
302 
303 	int nSize = 0;
304 	const sal_Char* pData = NULL;
305 
306 	Dbt data;
307 	DBData aDBData;
308 	rtl::OUString aExtensionPath;
309     rtl::OUString aExtensionRegistryPath;
310 	while( true )
311 	{
312 		Db* db = aDbIt.nextDb( &aExtensionPath, &aExtensionRegistryPath );
313 		if( !db )
314 			break;
315 
316 		rtl::OString keyStr( m_aId.getStr(),m_aId.getLength(),RTL_TEXTENCODING_UTF8 );
317 
318 		DBHelp* pDBHelp = db->getDBHelp();
319 		if( pDBHelp != NULL )
320 		{
321 			bSuccess = pDBHelp->getValueForKey( keyStr, aDBData );
322 			if( bSuccess )
323 			{
324 				nSize = aDBData.getSize();
325 				pData = aDBData.getData();
326 				break;
327 			}
328 		}
329 		else
330 		{
331 			Dbt key( static_cast< void* >( const_cast< sal_Char* >( keyStr.getStr() ) ),
332 					 keyStr.getLength() );
333 			int err = db->get( 0,&key,&data,0 );
334 			if( err == 0 )
335 			{
336 				bSuccess = true;
337 				nSize = data.get_size();
338 				pData = static_cast<sal_Char*>( data.get_data() );
339 				break;
340 			}
341 		}
342 	}
343 
344 	if( bSuccess )
345 	{
346 		DbtToStringConverter converter( pData, nSize );
347 		m_aTitle = converter.getTitle();
348 		m_pDatabases->replaceName( m_aTitle );
349 		m_aPath  = converter.getFile();
350 		m_aJar   = converter.getDatabase();
351 		if( aExtensionPath.getLength() > 0 )
352 		{
353 			rtl::OUStringBuffer aExtendedJarStrBuf;
354 			aExtendedJarStrBuf.append( aQuestionMark );
355 			aExtendedJarStrBuf.append( aExtensionPath );
356 			aExtendedJarStrBuf.append( aQuestionMark );
357 			aExtendedJarStrBuf.append( m_aJar );
358 			m_aJar = aExtendedJarStrBuf.makeStringAndClear();
359             m_aExtensionRegistryPath = aExtensionRegistryPath;
360 		}
361 		m_aTag   = converter.getHash();
362 	}
363 }
364 
365 
366 
367 // Class encapsulating the transformation of the XInputStream to XHTML
368 
369 
370 class InputStreamTransformer
371 	: public OWeakObject,
372 	  public XInputStream,
373 	  public XSeekable
374 {
375 public:
376 
377 	InputStreamTransformer( URLParameter* urlParam,
378 							Databases*    pDatatabases,
379 							bool isRoot = false );
380 
381 	~InputStreamTransformer();
382 
383 	virtual Any SAL_CALL queryInterface( const Type& rType ) throw( RuntimeException );
384 	virtual void SAL_CALL acquire( void ) throw();
385 	virtual void SAL_CALL release( void ) throw();
386 
387 	virtual sal_Int32 SAL_CALL readBytes( Sequence< sal_Int8 >& aData,sal_Int32 nBytesToRead )
388 		throw( NotConnectedException,
389 			   BufferSizeExceededException,
390 			   IOException,
391 			   RuntimeException);
392 
393 	virtual sal_Int32 SAL_CALL readSomeBytes( Sequence< sal_Int8 >& aData,sal_Int32 nMaxBytesToRead )
394 		throw( NotConnectedException,
395 			   BufferSizeExceededException,
396 			   IOException,
397 			   RuntimeException);
398 
399 	virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) throw( NotConnectedException,
400 																	 BufferSizeExceededException,
401 																	 IOException,
402 																	 RuntimeException );
403 
404 	virtual sal_Int32 SAL_CALL available( void ) throw( NotConnectedException,
405 														IOException,
406 														RuntimeException );
407 
408 	virtual void SAL_CALL closeInput( void ) throw( NotConnectedException,
409 													IOException,
410 													RuntimeException );
411 
412 	virtual void SAL_CALL seek( sal_Int64 location ) throw( IllegalArgumentException,
413 															IOException,
414 															RuntimeException );
415 
416 	virtual sal_Int64 SAL_CALL getPosition( void ) throw( IOException,RuntimeException );
417 
418 	virtual sal_Int64 SAL_CALL getLength( void ) throw( IOException,RuntimeException );
419 
420 	void addToBuffer( const char* buffer,int len );
421 
422 	sal_Int8* getData() const { return (sal_Int8*) buffer; }
423 
424     sal_Int32 getLen() const { return sal_Int32( len ); }
425 
426 private:
427 
428 	osl::Mutex m_aMutex;
429 
430 	int len,pos;
431 	char *buffer;
432 };
433 
434 
435 
436 void URLParameter::open( const Reference< XMultiServiceFactory >& rxSMgr,
437 						 const Command& aCommand,
438 						 sal_Int32 CommandId,
439 						 const Reference< XCommandEnvironment >& Environment,
440 						 const Reference< XOutputStream >& xDataSink )
441 {
442 	(void)rxSMgr;
443 	(void)aCommand;
444 	(void)CommandId;
445 	(void)Environment;
446 
447     if( ! xDataSink.is() )
448         return;
449 
450 	if( isPicture() )
451 	{
452 		Reference< XInputStream > xStream;
453 		Reference< XHierarchicalNameAccess > xNA =
454 			m_pDatabases->jarFile( rtl::OUString::createFromAscii( "picture.jar" ),
455 								   get_language() );
456 
457 		rtl::OUString path = get_path();
458 		if( xNA.is() )
459 		{
460 			try
461 			{
462 				Any aEntry = xNA->getByHierarchicalName( path );
463 				Reference< XActiveDataSink > xSink;
464 				if( ( aEntry >>= xSink ) && xSink.is() )
465 					xStream = xSink->getInputStream();
466 			}
467 			catch ( NoSuchElementException & )
468 			{
469 			}
470 		}
471         if( xStream.is() )
472         {
473             sal_Int32 ret;
474             Sequence< sal_Int8 > aSeq( 4096 );
475             while( true )
476             {
477                 try
478                 {
479                     ret = xStream->readBytes( aSeq,4096 );
480                     xDataSink->writeBytes( aSeq );
481                     if( ret < 4096 )
482                         break;
483                 }
484                 catch( const Exception& )
485                 {
486                     break;
487                 }
488             }
489         }
490 	}
491 	else
492     {
493 		// a standard document or else an active help text, plug in the new input stream
494 		InputStreamTransformer* p = new InputStreamTransformer( this,m_pDatabases,isRoot() );
495         try
496         {
497             xDataSink->writeBytes( Sequence< sal_Int8 >( p->getData(),p->getLen() ) );
498         }
499         catch( const Exception& )
500         {
501         }
502         delete p;
503     }
504     xDataSink->closeOutput();
505 }
506 
507 
508 
509 void URLParameter::open( const Reference< XMultiServiceFactory >& rxSMgr,
510 						 const Command& aCommand,
511 						 sal_Int32 CommandId,
512 						 const Reference< XCommandEnvironment >& Environment,
513 						 const Reference< XActiveDataSink >& xDataSink )
514 {
515 	(void)rxSMgr;
516 	(void)aCommand;
517 	(void)CommandId;
518 	(void)Environment;
519 
520 	if( isPicture() )
521 	{
522 		Reference< XInputStream > xStream;
523 		Reference< XHierarchicalNameAccess > xNA =
524 			m_pDatabases->jarFile( rtl::OUString::createFromAscii( "picture.jar" ),
525 								   get_language() );
526 
527 		rtl::OUString path = get_path();
528 		if( xNA.is() )
529 		{
530 			try
531 			{
532 				Any aEntry = xNA->getByHierarchicalName( path );
533 				Reference< XActiveDataSink > xSink;
534 				if( ( aEntry >>= xSink ) && xSink.is() )
535 					xStream = xSink->getInputStream();
536 			}
537 			catch ( NoSuchElementException & )
538 			{
539 			}
540 		}
541 #ifdef WORKAROUND_98119
542 		xDataSink->setInputStream( turnToSeekable(xStream) );
543 #else
544 		xDataSink->setInputStream( xStream );
545 #endif
546 	}
547 	else
548 		// a standard document or else an active help text, plug in the new input stream
549 		xDataSink->setInputStream( new InputStreamTransformer( this,m_pDatabases,isRoot() ) );
550 }
551 
552 
553 // #include <stdio.h>
554 
555 void URLParameter::parse() throw( com::sun::star::ucb::IllegalIdentifierException )
556 {
557     // fprintf(stdout,"url send to xmlhelp: %s\n",(rtl::OUStringToOString(m_aURL,RTL_TEXTENCODING_UTF8).getStr()));
558 	m_aExpr = m_aURL;
559 
560 	sal_Int32 lstIdx = m_aExpr.lastIndexOf( sal_Unicode( '#' ) );
561 	if( lstIdx != -1 )
562 		m_aExpr = m_aExpr.copy( 0,lstIdx );
563 
564 	if( ! scheme() ||
565         ! name( module() ) ||
566         ! query() ||
567         ! m_aLanguage.getLength() ||
568         ! m_aSystem.getLength() )
569 		throw com::sun::star::ucb::IllegalIdentifierException();
570 }
571 
572 
573 bool URLParameter::scheme()
574 {
575 	// Correct extension help links as sometimes the
576 	// module is missing resulting in a misformed URL
577 	if( m_aExpr.compareToAscii( "vnd.sun.star.help:///", 21 ) == 0 )
578 	{
579 		sal_Int32 nLen = m_aExpr.getLength();
580 		rtl::OUString aLastStr = m_aExpr.copy( nLen - 6 );
581 		if( aLastStr.compareToAscii( "DbPAR=" ) == 0 )
582 		{
583 			rtl::OUString aNewExpr = m_aExpr.copy( 0, 20 );
584 			rtl::OUString aSharedStr = rtl::OUString::createFromAscii( "shared" );
585 			aNewExpr += aSharedStr;
586 			aNewExpr += m_aExpr.copy( 20 );
587 			aNewExpr += aSharedStr;
588 			m_aExpr = aNewExpr;
589 		}
590 	}
591 
592 	for( sal_Int32 nPrefixLen = 20 ; nPrefixLen >= 18 ; --nPrefixLen )
593 	{
594 		if( m_aExpr.compareToAscii( "vnd.sun.star.help://", nPrefixLen ) == 0 )
595 		{
596 			m_aExpr = m_aExpr.copy( nPrefixLen );
597 			return true;
598 		}
599 	}
600 	return false;
601 }
602 
603 
604 bool URLParameter::module()
605 {
606 	sal_Int32 idx = 0,length = m_aExpr.getLength();
607 
608 	while( idx < length && isLetterOrDigit( (m_aExpr.getStr())[idx] ) )
609 		++idx;
610 
611 	if( idx != 0 )
612 	{
613 		m_aModule = m_aExpr.copy( 0,idx );
614 		m_aExpr = m_aExpr.copy( idx );
615 		return true;
616 	}
617 	else
618 		return false;
619 }
620 
621 
622 
623 bool URLParameter::name( bool modulePresent )
624 {
625 	// if modulepresent, a name may be present, but must not
626 
627 	sal_Int32 length = m_aExpr.getLength();
628 
629 	if( length != 0 && (m_aExpr.getStr())[0] == sal_Unicode( '/' ) )
630 	{
631 		sal_Int32 idx = 1;
632 		while( idx < length && (m_aExpr.getStr())[idx] != '?' )
633 //                ( isLetterOrDigit( (m_aExpr.getStr())[idx] )
634 //                  || (m_aExpr.getStr())[idx] == '/'
635 //                  || (m_aExpr.getStr())[idx] == '.' ))
636 			++idx;
637 
638 		if( idx != 1 && ! modulePresent )
639 			return false;
640 		else
641 		{
642 			m_aId = m_aExpr.copy( 1,idx-1 );
643 			m_aExpr = m_aExpr.copy( idx );
644 		}
645 	}
646 
647 //    fprintf(stdout,"id %s\n",(rtl::OUStringToOString(m_aId,RTL_TEXTENCODING_UTF8).getStr()));
648 	return true;
649 }
650 
651 
652 bool URLParameter::query()
653 {
654 	rtl::OUString query_;
655 
656 	if( ! m_aExpr.getLength() )
657 		return true;
658 	else if( (m_aExpr.getStr())[0] == sal_Unicode( '?' ) )
659 		query_ = m_aExpr.copy( 1 ).trim();
660 	else
661 		return false;
662 
663 
664 	bool ret = true;
665 	sal_Int32 delimIdx,equalIdx;
666 	rtl::OUString parameter,value;
667 
668 	while( query_.getLength() != 0 )
669 	{
670 		delimIdx = query_.indexOf( sal_Unicode( '&' ) );
671 		equalIdx = query_.indexOf( sal_Unicode( '=' ) );
672 		parameter = query_.copy( 0,equalIdx ).trim();
673 		if( delimIdx == -1 )
674 		{
675 			value = query_.copy( equalIdx + 1 ).trim();
676 			query_ = rtl::OUString();
677 		}
678 		else
679 		{
680 			value = query_.copy( equalIdx+1,delimIdx - equalIdx - 1 ).trim();
681 			query_ = query_.copy( delimIdx+1 ).trim();
682 		}
683 
684 		if( parameter.compareToAscii( "Language" ) == 0 )
685 			m_aLanguage = value;
686 		else if( parameter.compareToAscii( "Device" ) == 0 )
687 			m_aDevice = value;
688 		else if( parameter.compareToAscii( "Program" ) == 0 )
689 			m_aProgram = value;
690 		else if( parameter.compareToAscii( "Eid" ) == 0 )
691 			m_aEid = value;
692 		else if( parameter.compareToAscii( "UseDB" ) == 0 )
693             m_bUseDB = ! ( value.compareToAscii("no") == 0 );
694         else if( parameter.compareToAscii( "DbPAR" ) == 0 )
695             m_aDbPar = value;
696 		else if( parameter.compareToAscii( "Query" ) == 0 )
697 		{
698 			if( ! m_aQuery.getLength() )
699 				m_aQuery = value;
700 			else
701 				m_aQuery += ( rtl::OUString::createFromAscii( " " ) + value );
702 		}
703 		else if( parameter.compareToAscii( "Scope" ) == 0 )
704 			m_aScope = value;
705 		else if( parameter.compareToAscii( "System" ) == 0 )
706 			m_aSystem = value;
707 		else if( parameter.compareToAscii( "HelpPrefix" ) == 0 )
708 			m_aPrefix = rtl::Uri::decode(
709                 value,
710                 rtl_UriDecodeWithCharset,
711                 RTL_TEXTENCODING_UTF8 );
712 		else if( parameter.compareToAscii( "HitCount" ) == 0 )
713 			m_nHitCount = value.toInt32();
714 		else if( parameter.compareToAscii( "Active" ) == 0 )
715 			m_aActive = value;
716 		else
717 			ret = false;
718 	}
719 
720 	return ret;
721 }
722 
723 struct UserData {
724 
725 	UserData( InputStreamTransformer* pTransformer,
726 			  URLParameter*           pInitial,
727 			  Databases*              pDatabases )
728 		: m_pTransformer( pTransformer ),
729           m_pDatabases( pDatabases ),
730 		  m_pInitial( pInitial )
731 	{
732 	}
733 
734 	InputStreamTransformer*             m_pTransformer;
735 	Databases*                          m_pDatabases;
736 	URLParameter*                       m_pInitial;
737 };
738 
739 UserData *ugblData = 0;
740 
741 extern "C" {
742 
743 static int
744 fileMatch(const char * URI) {
745 	if ((URI != NULL) && !strncmp(URI, "file:/", 6))
746         return 1;
747     return 0;
748 }
749 
750 static int
751 zipMatch(const char * URI) {
752 	if ((URI != NULL) && !strncmp(URI, "vnd.sun.star.zip:/", 18))
753         return 1;
754     return 0;
755 }
756 
757 static int
758 helpMatch(const char * URI) {
759 	if ((URI != NULL) && !strncmp(URI, "vnd.sun.star.help:/", 19))
760         return 1;
761     return 0;
762 }
763 
764 static void *
765 fileOpen(const char *URI) {
766 	osl::File *pRet = new osl::File(rtl::OUString(URI, strlen(URI), RTL_TEXTENCODING_UTF8));
767 	pRet->open(OpenFlag_Read);
768 	return pRet;
769 }
770 
771 static void *
772 zipOpen(const char * /*URI*/) {
773 	rtl::OUString language,jar,path;
774 
775 	if( ugblData->m_pInitial->get_eid().getLength() )
776 		return (void*)(new Reference< XHierarchicalNameAccess >);
777 	else
778 	{
779 		jar = ugblData->m_pInitial->get_jar();
780 		language = ugblData->m_pInitial->get_language();
781 		path = ugblData->m_pInitial->get_path();
782 	}
783 
784 	Reference< XHierarchicalNameAccess > xNA =
785 		ugblData->m_pDatabases->findJarFileForPath( jar, language, path );
786 
787     Reference< XInputStream > xInputStream;
788 
789 	if( xNA.is() )
790 	{
791 		try
792 		{
793 			Any aEntry = xNA->getByHierarchicalName( path );
794 			Reference< XActiveDataSink > xSink;
795 			if( ( aEntry >>= xSink ) && xSink.is() )
796 				xInputStream = xSink->getInputStream();
797 		}
798 		catch ( NoSuchElementException & )
799 		{
800 		}
801 	}
802 
803 	if( xInputStream.is() )
804 	{
805 		return new Reference<XInputStream>(xInputStream);
806 	}
807 	return 0;
808 }
809 
810 static void *
811 helpOpen(const char * URI) {
812 	rtl::OUString language,jar,path;
813 
814 	URLParameter urlpar( rtl::OUString::createFromAscii( URI ),
815 						 ugblData->m_pDatabases );
816 
817 	jar = urlpar.get_jar();
818 	language = urlpar.get_language();
819 	path = urlpar.get_path();
820 
821 	Reference< XHierarchicalNameAccess > xNA =
822 		ugblData->m_pDatabases->findJarFileForPath( jar, language, path );
823 
824     Reference< XInputStream > xInputStream;
825 
826 	if( xNA.is() )
827 	{
828 		try
829 		{
830 			Any aEntry = xNA->getByHierarchicalName( path );
831 			Reference< XActiveDataSink > xSink;
832 			if( ( aEntry >>= xSink ) && xSink.is() )
833 				xInputStream = xSink->getInputStream();
834 		}
835 		catch ( NoSuchElementException & )
836 		{
837 		}
838 	}
839 
840 	if( xInputStream.is() )
841 		return new Reference<XInputStream>(xInputStream);
842 	return 0;
843 }
844 
845 static int
846 helpRead(void * context, char * buffer, int len) {
847 	Reference< XInputStream > *pRef = (Reference< XInputStream >*)context;
848 
849 	Sequence< sal_Int8 > aSeq;
850 	len = (*pRef)->readBytes( aSeq,len);
851 	memcpy(buffer, aSeq.getConstArray(), len);
852 
853 	return len;
854 }
855 
856 static int
857 zipRead(void * context, char * buffer, int len) {
858 	if( ugblData->m_pInitial->get_eid().getLength() )
859 	{
860 		ugblData->m_pDatabases->popupDocument( ugblData->m_pInitial,&buffer,&len);
861 		return len;
862 	}
863 	else
864 		return helpRead(context, buffer, len);
865 }
866 
867 static int
868 fileRead(void * context, char * buffer, int len) {
869 	int nRead = 0;
870 	osl::File *pFile = (osl::File*)context;
871 	if (pFile)
872 	{
873 		sal_uInt64 uRead = 0;
874 		if (osl::FileBase::E_None == pFile->read(buffer, len, uRead))
875 			nRead = static_cast<int>(uRead);
876 	}
877 	return nRead;
878 }
879 
880 static int
881 uriClose(void * context) {
882 	Reference< XInputStream > *pRef = (Reference< XInputStream >*)context;
883 	delete pRef;
884     return 0;
885 }
886 
887 static int
888 fileClose(void * context) {
889 	osl::File *pFile = (osl::File*)context;
890 	if (pFile)
891 	{
892 		pFile->close();
893 		delete pFile;
894 	}
895 	return 0;
896 }
897 
898 } // extern "C"
899 
900 /*
901 // For debugging only
902 extern "C" void StructuredXMLErrorFunction(void *userData, xmlErrorPtr error)
903 {
904 	(void)userData;
905 	(void)error;
906 
907 	// Reset error handler
908 	xmlSetStructuredErrorFunc( NULL, NULL );
909 }
910 */
911 
912 InputStreamTransformer::InputStreamTransformer( URLParameter* urlParam,
913 												Databases*    pDatabases,
914 												bool isRoot )
915 	: len( 0 ),
916 	  pos( 0 ),
917 	  buffer( new char[1] ) // Initializing with one element to avoid gcc compiler warning
918 {
919 	if( isRoot )
920 	{
921 		delete[] buffer;
922 		pDatabases->cascadingStylesheet( urlParam->get_language(),
923 										 &buffer,
924 										 &len );
925 	}
926 	else if( urlParam->isActive() )
927 	{
928 		delete[] buffer;
929 		pDatabases->setActiveText( urlParam->get_module(),
930 								   urlParam->get_language(),
931 								   urlParam->get_id(),
932 								   &buffer,
933 								   &len );
934 	}
935 	else
936 	{
937 		UserData userData( this,urlParam,pDatabases );
938 
939 		// Uses the implementation detail, that rtl::OString::getStr returns a zero terminated character-array
940 
941 		const char* parameter[47];
942 		rtl::OString parString[46];
943 		int last = 0;
944 
945 		parString[last++] = "Program";
946 		rtl::OString aPureProgramm( urlParam->getByName( "Program" ) );
947 		parString[last++] = rtl::OString('\'') + aPureProgramm + rtl::OString('\'');
948 		parString[last++] = "Database";
949 		parString[last++] = rtl::OString('\'') + urlParam->getByName( "DatabasePar" ) + rtl::OString('\'');
950 		parString[last++] = "Id";
951 		parString[last++] = rtl::OString('\'') + urlParam->getByName( "Id" ) + rtl::OString('\'');
952 		parString[last++] = "Path";
953 		rtl::OString aPath( urlParam->getByName( "Path" ) );
954 		parString[last++] = rtl::OString('\'') + aPath + rtl::OString('\'');
955 
956 		rtl::OString aPureLanguage = urlParam->getByName( "Language" );
957 		parString[last++] = "Language";
958 		parString[last++] = rtl::OString('\'') + aPureLanguage + rtl::OString('\'');
959 		parString[last++] = "System";
960 		parString[last++] = rtl::OString('\'') + urlParam->getByName( "System" ) + rtl::OString('\'');
961 		parString[last++] = "productname";
962 		parString[last++] = rtl::OString('\'') + rtl::OString(
963             pDatabases->getProductName().getStr(),
964             pDatabases->getProductName().getLength(),
965             RTL_TEXTENCODING_UTF8 ) + rtl::OString('\'');
966 		parString[last++] = "productversion";
967 		parString[last++] = rtl::OString('\'') +
968             rtl::OString(  pDatabases->getProductVersion().getStr(),
969                           pDatabases->getProductVersion().getLength(),
970                           RTL_TEXTENCODING_UTF8 ) + rtl::OString('\'');
971 
972         parString[last++] = "imgrepos";
973         parString[last++] = rtl::OString('\'') + pDatabases->getImagesZipFileURL() + rtl::OString('\'');
974         parString[last++] = "hp";
975         parString[last++] = rtl::OString('\'') + urlParam->getByName( "HelpPrefix" ) + rtl::OString('\'');
976 
977         if( parString[last-1].getLength() )
978         {
979             parString[last++] = "sm";
980             parString[last++] = "'vnd.sun.star.help%3A%2F%2F'";
981             parString[last++] = "qm";
982             parString[last++] = "'%3F'";
983             parString[last++] = "es";
984             parString[last++] = "'%3D'";
985             parString[last++] = "am";
986             parString[last++] = "'%26'";
987             parString[last++] = "cl";
988             parString[last++] = "'%3A'";
989             parString[last++] = "sl";
990             parString[last++] = "'%2F'";
991             parString[last++] = "hm";
992             parString[last++] = "'%23'";
993             parString[last++] = "cs";
994             parString[last++] = "'css'";
995 
996             parString[last++] = "vendorname";
997             parString[last++] = rtl::OString("''");
998             parString[last++] = "vendorversion";
999             parString[last++] = rtl::OString("''");
1000             parString[last++] = "vendorshort";
1001             parString[last++] = rtl::OString("''");
1002         }
1003 
1004 		// Do we need to add extension path?
1005 		::rtl::OUString aExtensionPath;
1006 		rtl::OUString aJar = urlParam->get_jar();
1007 
1008 		bool bAddExtensionPath = false;
1009         rtl::OUString aExtensionRegistryPath;
1010         sal_Int32 nQuestionMark1 = aJar.indexOf( sal_Unicode('?') );
1011         sal_Int32 nQuestionMark2 = aJar.lastIndexOf( sal_Unicode('?') );
1012 		if( nQuestionMark1 != -1 && nQuestionMark2 != -1 && nQuestionMark1 != nQuestionMark2 )
1013 		{
1014 			aExtensionPath = aJar.copy( nQuestionMark1 + 1, nQuestionMark2 - nQuestionMark1 - 1 );
1015             aExtensionRegistryPath = urlParam->get_ExtensionRegistryPath();
1016 			bAddExtensionPath = true;
1017 		}
1018 		else
1019 		{
1020 			// Path not yet specified, search directly
1021 			Reference< XHierarchicalNameAccess > xNA = pDatabases->findJarFileForPath
1022 				( aJar, urlParam->get_language(), urlParam->get_path(), &aExtensionPath, &aExtensionRegistryPath );
1023 			if( xNA.is() && aExtensionPath.getLength() )
1024 				bAddExtensionPath = true;
1025 		}
1026 
1027 		if( bAddExtensionPath )
1028 		{
1029 			Reference< XMultiServiceFactory > xFactory = comphelper::getProcessServiceFactory();
1030 			Reference< XPropertySet > xProps( xFactory, UNO_QUERY );
1031 			OSL_ASSERT( xProps.is() );
1032 			Reference< XComponentContext > xContext;
1033 			if (xProps.is())
1034 			{
1035 				xProps->getPropertyValue(
1036 					::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("DefaultContext") ) ) >>= xContext;
1037 			}
1038 			if( !xContext.is() )
1039 			{
1040 				throw RuntimeException(
1041 					::rtl::OUString::createFromAscii( "InputStreamTransformer::InputStreamTransformer(), no XComponentContext" ),
1042 					Reference< XInterface >() );
1043 			}
1044 
1045 			rtl::OUString aOUExpandedExtensionPath = Databases::expandURL( aExtensionRegistryPath, xContext );
1046 			rtl::OString aExpandedExtensionPath = rtl::OUStringToOString( aOUExpandedExtensionPath, osl_getThreadTextEncoding() );
1047 
1048 			parString[last++] = "ExtensionPath";
1049 			parString[last++] = rtl::OString('\'') + aExpandedExtensionPath + rtl::OString('\'');
1050 
1051 			// ExtensionId
1052 			rtl::OString aPureExtensionId;
1053 			sal_Int32 iSlash = aPath.indexOf( '/' );
1054 			if( iSlash != -1 )
1055 				aPureExtensionId = aPath.copy( 0, iSlash );
1056 
1057 			parString[last++] = "ExtensionId";
1058 			parString[last++] = rtl::OString('\'') + aPureExtensionId + rtl::OString('\'');
1059 		}
1060 
1061 		for( int i = 0; i < last; ++i )
1062 			parameter[i] = parString[i].getStr();
1063 		parameter[last] = 0;
1064 
1065 		rtl::OUString xslURL = pDatabases->getInstallPathAsURL();
1066 
1067 		rtl::OString xslURLascii(
1068 			xslURL.getStr(),
1069 			xslURL.getLength(),
1070 			RTL_TEXTENCODING_UTF8);
1071 		xslURLascii += "main_transform.xsl";
1072 
1073         ugblData = &userData;
1074 
1075         xmlInitParser();
1076         xmlRegisterInputCallbacks(zipMatch, zipOpen, zipRead, uriClose);
1077         xmlRegisterInputCallbacks(helpMatch, helpOpen, helpRead, uriClose);
1078 		xmlRegisterInputCallbacks(fileMatch, fileOpen, fileRead, fileClose);
1079 		//xmlSetStructuredErrorFunc( NULL, (xmlStructuredErrorFunc)StructuredXMLErrorFunction );
1080 
1081         xsltStylesheetPtr cur =
1082             xsltParseStylesheetFile((const xmlChar *)xslURLascii.getStr());
1083 
1084         xmlDocPtr doc = xmlParseFile("vnd.sun.star.zip:/");
1085 
1086         xmlDocPtr res = xsltApplyStylesheet(cur, doc, parameter);
1087         if (res)
1088 		{
1089 			xmlChar *doc_txt_ptr=0;
1090 			int doc_txt_len;
1091 			xsltSaveResultToString(&doc_txt_ptr, &doc_txt_len, res, cur);
1092 			addToBuffer((const char*)doc_txt_ptr, doc_txt_len);
1093 			xmlFree(doc_txt_ptr);
1094         }
1095         xmlPopInputCallbacks();	//filePatch
1096         xmlPopInputCallbacks();	//helpPatch
1097         xmlPopInputCallbacks();	//zipMatch
1098         xmlFreeDoc(res);
1099         xmlFreeDoc(doc);
1100         xsltFreeStylesheet(cur);
1101 	}
1102 }
1103 
1104 
1105 InputStreamTransformer::~InputStreamTransformer()
1106 {
1107 	delete[] buffer;
1108 }
1109 
1110 
1111 Any SAL_CALL InputStreamTransformer::queryInterface( const Type& rType ) throw( RuntimeException )
1112 {
1113 	Any aRet = ::cppu::queryInterface( rType,
1114 									   SAL_STATIC_CAST( XInputStream*,this ),
1115 									   SAL_STATIC_CAST( XSeekable*,this ) );
1116 
1117 	return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
1118 }
1119 
1120 
1121 
1122 void SAL_CALL InputStreamTransformer::acquire( void ) throw()
1123 {
1124 	OWeakObject::acquire();
1125 }
1126 
1127 
1128 
1129 void SAL_CALL InputStreamTransformer::release( void ) throw()
1130 {
1131 	OWeakObject::release();
1132 }
1133 
1134 
1135 
1136 sal_Int32 SAL_CALL InputStreamTransformer::readBytes( Sequence< sal_Int8 >& aData,sal_Int32 nBytesToRead )
1137 	throw( NotConnectedException,
1138 		   BufferSizeExceededException,
1139 		   IOException,
1140 		   RuntimeException)
1141 {
1142 	osl::MutexGuard aGuard( m_aMutex );
1143 
1144 	int curr,available_ = len-pos;
1145 	if( nBytesToRead <= available_ )
1146 		curr = nBytesToRead;
1147 	else
1148 		curr = available_;
1149 
1150 	if( 0 <= curr && aData.getLength() < curr )
1151 		aData.realloc( curr );
1152 
1153 	for( int k = 0; k < curr; ++k )
1154 		aData[k] = buffer[pos++];
1155 
1156 	return curr > 0 ? curr : 0;
1157 }
1158 
1159 
1160 sal_Int32 SAL_CALL InputStreamTransformer::readSomeBytes( Sequence< sal_Int8 >& aData,sal_Int32 nMaxBytesToRead )
1161 	throw( NotConnectedException,
1162 		   BufferSizeExceededException,
1163 		   IOException,
1164 		   RuntimeException)
1165 {
1166 	return readBytes( aData,nMaxBytesToRead );
1167 }
1168 
1169 
1170 
1171 void SAL_CALL InputStreamTransformer::skipBytes( sal_Int32 nBytesToSkip ) throw( NotConnectedException,
1172 																				 BufferSizeExceededException,
1173 																				 IOException,
1174 																				 RuntimeException )
1175 {
1176 	osl::MutexGuard aGuard( m_aMutex );
1177 	while( nBytesToSkip-- ) ++pos;
1178 }
1179 
1180 
1181 
1182 sal_Int32 SAL_CALL InputStreamTransformer::available( void ) throw( NotConnectedException,
1183 																	IOException,
1184 																	RuntimeException )
1185 {
1186 	osl::MutexGuard aGuard( m_aMutex );
1187 	return len-pos > 0 ? len - pos : 0 ;
1188 }
1189 
1190 
1191 
1192 void SAL_CALL InputStreamTransformer::closeInput( void ) throw( NotConnectedException,
1193 																IOException,
1194 																RuntimeException )
1195 {
1196 }
1197 
1198 
1199 
1200 void SAL_CALL InputStreamTransformer::seek( sal_Int64 location ) throw( IllegalArgumentException,
1201 																		IOException,
1202 																		RuntimeException )
1203 {
1204 	osl::MutexGuard aGuard( m_aMutex );
1205 	if( location < 0 )
1206 		throw IllegalArgumentException();
1207 	else
1208 		pos = sal::static_int_cast<sal_Int32>( location );
1209 
1210 	if( pos > len )
1211 		pos = len;
1212 }
1213 
1214 
1215 
1216 sal_Int64 SAL_CALL InputStreamTransformer::getPosition( void ) throw( IOException,
1217 																	  RuntimeException )
1218 {
1219 	osl::MutexGuard aGuard( m_aMutex );
1220 	return sal_Int64( pos );
1221 }
1222 
1223 
1224 
1225 sal_Int64 SAL_CALL InputStreamTransformer::getLength( void ) throw( IOException,RuntimeException )
1226 {
1227 	osl::MutexGuard aGuard( m_aMutex );
1228 
1229 	return len;
1230 }
1231 
1232 
1233 void InputStreamTransformer::addToBuffer( const char* buffer_,int len_ )
1234 {
1235 	osl::MutexGuard aGuard( m_aMutex );
1236 
1237 	char* tmp = buffer;
1238 	buffer = new char[ len+len_ ];
1239 	rtl_copyMemory( (void*)(buffer),(void*)(tmp),sal_uInt32( len ) );
1240 	rtl_copyMemory( (void*)(buffer+len),(void*)(buffer_),sal_uInt32( len_ ) );
1241 	delete[] tmp;
1242 	len += len_;
1243 }
1244