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