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 #include "db.hxx"
31 #ifndef _VOS_DIAGNOSE_HXX_
32 #include <vos/diagnose.hxx>
33 #endif
34 #include <osl/thread.h>
35 #include <rtl/uri.hxx>
36 #include <osl/file.hxx>
37 #include <rtl/memory.h>
38 #include <com/sun/star/lang/Locale.hpp>
39 #include <rtl/ustrbuf.hxx>
40 #include "inputstream.hxx"
41 #include <algorithm>
42 #include <string.h>
43 
44 // Extensible help
45 #include "com/sun/star/deployment/ExtensionManager.hpp"
46 #include "com/sun/star/deployment/thePackageManagerFactory.hpp"
47 #include <comphelper/processfactory.hxx>
48 #include <com/sun/star/beans/XPropertySet.hpp>
49 #include <com/sun/star/uno/XComponentContext.hpp>
50 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
51 #include <com/sun/star/beans/Optional.hpp>
52 #include <com/sun/star/beans/PropertyValue.hpp>
53 #include <com/sun/star/beans/NamedValue.hpp>
54 #include <com/sun/star/frame/XConfigManager.hpp>
55 #include <com/sun/star/util/XMacroExpander.hpp>
56 #include <com/sun/star/uri/XUriReferenceFactory.hpp>
57 #include <com/sun/star/uri/XVndSunStarExpandUrl.hpp>
58 #include <com/sun/star/script/XInvocation.hpp>
59 #include <comphelper/locale.hxx>
60 
61 #include <com/sun/star/awt/XToolkit.hpp>
62 #include <com/sun/star/awt/XExtendedToolkit.hpp>
63 #include <com/sun/star/awt/XWindowPeer.hpp>
64 #include <com/sun/star/awt/XVclWindowPeer.hpp>
65 #include <com/sun/star/awt/XTopWindow.hpp>
66 
67 #include <l10ntools/compilehelp.hxx>
68 #include <comphelper/storagehelper.hxx>
69 
70 #include "databases.hxx"
71 #include "urlparameter.hxx"
72 
73 using namespace chelp;
74 using namespace berkeleydbproxy;
75 using namespace com::sun::star;
76 using namespace com::sun::star::uno;
77 using namespace com::sun::star::io;
78 using namespace com::sun::star::container;
79 using namespace com::sun::star::i18n;
80 using namespace com::sun::star::lang;
81 using namespace com::sun::star::deployment;
82 using namespace com::sun::star::beans;
83 
84 
85 static rtl::OUString aSlash( rtl::OUString::createFromAscii( "/" ) );
86 static rtl::OUString aHelpFilesBaseName( rtl::OUString::createFromAscii( "help" ) );
87 static rtl::OUString aHelpMediaType( rtl::OUString::createFromAscii( "application/vnd.sun.star.help" ) );
88 
89 rtl::OUString Databases::expandURL( const rtl::OUString& aURL )
90 {
91 	osl::MutexGuard aGuard( m_aMutex );
92 	rtl::OUString aRetURL = expandURL( aURL, m_xContext );
93 	return aRetURL;
94 }
95 
96 rtl::OUString Databases::expandURL( const rtl::OUString& aURL, Reference< uno::XComponentContext > xContext )
97 {
98 	static Reference< util::XMacroExpander > xMacroExpander;
99 	static Reference< uri::XUriReferenceFactory > xFac;
100 
101 	if( !xContext.is() )
102 		return rtl::OUString();
103 
104 	if( !xMacroExpander.is() || !xFac.is() )
105 	{
106 		Reference< XMultiComponentFactory > xSMgr( xContext->getServiceManager(), UNO_QUERY );
107 
108 		xFac = Reference< uri::XUriReferenceFactory >(
109 			xSMgr->createInstanceWithContext( rtl::OUString::createFromAscii(
110 			"com.sun.star.uri.UriReferenceFactory"), xContext ) , UNO_QUERY );
111 		if( !xFac.is() )
112 		{
113 			throw RuntimeException(
114 				::rtl::OUString::createFromAscii( "Databases::expand(), could not instatiate UriReferenceFactory." ),
115 				Reference< XInterface >() );
116 		}
117 
118 		xMacroExpander = Reference< util::XMacroExpander >(
119 			xContext->getValueByName(
120 			::rtl::OUString::createFromAscii( "/singletons/com.sun.star.util.theMacroExpander" ) ),
121 			UNO_QUERY_THROW );
122  	}
123 
124 	rtl::OUString aRetURL = aURL;
125 	if( xMacroExpander.is() )
126 	{
127 		Reference< uri::XUriReference > uriRef;
128 		for (;;)
129 		{
130 			uriRef = Reference< uri::XUriReference >( xFac->parse( aRetURL ), UNO_QUERY );
131 			if ( uriRef.is() )
132 			{
133 				Reference < uri::XVndSunStarExpandUrl > sxUri( uriRef, UNO_QUERY );
134 				if( !sxUri.is() )
135 					break;
136 
137 				aRetURL = sxUri->expand( xMacroExpander );
138 			}
139 		}
140  	}
141 	return aRetURL;
142 }
143 
144 Databases::Databases( sal_Bool showBasic,
145 					  const rtl::OUString& instPath,
146 					  const com::sun::star::uno::Sequence< rtl::OUString >& imagesZipPaths,
147 					  const rtl::OUString& productName,
148 					  const rtl::OUString& productVersion,
149 					  const rtl::OUString& styleSheet,
150 					  Reference< uno::XComponentContext > xContext )
151 	: m_xContext( xContext ),
152 	  m_bShowBasic(showBasic),
153 	  m_nErrorDocLength( 0 ),
154 	  m_pErrorDoc( 0 ),
155 	  m_nCustomCSSDocLength( 0 ),
156 	  m_pCustomCSSDoc( 0 ),
157 	  m_aCSS(styleSheet.toAsciiLowerCase()),
158 	  newProdName(rtl::OUString::createFromAscii( "$[officename]" ) ),
159       newProdVersion(rtl::OUString::createFromAscii( "$[officeversion]" ) ),
160       prodName( rtl::OUString::createFromAscii( "%PRODUCTNAME" ) ),
161 	  prodVersion( rtl::OUString::createFromAscii( "%PRODUCTVERSION" ) ),
162 	  vendName( rtl::OUString::createFromAscii( "%VENDORNAME" ) ),
163 	  vendVersion( rtl::OUString::createFromAscii( "%VENDORVERSION" ) ),
164 	  vendShort( rtl::OUString::createFromAscii( "%VENDORSHORT" ) ),
165 	  m_aImagesZipPaths( imagesZipPaths ),
166 	  m_nSymbolsStyle( 0 )
167 {
168 	m_xSMgr = Reference< XMultiComponentFactory >( m_xContext->getServiceManager(), UNO_QUERY );
169 
170 	m_vAdd[0] = 12;
171 	m_vAdd[1] = 15;
172 	m_vAdd[2] = 11;
173 	m_vAdd[3] = 14;
174 	m_vAdd[4] = 12;
175 	m_vAdd[5] = 13;
176     m_vAdd[6] = 16;
177 
178 	m_vReplacement[0] = productName;
179 	m_vReplacement[1] = productVersion;
180     // m_vReplacement[2...4] (vendorName/-Version/-Short) are empty strings
181 	m_vReplacement[5] = productName;
182     m_vReplacement[6] = productVersion;
183 
184 	setInstallPath( instPath );
185 
186 	m_xSFA = Reference< ucb::XSimpleFileAccess >(
187 		m_xSMgr->createInstanceWithContext( rtl::OUString::createFromAscii( "com.sun.star.ucb.SimpleFileAccess" ),
188 		m_xContext ), UNO_QUERY_THROW );
189 }
190 
191 Databases::~Databases()
192 {
193 	// release stylesheet
194 
195 	delete[] m_pCustomCSSDoc;
196 
197 	// release errorDocument
198 
199 	delete[] m_pErrorDoc;
200 
201 	// unload the databases
202 
203 	{
204 		// DatabasesTable
205 		DatabasesTable::iterator it = m_aDatabases.begin();
206 		while( it != m_aDatabases.end() )
207 		{
208             if( it->second )
209                 it->second->close( 0 );
210             delete it->second;
211             ++it;
212 		}
213 	}
214 
215 	{
216 		//  ModInfoTable
217 
218 		ModInfoTable::iterator it = m_aModInfo.begin();
219 		while( it != m_aModInfo.end() )
220 		{
221 			delete it->second;
222 			++it;
223 		}
224 	}
225 
226 	{
227 		// KeywordInfoTable
228 
229 		KeywordInfoTable::iterator it = m_aKeywordInfo.begin();
230 		while( it != m_aKeywordInfo.end() )
231 		{
232 			delete it->second;
233 			++it;
234 		}
235 	}
236 }
237 
238 static bool impl_getZipFile(
239         Sequence< rtl::OUString > & rImagesZipPaths,
240         const rtl::OUString & rZipName,
241         rtl::OUString & rFileName )
242 {
243     const rtl::OUString *pPathArray = rImagesZipPaths.getArray();
244     for ( int i = 0; i < rImagesZipPaths.getLength(); ++i )
245     {
246         rFileName = pPathArray[ i ];
247         if ( rFileName.getLength() )
248         {
249             if ( 1 + rFileName.lastIndexOf( '/' ) != rFileName.getLength() )
250             {
251                 rFileName += rtl::OUString::createFromAscii( "/" );
252             }
253             rFileName += rZipName;
254 
255             // test existence
256             osl::DirectoryItem aDirItem;
257             if ( osl::DirectoryItem::get( rFileName, aDirItem ) == osl::FileBase::E_None )
258                 return true;
259         }
260     }
261     return false;
262 }
263 
264 rtl::OString Databases::getImagesZipFileURL()
265 {
266     //sal_Int16 nSymbolsStyle = SvtMiscOptions().GetCurrentSymbolsStyle();
267     sal_Int16 nSymbolsStyle = 0;
268     try
269     {
270         uno::Reference< lang::XMultiServiceFactory > xConfigProvider(
271             m_xSMgr ->createInstanceWithContext(::rtl::OUString::createFromAscii("com.sun.star.configuration.ConfigurationProvider"), m_xContext), uno::UNO_QUERY_THROW);
272 
273         // set root path
274         uno::Sequence < uno::Any > lParams(1);
275         beans::PropertyValue                       aParam ;
276         aParam.Name    = ::rtl::OUString::createFromAscii("nodepath");
277         aParam.Value <<= ::rtl::OUString::createFromAscii("org.openoffice.Office.Common");
278         lParams[0] = uno::makeAny(aParam);
279 
280         // open it
281         uno::Reference< uno::XInterface > xCFG( xConfigProvider->createInstanceWithArguments(
282                     ::rtl::OUString::createFromAscii("com.sun.star.configuration.ConfigurationAccess"),
283                     lParams) );
284 
285         bool bChanged = false;
286 		uno::Reference< container::XHierarchicalNameAccess > xAccess(xCFG, uno::UNO_QUERY_THROW);
287         uno::Any aResult = xAccess->getByHierarchicalName(::rtl::OUString::createFromAscii("Misc/SymbolSet"));
288         if ( (aResult >>= nSymbolsStyle) && m_nSymbolsStyle != nSymbolsStyle )
289 		{
290 			m_nSymbolsStyle = nSymbolsStyle;
291 			bChanged = true;
292 		}
293 
294         if ( !m_aImagesZipFileURL.getLength() || bChanged )
295         {
296             rtl::OUString aImageZip, aSymbolsStyleName;
297 			aResult = xAccess->getByHierarchicalName(::rtl::OUString::createFromAscii("Misc/SymbolStyle"));
298 			aResult >>= aSymbolsStyleName;
299 
300             bool bFound = false;
301             if ( aSymbolsStyleName.getLength() != 0 )
302             {
303                 rtl::OUString aZipName = rtl::OUString::createFromAscii( "images_" );
304                 aZipName += aSymbolsStyleName;
305                 aZipName += rtl::OUString::createFromAscii( ".zip" );
306 
307                 bFound = impl_getZipFile( m_aImagesZipPaths, aZipName, aImageZip );
308             }
309 
310             if ( ! bFound )
311                 bFound = impl_getZipFile( m_aImagesZipPaths, rtl::OUString::createFromAscii( "images.zip" ), aImageZip );
312 
313             if ( ! bFound )
314                 aImageZip = rtl::OUString();
315 
316             m_aImagesZipFileURL = rtl::OUStringToOString(
317                         rtl::Uri::encode(
318                             aImageZip,
319                             rtl_UriCharClassPchar,
320                             rtl_UriEncodeIgnoreEscapes,
321                             RTL_TEXTENCODING_UTF8 ), RTL_TEXTENCODING_UTF8 );
322         }
323     }
324     catch ( NoSuchElementException const & )
325     {
326     }
327 
328     return m_aImagesZipFileURL;
329 }
330 
331 void Databases::replaceName( rtl::OUString& oustring ) const
332 {
333 	sal_Int32 idx = -1,idx1 = -1,idx2 = -1,k = 0,off;
334 	bool cap = false;
335 	rtl::OUStringBuffer aStrBuf( 0 );
336 
337 	while( true )
338 	{
339         ++idx;
340         idx1 = oustring.indexOf( sal_Unicode('%'),idx);
341         idx2 = oustring.indexOf( sal_Unicode('$'),idx);
342 
343         if(idx1 == -1 && idx2 == -1)
344             break;
345 
346         if(idx1 == -1)
347             idx = idx2;
348         else if(idx2 == -1)
349             idx = idx1;
350         else {
351             // no index is zero
352             if(idx1 < idx2)
353                 idx = idx1;
354             else if(idx2 < idx1 )
355                 idx = idx2;
356         }
357 
358 		if( oustring.indexOf( prodName,idx ) == idx )
359 			off = PRODUCTNAME;
360 		else if( oustring.indexOf( prodVersion,idx ) == idx )
361 			off = PRODUCTVERSION;
362 		else if( oustring.indexOf( vendName,idx ) == idx )
363 			off = VENDORNAME;
364 		else if( oustring.indexOf( vendVersion,idx ) == idx )
365 			off = VENDORVERSION;
366 		else if( oustring.indexOf( vendShort,idx ) == idx )
367 			off = VENDORSHORT;
368 		else if( oustring.indexOf( newProdName,idx ) == idx )
369 			off = NEWPRODUCTNAME;
370 		else if( oustring.indexOf( newProdVersion,idx ) == idx )
371 			off = NEWPRODUCTVERSION;
372 		else
373 			off = -1;
374 
375 		if( off != -1 )
376 		{
377 			if( ! cap )
378 			{
379 				cap = true;
380 				aStrBuf.ensureCapacity( 256 );
381 			}
382 
383 			aStrBuf.append( &oustring.getStr()[k],idx - k );
384 			aStrBuf.append( m_vReplacement[off] );
385 			k = idx + m_vAdd[off];
386 		}
387 	}
388 
389 	if( cap )
390 	{
391 		if( k < oustring.getLength() )
392 			aStrBuf.append( &oustring.getStr()[k],oustring.getLength()-k );
393 		oustring = aStrBuf.makeStringAndClear();
394 	}
395 }
396 
397 
398 
399 
400 rtl::OUString Databases::getInstallPathAsSystemPath()
401 {
402 	osl::MutexGuard aGuard( m_aMutex );
403 
404 	if( ! m_aInstallDirectoryAsSystemPath.getLength() )
405 	{
406 #ifdef DBG_UTIL
407 		bool bla =
408 			osl::FileBase::E_None ==
409 			osl::FileBase::getSystemPathFromFileURL( m_aInstallDirectory,m_aInstallDirectoryAsSystemPath );
410         VOS_ENSURE( bla,"HelpProvider, no installpath" );
411 #else
412         osl::FileBase::getSystemPathFromFileURL( m_aInstallDirectory,m_aInstallDirectoryAsSystemPath );
413 #endif
414 	}
415 
416 	return m_aInstallDirectoryAsSystemPath;
417 }
418 
419 rtl::OUString Databases::getInstallPathAsURL()
420 {
421 	osl::MutexGuard aGuard( m_aMutex );
422 
423   	return m_aInstallDirectory;
424 }
425 
426 
427 const std::vector< rtl::OUString >& Databases::getModuleList( const rtl::OUString& Language )
428 {
429 	if( m_avModules.size() == 0 )
430 	{
431 		rtl::OUString  fileName,dirName = getInstallPathAsURL() + processLang( Language );
432 		osl::Directory dirFile( dirName );
433 
434 		osl::DirectoryItem aDirItem;
435 		osl::FileStatus    aStatus( FileStatusMask_FileName );
436 
437 		sal_Int32 idx;
438 
439 		if( osl::FileBase::E_None != dirFile.open() )
440 			return m_avModules;
441 
442 		while( dirFile.getNextItem( aDirItem ) == osl::FileBase::E_None &&
443 			   aDirItem.getFileStatus( aStatus ) == osl::FileBase::E_None )
444 		{
445 			if( ! aStatus.isValid( FileStatusMask_FileName ) )
446 				continue;
447 
448 			fileName = aStatus.getFileName();
449 
450 			// Check, whether fileName is of the form *.cfg
451 			idx = fileName.lastIndexOf(  sal_Unicode( '.' ) );
452 
453 			if( idx == -1 )
454 				continue;
455 
456 			const sal_Unicode* str = fileName.getStr();
457 
458 			if( fileName.getLength() == idx + 4                   &&
459 				( str[idx + 1] == 'c' || str[idx + 1] == 'C' )    &&
460 				( str[idx + 2] == 'f' || str[idx + 2] == 'F' )    &&
461 				( str[idx + 3] == 'g' || str[idx + 3] == 'G' )    &&
462 				( fileName = fileName.copy(0,idx).toAsciiLowerCase() ).compareToAscii( "picture" ) != 0 ) {
463 			  if(! m_bShowBasic && fileName.compareToAscii("sbasic") == 0 )
464 				continue;
465 			  m_avModules.push_back( fileName );
466 			}
467 		}
468 	}
469 	return m_avModules;
470 }
471 
472 
473 
474 StaticModuleInformation* Databases::getStaticInformationForModule( const rtl::OUString& Module,
475 																   const rtl::OUString& Language )
476 {
477 	osl::MutexGuard aGuard( m_aMutex );
478 
479 	rtl::OUString key = processLang(Language) + rtl::OUString::createFromAscii( "/" ) + Module;
480 
481 	std::pair< ModInfoTable::iterator,bool > aPair =
482 		m_aModInfo.insert( ModInfoTable::value_type( key,0 ) );
483 
484 	ModInfoTable::iterator it = aPair.first;
485 
486 	if( aPair.second && ! it->second )
487 	{
488 		osl::File cfgFile( getInstallPathAsURL() +
489 						   key +
490 						   rtl::OUString::createFromAscii( ".cfg" ) );
491 
492 		if( osl::FileBase::E_None != cfgFile.open( OpenFlag_Read ) )
493 			it->second = 0;
494 		else
495 		{
496 			sal_uInt32 pos = 0;
497 			sal_uInt64 nRead;
498 			sal_Char buffer[2048];
499 			sal_Unicode lineBuffer[1028];
500 			rtl::OUString fileContent;
501 
502 			while( osl::FileBase::E_None == cfgFile.read( &buffer,2048,nRead ) && nRead )
503 				fileContent += rtl::OUString( buffer,sal_Int32( nRead ),RTL_TEXTENCODING_UTF8 );
504 
505 			cfgFile.close();
506 
507 			const sal_Unicode* str = fileContent.getStr();
508 			rtl::OUString current,lang_,program,startid,title,heading,fulltext;
509             rtl::OUString order = rtl::OUString::createFromAscii( "1" );
510 
511 			for( sal_Int32 i = 0;i < fileContent.getLength();i++ )
512 			{
513 				sal_Unicode ch = str[ i ];
514 				if( ch == sal_Unicode( '\n' ) || ch == sal_Unicode( '\r' ) )
515 				{
516 					if( pos )
517 					{
518 						current = rtl::OUString( lineBuffer,pos );
519 
520 						if( current.compareToAscii( "Title",5 ) == 0 )
521 						{
522 							title = current.copy( current.indexOf(sal_Unicode( '=' ) ) + 1 );
523 						}
524 						else if( current.compareToAscii( "Start",5 ) == 0 )
525 						{
526 							startid = current.copy( current.indexOf('=') + 1 );
527 						}
528 						else if( current.compareToAscii( "Language",8 ) == 0 )
529 						{
530 							lang_ = current.copy( current.indexOf('=') + 1 );
531 						}
532 						else if( current.compareToAscii( "Program",7 ) == 0 )
533 						{
534 							program = current.copy( current.indexOf('=') + 1 );
535 						}
536 						else if( current.compareToAscii( "Heading",7 ) == 0 )
537 						{
538 							heading = current.copy( current.indexOf('=') + 1 );
539 						}
540 						else if( current.compareToAscii( "FullText",8 ) == 0 )
541 						{
542 							fulltext = current.copy( current.indexOf('=') + 1 );
543 						}
544 						else if( current.compareToAscii( "Order",5 ) == 0 )
545 						{
546 							order = current.copy( current.indexOf('=') + 1 );
547 						}
548 					}
549 					pos = 0;
550 				}
551 				else
552 					lineBuffer[ pos++ ] = ch;
553 			}
554 			replaceName( title );
555 			it->second = new StaticModuleInformation( title,
556 													  startid,
557 													  program,
558 													  heading,
559 													  fulltext,
560 													  order );
561 		}
562 	}
563 
564 	return it->second;
565 }
566 
567 
568 
569 
570 rtl::OUString Databases::processLang( const rtl::OUString& Language )
571 {
572 	osl::MutexGuard aGuard( m_aMutex );
573 
574 	rtl::OUString ret;
575 	LangSetTable::iterator it = m_aLangSet.find( Language );
576 
577 	if( it == m_aLangSet.end() )
578 	{
579 		sal_Int32 idx;
580 		osl::DirectoryItem aDirItem;
581 
582 		if( osl::FileBase::E_None == osl::DirectoryItem::get( getInstallPathAsURL() + Language,aDirItem ) )
583 		{
584 			ret = Language;
585 			m_aLangSet[ Language ] = ret;
586 		}
587 		else if( ( ( idx = Language.indexOf( '-' ) ) != -1 ||
588 				   ( idx = Language.indexOf( '_' ) ) != -1 ) &&
589 					osl::FileBase::E_None == osl::DirectoryItem::get( getInstallPathAsURL() + Language.copy( 0,idx ),
590 																   aDirItem ) )
591 		{
592 			ret = Language.copy( 0,idx );
593 			m_aLangSet[ Language ] = ret;
594 		}
595 	}
596 	else
597 		ret = it->second;
598 
599 	return ret;
600 }
601 
602 
603 rtl::OUString Databases::country( const rtl::OUString& Language )
604 {
605 	sal_Int32 idx;
606 	if( ( idx = Language.indexOf( '-' ) ) != -1 ||
607 		( idx = Language.indexOf( '_' ) ) != -1 )
608 		return Language.copy( 1+idx );
609 
610 	return rtl::OUString();
611 }
612 
613 
614 
615 Db* Databases::getBerkeley( const rtl::OUString& Database,
616 							const rtl::OUString& Language, bool helpText,
617 							const rtl::OUString* pExtensionPath )
618 {
619 	if( ! Database.getLength() || ! Language.getLength() )
620 		return 0;
621 
622 	osl::MutexGuard aGuard( m_aMutex );
623 
624 
625 	rtl::OUString aFileExt( rtl::OUString::createFromAscii( helpText ? ".ht" : ".db" ) );
626 	rtl::OUString dbFileName = aSlash + Database + aFileExt;
627 	rtl::OUString key;
628 	if( pExtensionPath == NULL )
629 		key = processLang( Language ) + dbFileName;
630 	else
631 		key = *pExtensionPath + Language + dbFileName;		// make unique, don't change language
632 
633 	std::pair< DatabasesTable::iterator,bool > aPair =
634 		m_aDatabases.insert( DatabasesTable::value_type( key,0 ) );
635 
636 	DatabasesTable::iterator it = aPair.first;
637 
638 	if( aPair.second && ! it->second )
639 	{
640 		Db* table = new Db();
641 
642 		rtl::OUString fileURL;
643 		if( pExtensionPath )
644             fileURL = expandURL(*pExtensionPath) + Language + dbFileName;
645 		else
646             fileURL = getInstallPathAsURL() + key;
647 
648 		rtl::OUString fileNameDBHelp( fileURL );
649         //Extensions always use the new format
650 		if( pExtensionPath != NULL )
651 			fileNameDBHelp += rtl::OUString::createFromAscii( "_" );
652         //SimpleFileAccess takes file URLs as arguments!!! Using filenames works accidentally but
653         //fails for example when using long path names on Windows (starting with \\?\)
654 		if( m_xSFA->exists( fileNameDBHelp ) )
655 		{
656 			DBHelp* pDBHelp = new DBHelp( fileNameDBHelp, m_xSFA );
657 			table->setDBHelp( pDBHelp );
658 
659 #ifdef TEST_DBHELP
660 			bool bSuccess;
661 			bool bOldDbAccess = false;
662 			bSuccess = pDBHelp->testAgainstDb( fileURL, bOldDbAccess );
663 
664 			bOldDbAccess = true;
665 			bSuccess = pDBHelp->testAgainstDb( fileURL, bOldDbAccess );
666 #endif
667 		}
668 		else if( table->open( 0,fileURL, DB_BTREE,DB_RDONLY,0644 ) )
669 		{
670 			table->close( 0 );
671 			delete table;
672 			table = 0;
673 		}
674 
675 		it->second = table;
676 	}
677 
678 	return it->second;
679 }
680 
681 Reference< XCollator >
682 Databases::getCollator( const rtl::OUString& Language,
683 						const rtl::OUString& System )
684 {
685 	(void)System;
686 
687 	rtl::OUString key = Language;
688 
689 	osl::MutexGuard aGuard( m_aMutex );
690 
691 	CollatorTable::iterator it =
692 		m_aCollatorTable.insert( CollatorTable::value_type( key,0 ) ).first;
693 
694 	if( ! it->second.is() )
695 	{
696 		it->second =
697 			Reference< XCollator > (
698 				m_xSMgr->createInstanceWithContext( rtl::OUString::createFromAscii( "com.sun.star.i18n.Collator" ),
699 				m_xContext ), UNO_QUERY );
700 		rtl::OUString langStr = processLang(Language);
701 		rtl::OUString countryStr = country(Language);
702 		if( !countryStr.getLength() )
703 		{
704 			if( langStr.compareToAscii("de") == 0 )
705 				countryStr = rtl::OUString::createFromAscii("DE");
706 			else if( langStr.compareToAscii("en") == 0 )
707 				countryStr = rtl::OUString::createFromAscii("US");
708 			else if( langStr.compareToAscii("es") == 0 )
709 				countryStr = rtl::OUString::createFromAscii("ES");
710 			else if( langStr.compareToAscii("it") == 0 )
711 				countryStr = rtl::OUString::createFromAscii("IT");
712 			else if( langStr.compareToAscii("fr") == 0 )
713 				countryStr = rtl::OUString::createFromAscii("FR");
714 			else if( langStr.compareToAscii("sv") == 0 )
715 				countryStr = rtl::OUString::createFromAscii("SE");
716 			else if( langStr.compareToAscii("ja") == 0 )
717 				countryStr = rtl::OUString::createFromAscii("JP");
718 			else if( langStr.compareToAscii("ko") == 0 )
719 				countryStr = rtl::OUString::createFromAscii("KR");
720 		}
721 		it->second->loadDefaultCollator(  Locale( langStr,
722 												  countryStr,
723 												  rtl::OUString() ),
724 										  0 );
725 	}
726 
727 	return it->second;
728 }
729 
730 
731 
732 namespace chelp {
733 
734     struct KeywordElementComparator
735     {
736         KeywordElementComparator( const Reference< XCollator >& xCollator )
737             : m_xCollator( xCollator )
738         { }
739 
740         bool operator()( const KeywordInfo::KeywordElement& la,
741                          const KeywordInfo::KeywordElement& ra ) const
742         {
743             const rtl::OUString& l = la.key;
744             const rtl::OUString& r = ra.key;
745 
746             bool ret;
747 
748             if( m_xCollator.is() )
749             {
750                 sal_Int32 l1 = l.indexOf( sal_Unicode( ';' ) );
751                 sal_Int32 l3 = ( l1 == -1 ? l.getLength() : l1 );
752 
753                 sal_Int32 r1 = r.indexOf( sal_Unicode( ';' ) );
754                 sal_Int32 r3 = ( r1 == -1 ? r.getLength() : r1 );
755 
756                 sal_Int32 c1 = m_xCollator->compareSubstring( l,0,l3,r,0,r3 );
757 
758                 if( c1 == +1 )
759                     ret = false;
760                 else if( c1 == 0 )
761                 {
762                     sal_Int32 l2 = l.getLength() - l1 - 1;
763                     sal_Int32 r2 = r.getLength() - r1 - 1;
764                     ret = ( m_xCollator->compareSubstring( l,1+l1,l2,r,1+r1,r2 ) < 0 );
765                 }
766                 else
767                     ret = true;
768             }
769             else
770                 ret = bool( l < r );
771 
772             return ret;
773         }
774 
775         Reference< XCollator > m_xCollator;
776     }; // end struct KeywordElementComparator
777 
778 }
779 
780 
781 
782 KeywordInfo::KeywordElement::KeywordElement( Databases *pDatabases,
783 											 Db* pDb,
784 											 rtl::OUString& ky,
785 											 rtl::OUString& data )
786 	: key( ky )
787 {
788 	pDatabases->replaceName( key );
789 	init( pDatabases,pDb,data );
790 }
791 
792 
793 
794 void KeywordInfo::KeywordElement::init( Databases *pDatabases,Db* pDb,const rtl::OUString& ids )
795 {
796 	const sal_Unicode* idstr = ids.getStr();
797 	std::vector< rtl::OUString > id,anchor;
798 	int idx = -1,k;
799 	while( ( idx = ids.indexOf( ';',k = ++idx ) ) != -1 )
800 	{
801 		int h = ids.indexOf( sal_Unicode( '#' ),k );
802 		if( h < idx )
803 		{
804 			// found an anchor
805 			id.push_back( rtl::OUString( &idstr[k],h-k ) );
806 			anchor.push_back( rtl::OUString( &idstr[h+1],idx-h-1 ) );
807 		}
808 		else
809 		{
810 			id.push_back( rtl::OUString( &idstr[k],idx-k ) );
811 			anchor.push_back( rtl::OUString() );
812 		}
813 	}
814 
815 	listId.realloc( id.size() );
816 	listAnchor.realloc( id.size() );
817 	listTitle.realloc( id.size() );
818 
819 	int nSize = 0;
820 	const sal_Char* pData = NULL;
821 	const sal_Char pEmpty[] = "";
822 
823 	for( sal_uInt32 i = 0; i < id.size(); ++i )
824 	{
825 		listId[i] = id[i];
826 		listAnchor[i] = anchor[i];
827 
828 		nSize = 0;
829 		pData = pEmpty;
830 		if( pDb )
831 		{
832 			rtl::OString idi( id[i].getStr(),id[i].getLength(),RTL_TEXTENCODING_UTF8 );
833 			DBHelp* pDBHelp = pDb->getDBHelp();
834 			if( pDBHelp != NULL )
835 			{
836 				DBData aDBData;
837 				bool bSuccess = pDBHelp->getValueForKey( idi, aDBData );
838 				if( bSuccess )
839 				{
840 					nSize = aDBData.getSize();
841 					pData = aDBData.getData();
842 				}
843 			}
844 			else
845 			{
846 				Dbt key_( static_cast< void* >( const_cast< sal_Char* >( idi.getStr() ) ),
847 						 idi.getLength() );
848 				Dbt data;
849 				pDb->get( 0,&key_,&data,0 );
850 				nSize = data.get_size();
851 				pData = static_cast<sal_Char*>( data.get_data() );
852 			}
853 		}
854 
855 		DbtToStringConverter converter( pData, nSize );
856 
857 		rtl::OUString title = converter.getTitle();
858 		pDatabases->replaceName( title );
859 		listTitle[i] = title;
860 	}
861 }
862 
863 
864 
865 KeywordInfo::KeywordInfo( const std::vector< KeywordElement >& aVec )
866 	: listKey( aVec.size() ),
867 	  listId( aVec.size() ),
868 	  listAnchor( aVec.size() ),
869 	  listTitle( aVec.size() )
870 {
871     for( unsigned int i = 0; i < aVec.size(); ++i )
872 	{
873 		listKey[i] = aVec[i].key;
874 		listId[i] = aVec[i].listId;
875 		listAnchor[i] = aVec[i].listAnchor;
876 		listTitle[i] = aVec[i].listTitle;
877 	}
878 }
879 
880 bool Databases::checkModuleMatchForExtension
881 	( const rtl::OUString& Database, const rtl::OUString& doclist )
882 {
883 	bool bBelongsToDatabase = true;
884 
885 	// Analyse doclist string to find module assignments
886 	bool bFoundAtLeastOneModule = false;
887 	bool bModuleMatch = false;
888 	sal_Int32 nLen = doclist.getLength();
889 	sal_Int32 nLastFound = doclist.lastIndexOf( sal_Unicode(';') );
890 	if( nLastFound == -1 )
891 		nLastFound = nLen;
892 	const sal_Unicode* pStr = doclist.getStr();
893 	sal_Int32 nFound = doclist.lastIndexOf( sal_Unicode('_') );
894 	while( nFound != -1 )
895 	{
896 		// Simple optimization, stop if '_' is followed by "id"
897 		if( nLen - nFound > 2 )
898 		{
899 			if( pStr[ nFound + 1 ] == sal_Unicode('i') &&
900 				pStr[ nFound + 2 ] == sal_Unicode('d') )
901 					break;
902 		}
903 
904 		rtl::OUString aModule = doclist.copy( nFound + 1, nLastFound - nFound - 1 );
905 		std::vector< rtl::OUString >::iterator result = std::find( m_avModules.begin(), m_avModules.end(), aModule );
906 		if( result != m_avModules.end() )
907 		{
908 			bFoundAtLeastOneModule = true;
909 			if( Database == aModule )
910 			{
911 				bModuleMatch = true;
912 				break;
913 			}
914 		}
915 
916 		nLastFound = nFound;
917 		if( nLastFound == 0 )
918 			break;
919 		nFound = doclist.lastIndexOf( sal_Unicode('_'), nLastFound - 1 );
920 	}
921 
922 	if( bFoundAtLeastOneModule && !bModuleMatch )
923 		bBelongsToDatabase = false;
924 
925 	return bBelongsToDatabase;
926 }
927 
928 
929 KeywordInfo* Databases::getKeyword( const rtl::OUString& Database,
930 									const rtl::OUString& Language )
931 {
932 	osl::MutexGuard aGuard( m_aMutex );
933 
934 	rtl::OUString key = processLang(Language) + rtl::OUString::createFromAscii( "/" ) + Database;
935 
936     std::pair< KeywordInfoTable::iterator,bool > aPair =
937 		m_aKeywordInfo.insert( KeywordInfoTable::value_type( key,0 ) );
938 
939 	KeywordInfoTable::iterator it = aPair.first;
940 
941 	if( aPair.second && ! it->second )
942 	{
943 		std::vector<KeywordInfo::KeywordElement> aVector;
944 
945 		KeyDataBaseFileIterator aDbFileIt( m_xContext, *this, Database, Language );
946 		rtl::OUString fileURL;
947 		bool bExtension = false;
948 		while( (fileURL = aDbFileIt.nextDbFile( bExtension )).getLength() > 0 )
949 		{
950 			Db table;
951 
952 			rtl::OUString fileNameDBHelp( fileURL );
953 			if( bExtension )
954 				fileNameDBHelp += rtl::OUString::createFromAscii( "_" );
955 			if( m_xSFA->exists( fileNameDBHelp ) )
956 			{
957 				DBHelp aDBHelp( fileNameDBHelp, m_xSFA );
958 
959 				DBData aKey;
960 				DBData aValue;
961 				if( aDBHelp.startIteration() )
962 				{
963 					Db* idmap = getBerkeley( Database,Language );
964 
965 				    DBHelp* pDBHelp = idmap->getDBHelp();
966 					if( pDBHelp != NULL )
967 					{
968 						bool bOptimizeForPerformance = true;
969 						pDBHelp->releaseHashMap();
970 						pDBHelp->createHashMap( bOptimizeForPerformance );
971 					}
972 
973 					while( aDBHelp.getNextKeyAndValue( aKey, aValue ) )
974 					{
975 						rtl::OUString keyword( aKey.getData(), aKey.getSize(),
976 											   RTL_TEXTENCODING_UTF8 );
977 						rtl::OUString doclist( aValue.getData(), aValue.getSize(),
978 											   RTL_TEXTENCODING_UTF8 );
979 
980 						bool bBelongsToDatabase = true;
981 						if( bExtension )
982 							bBelongsToDatabase = checkModuleMatchForExtension( Database, doclist );
983 
984 						if( !bBelongsToDatabase )
985 							continue;
986 
987 						aVector.push_back( KeywordInfo::KeywordElement( this,
988 																		idmap,
989 																		keyword,
990 																		doclist ) );
991 					}
992 					aDBHelp.stopIteration();
993 
994 					if( pDBHelp != NULL )
995 						pDBHelp->releaseHashMap();
996 				}
997 
998 #ifdef TEST_DBHELP
999 				bool bSuccess;
1000 				bool bOldDbAccess = false;
1001 				bSuccess = aDBHelp.testAgainstDb( fileURL, bOldDbAccess );
1002 
1003 				bOldDbAccess = true;
1004 				bSuccess = aDBHelp.testAgainstDb( fileURL, bOldDbAccess );
1005 
1006 				int nDummy = 0;
1007 #endif
1008 			}
1009 
1010 			else if( 0 == table.open( 0,fileURL,DB_BTREE,DB_RDONLY,0644 ) )
1011 			{
1012 				Db* idmap = getBerkeley( Database,Language );
1013 
1014 				bool first = true;
1015 
1016 				Dbc* cursor = 0;
1017 				table.cursor( 0,&cursor,0 );
1018 				Dbt key_,data;
1019 				key_.set_flags( DB_DBT_MALLOC ); // Initially the cursor must allocate the necessary memory
1020 				data.set_flags( DB_DBT_MALLOC );
1021 				while( cursor && DB_NOTFOUND != cursor->get( &key_,&data,DB_NEXT ) )
1022 				{
1023 					rtl::OUString keyword( static_cast<sal_Char*>(key_.get_data()),
1024 										   key_.get_size(),
1025 										   RTL_TEXTENCODING_UTF8 );
1026 					rtl::OUString doclist( static_cast<sal_Char*>(data.get_data()),
1027 										   data.get_size(),
1028 										   RTL_TEXTENCODING_UTF8 );
1029 
1030 					bool bBelongsToDatabase = true;
1031 					if( bExtension )
1032 						bBelongsToDatabase = checkModuleMatchForExtension( Database, doclist );
1033 
1034 					if( !bBelongsToDatabase )
1035 						continue;
1036 
1037 					aVector.push_back( KeywordInfo::KeywordElement( this,
1038 																	idmap,
1039 																	keyword,
1040 																	doclist ) );
1041 					if( first )
1042 					{
1043 						key_.set_flags( DB_DBT_REALLOC );
1044 						data.set_flags( DB_DBT_REALLOC );
1045 						first = false;
1046 					}
1047 				}
1048 
1049 				if( cursor ) cursor->close();
1050 			}
1051 			table.close( 0 );
1052 		}
1053 
1054 		// sorting
1055 		Reference< XCollator > xCollator = getCollator( Language,rtl::OUString());
1056 		KeywordElementComparator aComparator( xCollator );
1057 		std::sort(aVector.begin(),aVector.end(),aComparator);
1058 
1059 		KeywordInfo* pInfo = it->second = new KeywordInfo( aVector );
1060 		(void)pInfo;
1061 	}
1062 
1063 	return it->second;
1064 }
1065 
1066 Reference< XHierarchicalNameAccess > Databases::jarFile( const rtl::OUString& jar,
1067 														 const rtl::OUString& Language )
1068 {
1069 	if( ! jar.getLength() ||
1070 		! Language.getLength() )
1071     {
1072 		return Reference< XHierarchicalNameAccess >( 0 );
1073     }
1074 	rtl::OUString key = processLang(Language) + aSlash + jar;
1075 
1076 	osl::MutexGuard aGuard( m_aMutex );
1077 
1078 	ZipFileTable::iterator it =
1079 		m_aZipFileTable.insert( ZipFileTable::value_type( key,Reference< XHierarchicalNameAccess >(0) ) ).first;
1080 
1081 	if( ! it->second.is() )
1082 	{
1083         rtl::OUString zipFile;
1084 		try
1085 		{
1086 			// Extension jar file? Search for ?
1087 	        sal_Int32 nQuestionMark1 = jar.indexOf( sal_Unicode('?') );
1088 	        sal_Int32 nQuestionMark2 = jar.lastIndexOf( sal_Unicode('?') );
1089 			if( nQuestionMark1 != -1 && nQuestionMark2 != -1 && nQuestionMark1 != nQuestionMark2 )
1090 			{
1091 				::rtl::OUString aExtensionPath = jar.copy( nQuestionMark1 + 1, nQuestionMark2 - nQuestionMark1 - 1 );
1092 				::rtl::OUString aPureJar = jar.copy( nQuestionMark2 + 1 );
1093 
1094 				rtl::OUStringBuffer aStrBuf;
1095 				aStrBuf.append( aExtensionPath );
1096 				aStrBuf.append( aSlash );
1097 				aStrBuf.append( aPureJar );
1098 
1099 				zipFile = expandURL( aStrBuf.makeStringAndClear() );
1100 			}
1101 			else
1102 			{
1103 	            zipFile = getInstallPathAsURL() + key;
1104 			}
1105 
1106             Sequence< Any > aArguments( 2 );
1107 
1108             XInputStream_impl* p = new XInputStream_impl( zipFile );
1109             if( p->CtorSuccess() )
1110             {
1111                 Reference< XInputStream > xInputStream( p );
1112                 aArguments[ 0 ] <<= xInputStream;
1113             }
1114             else
1115             {
1116                 delete p;
1117                 aArguments[ 0 ] <<= zipFile;
1118             }
1119 
1120             // let ZipPackage be used ( no manifest.xml is required )
1121             beans::NamedValue aArg;
1122             aArg.Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StorageFormat" ) );
1123             aArg.Value <<= ZIP_STORAGE_FORMAT_STRING;
1124             aArguments[ 1 ] <<= aArg;
1125 
1126 			Reference< XInterface > xIfc
1127 				= m_xSMgr->createInstanceWithArgumentsAndContext(
1128 					rtl::OUString::createFromAscii(
1129 						"com.sun.star.packages.comp.ZipPackage" ),
1130 					aArguments, m_xContext );
1131 
1132 			if ( xIfc.is() )
1133 			{
1134 				it->second = Reference< XHierarchicalNameAccess >( xIfc, UNO_QUERY );
1135 
1136 				VOS_ENSURE( it->second.is(),
1137 							"ContentProvider::createPackage - "
1138 							"Got no hierarchical name access!" );
1139 
1140 			}
1141 		}
1142 		catch ( RuntimeException & )
1143 		{
1144 		}
1145 		catch ( Exception & )
1146 		{
1147 		}
1148 	}
1149 
1150 	return it->second;
1151 }
1152 
1153 Reference< XHierarchicalNameAccess > Databases::findJarFileForPath
1154 	( const rtl::OUString& jar, const rtl::OUString& Language,
1155 	  const rtl::OUString& path, rtl::OUString* o_pExtensionPath,
1156       rtl::OUString* o_pExtensionRegistryPath )
1157 {
1158 	Reference< XHierarchicalNameAccess > xNA;
1159 	if( ! jar.getLength() ||
1160 		! Language.getLength() )
1161     {
1162 		return xNA;
1163     }
1164 
1165 	JarFileIterator aJarFileIt( m_xContext, *this, jar, Language );
1166 	Reference< XHierarchicalNameAccess > xTestNA;
1167 	Reference< deployment::XPackage > xParentPackageBundle;
1168 	while( (xTestNA = aJarFileIt.nextJarFile( xParentPackageBundle, o_pExtensionPath, o_pExtensionRegistryPath )).is() )
1169 	{
1170 		if( xTestNA.is() && xTestNA->hasByHierarchicalName( path ) )
1171 		{
1172 			bool bSuccess = true;
1173 			if( xParentPackageBundle.is() )
1174 			{
1175 				rtl::OUString aIdentifierInPath;
1176 				sal_Int32 nFindSlash = path.indexOf( '/' );
1177 				if( nFindSlash != -1 )
1178 					aIdentifierInPath = path.copy( 0, nFindSlash );
1179 
1180 				beans::Optional<rtl::OUString> aIdentifierOptional = xParentPackageBundle->getIdentifier();
1181 				if( aIdentifierInPath.getLength() && aIdentifierOptional.IsPresent )
1182 				{
1183 					rtl::OUString aUnencodedIdentifier = aIdentifierOptional.Value;
1184 					rtl::OUString aIdentifier = rtl::Uri::encode( aUnencodedIdentifier,
1185 						rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8 );
1186 
1187 					if( !aIdentifierInPath.equals( aIdentifier ) )
1188 					{
1189 						// path does not start with extension identifier -> ignore
1190 						bSuccess = false;
1191 					}
1192 				}
1193 				else
1194 				{
1195 					// No identifier -> ignore
1196 					bSuccess = false;
1197 				}
1198 			}
1199 
1200 			if( bSuccess )
1201 			{
1202 				xNA = xTestNA;
1203 				break;
1204 			}
1205 		}
1206 	}
1207 
1208 	return xNA;
1209 }
1210 
1211 void Databases::popupDocument( URLParameter* urlPar,char **buffer,int *byteCount )
1212 {
1213 	const char* pop1 =
1214 		" <html>                                                                "
1215 		" <head>                                                                "
1216 		" <help:css-file-link xmlns:help=\"http://openoffice.org/2000/help\"/>  "
1217 		" </head>                                                               "
1218 		" <body>                                                                "
1219 		" <help:popup-cut Id=\"";
1220 	const sal_Int32 l1 = strlen( pop1 );
1221 
1222 	const char* pop3 = "\" Eid=\"";
1223 	const sal_Int32 l3 = strlen( pop3 );
1224 
1225 	const char* pop5 =
1226 		"\" xmlns:help=\"http://openoffice.org/2000/help\"></help:popup-cut>  "
1227 		" </body>                                                             "
1228 		" </html>";
1229 	const sal_Int32 l5 = strlen( pop5 );
1230 	sal_Int32 l2,l4;
1231 
1232 	rtl::OUString val = urlPar->get_id();
1233 	rtl::OString pop2O( val.getStr(),l2 = val.getLength(),RTL_TEXTENCODING_UTF8 );
1234 	const char* pop2 = pop2O.getStr();
1235 
1236 	val = urlPar->get_eid();
1237 	rtl::OString pop4O( val.getStr(),l4 = val.getLength(),RTL_TEXTENCODING_UTF8 );
1238 	const char* pop4 = pop4O.getStr();
1239 
1240 	(*byteCount) = l1 + l2 + l3 + l4 + l5;
1241 
1242 	*buffer = new char[ 1+*byteCount ];
1243 
1244 	rtl_copyMemory( *buffer,pop1,l1 );
1245 	rtl_copyMemory( *buffer+l1,pop2,l2 );
1246 	rtl_copyMemory( *buffer+(l1+l2),pop3,l3 );
1247 	rtl_copyMemory( *buffer+(l1+l2+l3),pop4,l4 );
1248 	rtl_copyMemory( *buffer+(l1+l2+l3+l4),pop5,l5 );
1249 	(*buffer)[*byteCount] = 0;
1250 }
1251 
1252 
1253 void Databases::changeCSS(const rtl::OUString& newStyleSheet)
1254 {
1255 	m_aCSS = newStyleSheet.toAsciiLowerCase();
1256 	delete[] m_pCustomCSSDoc, m_pCustomCSSDoc = 0,m_nCustomCSSDocLength = 0;
1257 }
1258 
1259 
1260 
1261 void Databases::cascadingStylesheet( const rtl::OUString& Language,
1262 									 char** buffer,
1263 									 int* byteCount )
1264 {
1265 	if( ! m_pCustomCSSDoc )
1266 	{
1267 		int retry = 2;
1268 		bool error = true;
1269 		rtl::OUString fileURL;
1270 
1271         sal_Bool bHighContrastMode = sal_False;
1272         rtl::OUString aCSS( m_aCSS );
1273         if ( aCSS.compareToAscii( "default" ) == 0 )
1274         {
1275             // #i50760: "default" needs to adapt HC mode
1276             uno::Reference< awt::XToolkit > xToolkit = uno::Reference< awt::XToolkit >(
1277                     ::comphelper::getProcessServiceFactory()->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.Toolkit" ) ) ), uno::UNO_QUERY );
1278             if ( xToolkit.is() )
1279             {
1280                 uno::Reference< awt::XExtendedToolkit > xExtToolkit( xToolkit, uno::UNO_QUERY );
1281                 if ( xExtToolkit.is() )
1282                 {
1283                     uno::Reference< awt::XTopWindow > xTopWindow = xExtToolkit->getActiveTopWindow();
1284                     if ( xTopWindow.is() )
1285                     {
1286                         uno::Reference< awt::XVclWindowPeer > xVclWindowPeer( xTopWindow, uno::UNO_QUERY );
1287                         if ( xVclWindowPeer.is() )
1288                         {
1289                             uno::Any aHCMode = xVclWindowPeer->getProperty( rtl::OUString::createFromAscii( "HighContrastMode" ) );
1290                             if ( ( aHCMode >>= bHighContrastMode ) && bHighContrastMode )
1291                                 aCSS = rtl::OUString::createFromAscii( "highcontrastblack" );
1292                         }
1293                     }
1294                 }
1295             }
1296         }
1297 
1298 		while( error && retry )
1299 		{
1300 
1301 			if( retry == 2 )
1302 				fileURL =
1303 					getInstallPathAsURL()  +
1304 					processLang( Language )       +
1305 					rtl::OUString::createFromAscii( "/" ) +
1306 					aCSS +
1307 					rtl::OUString::createFromAscii( ".css" );
1308 			else if( retry == 1 )
1309 				fileURL =
1310 					getInstallPathAsURL()  +
1311 					aCSS +
1312 					rtl::OUString::createFromAscii( ".css" );
1313 
1314 			osl::DirectoryItem aDirItem;
1315 			osl::File aFile( fileURL );
1316 			osl::FileStatus aStatus( FileStatusMask_FileSize );
1317 
1318 			if( osl::FileBase::E_None == osl::DirectoryItem::get( fileURL,aDirItem ) &&
1319 				osl::FileBase::E_None == aFile.open( OpenFlag_Read )                 &&
1320 				osl::FileBase::E_None == aDirItem.getFileStatus( aStatus ) )
1321 			{
1322 				m_nCustomCSSDocLength = int( aStatus.getFileSize() );
1323 				m_pCustomCSSDoc = new char[ 1 + m_nCustomCSSDocLength ];
1324 				m_pCustomCSSDoc[ m_nCustomCSSDocLength ] = 0;
1325 				sal_uInt64 a = m_nCustomCSSDocLength,b = m_nCustomCSSDocLength;
1326 				aFile.read( m_pCustomCSSDoc,a,b );
1327 				aFile.close();
1328 				error = false;
1329 			}
1330 
1331 			--retry;
1332 			if ( !retry && error && bHighContrastMode )
1333 			{
1334 			    // fall back to default css
1335 			    aCSS = rtl::OUString::createFromAscii( "default" );
1336 			    retry = 2;
1337 			    bHighContrastMode = sal_False;
1338 			}
1339 		}
1340 
1341 		if( error )
1342 		{
1343 			m_nCustomCSSDocLength = 0;
1344 			m_pCustomCSSDoc = new char[ 1 ]; // Initialize with 1 to avoid gcc compiler warning
1345 		}
1346 	}
1347 
1348 	*byteCount = m_nCustomCSSDocLength;
1349 	*buffer = new char[ 1 + *byteCount ];
1350 	(*buffer)[*byteCount] = 0;
1351 	rtl_copyMemory( *buffer,m_pCustomCSSDoc,m_nCustomCSSDocLength );
1352 
1353 }
1354 
1355 
1356 void Databases::setActiveText( const rtl::OUString& Module,
1357 							   const rtl::OUString& Language,
1358 							   const rtl::OUString& Id,
1359 							   char** buffer,
1360 							   int* byteCount )
1361 {
1362 	DataBaseIterator aDbIt( m_xContext, *this, Module, Language, true );
1363 
1364 	// #i84550 Cache information about failed ids
1365 	rtl::OString id( Id.getStr(),Id.getLength(),RTL_TEXTENCODING_UTF8 );
1366 	EmptyActiveTextSet::iterator it = m_aEmptyActiveTextSet.find( id );
1367 	bool bFoundAsEmpty = ( it != m_aEmptyActiveTextSet.end() );
1368 	Dbt data;
1369 	DBData aDBData;
1370 
1371 	int nSize = 0;
1372 	const sal_Char* pData = NULL;
1373 
1374 	bool bSuccess = false;
1375 	if( !bFoundAsEmpty )
1376 	{
1377 		Db* db;
1378 		Dbt key( static_cast< void* >( const_cast< sal_Char* >( id.getStr() ) ),id.getLength() );
1379 		while( !bSuccess && (db = aDbIt.nextDb()) != NULL )
1380 		{
1381 			DBHelp* pDBHelp = db->getDBHelp();
1382 			if( pDBHelp != NULL )
1383 			{
1384 				bSuccess = pDBHelp->getValueForKey( id, aDBData );
1385 				nSize = aDBData.getSize();
1386 				pData = aDBData.getData();
1387 			}
1388 			else
1389 			{
1390 				int err = db->get( 0, &key, &data, 0 );
1391 				if( err == 0 )
1392 				{
1393 					bSuccess = true;
1394 					nSize = data.get_size();
1395 					pData = static_cast<sal_Char*>( data.get_data() );
1396 				}
1397 			}
1398 		}
1399 	}
1400 
1401 	if( bSuccess )
1402 	{
1403 		// ensure existence of tmp after for
1404 		rtl::OString tmp;
1405 		for( int i = 0; i < nSize; ++i )
1406 			if( pData[i] == '%' || pData[i] == '$' )
1407 			{
1408 				// need of replacement
1409 				rtl::OUString temp = rtl::OUString( pData, nSize, RTL_TEXTENCODING_UTF8 );
1410 				replaceName( temp );
1411 				tmp = rtl::OString( temp.getStr(),
1412 									temp.getLength(),
1413 									RTL_TEXTENCODING_UTF8 );
1414 				nSize = tmp.getLength();
1415 				pData = tmp.getStr();
1416 				break;
1417 			}
1418 
1419 		*byteCount = nSize;
1420 		*buffer = new char[ 1 + nSize ];
1421 		(*buffer)[nSize] = 0;
1422 		rtl_copyMemory( *buffer, pData, nSize );
1423 	}
1424 	else
1425 	{
1426 		*byteCount = 0;
1427 		*buffer = new char[1]; // Initialize with 1 to avoid compiler warnings
1428 		if( !bFoundAsEmpty )
1429 			m_aEmptyActiveTextSet.insert( id );
1430 	}
1431 }
1432 
1433 
1434 void Databases::setInstallPath( const rtl::OUString& aInstDir )
1435 {
1436 	osl::MutexGuard aGuard( m_aMutex );
1437 
1438     osl::FileBase::getFileURLFromSystemPath( aInstDir,m_aInstallDirectory );
1439         //TODO: check returned error code
1440 
1441 	if( m_aInstallDirectory.lastIndexOf( sal_Unicode( '/' ) ) != m_aInstallDirectory.getLength() - 1 )
1442 		m_aInstallDirectory += rtl::OUString::createFromAscii( "/" );
1443 
1444 	m_aInstallDirectoryWithoutEncoding = rtl::Uri::decode( m_aInstallDirectory,
1445 														   rtl_UriDecodeWithCharset,
1446 														   RTL_TEXTENCODING_UTF8 );
1447 }
1448 
1449 
1450 //===================================================================
1451 // class ExtensionIteratorBase
1452 
1453 ExtensionHelpExistanceMap ExtensionIteratorBase::aHelpExistanceMap;
1454 
1455 ExtensionIteratorBase::ExtensionIteratorBase( Reference< XComponentContext > xContext,
1456 	Databases& rDatabases, const rtl::OUString& aInitialModule, const rtl::OUString& aLanguage )
1457 		: m_xContext( xContext )
1458 		, m_rDatabases( rDatabases )
1459 		, m_eState( INITIAL_MODULE )
1460 		, m_aInitialModule( aInitialModule )
1461 		, m_aLanguage( aLanguage )
1462 {
1463 	init();
1464 }
1465 
1466 ExtensionIteratorBase::ExtensionIteratorBase( Databases& rDatabases,
1467 	const rtl::OUString& aInitialModule, const rtl::OUString& aLanguage )
1468 		: m_rDatabases( rDatabases )
1469 		, m_eState( INITIAL_MODULE )
1470 		, m_aInitialModule( aInitialModule )
1471 		, m_aLanguage( aLanguage )
1472 {
1473 	init();
1474 }
1475 
1476 void ExtensionIteratorBase::init()
1477 {
1478 	if( !m_xContext.is() )
1479 	{
1480 		Reference< XMultiServiceFactory > xFactory = comphelper::getProcessServiceFactory();
1481 		Reference< XPropertySet > xProps( xFactory, UNO_QUERY );
1482 		OSL_ASSERT( xProps.is() );
1483 		if (xProps.is())
1484 		{
1485 			xProps->getPropertyValue(
1486 				::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("DefaultContext") ) ) >>= m_xContext;
1487 			OSL_ASSERT( m_xContext.is() );
1488 		}
1489 	}
1490 	if( !m_xContext.is() )
1491 	{
1492 		throw RuntimeException(
1493 			::rtl::OUString::createFromAscii( "ExtensionIteratorBase::init(), no XComponentContext" ),
1494 			Reference< XInterface >() );
1495 	}
1496 
1497 	Reference< XMultiComponentFactory > xSMgr( m_xContext->getServiceManager(), UNO_QUERY );
1498 	m_xSFA = Reference< ucb::XSimpleFileAccess >(
1499 		xSMgr->createInstanceWithContext( rtl::OUString::createFromAscii( "com.sun.star.ucb.SimpleFileAccess" ),
1500 		m_xContext ), UNO_QUERY_THROW );
1501 
1502 	m_bUserPackagesLoaded = false;
1503 	m_bSharedPackagesLoaded = false;
1504     m_bBundledPackagesLoaded = false;
1505 	m_iUserPackage = 0;
1506 	m_iSharedPackage = 0;
1507     m_iBundledPackage = 0;
1508 }
1509 
1510 Reference< deployment::XPackage > ExtensionIteratorBase::implGetHelpPackageFromPackage
1511 	( Reference< deployment::XPackage > xPackage, Reference< deployment::XPackage >& o_xParentPackageBundle )
1512 {
1513 	o_xParentPackageBundle.clear();
1514 
1515 	Reference< deployment::XPackage > xHelpPackage;
1516 	if( !xPackage.is() )
1517 		return xHelpPackage;
1518 
1519 	// #i84550 Cache information about help content in extension
1520 	rtl::OUString aExtensionPath = xPackage->getURL();
1521 	ExtensionHelpExistanceMap::iterator it = aHelpExistanceMap.find( aExtensionPath );
1522 	bool bFound = ( it != aHelpExistanceMap.end() );
1523 	bool bHasHelp = bFound ? it->second : false;
1524 	if( bFound && !bHasHelp )
1525 		return xHelpPackage;
1526 
1527 	// Check if parent package is registered
1528     beans::Optional< beans::Ambiguous<sal_Bool> > option( xPackage->isRegistered
1529 		( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() ) );
1530 	bool bRegistered = false;
1531     if( option.IsPresent )
1532     {
1533         beans::Ambiguous<sal_Bool> const & reg = option.Value;
1534         if( !reg.IsAmbiguous && reg.Value )
1535 			bRegistered = true;
1536     }
1537 	if( bRegistered )
1538 	{
1539 		if( xPackage->isBundle() )
1540 		{
1541 			Sequence< Reference< deployment::XPackage > > aPkgSeq = xPackage->getBundle
1542 				( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() );
1543 			sal_Int32 nPkgCount = aPkgSeq.getLength();
1544 			const Reference< deployment::XPackage >* pSeq = aPkgSeq.getConstArray();
1545 			for( sal_Int32 iPkg = 0 ; iPkg < nPkgCount ; ++iPkg )
1546 			{
1547 				const Reference< deployment::XPackage > xSubPkg = pSeq[ iPkg ];
1548 				const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xSubPkg->getPackageType();
1549 				rtl::OUString aMediaType = xPackageTypeInfo->getMediaType();
1550 				if( aMediaType.equals( aHelpMediaType ) )
1551 				{
1552 					xHelpPackage = xSubPkg;
1553 					o_xParentPackageBundle = xPackage;
1554 					break;
1555 				}
1556 			}
1557 		}
1558 		else
1559 		{
1560 			const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xPackage->getPackageType();
1561 			rtl::OUString aMediaType = xPackageTypeInfo->getMediaType();
1562 			if( aMediaType.equals( aHelpMediaType ) )
1563 				xHelpPackage = xPackage;
1564 		}
1565 	}
1566 
1567 	if( !bFound )
1568 		aHelpExistanceMap[ aExtensionPath ] = xHelpPackage.is();
1569 
1570 	return xHelpPackage;
1571 }
1572 
1573 Reference< deployment::XPackage > ExtensionIteratorBase::implGetNextUserHelpPackage
1574 	( Reference< deployment::XPackage >& o_xParentPackageBundle )
1575 {
1576 	Reference< deployment::XPackage > xHelpPackage;
1577 
1578 	if( !m_bUserPackagesLoaded )
1579 	{
1580         Reference< XExtensionManager > xExtensionManager = ExtensionManager::get(m_xContext);
1581 		m_aUserPackagesSeq = xExtensionManager->getDeployedExtensions
1582 			( rtl::OUString::createFromAscii("user"), Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
1583 		m_bUserPackagesLoaded = true;
1584 	}
1585 
1586 	if( m_iUserPackage == m_aUserPackagesSeq.getLength() )
1587 	{
1588 		m_eState = SHARED_EXTENSIONS;		// Later: SHARED_MODULE
1589 	}
1590 	else
1591 	{
1592 		const Reference< deployment::XPackage >* pUserPackages = m_aUserPackagesSeq.getConstArray();
1593 		Reference< deployment::XPackage > xPackage = pUserPackages[ m_iUserPackage++ ];
1594 		VOS_ENSURE( xPackage.is(), "ExtensionIteratorBase::implGetNextUserHelpPackage(): Invalid package" );
1595 		xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
1596 	}
1597 
1598 	return xHelpPackage;
1599 }
1600 
1601 Reference< deployment::XPackage > ExtensionIteratorBase::implGetNextSharedHelpPackage
1602 	( Reference< deployment::XPackage >& o_xParentPackageBundle )
1603 {
1604 	Reference< deployment::XPackage > xHelpPackage;
1605 
1606 	if( !m_bSharedPackagesLoaded )
1607 	{
1608 		Reference< XExtensionManager > xExtensionManager = ExtensionManager::get(m_xContext);
1609 		m_aSharedPackagesSeq = xExtensionManager->getDeployedExtensions
1610 			( rtl::OUString::createFromAscii("shared"), Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
1611 		m_bSharedPackagesLoaded = true;
1612 	}
1613 
1614 	if( m_iSharedPackage == m_aSharedPackagesSeq.getLength() )
1615 	{
1616 		m_eState = BUNDLED_EXTENSIONS;
1617 	}
1618 	else
1619 	{
1620 		const Reference< deployment::XPackage >* pSharedPackages = m_aSharedPackagesSeq.getConstArray();
1621 		Reference< deployment::XPackage > xPackage = pSharedPackages[ m_iSharedPackage++ ];
1622 		VOS_ENSURE( xPackage.is(), "ExtensionIteratorBase::implGetNextSharedHelpPackage(): Invalid package" );
1623 		xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
1624 	}
1625 
1626 	return xHelpPackage;
1627 }
1628 
1629 Reference< deployment::XPackage > ExtensionIteratorBase::implGetNextBundledHelpPackage
1630 	( Reference< deployment::XPackage >& o_xParentPackageBundle )
1631 {
1632 	Reference< deployment::XPackage > xHelpPackage;
1633 
1634 	if( !m_bBundledPackagesLoaded )
1635 	{
1636 		Reference< XExtensionManager > xExtensionManager = ExtensionManager::get(m_xContext);
1637 		m_aBundledPackagesSeq = xExtensionManager->getDeployedExtensions
1638 			( rtl::OUString::createFromAscii("bundled"), Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() );
1639 		m_bBundledPackagesLoaded = true;
1640 	}
1641 
1642 	if( m_iBundledPackage == m_aBundledPackagesSeq.getLength() )
1643 	{
1644 		m_eState = END_REACHED;
1645 	}
1646 	else
1647 	{
1648 		const Reference< deployment::XPackage >* pBundledPackages =
1649             m_aBundledPackagesSeq.getConstArray();
1650 		Reference< deployment::XPackage > xPackage = pBundledPackages[ m_iBundledPackage++ ];
1651 		VOS_ENSURE( xPackage.is(), "ExtensionIteratorBase::implGetNextBundledHelpPackage(): Invalid package" );
1652 		xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle );
1653 	}
1654 
1655 	return xHelpPackage;
1656 }
1657 
1658 rtl::OUString ExtensionIteratorBase::implGetFileFromPackage(
1659 	const rtl::OUString& rFileExtension, Reference< deployment::XPackage > xPackage )
1660 {
1661 	// No extension -> search for pure language folder
1662 	bool bLangFolderOnly = (rFileExtension.getLength() == 0);
1663 
1664 	rtl::OUString aFile;
1665 	rtl::OUString aLanguage = m_aLanguage;
1666 	for( sal_Int32 iPass = 0 ; iPass < 2 ; ++iPass )
1667 	{
1668 		rtl::OUStringBuffer aStrBuf;
1669 		aStrBuf.append( xPackage->getRegistrationDataURL().Value);
1670 		aStrBuf.append( aSlash );
1671 		aStrBuf.append( aLanguage );
1672 		if( !bLangFolderOnly )
1673 		{
1674 			aStrBuf.append( aSlash );
1675 			aStrBuf.append( aHelpFilesBaseName );
1676 			aStrBuf.append( rFileExtension );
1677 		}
1678 
1679 		aFile = m_rDatabases.expandURL( aStrBuf.makeStringAndClear() );
1680 		if( iPass == 0 )
1681 		{
1682 			if( m_xSFA->exists( aFile ) )
1683 				break;
1684 
1685 			::std::vector< ::rtl::OUString > av;
1686 			implGetLanguageVectorFromPackage( av, xPackage );
1687 			::std::vector< ::rtl::OUString >::const_iterator pFound = av.end();
1688 			try
1689 			{
1690 				pFound = ::comphelper::Locale::getFallback( av, m_aLanguage );
1691 			}
1692 			catch( ::comphelper::Locale::MalFormedLocaleException& )
1693 			{}
1694 			if( pFound != av.end() )
1695 				aLanguage = *pFound;
1696 		}
1697 	}
1698 	return aFile;
1699 }
1700 
1701 inline bool isLetter( sal_Unicode c )
1702 {
1703 	bool bLetter = ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
1704 	return bLetter;
1705 }
1706 
1707 void ExtensionIteratorBase::implGetLanguageVectorFromPackage( ::std::vector< ::rtl::OUString > &rv,
1708 	com::sun::star::uno::Reference< com::sun::star::deployment::XPackage > xPackage )
1709 {
1710 	rv.clear();
1711 	rtl::OUString aExtensionPath = xPackage->getURL();
1712 	Sequence< rtl::OUString > aEntrySeq = m_xSFA->getFolderContents( aExtensionPath, true );
1713 
1714 	const rtl::OUString* pSeq = aEntrySeq.getConstArray();
1715     sal_Int32 nCount = aEntrySeq.getLength();
1716 	for( sal_Int32 i = 0 ; i < nCount ; ++i )
1717 	{
1718 		rtl::OUString aEntry = pSeq[i];
1719 		if( m_xSFA->isFolder( aEntry ) )
1720 		{
1721 			sal_Int32 nLastSlash = aEntry.lastIndexOf( '/' );
1722 			if( nLastSlash != -1 )
1723 			{
1724 				rtl::OUString aPureEntry = aEntry.copy( nLastSlash + 1 );
1725 
1726 				// Check language sceme
1727 				int nLen = aPureEntry.getLength();
1728 				const sal_Unicode* pc = aPureEntry.getStr();
1729 				bool bStartCanBeLanguage = ( nLen >= 2 && isLetter( pc[0] ) && isLetter( pc[1] ) );
1730 				bool bIsLanguage = bStartCanBeLanguage &&
1731 					( nLen == 2 || (nLen == 5 && pc[2] == '-' && isLetter( pc[3] ) && isLetter( pc[4] )) );
1732 				if( bIsLanguage )
1733 					rv.push_back( aPureEntry );
1734 			}
1735 		}
1736 	}
1737 }
1738 
1739 
1740 //===================================================================
1741 // class DataBaseIterator
1742 
1743 Db* DataBaseIterator::nextDb( rtl::OUString* o_pExtensionPath, rtl::OUString* o_pExtensionRegistryPath )
1744 {
1745 	Db* pRetDb = NULL;
1746 
1747 	while( !pRetDb && m_eState != END_REACHED )
1748 	{
1749 		switch( m_eState )
1750 		{
1751 			case INITIAL_MODULE:
1752 				pRetDb = m_rDatabases.getBerkeley( m_aInitialModule, m_aLanguage, m_bHelpText );
1753 				m_eState = USER_EXTENSIONS;		// Later: SHARED_MODULE
1754 				break;
1755 
1756 			// Later:
1757 			//case SHARED_MODULE
1758 				//...
1759 
1760 			case USER_EXTENSIONS:
1761 			{
1762 				Reference< deployment::XPackage > xParentPackageBundle;
1763 				Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
1764 				if( !xHelpPackage.is() )
1765 					break;
1766 				pRetDb = implGetDbFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
1767 				break;
1768 			}
1769 
1770 			case SHARED_EXTENSIONS:
1771 			{
1772 				Reference< deployment::XPackage > xParentPackageBundle;
1773 				Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
1774 				if( !xHelpPackage.is() )
1775 					break;
1776 
1777 				pRetDb = implGetDbFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
1778 				break;
1779 			}
1780 
1781    			case BUNDLED_EXTENSIONS:
1782 			{
1783 				Reference< deployment::XPackage > xParentPackageBundle;
1784 				Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
1785 				if( !xHelpPackage.is() )
1786 					break;
1787 
1788 				pRetDb = implGetDbFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
1789 				break;
1790 			}
1791 
1792 			case END_REACHED:
1793 				VOS_ENSURE( false, "DataBaseIterator::nextDb(): Invalid case END_REACHED" );
1794 				break;
1795 		}
1796 	}
1797 
1798 	return pRetDb;
1799 }
1800 
1801 Db* DataBaseIterator::implGetDbFromPackage( Reference< deployment::XPackage > xPackage,
1802             rtl::OUString* o_pExtensionPath, rtl::OUString* o_pExtensionRegistryPath )
1803 {
1804 
1805     beans::Optional< ::rtl::OUString> optRegData;
1806     try
1807     {
1808         optRegData = xPackage->getRegistrationDataURL();
1809     }
1810     catch ( deployment::ExtensionRemovedException&)
1811     {
1812         return NULL;
1813     }
1814 
1815     Db* pRetDb = NULL;
1816     if (optRegData.IsPresent && optRegData.Value.getLength() > 0)
1817     {
1818         rtl::OUString aRegDataUrl(optRegData.Value);
1819         aRegDataUrl += aSlash;
1820 
1821         rtl::OUString aUsedLanguage = m_aLanguage;
1822         pRetDb = m_rDatabases.getBerkeley(
1823             aHelpFilesBaseName, aUsedLanguage, m_bHelpText, &aRegDataUrl);
1824 
1825         // Language fallback
1826         if( !pRetDb )
1827         {
1828             ::std::vector< ::rtl::OUString > av;
1829             implGetLanguageVectorFromPackage( av, xPackage );
1830             ::std::vector< ::rtl::OUString >::const_iterator pFound = av.end();
1831             try
1832             {
1833                 pFound = ::comphelper::Locale::getFallback( av, m_aLanguage );
1834             }
1835             catch( ::comphelper::Locale::MalFormedLocaleException& )
1836             {}
1837             if( pFound != av.end() )
1838             {
1839                 aUsedLanguage = *pFound;
1840                 pRetDb = m_rDatabases.getBerkeley(
1841                     aHelpFilesBaseName, aUsedLanguage, m_bHelpText, &aRegDataUrl);
1842             }
1843         }
1844 
1845         if( o_pExtensionPath )
1846             *o_pExtensionPath = aRegDataUrl + aUsedLanguage;
1847 
1848         if( o_pExtensionRegistryPath )
1849             *o_pExtensionRegistryPath = xPackage->getURL() + aSlash + aUsedLanguage;
1850     }
1851 
1852 	return pRetDb;
1853 }
1854 
1855 
1856 //===================================================================
1857 // class KeyDataBaseFileIterator
1858 
1859 //returns a file URL
1860 rtl::OUString KeyDataBaseFileIterator::nextDbFile( bool& o_rbExtension )
1861 {
1862 	rtl::OUString aRetFile;
1863 
1864 	while( !aRetFile.getLength() && m_eState != END_REACHED )
1865 	{
1866 		switch( m_eState )
1867 		{
1868 			case INITIAL_MODULE:
1869 				aRetFile =
1870                     m_rDatabases.getInstallPathAsURL() +
1871 					m_rDatabases.processLang( m_aLanguage ) + aSlash + m_aInitialModule +
1872 					rtl::OUString::createFromAscii( ".key" );
1873 
1874 				o_rbExtension = false;
1875 
1876 				m_eState = USER_EXTENSIONS;		// Later: SHARED_MODULE
1877 				break;
1878 
1879 			// Later:
1880 			//case SHARED_MODULE
1881 				//...
1882 
1883 			case USER_EXTENSIONS:
1884 			{
1885 				Reference< deployment::XPackage > xParentPackageBundle;
1886 				Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
1887 				if( !xHelpPackage.is() )
1888 					break;
1889 
1890 				aRetFile = implGetDbFileFromPackage( xHelpPackage );
1891 				o_rbExtension = true;
1892 				break;
1893 			}
1894 
1895 			case SHARED_EXTENSIONS:
1896 			{
1897 				Reference< deployment::XPackage > xParentPackageBundle;
1898 				Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
1899 				if( !xHelpPackage.is() )
1900 					break;
1901 
1902 				aRetFile = implGetDbFileFromPackage( xHelpPackage );
1903 				o_rbExtension = true;
1904 				break;
1905 			}
1906 
1907             case BUNDLED_EXTENSIONS:
1908 			{
1909 				Reference< deployment::XPackage > xParentPackageBundle;
1910 				Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
1911 				if( !xHelpPackage.is() )
1912 					break;
1913 
1914 				aRetFile = implGetDbFileFromPackage( xHelpPackage );
1915 				o_rbExtension = true;
1916 				break;
1917 			}
1918 
1919             case END_REACHED:
1920 				VOS_ENSURE( false, "DataBaseIterator::nextDbFile(): Invalid case END_REACHED" );
1921 				break;
1922 		}
1923 	}
1924 
1925 	return aRetFile;
1926 }
1927 
1928 //Returns a file URL, that does not contain macros
1929 rtl::OUString KeyDataBaseFileIterator::implGetDbFileFromPackage
1930 	( Reference< deployment::XPackage > xPackage )
1931 {
1932 	rtl::OUString aExpandedURL =
1933 		implGetFileFromPackage( rtl::OUString::createFromAscii( ".key" ), xPackage );
1934 
1935     return aExpandedURL;
1936 }
1937 
1938 
1939 //===================================================================
1940 // class JarFileIterator
1941 
1942 Reference< XHierarchicalNameAccess > JarFileIterator::nextJarFile
1943 	( Reference< deployment::XPackage >& o_xParentPackageBundle,
1944         rtl::OUString* o_pExtensionPath, rtl::OUString* o_pExtensionRegistryPath )
1945 {
1946 	Reference< XHierarchicalNameAccess > xNA;
1947 
1948 	while( !xNA.is() && m_eState != END_REACHED )
1949 	{
1950 		switch( m_eState )
1951 		{
1952 			case INITIAL_MODULE:
1953 				xNA = m_rDatabases.jarFile( m_aInitialModule, m_aLanguage );
1954 				m_eState = USER_EXTENSIONS;		// Later: SHARED_MODULE
1955 				break;
1956 
1957 			// Later:
1958 			//case SHARED_MODULE
1959 				//...
1960 
1961 			case USER_EXTENSIONS:
1962 			{
1963 				Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( o_xParentPackageBundle );
1964 				if( !xHelpPackage.is() )
1965 					break;
1966 
1967 				xNA = implGetJarFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
1968 				break;
1969 			}
1970 
1971 			case SHARED_EXTENSIONS:
1972 			{
1973 				Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( o_xParentPackageBundle );
1974 				if( !xHelpPackage.is() )
1975 					break;
1976 
1977 				xNA = implGetJarFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
1978 				break;
1979 			}
1980 
1981 			case BUNDLED_EXTENSIONS:
1982 			{
1983 				Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( o_xParentPackageBundle );
1984 				if( !xHelpPackage.is() )
1985 					break;
1986 
1987 				xNA = implGetJarFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath );
1988 				break;
1989 			}
1990 
1991             case END_REACHED:
1992 				VOS_ENSURE( false, "JarFileIterator::nextJarFile(): Invalid case END_REACHED" );
1993 				break;
1994 		}
1995 	}
1996 
1997 	return xNA;
1998 }
1999 
2000 Reference< XHierarchicalNameAccess > JarFileIterator::implGetJarFromPackage
2001 ( Reference< deployment::XPackage > xPackage, rtl::OUString* o_pExtensionPath, rtl::OUString* o_pExtensionRegistryPath )
2002 {
2003 	Reference< XHierarchicalNameAccess > xNA;
2004 
2005 	rtl::OUString zipFile =
2006 		implGetFileFromPackage( rtl::OUString::createFromAscii( ".jar" ), xPackage );
2007 
2008 	try
2009 	{
2010         Sequence< Any > aArguments( 2 );
2011         aArguments[ 0 ] <<= zipFile;
2012 
2013         // let ZipPackage be used ( no manifest.xml is required )
2014         beans::NamedValue aArg;
2015         aArg.Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StorageFormat" ) );
2016         aArg.Value <<= ZIP_STORAGE_FORMAT_STRING;
2017         aArguments[ 1 ] <<= aArg;
2018 
2019 		Reference< XMultiComponentFactory >xSMgr( m_xContext->getServiceManager(), UNO_QUERY );
2020 		Reference< XInterface > xIfc
2021 			= xSMgr->createInstanceWithArgumentsAndContext(
2022 				rtl::OUString::createFromAscii(
2023 					"com.sun.star.packages.comp.ZipPackage" ),
2024 				aArguments, m_xContext );
2025 
2026 		if ( xIfc.is() )
2027 		{
2028 			xNA = Reference< XHierarchicalNameAccess >( xIfc, UNO_QUERY );
2029 
2030 			VOS_ENSURE( xNA.is(),
2031 				"JarFileIterator::implGetJarFromPackage() - "
2032 				"Got no hierarchical name access!" );
2033 		}
2034 	}
2035 	catch ( RuntimeException & )
2036 	{}
2037 	catch ( Exception & )
2038 	{}
2039 
2040 	if( xNA.is() && o_pExtensionPath != NULL )
2041 	{
2042 		// Extract path including language from file name
2043 		sal_Int32 nLastSlash = zipFile.lastIndexOf( '/' );
2044 		if( nLastSlash != -1 )
2045 			*o_pExtensionPath = zipFile.copy( 0, nLastSlash );
2046 
2047         if( o_pExtensionRegistryPath != NULL )
2048         {
2049             rtl::OUString& rPath = *o_pExtensionPath;
2050             sal_Int32 nLastSlashInPath = rPath.lastIndexOf( '/', rPath.getLength() - 1 );
2051 
2052             *o_pExtensionRegistryPath = xPackage->getURL();
2053             *o_pExtensionRegistryPath += rPath.copy( nLastSlashInPath);
2054         }
2055 	}
2056 
2057 	return xNA;
2058 }
2059 
2060 
2061 //===================================================================
2062 // class IndexFolderIterator
2063 
2064 rtl::OUString IndexFolderIterator::nextIndexFolder( bool& o_rbExtension, bool& o_rbTemporary )
2065 {
2066 	rtl::OUString aIndexFolder;
2067 
2068 	while( !aIndexFolder.getLength() && m_eState != END_REACHED )
2069 	{
2070 		switch( m_eState )
2071 		{
2072 			case INITIAL_MODULE:
2073 				aIndexFolder =
2074 					m_rDatabases.getInstallPathAsURL() +
2075 					m_rDatabases.processLang( m_aLanguage ) + aSlash + m_aInitialModule +
2076 					rtl::OUString::createFromAscii( ".idxl" );
2077 
2078 				o_rbTemporary = false;
2079 				o_rbExtension = false;
2080 
2081 				m_eState = USER_EXTENSIONS;		// Later: SHARED_MODULE
2082 				break;
2083 
2084 			// Later:
2085 			//case SHARED_MODULE
2086 				//...
2087 
2088 			case USER_EXTENSIONS:
2089 			{
2090 				Reference< deployment::XPackage > xParentPackageBundle;
2091 				Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle );
2092 				if( !xHelpPackage.is() )
2093 					break;
2094 
2095 				aIndexFolder = implGetIndexFolderFromPackage( o_rbTemporary, xHelpPackage );
2096 				o_rbExtension = true;
2097 				break;
2098 			}
2099 
2100 			case SHARED_EXTENSIONS:
2101 			{
2102 				Reference< deployment::XPackage > xParentPackageBundle;
2103 				Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle );
2104 				if( !xHelpPackage.is() )
2105 					break;
2106 
2107 				aIndexFolder = implGetIndexFolderFromPackage( o_rbTemporary, xHelpPackage );
2108 				o_rbExtension = true;
2109 				break;
2110 			}
2111 
2112 			case BUNDLED_EXTENSIONS:
2113 			{
2114 				Reference< deployment::XPackage > xParentPackageBundle;
2115 				Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle );
2116 				if( !xHelpPackage.is() )
2117 					break;
2118 
2119 				aIndexFolder = implGetIndexFolderFromPackage( o_rbTemporary, xHelpPackage );
2120 				o_rbExtension = true;
2121 				break;
2122 			}
2123 
2124             case END_REACHED:
2125 				VOS_ENSURE( false, "IndexFolderIterator::nextIndexFolder(): Invalid case END_REACHED" );
2126 				break;
2127 		}
2128 	}
2129 
2130 	return aIndexFolder;
2131 }
2132 
2133 rtl::OUString IndexFolderIterator::implGetIndexFolderFromPackage( bool& o_rbTemporary, Reference< deployment::XPackage > xPackage )
2134 {
2135 	rtl::OUString aIndexFolder =
2136 		implGetFileFromPackage( rtl::OUString::createFromAscii( ".idxl" ), xPackage );
2137 
2138 	o_rbTemporary = false;
2139 	if( !m_xSFA->isFolder( aIndexFolder ) )
2140 	{
2141 		// i98680: Missing index? Try to generate now
2142 		rtl::OUString aLangURL = implGetFileFromPackage( rtl::OUString(), xPackage );
2143 		if( m_xSFA->isFolder( aLangURL ) )
2144 		{
2145 			// Test write access (shared extension may be read only)
2146 			bool bIsWriteAccess = false;
2147 			try
2148 			{
2149 				rtl::OUString aCreateTestFolder = aLangURL + rtl::OUString::createFromAscii( "CreateTestFolder" );
2150 				m_xSFA->createFolder( aCreateTestFolder );
2151 				if( m_xSFA->isFolder( aCreateTestFolder  ) )
2152 					bIsWriteAccess = true;
2153 
2154 				m_xSFA->kill( aCreateTestFolder );
2155 			}
2156 			catch (Exception &)
2157 			{}
2158 
2159 			// TEST
2160 			//bIsWriteAccess = false;
2161 
2162 			Reference< script::XInvocation > xInvocation;
2163 			Reference< XMultiComponentFactory >xSMgr( m_xContext->getServiceManager(), UNO_QUERY );
2164 			try
2165 			{
2166 				xInvocation = Reference< script::XInvocation >(
2167 					m_xContext->getServiceManager()->createInstanceWithContext( rtl::OUString::createFromAscii(
2168 					"com.sun.star.help.HelpIndexer" ), m_xContext ) , UNO_QUERY );
2169 
2170 				if( xInvocation.is() )
2171 				{
2172 					Sequence<uno::Any> aParamsSeq( bIsWriteAccess ? 6 : 8 );
2173 
2174 					aParamsSeq[0] = uno::makeAny( rtl::OUString::createFromAscii( "-lang" ) );
2175 
2176 					rtl::OUString aLang;
2177 					sal_Int32 nLastSlash = aLangURL.lastIndexOf( '/' );
2178 					if( nLastSlash != -1 )
2179 						aLang = aLangURL.copy( nLastSlash + 1 );
2180 					else
2181 						aLang = rtl::OUString::createFromAscii( "en" );
2182 					aParamsSeq[1] = uno::makeAny( aLang );
2183 
2184 					aParamsSeq[2] = uno::makeAny( rtl::OUString::createFromAscii( "-mod" ) );
2185 					aParamsSeq[3] = uno::makeAny( rtl::OUString::createFromAscii( "help" ) );
2186 
2187 					rtl::OUString aZipDir = aLangURL;
2188 					if( !bIsWriteAccess )
2189 					{
2190 						rtl::OUString aTempFileURL;
2191 						::osl::FileBase::RC eErr = ::osl::File::createTempFile( 0, 0, &aTempFileURL );
2192 						if( eErr == ::osl::FileBase::E_None )
2193 						{
2194 							rtl::OUString aTempDirURL = aTempFileURL;
2195 							try
2196 							{
2197 								m_xSFA->kill( aTempDirURL );
2198 							}
2199 							catch (Exception &)
2200 							{}
2201 							m_xSFA->createFolder( aTempDirURL );
2202 
2203 							aZipDir = aTempDirURL;
2204 							o_rbTemporary = true;
2205 						}
2206 					}
2207 
2208 					aParamsSeq[4] = uno::makeAny( rtl::OUString::createFromAscii( "-zipdir" ) );
2209 					rtl::OUString aSystemPath;
2210 					osl::FileBase::getSystemPathFromFileURL( aZipDir, aSystemPath );
2211 					aParamsSeq[5] = uno::makeAny( aSystemPath );
2212 
2213 					if( !bIsWriteAccess )
2214 					{
2215 						aParamsSeq[6] = uno::makeAny( rtl::OUString::createFromAscii( "-srcdir" ) );
2216 						rtl::OUString aSrcDirVal;
2217 						osl::FileBase::getSystemPathFromFileURL( aLangURL, aSrcDirVal );
2218 						aParamsSeq[7] = uno::makeAny( aSrcDirVal );
2219 					}
2220 
2221 					Sequence< sal_Int16 > aOutParamIndex;
2222 					Sequence< uno::Any > aOutParam;
2223 					uno::Any aRet = xInvocation->invoke( rtl::OUString::createFromAscii( "createIndex" ),
2224 						aParamsSeq, aOutParamIndex, aOutParam );
2225 
2226 					if( bIsWriteAccess )
2227 						aIndexFolder = implGetFileFromPackage( rtl::OUString::createFromAscii( ".idxl" ), xPackage );
2228 					else
2229 						aIndexFolder = aZipDir + rtl::OUString::createFromAscii( "/help.idxl" );
2230 				}
2231 			}
2232 			catch (Exception &)
2233 			{}
2234 		}
2235 	}
2236 
2237 	return aIndexFolder;
2238 }
2239 
2240 void IndexFolderIterator::deleteTempIndexFolder( const rtl::OUString& aIndexFolder )
2241 {
2242 	sal_Int32 nLastSlash = aIndexFolder.lastIndexOf( '/' );
2243 	if( nLastSlash != -1 )
2244 	{
2245 		rtl::OUString aTmpFolder = aIndexFolder.copy( 0, nLastSlash );
2246 		try
2247 		{
2248 			m_xSFA->kill( aTmpFolder );
2249 		}
2250 		catch (Exception &)
2251 		{}
2252 	}
2253 }
2254