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