diff options
Diffstat (limited to 'xmlhelp/source/cxxhelp/provider')
22 files changed, 6745 insertions, 0 deletions
diff --git a/xmlhelp/source/cxxhelp/provider/content.cxx b/xmlhelp/source/cxxhelp/provider/content.cxx new file mode 100644 index 0000000000..6d12041de9 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/content.cxx @@ -0,0 +1,441 @@ +/* -*- 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 . + */ + +/************************************************************************** + TODO + ************************************************************************** + + *************************************************************************/ +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/ucb/OpenCommandArgument2.hpp> +#include <com/sun/star/ucb/UnsupportedCommandException.hpp> +#include <com/sun/star/ucb/XCommandInfo.hpp> +#include <com/sun/star/io/XActiveDataSink.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/lang/IllegalAccessException.hpp> +#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp> +#include <com/sun/star/io/XActiveDataStreamer.hpp> +#include <ucbhelper/propertyvalueset.hxx> +#include <ucbhelper/cancelcommandexecution.hxx> +#include <ucbhelper/macros.hxx> +#include <utility> +#include "content.hxx" +#include "provider.hxx" +#include "resultset.hxx" +#include "databases.hxx" +#include "resultsetfactory.hxx" +#include "resultsetbase.hxx" +#include "resultsetforroot.hxx" +#include "resultsetforquery.hxx" + +using namespace com::sun::star; +using namespace chelp; + +// Content Implementation. + +Content::Content( const uno::Reference< uno::XComponentContext >& rxContext, + ::ucbhelper::ContentProviderImplHelper* pProvider, + const uno::Reference< ucb::XContentIdentifier >& + Identifier, + Databases* pDatabases ) + : ContentImplHelper( rxContext, pProvider, Identifier ), + m_aURLParameter( Identifier->getContentIdentifier(),pDatabases ), + m_pDatabases( pDatabases ) // not owner +{ +} + +// virtual +Content::~Content() +{ +} + +// virtual +uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType ) +{ + uno::Any aRet; + return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface( rType ); +} + +// XTypeProvider methods. + +XTYPEPROVIDER_COMMON_IMPL( Content ); + +// virtual +uno::Sequence< uno::Type > SAL_CALL Content::getTypes() +{ + static cppu::OTypeCollection ourTypeCollection( + CPPU_TYPE_REF( lang::XTypeProvider ), + CPPU_TYPE_REF( lang::XServiceInfo ), + CPPU_TYPE_REF( lang::XComponent ), + CPPU_TYPE_REF( ucb::XContent ), + CPPU_TYPE_REF( ucb::XCommandProcessor ), + CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ), + CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ), + CPPU_TYPE_REF( beans::XPropertyContainer ), + CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ), + CPPU_TYPE_REF( container::XChild ) ); + + return ourTypeCollection.getTypes(); +} + +// XServiceInfo methods. + +// virtual +OUString SAL_CALL Content::getImplementationName() +{ + return "CHelpContent"; +} + +// virtual +uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.CHelpContent" }; +} + +// XContent methods. + +// virtual +OUString SAL_CALL Content::getContentType() +{ + return MYUCP_CONTENT_TYPE; +} + +// XCommandProcessor methods. + +//virtual +void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ ) +{ +} + +namespace { + +class ResultSetForRootFactory + : public ResultSetFactory +{ +private: + + uno::Reference< uno::XComponentContext > m_xContext; + uno::Reference< ucb::XContentProvider > m_xProvider; + uno::Sequence< beans::Property > m_seq; + URLParameter m_aURLParameter; + Databases* m_pDatabases; + +public: + + ResultSetForRootFactory( + uno::Reference< uno::XComponentContext > xContext, + uno::Reference< ucb::XContentProvider > xProvider, + const uno::Sequence< beans::Property >& seq, + const URLParameter& rURLParameter, + Databases* pDatabases ) + : m_xContext(std::move( xContext )), + m_xProvider(std::move( xProvider )), + m_seq( seq ), + m_aURLParameter(rURLParameter), + m_pDatabases( pDatabases ) + { + } + + rtl::Reference<ResultSetBase> createResultSet() override + { + return new ResultSetForRoot( m_xContext, + m_xProvider, + m_seq, + m_aURLParameter, + m_pDatabases ); + } +}; + +class ResultSetForQueryFactory + : public ResultSetFactory +{ +private: + + uno::Reference< uno::XComponentContext > m_xContext; + uno::Reference< ucb::XContentProvider > m_xProvider; + uno::Sequence< beans::Property > m_seq; + URLParameter m_aURLParameter; + Databases* m_pDatabases; + +public: + + ResultSetForQueryFactory( + uno::Reference< uno::XComponentContext > xContext, + uno::Reference< ucb::XContentProvider > xProvider, + const uno::Sequence< beans::Property >& seq, + const URLParameter& rURLParameter, + Databases* pDatabases ) + : m_xContext(std::move( xContext )), + m_xProvider(std::move( xProvider )), + m_seq( seq ), + m_aURLParameter(rURLParameter), + m_pDatabases( pDatabases ) + { + } + + rtl::Reference<ResultSetBase> createResultSet() override + { + return new ResultSetForQuery( m_xContext, + m_xProvider, + m_seq, + m_aURLParameter, + m_pDatabases ); + } +}; + +} + +// virtual +uno::Any SAL_CALL Content::execute( + const ucb::Command& aCommand, + sal_Int32, + const uno::Reference< ucb::XCommandEnvironment >& Environment ) +{ + uno::Any aRet; + + if ( aCommand.Name == "getPropertyValues" ) + { + uno::Sequence< beans::Property > Properties; + if ( !( aCommand.Argument >>= Properties ) ) + { + aRet <<= lang::IllegalArgumentException(); + ucbhelper::cancelCommandExecution(aRet,Environment); + } + + aRet <<= getPropertyValues( Properties ); + } + else if ( aCommand.Name == "setPropertyValues" ) + { + uno::Sequence<beans::PropertyValue> propertyValues; + + if( ! ( aCommand.Argument >>= propertyValues ) ) { + aRet <<= lang::IllegalArgumentException(); + ucbhelper::cancelCommandExecution(aRet,Environment); + } + + uno::Sequence< uno::Any > ret(propertyValues.getLength()); + const uno::Sequence< beans::Property > props(getProperties(Environment)); + // No properties can be set + std::transform(std::cbegin(propertyValues), std::cend(propertyValues), ret.getArray(), + [&props](const beans::PropertyValue& rPropVal) { + if (std::any_of(props.begin(), props.end(), + [&rPropVal](const beans::Property& rProp) { return rProp.Name == rPropVal.Name; })) + return css::uno::toAny(lang::IllegalAccessException()); + return css::uno::toAny(beans::UnknownPropertyException()); + }); + + aRet <<= ret; + } + else if ( aCommand.Name == "getPropertySetInfo" ) + { + // Note: Implemented by base class. + aRet <<= getPropertySetInfo( Environment ); + } + else if ( aCommand.Name == "getCommandInfo" ) + { + // Note: Implemented by base class. + aRet <<= getCommandInfo( Environment ); + } + else if ( aCommand.Name == "open" ) + { + ucb::OpenCommandArgument2 aOpenCommand; + if ( !( aCommand.Argument >>= aOpenCommand ) ) + { + aRet <<= lang::IllegalArgumentException(); + ucbhelper::cancelCommandExecution(aRet,Environment); + } + + uno::Reference< io::XActiveDataSink > xActiveDataSink( + aOpenCommand.Sink, uno::UNO_QUERY); + + if(xActiveDataSink.is()) + m_aURLParameter.open(xActiveDataSink); + + uno::Reference< io::XActiveDataStreamer > xActiveDataStreamer( + aOpenCommand.Sink, uno::UNO_QUERY); + + if(xActiveDataStreamer.is()) { + aRet <<= ucb::UnsupportedDataSinkException(); + ucbhelper::cancelCommandExecution(aRet,Environment); + } + + uno::Reference< io::XOutputStream > xOutputStream( + aOpenCommand.Sink, uno::UNO_QUERY); + + if(xOutputStream.is() ) + m_aURLParameter.open(xOutputStream); + + if( m_aURLParameter.isRoot() ) + { + uno::Reference< ucb::XDynamicResultSet > xSet + = new DynamicResultSet( + m_xContext, + aOpenCommand, + std::make_unique<ResultSetForRootFactory>( + m_xContext, + m_xProvider.get(), + aOpenCommand.Properties, + m_aURLParameter, + m_pDatabases)); + aRet <<= xSet; + } + else if( m_aURLParameter.isQuery() ) + { + uno::Reference< ucb::XDynamicResultSet > xSet + = new DynamicResultSet( + m_xContext, + aOpenCommand, + std::make_unique<ResultSetForQueryFactory>( + m_xContext, + m_xProvider.get(), + aOpenCommand.Properties, + m_aURLParameter, + m_pDatabases ) ); + aRet <<= xSet; + } + } + else + { + // Unsupported command + aRet <<= ucb::UnsupportedCommandException(); + ucbhelper::cancelCommandExecution(aRet,Environment); + } + + return aRet; +} + +uno::Reference< sdbc::XRow > Content::getPropertyValues( + const uno::Sequence< beans::Property >& rProperties ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = + new ::ucbhelper::PropertyValueSet( m_xContext ); + + for ( const beans::Property& rProp : rProperties ) + { + if ( rProp.Name == "ContentType" ) + xRow->appendString( + rProp, + OUString( + "application/vnd.sun.star.help" ) ); + else if ( rProp.Name == "Title" ) + xRow->appendString ( rProp,m_aURLParameter.get_title() ); + else if ( rProp.Name == "IsReadOnly" ) + xRow->appendBoolean( rProp,true ); + else if ( rProp.Name == "IsDocument" ) + xRow->appendBoolean( + rProp, + m_aURLParameter.isFile() || m_aURLParameter.isRoot() ); + else if ( rProp.Name == "IsFolder" ) + xRow->appendBoolean( + rProp, + ! m_aURLParameter.isFile() || m_aURLParameter.isRoot() ); + else if ( rProp.Name == "IsErrorDocument" ) + xRow->appendBoolean( rProp, m_aURLParameter.isErrorDocument() ); + else if ( rProp.Name == "MediaType" ) + if( m_aURLParameter.isActive() ) + xRow->appendString( + rProp, + OUString( "text/plain" ) ); + else if( m_aURLParameter.isFile() ) + xRow->appendString( + rProp,OUString( "text/html" ) ); + else if( m_aURLParameter.isRoot() ) + xRow->appendString( + rProp, + OUString( "text/css" ) ); + else + xRow->appendVoid( rProp ); + else if( m_aURLParameter.isModule() ) + if ( rProp.Name == "KeywordList" ) + { + KeywordInfo *inf = + m_pDatabases->getKeyword( m_aURLParameter.get_module(), + m_aURLParameter.get_language() ); + + uno::Any aAny; + if( inf ) + aAny <<= inf->getKeywordList(); + xRow->appendObject( rProp,aAny ); + } + else if ( rProp.Name == "KeywordRef" ) + { + KeywordInfo *inf = + m_pDatabases->getKeyword( m_aURLParameter.get_module(), + m_aURLParameter.get_language() ); + + uno::Any aAny; + if( inf ) + aAny <<= inf->getIdList(); + xRow->appendObject( rProp,aAny ); + } + else if ( rProp.Name == "KeywordAnchorForRef" ) + { + KeywordInfo *inf = + m_pDatabases->getKeyword( m_aURLParameter.get_module(), + m_aURLParameter.get_language() ); + + uno::Any aAny; + if( inf ) + aAny <<= inf->getAnchorList(); + xRow->appendObject( rProp,aAny ); + } + else if ( rProp.Name == "KeywordTitleForRef" ) + { + KeywordInfo *inf = + m_pDatabases->getKeyword( m_aURLParameter.get_module(), + m_aURLParameter.get_language() ); + + uno::Any aAny; + if( inf ) + aAny <<= inf->getTitleList(); + xRow->appendObject( rProp,aAny ); + } + else if ( rProp.Name == "SearchScopes" ) + { + uno::Sequence< OUString > seq{ "Heading", "FullText" }; + xRow->appendObject( rProp, uno::Any(seq) ); + } + else if ( rProp.Name == "Order" ) + { + StaticModuleInformation *inf = + m_pDatabases->getStaticInformationForModule( + m_aURLParameter.get_module(), + m_aURLParameter.get_language() ); + + uno::Any aAny; + if( inf ) + aAny <<= sal_Int32( inf->get_order() ); + xRow->appendObject( rProp,aAny ); + } + else + xRow->appendVoid( rProp ); + else if( "AnchorName" == rProp.Name && + m_aURLParameter.isFile() ) + xRow->appendString( rProp,m_aURLParameter.get_tag() ); + else + xRow->appendVoid( rProp ); + } + + return xRow; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/content.hxx b/xmlhelp/source/cxxhelp/provider/content.hxx new file mode 100644 index 0000000000..362797a419 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/content.hxx @@ -0,0 +1,99 @@ +/* -*- 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 . + */ + +#pragma once + +#include <ucbhelper/contenthelper.hxx> + +#include "urlparameter.hxx" + +namespace com::sun::star::beans { + struct Property; + struct PropertyValue; +} +namespace com::sun::star::sdbc { + class XRow; +} + +namespace chelp +{ + + class Databases; + + class Content : public ::ucbhelper::ContentImplHelper + { + public: + + Content( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + ::ucbhelper::ContentProviderImplHelper* pProvider, + const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier, + Databases* pDatabases ); + + virtual ~Content() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + + // XTypeProvider + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + + // XServiceInfo + virtual OUString SAL_CALL + getImplementationName() override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + // XContent + virtual OUString SAL_CALL + getContentType() override; + + // XCommandProcessor + virtual css::uno::Any SAL_CALL + execute( const css::ucb::Command& aCommand, + sal_Int32 CommandId, + const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ) override; + + virtual void SAL_CALL + abort( sal_Int32 CommandId ) override; + + private: + + // private members; + + URLParameter m_aURLParameter; + Databases* m_pDatabases; + + // private methods + + virtual css::uno::Sequence< css::beans::Property > + getProperties( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) override; + virtual css::uno::Sequence< css::ucb::CommandInfo > + getCommands( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) override; + + virtual OUString getParentURL() override { return OUString(); } + + css::uno::Reference< css::sdbc::XRow > + getPropertyValues( const css::uno::Sequence< css::beans::Property >& rProperties ); + }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/contentcaps.cxx b/xmlhelp/source/cxxhelp/provider/contentcaps.cxx new file mode 100644 index 0000000000..b18061e434 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/contentcaps.cxx @@ -0,0 +1,199 @@ +/* -*- 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 <com/sun/star/beans/Property.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/ucb/CommandInfo.hpp> +#include <com/sun/star/ucb/OpenCommandArgument2.hpp> +#include <com/sun/star/uno/Sequence.hxx> + +#include "content.hxx" + +using namespace com::sun::star; + +using namespace chelp; + +// virtual +uno::Sequence< beans::Property > Content::getProperties( + const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ ) +{ + bool withMediaType = m_aURLParameter.isFile() || m_aURLParameter.isRoot(); + bool isModule = m_aURLParameter.isModule(); + bool isFile = m_aURLParameter.isFile(); + + sal_Int32 num = withMediaType ? 7 : 6; + if( isModule ) num+=6; + if( isFile ) num++; + + uno::Sequence< beans::Property > props(num); + auto pprops = props.getArray(); + + sal_Int32 idx = 0; + pprops[idx++] = + beans::Property( + "ContentType", + -1, + cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + pprops[idx++] = + beans::Property( + "IsReadOnly", + -1, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + pprops[idx++] = + beans::Property( + "IsErrorDocument", + -1, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + pprops[idx++] = + beans::Property( + "IsDocument", + -1, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + pprops[idx++] = + beans::Property( + "IsFolder", + -1, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + pprops[idx++] = + beans::Property( + "Title", + -1, + cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + if( withMediaType ) + pprops[idx++] = + beans::Property( + "MediaType", + -1, + cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + if( isModule ) + { + pprops[idx++] = + beans::Property( + "Order", + -1, + cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + pprops[idx++] = + beans::Property( + "KeywordList", + -1, + cppu::UnoType<uno::Sequence< OUString >>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + pprops[idx++] = + beans::Property( + "KeywordRef", + -1, + cppu::UnoType<uno::Sequence< uno::Sequence< OUString > >>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + pprops[idx++] = + beans::Property( + "KeywordTitleForRef", + -1, + cppu::UnoType<uno::Sequence< uno::Sequence< OUString > >>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + pprops[idx++] = + beans::Property( + "KeywordAnchorForRef", + -1, + cppu::UnoType<uno::Sequence< uno::Sequence< OUString > >>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + + pprops[idx++] = + beans::Property( + "SearchScopes", + -1, + cppu::UnoType<uno::Sequence< OUString >>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + } + + if( isFile ) + { + pprops[idx++] = + beans::Property( + "AnchorName", + -1, + cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ); + } + + return props; +} + +// virtual +uno::Sequence< ucb::CommandInfo > Content::getCommands( + const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ ) +{ + // Supported commands + +#define COMMAND_COUNT 5 + + static const ucb::CommandInfo aCommandInfoTable[] = + { + // Required commands + ucb::CommandInfo( + "getCommandInfo", + -1, + cppu::UnoType<void>::get() + ), + ucb::CommandInfo( + "getPropertySetInfo", + -1, + cppu::UnoType<void>::get() + ), + ucb::CommandInfo( + "getPropertyValues", + -1, + cppu::UnoType<uno::Sequence< beans::Property >>::get() + ), + ucb::CommandInfo( + "setPropertyValues", + -1, + cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get() + ), + ucb::CommandInfo( + "open", + -1, + cppu::UnoType<ucb::OpenCommandArgument2>::get() + ) + }; + + return uno::Sequence< ucb::CommandInfo >( + aCommandInfoTable, COMMAND_COUNT ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/databases.cxx b/xmlhelp/source/cxxhelp/provider/databases.cxx new file mode 100644 index 0000000000..1969c1d390 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/databases.cxx @@ -0,0 +1,1876 @@ +/* -*- 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 <memory> +#include "db.hxx" +#include <osl/diagnose.h> +#include <osl/file.hxx> +#include <rtl/character.hxx> +#include <rtl/uri.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ref.hxx> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/awt/Toolkit.hpp> +#include <com/sun/star/i18n/Collator.hpp> +#include <comphelper/propertysequence.hxx> +#include "inputstream.hxx" +#include <algorithm> +#include <cassert> +#include <string.h> +#include <string_view> + +#include <helpcompiler/HelpIndexer.hxx> + +// Extensible help +#include <com/sun/star/deployment/ExtensionManager.hpp> +#include <com/sun/star/deployment/ExtensionRemovedException.hpp> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/beans/Optional.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/util/theMacroExpander.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarExpandUrl.hpp> +#include <i18nlangtag/languagetag.hxx> + +#include <com/sun/star/awt/XVclWindowPeer.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <comphelper/storagehelper.hxx> +#include <utility> + +#include "databases.hxx" +#include "urlparameter.hxx" + +#ifdef _WIN32 +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#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<std::mutex>& /*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; +} + +const char vendVersion[] = "%VENDORVERSION"; +const char vendName[] = "%VENDORNAME"; +const char prodVersion[] = "%PRODUCTVERSION"; +const char vendShort[] = "%VENDORSHORT"; +const char prodName[] = "%PRODUCTNAME"; +const char newProdVersion[] = "$[officeversion]"; +const char newProdName[] = "$[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(); +} + +OString Databases::getImageTheme() const +{ + uno::Reference< lang::XMultiServiceFactory > xConfigProvider = + configuration::theDefaultProvider::get(m_xContext); + + // set root path + uno::Sequence<uno::Any> lParams(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(OUString("org.openoffice.Office.Common"))} + })); + + // open it + uno::Reference< uno::XInterface > xCFG( xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + lParams) ); + + uno::Reference< container::XHierarchicalNameAccess > xAccess(xCFG, uno::UNO_QUERY_THROW); + uno::Any aResult = xAccess->getByHierarchicalName("Misc/SymbolStyle"); + OUString aSymbolsStyleName; + aResult >>= aSymbolsStyleName; + + 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; +} + +OUString Databases::getInstallPathAsURL(std::unique_lock<std::mutex>& ) +{ + 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(".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( "1" ); + + 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<std::mutex>& /*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<OUString> 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<std::mutex>& rGuard, + std::u16string_view Database, + const OUString& Language, bool helpText, + const OUString* pExtensionPath ) + +{ + if( Database.empty() || Language.isEmpty() ) + return nullptr; + + OUString aFileExt( helpText ? OUString(".ht") : OUString(".db") ); + 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<helpdatafileproxy::Hdf> 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<std::mutex>&, 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<KeywordInfo::KeywordElement> 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> xCollator = getCollator(aGuard, Language); + KeywordElementComparator aComparator( xCollator ); + std::sort(aVector.begin(),aVector.end(),aComparator); + + it->second.reset(new KeywordInfo( aVector )); + } + + return it->second.get(); +} + +Reference< XHierarchicalNameAccess > Databases::jarFile( + std::unique_lock<std::mutex>& 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<XInputStream_impl> 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( + "com.sun.star.packages.comp.ZipPackage", + 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<OUString> 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( "HighContrastMode" ); + 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<LPBYTE>(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<sal_Bool> > option( xPackage->isRegistered + ( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() ) ); + bool bRegistered = false; + if( option.IsPresent ) + { + beans::Ambiguous<sal_Bool> const & reg = option.Value; + if( !reg.IsAmbiguous && reg.Value ) + bRegistered = true; + } + if( bRegistered ) + { + OUString aHelpMediaType( "application/vnd.sun.star.help" ); + if( xPackage->isBundle() ) + { + const Sequence< Reference< deployment::XPackage > > aPkgSeq = xPackage->getBundle + ( Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>() ); + 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 + ( "user", 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 + ( "shared", 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 + ( "bundled", 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<std::mutex> & 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("help"); + + 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<std::mutex>& 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<std::mutex>& rGuard, + const Reference<deployment::XPackage>& xPackage) +{ + OUString aExpandedURL = + implGetFileFromPackage(rGuard, u".key", xPackage); + + return aExpandedURL; +} + + +Reference<XHierarchicalNameAccess> JarFileIterator::nextJarFile( + std::unique_lock<std::mutex>& 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<std::mutex>& rGuard, + const Reference<deployment::XPackage>& 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("StorageFormat", + ZIP_STORAGE_FORMAT_STRING)) + }; + + Reference< XMultiComponentFactory >xSMgr = m_xContext->getServiceManager(); + Reference< XInterface > xIfc + = xSMgr->createInstanceWithArgumentsAndContext( + "com.sun.star.packages.comp.ZipPackage", + 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, "help", 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: */ diff --git a/xmlhelp/source/cxxhelp/provider/databases.hxx b/xmlhelp/source/cxxhelp/provider/databases.hxx new file mode 100644 index 0000000000..448d1f92cd --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/databases.hxx @@ -0,0 +1,449 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <memory> +#include <mutex> +#include <string_view> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <vector> +#include <rtl/ustring.hxx> +#include <rtl/strbuf.hxx> +#include <o3tl/string_view.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/i18n/XCollator.hpp> +#include <com/sun/star/deployment/XPackage.hpp> +#include <com/sun/star/ucb/XSimpleFileAccess3.hpp> + +// Forward declaration + +namespace helpdatafileproxy { + + class Hdf; + +} + +namespace chelp { + + class Databases; + class URLParameter; + + class StaticModuleInformation + { + private: + + OUString m_aStartId; + OUString m_aProgramSwitch; + OUString m_aTitle; + int m_nOrder; + + public: + + StaticModuleInformation( OUString aTitle, + OUString aStartId, + OUString aProgramSwitch, + std::u16string_view aOrder ) + : m_aStartId(std::move( aStartId )), + m_aProgramSwitch(std::move( aProgramSwitch )), + m_aTitle(std::move( aTitle )), + m_nOrder( o3tl::toInt32(aOrder) ) + { + } + + const OUString& get_title() const { return m_aTitle; } + const OUString& get_id() const { return m_aStartId; } + const OUString& get_program() const { return m_aProgramSwitch; } + int get_order() const { return m_nOrder; } + }; // end class StaticModuleInformation + + class KeywordInfo + { + public: + + class KeywordElement + { + friend struct KeywordElementComparator; + friend class KeywordInfo; + + public: + + KeywordElement( Databases const * pDatabases, + helpdatafileproxy::Hdf* pHdf, + OUString key, + std::u16string_view ids ); + + private: + + OUString key; + css::uno::Sequence< OUString > listId; + css::uno::Sequence< OUString > listAnchor; + css::uno::Sequence< OUString > listTitle; + + void init( Databases const *pDatabases,helpdatafileproxy::Hdf* pHdf, std::u16string_view ids ); + }; + + explicit KeywordInfo( const std::vector< KeywordElement >& aVector ); + + css::uno::Sequence< OUString >& + getKeywordList() { return listKey; } + + css::uno::Sequence< css::uno::Sequence< OUString > >& + getIdList() { return listId; } + + css::uno::Sequence< css::uno::Sequence< OUString > >& + getAnchorList() { return listAnchor; } + + css::uno::Sequence< css::uno::Sequence< OUString > >& + getTitleList() { return listTitle; } + + private: + + css::uno::Sequence< OUString > listKey; + css::uno::Sequence< css::uno::Sequence< OUString > > listId,listAnchor,listTitle; + }; // end class KeywordInfo + + class Databases + { + public: + + /** + * Input is the installdirectory in system dependent notation + */ + + Databases( bool showBasic, + const OUString& instPath, + const OUString& productName, + const OUString& productVersion, + const OUString& styleSheet, + css::uno::Reference< css::uno::XComponentContext > const & xContext ); + + ~Databases(); + + OString getImageTheme() const; + + OUString getInstallPathAsURL(); + OUString getInstallPathAsURL(std::unique_lock<std::mutex>& rGuard); + + const std::vector< OUString >& getModuleList( const OUString& Language ); + + StaticModuleInformation* getStaticInformationForModule( std::u16string_view Module, + const OUString& Language ); + + bool checkModuleMatchForExtension( std::u16string_view Database, const OUString& doclist ); + KeywordInfo* getKeyword( const OUString& Module, + const OUString& Language ); + + helpdatafileproxy::Hdf* getHelpDataFile( std::u16string_view Module, + const OUString& Language, bool helpText = false, + const OUString* pExtensionPath = nullptr ); + helpdatafileproxy::Hdf* getHelpDataFile(std::unique_lock<std::mutex>& rGuard, + std::u16string_view Module, + const OUString& Language, bool helpText = false, + const OUString* pExtensionPath = nullptr ); + + + /** + * The following method returns the Collator for the given language-country combination + */ + css::uno::Reference< css::i18n::XCollator > + getCollator(std::unique_lock<std::mutex>& rGuard, const OUString& Language); + + /** + * Returns the cascading style sheet used to format the HTML-output. + * First try is language directory, second try is main installation directory. + */ + + void cascadingStylesheet( const OUString& Language, + OStringBuffer& buffer ); + + /** + * Changes the stylesheet for further reads. + */ + + void changeCSS(const OUString& newStyleSheet); + + /** + * Returns the active help text for the given module, language and id. + */ + + void setActiveText( const OUString& Module, + const OUString& Language, + std::u16string_view Id, + OStringBuffer& buffer ); + + /** + * Has the purpose of forcing the jarfile to stay open + */ + + css::uno::Reference< css::container::XHierarchicalNameAccess > + jarFile(std::unique_lock<std::mutex>& rGuard, std::u16string_view jar, + const OUString& Language ); + + css::uno::Reference< css::container::XHierarchicalNameAccess > + findJarFileForPath( const OUString& jar, const OUString& Language, + const OUString& path, OUString* o_pExtensionPath = nullptr, + OUString* o_pExtensionRegistryPath = nullptr ); + + /** + * Maps a given language-locale combination to language or locale. + */ + OUString processLang( const OUString& Language ); + OUString processLang( std::unique_lock<std::mutex>& rGuard, const OUString& Language ); + + void replaceName( OUString& oustring ) const; + + const OUString& getProductName() const { return m_vReplacement[0]; } + const OUString& getProductVersion() const { return m_vReplacement[1]; } + + OUString expandURL( const OUString& aURL ); + OUString expandURL( std::unique_lock<std::mutex>& rGuard, const OUString& aURL ); + + static OUString expandURL( const OUString& aURL, + const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + private: + + std::mutex m_aMutex; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::lang::XMultiComponentFactory > m_xSMgr; + css::uno::Reference< css::ucb::XSimpleFileAccess3 > m_xSFA; + + bool m_bShowBasic; + + std::vector<char> m_vCustomCSSDoc; + OUString m_aCSS; + + enum { + PRODUCTNAME = 0, + PRODUCTVERSION, + VENDORNAME, + VENDORVERSION, + VENDORSHORT, + NEWPRODUCTNAME, + NEWPRODUCTVERSION + }; + + int m_vAdd[7]; + OUString m_vReplacement[7]; + + OUString m_aInstallDirectory; // Installation directory + + std::vector< OUString > m_avModules; + + typedef std::unordered_map< OUString, std::unique_ptr<helpdatafileproxy::Hdf> > DatabasesTable; + DatabasesTable m_aDatabases; // Language and module dependent databases + + typedef std::unordered_map< OUString,OUString > LangSetTable; + LangSetTable m_aLangSet; // Mapping to of lang-country to lang + + typedef std::unordered_map< OUString, std::unique_ptr<StaticModuleInformation> > ModInfoTable; + ModInfoTable m_aModInfo; // Module information + + typedef std::unordered_map< OUString, std::unique_ptr<KeywordInfo> > KeywordInfoTable; + KeywordInfoTable m_aKeywordInfo; // Module information + + typedef + std::unordered_map< + OUString, + css::uno::Reference< css::container::XHierarchicalNameAccess > > ZipFileTable; + ZipFileTable m_aZipFileTable; // No closing of an once opened jarfile + + typedef + std::unordered_map< + OUString, + css::uno::Reference< css::i18n::XCollator > > CollatorTable; + CollatorTable m_aCollatorTable; + + + typedef + std::unordered_set< + OString > EmptyActiveTextSet; + EmptyActiveTextSet m_aEmptyActiveTextSet; + + // methods + + void setInstallPath( const OUString& aInstallDirectory ); + + }; // end class Databases + + enum class IteratorState + { + InitialModule, + //SHARED_MODULE, // Later, avoids redundancies in help compiling + UserExtensions, + SharedExtensions, + BundledExtensions, + EndReached + }; + + // Hashtable to cache extension help status + typedef std::unordered_map + < + OUString, + bool + > + ExtensionHelpExistenceMap; + + class ExtensionIteratorBase + { + static ExtensionHelpExistenceMap aHelpExistenceMap; + + public: + ExtensionIteratorBase( css::uno::Reference< css::uno::XComponentContext > const & xContext, + Databases& rDatabases, OUString aInitialModule, OUString aLanguage ); + ExtensionIteratorBase( Databases& rDatabases, OUString aInitialModule, + OUString aLanguage ); + void init(); + + private: + static css::uno::Reference< css::deployment::XPackage > implGetHelpPackageFromPackage + ( const css::uno::Reference< css::deployment::XPackage >& xPackage, + css::uno::Reference< css::deployment::XPackage >& o_xParentPackageBundle ); + + protected: + css::uno::Reference< css::deployment::XPackage > implGetNextUserHelpPackage + ( css::uno::Reference< css::deployment::XPackage >& o_xParentPackageBundle ); + css::uno::Reference< css::deployment::XPackage > implGetNextSharedHelpPackage + ( css::uno::Reference< css::deployment::XPackage >& o_xParentPackageBundle ); + css::uno::Reference< css::deployment::XPackage > implGetNextBundledHelpPackage + ( css::uno::Reference< css::deployment::XPackage >& o_xParentPackageBundle ); + OUString implGetFileFromPackage( std::u16string_view rFileExtension, + const css::uno::Reference< css::deployment::XPackage >& xPackage ); + OUString implGetFileFromPackage(std::unique_lock<std::mutex>& rGuard, + std::u16string_view rFileExtension, + const css::uno::Reference< css::deployment::XPackage >& xPackage ); + void implGetLanguageVectorFromPackage( ::std::vector< OUString > &rv, + const css::uno::Reference< css::deployment::XPackage >& xPackage ); + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::ucb::XSimpleFileAccess3 > m_xSFA; + Databases& m_rDatabases; + + IteratorState m_eState; + + OUString m_aInitialModule; + OUString m_aLanguage; + + css::uno::Sequence< css::uno::Reference + < css::deployment::XPackage > > m_aUserPackagesSeq; + bool m_bUserPackagesLoaded; + + css::uno::Sequence< css::uno::Reference + < css::deployment::XPackage > > m_aSharedPackagesSeq; + bool m_bSharedPackagesLoaded; + + css::uno::Sequence< css::uno::Reference + < css::deployment::XPackage > > m_aBundledPackagesSeq; + bool m_bBundledPackagesLoaded; + + int m_iUserPackage; + int m_iSharedPackage; + int m_iBundledPackage; + + }; // end class ExtensionIteratorBase + + class DataBaseIterator : public ExtensionIteratorBase + { + public: + DataBaseIterator( css::uno::Reference< css::uno::XComponentContext > const & xContext, + Databases& rDatabases, const OUString& aInitialModule, const OUString& aLanguage, bool bHelpText ) + : ExtensionIteratorBase( xContext, rDatabases, aInitialModule, aLanguage ) + , m_bHelpText( bHelpText ) + {} + DataBaseIterator( Databases& rDatabases, const OUString& aInitialModule, + const OUString& aLanguage, bool bHelpText ) + : ExtensionIteratorBase( rDatabases, aInitialModule, aLanguage ) + , m_bHelpText( bHelpText ) + {} + + helpdatafileproxy::Hdf* nextHdf( OUString* o_pExtensionPath = nullptr, OUString* o_pExtensionRegistryPath = nullptr ); + + private: + helpdatafileproxy::Hdf* implGetHdfFromPackage( + const css::uno::Reference< css::deployment::XPackage >& xPackage, + OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath ); + + bool m_bHelpText; + + }; // end class DataBaseIterator + + class KeyDataBaseFileIterator : public ExtensionIteratorBase + { + public: + KeyDataBaseFileIterator( css::uno::Reference< css::uno::XComponentContext > const & xContext, + Databases& rDatabases, const OUString& aInitialModule, const OUString& aLanguage ) + : ExtensionIteratorBase( xContext, rDatabases, aInitialModule, aLanguage ) + {} + //Returns a file URL + OUString nextDbFile(std::unique_lock<std::mutex>& rGuard, bool& o_rbExtension); + + private: + OUString implGetDbFileFromPackage(std::unique_lock<std::mutex>& rGuard, + const css::uno::Reference< css::deployment::XPackage >& xPackage ); + + }; // end class KeyDataBaseFileIterator + + class JarFileIterator : public ExtensionIteratorBase + { + public: + JarFileIterator( css::uno::Reference< css::uno::XComponentContext > const & xContext, + Databases& rDatabases, const OUString& aInitialModule, const OUString& aLanguage ) + : ExtensionIteratorBase( xContext, rDatabases, aInitialModule, aLanguage ) + {} + + css::uno::Reference< css::container::XHierarchicalNameAccess > + nextJarFile(std::unique_lock<std::mutex>& rGuard, + css::uno::Reference<css::deployment::XPackage>& o_xParentPackageBundle, + OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath ); + + private: + css::uno::Reference< css::container::XHierarchicalNameAccess > + implGetJarFromPackage(std::unique_lock<std::mutex>& rGuard, + const css::uno::Reference< css::deployment::XPackage >& xPackage, + OUString* o_pExtensionPath, OUString* o_pExtensionRegistryPath ); + + }; // end class JarFileIterator + + class IndexFolderIterator : public ExtensionIteratorBase + { + public: + IndexFolderIterator( Databases& rDatabases, const OUString& aInitialModule, const OUString& aLanguage ) + : ExtensionIteratorBase( rDatabases, aInitialModule, aLanguage ) + {} + + OUString nextIndexFolder( bool& o_rbExtension, bool& o_rbTemporary ); + void deleteTempIndexFolder( std::u16string_view aIndexFolder ); + + private: + OUString implGetIndexFolderFromPackage( bool& o_rbTemporary, + const css::uno::Reference< css::deployment::XPackage >& xPackage ); + + }; // end class KeyDataBaseFileIterator + +} // end namespace chelp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/db.cxx b/xmlhelp/source/cxxhelp/provider/db.cxx new file mode 100644 index 0000000000..d60bff78ee --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/db.cxx @@ -0,0 +1,264 @@ +/* -*- 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 "db.hxx" + +#include <algorithm> +#include <charconv> +#include <cstring> +#include <system_error> +#include <utility> + +#include <com/sun/star/io/XSeekable.hpp> + +using namespace com::sun::star::uno; +using namespace com::sun::star::io; + +namespace { + +std::pair<sal_Int32, char const *> readInt32(char const * begin, char const * end) { + sal_Int32 n = 0; + auto const [ptr, ec] = std::from_chars(begin, end, n, 16); + return {std::max(n, sal_Int32(0)), ec == std::errc{} && n >= 0 ? ptr : begin}; +} + +} + +namespace helpdatafileproxy { + +void HDFData::copyToBuffer( const char* pSrcData, int nSize ) +{ + m_nSize = nSize; + m_pBuffer.reset( new char[m_nSize+1] ); + memcpy( m_pBuffer.get(), pSrcData, m_nSize ); + m_pBuffer[m_nSize] = 0; +} + + +// Hdf + +bool Hdf::implReadLenAndData( const char* pData, char const * end, int& riPos, HDFData& rValue ) +{ + bool bSuccess = false; + + // Read key len + const char* pStartPtr = pData + riPos; + auto [nKeyLen, pEndPtr] = readInt32(pStartPtr, end); + if( pEndPtr == pStartPtr ) + return bSuccess; + riPos += (pEndPtr - pStartPtr) + 1; + + const char* pKeySrc = pData + riPos; + rValue.copyToBuffer( pKeySrc, nKeyLen ); + riPos += nKeyLen + 1; + + bSuccess = true; + return bSuccess; +} + +void Hdf::createHashMap( bool bOptimizeForPerformance ) +{ + releaseHashMap(); + if( bOptimizeForPerformance ) + { + if( m_pStringToDataMap != nullptr ) + return; + m_pStringToDataMap.reset(new StringToDataMap); + } + else + { + if( m_pStringToValPosMap != nullptr ) + return; + m_pStringToValPosMap.reset(new StringToValPosMap); + } + + Reference< XInputStream > xIn = m_xSFA->openFileRead( m_aFileURL ); + if( !xIn.is() ) + return; + + Sequence< sal_Int8 > aData; + sal_Int32 nSize = m_xSFA->getSize( m_aFileURL ); + sal_Int32 nRead = xIn->readBytes( aData, nSize ); + + const char* pData = reinterpret_cast<const char*>(aData.getConstArray()); + auto const end = pData + nRead; + int iPos = 0; + while( iPos < nRead ) + { + HDFData aDBKey; + if( !implReadLenAndData( pData, end, iPos, aDBKey ) ) + break; + + OString aOKeyStr = aDBKey.getData(); + + // Read val len + const char* pStartPtr = pData + iPos; + auto [nValLen, pEndPtr] = readInt32(pStartPtr, end); + if( pEndPtr == pStartPtr ) + break; + + iPos += (pEndPtr - pStartPtr) + 1; + + if( bOptimizeForPerformance ) + { + const char* pValSrc = pData + iPos; + OString aValStr( pValSrc, nValLen ); + (*m_pStringToDataMap)[aOKeyStr] = aValStr; + } + else + { + // store value start position + (*m_pStringToValPosMap)[aOKeyStr] = std::pair<int,int>( iPos, nValLen ); + } + iPos += nValLen + 1; + } + + xIn->closeInput(); +} + +void Hdf::releaseHashMap() +{ + m_pStringToDataMap.reset(); + m_pStringToValPosMap.reset(); +} + + +Hdf::~Hdf() +{ +} + +bool Hdf::getValueForKey( const OString& rKey, HDFData& rValue ) +{ + bool bSuccess = false; + if( !m_xSFA.is() ) + return bSuccess; + + try + { + + if( m_pStringToDataMap == nullptr && m_pStringToValPosMap == nullptr ) + { + createHashMap( false/*bOptimizeForPerformance*/ ); + } + + if( m_pStringToValPosMap != nullptr ) + { + StringToValPosMap::const_iterator it = m_pStringToValPosMap->find( rKey ); + if( it != m_pStringToValPosMap->end() ) + { + const std::pair<int,int>& rValPair = it->second; + int iValuePos = rValPair.first; + int nValueLen = rValPair.second; + + Reference< XInputStream > xIn = m_xSFA->openFileRead( m_aFileURL ); + if( xIn.is() ) + { + Reference< XSeekable > xXSeekable( xIn, UNO_QUERY ); + if( xXSeekable.is() ) + { + xXSeekable->seek( iValuePos ); + + Sequence< sal_Int8 > aData; + sal_Int32 nRead = xIn->readBytes( aData, nValueLen ); + if( nRead == nValueLen ) + { + const char* pData = reinterpret_cast<const char*>(aData.getConstArray()); + rValue.copyToBuffer( pData, nValueLen ); + bSuccess = true; + } + } + xIn->closeInput(); + } + } + } + + else if( m_pStringToDataMap != nullptr ) + { + StringToDataMap::const_iterator it = m_pStringToDataMap->find( rKey ); + if( it != m_pStringToDataMap->end() ) + { + const OString& rValueStr = it->second; + int nValueLen = rValueStr.getLength(); + const char* pData = rValueStr.getStr(); + rValue.copyToBuffer( pData, nValueLen ); + bSuccess = true; + } + } + + } + catch( Exception & ) + { + bSuccess = false; + } + + return bSuccess; +} + +bool Hdf::startIteration() +{ + bool bSuccess = false; + + sal_Int32 nSize = m_xSFA->getSize( m_aFileURL ); + + Reference< XInputStream > xIn = m_xSFA->openFileRead( m_aFileURL ); + if( xIn.is() ) + { + m_nItRead = xIn->readBytes( m_aItData, nSize ); + if( m_nItRead == nSize ) + { + bSuccess = true; + m_iItPos = 0; + } + else + { + stopIteration(); + } + } + + return bSuccess; +} + +bool Hdf::getNextKeyAndValue( HDFData& rKey, HDFData& rValue ) +{ + bool bSuccess = false; + + if( m_iItPos < m_nItRead ) + { + auto const p = reinterpret_cast<const char*>(m_aItData.getConstArray()); + if( implReadLenAndData( p, p + m_aItData.size(), m_iItPos, rKey ) ) + { + if( implReadLenAndData( p, p + m_aItData.size(), m_iItPos, rValue ) ) + bSuccess = true; + } + } + + return bSuccess; +} + +void Hdf::stopIteration() +{ + m_aItData = Sequence<sal_Int8>(); + m_nItRead = -1; + m_iItPos = -1; +} + +} // end of namespace helpdatafileproxy + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/db.hxx b/xmlhelp/source/cxxhelp/provider/db.hxx new file mode 100644 index 0000000000..9a63c8f098 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/db.hxx @@ -0,0 +1,96 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/ucb/XSimpleFileAccess3.hpp> +#include <comphelper/fileurl.hxx> +#include <osl/diagnose.h> +#include <rtl/string.hxx> +#include <memory> +#include <unordered_map> + +namespace helpdatafileproxy { + + class HDFData + { + friend class Hdf; + + int m_nSize; + std::unique_ptr<char[]> m_pBuffer; + + void copyToBuffer( const char* pSrcData, int nSize ); + + public: + HDFData() : m_nSize( 0 ) {} + + int getSize() const + { return m_nSize; } + const char* getData() const + { return m_pBuffer.get(); } + }; + + typedef std::unordered_map< OString,std::pair<int,int> > StringToValPosMap; + typedef std::unordered_map< OString,OString > StringToDataMap; + + class Hdf + { + OUString m_aFileURL; + std::unique_ptr<StringToDataMap> m_pStringToDataMap; + std::unique_ptr<StringToValPosMap> m_pStringToValPosMap; + css::uno::Reference< css::ucb::XSimpleFileAccess3 > + m_xSFA; + + css::uno::Sequence< sal_Int8 > + m_aItData; + int m_nItRead; + int m_iItPos; + + static bool implReadLenAndData( + const char* pData, char const * end, int& riPos, HDFData& rValue ); + + public: + //HDFHelp must get a fileURL which can then directly be used by simple file access. + //SimpleFileAccess requires file URLs as arguments. Passing file path may work but fails + //for example when using long file paths on Windows, which start with "\\?\" + Hdf( OUString aFileURL, + css::uno::Reference< css::ucb::XSimpleFileAccess3 > xSFA ) + : m_aFileURL( std::move(aFileURL) ) + , m_xSFA( std::move(xSFA) ) + , m_nItRead( -1 ) + , m_iItPos( -1 ) + { + OSL_ASSERT(comphelper::isFileUrl(m_aFileURL)); + } + ~Hdf(); + + void createHashMap( bool bOptimizeForPerformance ); + void releaseHashMap(); + + bool getValueForKey( const OString& rKey, HDFData& rValue ); + + bool startIteration(); + bool getNextKeyAndValue( HDFData& rKey, HDFData& rValue ); + void stopIteration(); + Hdf(const Hdf&) = delete; + void operator=(const Hdf&) = delete; + }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/inputstream.cxx b/xmlhelp/source/cxxhelp/provider/inputstream.cxx new file mode 100644 index 0000000000..59c87cef40 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/inputstream.cxx @@ -0,0 +1,185 @@ +/* -*- 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 "inputstream.hxx" + +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <cppuhelper/queryinterface.hxx> + + +using namespace chelp; +using namespace com::sun::star; + + +XInputStream_impl::XInputStream_impl( const OUString& aUncPath ) + : m_bIsOpen( false ), + m_aFile( aUncPath ) +{ + m_bIsOpen = ( osl::FileBase::E_None == m_aFile.open( osl_File_OpenFlag_Read ) ); +} + +XInputStream_impl::~XInputStream_impl() +{ + if (m_bIsOpen) + m_aFile.close(); +} + +uno::Any SAL_CALL +XInputStream_impl::queryInterface( const uno::Type& rType ) +{ + uno::Any aRet = cppu::queryInterface( rType, + static_cast< io::XInputStream* >(this), + static_cast< io::XSeekable* >(this) ); + return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ); +} + + +void SAL_CALL +XInputStream_impl::acquire() + noexcept +{ + OWeakObject::acquire(); +} + + +void SAL_CALL +XInputStream_impl::release() + noexcept +{ + OWeakObject::release(); +} + + +sal_Int32 SAL_CALL +XInputStream_impl::readBytes( + uno::Sequence< sal_Int8 >& aData, + sal_Int32 nBytesToRead ) +{ + if( ! m_bIsOpen ) + throw io::IOException(); + + if (aData.getLength() < nBytesToRead) + aData.realloc(nBytesToRead); + //TODO! translate memory exhaustion (if it were detectable...) into + // io::BufferSizeExceededException + + sal_uInt64 nBytesRead; + m_aFile.read( aData.getArray(), sal_uInt64(nBytesToRead), nBytesRead ); + + // Shrink aData in case we read less than nBytesToRead (XInputStream + // documentation does not tell whether this is required, and I do not know + // if any code relies on this, so be conservative---SB): + if (nBytesRead != sal::static_int_cast<sal_uInt64>(nBytesToRead) ) + aData.realloc(sal_Int32(nBytesRead)); + return static_cast<sal_Int32>(nBytesRead); +} + +sal_Int32 SAL_CALL +XInputStream_impl::readSomeBytes( + uno::Sequence< sal_Int8 >& aData, + sal_Int32 nMaxBytesToRead ) +{ + return readBytes( aData,nMaxBytesToRead ); +} + + +void SAL_CALL +XInputStream_impl::skipBytes( + sal_Int32 nBytesToSkip ) +{ + if (m_aFile.setPos(osl_Pos_Current, sal_uInt64(nBytesToSkip)) != osl::FileBase::E_None) + { + throw io::IOException("XInputStream_impl::skipBytes failed seek"); + } +} + + +sal_Int32 SAL_CALL +XInputStream_impl::available() +{ + sal_uInt64 uPos; + if( osl::FileBase::E_None != m_aFile.getPos( uPos ) ) + throw io::IOException(); + sal_uInt64 uSize; + if( osl::FileBase::E_None != m_aFile.getSize( uSize ) ) + throw io::IOException(); + return std::min<sal_uInt64>(SAL_MAX_INT32, uSize - uPos); +} + + +void SAL_CALL +XInputStream_impl::closeInput() +{ + if( m_bIsOpen ) + { + osl::FileBase::RC err = m_aFile.close(); + if( err != osl::FileBase::E_None ) + throw io::IOException(); + m_bIsOpen = false; + } +} + + +void SAL_CALL +XInputStream_impl::seek( sal_Int64 location ) +{ + if( location < 0 ) + throw lang::IllegalArgumentException(); + if( osl::FileBase::E_None != m_aFile.setPos( osl_Pos_Absolut, sal_uInt64( location ) ) ) + throw io::IOException(); +} + + +sal_Int64 SAL_CALL +XInputStream_impl::getPosition() +{ + sal_uInt64 uPos; + if( osl::FileBase::E_None != m_aFile.getPos( uPos ) ) + throw io::IOException(); + return sal_Int64( uPos ); +} + +sal_Int64 SAL_CALL +XInputStream_impl::getLength() +{ + osl::FileBase::RC err; + sal_uInt64 uCurrentPos, uEndPos; + + err = m_aFile.getPos( uCurrentPos ); + if( err != osl::FileBase::E_None ) + throw io::IOException(); + + err = m_aFile.setPos( osl_Pos_End, 0 ); + if( err != osl::FileBase::E_None ) + throw io::IOException(); + + err = m_aFile.getPos( uEndPos ); + if( err != osl::FileBase::E_None ) + throw io::IOException(); + + err = m_aFile.setPos( osl_Pos_Absolut, uCurrentPos ); + if( err != osl::FileBase::E_None ) + throw io::IOException(); + + return sal_Int64( uEndPos ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/inputstream.hxx b/xmlhelp/source/cxxhelp/provider/inputstream.hxx new file mode 100644 index 0000000000..5f9146293b --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/inputstream.hxx @@ -0,0 +1,96 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <osl/file.hxx> +#include <cppuhelper/weak.hxx> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/io/XInputStream.hpp> + + +namespace chelp { + + class XInputStream_impl + : public cppu::OWeakObject, + public css::io::XInputStream, + public css::io::XSeekable + { + + public: + explicit XInputStream_impl( const OUString& aUncPath ); + + virtual ~XInputStream_impl() override; + + /** + * Returns an error code as given by filerror.hxx + */ + + bool CtorSuccess() { return m_bIsOpen;} + + virtual css::uno::Any SAL_CALL + queryInterface( + const css::uno::Type& rType ) override; + + virtual void SAL_CALL + acquire() + noexcept override; + + virtual void SAL_CALL + release() + noexcept override; + + virtual sal_Int32 SAL_CALL + readBytes( + css::uno::Sequence< sal_Int8 >& aData, + sal_Int32 nBytesToRead ) override; + + virtual sal_Int32 SAL_CALL + readSomeBytes( + css::uno::Sequence< sal_Int8 >& aData, + sal_Int32 nMaxBytesToRead ) override; + + virtual void SAL_CALL + skipBytes( sal_Int32 nBytesToSkip ) override; + + virtual sal_Int32 SAL_CALL + available() override; + + virtual void SAL_CALL + closeInput() override; + + virtual void SAL_CALL + seek( sal_Int64 location ) override; + + virtual sal_Int64 SAL_CALL + getPosition() override; + + virtual sal_Int64 SAL_CALL + getLength() override; + + private: + bool m_bIsOpen; + osl::File m_aFile; + }; + + +} // end namespace XInputStream_impl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/provider.cxx b/xmlhelp/source/cxxhelp/provider/provider.cxx new file mode 100644 index 0000000000..eb28f5a9ca --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/provider.cxx @@ -0,0 +1,198 @@ +/* -*- 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 <config_folders.h> + +#include <officecfg/Office/Common.hxx> +#include <officecfg/Setup.hxx> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/ucb/IllegalIdentifierException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/factory.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/pathoptions.hxx> + +#include "databases.hxx" +#include "provider.hxx" +#include "content.hxx" + +using namespace com::sun::star; +using namespace chelp; + + +// ContentProvider Implementation. + +ContentProvider::ContentProvider( const uno::Reference< uno::XComponentContext >& rxContext ) + : ContentProvider_Base( rxContext ) + , isInitialized( false ) +{ +} + +// virtual +ContentProvider::~ContentProvider() +{ +} + +// XServiceInfo methods. + +OUString SAL_CALL ContentProvider::getImplementationName() +{ + return "CHelpContentProvider"; +} + +sal_Bool SAL_CALL +ContentProvider::supportsService(const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL +ContentProvider::getSupportedServiceNames() +{ + return { "com.sun.star.help.XMLHelp", "com.sun.star.ucb.HelpContentProvider" }; +} + +// XContentProvider methods. + +// virtual +uno::Reference< ucb::XContent > SAL_CALL +ContentProvider::queryContent( + const uno::Reference< ucb::XContentIdentifier >& xCanonicId ) +{ + if ( !xCanonicId->getContentProviderScheme() + .equalsIgnoreAsciiCase( MYUCP_URL_SCHEME ) ) + { // Wrong URL-scheme + throw ucb::IllegalIdentifierException(); + } + + { + osl::MutexGuard aGuard( m_aMutex ); + if( !isInitialized ) + init(); + } + + if( !m_pDatabases ) + throw uno::RuntimeException(); + + // Check, if a content with given id already exists... + uno::Reference< ucb::XContent > xContent + = queryExistingContent( xCanonicId ); + if ( xContent.is() ) + return xContent; + + xContent = new Content( m_xContext, this, xCanonicId, m_pDatabases.get() ); + + // register new content + registerNewContent( xContent ); + + // Further checks + + if ( !xContent->getIdentifier().is() ) + throw ucb::IllegalIdentifierException(); + + return xContent; +} + +void SAL_CALL +ContentProvider::dispose() +{ + if(m_xContainer.is()) + { + m_xContainer->removeContainerListener(this); + m_xContainer.clear(); + } +} + +void SAL_CALL +ContentProvider::elementReplaced(const container::ContainerEvent& Event) +{ + if(!m_pDatabases) + return; + + OUString accessor; + Event.Accessor >>= accessor; + if(accessor != "HelpStyleSheet") + return; + + OUString replacedElement,element; + Event.ReplacedElement >>= replacedElement; + Event.Element >>= element; + + if(replacedElement == element) + return; + + m_pDatabases->changeCSS(element); +} + +void ContentProvider::init() +{ + osl::MutexGuard aGuard( m_aMutex ); + + isInitialized = true; + + OUString instPath( + officecfg::Office::Common::Path::Current::Help::get()); + if( instPath.isEmpty() ) + // try to determine path from default + instPath = "$(instpath)/" LIBO_SHARE_HELP_FOLDER; + // replace anything like $(instpath); + subst( instPath ); + + OUString stylesheet( + officecfg::Office::Common::Help::HelpStyleSheet::get()); + + // now adding as configuration change listener for the stylesheet + m_xContainer.set( + officecfg::Office::Common::Help::get(), + css::uno::UNO_QUERY_THROW); + m_xContainer->addContainerListener( this ); + + OUString setupversion( + officecfg::Setup::Product::ooSetupVersion::get()); + OUString setupextension( + officecfg::Setup::Product::ooSetupExtension::get()); + OUString productversion( setupversion + " " + setupextension ); + + bool showBasic = officecfg::Office::Common::Help::ShowBasic::get(); + m_pDatabases.reset( new Databases( showBasic, + instPath, + utl::ConfigManager::getProductName(), + productversion, + stylesheet, + m_xContext ) ); +} + +void ContentProvider::subst( OUString& instpath ) +{ + SvtPathOptions aOptions; + instpath = aOptions.SubstituteVariable( instpath ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +CHelpContentProvider_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new ContentProvider(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/provider.hxx b/xmlhelp/source/cxxhelp/provider/provider.hxx new file mode 100644 index 0000000000..3c1c8eec50 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/provider.hxx @@ -0,0 +1,106 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include <rtl/ustring.hxx> +#include <ucbhelper/providerhelper.hxx> +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +namespace chelp { + +// URL scheme. This is the scheme the provider will be able to create +// contents for. The UCB will select the provider ( i.e. in order to create +// contents ) according to this scheme. + +#define MYUCP_URL_SCHEME "vnd.sun.star.help" +inline constexpr OUString MYUCP_CONTENT_TYPE = u"application/vnd.sun.star.xmlhelp"_ustr; // UCB Content Type. + + class Databases; + + typedef cppu::ImplInheritanceHelper< ::ucbhelper::ContentProviderImplHelper, css::container::XContainerListener, css::lang::XComponent> ContentProvider_Base; + class ContentProvider : public ContentProvider_Base + { + public: + explicit ContentProvider( + const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + virtual ~ContentProvider() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XContentProvider + virtual css::uno::Reference< css::ucb::XContent > SAL_CALL queryContent( + const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier ) override; + + // Additional interfaces + + // XComponent + + virtual void SAL_CALL + dispose( ) override; + + virtual void SAL_CALL + addEventListener( const css::uno::Reference< css::lang::XEventListener >& ) override {} + + virtual void SAL_CALL + removeEventListener( const css::uno::Reference< css::lang::XEventListener >& ) override {} + + // XContainerListener ( derive from XEventListener ) + + virtual void SAL_CALL + disposing( const css::lang::EventObject& /*Source*/ ) override + { + m_xContainer.clear(); + } + + virtual void SAL_CALL + elementInserted( const css::container::ContainerEvent& ) override {} + + virtual void SAL_CALL + elementRemoved( const css::container::ContainerEvent& ) override {} + + virtual void SAL_CALL + elementReplaced( const css::container::ContainerEvent& Event ) override; + + // Non-interface methods. + + private: + bool isInitialized; + std::unique_ptr<Databases> m_pDatabases; + css::uno::Reference<css::container::XContainer> m_xContainer; + + // private methods + + void init(); + + static void subst( OUString& instpath ); + }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/resultset.cxx b/xmlhelp/source/cxxhelp/provider/resultset.cxx new file mode 100644 index 0000000000..23d7dcbf79 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultset.cxx @@ -0,0 +1,61 @@ +/* -*- 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 <com/sun/star/sdbc/XResultSet.hpp> + +#include "resultset.hxx" +#include "resultsetfactory.hxx" + +using namespace com::sun::star::lang; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::ucb; +using namespace com::sun::star::uno; + +using namespace chelp; + +// DynamicResultSet Implementation. + +DynamicResultSet::DynamicResultSet( + const Reference< XComponentContext >& rxContext, + const OpenCommandArgument2& rCommand, + std::unique_ptr<ResultSetFactory> pFactory ) + : ResultSetImplHelper( rxContext, rCommand ), + m_pFactory( std::move(pFactory) ) +{ +} + +DynamicResultSet::~DynamicResultSet() +{ +} + +// Non-interface methods. + +void DynamicResultSet::initStatic() +{ + m_xResultSet1.set( m_pFactory->createResultSet() ); +} + +void DynamicResultSet::initDynamic() +{ + m_xResultSet1.set( m_pFactory->createResultSet() ); + + m_xResultSet2 = m_xResultSet1; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/resultset.hxx b/xmlhelp/source/cxxhelp/provider/resultset.hxx new file mode 100644 index 0000000000..81a7bb0b3a --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultset.hxx @@ -0,0 +1,48 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include <ucbhelper/resultsethelper.hxx> + +namespace chelp { + + class ResultSetFactory; + + class DynamicResultSet : public ::ucbhelper::ResultSetImplHelper + { + std::unique_ptr<ResultSetFactory> m_pFactory; + + private: + virtual void initStatic() override; + virtual void initDynamic() override; + + public: + DynamicResultSet( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::ucb::OpenCommandArgument2& rCommand, + std::unique_ptr<ResultSetFactory> pFactory ); + + virtual ~DynamicResultSet() override; + }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/resultsetbase.cxx b/xmlhelp/source/cxxhelp/provider/resultsetbase.cxx new file mode 100644 index 0000000000..58d8e70c5b --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultsetbase.cxx @@ -0,0 +1,485 @@ +/* -*- 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 <ucbhelper/contentidentifier.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <ucbhelper/resultsetmetadata.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <utility> + +#include "resultsetbase.hxx" + +using namespace chelp; +using namespace com::sun::star; + +ResultSetBase::ResultSetBase( uno::Reference< uno::XComponentContext > xContext, + uno::Reference< ucb::XContentProvider > xProvider, + const uno::Sequence< beans::Property >& seq ) + : m_xContext(std::move( xContext )), + m_xProvider(std::move( xProvider )), + m_nRow( -1 ), + m_nWasNull( true ), + m_sProperty( seq ) +{ +} + +ResultSetBase::~ResultSetBase() +{ +} + + +// XInterface + +void SAL_CALL +ResultSetBase::acquire() + noexcept +{ + OWeakObject::acquire(); +} + + +void SAL_CALL +ResultSetBase::release() + noexcept +{ + OWeakObject::release(); +} + + +uno::Any SAL_CALL +ResultSetBase::queryInterface( const uno::Type& rType ) +{ + uno::Any aRet = cppu::queryInterface( rType, + static_cast< lang::XComponent* >(this), + static_cast< sdbc::XRow* >(this), + static_cast< sdbc::XResultSet* >(this), + static_cast< sdbc::XResultSetMetaDataSupplier* >(this), + static_cast< beans::XPropertySet* >(this), + static_cast< ucb::XContentAccess* >(this) ); + return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ); +} + + +// XComponent + + +void SAL_CALL +ResultSetBase::addEventListener( + const uno::Reference< lang::XEventListener >& Listener ) +{ + std::unique_lock aGuard( m_aMutex ); + m_aDisposeEventListeners.addInterface( aGuard, Listener ); +} + + +void SAL_CALL +ResultSetBase::removeEventListener( + const uno::Reference< lang::XEventListener >& Listener ) +{ + std::unique_lock aGuard( m_aMutex ); + m_aDisposeEventListeners.removeInterface( aGuard, Listener ); +} + + +void SAL_CALL +ResultSetBase::dispose() +{ + std::unique_lock aGuard( m_aMutex ); + + lang::EventObject aEvt; + aEvt.Source = static_cast< lang::XComponent * >( this ); + + if ( m_aDisposeEventListeners.getLength(aGuard) ) + { + m_aDisposeEventListeners.disposeAndClear( aGuard, aEvt ); + } + if( m_aRowCountListeners.getLength(aGuard) ) + { + m_aRowCountListeners.disposeAndClear( aGuard, aEvt ); + } + if( m_aIsFinalListeners.getLength(aGuard) ) + { + m_aIsFinalListeners.disposeAndClear( aGuard, aEvt ); + } +} + + +// XResultSet + +sal_Bool SAL_CALL +ResultSetBase::next() +{ + m_nRow++; + return sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size(); +} + + +sal_Bool SAL_CALL +ResultSetBase::isBeforeFirst() +{ + return m_nRow == -1; +} + + +sal_Bool SAL_CALL +ResultSetBase::isAfterLast() +{ + return sal::static_int_cast<sal_uInt32>( m_nRow ) >= m_aItems.size(); // Cannot happen, if m_aFolder.isOpen() +} + + +sal_Bool SAL_CALL +ResultSetBase::isFirst() +{ + return m_nRow == 0; +} + + +sal_Bool SAL_CALL +ResultSetBase::isLast() +{ + if( sal::static_int_cast<sal_uInt32>( m_nRow ) == m_aItems.size() - 1 ) + return true; + else + return false; +} + + +void SAL_CALL +ResultSetBase::beforeFirst() +{ + m_nRow = -1; +} + + +void SAL_CALL +ResultSetBase::afterLast() +{ + m_nRow = m_aItems.size(); +} + + +sal_Bool SAL_CALL +ResultSetBase::first() +{ + m_nRow = -1; + return next(); +} + + +sal_Bool SAL_CALL +ResultSetBase::last() +{ + m_nRow = m_aItems.size() - 1; + return true; +} + + +sal_Int32 SAL_CALL +ResultSetBase::getRow() +{ + // Test, whether behind last row + if( -1 == m_nRow || sal::static_int_cast<sal_uInt32>( m_nRow ) >= m_aItems.size() ) + return 0; + else + return m_nRow+1; +} + + +sal_Bool SAL_CALL ResultSetBase::absolute( sal_Int32 row ) +{ + if( row >= 0 ) + m_nRow = row - 1; + else + { + last(); + m_nRow += ( row + 1 ); + if( m_nRow < -1 ) + m_nRow = -1; + } + + return 0<= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size(); +} + + +sal_Bool SAL_CALL +ResultSetBase::relative( sal_Int32 row ) +{ + if( isAfterLast() || isBeforeFirst() ) + throw sdbc::SQLException(); + + if( row > 0 ) + while( row-- ) + next(); + else if( row < 0 ) + while( row++ && m_nRow > -1 ) + previous(); + + return 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size(); +} + + +sal_Bool SAL_CALL +ResultSetBase::previous() +{ + if( sal::static_int_cast<sal_uInt32>( m_nRow ) > m_aItems.size() ) + m_nRow = m_aItems.size(); // Correct Handling of afterLast + if( 0 <= m_nRow ) -- m_nRow; + + return 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size(); +} + + +void SAL_CALL +ResultSetBase::refreshRow() +{ +} + + +sal_Bool SAL_CALL +ResultSetBase::rowUpdated() +{ + return false; +} + +sal_Bool SAL_CALL +ResultSetBase::rowInserted() +{ + return false; +} + +sal_Bool SAL_CALL +ResultSetBase::rowDeleted() +{ + return false; +} + + +uno::Reference< uno::XInterface > SAL_CALL +ResultSetBase::getStatement() +{ + return uno::Reference< uno::XInterface >(); +} + + +// XCloseable + +void SAL_CALL +ResultSetBase::close() +{ +} + + +OUString SAL_CALL +ResultSetBase::queryContentIdentifierString() +{ + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aPath[m_nRow]; + else + return OUString(); +} + + +uno::Reference< ucb::XContentIdentifier > SAL_CALL +ResultSetBase::queryContentIdentifier() +{ + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + { + OUString url = queryContentIdentifierString(); + if( ! m_aIdents[m_nRow].is() && !url.isEmpty() ) + m_aIdents[m_nRow].set( new ::ucbhelper::ContentIdentifier( url ) ); + return m_aIdents[m_nRow]; + } + + return uno::Reference< ucb::XContentIdentifier >(); +} + + +uno::Reference< ucb::XContent > SAL_CALL +ResultSetBase::queryContent() +{ + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_xProvider->queryContent( queryContentIdentifier() ); + else + return uno::Reference< ucb::XContent >(); +} + +namespace { + +class XPropertySetInfoImpl + : public cppu::OWeakObject, + public beans::XPropertySetInfo +{ +public: + + explicit XPropertySetInfoImpl( const uno::Sequence< beans::Property >& aSeq ) + : m_aSeq( aSeq ) + { + } + + void SAL_CALL acquire() + noexcept override + { + OWeakObject::acquire(); + } + + + void SAL_CALL release() + noexcept override + { + OWeakObject::release(); + } + + uno::Any SAL_CALL queryInterface( const uno::Type& rType ) override + { + uno::Any aRet = cppu::queryInterface( rType, + static_cast< beans::XPropertySetInfo* >(this) ); + return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ); + } + + uno::Sequence< beans::Property > SAL_CALL getProperties() override + { + return m_aSeq; + } + + beans::Property SAL_CALL getPropertyByName( const OUString& aName ) override + { + auto pProp = std::find_if(std::cbegin(m_aSeq), std::cend(m_aSeq), + [&aName](const beans::Property& rProp) { return aName == rProp.Name; }); + if (pProp != std::cend(m_aSeq)) + return *pProp; + throw beans::UnknownPropertyException(aName); + } + + sal_Bool SAL_CALL hasPropertyByName( const OUString& Name ) override + { + return std::any_of(std::cbegin(m_aSeq), std::cend(m_aSeq), + [&Name](const beans::Property& rProp) { return Name == rProp.Name; }); + } + +private: + + uno::Sequence< beans::Property > m_aSeq; +}; + +} + +// XPropertySet +uno::Reference< beans::XPropertySetInfo > SAL_CALL +ResultSetBase::getPropertySetInfo() +{ + uno::Sequence< beans::Property > seq + { + { "RowCount", -1, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY }, + { "IsRowCountFinal", -1, cppu::UnoType<sal_Bool>::get(), beans::PropertyAttribute::READONLY } + }; + + //t + return uno::Reference< beans::XPropertySetInfo > ( new XPropertySetInfoImpl( seq ) ); +} + + +void SAL_CALL ResultSetBase::setPropertyValue( + const OUString& aPropertyName, const uno::Any& ) +{ + if( aPropertyName == "IsRowCountFinal" || + aPropertyName == "RowCount" ) + return; + + throw beans::UnknownPropertyException(aPropertyName); +} + + +uno::Any SAL_CALL ResultSetBase::getPropertyValue( + const OUString& PropertyName ) +{ + if( PropertyName == "IsRowCountFinal" ) + { + return uno::Any(true); + } + else if ( PropertyName == "RowCount" ) + { + sal_Int32 count = m_aItems.size(); + return uno::Any(count); + } + else + throw beans::UnknownPropertyException(PropertyName); +} + + +void SAL_CALL ResultSetBase::addPropertyChangeListener( + const OUString& aPropertyName, + const uno::Reference< beans::XPropertyChangeListener >& xListener ) +{ + if( aPropertyName == "IsRowCountFinal" ) + { + std::unique_lock aGuard( m_aMutex ); + m_aIsFinalListeners.addInterface( aGuard, xListener ); + } + else if ( aPropertyName == "RowCount" ) + { + std::unique_lock aGuard( m_aMutex ); + m_aRowCountListeners.addInterface( aGuard, xListener ); + } + else + throw beans::UnknownPropertyException(aPropertyName); +} + + +void SAL_CALL ResultSetBase::removePropertyChangeListener( + const OUString& aPropertyName, + const uno::Reference< beans::XPropertyChangeListener >& aListener ) +{ + if( aPropertyName == "IsRowCountFinal" ) + { + std::unique_lock aGuard( m_aMutex ); + m_aIsFinalListeners.removeInterface( aGuard, aListener ); + } + else if ( aPropertyName == "RowCount" ) + { + std::unique_lock aGuard( m_aMutex ); + m_aRowCountListeners.removeInterface( aGuard, aListener ); + } + else + throw beans::UnknownPropertyException(aPropertyName); +} + + +void SAL_CALL ResultSetBase::addVetoableChangeListener( + const OUString&, + const uno::Reference< beans::XVetoableChangeListener >& ) +{} + + +void SAL_CALL ResultSetBase::removeVetoableChangeListener( + const OUString&, + const uno::Reference< beans::XVetoableChangeListener >& ) +{} + + +// XResultSetMetaDataSupplier +uno::Reference< sdbc::XResultSetMetaData > SAL_CALL +ResultSetBase::getMetaData() +{ + return new ::ucbhelper::ResultSetMetaData( m_xContext, m_sProperty ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/resultsetbase.hxx b/xmlhelp/source/cxxhelp/provider/resultsetbase.hxx new file mode 100644 index 0000000000..4327d3e972 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultsetbase.hxx @@ -0,0 +1,400 @@ +/* -*- 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 . + */ +#pragma once + +#include <vector> +#include <memory> +#include <cppuhelper/weak.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/ucb/XContentProvider.hpp> +#include <com/sun/star/ucb/XContentIdentifier.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/beans/Property.hpp> + + +namespace chelp { + + class ResultSetBase + : public cppu::OWeakObject, + public css::lang::XComponent, + public css::sdbc::XRow, + public css::sdbc::XResultSet, + public css::sdbc::XCloseable, + public css::sdbc::XResultSetMetaDataSupplier, + public css::beans::XPropertySet, + public css::ucb::XContentAccess + { + public: + + ResultSetBase( css::uno::Reference< css::uno::XComponentContext > xContext, + css::uno::Reference< css::ucb::XContentProvider > xProvider, + const css::uno::Sequence< css::beans::Property >& seq ); + + virtual ~ResultSetBase() override; + + // XInterface + virtual css::uno::Any SAL_CALL + queryInterface( const css::uno::Type& aType ) override; + + virtual void SAL_CALL + acquire() + noexcept override; + + virtual void SAL_CALL + release() + noexcept override; + + // XComponent + virtual void SAL_CALL + dispose() override; + + virtual void SAL_CALL + addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + + virtual void SAL_CALL + removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + + // XRow + virtual sal_Bool SAL_CALL + wasNull() override + { + if( 0<= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + m_nWasNull = m_aItems[m_nRow]->wasNull(); + else + m_nWasNull = true; + return m_nWasNull; + } + + virtual OUString SAL_CALL + getString( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getString( columnIndex ); + else + return OUString(); + } + + virtual sal_Bool SAL_CALL + getBoolean( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getBoolean( columnIndex ); + else + return false; + } + + virtual sal_Int8 SAL_CALL + getByte( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getByte( columnIndex ); + else + return sal_Int8( 0 ); + } + + virtual sal_Int16 SAL_CALL + getShort( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getShort( columnIndex ); + else + return sal_Int16( 0 ); + } + + virtual sal_Int32 SAL_CALL + getInt( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getInt( columnIndex ); + else + return 0; + } + + virtual sal_Int64 SAL_CALL + getLong( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getLong( columnIndex ); + else + return sal_Int64( 0 ); + } + + virtual float SAL_CALL + getFloat( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getFloat( columnIndex ); + else + return float( 0 ); + } + + virtual double SAL_CALL + getDouble( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getDouble( columnIndex ); + else + return double( 0 ); + } + + virtual css::uno::Sequence< sal_Int8 > SAL_CALL + getBytes( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getBytes( columnIndex ); + else + return css::uno::Sequence< sal_Int8 >(); + } + + virtual css::util::Date SAL_CALL + getDate( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getDate( columnIndex ); + else + return css::util::Date(); + } + + virtual css::util::Time SAL_CALL + getTime( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getTime( columnIndex ); + else + return css::util::Time(); + } + + virtual css::util::DateTime SAL_CALL + getTimestamp( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getTimestamp( columnIndex ); + else + return css::util::DateTime(); + } + + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL + getBinaryStream( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getBinaryStream( columnIndex ); + else + return css::uno::Reference< css::io::XInputStream >(); + } + + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL + getCharacterStream( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getCharacterStream( columnIndex ); + else + return css::uno::Reference< css::io::XInputStream >(); + } + + virtual css::uno::Any SAL_CALL + getObject( sal_Int32 columnIndex, + const css::uno::Reference< css::container::XNameAccess >& typeMap ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getObject( columnIndex,typeMap ); + else + return css::uno::Any(); + } + + virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL + getRef( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getRef( columnIndex ); + else + return css::uno::Reference< css::sdbc::XRef >(); + } + + virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL + getBlob( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getBlob( columnIndex ); + else + return css::uno::Reference< css::sdbc::XBlob >(); + } + + virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL + getClob( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getClob( columnIndex ); + else + return css::uno::Reference< css::sdbc::XClob >(); + } + + virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL + getArray( sal_Int32 columnIndex ) override + { + if( 0 <= m_nRow && sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aItems.size() ) + return m_aItems[m_nRow]->getArray( columnIndex ); + else + return css::uno::Reference< css::sdbc::XArray >(); + } + + + // XResultSet + + virtual sal_Bool SAL_CALL + next() override; + + virtual sal_Bool SAL_CALL + isBeforeFirst() override; + + virtual sal_Bool SAL_CALL + isAfterLast() override; + + virtual sal_Bool SAL_CALL + isFirst() override; + + virtual sal_Bool SAL_CALL + isLast() override; + + virtual void SAL_CALL + beforeFirst() override; + + virtual void SAL_CALL + afterLast() override; + + virtual sal_Bool SAL_CALL + first() override; + + virtual sal_Bool SAL_CALL + last() override; + + virtual sal_Int32 SAL_CALL + getRow() override; + + virtual sal_Bool SAL_CALL + absolute( sal_Int32 row ) override; + + virtual sal_Bool SAL_CALL + relative( sal_Int32 rows ) override; + + virtual sal_Bool SAL_CALL + previous() override; + + virtual void SAL_CALL + refreshRow() override; + + virtual sal_Bool SAL_CALL + rowUpdated() override; + + virtual sal_Bool SAL_CALL + rowInserted() override; + + virtual sal_Bool SAL_CALL + rowDeleted() override; + + + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL + getStatement() override; + + // XCloseable + + virtual void SAL_CALL + close() override; + + // XContentAccess + + virtual OUString SAL_CALL + queryContentIdentifierString() override; + + virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL + queryContentIdentifier() override; + + virtual css::uno::Reference< css::ucb::XContent > SAL_CALL + queryContent() override; + + // XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL + getMetaData() override; + + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL + getPropertySetInfo() override; + + virtual void SAL_CALL setPropertyValue( + const OUString& aPropertyName, + const css::uno::Any& aValue ) override; + + virtual css::uno::Any SAL_CALL + getPropertyValue( + const OUString& PropertyName ) override; + + virtual void SAL_CALL + addPropertyChangeListener( + const OUString& aPropertyName, + const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + + virtual void SAL_CALL + removePropertyChangeListener( + const OUString& aPropertyName, + const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + + virtual void SAL_CALL + addVetoableChangeListener( + const OUString& PropertyName, + const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + virtual void SAL_CALL removeVetoableChangeListener( + const OUString& PropertyName, + const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + protected: + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::ucb::XContentProvider > m_xProvider; + sal_Int32 m_nRow; + bool m_nWasNull; + + typedef std::vector< css::uno::Reference< css::ucb::XContentIdentifier > > IdentSet; + typedef std::vector< css::uno::Reference< css::sdbc::XRow > > ItemSet; + + IdentSet m_aIdents; + ItemSet m_aItems; + std::vector<OUString> m_aPath; + + css::uno::Sequence< css::beans::Property > m_sProperty; + + std::mutex m_aMutex; + comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_aDisposeEventListeners; + comphelper::OInterfaceContainerHelper4<css::beans::XPropertyChangeListener> m_aRowCountListeners; + comphelper::OInterfaceContainerHelper4<css::beans::XPropertyChangeListener> m_aIsFinalListeners; + }; + + +} // end namespace fileaccess + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/resultsetfactory.hxx b/xmlhelp/source/cxxhelp/provider/resultsetfactory.hxx new file mode 100644 index 0000000000..b78b7a4e32 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultsetfactory.hxx @@ -0,0 +1,42 @@ +/* -*- 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 . + */ + +#pragma once + +#include "resultsetbase.hxx" +#include <rtl/ref.hxx> + +namespace chelp { + + class ResultSetBase; + + class ResultSetFactory + { + public: + + virtual ~ResultSetFactory() { }; + + virtual rtl::Reference<ResultSetBase> createResultSet() = 0; + }; + + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/resultsetforquery.cxx b/xmlhelp/source/cxxhelp/provider/resultsetforquery.cxx new file mode 100644 index 0000000000..b598c241e7 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultsetforquery.cxx @@ -0,0 +1,331 @@ +/* -*- 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 <iterator> + +#include <com/sun/star/ucb/Command.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/i18n/Transliteration.hpp> +#include <com/sun/star/ucb/XCommandProcessor.hpp> +#include <com/sun/star/lang/Locale.hpp> + +#include <helpcompiler/HelpSearch.hxx> +#include <comphelper/diagnose_ex.hxx> + +#if defined(__GNUC__) +# pragma GCC visibility push (default) +#endif +#include <CLucene.h> +#if defined(__GNUC__) +# pragma GCC visibility pop +#endif + +#include <rtl/ustring.hxx> +#include <sal/log.hxx> + +#include <algorithm> +#include <set> +#include <utility> +#include "resultsetforquery.hxx" +#include "databases.hxx" + +using namespace chelp; +using namespace com::sun::star; +using namespace com::sun::star::ucb; +using namespace com::sun::star::i18n; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; + +namespace { + +struct HitItem +{ + OUString m_aURL; + float m_fScore; + + HitItem(OUString aURL, float fScore) + : m_aURL(std::move(aURL)) + , m_fScore(fScore) + {} + bool operator < ( const HitItem& rHitItem ) const + { + return rHitItem.m_fScore < m_fScore; + } +}; + +} + +ResultSetForQuery::ResultSetForQuery( const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< XContentProvider >& xProvider, + const uno::Sequence< beans::Property >& seq, + const URLParameter& aURLParameter, + Databases* pDatabases ) + : ResultSetBase( rxContext,xProvider,seq ) +{ + Reference< XExtendedTransliteration > xTrans = Transliteration::create( rxContext ); + Locale aLocale( aURLParameter.get_language(), + OUString(), + OUString() ); + xTrans->loadModule(TransliterationModules_UPPERCASE_LOWERCASE, + aLocale ); + + std::vector< std::vector< OUString > > queryList; + { + sal_Int32 idx; + OUString query = aURLParameter.get_query(); + while( !query.isEmpty() ) + { + idx = query.indexOf( ' ' ); + if( idx == -1 ) + idx = query.getLength(); + + std::vector< OUString > currentQuery; + OUString tmp(query.copy( 0,idx )); + Sequence<sal_Int32> aSeq; + OUString toliterate = xTrans->transliterate( + tmp,0,tmp.getLength(),aSeq); + + currentQuery.push_back( toliterate ); + queryList.push_back( currentQuery ); + + int nCpy = 1 + idx; + if( nCpy >= query.getLength() ) + query.clear(); + else + query = query.copy( 1 + idx ); + } + } + + std::vector< OUString > aCompleteResultVector; + OUString scope = aURLParameter.get_scope(); + bool bCaptionsOnly = scope == "Heading"; + sal_Int32 hitCount = aURLParameter.get_hitCount(); + + IndexFolderIterator aIndexFolderIt( *pDatabases, aURLParameter.get_module(), aURLParameter.get_language() ); + OUString idxDir; + bool bExtension = false; + std::vector< std::vector<HitItem> > aIndexFolderResultVectorVector; + + bool bTemporary; + for (;;) + { + idxDir = aIndexFolderIt.nextIndexFolder( bExtension, bTemporary ); + if( idxDir.isEmpty() ) + break; + std::vector<HitItem> aIndexFolderResultVector; + + try + { + std::vector< std::vector<HitItem> > aQueryListResultVectorVector; + std::set< OUString > aSet,aCurrent,aResultSet; + + int nQueryListSize = queryList.size(); + if( nQueryListSize > 1 ) + hitCount = 2000; + + for( int i = 0; i < nQueryListSize; ++i ) + { + std::vector<HitItem>* pQueryResultVector; + if( nQueryListSize > 1 ) + { + aQueryListResultVectorVector.emplace_back(); + pQueryResultVector = &aQueryListResultVectorVector.back(); + } + else + { + pQueryResultVector = &aIndexFolderResultVector; + } + pQueryResultVector->reserve( hitCount ); + + const std::vector< OUString >& aListItem = queryList[i]; + OUString aNewQueryStr = aListItem[0]; + + std::vector<float> aScoreVector; + std::vector<OUString> aPathVector; + + try + { + HelpSearch searcher(idxDir); + searcher.query(aNewQueryStr, bCaptionsOnly, aPathVector, aScoreVector); + } + catch (CLuceneError &e) + { + SAL_WARN("xmlhelp", "CLuceneError: " << e.what()); + } + + if( nQueryListSize > 1 ) + aSet.clear(); + + for (size_t j = 0; j < aPathVector.size(); ++j) { + pQueryResultVector->push_back(HitItem(aPathVector[j], aScoreVector[j])); + if (nQueryListSize > 1) + aSet.insert(aPathVector[j]); + } + + // intersect + if( nQueryListSize > 1 ) + { + if( i == 0 ) + { + aResultSet = aSet; + } + else + { + aCurrent = aResultSet; + aResultSet.clear(); + set_intersection( aSet.begin(),aSet.end(), + aCurrent.begin(),aCurrent.end(), + inserter(aResultSet,aResultSet.begin())); + } + } + } + + // Combine results in aIndexFolderResultVector + if( nQueryListSize > 1 ) + { + for( int n = 0 ; n < nQueryListSize ; ++n ) + { + std::vector<HitItem>& rQueryResultVector = aQueryListResultVectorVector[n]; + + int nItemCount = rQueryResultVector.size(); + for( int i = 0 ; i < nItemCount ; ++i ) + { + const HitItem& rItem = rQueryResultVector[ i ]; + if( (aResultSet.find( rItem.m_aURL )) != aResultSet.end() ) + { + HitItem aItemCopy( rItem ); + aItemCopy.m_fScore /= nQueryListSize; // To get average score + if( n == 0 ) + { + // Use first pass to create entry + aIndexFolderResultVector.push_back( aItemCopy ); + } + else + { + // Find entry in vector + int nCount = aIndexFolderResultVector.size(); + for( int j = 0 ; j < nCount ; ++j ) + { + HitItem& rFindItem = aIndexFolderResultVector[ j ]; + if( rFindItem.m_aURL == aItemCopy.m_aURL ) + { + rFindItem.m_fScore += aItemCopy.m_fScore; + break; + } + } + } + } + } + } + + sort( aIndexFolderResultVector.begin(), aIndexFolderResultVector.end() ); + } + + aIndexFolderResultVectorVector.push_back( std::move(aIndexFolderResultVector) ); + } + catch (const Exception &) + { + TOOLS_WARN_EXCEPTION("xmlhelp", ""); + } + + if( bTemporary ) + aIndexFolderIt.deleteTempIndexFolder( idxDir ); + + } // Iterator + + + int nVectorCount = aIndexFolderResultVectorVector.size(); + std::unique_ptr<std::vector<HitItem>::size_type[]> pCurrentVectorIndex(new std::vector<HitItem>::size_type[nVectorCount]); + for( int j = 0 ; j < nVectorCount ; ++j ) + pCurrentVectorIndex[j] = 0; + + sal_Int32 nTotalHitCount = aURLParameter.get_hitCount(); + sal_Int32 nHitCount = 0; + while( nHitCount < nTotalHitCount ) + { + int iVectorWithBestScore = -1; + float fBestScore = 0.0; + for( int k = 0 ; k < nVectorCount ; ++k ) + { + std::vector<HitItem>& rIndexFolderVector = aIndexFolderResultVectorVector[k]; + if( pCurrentVectorIndex[k] < rIndexFolderVector.size() ) + { + const HitItem& rItem = rIndexFolderVector[ pCurrentVectorIndex[k] ]; + + if( fBestScore < rItem.m_fScore ) + { + fBestScore = rItem.m_fScore; + iVectorWithBestScore = k; + } + } + } + + if( iVectorWithBestScore == -1 ) // No item left at all + break; + + std::vector<HitItem>& rIndexFolderVector = aIndexFolderResultVectorVector[iVectorWithBestScore]; + const HitItem& rItem = rIndexFolderVector[ pCurrentVectorIndex[iVectorWithBestScore] ]; + + pCurrentVectorIndex[iVectorWithBestScore]++; + + aCompleteResultVector.push_back( rItem.m_aURL ); + ++nHitCount; + } + + pCurrentVectorIndex.reset(); + aIndexFolderResultVectorVector.clear(); + + sal_Int32 replIdx = strlen( "#HLP#" ); + OUString replWith = "vnd.sun.star.help://"; + + int nResultCount = aCompleteResultVector.size(); + for( int r = 0 ; r < nResultCount ; ++r ) + { + OUString aURL = aCompleteResultVector[r]; + OUString aResultStr = replWith + aURL.subView(replIdx); + m_aPath.push_back( aResultStr ); + } + + m_aItems.resize( m_aPath.size() ); + m_aIdents.resize( m_aPath.size() ); + + Command aCommand; + aCommand.Name = "getPropertyValues"; + aCommand.Argument <<= m_sProperty; + + for( m_nRow = 0; sal::static_int_cast<sal_uInt32>( m_nRow ) < m_aPath.size(); ++m_nRow ) + { + m_aPath[m_nRow] = + m_aPath[m_nRow] + + "?Language=" + + aURLParameter.get_language() + + "&System=" + + aURLParameter.get_system(); + + uno::Reference< XContent > content = queryContent(); + if( content.is() ) + { + uno::Reference< XCommandProcessor > cmd( content,uno::UNO_QUERY ); + cmd->execute( aCommand,0,uno::Reference< XCommandEnvironment >( nullptr ) ) >>= m_aItems[m_nRow]; //TODO: check return value of operator >>= + } + } + m_nRow = 0xffffffff; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/resultsetforquery.hxx b/xmlhelp/source/cxxhelp/provider/resultsetforquery.hxx new file mode 100644 index 0000000000..c20d64b9a7 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultsetforquery.hxx @@ -0,0 +1,47 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/ucb/XContentProvider.hpp> +#include <com/sun/star/beans/Property.hpp> + +#include "resultsetbase.hxx" +#include "urlparameter.hxx" + +namespace chelp { + + class Databases; + + class ResultSetForQuery + : public ResultSetBase + { + public: + + ResultSetForQuery( const css::uno::Reference<css::uno::XComponentContext>& rxContext, + const css::uno::Reference<css::ucb::XContentProvider>& xProvider, + const css::uno::Sequence< css::beans::Property >& seq, + const URLParameter& aURLParameter, + Databases* pDatabases ); + }; + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/resultsetforroot.cxx b/xmlhelp/source/cxxhelp/provider/resultsetforroot.cxx new file mode 100644 index 0000000000..b2b7e29a2d --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultsetforroot.cxx @@ -0,0 +1,69 @@ +/* -*- 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 <com/sun/star/ucb/Command.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/ucb/XCommandProcessor.hpp> + +#include "resultsetforroot.hxx" +#include "databases.hxx" + +using namespace chelp; +using namespace com::sun::star; +using namespace com::sun::star::ucb; + + +ResultSetForRoot::ResultSetForRoot( const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< XContentProvider >& xProvider, + const uno::Sequence< beans::Property >& seq, + URLParameter const & aURLParameter, + Databases* pDatabases ) + : ResultSetBase( rxContext, xProvider,seq ) +{ + m_aPath = pDatabases->getModuleList( aURLParameter.get_language() ); + m_aItems.resize( m_aPath.size() ); + m_aIdents.resize( m_aPath.size() ); + + Command aCommand; + aCommand.Name = "getPropertyValues"; + aCommand.Argument <<= m_sProperty; + + for( size_t i = 0; i < m_aPath.size(); ++i ) + { + m_aPath[i] = + "vnd.sun.star.help://" + + m_aPath[i] + + "?Language=" + + aURLParameter.get_language() + + "&System=" + + aURLParameter.get_system(); + + m_nRow = sal_Int32( i ); + + uno::Reference< XContent > content = queryContent(); + if( content.is() ) + { + uno::Reference< XCommandProcessor > cmd( content,uno::UNO_QUERY ); + cmd->execute( aCommand,0,uno::Reference< XCommandEnvironment >( nullptr ) ) >>= m_aItems[i]; //TODO: check return value of operator >>= + } + m_nRow = 0xffffffff; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/resultsetforroot.hxx b/xmlhelp/source/cxxhelp/provider/resultsetforroot.hxx new file mode 100644 index 0000000000..58e04407e4 --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/resultsetforroot.hxx @@ -0,0 +1,47 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/ucb/XContentProvider.hpp> +#include <com/sun/star/beans/Property.hpp> + +#include "resultsetbase.hxx" +#include "urlparameter.hxx" + +namespace chelp { + + class Databases; + + class ResultSetForRoot + : public ResultSetBase + { + public: + + ResultSetForRoot( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::ucb::XContentProvider >& xProvider, + const css::uno::Sequence< css::beans::Property >& seq, + URLParameter const & aURLParameter, + Databases* pDatabases ); + }; + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/urlparameter.cxx b/xmlhelp/source/cxxhelp/provider/urlparameter.cxx new file mode 100644 index 0000000000..c242c74add --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/urlparameter.cxx @@ -0,0 +1,983 @@ +/* -*- 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 <string.h> +#include <osl/thread.h> +#include <osl/file.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/processfactory.hxx> +#include <rtl/uri.hxx> +#include <rtl/ref.hxx> +#include <rtl/character.hxx> +#include <o3tl/string_view.hxx> +#include <libxslt/transform.h> +#include <libxslt/xsltutils.h> +#include <libxslt/security.h> +#include "db.hxx" +#include <com/sun/star/io/XActiveDataSink.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/ucb/IllegalIdentifierException.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> + +#include "urlparameter.hxx" +#include "databases.hxx" + +#include <algorithm> +#include <memory> +#include <mutex> + +using namespace cppu; +using namespace com::sun::star::io; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::ucb; +using namespace com::sun::star::beans; +using namespace com::sun::star::container; +using namespace chelp; + + +URLParameter::URLParameter( const OUString& aURL, + Databases* pDatabases ) + : m_pDatabases( pDatabases ), + m_aURL( aURL ) +{ + init(); + parse(); +} + + +bool URLParameter::isErrorDocument() +{ + bool bErrorDoc = false; + + if( isFile() ) + { + Reference< XHierarchicalNameAccess > xNA = + m_pDatabases->findJarFileForPath( get_jar(), get_language(), get_path() ); + bErrorDoc = !xNA.is(); + } + + return bErrorDoc; +} + + +OString URLParameter::getByName( const char* par ) +{ + OUString val; + + if( strcmp( par,"Program" ) == 0 ) + val = get_program(); + else if( strcmp( par,"Database" ) == 0 ) + val = get_module(); + else if( strcmp( par,"DatabasePar" ) == 0 ) + val = get_dbpar(); + else if( strcmp( par,"Id" ) == 0 ) + val = get_id(); + else if( strcmp( par,"Path" ) == 0 ) + val = get_path(); + else if( strcmp( par,"Language" ) == 0 ) + val = get_language(); + else if( strcmp( par,"System" ) == 0 ) + val = get_system(); + else if( strcmp( par,"HelpPrefix" ) == 0 ) + val = m_aPrefix; + + return OUStringToOString( val, RTL_TEXTENCODING_UTF8 ); +} + + +OUString const & URLParameter::get_id() +{ + if( m_aId == "start" ) + { // module is set + StaticModuleInformation* inf = + m_pDatabases->getStaticInformationForModule( get_module(), + get_language() ); + if( inf ) + m_aId = inf->get_id(); + } + + return m_aId; +} + +OUString URLParameter::get_tag() +{ + if( isFile() ) + return get_the_tag(); + else + return m_aTag; +} + + +OUString URLParameter::get_title() +{ + if( isFile() ) + return get_the_title(); + else if( !m_aModule.isEmpty() ) + { + StaticModuleInformation* inf = + m_pDatabases->getStaticInformationForModule( get_module(), + get_language() ); + if( inf ) + m_aTitle = inf->get_title(); + } + else // This must be the root + m_aTitle = "root"; + + return m_aTitle; +} + + +OUString const & URLParameter::get_language() const +{ + return m_aLanguage; +} + + +OUString const & URLParameter::get_program() +{ + if( m_aProgram.isEmpty() ) + { + StaticModuleInformation* inf = + m_pDatabases->getStaticInformationForModule( get_module(), + get_language() ); + if( inf ) + m_aProgram = inf->get_program(); + } + return m_aProgram; +} + + +void URLParameter::init() +{ + m_bHelpDataFileRead = false; + m_bUseDB = true; + m_nHitCount = 100; // The default maximum hitcount +} + + +OUString URLParameter::get_the_tag() +{ + if(m_bUseDB) { + if( ! m_bHelpDataFileRead ) + readHelpDataFile(); + + m_bHelpDataFileRead = true; + + return m_aTag; + } + else + return OUString(); +} + + +OUString const & URLParameter::get_path() +{ + if(m_bUseDB) { + if( ! m_bHelpDataFileRead ) + readHelpDataFile(); + m_bHelpDataFileRead = true; + + return m_aPath; + } + else + return get_id(); +} + + +OUString URLParameter::get_the_title() +{ + if(m_bUseDB) { + if( ! m_bHelpDataFileRead ) + readHelpDataFile(); + m_bHelpDataFileRead = true; + + return m_aTitle; + } + else + return OUString(); +} + + +OUString URLParameter::get_jar() +{ + if(m_bUseDB) { + if( ! m_bHelpDataFileRead ) + readHelpDataFile(); + m_bHelpDataFileRead = true; + + return m_aJar; + } + else + return get_module() + ".jar"; +} + + +void URLParameter::readHelpDataFile() +{ + if( get_id().isEmpty() ) + return; + + OUString aModule = get_module(); + OUString aLanguage = get_language(); + + DataBaseIterator aDbIt( *m_pDatabases, aModule, aLanguage, false ); + bool bSuccess = false; + + const char* pData = nullptr; + + helpdatafileproxy::HDFData aHDFData; + OUString aExtensionPath; + OUString aExtensionRegistryPath; + while( true ) + { + helpdatafileproxy::Hdf* pHdf = aDbIt.nextHdf( &aExtensionPath, &aExtensionRegistryPath ); + if( !pHdf ) + break; + + OString keyStr = OUStringToOString( m_aId,RTL_TEXTENCODING_UTF8 ); + bSuccess = pHdf->getValueForKey( keyStr, aHDFData ); + if( bSuccess ) + { + pData = aHDFData.getData(); + break; + } + } + + if( !bSuccess ) + return; + + DbtToStringConverter converter( pData ); + m_aTitle = converter.getTitle(); + m_pDatabases->replaceName( m_aTitle ); + m_aPath = converter.getFile(); + m_aJar = converter.getDatabase(); + if( !aExtensionPath.isEmpty() ) + { + m_aJar = "?" + aExtensionPath + "?" + m_aJar; + m_aExtensionRegistryPath = aExtensionRegistryPath; + } + m_aTag = converter.getHash(); +} + + +// Class encapsulating the transformation of the XInputStream to XHTML + +namespace { + +class InputStreamTransformer + : public OWeakObject, + public XInputStream, + public XSeekable +{ +public: + + InputStreamTransformer( URLParameter* urlParam, + Databases* pDatatabases, + bool isRoot ); + + virtual Any SAL_CALL queryInterface( const Type& rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + virtual sal_Int32 SAL_CALL readBytes( Sequence< sal_Int8 >& aData,sal_Int32 nBytesToRead ) override; + + virtual sal_Int32 SAL_CALL readSomeBytes( Sequence< sal_Int8 >& aData,sal_Int32 nMaxBytesToRead ) override; + + virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override; + + virtual sal_Int32 SAL_CALL available() override; + + virtual void SAL_CALL closeInput() override; + + virtual void SAL_CALL seek( sal_Int64 location ) override; + + virtual sal_Int64 SAL_CALL getPosition() override; + + virtual sal_Int64 SAL_CALL getLength() override; + + void addToBuffer( const char* buffer,int len ); + + OStringBuffer const & getData() const { return buffer; } + +private: + + std::mutex m_aMutex; + + int pos; + OStringBuffer buffer; +}; + +} + +void URLParameter::open( const Reference< XOutputStream >& xDataSink ) +{ + if( ! xDataSink.is() ) + return; + + // a standard document or else an active help text, plug in the new input stream + rtl::Reference<InputStreamTransformer> p(new InputStreamTransformer( this,m_pDatabases,isRoot() )); + try + { + xDataSink->writeBytes( Sequence< sal_Int8 >( reinterpret_cast<const sal_Int8*>(p->getData().getStr()), p->getData().getLength() ) ); + } + catch( const Exception& ) + { + } + p.clear(); + xDataSink->closeOutput(); +} + + +void URLParameter::open( const Reference< XActiveDataSink >& xDataSink ) +{ + // a standard document or else an active help text, plug in the new input stream + xDataSink->setInputStream( new InputStreamTransformer( this,m_pDatabases,isRoot() ) ); +} + + +void URLParameter::parse() +{ + m_aExpr = m_aURL; + + sal_Int32 lstIdx = m_aExpr.lastIndexOf( '#' ); + if( lstIdx != -1 ) + m_aExpr = m_aExpr.copy( 0,lstIdx ); + + if( ! scheme() || + ! name( module() ) || + ! query() || + m_aLanguage.isEmpty() || + m_aSystem.isEmpty() ) + throw css::ucb::IllegalIdentifierException(); +} + + +bool URLParameter::scheme() +{ + // Correct extension help links as sometimes the + // module is missing resulting in a malformed URL + if( m_aExpr.startsWith("vnd.sun.star.help:///") ) + { + sal_Int32 nLen = m_aExpr.getLength(); + std::u16string_view aLastStr = + m_aExpr.subView(sal::static_int_cast<sal_uInt32>(nLen) - 6); + if( aLastStr == u"DbPAR=" ) + { + m_aExpr = OUString::Concat(m_aExpr.subView( 0, 20 )) + + "shared" + + m_aExpr.subView( 20 ) + + "shared"; + } + } + + for( sal_Int32 nPrefixLen = 20 ; nPrefixLen >= 18 ; --nPrefixLen ) + { + if( m_aExpr.matchAsciiL( "vnd.sun.star.help://", nPrefixLen ) ) + { + m_aExpr = m_aExpr.copy( nPrefixLen ); + return true; + } + } + return false; +} + + +bool URLParameter::module() +{ + sal_Int32 idx = 0,length = m_aExpr.getLength(); + + while( idx < length && rtl::isAsciiAlphanumeric( m_aExpr[idx] ) ) + ++idx; + + if( idx != 0 ) + { + m_aModule = m_aExpr.copy( 0,idx ); + m_aExpr = m_aExpr.copy( idx ); + return true; + } + else + return false; +} + + +bool URLParameter::name( bool modulePresent ) +{ + // if modulepresent, a name may be present, but must not + + sal_Int32 length = m_aExpr.getLength(); + + if( length != 0 && m_aExpr[0] == '/' ) + { + sal_Int32 idx = 1; + while( idx < length && m_aExpr[idx] != '?' ) + ++idx; + + if( idx != 1 && ! modulePresent ) + return false; + else + { + m_aId = m_aExpr.copy( 1,idx-1 ); + m_aExpr = m_aExpr.copy( idx ); + } + } + + return true; +} + + +bool URLParameter::query() +{ + OUString query_; + + if( m_aExpr.isEmpty() ) + return true; + else if( m_aExpr[0] == '?' ) + query_ = o3tl::trim(m_aExpr.subView( 1 )); + else + return false; + + + bool ret = true; + sal_Int32 delimIdx,equalIdx; + OUString parameter,value; + + while( !query_.isEmpty() ) + { + delimIdx = query_.indexOf( '&' ); + equalIdx = query_.indexOf( '=' ); + parameter = o3tl::trim(query_.subView( 0,equalIdx )); + if( delimIdx == -1 ) + { + value = o3tl::trim(query_.subView( equalIdx + 1 )); + query_.clear(); + } + else + { + value = o3tl::trim(query_.subView( equalIdx+1,delimIdx - equalIdx - 1 )); + query_ = o3tl::trim(query_.subView( delimIdx+1 )); + } + + if( parameter == "Language" ) + m_aLanguage = value; + else if( parameter == "Device" ) + ; + else if( parameter == "Program" ) + m_aProgram = value; + else if( parameter == "Eid" ) + m_aEid = value; + else if( parameter == "UseDB" ) + m_bUseDB = value != "no"; + else if( parameter == "DbPAR" ) + m_aDbPar = value; + else if( parameter == "Query" ) + { + if( m_aQuery.isEmpty() ) + m_aQuery = value; + else + m_aQuery += " " + value; + } + else if( parameter == "Scope" ) + m_aScope = value; + else if( parameter == "System" ) + m_aSystem = value; + else if( parameter == "HelpPrefix" ) + m_aPrefix = rtl::Uri::decode( + value, + rtl_UriDecodeWithCharset, + RTL_TEXTENCODING_UTF8 ); + else if( parameter == "HitCount" ) + m_nHitCount = value.toInt32(); + else if( parameter == "Active" ) + m_aActive = value; + else if( parameter == "Version" ) + ; // ignored (but accepted) in the built-in help, useful only for the online help + else + ret = false; + } + + return ret; +} + +namespace { + +struct UserData { + + UserData( URLParameter* pInitial, + Databases* pDatabases ) + : m_pDatabases( pDatabases ), + m_pInitial( pInitial ) + { + } + + Databases* m_pDatabases; + URLParameter* m_pInitial; +}; + +} + +static UserData *ugblData = nullptr; + +extern "C" { + +static int +fileMatch(const char * URI) { + if ((URI != nullptr) && !strncmp(URI, "file:/", 6)) + return 1; + return 0; +} + +static int +zipMatch(const char * URI) { + if ((URI != nullptr) && !strncmp(URI, "vnd.sun.star.zip:/", 18)) + return 1; + return 0; +} + +static int +helpMatch(const char * URI) { + if ((URI != nullptr) && !strncmp(URI, "vnd.sun.star.help:/", 19)) + return 1; + return 0; +} + +static void * +fileOpen(const char *URI) { + osl::File *pRet = new osl::File(OUString(URI, strlen(URI), RTL_TEXTENCODING_UTF8)); + (void)pRet->open(osl_File_OpenFlag_Read); + return pRet; +} + +static void * +zipOpen(SAL_UNUSED_PARAMETER const char *) { + OUString language,jar,path; + + if( !ugblData->m_pInitial->get_eid().isEmpty() ) + return new Reference<XHierarchicalNameAccess>; + else + { + jar = ugblData->m_pInitial->get_jar(); + language = ugblData->m_pInitial->get_language(); + path = ugblData->m_pInitial->get_path(); + } + + Reference< XHierarchicalNameAccess > xNA = + ugblData->m_pDatabases->findJarFileForPath( jar, language, path ); + + Reference< XInputStream > xInputStream; + + if( xNA.is() ) + { + try + { + Any aEntry = xNA->getByHierarchicalName( path ); + Reference< XActiveDataSink > xSink; + if( ( aEntry >>= xSink ) && xSink.is() ) + xInputStream = xSink->getInputStream(); + } + catch ( NoSuchElementException & ) + { + } + } + + if( xInputStream.is() ) + { + return new Reference<XInputStream>(xInputStream); + } + return nullptr; +} + +static void * +helpOpen(const char * URI) { + OUString language,jar,path; + + URLParameter urlpar( OUString::createFromAscii( URI ), + ugblData->m_pDatabases ); + + jar = urlpar.get_jar(); + language = urlpar.get_language(); + path = urlpar.get_path(); + + Reference< XHierarchicalNameAccess > xNA = + ugblData->m_pDatabases->findJarFileForPath( jar, language, path ); + + Reference< XInputStream > xInputStream; + + if( xNA.is() ) + { + try + { + Any aEntry = xNA->getByHierarchicalName( path ); + Reference< XActiveDataSink > xSink; + if( ( aEntry >>= xSink ) && xSink.is() ) + xInputStream = xSink->getInputStream(); + } + catch ( NoSuchElementException & ) + { + } + } + + if( xInputStream.is() ) + return new Reference<XInputStream>(xInputStream); + return nullptr; +} + +static int +helpRead(void * context, char * buffer, int len) { + Reference< XInputStream > *pRef = static_cast<Reference< XInputStream >*>(context); + + Sequence< sal_Int8 > aSeq; + len = (*pRef)->readBytes( aSeq,len); + memcpy(buffer, aSeq.getConstArray(), len); + + return len; +} + +static int +zipRead(void * context, char * buffer, int len) { + return helpRead(context, buffer, len); +} + +static int +fileRead(void * context, char * buffer, int len) { + int nRead = 0; + osl::File *pFile = static_cast<osl::File*>(context); + if (pFile) + { + sal_uInt64 uRead = 0; + if (osl::FileBase::E_None == pFile->read(buffer, len, uRead)) + nRead = static_cast<int>(uRead); + } + return nRead; +} + +static int +uriClose(void * context) { + Reference< XInputStream > *pRef = static_cast<Reference< XInputStream >*>(context); + delete pRef; + return 0; +} + +static int +fileClose(void * context) { + osl::File *pFile = static_cast<osl::File*>(context); + if (pFile) + { + pFile->close(); + delete pFile; + } + return 0; +} + +} // extern "C" + +InputStreamTransformer::InputStreamTransformer( URLParameter* urlParam, + Databases* pDatabases, + bool isRoot ) + : pos( 0 ) +{ + if( isRoot ) + { + buffer.setLength(0); + pDatabases->cascadingStylesheet( urlParam->get_language(), + buffer ); + } + else if( urlParam->isActive() ) + { + buffer.setLength(0); + pDatabases->setActiveText( urlParam->get_module(), + urlParam->get_language(), + urlParam->get_id(), + buffer ); + } + else + { + UserData userData( urlParam,pDatabases ); + + // Uses the implementation detail, that OString::getStr returns a zero terminated character-array + + const char* parameter[47]; + OString parString[46]; + int last = 0; + + parString[last++] = "Program"_ostr; + OString aPureProgramm( urlParam->getByName( "Program" ) ); + parString[last++] = "'" + aPureProgramm + "'"; + parString[last++] = "Database"_ostr; + parString[last++] = "'" + urlParam->getByName( "DatabasePar" ) + "'"; + parString[last++] = "Id"_ostr; + parString[last++] = "'" + urlParam->getByName( "Id" ) + "'"; + parString[last++] = "Path"_ostr; + OString aPath( urlParam->getByName( "Path" ) ); + parString[last++] = "'" + aPath + "'"; + + OString aPureLanguage = urlParam->getByName( "Language" ); + parString[last++] = "Language"_ostr; + parString[last++] = "'" + aPureLanguage + "'"; + parString[last++] = "System"_ostr; + parString[last++] = "'" + urlParam->getByName( "System" ) + "'"; + parString[last++] = "productname"_ostr; + parString[last++] = "'" + OUStringToOString( + pDatabases->getProductName(), + RTL_TEXTENCODING_UTF8 ) + "'"; + parString[last++] = "productversion"_ostr; + parString[last++] = "'" + + OUStringToOString( pDatabases->getProductVersion(), + RTL_TEXTENCODING_UTF8 ) + "'"; + + parString[last++] = "imgtheme"_ostr; + parString[last++] = "'" + pDatabases->getImageTheme() + "'"; + parString[last++] = "hp"_ostr; + parString[last++] = "'" + urlParam->getByName( "HelpPrefix" ) + "'"; + + if( !parString[last-1].isEmpty() ) + { + parString[last++] = "sm"_ostr; + parString[last++] = "'vnd.sun.star.help%3A%2F%2F'"_ostr; + parString[last++] = "qm"_ostr; + parString[last++] = "'%3F'"_ostr; + parString[last++] = "es"_ostr; + parString[last++] = "'%3D'"_ostr; + parString[last++] = "am"_ostr; + parString[last++] = "'%26'"_ostr; + parString[last++] = "cl"_ostr; + parString[last++] = "'%3A'"_ostr; + parString[last++] = "sl"_ostr; + parString[last++] = "'%2F'"_ostr; + parString[last++] = "hm"_ostr; + parString[last++] = "'%23'"_ostr; + parString[last++] = "cs"_ostr; + parString[last++] = "'css'"_ostr; + + parString[last++] = "vendorname"_ostr; + parString[last++] = "''"_ostr; + parString[last++] = "vendorversion"_ostr; + parString[last++] = "''"_ostr; + parString[last++] = "vendorshort"_ostr; + parString[last++] = "''"_ostr; + } + + // Do we need to add extension path? + OUString aExtensionPath; + OUString aJar = urlParam->get_jar(); + + bool bAddExtensionPath = false; + OUString aExtensionRegistryPath; + sal_Int32 nQuestionMark1 = aJar.indexOf( '?' ); + sal_Int32 nQuestionMark2 = aJar.lastIndexOf( '?' ); + if( nQuestionMark1 != -1 && nQuestionMark2 != -1 && nQuestionMark1 != nQuestionMark2 ) + { + aExtensionPath = aJar.copy( nQuestionMark1 + 1, nQuestionMark2 - nQuestionMark1 - 1 ); + aExtensionRegistryPath = urlParam->get_ExtensionRegistryPath(); + bAddExtensionPath = true; + } + else + { + // Path not yet specified, search directly + Reference< XHierarchicalNameAccess > xNA = pDatabases->findJarFileForPath + ( aJar, urlParam->get_language(), urlParam->get_path(), &aExtensionPath, &aExtensionRegistryPath ); + if( xNA.is() && !aExtensionPath.isEmpty() ) + bAddExtensionPath = true; + } + + if( bAddExtensionPath ) + { + Reference< XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + + OUString aOUExpandedExtensionPath = Databases::expandURL( aExtensionRegistryPath, xContext ); + OString aExpandedExtensionPath = OUStringToOString( aOUExpandedExtensionPath, osl_getThreadTextEncoding() ); + + parString[last++] = "ExtensionPath"_ostr; + parString[last++] = "'" + aExpandedExtensionPath + "'"; + + // ExtensionId + OString aPureExtensionId; + sal_Int32 iSlash = aPath.indexOf( '/' ); + if( iSlash != -1 ) + aPureExtensionId = aPath.copy( 0, iSlash ); + + parString[last++] = "ExtensionId"_ostr; + parString[last++] = "'" + aPureExtensionId + "'"; + } + + for( int i = 0; i < last; ++i ) + parameter[i] = parString[i].getStr(); + parameter[last] = nullptr; + + OUString xslURL = pDatabases->getInstallPathAsURL(); + + OString xslURLascii = OUStringToOString( + xslURL, + RTL_TEXTENCODING_UTF8) + + "main_transform.xsl"; + + ugblData = &userData; + + xmlInitParser(); + xmlRegisterInputCallbacks(zipMatch, zipOpen, zipRead, uriClose); + xmlRegisterInputCallbacks(helpMatch, helpOpen, helpRead, uriClose); + xmlRegisterInputCallbacks(fileMatch, fileOpen, fileRead, fileClose); + + xsltStylesheetPtr cur = + xsltParseStylesheetFile(reinterpret_cast<const xmlChar *>(xslURLascii.getStr())); + + xmlDocPtr doc = xmlParseFile("vnd.sun.star.zip:/"); + + xmlDocPtr res = nullptr; + xsltTransformContextPtr transformContext = xsltNewTransformContext(cur, doc); + if (transformContext) + { + xsltSecurityPrefsPtr securityPrefs = xsltNewSecurityPrefs(); + if (securityPrefs) + { + xsltSetSecurityPrefs(securityPrefs, XSLT_SECPREF_READ_FILE, xsltSecurityAllow); + if (xsltSetCtxtSecurityPrefs(securityPrefs, transformContext) == 0) + { + res = xsltApplyStylesheetUser(cur, doc, parameter, nullptr, nullptr, transformContext); + if (res) + { + xmlChar *doc_txt_ptr=nullptr; + int doc_txt_len; + xsltSaveResultToString(&doc_txt_ptr, &doc_txt_len, res, cur); + addToBuffer(reinterpret_cast<char*>(doc_txt_ptr), doc_txt_len); + xmlFree(doc_txt_ptr); + } + } + xsltFreeSecurityPrefs(securityPrefs); + } + xsltFreeTransformContext(transformContext); + } + xmlPopInputCallbacks(); //filePatch + xmlPopInputCallbacks(); //helpPatch + xmlPopInputCallbacks(); //zipMatch + xmlFreeDoc(res); + xmlFreeDoc(doc); + xsltFreeStylesheet(cur); + } +} + + +Any SAL_CALL InputStreamTransformer::queryInterface( const Type& rType ) +{ + Any aRet = ::cppu::queryInterface( rType, + static_cast< XInputStream* >(this), + static_cast< XSeekable* >(this) ); + + return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ); +} + + +void SAL_CALL InputStreamTransformer::acquire() noexcept +{ + OWeakObject::acquire(); +} + + +void SAL_CALL InputStreamTransformer::release() noexcept +{ + OWeakObject::release(); +} + + +sal_Int32 SAL_CALL InputStreamTransformer::readBytes( Sequence< sal_Int8 >& aData,sal_Int32 nBytesToRead ) +{ + std::scoped_lock aGuard( m_aMutex ); + + int curr,available_ = buffer.getLength() - pos; + if( nBytesToRead <= available_ ) + curr = nBytesToRead; + else + curr = available_; + + if( 0 <= curr && aData.getLength() < curr ) + aData.realloc( curr ); + + std::copy_n(buffer.getStr() + pos, curr, aData.getArray()); + pos += curr; + + return std::max(curr, 0); +} + + +sal_Int32 SAL_CALL InputStreamTransformer::readSomeBytes( Sequence< sal_Int8 >& aData,sal_Int32 nMaxBytesToRead ) +{ + return readBytes( aData,nMaxBytesToRead ); +} + + +void SAL_CALL InputStreamTransformer::skipBytes( sal_Int32 nBytesToSkip ) +{ + std::scoped_lock aGuard( m_aMutex ); + while( nBytesToSkip-- ) ++pos; +} + + +sal_Int32 SAL_CALL InputStreamTransformer::available() +{ + std::scoped_lock aGuard( m_aMutex ); + return std::min<sal_Int64>(SAL_MAX_INT32, buffer.getLength() - pos); +} + + +void SAL_CALL InputStreamTransformer::closeInput() +{ +} + + +void SAL_CALL InputStreamTransformer::seek( sal_Int64 location ) +{ + std::scoped_lock aGuard( m_aMutex ); + if( location < 0 ) + throw IllegalArgumentException(); + + pos = sal::static_int_cast<sal_Int32>( location ); + + if( pos > buffer.getLength() ) + pos = buffer.getLength(); +} + + +sal_Int64 SAL_CALL InputStreamTransformer::getPosition() +{ + std::scoped_lock aGuard( m_aMutex ); + return sal_Int64( pos ); +} + + +sal_Int64 SAL_CALL InputStreamTransformer::getLength() +{ + std::scoped_lock aGuard( m_aMutex ); + + return buffer.getLength(); +} + + +void InputStreamTransformer::addToBuffer( const char* buffer_,int len_ ) +{ + std::scoped_lock aGuard( m_aMutex ); + + buffer.append( buffer_, len_ ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlhelp/source/cxxhelp/provider/urlparameter.hxx b/xmlhelp/source/cxxhelp/provider/urlparameter.hxx new file mode 100644 index 0000000000..3c6d02430b --- /dev/null +++ b/xmlhelp/source/cxxhelp/provider/urlparameter.hxx @@ -0,0 +1,223 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <rtl/string.hxx> +#include <com/sun/star/io/XActiveDataSink.hpp> +#include <com/sun/star/io/XOutputStream.hpp> + +namespace chelp { + + + class Databases; + + + class DbtToStringConverter + { + public: + + explicit DbtToStringConverter( const char* ptr ) + : m_ptr( ptr ) + { + } + + OUString getHash() const + { + if( m_ptr ) + { + sal_Int32 sizeOfFile = static_cast<sal_Int32>(m_ptr[0]); + OUString Hash( m_ptr+1,sizeOfFile,RTL_TEXTENCODING_UTF8 ); + sal_Int32 idx; + if( ( idx = Hash.indexOf( u'#' ) ) != -1 ) + return Hash.copy( 1+idx ); + } + return OUString(); + } + + + OUString getFile() const + { + if( ! m_ptr ) + return OUString(); + + sal_Int32 sizeOfFile = static_cast<sal_Int32>(m_ptr[0]); + OUString File( m_ptr+1,sizeOfFile,RTL_TEXTENCODING_UTF8 ); + sal_Int32 idx; + if( ( idx = File.indexOf( u'#' ) ) != -1 ) + return File.copy( 0,idx ); + else + return File; + } + + + OUString getDatabase() const + { + if( ! m_ptr ) + return OUString(); + + sal_Int32 sizeOfDatabase = static_cast<int>(m_ptr[ 1+ static_cast<sal_Int32>(m_ptr[0]) ]); + return OUString( m_ptr + 2 + static_cast<sal_Int32>(m_ptr[0]),sizeOfDatabase,RTL_TEXTENCODING_UTF8 ); + } + + + OUString getTitle() const + { + if( ! m_ptr ) + return OUString(); + + //fdo#82025 - use strlen instead of stored length byte to determine string len + //There is a one byte length field at m_ptr[2 + m_ptr[0] + m_ptr[1 + //+ m_ptr[0]]] but by default char is signed so anything larger + //than 127 defaults to a negative value, casting it would allow up + //to 255 but instead make use of the null termination to avoid + //running into a later problem with strings >= 255 + const char* pTitle = m_ptr + 3 + m_ptr[0] + static_cast<sal_Int32>(m_ptr[ 1+ static_cast<sal_Int32>(m_ptr[0]) ]); + + return OStringToOUString(pTitle, RTL_TEXTENCODING_UTF8); + } + + + private: + + const char* m_ptr; + + }; + + + class URLParameter + { + public: + /// @throws css::ucb::IllegalIdentifierException + URLParameter( const OUString& aURL, + Databases* pDatabases ); + + bool isActive() const { return !m_aActive.isEmpty() && m_aActive == "true"; } + bool isQuery() const { return m_aId.isEmpty() && !m_aQuery.isEmpty(); } + bool isFile() const { return !m_aId.isEmpty(); } + bool isModule() const { return m_aId.isEmpty() && !m_aModule.isEmpty(); } + bool isRoot() const { return m_aModule.isEmpty(); } + bool isErrorDocument(); + + OUString const & get_id(); + + OUString get_tag(); + + // Not called for a directory + + OUString const & get_path(); + + const OUString& get_eid() const { return m_aEid; } + + OUString get_title(); + + OUString get_jar(); + + const OUString& get_ExtensionRegistryPath() const { return m_aExtensionRegistryPath; } + + const OUString& get_module() const { return m_aModule; } + + OUString const & get_dbpar() const + { + if( !m_aDbPar.isEmpty() ) + return m_aDbPar; + else + return m_aModule; + } + + OUString const & get_language() const; + + OUString const & get_program(); + + const OUString& get_query() const { return m_aQuery; } + + const OUString& get_scope() const { return m_aScope; } + + const OUString& get_system() const { return m_aSystem; } + + sal_Int32 get_hitCount() const { return m_nHitCount; } + + OString getByName( const char* par ); + + void open( const css::uno::Reference< css::io::XActiveDataSink >& xDataSink ); + + void open( const css::uno::Reference< css::io::XOutputStream >& xDataSink ); + + private: + + Databases* m_pDatabases; + + bool m_bHelpDataFileRead; + bool m_bUseDB; + + OUString m_aURL; + + OUString m_aTag; + OUString m_aId; + OUString m_aPath; + OUString m_aModule; + OUString m_aTitle; + OUString m_aJar; + OUString m_aExtensionRegistryPath; + OUString m_aEid; + OUString m_aDbPar; + + OUString m_aLanguage; + + OUString m_aPrefix; + OUString m_aProgram; + OUString m_aSystem; + OUString m_aActive; + + OUString m_aQuery; + OUString m_aScope; + + OUString m_aExpr; + + sal_Int32 m_nHitCount; // The default maximum hitcount + + + // private methods + + void init(); + + OUString get_the_tag(); + + OUString get_the_title(); + + void readHelpDataFile(); + + /// @throws css::ucb::IllegalIdentifierException + void parse(); + + bool scheme(); + + bool module(); + + bool name( bool modulePresent ); + + bool query(); + + }; // end class URLParameter + + +} // end namespace chelp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |