xref: /trunk/main/xmlhelp/source/cxxhelp/provider/urlparameter.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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