/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include "db.hxx" #include #include #include #include #include #include #include #include #include #include #include "inputstream.hxx" #include #include #include #include #include // Extensible help #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "databases.hxx" #include "urlparameter.hxx" #ifdef _WIN32 #if !defined WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN #endif #include #endif using namespace chelp; using namespace com::sun::star; using namespace com::sun::star::uno; using namespace com::sun::star::io; using namespace com::sun::star::container; using namespace com::sun::star::i18n; using namespace com::sun::star::lang; using namespace com::sun::star::deployment; using namespace com::sun::star::beans; OUString Databases::expandURL( const OUString& aURL ) { std::unique_lock aGuard(m_aMutex); return expandURL(aGuard, aURL); } OUString Databases::expandURL( std::unique_lock& /*rGuard*/, const OUString& aURL ) { OUString aRetURL = expandURL( aURL, m_xContext ); return aRetURL; } OUString Databases::expandURL( const OUString& aURL, const Reference< uno::XComponentContext >& xContext ) { static Reference< util::XMacroExpander > xMacroExpander; static Reference< uri::XUriReferenceFactory > xFac; if( !xMacroExpander.is() || !xFac.is() ) { xFac = uri::UriReferenceFactory::create( xContext ); xMacroExpander = util::theMacroExpander::get(xContext); } OUString aRetURL = aURL; if( xMacroExpander.is() ) { Reference< uri::XUriReference > uriRef; for (;;) { uriRef = xFac->parse( aRetURL ); if ( uriRef.is() ) { Reference < uri::XVndSunStarExpandUrl > sxUri( uriRef, UNO_QUERY ); if( !sxUri.is() ) break; aRetURL = sxUri->expand( xMacroExpander ); } } } return aRetURL; } constexpr OUStringLiteral vendVersion = u"%VENDORVERSION"; constexpr OUStringLiteral vendName = u"%VENDORNAME"; constexpr OUStringLiteral prodVersion = u"%PRODUCTVERSION"; constexpr OUStringLiteral vendShort = u"%VENDORSHORT"; constexpr OUStringLiteral prodName = u"%PRODUCTNAME"; constexpr OUStringLiteral newProdVersion = u"$[officeversion]"; constexpr OUStringLiteral newProdName = u"$[officename]"; Databases::Databases( bool showBasic, const OUString& instPath, const OUString& productName, const OUString& productVersion, const OUString& styleSheet, Reference< uno::XComponentContext > const & xContext ) : m_xContext( xContext ), m_bShowBasic(showBasic), m_aCSS(styleSheet.toAsciiLowerCase()) { m_xSMgr = m_xContext->getServiceManager(); m_vAdd[0] = 12; m_vAdd[1] = 15; m_vAdd[2] = 11; m_vAdd[3] = 14; m_vAdd[4] = 12; m_vAdd[5] = 13; m_vAdd[6] = 16; m_vReplacement[0] = productName; m_vReplacement[1] = productVersion; // m_vReplacement[2...4] (vendorName/-Version/-Short) are empty strings m_vReplacement[5] = productName; m_vReplacement[6] = productVersion; setInstallPath( instPath ); m_xSFA = ucb::SimpleFileAccess::create(m_xContext); } Databases::~Databases() { // unload the databases // DatabasesTable m_aDatabases.clear(); // ModInfoTable m_aModInfo.clear(); // KeywordInfoTable m_aKeywordInfo.clear(); } // static OString Databases::getImageTheme() { OUString aSymbolsStyleName = officecfg::Office::Common::Misc::SymbolStyle::get(); if ( aSymbolsStyleName.isEmpty() || aSymbolsStyleName == "auto" ) { aSymbolsStyleName = "colibre"; } return aSymbolsStyleName.toUtf8(); } void Databases::replaceName( OUString& oustring ) const { sal_Int32 idx = -1,idx1 = -1,idx2 = -1,k = 0,off; bool cap = false; OUStringBuffer aStrBuf( 0 ); while( true ) { ++idx; idx1 = oustring.indexOf( '%', idx); idx2 = oustring.indexOf( '$', idx); if(idx1 == -1 && idx2 == -1) break; if(idx1 == -1) idx = idx2; else if(idx2 == -1) idx = idx1; else { // no index is zero if(idx1 < idx2) idx = idx1; else if(idx2 < idx1 ) idx = idx2; } if( oustring.indexOf( prodName,idx ) == idx ) off = PRODUCTNAME; else if( oustring.indexOf( prodVersion,idx ) == idx ) off = PRODUCTVERSION; else if( oustring.indexOf( vendName,idx ) == idx ) off = VENDORNAME; else if( oustring.indexOf( vendVersion,idx ) == idx ) off = VENDORVERSION; else if( oustring.indexOf( vendShort,idx ) == idx ) off = VENDORSHORT; else if( oustring.indexOf( newProdName,idx ) == idx ) off = NEWPRODUCTNAME; else if( oustring.indexOf( newProdVersion,idx ) == idx ) off = NEWPRODUCTVERSION; else off = -1; if( off != -1 ) { if( ! cap ) { cap = true; aStrBuf.ensureCapacity( 256 ); } aStrBuf.append( &oustring.getStr()[k],idx - k ); aStrBuf.append( m_vReplacement[off] ); k = idx + m_vAdd[off]; } } if( cap ) { if( k < oustring.getLength() ) aStrBuf.append( &oustring.getStr()[k],oustring.getLength()-k ); oustring = aStrBuf.makeStringAndClear(); } } OUString Databases::getInstallPathAsURL() { std::unique_lock aGuard( m_aMutex ); return m_aInstallDirectory; } const OUString & Databases::getInstallPathAsURL(std::unique_lock& ) { return m_aInstallDirectory; } const std::vector< OUString >& Databases::getModuleList( const OUString& Language ) { if( m_avModules.empty() ) { OUString fileName,dirName = getInstallPathAsURL() + processLang( Language ); osl::Directory dirFile( dirName ); osl::DirectoryItem aDirItem; osl::FileStatus aStatus( osl_FileStatus_Mask_FileName ); if( osl::FileBase::E_None != dirFile.open() ) return m_avModules; while( dirFile.getNextItem( aDirItem ) == osl::FileBase::E_None && aDirItem.getFileStatus( aStatus ) == osl::FileBase::E_None ) { if( ! aStatus.isValid( osl_FileStatus_Mask_FileName ) ) continue; fileName = aStatus.getFileName(); // Check, whether fileName is of the form *.cfg if (!fileName.endsWithIgnoreAsciiCase(u".cfg", &fileName)) { continue; } fileName = fileName.toAsciiLowerCase(); if (fileName == "picture" || (!m_bShowBasic && fileName == "sbasic")) { continue; } m_avModules.push_back( fileName ); } } return m_avModules; } StaticModuleInformation* Databases::getStaticInformationForModule( std::u16string_view Module, const OUString& Language ) { std::unique_lock aGuard( m_aMutex ); OUString key = processLang(aGuard, Language) + "/" + Module; std::pair< ModInfoTable::iterator,bool > aPair = m_aModInfo.emplace(key,nullptr); ModInfoTable::iterator it = aPair.first; if( aPair.second && ! it->second ) { osl::File cfgFile( m_aInstallDirectory + key + ".cfg" ); if( osl::FileBase::E_None != cfgFile.open( osl_File_OpenFlag_Read ) ) it->second = nullptr; else { sal_uInt32 pos = 0; sal_uInt64 nRead; char buffer[2048]; sal_Unicode lineBuffer[1028]; OUStringBuffer fileContent; while( osl::FileBase::E_None == cfgFile.read( &buffer,2048,nRead ) && nRead ) fileContent.append(OUString( buffer,sal_Int32( nRead ),RTL_TEXTENCODING_UTF8 )); cfgFile.close(); const sal_Unicode* str = fileContent.getStr(); OUString current,program,startid,title; OUString order( u"1"_ustr ); for( sal_Int32 i = 0;i < fileContent.getLength();i++ ) { sal_Unicode ch = str[ i ]; if( ch == '\n' || ch == '\r' ) { if( pos ) { current = OUString( lineBuffer,pos ); if( current.startsWith("Title") ) { title = current.copy( current.indexOf( '=' ) + 1 ); } else if( current.startsWith("Start") ) { startid = current.copy( current.indexOf('=') + 1 ); } else if( current.startsWith("Program") ) { program = current.copy( current.indexOf('=') + 1 ); } else if( current.startsWith("Order") ) { order = current.copy( current.indexOf('=') + 1 ); } } pos = 0; } else lineBuffer[ pos++ ] = ch; } replaceName( title ); it->second.reset(new StaticModuleInformation( title, startid, program, order )); } } return it->second.get(); } OUString Databases::processLang( const OUString& Language ) { std::unique_lock aGuard( m_aMutex ); return processLang(aGuard, Language); } OUString Databases::processLang( std::unique_lock& /*rGuard*/, const OUString& Language ) { OUString ret; LangSetTable::iterator it = m_aLangSet.find( Language ); if( it == m_aLangSet.end() ) { // XXX the old code looked for '-' and '_' as separator between // language and country, no idea if '_' actually still can happen // (probably not), but play safe and keep that and transform to proper // BCP47. const OUString aBcp47( Language.replaceAll( "_", "-")); // Try if language tag or fallbacks are installed. osl::DirectoryItem aDirItem; std::vector aFallbacks( LanguageTag( aBcp47).getFallbackStrings(true)); for (auto const & rFB : aFallbacks) { if (osl::FileBase::E_None == osl::DirectoryItem::get( m_aInstallDirectory + rFB, aDirItem)) { ret = rFB; m_aLangSet[ Language ] = ret; break; // for } } } else ret = it->second; return ret; } helpdatafileproxy::Hdf* Databases::getHelpDataFile(std::u16string_view Database, const OUString& Language, bool helpText, const OUString* pExtensionPath ) { std::unique_lock aGuard( m_aMutex ); return getHelpDataFile(aGuard, Database, Language, helpText, pExtensionPath); } helpdatafileproxy::Hdf* Databases::getHelpDataFile(std::unique_lock& rGuard, std::u16string_view Database, const OUString& Language, bool helpText, const OUString* pExtensionPath ) { if( Database.empty() || Language.isEmpty() ) return nullptr; OUString aFileExt( helpText ? u".ht"_ustr : u".db"_ustr ); OUString dbFileName = OUString::Concat("/") + Database + aFileExt; OUString key; if( pExtensionPath == nullptr ) key = processLang( rGuard, Language ) + dbFileName; else key = *pExtensionPath + Language + dbFileName; // make unique, don't change language std::pair< DatabasesTable::iterator,bool > aPair = m_aDatabases.emplace( key, nullptr); DatabasesTable::iterator it = aPair.first; if( aPair.second && ! it->second ) { std::unique_ptr pHdf; OUString fileURL; if( pExtensionPath ) fileURL = expandURL(rGuard, *pExtensionPath) + Language + dbFileName; else fileURL = m_aInstallDirectory + key; OUString fileNameHDFHelp( fileURL ); //Extensions always use the new format if( pExtensionPath != nullptr ) fileNameHDFHelp += "_"; //SimpleFileAccess takes file URLs as arguments!!! Using filenames works accidentally but //fails for example when using long path names on Windows (starting with \\?\) if( m_xSFA->exists( fileNameHDFHelp ) ) { pHdf.reset(new helpdatafileproxy::Hdf( fileNameHDFHelp, m_xSFA )); } it->second = std::move(pHdf); } return it->second.get(); } Reference< XCollator > Databases::getCollator(std::unique_lock&, const OUString& Language) { OUString key = Language; CollatorTable::iterator it = m_aCollatorTable.emplace( key, Reference< XCollator >() ).first; if( ! it->second.is() ) { it->second = Collator::create(m_xContext); LanguageTag aLanguageTag( Language); OUString countryStr = aLanguageTag.getCountry(); if( countryStr.isEmpty() ) { const OUString langStr = aLanguageTag.getLanguage(); if( langStr == "de" ) countryStr = "DE"; else if( langStr == "en" ) countryStr = "US"; else if( langStr == "es" ) countryStr = "ES"; else if( langStr == "it" ) countryStr = "IT"; else if( langStr == "fr" ) countryStr = "FR"; else if( langStr == "sv" ) countryStr = "SE"; else if( langStr == "ja" ) countryStr = "JP"; else if( langStr == "ko" ) countryStr = "KR"; // XXX NOTE: there are no complex language tags involved in those // "add country" cases, only because of this we can use this // simplified construction. if (!countryStr.isEmpty()) aLanguageTag.reset( langStr + "-" + countryStr); } it->second->loadDefaultCollator( aLanguageTag.getLocale(), 0); } return it->second; } namespace chelp { struct KeywordElementComparator { explicit KeywordElementComparator( const Reference< XCollator >& xCollator ) : m_xCollator( xCollator ) { } bool operator()( const KeywordInfo::KeywordElement& la, const KeywordInfo::KeywordElement& ra ) const { const OUString& l = la.key; const OUString& r = ra.key; bool ret; if( m_xCollator.is() ) { sal_Int32 l1 = l.indexOf( ';' ); sal_Int32 l3 = ( l1 == -1 ? l.getLength() : l1 ); sal_Int32 r1 = r.indexOf( ';' ); sal_Int32 r3 = ( r1 == -1 ? r.getLength() : r1 ); sal_Int32 c1 = m_xCollator->compareSubstring( l,0,l3,r,0,r3 ); if( c1 == +1 ) ret = false; else if( c1 == 0 ) { sal_Int32 l2 = l.getLength() - l1 - 1; sal_Int32 r2 = r.getLength() - r1 - 1; ret = ( m_xCollator->compareSubstring( l,1+l1,l2,r,1+r1,r2 ) < 0 ); } else ret = true; } else ret = l < r; return ret; } Reference< XCollator > m_xCollator; }; // end struct KeywordElementComparator } KeywordInfo::KeywordElement::KeywordElement( Databases const *pDatabases, helpdatafileproxy::Hdf* pHdf, OUString ky, std::u16string_view data ) : key(std::move( ky )) { pDatabases->replaceName( key ); init( pDatabases,pHdf,data ); } void KeywordInfo::KeywordElement::init( Databases const *pDatabases,helpdatafileproxy::Hdf* pHdf, std::u16string_view ids ) { std::vector< OUString > id,anchor; size_t idx = std::u16string_view::npos; size_t k = 0; for (;;) { idx = ids.find( ';', k ); if( idx == std::u16string_view::npos ) break; size_t h = ids.find( '#', k ); if( h != std::u16string_view::npos && h < idx ) { // found an anchor id.push_back( OUString(ids.substr( k, h-k )) ); anchor.push_back( OUString(ids.substr( h+1, idx-h-1 )) ); } else { id.push_back( OUString(ids.substr( k, idx-k )) ); anchor.emplace_back( ); } k = ++idx; } listId.realloc( id.size() ); auto plistId = listId.getArray(); listAnchor.realloc( id.size() ); auto plistAnchor = listAnchor.getArray(); listTitle.realloc( id.size() ); auto plistTitle = listTitle.getArray(); for( size_t i = 0; i < id.size(); ++i ) { plistId[i] = id[i]; plistAnchor[i] = anchor[i]; helpdatafileproxy::HDFData aHDFData; const char* pData = nullptr; if( pHdf ) { OString idi = OUStringToOString( id[i], RTL_TEXTENCODING_UTF8 ); bool bSuccess = pHdf->getValueForKey( idi, aHDFData ); if( bSuccess ) pData = aHDFData.getData(); } DbtToStringConverter converter( pData ); OUString title = converter.getTitle(); pDatabases->replaceName( title ); plistTitle[i] = title; } } KeywordInfo::KeywordInfo( const std::vector< KeywordElement >& aVec ) : listKey( aVec.size() ), listId( aVec.size() ), listAnchor( aVec.size() ), listTitle( aVec.size() ) { auto listKeyRange = asNonConstRange(listKey); auto listIdRange = asNonConstRange(listId); auto listAnchorRange = asNonConstRange(listAnchor); auto listTitleRange = asNonConstRange(listTitle); for( size_t i = 0; i < aVec.size(); ++i ) { listKeyRange[i] = aVec[i].key; listIdRange[i] = aVec[i].listId; listAnchorRange[i] = aVec[i].listAnchor; listTitleRange[i] = aVec[i].listTitle; } } bool Databases::checkModuleMatchForExtension ( std::u16string_view Database, const OUString& doclist ) { bool bBelongsToDatabase = true; // Analyse doclist string to find module assignments bool bFoundAtLeastOneModule = false; bool bModuleMatch = false; sal_Int32 nLen = doclist.getLength(); sal_Int32 nLastFound = doclist.lastIndexOf( ';' ); if( nLastFound == -1 ) nLastFound = nLen; const sal_Unicode* pStr = doclist.getStr(); sal_Int32 nFound = doclist.lastIndexOf( '_' ); while( nFound != -1 ) { // Simple optimization, stop if '_' is followed by "id" if( nLen - nFound > 2 ) { if( pStr[ nFound + 1 ] == 'i' && pStr[ nFound + 2 ] == 'd' ) break; } OUString aModule = doclist.copy( nFound + 1, nLastFound - nFound - 1 ); std::vector< OUString >::iterator result = std::find( m_avModules.begin(), m_avModules.end(), aModule ); if( result != m_avModules.end() ) { bFoundAtLeastOneModule = true; if( Database == aModule ) { bModuleMatch = true; break; } } nLastFound = nFound; if( nLastFound == 0 ) break; nFound = doclist.lastIndexOf( '_', nLastFound - 1 ); } if( bFoundAtLeastOneModule && !bModuleMatch ) bBelongsToDatabase = false; return bBelongsToDatabase; } KeywordInfo* Databases::getKeyword( const OUString& Database, const OUString& Language ) { std::unique_lock aGuard( m_aMutex ); OUString key = processLang(aGuard, Language) + "/" + Database; std::pair< KeywordInfoTable::iterator,bool > aPair = m_aKeywordInfo.emplace( key,nullptr ); KeywordInfoTable::iterator it = aPair.first; if( aPair.second && ! it->second ) { std::vector aVector; KeyDataBaseFileIterator aDbFileIt( m_xContext, *this, Database, Language ); OUString fileURL; bool bExtension = false; for (;;) { fileURL = aDbFileIt.nextDbFile(aGuard, bExtension); if( fileURL.isEmpty() ) break; OUString fileNameHDFHelp( fileURL ); if( bExtension ) fileNameHDFHelp += "_"; if( m_xSFA->exists( fileNameHDFHelp ) ) { helpdatafileproxy::Hdf aHdf( fileNameHDFHelp, m_xSFA ); helpdatafileproxy::HDFData aKey; helpdatafileproxy::HDFData aValue; if( aHdf.startIteration() ) { helpdatafileproxy::Hdf* pHdf = getHelpDataFile(aGuard, Database,Language ); if( pHdf != nullptr ) { pHdf->releaseHashMap(); pHdf->createHashMap( true/*bOptimizeForPerformance*/ ); } while( aHdf.getNextKeyAndValue( aKey, aValue ) ) { OUString keyword( aKey.getData(), aKey.getSize(), RTL_TEXTENCODING_UTF8 ); OUString doclist( aValue.getData(), aValue.getSize(), RTL_TEXTENCODING_UTF8 ); bool bBelongsToDatabase = true; if( bExtension ) bBelongsToDatabase = checkModuleMatchForExtension( Database, doclist ); if( !bBelongsToDatabase ) continue; aVector.emplace_back( this, pHdf, keyword, doclist ); } aHdf.stopIteration(); if( pHdf != nullptr ) pHdf->releaseHashMap(); } } } // sorting Reference xCollator = getCollator(aGuard, Language); std::sort(aVector.begin(), aVector.end(), KeywordElementComparator(xCollator)); it->second.reset(new KeywordInfo( aVector )); } return it->second.get(); } Reference< XHierarchicalNameAccess > Databases::jarFile( std::unique_lock& rGuard, std::u16string_view jar, const OUString& Language ) { if( jar.empty() || Language.isEmpty() ) { return Reference< XHierarchicalNameAccess >( nullptr ); } OUString key = processLang(rGuard, Language) + "/" + jar; ZipFileTable::iterator it = m_aZipFileTable.emplace( key,Reference< XHierarchicalNameAccess >(nullptr) ).first; if( ! it->second.is() ) { try { OUString zipFile; // Extension jar file? Search for ? size_t nQuestionMark1 = jar.find( '?' ); size_t nQuestionMark2 = jar.rfind( '?' ); if( nQuestionMark1 != std::u16string_view::npos && nQuestionMark2 != std::u16string_view::npos && nQuestionMark1 != nQuestionMark2 ) { std::u16string_view aExtensionPath = jar.substr( nQuestionMark1 + 1, nQuestionMark2 - nQuestionMark1 - 1 ); std::u16string_view aPureJar = jar.substr( nQuestionMark2 + 1 ); zipFile = expandURL(rGuard, OUString::Concat(aExtensionPath) + "/" + aPureJar); } else { zipFile = m_aInstallDirectory + key; } Sequence< Any > aArguments( 2 ); auto pArguments = aArguments.getArray(); rtl::Reference p(new XInputStream_impl( zipFile )); if( p->CtorSuccess() ) { pArguments[ 0 ] <<= Reference< XInputStream >( p ); } else { p.clear(); pArguments[ 0 ] <<= zipFile; } // let ZipPackage be used ( no manifest.xml is required ) beans::NamedValue aArg; aArg.Name = "StorageFormat"; aArg.Value <<= ZIP_STORAGE_FORMAT_STRING; pArguments[ 1 ] <<= aArg; Reference< XInterface > xIfc = m_xSMgr->createInstanceWithArgumentsAndContext( u"com.sun.star.packages.comp.ZipPackage"_ustr, aArguments, m_xContext ); if ( xIfc.is() ) { it->second.set( xIfc, UNO_QUERY ); OSL_ENSURE( it->second.is(), "ContentProvider::createPackage - " "Got no hierarchical name access!" ); } } catch ( RuntimeException & ) { } catch ( Exception & ) { } } return it->second; } Reference< XHierarchicalNameAccess > Databases::findJarFileForPath ( const OUString& jar, const OUString& Language, const OUString& path, OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath ) { Reference< XHierarchicalNameAccess > xNA; if( jar.isEmpty() || Language.isEmpty() ) { return xNA; } ::std::unique_lock aGuard(m_aMutex); JarFileIterator aJarFileIt( m_xContext, *this, jar, Language ); Reference< XHierarchicalNameAccess > xTestNA; Reference< deployment::XPackage > xParentPackageBundle; for (;;) { xTestNA = aJarFileIt.nextJarFile(aGuard, xParentPackageBundle, o_pExtensionPath, o_pExtensionRegistryPath); if( !xTestNA.is() ) break; if( xTestNA.is() && xTestNA->hasByHierarchicalName( path ) ) { bool bSuccess = true; if( xParentPackageBundle.is() ) { OUString aIdentifierInPath; sal_Int32 nFindSlash = path.indexOf( '/' ); if( nFindSlash != -1 ) aIdentifierInPath = path.copy( 0, nFindSlash ); beans::Optional aIdentifierOptional = xParentPackageBundle->getIdentifier(); if( !aIdentifierInPath.isEmpty() && aIdentifierOptional.IsPresent ) { OUString aUnencodedIdentifier = aIdentifierOptional.Value; OUString aIdentifier = rtl::Uri::encode( aUnencodedIdentifier, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8 ); if( aIdentifierInPath != aIdentifier ) { // path does not start with extension identifier -> ignore bSuccess = false; } } else { // No identifier -> ignore bSuccess = false; } } if( bSuccess ) { xNA = xTestNA; break; } } } return xNA; } void Databases::changeCSS(const OUString& newStyleSheet) { m_aCSS = newStyleSheet.toAsciiLowerCase(); m_vCustomCSSDoc.clear(); } void Databases::cascadingStylesheet( const OUString& Language, OStringBuffer& buffer ) { if( m_vCustomCSSDoc.empty() ) { int retry = 2; bool error = true; OUString fileURL; bool bHighContrastMode = false; OUString aCSS( m_aCSS ); if ( aCSS == "default" ) { // #i50760: "default" needs to adapt HC mode uno::Reference< awt::XToolkit2 > xToolkit = awt::Toolkit::create( ::comphelper::getProcessComponentContext() ); uno::Reference< awt::XTopWindow > xTopWindow = xToolkit->getActiveTopWindow(); if ( xTopWindow.is() ) { uno::Reference< awt::XVclWindowPeer > xVclWindowPeer( xTopWindow, uno::UNO_QUERY ); if ( xVclWindowPeer.is() ) { uno::Any aHCMode = xVclWindowPeer->getProperty( u"HighContrastMode"_ustr ); if ( ( aHCMode >>= bHighContrastMode ) && bHighContrastMode ) { aCSS = "highcontrastblack"; #ifdef _WIN32 HKEY hKey = nullptr; LONG lResult = RegOpenKeyExW( HKEY_CURRENT_USER, L"Control Panel\\Accessibility\\HighContrast", 0, KEY_QUERY_VALUE, &hKey ); if ( ERROR_SUCCESS == lResult ) { WCHAR szBuffer[1024]; DWORD nSize = sizeof( szBuffer ); lResult = RegQueryValueExW( hKey, L"High Contrast Scheme", nullptr, nullptr, reinterpret_cast(szBuffer), &nSize ); if ( ERROR_SUCCESS == lResult && nSize > 0 ) { szBuffer[nSize] = '\0'; if ( wcscmp( szBuffer, L"High Contrast #1" ) == 0 ) aCSS = "highcontrast1"; if ( wcscmp( szBuffer, L"High Contrast #2" ) == 0 ) aCSS = "highcontrast2"; if ( wcscmp( szBuffer, L"High Contrast White" ) == 0 ) aCSS = "highcontrastwhite"; } RegCloseKey( hKey ); } #endif } } } } while( error && retry ) { if( retry == 2 ) fileURL = getInstallPathAsURL() + processLang( Language ) + "/" + aCSS + ".css"; else if( retry == 1 ) fileURL = getInstallPathAsURL() + aCSS + ".css"; osl::DirectoryItem aDirItem; osl::File aFile( fileURL ); osl::FileStatus aStatus( osl_FileStatus_Mask_FileSize ); if( osl::FileBase::E_None == osl::DirectoryItem::get( fileURL,aDirItem ) && osl::FileBase::E_None == aFile.open( osl_File_OpenFlag_Read ) && osl::FileBase::E_None == aDirItem.getFileStatus( aStatus ) ) { sal_uInt64 nSize; aFile.getSize( nSize ); m_vCustomCSSDoc.resize( nSize + 1); m_vCustomCSSDoc[nSize] = 0; sal_uInt64 a = nSize,b = nSize; aFile.read( m_vCustomCSSDoc.data(), a, b ); aFile.close(); error = false; } --retry; if ( !retry && error && bHighContrastMode ) { // fall back to default css aCSS = "default"; retry = 2; bHighContrastMode = false; } } if( error ) { m_vCustomCSSDoc.clear(); } } if (!m_vCustomCSSDoc.empty()) buffer.append( m_vCustomCSSDoc.data(), m_vCustomCSSDoc.size() - 1 ); } void Databases::setActiveText( const OUString& Module, const OUString& Language, std::u16string_view Id, OStringBuffer& buffer ) { DataBaseIterator aDbIt( m_xContext, *this, Module, Language, true ); // #i84550 Cache information about failed ids OString id = OUStringToOString( Id, RTL_TEXTENCODING_UTF8 ); EmptyActiveTextSet::iterator it = m_aEmptyActiveTextSet.find( id ); bool bFoundAsEmpty = ( it != m_aEmptyActiveTextSet.end() ); helpdatafileproxy::HDFData aHDFData; int nSize = 0; const char* pData = nullptr; bool bSuccess = false; if( !bFoundAsEmpty ) { while( !bSuccess ) { helpdatafileproxy::Hdf* pHdf = aDbIt.nextHdf(); if( !pHdf ) break; bSuccess = pHdf->getValueForKey( id, aHDFData ); nSize = aHDFData.getSize(); pData = aHDFData.getData(); } } if( bSuccess ) { // ensure existence of tmp after for OString tmp; for( int i = 0; i < nSize; ++i ) if( pData[i] == '%' || pData[i] == '$' ) { // need of replacement OUString temp( pData, nSize, RTL_TEXTENCODING_UTF8 ); replaceName( temp ); tmp = OString( temp.getStr(), temp.getLength(), RTL_TEXTENCODING_UTF8 ); nSize = tmp.getLength(); pData = tmp.getStr(); break; } buffer.append( pData, nSize ); } else { if( !bFoundAsEmpty ) m_aEmptyActiveTextSet.insert( id ); } } void Databases::setInstallPath( const OUString& aInstDir ) { std::unique_lock aGuard( m_aMutex ); osl::FileBase::getFileURLFromSystemPath( aInstDir,m_aInstallDirectory ); //TODO: check returned error code if( !m_aInstallDirectory.endsWith( "/" ) ) m_aInstallDirectory += "/"; } ExtensionHelpExistenceMap ExtensionIteratorBase::aHelpExistenceMap; ExtensionIteratorBase::ExtensionIteratorBase( Reference< XComponentContext > const & xContext, Databases& rDatabases, OUString aInitialModule, OUString aLanguage ) : m_xContext( xContext ) , m_rDatabases( rDatabases ) , m_eState( IteratorState::InitialModule ) , m_aInitialModule(std::move( aInitialModule )) , m_aLanguage(std::move( aLanguage )) { assert( m_xContext.is() ); init(); } ExtensionIteratorBase::ExtensionIteratorBase( Databases& rDatabases, OUString aInitialModule, OUString aLanguage ) : m_xContext( comphelper::getProcessComponentContext() ) , m_rDatabases( rDatabases ) , m_eState( IteratorState::InitialModule ) , m_aInitialModule(std::move( aInitialModule )) , m_aLanguage(std::move( aLanguage )) { init(); } void ExtensionIteratorBase::init() { m_xSFA = ucb::SimpleFileAccess::create(m_xContext); m_bUserPackagesLoaded = false; m_bSharedPackagesLoaded = false; m_bBundledPackagesLoaded = false; m_iUserPackage = 0; m_iSharedPackage = 0; m_iBundledPackage = 0; } Reference< deployment::XPackage > ExtensionIteratorBase::implGetHelpPackageFromPackage ( const Reference< deployment::XPackage >& xPackage, Reference< deployment::XPackage >& o_xParentPackageBundle ) { o_xParentPackageBundle.clear(); Reference< deployment::XPackage > xHelpPackage; if( !xPackage.is() ) return xHelpPackage; // #i84550 Cache information about help content in extension OUString aExtensionPath = xPackage->getURL(); ExtensionHelpExistenceMap::iterator it = aHelpExistenceMap.find( aExtensionPath ); bool bFound = ( it != aHelpExistenceMap.end() ); bool bHasHelp = bFound && it->second; if( bFound && !bHasHelp ) return xHelpPackage; // Check if parent package is registered beans::Optional< beans::Ambiguous > option( xPackage->isRegistered ( Reference(), Reference() ) ); bool bRegistered = false; if( option.IsPresent ) { beans::Ambiguous const & reg = option.Value; if( !reg.IsAmbiguous && reg.Value ) bRegistered = true; } if( bRegistered ) { OUString aHelpMediaType( u"application/vnd.sun.star.help"_ustr ); if( xPackage->isBundle() ) { const Sequence< Reference< deployment::XPackage > > aPkgSeq = xPackage->getBundle ( Reference(), Reference() ); auto pSubPkg = std::find_if(aPkgSeq.begin(), aPkgSeq.end(), [&aHelpMediaType](const Reference< deployment::XPackage >& xSubPkg) { const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xSubPkg->getPackageType(); OUString aMediaType = xPackageTypeInfo->getMediaType(); return aMediaType == aHelpMediaType; }); if (pSubPkg != aPkgSeq.end()) { xHelpPackage = *pSubPkg; o_xParentPackageBundle = xPackage; } } else { const Reference< deployment::XPackageTypeInfo > xPackageTypeInfo = xPackage->getPackageType(); OUString aMediaType = xPackageTypeInfo->getMediaType(); if( aMediaType == aHelpMediaType ) xHelpPackage = xPackage; } } if( !bFound ) aHelpExistenceMap[ aExtensionPath ] = xHelpPackage.is(); return xHelpPackage; } Reference< deployment::XPackage > ExtensionIteratorBase::implGetNextUserHelpPackage ( Reference< deployment::XPackage >& o_xParentPackageBundle ) { Reference< deployment::XPackage > xHelpPackage; if( !m_bUserPackagesLoaded ) { Reference< XExtensionManager > xExtensionManager = ExtensionManager::get(m_xContext); m_aUserPackagesSeq = xExtensionManager->getDeployedExtensions ( u"user"_ustr, Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() ); m_bUserPackagesLoaded = true; } if( m_iUserPackage == m_aUserPackagesSeq.getLength() ) { m_eState = IteratorState::SharedExtensions; // Later: SHARED_MODULE } else { const Reference< deployment::XPackage >* pUserPackages = m_aUserPackagesSeq.getConstArray(); Reference< deployment::XPackage > xPackage = pUserPackages[ m_iUserPackage++ ]; OSL_ENSURE( xPackage.is(), "ExtensionIteratorBase::implGetNextUserHelpPackage(): Invalid package" ); xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle ); } return xHelpPackage; } Reference< deployment::XPackage > ExtensionIteratorBase::implGetNextSharedHelpPackage ( Reference< deployment::XPackage >& o_xParentPackageBundle ) { Reference< deployment::XPackage > xHelpPackage; if( !m_bSharedPackagesLoaded ) { Reference< XExtensionManager > xExtensionManager = ExtensionManager::get(m_xContext); m_aSharedPackagesSeq = xExtensionManager->getDeployedExtensions ( u"shared"_ustr, Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() ); m_bSharedPackagesLoaded = true; } if( m_iSharedPackage == m_aSharedPackagesSeq.getLength() ) { m_eState = IteratorState::BundledExtensions; } else { const Reference< deployment::XPackage >* pSharedPackages = m_aSharedPackagesSeq.getConstArray(); Reference< deployment::XPackage > xPackage = pSharedPackages[ m_iSharedPackage++ ]; OSL_ENSURE( xPackage.is(), "ExtensionIteratorBase::implGetNextSharedHelpPackage(): Invalid package" ); xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle ); } return xHelpPackage; } Reference< deployment::XPackage > ExtensionIteratorBase::implGetNextBundledHelpPackage ( Reference< deployment::XPackage >& o_xParentPackageBundle ) { Reference< deployment::XPackage > xHelpPackage; if( !m_bBundledPackagesLoaded ) { Reference< XExtensionManager > xExtensionManager = ExtensionManager::get(m_xContext); m_aBundledPackagesSeq = xExtensionManager->getDeployedExtensions ( u"bundled"_ustr, Reference< task::XAbortChannel >(), Reference< ucb::XCommandEnvironment >() ); m_bBundledPackagesLoaded = true; } if( m_iBundledPackage == m_aBundledPackagesSeq.getLength() ) { m_eState = IteratorState::EndReached; } else { const Reference< deployment::XPackage >* pBundledPackages = m_aBundledPackagesSeq.getConstArray(); Reference< deployment::XPackage > xPackage = pBundledPackages[ m_iBundledPackage++ ]; OSL_ENSURE( xPackage.is(), "ExtensionIteratorBase::implGetNextBundledHelpPackage(): Invalid package" ); xHelpPackage = implGetHelpPackageFromPackage( xPackage, o_xParentPackageBundle ); } return xHelpPackage; } OUString ExtensionIteratorBase::implGetFileFromPackage( std::unique_lock & rGuard, std::u16string_view rFileExtension, const Reference< deployment::XPackage >& xPackage ) { // No extension -> search for pure language folder bool bLangFolderOnly = rFileExtension.empty(); OUString aFile; OUString aLanguage = m_aLanguage; for( sal_Int32 iPass = 0 ; iPass < 2 ; ++iPass ) { OUString aStr = xPackage->getRegistrationDataURL().Value + "/" + aLanguage; if( !bLangFolderOnly ) { aStr += OUString::Concat("/help") + rFileExtension; } aFile = m_rDatabases.expandURL(rGuard, aStr); if( iPass == 0 ) { if( m_xSFA->exists( aFile ) ) break; ::std::vector< OUString > av; implGetLanguageVectorFromPackage( av, xPackage ); ::std::vector< OUString >::const_iterator pFound = LanguageTag::getFallback( av, m_aLanguage ); if( pFound != av.end() ) aLanguage = *pFound; } } return aFile; } OUString ExtensionIteratorBase::implGetFileFromPackage( std::u16string_view rFileExtension, const Reference< deployment::XPackage >& xPackage ) { // No extension -> search for pure language folder bool bLangFolderOnly = rFileExtension.empty(); OUString aFile; OUString aLanguage = m_aLanguage; for( sal_Int32 iPass = 0 ; iPass < 2 ; ++iPass ) { OUString aStr = xPackage->getRegistrationDataURL().Value + "/" + aLanguage; if( !bLangFolderOnly ) { aStr += OUString::Concat("/help") + rFileExtension; } aFile = m_rDatabases.expandURL( aStr ); if( iPass == 0 ) { if( m_xSFA->exists( aFile ) ) break; ::std::vector< OUString > av; implGetLanguageVectorFromPackage( av, xPackage ); ::std::vector< OUString >::const_iterator pFound = LanguageTag::getFallback( av, m_aLanguage ); if( pFound != av.end() ) aLanguage = *pFound; } } return aFile; } static bool isLetter( sal_Unicode c ) { return rtl::isAsciiAlpha(c); } void ExtensionIteratorBase::implGetLanguageVectorFromPackage( ::std::vector< OUString > &rv, const css::uno::Reference< css::deployment::XPackage >& xPackage ) { rv.clear(); OUString aExtensionPath = xPackage->getURL(); const Sequence< OUString > aEntrySeq = m_xSFA->getFolderContents( aExtensionPath, true ); for( const OUString& aEntry : aEntrySeq ) { if( m_xSFA->isFolder( aEntry ) ) { sal_Int32 nLastSlash = aEntry.lastIndexOf( '/' ); if( nLastSlash != -1 ) { OUString aPureEntry = aEntry.copy( nLastSlash + 1 ); // Check language scheme int nLen = aPureEntry.getLength(); const sal_Unicode* pc = aPureEntry.getStr(); bool bStartCanBeLanguage = ( nLen >= 2 && isLetter( pc[0] ) && isLetter( pc[1] ) ); bool bIsLanguage = bStartCanBeLanguage && ( nLen == 2 || (nLen == 5 && pc[2] == '-' && isLetter( pc[3] ) && isLetter( pc[4] )) ); if( bIsLanguage ) rv.push_back( aPureEntry ); } } } } helpdatafileproxy::Hdf* DataBaseIterator::nextHdf( OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath ) { helpdatafileproxy::Hdf* pRetHdf = nullptr; while( !pRetHdf && m_eState != IteratorState::EndReached ) { switch( m_eState ) { case IteratorState::InitialModule: pRetHdf = m_rDatabases.getHelpDataFile( m_aInitialModule, m_aLanguage, m_bHelpText ); m_eState = IteratorState::UserExtensions; // Later: SHARED_MODULE break; // Later: //case SHARED_MODULE case IteratorState::UserExtensions: { Reference< deployment::XPackage > xParentPackageBundle; Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle ); if( !xHelpPackage.is() ) break; pRetHdf = implGetHdfFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath ); break; } case IteratorState::SharedExtensions: { Reference< deployment::XPackage > xParentPackageBundle; Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle ); if( !xHelpPackage.is() ) break; pRetHdf = implGetHdfFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath ); break; } case IteratorState::BundledExtensions: { Reference< deployment::XPackage > xParentPackageBundle; Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle ); if( !xHelpPackage.is() ) break; pRetHdf = implGetHdfFromPackage( xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath ); break; } case IteratorState::EndReached: OSL_FAIL( "DataBaseIterator::nextDb(): Invalid case IteratorState::EndReached" ); break; } } return pRetHdf; } helpdatafileproxy::Hdf* DataBaseIterator::implGetHdfFromPackage( const Reference< deployment::XPackage >& xPackage, OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath ) { beans::Optional< OUString> optRegData; try { optRegData = xPackage->getRegistrationDataURL(); } catch ( deployment::ExtensionRemovedException&) { return nullptr; } helpdatafileproxy::Hdf* pRetHdf = nullptr; if (optRegData.IsPresent && !optRegData.Value.isEmpty()) { OUString aRegDataUrl = optRegData.Value + "/"; OUString aHelpFilesBaseName(u"help"_ustr); OUString aUsedLanguage = m_aLanguage; pRetHdf = m_rDatabases.getHelpDataFile( aHelpFilesBaseName, aUsedLanguage, m_bHelpText, &aRegDataUrl); // Language fallback if( !pRetHdf ) { ::std::vector< OUString > av; implGetLanguageVectorFromPackage( av, xPackage ); ::std::vector< OUString >::const_iterator pFound = LanguageTag::getFallback( av, m_aLanguage ); if( pFound != av.end() ) { aUsedLanguage = *pFound; pRetHdf = m_rDatabases.getHelpDataFile( aHelpFilesBaseName, aUsedLanguage, m_bHelpText, &aRegDataUrl); } } if( o_pExtensionPath ) *o_pExtensionPath = aRegDataUrl + aUsedLanguage; if( o_pExtensionRegistryPath ) *o_pExtensionRegistryPath = xPackage->getURL() + "/" + aUsedLanguage; } return pRetHdf; } //returns a file URL OUString KeyDataBaseFileIterator::nextDbFile(std::unique_lock& rGuard, bool& o_rbExtension) { OUString aRetFile; while( aRetFile.isEmpty() && m_eState != IteratorState::EndReached ) { switch( m_eState ) { case IteratorState::InitialModule: aRetFile = m_rDatabases.getInstallPathAsURL(rGuard) + m_rDatabases.processLang(rGuard, m_aLanguage) + "/" + m_aInitialModule + ".key"; o_rbExtension = false; m_eState = IteratorState::UserExtensions; // Later: SHARED_MODULE break; // Later: //case SHARED_MODULE case IteratorState::UserExtensions: { Reference< deployment::XPackage > xParentPackageBundle; Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle ); if( !xHelpPackage.is() ) break; aRetFile = implGetDbFileFromPackage(rGuard, xHelpPackage); o_rbExtension = true; break; } case IteratorState::SharedExtensions: { Reference< deployment::XPackage > xParentPackageBundle; Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle ); if( !xHelpPackage.is() ) break; aRetFile = implGetDbFileFromPackage(rGuard, xHelpPackage); o_rbExtension = true; break; } case IteratorState::BundledExtensions: { Reference< deployment::XPackage > xParentPackageBundle; Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle ); if( !xHelpPackage.is() ) break; aRetFile = implGetDbFileFromPackage(rGuard, xHelpPackage); o_rbExtension = true; break; } case IteratorState::EndReached: OSL_FAIL( "DataBaseIterator::nextDbFile(): Invalid case IteratorState::EndReached" ); break; } } return aRetFile; } //Returns a file URL, that does not contain macros OUString KeyDataBaseFileIterator::implGetDbFileFromPackage( std::unique_lock& rGuard, const Reference& xPackage) { OUString aExpandedURL = implGetFileFromPackage(rGuard, u".key", xPackage); return aExpandedURL; } Reference JarFileIterator::nextJarFile( std::unique_lock& rGuard, Reference< deployment::XPackage >& o_xParentPackageBundle, OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath ) { Reference< XHierarchicalNameAccess > xNA; while( !xNA.is() && m_eState != IteratorState::EndReached ) { switch( m_eState ) { case IteratorState::InitialModule: xNA = m_rDatabases.jarFile(rGuard, m_aInitialModule, m_aLanguage); m_eState = IteratorState::UserExtensions; // Later: SHARED_MODULE break; // Later: //case SHARED_MODULE case IteratorState::UserExtensions: { Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( o_xParentPackageBundle ); if( !xHelpPackage.is() ) break; xNA = implGetJarFromPackage(rGuard, xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath); break; } case IteratorState::SharedExtensions: { Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( o_xParentPackageBundle ); if( !xHelpPackage.is() ) break; xNA = implGetJarFromPackage(rGuard, xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath); break; } case IteratorState::BundledExtensions: { Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( o_xParentPackageBundle ); if( !xHelpPackage.is() ) break; xNA = implGetJarFromPackage(rGuard, xHelpPackage, o_pExtensionPath, o_pExtensionRegistryPath); break; } case IteratorState::EndReached: OSL_FAIL( "JarFileIterator::nextJarFile(): Invalid case IteratorState::EndReached" ); break; } } return xNA; } Reference< XHierarchicalNameAccess > JarFileIterator::implGetJarFromPackage( std::unique_lock& rGuard, const Reference& xPackage, OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath) { Reference< XHierarchicalNameAccess > xNA; OUString zipFile = implGetFileFromPackage(rGuard, u".jar", xPackage); try { Sequence< Any > aArguments{ Any(zipFile), // let ZipPackage be used ( no manifest.xml is required ) Any(comphelper::makePropertyValue(u"StorageFormat"_ustr, ZIP_STORAGE_FORMAT_STRING)) }; Reference< XMultiComponentFactory >xSMgr = m_xContext->getServiceManager(); Reference< XInterface > xIfc = xSMgr->createInstanceWithArgumentsAndContext( u"com.sun.star.packages.comp.ZipPackage"_ustr, aArguments, m_xContext ); if ( xIfc.is() ) { xNA.set( xIfc, UNO_QUERY ); OSL_ENSURE( xNA.is(), "JarFileIterator::implGetJarFromPackage() - " "Got no hierarchical name access!" ); } } catch ( RuntimeException & ) {} catch ( Exception & ) {} if( xNA.is() && o_pExtensionPath != nullptr ) { // Extract path including language from file name sal_Int32 nLastSlash = zipFile.lastIndexOf( '/' ); if( nLastSlash != -1 ) *o_pExtensionPath = zipFile.copy( 0, nLastSlash ); if( o_pExtensionRegistryPath != nullptr ) { OUString& rPath = *o_pExtensionPath; sal_Int32 nLastSlashInPath = rPath.lastIndexOf( '/', rPath.getLength() - 1 ); *o_pExtensionRegistryPath = xPackage->getURL(); *o_pExtensionRegistryPath += rPath.subView( nLastSlashInPath); } } return xNA; } OUString IndexFolderIterator::nextIndexFolder( bool& o_rbExtension, bool& o_rbTemporary ) { OUString aIndexFolder; while( aIndexFolder.isEmpty() && m_eState != IteratorState::EndReached ) { switch( m_eState ) { case IteratorState::InitialModule: aIndexFolder = m_rDatabases.getInstallPathAsURL() + m_rDatabases.processLang(m_aLanguage) + "/" + m_aInitialModule + ".idxl"; o_rbTemporary = false; o_rbExtension = false; m_eState = IteratorState::UserExtensions; // Later: SHARED_MODULE break; // Later: //case SHARED_MODULE case IteratorState::UserExtensions: { Reference< deployment::XPackage > xParentPackageBundle; Reference< deployment::XPackage > xHelpPackage = implGetNextUserHelpPackage( xParentPackageBundle ); if( !xHelpPackage.is() ) break; aIndexFolder = implGetIndexFolderFromPackage( o_rbTemporary, xHelpPackage ); o_rbExtension = true; break; } case IteratorState::SharedExtensions: { Reference< deployment::XPackage > xParentPackageBundle; Reference< deployment::XPackage > xHelpPackage = implGetNextSharedHelpPackage( xParentPackageBundle ); if( !xHelpPackage.is() ) break; aIndexFolder = implGetIndexFolderFromPackage( o_rbTemporary, xHelpPackage ); o_rbExtension = true; break; } case IteratorState::BundledExtensions: { Reference< deployment::XPackage > xParentPackageBundle; Reference< deployment::XPackage > xHelpPackage = implGetNextBundledHelpPackage( xParentPackageBundle ); if( !xHelpPackage.is() ) break; aIndexFolder = implGetIndexFolderFromPackage( o_rbTemporary, xHelpPackage ); o_rbExtension = true; break; } case IteratorState::EndReached: OSL_FAIL( "IndexFolderIterator::nextIndexFolder(): Invalid case IteratorState::EndReached" ); break; } } return aIndexFolder; } OUString IndexFolderIterator::implGetIndexFolderFromPackage( bool& o_rbTemporary, const Reference< deployment::XPackage >& xPackage ) { OUString aIndexFolder = implGetFileFromPackage( u".idxl", xPackage ); o_rbTemporary = false; if( !m_xSFA->isFolder( aIndexFolder ) ) { // i98680: Missing index? Try to generate now OUString aLangURL = implGetFileFromPackage( std::u16string_view(), xPackage ); if( m_xSFA->isFolder( aLangURL ) ) { // Test write access (shared extension may be read only) bool bIsWriteAccess = false; try { OUString aCreateTestFolder = aLangURL + "CreateTestFolder"; m_xSFA->createFolder( aCreateTestFolder ); if( m_xSFA->isFolder( aCreateTestFolder ) ) bIsWriteAccess = true; m_xSFA->kill( aCreateTestFolder ); } catch (const Exception &) { } // TEST //bIsWriteAccess = false; try { OUString aLang; sal_Int32 nLastSlash = aLangURL.lastIndexOf( '/' ); if( nLastSlash != -1 ) aLang = aLangURL.copy( nLastSlash + 1 ); else aLang = "en"; OUString aZipDir = aLangURL; if( !bIsWriteAccess ) { OUString aTempFileURL; ::osl::FileBase::RC eErr = ::osl::File::createTempFile( nullptr, nullptr, &aTempFileURL ); if( eErr == ::osl::FileBase::E_None ) { try { m_xSFA->kill( aTempFileURL ); } catch (const Exception &) { } m_xSFA->createFolder( aTempFileURL ); aZipDir = aTempFileURL; o_rbTemporary = true; } } HelpIndexer aIndexer(aLang, u"help"_ustr, aLangURL, aZipDir); aIndexer.indexDocuments(); if( bIsWriteAccess ) aIndexFolder = implGetFileFromPackage( u".idxl", xPackage ); else aIndexFolder = aZipDir + "/help.idxl"; } catch (const Exception &) { } } } return aIndexFolder; } void IndexFolderIterator::deleteTempIndexFolder( std::u16string_view aIndexFolder ) { size_t nLastSlash = aIndexFolder.rfind( '/' ); if( nLastSlash != std::u16string_view::npos ) { OUString aTmpFolder( aIndexFolder.substr( 0, nLastSlash ) ); try { m_xSFA->kill( aTmpFolder ); } catch (const Exception &) { } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */