diff options
Diffstat (limited to 'comphelper/source/misc')
64 files changed, 16950 insertions, 0 deletions
diff --git a/comphelper/source/misc/AccessibleImplementationHelper.cxx b/comphelper/source/misc/AccessibleImplementationHelper.cxx new file mode 100644 index 000000000..a45b6e5f3 --- /dev/null +++ b/comphelper/source/misc/AccessibleImplementationHelper.cxx @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 <comphelper/AccessibleImplementationHelper.hxx> + +#include <com/sun/star/awt/KeyStroke.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <rtl/ustrbuf.hxx> + +using namespace css::awt; +using namespace css::uno; + +namespace comphelper +{ +OUString GetkeyBindingStrByXkeyBinding(const Sequence<KeyStroke>& keySet) +{ + OUStringBuffer buf; + for (const auto& k : keySet) + { + buf.append('\n'); + buf.append(k.KeyChar); + } + return buf.makeStringAndClear(); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/comphelper/source/misc/DirectoryHelper.cxx b/comphelper/source/misc/DirectoryHelper.cxx new file mode 100644 index 000000000..badfe9b62 --- /dev/null +++ b/comphelper/source/misc/DirectoryHelper.cxx @@ -0,0 +1,213 @@ +/* -*- 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/. + */ + +#include <comphelper/DirectoryHelper.hxx> + +#include <sal/config.h> +#include <osl/file.hxx> +#include <rtl/uri.hxx> + +#include <memory> + +namespace comphelper +{ +typedef std::shared_ptr<osl::File> FileSharedPtr; + +std::u16string_view DirectoryHelper::splitAtLastToken(std::u16string_view rSrc, sal_Unicode aToken, + OUString& rRight) +{ + const size_t nIndex(rSrc.rfind(aToken)); + std::u16string_view aRetval; + + if (std::u16string_view::npos == nIndex) + { + aRetval = rSrc; + rRight.clear(); + } + else if (nIndex > 0) + { + aRetval = rSrc.substr(0, nIndex); + + if (rSrc.size() > nIndex + 1) + { + rRight = rSrc.substr(nIndex + 1); + } + } + + return aRetval; +} + +bool DirectoryHelper::fileExists(const OUString& rBaseURL) +{ + if (!rBaseURL.isEmpty()) + { + FileSharedPtr aBaseFile = std::make_shared<osl::File>(rBaseURL); + + return (osl::File::E_None == aBaseFile->open(osl_File_OpenFlag_Read)); + } + + return false; +} + +bool DirectoryHelper::dirExists(const OUString& rDirURL) +{ + if (!rDirURL.isEmpty()) + { + osl::Directory aDirectory(rDirURL); + + return (osl::FileBase::E_None == aDirectory.open()); + } + + return false; +} + +void DirectoryHelper::scanDirsAndFiles(const OUString& rDirURL, std::set<OUString>& rDirs, + std::set<std::pair<OUString, OUString>>& rFiles) +{ + if (rDirURL.isEmpty()) + return; + + osl::Directory aDirectory(rDirURL); + + if (osl::FileBase::E_None != aDirectory.open()) + return; + + auto lcl_encodeUriSegment = [](OUString const& rPath) { + return rtl::Uri::encode(rPath, rtl_UriCharClassUricNoSlash, rtl_UriEncodeIgnoreEscapes, + RTL_TEXTENCODING_UTF8); + }; + + osl::DirectoryItem aDirectoryItem; + + while (osl::FileBase::E_None == aDirectory.getNextItem(aDirectoryItem)) + { + osl::FileStatus aFileStatus(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName); + + if (osl::FileBase::E_None == aDirectoryItem.getFileStatus(aFileStatus)) + { + if (aFileStatus.isDirectory()) + { + const OUString aFileName(aFileStatus.getFileName()); + + if (!aFileName.isEmpty()) + { + rDirs.insert(lcl_encodeUriSegment(aFileName)); + } + } + else if (aFileStatus.isRegular()) + { + OUString aFileName(aFileStatus.getFileName()); + OUString aExtension; + aFileName = splitAtLastToken(aFileName, '.', aExtension); + + if (!aFileName.isEmpty()) + { + rFiles.insert(std::pair<OUString, OUString>(lcl_encodeUriSegment(aFileName), + lcl_encodeUriSegment(aExtension))); + } + } + } + } +} + +bool DirectoryHelper::deleteDirRecursively(const OUString& rDirURL) +{ + std::set<OUString> aDirs; + std::set<std::pair<OUString, OUString>> aFiles; + bool bError(false); + + scanDirsAndFiles(rDirURL, aDirs, aFiles); + + for (const auto& dir : aDirs) + { + const OUString aNewDirURL(rDirURL + "/" + dir); + + bError |= deleteDirRecursively(aNewDirURL); + } + + for (const auto& file : aFiles) + { + OUString aNewFileURL(rDirURL + "/" + file.first); + + if (!file.second.isEmpty()) + { + aNewFileURL += "." + file.second; + } + bError |= (osl::FileBase::E_None != osl::File::remove(aNewFileURL)); + } + + bError |= (osl::FileBase::E_None != osl::Directory::remove(rDirURL)); + + return bError; +} + +// both exist, move content +bool DirectoryHelper::moveDirContent(const OUString& rSourceDirURL, + std::u16string_view rTargetDirURL, + const std::set<OUString>& rExcludeList) +{ + std::set<OUString> aDirs; + std::set<std::pair<OUString, OUString>> aFiles; + bool bError(false); + + scanDirsAndFiles(rSourceDirURL, aDirs, aFiles); + + for (const auto& dir : aDirs) + { + const bool bExcluded(!rExcludeList.empty() && rExcludeList.find(dir) != rExcludeList.end()); + + if (!bExcluded) + { + const OUString aNewSourceDirURL(rSourceDirURL + "/" + dir); + + if (dirExists(aNewSourceDirURL)) + { + const OUString aNewTargetDirURL(OUString::Concat(rTargetDirURL) + "/" + dir); + + if (dirExists(aNewTargetDirURL)) + { + deleteDirRecursively(aNewTargetDirURL); + } + + bError |= (osl::FileBase::E_None + != osl::File::move(aNewSourceDirURL, aNewTargetDirURL)); + } + } + } + + for (const auto& file : aFiles) + { + OUString aSourceFileURL(rSourceDirURL + "/" + file.first); + + if (!file.second.isEmpty()) + { + aSourceFileURL += "." + file.second; + } + + if (fileExists(aSourceFileURL)) + { + OUString aTargetFileURL(OUString::Concat(rTargetDirURL) + "/" + file.first); + + if (!file.second.isEmpty()) + { + aTargetFileURL += "." + file.second; + } + + if (fileExists(aTargetFileURL)) + { + osl::File::remove(aTargetFileURL); + } + + bError |= (osl::FileBase::E_None != osl::File::move(aSourceFileURL, aTargetFileURL)); + } + } + + return bError; +} +} diff --git a/comphelper/source/misc/SelectionMultiplex.cxx b/comphelper/source/misc/SelectionMultiplex.cxx new file mode 100644 index 000000000..438eaa80d --- /dev/null +++ b/comphelper/source/misc/SelectionMultiplex.cxx @@ -0,0 +1,111 @@ +/* -*- 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 <comphelper/SelectionMultiplex.hxx> +#include <com/sun/star/view/XSelectionSupplier.hpp> + +namespace comphelper +{ + + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::view; + +OSelectionChangeListener::~OSelectionChangeListener() +{ +} + + +void OSelectionChangeListener::_disposing(const EventObject&) +{ + // nothing to do here +} + + +OSelectionChangeMultiplexer::OSelectionChangeMultiplexer(OSelectionChangeListener* _pListener, const Reference< XSelectionSupplier>& _rxSet) + :m_xSet(_rxSet) + ,m_pListener(_pListener) + ,m_nLockCount(0) +{ + osl_atomic_increment(&m_refCount); + { + Reference< XSelectionChangeListener> xPreventDelete(this); + m_xSet->addSelectionChangeListener(xPreventDelete); + } + osl_atomic_decrement(&m_refCount); +} + + +OSelectionChangeMultiplexer::~OSelectionChangeMultiplexer() +{ +} + + +void OSelectionChangeMultiplexer::lock() +{ + ++m_nLockCount; +} + + +void OSelectionChangeMultiplexer::unlock() +{ + --m_nLockCount; +} + + +// XEventListener + +void SAL_CALL OSelectionChangeMultiplexer::disposing( const EventObject& _rSource) +{ + if (m_pListener) + { + // tell the listener + if (!locked()) + m_pListener->_disposing(_rSource); + } + + m_pListener = nullptr; + + m_xSet = nullptr; +} + +// XSelectionChangeListener + +void SAL_CALL OSelectionChangeMultiplexer::selectionChanged( const EventObject& _rEvent ) +{ + if (m_pListener && !locked()) + m_pListener->_selectionChanged(_rEvent); +} + +void OSelectionChangeMultiplexer::dispose() +{ + osl_atomic_increment(&m_refCount); + { + Reference< XSelectionChangeListener> xPreventDelete(this); + if(m_xSet.is()) + m_xSet->removeSelectionChangeListener(xPreventDelete); + } + osl_atomic_decrement(&m_refCount); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/accessiblecomponenthelper.cxx b/comphelper/source/misc/accessiblecomponenthelper.cxx new file mode 100644 index 000000000..2962bbac9 --- /dev/null +++ b/comphelper/source/misc/accessiblecomponenthelper.cxx @@ -0,0 +1,177 @@ +/* -*- 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 <comphelper/accessiblecomponenthelper.hxx> + + +namespace comphelper +{ + + + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::accessibility; + + OCommonAccessibleComponent::OCommonAccessibleComponent( ) + { + } + + + OCommonAccessibleComponent::~OCommonAccessibleComponent( ) + { + } + + + bool OCommonAccessibleComponent::containsPoint( const awt::Point& _rPoint ) + { + OExternalLockGuard aGuard( this ); + awt::Rectangle aBounds( implGetBounds() ); + return ( _rPoint.X >= 0 ) + && ( _rPoint.Y >= 0 ) + && ( _rPoint.X < aBounds.Width ) + && ( _rPoint.Y < aBounds.Height ); + } + + + awt::Point OCommonAccessibleComponent::getLocation( ) + { + OExternalLockGuard aGuard( this ); + awt::Rectangle aBounds( implGetBounds() ); + return awt::Point( aBounds.X, aBounds.Y ); + } + + + awt::Point OCommonAccessibleComponent::getLocationOnScreen( ) + { + OExternalLockGuard aGuard( this ); + + awt::Point aScreenLoc( 0, 0 ); + + Reference< XAccessibleComponent > xParentComponent( implGetParentContext(), UNO_QUERY ); + OSL_ENSURE( xParentComponent.is(), "OCommonAccessibleComponent::getLocationOnScreen: no parent component!" ); + if ( xParentComponent.is() ) + { + awt::Point aParentScreenLoc( xParentComponent->getLocationOnScreen() ); + awt::Point aOwnRelativeLoc( getLocation() ); + aScreenLoc.X = aParentScreenLoc.X + aOwnRelativeLoc.X; + aScreenLoc.Y = aParentScreenLoc.Y + aOwnRelativeLoc.Y; + } + + return aScreenLoc; + } + + + awt::Size OCommonAccessibleComponent::getSize( ) + { + OExternalLockGuard aGuard( this ); + awt::Rectangle aBounds( implGetBounds() ); + return awt::Size( aBounds.Width, aBounds.Height ); + } + + + awt::Rectangle OCommonAccessibleComponent::getBounds( ) + { + OExternalLockGuard aGuard( this ); + return implGetBounds(); + } + + OAccessibleComponentHelper::OAccessibleComponentHelper( ) + { + } + + + IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleComponentHelper, OCommonAccessibleComponent, OAccessibleComponentHelper_Base ) + IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleComponentHelper, OCommonAccessibleComponent, OAccessibleComponentHelper_Base ) + // (order matters: the first is the class name, the second is the class doing the ref counting) + + + sal_Bool SAL_CALL OAccessibleComponentHelper::containsPoint( const awt::Point& _rPoint ) + { + return OCommonAccessibleComponent::containsPoint( _rPoint ); + } + + + awt::Point SAL_CALL OAccessibleComponentHelper::getLocation( ) + { + return OCommonAccessibleComponent::getLocation( ); + } + + + awt::Point SAL_CALL OAccessibleComponentHelper::getLocationOnScreen( ) + { + return OCommonAccessibleComponent::getLocationOnScreen( ); + } + + + awt::Size SAL_CALL OAccessibleComponentHelper::getSize( ) + { + return OCommonAccessibleComponent::getSize( ); + } + + + awt::Rectangle SAL_CALL OAccessibleComponentHelper::getBounds( ) + { + return OCommonAccessibleComponent::getBounds( ); + } + + OAccessibleExtendedComponentHelper::OAccessibleExtendedComponentHelper( ) + { + } + + + IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleExtendedComponentHelper, OCommonAccessibleComponent, OAccessibleExtendedComponentHelper_Base ) + IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleExtendedComponentHelper, OCommonAccessibleComponent, OAccessibleExtendedComponentHelper_Base ) + // (order matters: the first is the class name, the second is the class doing the ref counting) + + + sal_Bool SAL_CALL OAccessibleExtendedComponentHelper::containsPoint( const awt::Point& _rPoint ) + { + return OCommonAccessibleComponent::containsPoint( _rPoint ); + } + + + awt::Point SAL_CALL OAccessibleExtendedComponentHelper::getLocation( ) + { + return OCommonAccessibleComponent::getLocation( ); + } + + + awt::Point SAL_CALL OAccessibleExtendedComponentHelper::getLocationOnScreen( ) + { + return OCommonAccessibleComponent::getLocationOnScreen( ); + } + + + awt::Size SAL_CALL OAccessibleExtendedComponentHelper::getSize( ) + { + return OCommonAccessibleComponent::getSize( ); + } + + + awt::Rectangle SAL_CALL OAccessibleExtendedComponentHelper::getBounds( ) + { + return OCommonAccessibleComponent::getBounds( ); + } + + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/accessiblecontexthelper.cxx b/comphelper/source/misc/accessiblecontexthelper.cxx new file mode 100644 index 000000000..ad2c63b1a --- /dev/null +++ b/comphelper/source/misc/accessiblecontexthelper.cxx @@ -0,0 +1,248 @@ +/* -*- 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 <comphelper/accessiblecontexthelper.hxx> +#include <osl/diagnose.h> +#include <cppuhelper/weakref.hxx> +#include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp> +#include <comphelper/accessibleeventnotifier.hxx> +#include <comphelper/solarmutex.hxx> + + +namespace comphelper +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::accessibility; + + OAccessibleContextHelper::OAccessibleContextHelper( ) + :OAccessibleContextHelper_Base( GetMutex() ) + ,m_nClientId( 0 ) + { + } + + + OAccessibleContextHelper::~OAccessibleContextHelper( ) + { + // this ensures that the lock, which may be already destroyed as part of the derivee, + // is not used anymore + + ensureDisposed(); + } + + + void SAL_CALL OAccessibleContextHelper::disposing() + { + // rhbz#1001768: de facto this class is locked by SolarMutex; + // do not lock m_Mutex because it may cause deadlock + osl::Guard<SolarMutex> aGuard(SolarMutex::get()); + + if ( m_nClientId ) + { + AccessibleEventNotifier::revokeClientNotifyDisposing( m_nClientId, *this ); + m_nClientId=0; + } + } + + + void SAL_CALL OAccessibleContextHelper::addAccessibleEventListener( const Reference< XAccessibleEventListener >& _rxListener ) + { + osl::Guard<SolarMutex> aGuard(SolarMutex::get()); + // don't use the OContextEntryGuard - it will throw an exception if we're not alive + // anymore, while the most recent specification for XComponent states that we should + // silently ignore the call in such a situation + if ( !isAlive() ) + { + if ( _rxListener.is() ) + _rxListener->disposing( EventObject( *this ) ); + return; + } + + if ( _rxListener.is() ) + { + if ( !m_nClientId ) + m_nClientId = AccessibleEventNotifier::registerClient( ); + + AccessibleEventNotifier::addEventListener( m_nClientId, _rxListener ); + } + } + + + void SAL_CALL OAccessibleContextHelper::removeAccessibleEventListener( const Reference< XAccessibleEventListener >& _rxListener ) + { + osl::Guard<SolarMutex> aGuard(SolarMutex::get()); + // don't use the OContextEntryGuard - it will throw an exception if we're not alive + // anymore, while the most recent specification for XComponent states that we should + // silently ignore the call in such a situation + if ( !isAlive() ) + return; + + if ( !(_rxListener.is() && m_nClientId) ) + return; + + sal_Int32 nListenerCount = AccessibleEventNotifier::removeEventListener( m_nClientId, _rxListener ); + if ( !nListenerCount ) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + AccessibleEventNotifier::revokeClient( m_nClientId ); + m_nClientId = 0; + } + } + + + void OAccessibleContextHelper::NotifyAccessibleEvent( const sal_Int16 _nEventId, + const Any& _rOldValue, const Any& _rNewValue ) + { + if ( !m_nClientId ) + // if we don't have a client id for the notifier, then we don't have listeners, then + // we don't need to notify anything + return; + + // build an event object + AccessibleEventObject aEvent; + aEvent.Source = *this; + aEvent.EventId = _nEventId; + aEvent.OldValue = _rOldValue; + aEvent.NewValue = _rNewValue; + + // let the notifier handle this event + AccessibleEventNotifier::addEvent( m_nClientId, aEvent ); + } + + + bool OAccessibleContextHelper::isAlive() const + { + return !rBHelper.bDisposed && !rBHelper.bInDispose; + } + + + void OAccessibleContextHelper::ensureAlive() const + { + if( !isAlive() ) + throw DisposedException(); + } + + + void OAccessibleContextHelper::ensureDisposed( ) + { + if ( !rBHelper.bDisposed ) + { + OSL_ENSURE( 0 == m_refCount, "OAccessibleContextHelper::ensureDisposed: this method _has_ to be called from without your dtor only!" ); + acquire(); + dispose(); + } + } + + + void OAccessibleContextHelper::lateInit( const Reference< XAccessible >& _rxAccessible ) + { + m_aCreator = _rxAccessible; + } + + + Reference< XAccessible > OAccessibleContextHelper::getAccessibleCreator( ) const + { + return m_aCreator; + } + + + OUString SAL_CALL OAccessibleContextHelper::getAccessibleId( ) + { + return OUString(); + } + + + sal_Int32 SAL_CALL OAccessibleContextHelper::getAccessibleIndexInParent( ) + { + OExternalLockGuard aGuard( this ); + + // -1 for child not found/no parent (according to specification) + sal_Int32 nRet = -1; + + try + { + + Reference< XAccessibleContext > xParentContext( implGetParentContext() ); + + // iterate over parent's children and search for this object + if ( xParentContext.is() ) + { + // our own XAccessible for comparing with the children of our parent + Reference< XAccessible > xCreator( m_aCreator); + + OSL_ENSURE( xCreator.is(), "OAccessibleContextHelper::getAccessibleIndexInParent: invalid creator!" ); + // two ideas why this could be NULL: + // * nobody called our late ctor (init), so we never had a creator at all -> bad + // * the creator is already dead. In this case, we should have been disposed, and + // never survived the above OContextEntryGuard. + // in all other situations the creator should be non-NULL + + if ( xCreator.is() ) + { + sal_Int32 nChildCount = xParentContext->getAccessibleChildCount(); + for ( sal_Int32 nChild = 0; ( nChild < nChildCount ) && ( -1 == nRet ); ++nChild ) + { + Reference< XAccessible > xChild( xParentContext->getAccessibleChild( nChild ) ); + if ( xChild.get() == xCreator.get() ) + nRet = nChild; + } + } + } + } + catch( const Exception& ) + { + OSL_FAIL( "OAccessibleContextHelper::getAccessibleIndexInParent: caught an exception!" ); + } + + return nRet; + } + + + Locale SAL_CALL OAccessibleContextHelper::getLocale( ) + { + // simply ask the parent + Reference< XAccessible > xParent = getAccessibleParent(); + Reference< XAccessibleContext > xParentContext; + if ( xParent.is() ) + xParentContext = xParent->getAccessibleContext(); + + if ( !xParentContext.is() ) + throw IllegalAccessibleComponentStateException( OUString(), *this ); + + return xParentContext->getLocale(); + } + + + Reference< XAccessibleContext > OAccessibleContextHelper::implGetParentContext() + { + Reference< XAccessible > xParent = getAccessibleParent(); + Reference< XAccessibleContext > xParentContext; + if ( xParent.is() ) + xParentContext = xParent->getAccessibleContext(); + return xParentContext; + } + + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/accessibleeventnotifier.cxx b/comphelper/source/misc/accessibleeventnotifier.cxx new file mode 100644 index 000000000..f8bd5a117 --- /dev/null +++ b/comphelper/source/misc/accessibleeventnotifier.cxx @@ -0,0 +1,274 @@ +/* -*- 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 <comphelper/accessibleeventnotifier.hxx> +#include <com/sun/star/accessibility/XAccessibleEventListener.hpp> +#include <comphelper/interfacecontainer4.hxx> + +#include <map> +#include <memory> +#include <limits> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::accessibility; +using namespace ::comphelper; + +namespace { + +typedef std::pair< AccessibleEventNotifier::TClientId, + AccessibleEventObject > ClientEvent; + +typedef ::comphelper::OInterfaceContainerHelper4<XAccessibleEventListener> ListenerContainer; +typedef std::map< AccessibleEventNotifier::TClientId, ListenerContainer* > ClientMap; + +/// key is the end of the interval, value is the start of the interval +typedef std::map<AccessibleEventNotifier::TClientId, + AccessibleEventNotifier::TClientId> IntervalMap; + +std::mutex& GetLocalMutex() +{ + static std::mutex MUTEX; + return MUTEX; +} + +ClientMap gaClients; + +IntervalMap& GetFreeIntervals() +{ + static IntervalMap MAP = + []() + { + IntervalMap map; + map.insert(std::make_pair( + std::numeric_limits<AccessibleEventNotifier::TClientId>::max(), 1)); + return map; + }(); + return MAP; +} + +void releaseId(AccessibleEventNotifier::TClientId const nId) +{ + IntervalMap & rFreeIntervals(GetFreeIntervals()); + IntervalMap::iterator const upper(rFreeIntervals.upper_bound(nId)); + assert(upper != rFreeIntervals.end()); + assert(nId < upper->second); // second is start of the interval! + if (nId + 1 == upper->second) + { + --upper->second; // add nId to existing interval + } + else + { + IntervalMap::iterator const lower(rFreeIntervals.lower_bound(nId)); + if (lower != rFreeIntervals.end() && lower->first == nId - 1) + { + // add nId by replacing lower with new merged entry + rFreeIntervals.insert(std::make_pair(nId, lower->second)); + rFreeIntervals.erase(lower); + } + else // otherwise just add new 1-element interval + { + rFreeIntervals.insert(std::make_pair(nId, nId)); + } + } + // currently it's not checked whether intervals can be merged now + // hopefully that won't be a problem in practice +} + +/// generates a new client id +AccessibleEventNotifier::TClientId generateId() +{ + IntervalMap & rFreeIntervals(GetFreeIntervals()); + assert(!rFreeIntervals.empty()); + IntervalMap::iterator const iter(rFreeIntervals.begin()); + AccessibleEventNotifier::TClientId const nFirst = iter->first; + AccessibleEventNotifier::TClientId const nFreeId = iter->second; + assert(nFreeId <= nFirst); + if (nFreeId != nFirst) + { + ++iter->second; // remove nFreeId from interval + } + else + { + rFreeIntervals.erase(iter); // remove 1-element interval + } + + assert(gaClients.end() == gaClients.find(nFreeId)); + + return nFreeId; +} + +/** looks up a client in our client map, asserts if it cannot find it or + no event thread is present + + @precond + to be called with our mutex locked + + @param nClient + the id of the client to lookup + @param rPos + out-parameter for the position of the client in the client map + + @return + <TRUE/> if and only if the client could be found and + <arg>rPos</arg> has been filled with its position +*/ +bool implLookupClient( + const AccessibleEventNotifier::TClientId nClient, + ClientMap::iterator& rPos ) +{ + // look up this client + ClientMap &rClients = gaClients; + rPos = rClients.find( nClient ); + assert( rClients.end() != rPos && + "AccessibleEventNotifier::implLookupClient: invalid client id " + "(did you register your client?)!" ); + + return ( rClients.end() != rPos ); +} + +} // anonymous namespace + +namespace comphelper { + +AccessibleEventNotifier::TClientId AccessibleEventNotifier::registerClient() +{ + std::scoped_lock aGuard( GetLocalMutex() ); + + // generate a new client id + TClientId nNewClientId = generateId( ); + + // the event listeners for the new client + ListenerContainer * pNewListeners = new ListenerContainer(); + // add the client + gaClients.emplace( nNewClientId, pNewListeners ); + + // outta here + return nNewClientId; +} + +void AccessibleEventNotifier::revokeClient( const TClientId _nClient ) +{ + std::scoped_lock aGuard( GetLocalMutex() ); + + ClientMap::iterator aClientPos; + if ( !implLookupClient( _nClient, aClientPos ) ) + // already asserted in implLookupClient + return; + + // remove it from the clients map + delete aClientPos->second; + gaClients.erase( aClientPos ); + releaseId(_nClient); +} + +void AccessibleEventNotifier::revokeClientNotifyDisposing( + const TClientId _nClient, const Reference< XInterface >& _rxEventSource ) +{ + std::unique_lock aGuard( GetLocalMutex() ); + + ClientMap::iterator aClientPos; + if (!implLookupClient(_nClient, aClientPos)) + // already asserted in implLookupClient + return; + + // notify the listeners + std::unique_ptr<ListenerContainer> pListeners(aClientPos->second); + + // we do not need the entry in the clients map anymore + // (do this before actually notifying, because some client + // implementations have re-entrance problems and call into + // revokeClient while we are notifying from here) + gaClients.erase(aClientPos); + releaseId(_nClient); + + // notify the "disposing" event for this client + EventObject aDisposalEvent; + aDisposalEvent.Source = _rxEventSource; + + // now really do the notification + pListeners->disposeAndClear( aGuard, aDisposalEvent ); +} + +sal_Int32 AccessibleEventNotifier::addEventListener( + const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener ) +{ + std::unique_lock aGuard( GetLocalMutex() ); + + ClientMap::iterator aClientPos; + if ( !implLookupClient( _nClient, aClientPos ) ) + // already asserted in implLookupClient + return 0; + + if ( _rxListener.is() ) + aClientPos->second->addInterface( aGuard, _rxListener ); + + return aClientPos->second->getLength(aGuard); +} + +sal_Int32 AccessibleEventNotifier::removeEventListener( + const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener ) +{ + std::unique_lock aGuard( GetLocalMutex() ); + + ClientMap::iterator aClientPos; + if ( !implLookupClient( _nClient, aClientPos ) ) + // already asserted in implLookupClient + return 0; + + if ( _rxListener.is() ) + aClientPos->second->removeInterface( aGuard, _rxListener ); + + return aClientPos->second->getLength(aGuard); +} + +void AccessibleEventNotifier::addEvent( const TClientId _nClient, const AccessibleEventObject& _rEvent ) +{ + std::vector< Reference< XAccessibleEventListener > > aListeners; + + { + std::unique_lock aGuard( GetLocalMutex() ); + + ClientMap::iterator aClientPos; + if ( !implLookupClient( _nClient, aClientPos ) ) + // already asserted in implLookupClient + return; + + // since we're synchronous, again, we want to notify immediately + aListeners = aClientPos->second->getElements(aGuard); + } + + // default handling: loop through all listeners, and notify them + for ( const auto& rListener : aListeners ) + { + try + { + rListener->notifyEvent( _rEvent ); + } + catch( const Exception& ) + { + // no assertion, because a broken access remote bridge or something like this + // can cause this exception + } + } +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/accessiblekeybindinghelper.cxx b/comphelper/source/misc/accessiblekeybindinghelper.cxx new file mode 100644 index 000000000..d1db69b98 --- /dev/null +++ b/comphelper/source/misc/accessiblekeybindinghelper.cxx @@ -0,0 +1,97 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <comphelper/accessiblekeybindinghelper.hxx> +#include <o3tl/safeint.hxx> + + +namespace comphelper +{ + + + using namespace ::com::sun::star; // MT 04/2003: was ::drafts::com::sun::star - otherwise too many changes + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::accessibility; + + + // OAccessibleKeyBindingHelper + + + OAccessibleKeyBindingHelper::OAccessibleKeyBindingHelper() + { + } + + + OAccessibleKeyBindingHelper::OAccessibleKeyBindingHelper( const OAccessibleKeyBindingHelper& rHelper ) + : cppu::WeakImplHelper<XAccessibleKeyBinding>( rHelper ) + , m_aKeyBindings( rHelper.m_aKeyBindings ) + { + } + + + OAccessibleKeyBindingHelper::~OAccessibleKeyBindingHelper() + { + } + + + void OAccessibleKeyBindingHelper::AddKeyBinding( const Sequence< awt::KeyStroke >& rKeyBinding ) + { + std::scoped_lock aGuard( m_aMutex ); + + m_aKeyBindings.push_back( rKeyBinding ); + } + + + void OAccessibleKeyBindingHelper::AddKeyBinding( const awt::KeyStroke& rKeyStroke ) + { + std::scoped_lock aGuard( m_aMutex ); + m_aKeyBindings.push_back( { rKeyStroke } ); + } + + + // XAccessibleKeyBinding + + + sal_Int32 OAccessibleKeyBindingHelper::getAccessibleKeyBindingCount() + { + std::scoped_lock aGuard( m_aMutex ); + + return m_aKeyBindings.size(); + } + + + Sequence< awt::KeyStroke > OAccessibleKeyBindingHelper::getAccessibleKeyBinding( sal_Int32 nIndex ) + { + std::scoped_lock aGuard( m_aMutex ); + + if ( nIndex < 0 || o3tl::make_unsigned(nIndex) >= m_aKeyBindings.size() ) + throw IndexOutOfBoundsException(); + + return m_aKeyBindings[nIndex]; + } + + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/accessibleselectionhelper.cxx b/comphelper/source/misc/accessibleselectionhelper.cxx new file mode 100644 index 000000000..967c1b079 --- /dev/null +++ b/comphelper/source/misc/accessibleselectionhelper.cxx @@ -0,0 +1,172 @@ +/* -*- 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 <comphelper/accessibleselectionhelper.hxx> + + +namespace comphelper +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::accessibility; + + OCommonAccessibleSelection::OCommonAccessibleSelection( ) + { + } + + OCommonAccessibleSelection::~OCommonAccessibleSelection() {} + + + void OCommonAccessibleSelection::selectAccessibleChild( sal_Int32 nChildIndex ) + { + implSelect( nChildIndex, true ); + } + + + bool OCommonAccessibleSelection::isAccessibleChildSelected( sal_Int32 nChildIndex ) + { + return implIsSelected( nChildIndex ); + } + + + void OCommonAccessibleSelection::clearAccessibleSelection( ) + { + implSelect( ACCESSIBLE_SELECTION_CHILD_ALL, false ); + } + + + void OCommonAccessibleSelection::selectAllAccessibleChildren( ) + { + implSelect( ACCESSIBLE_SELECTION_CHILD_ALL, true ); + } + + + sal_Int32 OCommonAccessibleSelection::getSelectedAccessibleChildCount( ) + { + sal_Int32 nRet = 0; + Reference< XAccessibleContext > xParentContext( implGetAccessibleContext() ); + + OSL_ENSURE( xParentContext.is(), "OCommonAccessibleSelection::getSelectedAccessibleChildCount: no parent context!" ); + + if( xParentContext.is() ) + { + for( sal_Int32 i = 0, nChildCount = xParentContext->getAccessibleChildCount(); i < nChildCount; i++ ) + if( implIsSelected( i ) ) + ++nRet; + } + + return nRet; + } + + + Reference< XAccessible > OCommonAccessibleSelection::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + Reference< XAccessible > xRet; + Reference< XAccessibleContext > xParentContext( implGetAccessibleContext() ); + + OSL_ENSURE( xParentContext.is(), "OCommonAccessibleSelection::getSelectedAccessibleChildCount: no parent context!" ); + + if( xParentContext.is() ) + { + for( sal_Int32 i = 0, nChildCount = xParentContext->getAccessibleChildCount(), nPos = 0; ( i < nChildCount ) && !xRet.is(); i++ ) + if( implIsSelected( i ) && ( nPos++ == nSelectedChildIndex ) ) + xRet = xParentContext->getAccessibleChild( i ); + } + + return xRet; + } + + + void OCommonAccessibleSelection::deselectAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + implSelect( nSelectedChildIndex, false ); + } + + OAccessibleSelectionHelper::OAccessibleSelectionHelper() + { + } + + + IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleSelectionHelper, OAccessibleComponentHelper, OAccessibleSelectionHelper_Base ) + IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleSelectionHelper, OAccessibleComponentHelper, OAccessibleSelectionHelper_Base ) + // (order matters: the first is the class name, the second is the class doing the ref counting) + + + Reference< XAccessibleContext > OAccessibleSelectionHelper::implGetAccessibleContext() + { + return this; + } + + + void SAL_CALL OAccessibleSelectionHelper::selectAccessibleChild( sal_Int32 nChildIndex ) + { + OExternalLockGuard aGuard( this ); + OCommonAccessibleSelection::selectAccessibleChild( nChildIndex ); + } + + + sal_Bool SAL_CALL OAccessibleSelectionHelper::isAccessibleChildSelected( sal_Int32 nChildIndex ) + { + OExternalLockGuard aGuard( this ); + return OCommonAccessibleSelection::isAccessibleChildSelected( nChildIndex ); + } + + + void SAL_CALL OAccessibleSelectionHelper::clearAccessibleSelection( ) + { + OExternalLockGuard aGuard( this ); + OCommonAccessibleSelection::clearAccessibleSelection(); + } + + + void SAL_CALL OAccessibleSelectionHelper::selectAllAccessibleChildren( ) + { + OExternalLockGuard aGuard( this ); + OCommonAccessibleSelection::selectAllAccessibleChildren(); + } + + + sal_Int32 SAL_CALL OAccessibleSelectionHelper::getSelectedAccessibleChildCount( ) + { + OExternalLockGuard aGuard( this ); + return OCommonAccessibleSelection::getSelectedAccessibleChildCount(); + } + + + Reference< XAccessible > SAL_CALL OAccessibleSelectionHelper::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + OExternalLockGuard aGuard( this ); + return OCommonAccessibleSelection::getSelectedAccessibleChild( nSelectedChildIndex ); + } + + + void SAL_CALL OAccessibleSelectionHelper::deselectAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + OExternalLockGuard aGuard( this ); + OCommonAccessibleSelection::deselectAccessibleChild( nSelectedChildIndex ); + } + + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/accessibletexthelper.cxx b/comphelper/source/misc/accessibletexthelper.cxx new file mode 100644 index 000000000..27747b3bc --- /dev/null +++ b/comphelper/source/misc/accessibletexthelper.cxx @@ -0,0 +1,800 @@ +/* -*- 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 <comphelper/accessibletexthelper.hxx> +#include <com/sun/star/accessibility/AccessibleTextType.hpp> +#include <com/sun/star/i18n/BreakIterator.hpp> +#include <com/sun/star/i18n/CharacterIteratorMode.hpp> +#include <com/sun/star/i18n/CharacterClassification.hpp> +#include <com/sun/star/i18n/WordType.hpp> +#include <com/sun/star/i18n/KCharacterType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/accessibility/TextSegment.hpp> + +#include <algorithm> + + +namespace comphelper +{ + + + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::accessibility; + + + // OCommonAccessibleText + + + OCommonAccessibleText::OCommonAccessibleText() + { + } + + + OCommonAccessibleText::~OCommonAccessibleText() + { + } + + + Reference < i18n::XBreakIterator > const & OCommonAccessibleText::implGetBreakIterator() + { + if ( !m_xBreakIter.is() ) + { + Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + m_xBreakIter = i18n::BreakIterator::create(xContext); + } + + return m_xBreakIter; + } + + + Reference < i18n::XCharacterClassification > const & OCommonAccessibleText::implGetCharacterClassification() + { + if ( !m_xCharClass.is() ) + { + m_xCharClass = i18n::CharacterClassification::create( ::comphelper::getProcessComponentContext() ); + } + + return m_xCharClass; + } + + + bool OCommonAccessibleText::implIsValidBoundary( i18n::Boundary const & rBoundary, sal_Int32 nLength ) + { + return ( rBoundary.startPos >= 0 ) && ( rBoundary.startPos < nLength ) && ( rBoundary.endPos >= 0 ) && ( rBoundary.endPos <= nLength ); + } + + + bool OCommonAccessibleText::implIsValidIndex( sal_Int32 nIndex, sal_Int32 nLength ) + { + return ( nIndex >= 0 ) && ( nIndex < nLength ); + } + + + bool OCommonAccessibleText::implIsValidRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex, sal_Int32 nLength ) + { + return ( nStartIndex >= 0 ) && ( nStartIndex <= nLength ) && ( nEndIndex >= 0 ) && ( nEndIndex <= nLength ); + } + + + void OCommonAccessibleText::implGetGlyphBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex ) + { + if ( implIsValidIndex( nIndex, rText.getLength() ) ) + { + Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator(); + if ( xBreakIter.is() ) + { + sal_Int32 nCount = 1; + sal_Int32 nDone; + sal_Int32 nStartIndex = xBreakIter->previousCharacters( rText, nIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone ); + if ( nDone != 0 ) + nStartIndex = xBreakIter->nextCharacters( rText, nStartIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone ); + sal_Int32 nEndIndex = xBreakIter->nextCharacters( rText, nStartIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone ); + if ( nDone != 0 ) + { + rBoundary.startPos = nStartIndex; + rBoundary.endPos = nEndIndex; + } + } + } + else + { + rBoundary.startPos = nIndex; + rBoundary.endPos = nIndex; + } + } + + + bool OCommonAccessibleText::implGetWordBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex ) + { + bool bWord = false; + + if ( implIsValidIndex( nIndex, rText.getLength() ) ) + { + Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator(); + if ( xBreakIter.is() ) + { + rBoundary = xBreakIter->getWordBoundary( rText, nIndex, implGetLocale(), i18n::WordType::ANY_WORD, true ); + + // it's a word, if the first character is an alpha-numeric character + Reference< i18n::XCharacterClassification > xCharClass = implGetCharacterClassification(); + if ( xCharClass.is() ) + { + sal_Int32 nType = xCharClass->getCharacterType( rText, rBoundary.startPos, implGetLocale() ); + if ( ( nType & ( i18n::KCharacterType::LETTER | i18n::KCharacterType::DIGIT ) ) != 0 ) + bWord = true; + } + } + } + else + { + rBoundary.startPos = nIndex; + rBoundary.endPos = nIndex; + } + + return bWord; + } + + + void OCommonAccessibleText::implGetSentenceBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex ) + { + if ( implIsValidIndex( nIndex, rText.getLength() ) ) + { + Locale aLocale = implGetLocale(); + Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator(); + if ( xBreakIter.is() ) + { + rBoundary.endPos = xBreakIter->endOfSentence( rText, nIndex, aLocale ); + rBoundary.startPos = xBreakIter->beginOfSentence( rText, rBoundary.endPos, aLocale ); + } + } + else + { + rBoundary.startPos = nIndex; + rBoundary.endPos = nIndex; + } + } + + + void OCommonAccessibleText::implGetParagraphBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex ) + { + if ( implIsValidIndex( nIndex, rText.getLength() ) ) + { + rBoundary.startPos = 0; + rBoundary.endPos = rText.getLength(); + + sal_Int32 nFound = rText.lastIndexOf( '\n', nIndex ); + if ( nFound != -1 ) + rBoundary.startPos = nFound + 1; + + nFound = rText.indexOf( '\n', nIndex ); + if ( nFound != -1 ) + rBoundary.endPos = nFound + 1; + } + else + { + rBoundary.startPos = nIndex; + rBoundary.endPos = nIndex; + } + } + + + void OCommonAccessibleText::implGetLineBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex ) + { + sal_Int32 nLength = rText.getLength(); + + if ( implIsValidIndex( nIndex, nLength ) || nIndex == nLength ) + { + rBoundary.startPos = 0; + rBoundary.endPos = nLength; + } + else + { + rBoundary.startPos = nIndex; + rBoundary.endPos = nIndex; + } + } + + + sal_Unicode OCommonAccessibleText::implGetCharacter( std::u16string_view rText, sal_Int32 nIndex ) + { + if ( !implIsValidIndex( nIndex, rText.size() ) ) + throw IndexOutOfBoundsException(); + + return rText[nIndex]; + } + + OUString OCommonAccessibleText::getSelectedText() + { + OUString sText; + sal_Int32 nStartIndex; + sal_Int32 nEndIndex; + + implGetSelection( nStartIndex, nEndIndex ); + + try + { + sText = implGetTextRange( implGetText(), nStartIndex, nEndIndex ); + } + catch ( IndexOutOfBoundsException& ) + { + } + + return sText; + } + + + sal_Int32 OCommonAccessibleText::getSelectionStart() + { + sal_Int32 nStartIndex; + sal_Int32 nEndIndex; + + implGetSelection( nStartIndex, nEndIndex ); + + return nStartIndex; + } + + + sal_Int32 OCommonAccessibleText::getSelectionEnd() + { + sal_Int32 nStartIndex; + sal_Int32 nEndIndex; + + implGetSelection( nStartIndex, nEndIndex ); + + return nEndIndex; + } + + + OUString OCommonAccessibleText::implGetTextRange( std::u16string_view rText, sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + + if ( !implIsValidRange( nStartIndex, nEndIndex, rText.size() ) ) + throw IndexOutOfBoundsException(); + + sal_Int32 nMinIndex = std::min( nStartIndex, nEndIndex ); + sal_Int32 nMaxIndex = std::max( nStartIndex, nEndIndex ); + + return OUString(rText.substr( nMinIndex, nMaxIndex - nMinIndex )); + } + + TextSegment OCommonAccessibleText::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + OUString sText( implGetText() ); + sal_Int32 nLength = sText.getLength(); + + if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength ) + throw IndexOutOfBoundsException(); + + i18n::Boundary aBoundary; + TextSegment aResult; + aResult.SegmentStart = -1; + aResult.SegmentEnd = -1; + + switch ( aTextType ) + { + case AccessibleTextType::CHARACTER: + { + if ( implIsValidIndex( nIndex, nLength ) ) + { + aResult.SegmentText = sText.copy( nIndex, 1 ); + aResult.SegmentStart = nIndex; + aResult.SegmentEnd = nIndex+1; + } + } + break; + case AccessibleTextType::GLYPH: + { + // get glyph at index + implGetGlyphBoundary( sText, aBoundary, nIndex ); + if ( implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + break; + case AccessibleTextType::WORD: + { + // get word at index + bool bWord = implGetWordBoundary( sText, aBoundary, nIndex ); + if ( bWord && implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + break; + case AccessibleTextType::SENTENCE: + { + // get sentence at index + implGetSentenceBoundary( sText, aBoundary, nIndex ); + if ( implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + break; + case AccessibleTextType::PARAGRAPH: + { + // get paragraph at index + implGetParagraphBoundary( sText, aBoundary, nIndex ); + if ( implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + break; + case AccessibleTextType::LINE: + { + // get line at index + implGetLineBoundary( sText, aBoundary, nIndex ); + if ( implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + break; + case AccessibleTextType::ATTRIBUTE_RUN: + { + // TODO: implGetAttributeRunBoundary() (incompatible!) + + aResult.SegmentText = sText; + aResult.SegmentStart = 0; + aResult.SegmentEnd = nLength; + } + break; + default: + { + // unknown text type + } + } + + return aResult; + } + + + TextSegment OCommonAccessibleText::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + OUString sText( implGetText() ); + sal_Int32 nLength = sText.getLength(); + + if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength ) + throw IndexOutOfBoundsException(); + + i18n::Boundary aBoundary; + TextSegment aResult; + aResult.SegmentStart = -1; + aResult.SegmentEnd = -1; + + switch ( aTextType ) + { + case AccessibleTextType::CHARACTER: + { + if ( implIsValidIndex( nIndex - 1, nLength ) ) + { + aResult.SegmentText = sText.copy( nIndex - 1, 1 ); + aResult.SegmentStart = nIndex-1; + aResult.SegmentEnd = nIndex; + } + } + break; + case AccessibleTextType::GLYPH: + { + // get glyph at index + implGetGlyphBoundary( sText, aBoundary, nIndex ); + // get previous glyph + if ( aBoundary.startPos > 0 ) + { + implGetGlyphBoundary( sText, aBoundary, aBoundary.startPos - 1 ); + if ( implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + } + break; + case AccessibleTextType::WORD: + { + // get word at index + implGetWordBoundary( sText, aBoundary, nIndex ); + // get previous word + bool bWord = false; + while ( !bWord && aBoundary.startPos > 0 ) + bWord = implGetWordBoundary( sText, aBoundary, aBoundary.startPos - 1 ); + if ( bWord && implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + break; + case AccessibleTextType::SENTENCE: + { + // get sentence at index + implGetSentenceBoundary( sText, aBoundary, nIndex ); + // get previous sentence + if ( aBoundary.startPos > 0 ) + { + implGetSentenceBoundary( sText, aBoundary, aBoundary.startPos - 1 ); + if ( implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + } + break; + case AccessibleTextType::PARAGRAPH: + { + // get paragraph at index + implGetParagraphBoundary( sText, aBoundary, nIndex ); + // get previous paragraph + if ( aBoundary.startPos > 0 ) + { + implGetParagraphBoundary( sText, aBoundary, aBoundary.startPos - 1 ); + if ( implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + } + break; + case AccessibleTextType::LINE: + { + // get line at index + implGetLineBoundary( sText, aBoundary, nIndex ); + // get previous line + if ( aBoundary.startPos > 0 ) + { + implGetLineBoundary( sText, aBoundary, aBoundary.startPos - 1 ); + if ( implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + } + break; + case AccessibleTextType::ATTRIBUTE_RUN: + { + // TODO: implGetAttributeRunBoundary() (incompatible!) + } + break; + default: + { + // unknown text type + } + } + + return aResult; + } + + + TextSegment OCommonAccessibleText::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + OUString sText( implGetText() ); + sal_Int32 nLength = sText.getLength(); + + if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength ) + throw IndexOutOfBoundsException(); + + i18n::Boundary aBoundary; + TextSegment aResult; + aResult.SegmentStart = -1; + aResult.SegmentEnd = -1; + + switch ( aTextType ) + { + case AccessibleTextType::CHARACTER: + { + if ( implIsValidIndex( nIndex + 1, nLength ) ) + { + aResult.SegmentText = sText.copy( nIndex + 1, 1 ); + aResult.SegmentStart = nIndex+1; + aResult.SegmentEnd = nIndex+2; + } + } + break; + case AccessibleTextType::GLYPH: + { + // get glyph at index + implGetGlyphBoundary( sText, aBoundary, nIndex ); + // get next glyph + if ( aBoundary.endPos < nLength ) + { + implGetGlyphBoundary( sText, aBoundary, aBoundary.endPos ); + if ( implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + } + break; + case AccessibleTextType::WORD: + { + // get word at index + implGetWordBoundary( sText, aBoundary, nIndex ); + // get next word + bool bWord = false; + while ( !bWord && aBoundary.endPos < nLength ) + bWord = implGetWordBoundary( sText, aBoundary, aBoundary.endPos ); + if ( bWord && implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + break; + case AccessibleTextType::SENTENCE: + { + // get sentence at index + implGetSentenceBoundary( sText, aBoundary, nIndex ); + // get next sentence + sal_Int32 nEnd = aBoundary.endPos; + sal_Int32 nI = aBoundary.endPos; + bool bFound = false; + while ( !bFound && ++nI < nLength ) + { + implGetSentenceBoundary( sText, aBoundary, nI ); + bFound = ( aBoundary.endPos > nEnd ); + } + if ( bFound && implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + break; + case AccessibleTextType::PARAGRAPH: + { + // get paragraph at index + implGetParagraphBoundary( sText, aBoundary, nIndex ); + // get next paragraph + if ( aBoundary.endPos < nLength ) + { + implGetParagraphBoundary( sText, aBoundary, aBoundary.endPos ); + if ( implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + } + break; + case AccessibleTextType::LINE: + { + // get line at index + implGetLineBoundary( sText, aBoundary, nIndex ); + // get next line + if ( aBoundary.endPos < nLength ) + { + implGetLineBoundary( sText, aBoundary, aBoundary.endPos ); + if ( implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + } + } + } + break; + case AccessibleTextType::ATTRIBUTE_RUN: + { + // TODO: implGetAttributeRunBoundary() (incompatible!) + } + break; + default: + { + // unknown text type + } + } + + return aResult; + } + + + bool OCommonAccessibleText::implInitTextChangedEvent( + std::u16string_view rOldString, + std::u16string_view rNewString, + css::uno::Any& rDeleted, + css::uno::Any& rInserted) // throw() + { + size_t nLenOld = rOldString.size(); + size_t nLenNew = rNewString.size(); + + // equal + if ((0 == nLenOld) && (0 == nLenNew)) + return false; + + TextSegment aDeletedText; + TextSegment aInsertedText; + + aDeletedText.SegmentStart = -1; + aDeletedText.SegmentEnd = -1; + aInsertedText.SegmentStart = -1; + aInsertedText.SegmentEnd = -1; + + // insert only + if ((0 == nLenOld) && (nLenNew > 0)) + { + aInsertedText.SegmentStart = 0; + aInsertedText.SegmentEnd = nLenNew; + aInsertedText.SegmentText = rNewString.substr( aInsertedText.SegmentStart, aInsertedText.SegmentEnd - aInsertedText.SegmentStart ); + + rInserted <<= aInsertedText; + return true; + } + + // delete only + if ((nLenOld > 0) && (0 == nLenNew)) + { + aDeletedText.SegmentStart = 0; + aDeletedText.SegmentEnd = nLenOld; + aDeletedText.SegmentText = rOldString.substr( aDeletedText.SegmentStart, aDeletedText.SegmentEnd - aDeletedText.SegmentStart ); + + rDeleted <<= aDeletedText; + return true; + } + + auto pFirstDiffOld = rOldString.begin(); + auto pLastDiffOld = rOldString.end(); + auto pFirstDiffNew = rNewString.begin(); + auto pLastDiffNew = rNewString.end(); + + // find first difference + while ((pFirstDiffOld < pLastDiffOld) && (pFirstDiffNew < pLastDiffNew) + && (*pFirstDiffOld == *pFirstDiffNew)) + { + pFirstDiffOld++; + pFirstDiffNew++; + } + + // equality test + if (pFirstDiffOld == pLastDiffOld && pFirstDiffNew == pLastDiffNew) + return false; + + // find last difference + while ( ( pLastDiffOld > pFirstDiffOld) && + ( pLastDiffNew > pFirstDiffNew) && + (pLastDiffOld[-1] == pLastDiffNew[-1])) + { + pLastDiffOld--; + pLastDiffNew--; + } + + if (pFirstDiffOld < pLastDiffOld) + { + aDeletedText.SegmentStart = pFirstDiffOld - rOldString.begin(); + aDeletedText.SegmentEnd = pLastDiffOld - rOldString.begin(); + aDeletedText.SegmentText = rOldString.substr( aDeletedText.SegmentStart, aDeletedText.SegmentEnd - aDeletedText.SegmentStart ); + + rDeleted <<= aDeletedText; + } + + if (pFirstDiffNew < pLastDiffNew) + { + aInsertedText.SegmentStart = pFirstDiffNew - rNewString.begin(); + aInsertedText.SegmentEnd = pLastDiffNew - rNewString.begin(); + aInsertedText.SegmentText = rNewString.substr( aInsertedText.SegmentStart, aInsertedText.SegmentEnd - aInsertedText.SegmentStart ); + + rInserted <<= aInsertedText; + } + return true; + } + + + // OAccessibleTextHelper + + + OAccessibleTextHelper::OAccessibleTextHelper( ) + { + } + + + // XInterface + + + IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleTextHelper, OAccessibleExtendedComponentHelper, OAccessibleTextHelper_Base ) + + + // XTypeProvider + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleTextHelper, OAccessibleExtendedComponentHelper, OAccessibleTextHelper_Base ) + + + // XAccessibleText + + + OUString OAccessibleTextHelper::getSelectedText() + { + OExternalLockGuard aGuard( this ); + + return OCommonAccessibleText::getSelectedText(); + } + + + sal_Int32 OAccessibleTextHelper::getSelectionStart() + { + OExternalLockGuard aGuard( this ); + + return OCommonAccessibleText::getSelectionStart(); + } + + + sal_Int32 OAccessibleTextHelper::getSelectionEnd() + { + OExternalLockGuard aGuard( this ); + + return OCommonAccessibleText::getSelectionEnd(); + } + + + TextSegment OAccessibleTextHelper::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + OExternalLockGuard aGuard( this ); + + return OCommonAccessibleText::getTextAtIndex( nIndex, aTextType ); + } + + + TextSegment OAccessibleTextHelper::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + OExternalLockGuard aGuard( this ); + + return OCommonAccessibleText::getTextBeforeIndex( nIndex, aTextType ); + } + + + TextSegment OAccessibleTextHelper::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + OExternalLockGuard aGuard( this ); + + return OCommonAccessibleText::getTextBehindIndex( nIndex, aTextType ); + } + + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/accessiblewrapper.cxx b/comphelper/source/misc/accessiblewrapper.cxx new file mode 100644 index 000000000..1167f5640 --- /dev/null +++ b/comphelper/source/misc/accessiblewrapper.cxx @@ -0,0 +1,616 @@ +/* -*- 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 <comphelper/accessiblewrapper.hxx> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> + +using namespace ::comphelper; +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + + +namespace comphelper +{ + OWrappedAccessibleChildrenManager::OWrappedAccessibleChildrenManager( const Reference< XComponentContext >& _rxContext ) + :m_xContext( _rxContext ) + ,m_bTransientChildren( true ) + { + } + + + OWrappedAccessibleChildrenManager::~OWrappedAccessibleChildrenManager( ) + { + } + + + void OWrappedAccessibleChildrenManager::setTransientChildren( bool _bSet ) + { + m_bTransientChildren = _bSet; + } + + + void OWrappedAccessibleChildrenManager::setOwningAccessible( const Reference< XAccessible >& _rxAcc ) + { + OSL_ENSURE( !m_aOwningAccessible.get().is(), "OWrappedAccessibleChildrenManager::setOwningAccessible: to be called only once!" ); + m_aOwningAccessible = WeakReference< XAccessible >( _rxAcc ); + } + + + void OWrappedAccessibleChildrenManager::removeFromCache( const Reference< XAccessible >& _rxKey ) + { + AccessibleMap::iterator aRemovedPos = m_aChildrenMap.find( _rxKey ); + if ( m_aChildrenMap.end() != aRemovedPos ) + { // it was cached + // remove ourself as event listener + Reference< XComponent > xComp( aRemovedPos->first, UNO_QUERY ); + if( xComp.is() ) + xComp->removeEventListener( this ); + // and remove the entry from the map + m_aChildrenMap.erase( aRemovedPos ); + } + } + + + void OWrappedAccessibleChildrenManager::invalidateAll( ) + { + // remove as event listener from the map elements + for( const auto& rChild : m_aChildrenMap ) + { + Reference< XComponent > xComp( rChild.first, UNO_QUERY ); + if( xComp.is() ) + xComp->removeEventListener( this ); + } + // clear the map + AccessibleMap().swap(m_aChildrenMap); + } + + + Reference< XAccessible > OWrappedAccessibleChildrenManager::getAccessibleWrapperFor( + const Reference< XAccessible >& _rxKey ) + { + rtl::Reference< OAccessibleWrapper > xValue; + + if( !_rxKey.is() ) + { + // fprintf( stderr, "It was this path that was crashing stuff\n" ); + return xValue; + } + + // do we have this child in the cache? + AccessibleMap::const_iterator aPos = m_aChildrenMap.find( _rxKey ); + if ( m_aChildrenMap.end() != aPos ) + { + xValue = aPos->second; + } + else + { // not found in the cache, and allowed to create + // -> new wrapper + xValue = new OAccessibleWrapper( m_xContext, _rxKey, m_aOwningAccessible ); + + // see if we do cache children + if ( !m_bTransientChildren ) + { + if (!m_aChildrenMap.emplace( _rxKey, xValue ).second) + { + OSL_FAIL( + "OWrappedAccessibleChildrenManager::" + "getAccessibleWrapperFor: element was already" + " inserted!" ); + } + + // listen for disposals of inner children - this may happen when the inner context + // is the owner for the inner children (it will dispose these children, and of course + // not our wrapper for these children) + Reference< XComponent > xComp( _rxKey, UNO_QUERY ); + if ( xComp.is() ) + xComp->addEventListener( this ); + } + } + + return xValue; + } + + + void OWrappedAccessibleChildrenManager::dispose() + { + // dispose our children + for( const auto& rChild : m_aChildrenMap ) + { + Reference< XComponent > xComp( rChild.first, UNO_QUERY ); + if( xComp.is() ) + xComp->removeEventListener( this ); + + Reference< XComponent > xContextComponent; + if( rChild.second.is() ) + xContextComponent.set( rChild.second->getContextNoCreate(), + ::css::uno::UNO_QUERY ); + if( xContextComponent.is() ) + xContextComponent->dispose(); + } + + // clear our children + AccessibleMap().swap(m_aChildrenMap); + } + + + void OWrappedAccessibleChildrenManager::implTranslateChildEventValue( const Any& _rInValue, Any& _rOutValue ) + { + _rOutValue.clear(); + Reference< XAccessible > xChild; + if ( _rInValue >>= xChild ) + _rOutValue <<= getAccessibleWrapperFor( xChild ); + } + + + void OWrappedAccessibleChildrenManager::translateAccessibleEvent( const AccessibleEventObject& _rEvent, AccessibleEventObject& _rTranslatedEvent ) + { + // just in case we can't translate some of the values: + _rTranslatedEvent.NewValue = _rEvent.NewValue; + _rTranslatedEvent.OldValue = _rEvent.OldValue; + + switch ( _rEvent.EventId ) + { + case AccessibleEventId::CHILD: + case AccessibleEventId::ACTIVE_DESCENDANT_CHANGED: + case AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED: + case AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED: + case AccessibleEventId::LABEL_FOR_RELATION_CHANGED: + case AccessibleEventId::LABELED_BY_RELATION_CHANGED: + case AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED: + case AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED: + // these are events where both the old and the new value contain child references + implTranslateChildEventValue( _rEvent.OldValue, _rTranslatedEvent.OldValue ); + implTranslateChildEventValue( _rEvent.NewValue, _rTranslatedEvent.NewValue ); + break; + + case AccessibleEventId::NAME_CHANGED: + case AccessibleEventId::DESCRIPTION_CHANGED: + case AccessibleEventId::ACTION_CHANGED: + case AccessibleEventId::STATE_CHANGED: + case AccessibleEventId::BOUNDRECT_CHANGED: + case AccessibleEventId::INVALIDATE_ALL_CHILDREN: + case AccessibleEventId::SELECTION_CHANGED: + case AccessibleEventId::VISIBLE_DATA_CHANGED: + case AccessibleEventId::VALUE_CHANGED: + case AccessibleEventId::MEMBER_OF_RELATION_CHANGED: + case AccessibleEventId::CARET_CHANGED: + case AccessibleEventId::TEXT_CHANGED: + case AccessibleEventId::HYPERTEXT_CHANGED: + case AccessibleEventId::TABLE_CAPTION_CHANGED: + case AccessibleEventId::TABLE_COLUMN_DESCRIPTION_CHANGED: + case AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED: + case AccessibleEventId::TABLE_MODEL_CHANGED: + case AccessibleEventId::TABLE_ROW_DESCRIPTION_CHANGED: + case AccessibleEventId::TABLE_ROW_HEADER_CHANGED: + case AccessibleEventId::TABLE_SUMMARY_CHANGED: + // these Ids are also missed: SUB_WINDOW_OF_RELATION_CHANGED & TEXT_ATTRIBUTE_CHANGED + case AccessibleEventId::TEXT_SELECTION_CHANGED: + // nothing to translate + break; + + default: + OSL_FAIL( "OWrappedAccessibleChildrenManager::translateAccessibleEvent: unknown (or unexpected) event id!" ); + break; + } + } + + + void OWrappedAccessibleChildrenManager::handleChildNotification( const AccessibleEventObject& _rEvent ) + { + if ( AccessibleEventId::INVALIDATE_ALL_CHILDREN == _rEvent.EventId ) + { // clear our child map + invalidateAll( ); + } + else if ( AccessibleEventId::CHILD == _rEvent.EventId ) + { + // check if the removed or replaced element is cached + Reference< XAccessible > xRemoved; + if ( _rEvent.OldValue >>= xRemoved ) + removeFromCache( xRemoved ); + } + } + + + void SAL_CALL OWrappedAccessibleChildrenManager::disposing( const EventObject& _rSource ) + { + // this should come from one of the inner XAccessible's of our children + Reference< XAccessible > xSource( _rSource.Source, UNO_QUERY ); + AccessibleMap::iterator aDisposedPos = m_aChildrenMap.find( xSource ); +#if OSL_DEBUG_LEVEL > 0 + if ( m_aChildrenMap.end() == aDisposedPos ) + { + OSL_FAIL( "OWrappedAccessibleChildrenManager::disposing: where did this come from?" ); + // helper for diagnostics + Reference< XAccessible > xOwningAccessible( m_aOwningAccessible ); + Reference< XAccessibleContext > xContext; + try + { + if ( xOwningAccessible.is() ) + xContext = xOwningAccessible->getAccessibleContext(); + if ( xContext.is() ) + { + //TODO: do something + //OUString sName = xContext->getAccessibleName(); + //OUString sDescription = xContext->getAccessibleDescription(); + //sal_Int32 nPlaceYourBreakpointHere = 0; + } + } + catch( const Exception& /*e*/ ) + { + // silent this, it's only diagnostics which failed + } + } +#endif + if ( m_aChildrenMap.end() != aDisposedPos ) + { + m_aChildrenMap.erase( aDisposedPos ); + } + } + + OAccessibleWrapper::OAccessibleWrapper( const Reference< XComponentContext >& _rxContext, + const Reference< XAccessible >& _rxInnerAccessible, const Reference< XAccessible >& _rxParentAccessible ) + :OAccessibleWrapper_Base( ) + ,OComponentProxyAggregation( _rxContext, Reference< XComponent >( _rxInnerAccessible, UNO_QUERY ) ) + ,m_xParentAccessible( _rxParentAccessible ) + ,m_xInnerAccessible( _rxInnerAccessible ) + { + } + + + OAccessibleWrapper::~OAccessibleWrapper( ) + { + if ( !m_rBHelper.bDisposed ) + { + acquire(); // to prevent duplicate dtor calls + dispose(); + } + } + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleWrapper, OComponentProxyAggregation, OAccessibleWrapper_Base ) + IMPLEMENT_FORWARD_REFCOUNT( OAccessibleWrapper, OComponentProxyAggregation ) + + + Any OAccessibleWrapper::queryInterface( const Type& _rType ) + { + // #111089# instead of the inner XAccessible the proxy XAccessible must be returned + Any aReturn = OAccessibleWrapper_Base::queryInterface( _rType ); + if ( !aReturn.hasValue() ) + aReturn = OComponentProxyAggregation::queryInterface( _rType ); + + return aReturn; + } + + + Reference< XAccessibleContext > OAccessibleWrapper::getContextNoCreate( ) const + { + return m_aContext; + } + + + rtl::Reference<OAccessibleContextWrapper> OAccessibleWrapper::createAccessibleContext( const Reference< XAccessibleContext >& _rxInnerContext ) + { + return new OAccessibleContextWrapper( getComponentContext(), _rxInnerContext, this, m_xParentAccessible ); + } + + + Reference< XAccessibleContext > SAL_CALL OAccessibleWrapper::getAccessibleContext( ) + { + // see if the context is still alive (we cache it) + Reference< XAccessibleContext > xContext = m_aContext; + if ( !xContext.is() ) + { + // create a new context + Reference< XAccessibleContext > xInnerContext = m_xInnerAccessible->getAccessibleContext( ); + if ( xInnerContext.is() ) + { + xContext = createAccessibleContext( xInnerContext ); + // cache it + m_aContext = WeakReference< XAccessibleContext >( xContext ); + } + } + + return xContext; + } + + OAccessibleContextWrapperHelper::OAccessibleContextWrapperHelper( + const Reference< XComponentContext >& _rxContext, + ::cppu::OBroadcastHelper& _rBHelper, + const Reference< XAccessibleContext >& _rxInnerAccessibleContext, + const Reference< XAccessible >& _rxOwningAccessible, + const Reference< XAccessible >& _rxParentAccessible ) + :OComponentProxyAggregationHelper( _rxContext, _rBHelper ) + ,m_xInnerContext( _rxInnerAccessibleContext ) + ,m_xOwningAccessible( _rxOwningAccessible ) + ,m_xParentAccessible( _rxParentAccessible ) + // initialize the mapper for our children + ,m_xChildMapper( new OWrappedAccessibleChildrenManager( getComponentContext() ) ) + { + // determine if we're allowed to cache children + Reference< XAccessibleStateSet > xStates( m_xInnerContext->getAccessibleStateSet( ) ); + OSL_ENSURE( xStates.is(), "OAccessibleContextWrapperHelper::OAccessibleContextWrapperHelper: no inner state set!" ); + m_xChildMapper->setTransientChildren( !xStates.is() || xStates->contains( AccessibleStateType::MANAGES_DESCENDANTS) ); + + m_xChildMapper->setOwningAccessible( m_xOwningAccessible ); + } + + + void OAccessibleContextWrapperHelper::aggregateProxy( oslInterlockedCount& _rRefCount, ::cppu::OWeakObject& _rDelegator ) + { + Reference< XComponent > xInnerComponent( m_xInnerContext, UNO_QUERY ); + OSL_ENSURE( xInnerComponent.is(), "OComponentProxyAggregation::aggregateProxy: accessible is no XComponent!" ); + if ( xInnerComponent.is() ) + componentAggregateProxyFor( xInnerComponent, _rRefCount, _rDelegator ); + + // add as event listener to the inner context, because we want to multiplex the AccessibleEvents + osl_atomic_increment( &_rRefCount ); + { + Reference< XAccessibleEventBroadcaster > xBroadcaster( m_xInner, UNO_QUERY ); + if ( xBroadcaster.is() ) + xBroadcaster->addAccessibleEventListener( this ); + } + osl_atomic_decrement( &_rRefCount ); + } + + + OAccessibleContextWrapperHelper::~OAccessibleContextWrapperHelper( ) + { + OSL_ENSURE( m_rBHelper.bDisposed, "OAccessibleContextWrapperHelper::~OAccessibleContextWrapperHelper: you should ensure (in your dtor) that the object is disposed!" ); + } + + + Any SAL_CALL OAccessibleContextWrapperHelper::queryInterface( const Type& _rType ) + { + Any aReturn = OComponentProxyAggregationHelper::queryInterface( _rType ); + if ( !aReturn.hasValue() ) + aReturn = OAccessibleContextWrapperHelper_Base::queryInterface( _rType ); + return aReturn; + } + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleContextWrapperHelper, OComponentProxyAggregationHelper, OAccessibleContextWrapperHelper_Base ) + + + sal_Int32 OAccessibleContextWrapperHelper::baseGetAccessibleChildCount( ) + { + return m_xInnerContext->getAccessibleChildCount(); + } + + + Reference< XAccessible > OAccessibleContextWrapperHelper::baseGetAccessibleChild( sal_Int32 i ) + { + // get the child of the wrapped component + Reference< XAccessible > xInnerChild = m_xInnerContext->getAccessibleChild( i ); + return m_xChildMapper->getAccessibleWrapperFor( xInnerChild ); + } + + + Reference< XAccessibleRelationSet > OAccessibleContextWrapperHelper::baseGetAccessibleRelationSet( ) + { + return m_xInnerContext->getAccessibleRelationSet(); + // TODO: if this relation set would contain relations to siblings, we would normally need + // to wrap them, too... + } + + + void SAL_CALL OAccessibleContextWrapperHelper::notifyEvent( const AccessibleEventObject& _rEvent ) + { +#if OSL_DEBUG_LEVEL > 0 + if ( AccessibleEventId::STATE_CHANGED == _rEvent.EventId ) + { + bool bChildTransienceChanged = false; + sal_Int16 nChangeState = 0; + if ( _rEvent.OldValue >>= nChangeState ) + bChildTransienceChanged = bChildTransienceChanged || AccessibleStateType::MANAGES_DESCENDANTS == nChangeState; + if ( _rEvent.NewValue >>= nChangeState ) + bChildTransienceChanged = bChildTransienceChanged || AccessibleStateType::MANAGES_DESCENDANTS == nChangeState; + OSL_ENSURE( !bChildTransienceChanged, "OAccessibleContextWrapperHelper::notifyEvent: MANAGES_DESCENDANTS is not expected to change during runtime!" ); + // if this asserts, then we would need to update our m_bTransientChildren flag here, + // as well as (potentially) our child cache + } +#endif + AccessibleEventObject aTranslatedEvent( _rEvent ); + + { + ::osl::MutexGuard aGuard( m_rBHelper.rMutex ); + + // translate the event + queryInterface( cppu::UnoType<XInterface>::get() ) >>= aTranslatedEvent.Source; + m_xChildMapper->translateAccessibleEvent( _rEvent, aTranslatedEvent ); + + // see if any of these notifications affect our child manager + m_xChildMapper->handleChildNotification( _rEvent ); + + if ( aTranslatedEvent.NewValue == m_xInner ) + aTranslatedEvent.NewValue <<= aTranslatedEvent.Source; + if ( aTranslatedEvent.OldValue == m_xInner ) + aTranslatedEvent.OldValue <<= aTranslatedEvent.Source; + } + + notifyTranslatedEvent( aTranslatedEvent ); + } + + + void SAL_CALL OAccessibleContextWrapperHelper::dispose() + { + ::osl::MutexGuard aGuard( m_rBHelper.rMutex ); + + // stop multiplexing events + Reference< XAccessibleEventBroadcaster > xBroadcaster( m_xInner, UNO_QUERY ); + OSL_ENSURE( xBroadcaster.is(), "OAccessibleContextWrapperHelper::disposing(): inner context is no broadcaster!" ); + if ( xBroadcaster.is() ) + xBroadcaster->removeAccessibleEventListener( this ); + + // dispose the child cache/map + m_xChildMapper->dispose(); + + // let the base class dispose the inner component + OComponentProxyAggregationHelper::dispose(); + } + + + void SAL_CALL OAccessibleContextWrapperHelper::disposing( const EventObject& _rEvent ) + { + // simply disambiguate this + OComponentProxyAggregationHelper::disposing( _rEvent ); + } + + IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleContextWrapper, OAccessibleContextWrapper_CBase, OAccessibleContextWrapperHelper ) + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleContextWrapper, OAccessibleContextWrapper_CBase, OAccessibleContextWrapperHelper ) + + + OAccessibleContextWrapper::OAccessibleContextWrapper( const Reference< XComponentContext >& _rxContext, + const Reference< XAccessibleContext >& _rxInnerAccessibleContext, const Reference< XAccessible >& _rxOwningAccessible, + const Reference< XAccessible >& _rxParentAccessible ) + :OAccessibleContextWrapper_CBase( m_aMutex ) + ,OAccessibleContextWrapperHelper( _rxContext, rBHelper, _rxInnerAccessibleContext, _rxOwningAccessible, _rxParentAccessible ) + ,m_nNotifierClient( 0 ) + { + aggregateProxy( m_refCount, *this ); + } + + + OAccessibleContextWrapper::~OAccessibleContextWrapper() + { + } + + + sal_Int32 SAL_CALL OAccessibleContextWrapper::getAccessibleChildCount( ) + { + return baseGetAccessibleChildCount(); + } + + + Reference< XAccessible > SAL_CALL OAccessibleContextWrapper::getAccessibleChild( sal_Int32 i ) + { + return baseGetAccessibleChild( i ); + } + + + Reference< XAccessible > SAL_CALL OAccessibleContextWrapper::getAccessibleParent( ) + { + return m_xParentAccessible; + } + + + sal_Int32 SAL_CALL OAccessibleContextWrapper::getAccessibleIndexInParent( ) + { + return m_xInnerContext->getAccessibleIndexInParent(); + } + + + sal_Int16 SAL_CALL OAccessibleContextWrapper::getAccessibleRole( ) + { + return m_xInnerContext->getAccessibleRole(); + } + + + OUString SAL_CALL OAccessibleContextWrapper::getAccessibleDescription( ) + { + return m_xInnerContext->getAccessibleDescription(); + } + + + OUString SAL_CALL OAccessibleContextWrapper::getAccessibleName( ) + { + return m_xInnerContext->getAccessibleName(); + } + + + Reference< XAccessibleRelationSet > SAL_CALL OAccessibleContextWrapper::getAccessibleRelationSet( ) + { + return baseGetAccessibleRelationSet(); + } + + + Reference< XAccessibleStateSet > SAL_CALL OAccessibleContextWrapper::getAccessibleStateSet( ) + { + return m_xInnerContext->getAccessibleStateSet(); + } + + + Locale SAL_CALL OAccessibleContextWrapper::getLocale( ) + { + return m_xInnerContext->getLocale(); + } + + + void OAccessibleContextWrapper::notifyTranslatedEvent( const AccessibleEventObject& _rEvent ) + { + if ( m_nNotifierClient ) + AccessibleEventNotifier::addEvent( m_nNotifierClient, _rEvent ); + } + + + void SAL_CALL OAccessibleContextWrapper::addAccessibleEventListener( const Reference< XAccessibleEventListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_nNotifierClient ) + m_nNotifierClient = AccessibleEventNotifier::registerClient( ); + AccessibleEventNotifier::addEventListener( m_nNotifierClient, _rxListener ); + } + + + void SAL_CALL OAccessibleContextWrapper::removeAccessibleEventListener( const Reference< XAccessibleEventListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_nNotifierClient ) + { + if ( 0 == AccessibleEventNotifier::removeEventListener( m_nNotifierClient, _rxListener ) ) + { + AccessibleEventNotifier::TClientId nId( m_nNotifierClient ); + m_nNotifierClient = 0; + AccessibleEventNotifier::revokeClient( nId ); + } + } + } + + + void SAL_CALL OAccessibleContextWrapper::disposing() + { + AccessibleEventNotifier::TClientId nClientId( 0 ); + + // --- <mutex lock> ----------------------------------------- + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // prepare notifying our AccessibleListeners + if ( m_nNotifierClient ) + { + nClientId = m_nNotifierClient; + m_nNotifierClient = 0; + } + } + // --- </mutex lock> ----------------------------------------- + + // let the base class do + OAccessibleContextWrapperHelper::dispose(); + + // notify the disposal + if ( nClientId ) + AccessibleEventNotifier::revokeClientNotifyDisposing( nClientId, *this ); + } +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/accimplaccess.cxx b/comphelper/source/misc/accimplaccess.cxx new file mode 100644 index 000000000..bcf05c926 --- /dev/null +++ b/comphelper/source/misc/accimplaccess.cxx @@ -0,0 +1,54 @@ +/* -*- 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 <comphelper/accimplaccess.hxx> +#include <comphelper/servicehelper.hxx> + + +namespace comphelper +{ + + + using ::com::sun::star::uno::Sequence; + + OAccessibleImplementationAccess::OAccessibleImplementationAccess( ) + { + } + + + OAccessibleImplementationAccess::~OAccessibleImplementationAccess( ) + { + } + + const Sequence<sal_Int8> & OAccessibleImplementationAccess::getUnoTunnelId() + { + static const comphelper::UnoIdInit implID; + return implID.getSeq(); + } + + + sal_Int64 SAL_CALL OAccessibleImplementationAccess::getSomething( const Sequence< sal_Int8 >& _rIdentifier ) + { + return comphelper::getSomethingImpl(_rIdentifier, this); + } + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/anycompare.cxx b/comphelper/source/misc/anycompare.cxx new file mode 100644 index 000000000..8a2387723 --- /dev/null +++ b/comphelper/source/misc/anycompare.cxx @@ -0,0 +1,453 @@ +/* -*- 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 <optional> +#include <comphelper/anycompare.hxx> +#include <typelib/typedescription.hxx> + +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/DateTime.hpp> + +#include "typedescriptionref.hxx" + +namespace comphelper +{ + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::Type; + using ::com::sun::star::uno::TypeDescription; + using ::com::sun::star::uno::TypeClass_CHAR; + using ::com::sun::star::uno::TypeClass_BOOLEAN; + using ::com::sun::star::uno::TypeClass_BYTE; + using ::com::sun::star::uno::TypeClass_SHORT; + using ::com::sun::star::uno::TypeClass_UNSIGNED_SHORT; + using ::com::sun::star::uno::TypeClass_LONG; + using ::com::sun::star::uno::TypeClass_UNSIGNED_LONG; + using ::com::sun::star::uno::TypeClass_HYPER; + using ::com::sun::star::uno::TypeClass_UNSIGNED_HYPER; + using ::com::sun::star::uno::TypeClass_FLOAT; + using ::com::sun::star::uno::TypeClass_DOUBLE; + using ::com::sun::star::uno::TypeClass_STRING; + using ::com::sun::star::uno::TypeClass_TYPE; + using ::com::sun::star::uno::TypeClass_ENUM; + using ::com::sun::star::uno::TypeClass_INTERFACE; + using ::com::sun::star::uno::TypeClass_STRUCT; + using ::com::sun::star::i18n::XCollator; + using ::com::sun::star::util::Date; + using ::com::sun::star::util::Time; + using ::com::sun::star::util::DateTime; + using ::comphelper::detail::TypeDescriptionRef; + + namespace { + + class DatePredicateLess : public IKeyPredicateLess + { + public: + virtual bool isLess( css::uno::Any const & _lhs, css::uno::Any const & _rhs ) const override + { + Date lhs, rhs; + if ( !( _lhs >>= lhs ) + || !( _rhs >>= rhs ) + ) + throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1); + // FIXME Timezone? + + if ( lhs.Year < rhs.Year ) + return true; + if ( lhs.Year > rhs.Year ) + return false; + + if ( lhs.Month < rhs.Month ) + return true; + if ( lhs.Month > rhs.Month ) + return false; + + if ( lhs.Day < rhs.Day ) + return true; + return false; + } + }; + + class TimePredicateLess : public IKeyPredicateLess + { + public: + virtual bool isLess( css::uno::Any const & _lhs, css::uno::Any const & _rhs ) const override + { + Time lhs, rhs; + if ( !( _lhs >>= lhs ) + || !( _rhs >>= rhs ) + ) + throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1); + // FIXME Timezone? + + if ( lhs.Hours < rhs.Hours ) + return true; + if ( lhs.Hours > rhs.Hours ) + return false; + + if ( lhs.Minutes < rhs.Minutes ) + return true; + if ( lhs.Minutes > rhs.Minutes ) + return false; + + if ( lhs.Seconds < rhs.Seconds ) + return true; + if ( lhs.Seconds > rhs.Seconds ) + return false; + + if ( lhs.NanoSeconds < rhs.NanoSeconds ) + return true; + return false; + } + }; + + class DateTimePredicateLess : public IKeyPredicateLess + { + public: + virtual bool isLess( css::uno::Any const & _lhs, css::uno::Any const & _rhs ) const override + { + DateTime lhs, rhs; + if ( !( _lhs >>= lhs ) + || !( _rhs >>= rhs ) + ) + throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1); + // FIXME Timezone? + + if ( lhs.Year < rhs.Year ) + return true; + if ( lhs.Year > rhs.Year ) + return false; + + if ( lhs.Month < rhs.Month ) + return true; + if ( lhs.Month > rhs.Month ) + return false; + + if ( lhs.Day < rhs.Day ) + return true; + if ( lhs.Day > rhs.Day ) + return false; + + if ( lhs.Hours < rhs.Hours ) + return true; + if ( lhs.Hours > rhs.Hours ) + return false; + + if ( lhs.Minutes < rhs.Minutes ) + return true; + if ( lhs.Minutes > rhs.Minutes ) + return false; + + if ( lhs.Seconds < rhs.Seconds ) + return true; + if ( lhs.Seconds > rhs.Seconds ) + return false; + + if ( lhs.NanoSeconds < rhs.NanoSeconds ) + return true; + return false; + } + }; + + bool anyLess( void const * lhs, typelib_TypeDescriptionReference * lhsType, + void const * rhs, typelib_TypeDescriptionReference * rhsType ); + + // For compound types we need to compare them member by member until we've + // checked them all or found a member that differs. For inequality checks + // we need to call anyLess() twice in both directions, this function does that. + std::optional<bool> anyCompare( void const * lhs, typelib_TypeDescriptionReference * lhsType, + void const * rhs, typelib_TypeDescriptionReference * rhsType ) + { + if( anyLess( lhs, lhsType, rhs, rhsType )) + return std::optional( true ); + if( anyLess( rhs, rhsType, lhs, lhsType )) + return std::optional( false ); + return std::nullopt; // equal, so can't yet tell if anyLess() should return + } + + // This is typelib_typedescription_equals(), but returns -1/0/1 values like strcmp(). + int compareTypes( const typelib_TypeDescription * lhsType, + const typelib_TypeDescription * rhsType ) + { + if( lhsType == rhsType ) + return 0; + if( lhsType->eTypeClass != rhsType->eTypeClass ) + return lhsType->eTypeClass - rhsType->eTypeClass; + if( lhsType->pTypeName->length != rhsType->pTypeName->length ) + return lhsType->pTypeName->length - rhsType->pTypeName->length; + return rtl_ustr_compare( lhsType->pTypeName->buffer, rhsType->pTypeName->buffer ); + } + + bool anyLess( void const * lhs, typelib_TypeDescriptionReference * lhsType, + void const * rhs, typelib_TypeDescriptionReference * rhsType ) + { + if (lhsType->eTypeClass != rhsType->eTypeClass) + return lhsType->eTypeClass < rhsType->eTypeClass; + + if (lhsType->eTypeClass == typelib_TypeClass_VOID) { + return false; + } + assert(lhs != nullptr); + assert(rhs != nullptr); + + switch (lhsType->eTypeClass) { + case typelib_TypeClass_INTERFACE: + return lhs < rhs; + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: { + TypeDescription lhsTypeDescr( lhsType ); + if (!lhsTypeDescr.is()) + lhsTypeDescr.makeComplete(); + if (!lhsTypeDescr.is()) + throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1); + TypeDescription rhsTypeDescr( rhsType ); + if (!rhsTypeDescr.is()) + rhsTypeDescr.makeComplete(); + if (!rhsTypeDescr.is()) + throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1); + int compare = compareTypes( lhsTypeDescr.get(), rhsTypeDescr.get()); + if( compare != 0 ) + return compare < 0; + + typelib_CompoundTypeDescription * compType = + reinterpret_cast< typelib_CompoundTypeDescription * >( + lhsTypeDescr.get() ); + sal_Int32 nDescr = compType->nMembers; + + if (compType->pBaseTypeDescription) { + std::optional<bool> subLess = anyCompare( + lhs, reinterpret_cast< + typelib_TypeDescription * >( + compType->pBaseTypeDescription)->pWeakRef, + rhs, reinterpret_cast< + typelib_TypeDescription * >( + compType->pBaseTypeDescription)->pWeakRef); + if(subLess.has_value()) + return *subLess; + } + + typelib_TypeDescriptionReference ** ppTypeRefs = + compType->ppTypeRefs; + sal_Int32 * memberOffsets = compType->pMemberOffsets; + + for ( sal_Int32 nPos = 0; nPos < nDescr; ++nPos ) + { + TypeDescriptionRef memberType( ppTypeRefs[ nPos ] ); + if (!memberType.is()) + throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1); + std::optional<bool> subLess = anyCompare( + static_cast< char const * >( + lhs ) + memberOffsets[ nPos ], + memberType->pWeakRef, + static_cast< char const * >( + rhs ) + memberOffsets[ nPos ], + memberType->pWeakRef); + if(subLess.has_value()) + return *subLess; + } + return false; // equal + } + case typelib_TypeClass_SEQUENCE: { + uno_Sequence * lhsSeq = *static_cast< uno_Sequence * const * >(lhs); + uno_Sequence * rhsSeq = *static_cast< uno_Sequence * const * >(rhs); + if( lhsSeq->nElements != rhsSeq->nElements) + return lhsSeq->nElements < rhsSeq->nElements; + sal_Int32 nElements = lhsSeq->nElements; + + TypeDescriptionRef lhsTypeDescr( lhsType ); + if (!lhsTypeDescr.is()) + throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1); + TypeDescriptionRef rhsTypeDescr( rhsType ); + if (!rhsTypeDescr.is()) + throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1); + int compare = compareTypes( lhsTypeDescr.get(), rhsTypeDescr.get()); + if( compare != 0 ) + return compare < 0; + + typelib_TypeDescriptionReference * elementTypeRef = + reinterpret_cast< typelib_IndirectTypeDescription * >(lhsTypeDescr.get())->pType; + TypeDescriptionRef elementTypeDescr( elementTypeRef ); + if (!elementTypeDescr.is()) + throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1); + assert( elementTypeDescr.equals( TypeDescriptionRef( + reinterpret_cast< typelib_IndirectTypeDescription * >(lhsTypeDescr.get())->pType ))); + + sal_Int32 nElementSize = elementTypeDescr->nSize; + if (nElements > 0) + { + char const * lhsElements = lhsSeq->elements; + char const * rhsElements = rhsSeq->elements; + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + std::optional<bool> subLess = anyCompare( + lhsElements + (nElementSize * nPos), + elementTypeDescr->pWeakRef, + rhsElements + (nElementSize * nPos), + elementTypeDescr->pWeakRef ); + if(subLess.has_value()) + return *subLess; + } + } + return false; // equal + } + case typelib_TypeClass_ANY: { + uno_Any const * lhsAny = static_cast< uno_Any const * >(lhs); + uno_Any const * rhsAny = static_cast< uno_Any const * >(rhs); + return anyLess( lhsAny->pData, lhsAny->pType, rhsAny->pData, rhsAny->pType ); + } + case typelib_TypeClass_TYPE: { + OUString const & lhsTypeName = OUString::unacquired( + &(*static_cast< typelib_TypeDescriptionReference * const * >(lhs))->pTypeName); + OUString const & rhsTypeName = OUString::unacquired( + &(*static_cast< typelib_TypeDescriptionReference * const * >(rhs))->pTypeName); + return lhsTypeName < rhsTypeName; + } + case typelib_TypeClass_STRING: { + OUString const & lhsStr = OUString::unacquired( + static_cast< rtl_uString * const * >(lhs) ); + OUString const & rhsStr = OUString::unacquired( + static_cast< rtl_uString * const * >(rhs) ); + return lhsStr < rhsStr; + } + case typelib_TypeClass_ENUM: { + TypeDescription lhsTypeDescr( lhsType ); + if (!lhsTypeDescr.is()) + lhsTypeDescr.makeComplete(); + if (!lhsTypeDescr.is()) + throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1); + TypeDescription rhsTypeDescr( rhsType ); + if (!rhsTypeDescr.is()) + rhsTypeDescr.makeComplete(); + if (!rhsTypeDescr.is()) + throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1); + int compare = compareTypes( lhsTypeDescr.get(), rhsTypeDescr.get()); + if( compare != 0 ) + return compare < 0; + + return *static_cast< int const * >(lhs) < *static_cast< int const * >(rhs); + } + case typelib_TypeClass_BOOLEAN: + return *static_cast< sal_Bool const * >(lhs) < *static_cast< sal_Bool const * >(rhs); + case typelib_TypeClass_CHAR: + return *static_cast< sal_Unicode const * >(lhs) < *static_cast< sal_Unicode const * >(rhs); + case typelib_TypeClass_FLOAT: + return *static_cast< float const * >(lhs) < *static_cast< float const * >(rhs); + case typelib_TypeClass_DOUBLE: + return *static_cast< double const * >(lhs) < *static_cast< double const * >(rhs); + case typelib_TypeClass_BYTE: + return *static_cast< sal_Int8 const * >(lhs) < *static_cast< sal_Int8 const * >(rhs); + case typelib_TypeClass_SHORT: + return *static_cast< sal_Int16 const * >(lhs) < *static_cast< sal_Int16 const * >(rhs); + case typelib_TypeClass_UNSIGNED_SHORT: + return *static_cast< sal_uInt16 const * >(lhs) < *static_cast< sal_uInt16 const * >(rhs); + case typelib_TypeClass_LONG: + return *static_cast< sal_Int32 const * >(lhs) < *static_cast< sal_Int32 const * >(rhs); + case typelib_TypeClass_UNSIGNED_LONG: + return *static_cast< sal_uInt32 const * >(lhs) < *static_cast< sal_uInt32 const * >(rhs); + case typelib_TypeClass_HYPER: + return *static_cast< sal_Int64 const * >(lhs) < *static_cast< sal_Int64 const * >(rhs); + case typelib_TypeClass_UNSIGNED_HYPER: + return *static_cast< sal_uInt64 const * >(lhs) < *static_cast< sal_uInt64 const * >(rhs); + // case typelib_TypeClass_UNKNOWN: + // case typelib_TypeClass_SERVICE: + // case typelib_TypeClass_MODULE: + default: + return false; + } + } + + } // namespace + + std::unique_ptr< IKeyPredicateLess > getStandardLessPredicate( Type const & i_type, Reference< XCollator > const & i_collator ) + { + std::unique_ptr< IKeyPredicateLess > pComparator; + switch ( i_type.getTypeClass() ) + { + case TypeClass_CHAR: + pComparator.reset( new ScalarPredicateLess< sal_Unicode > ); + break; + case TypeClass_BOOLEAN: + pComparator.reset( new ScalarPredicateLess< bool > ); + break; + case TypeClass_BYTE: + pComparator.reset( new ScalarPredicateLess< sal_Int8 > ); + break; + case TypeClass_SHORT: + pComparator.reset( new ScalarPredicateLess< sal_Int16 > ); + break; + case TypeClass_UNSIGNED_SHORT: + pComparator.reset( new ScalarPredicateLess< sal_uInt16 > ); + break; + case TypeClass_LONG: + pComparator.reset( new ScalarPredicateLess< sal_Int32 > ); + break; + case TypeClass_UNSIGNED_LONG: + pComparator.reset( new ScalarPredicateLess< sal_uInt32 > ); + break; + case TypeClass_HYPER: + pComparator.reset( new ScalarPredicateLess< sal_Int64 > ); + break; + case TypeClass_UNSIGNED_HYPER: + pComparator.reset( new ScalarPredicateLess< sal_uInt64 > ); + break; + case TypeClass_FLOAT: + pComparator.reset( new ScalarPredicateLess< float > ); + break; + case TypeClass_DOUBLE: + pComparator.reset( new ScalarPredicateLess< double > ); + break; + case TypeClass_STRING: + if ( i_collator.is() ) + pComparator.reset( new StringCollationPredicateLess( i_collator ) ); + else + pComparator.reset( new StringPredicateLess ); + break; + case TypeClass_TYPE: + pComparator.reset( new TypePredicateLess ); + break; + case TypeClass_ENUM: + pComparator.reset( new EnumPredicateLess( i_type ) ); + break; + case TypeClass_INTERFACE: + pComparator.reset( new InterfacePredicateLess ); + break; + case TypeClass_STRUCT: + if ( i_type.equals( ::cppu::UnoType< Date >::get() ) ) + pComparator.reset( new DatePredicateLess ); + else if ( i_type.equals( ::cppu::UnoType< Time >::get() ) ) + pComparator.reset( new TimePredicateLess ); + else if ( i_type.equals( ::cppu::UnoType< DateTime >::get() ) ) + pComparator.reset( new DateTimePredicateLess ); + break; + default: + break; + } + return pComparator; + } + + bool anyLess( css::uno::Any const & lhs, css::uno::Any const & rhs) + { + return anyLess( lhs.getValue(), lhs.getValueTypeRef(), rhs.getValue(), rhs.getValueTypeRef()); + } + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/anytohash.cxx b/comphelper/source/misc/anytohash.cxx new file mode 100644 index 000000000..4e97ea124 --- /dev/null +++ b/comphelper/source/misc/anytohash.cxx @@ -0,0 +1,210 @@ +/* -*- 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 <comphelper/anytohash.hxx> + +#include <o3tl/hash_combine.hxx> +#include <typelib/typedescription.hxx> + +#include <com/sun/star/uno/Sequence.hxx> + +#include "typedescriptionref.hxx" + +using namespace ::com::sun::star; +using ::com::sun::star::uno::TypeDescription; +using ::comphelper::detail::TypeDescriptionRef; + +namespace comphelper { +namespace { + +std::optional<size_t> hashValue( size_t hash, + void const * val, typelib_TypeDescriptionReference * typeRef ) +{ + o3tl::hash_combine( hash, typeRef->eTypeClass ); + if (typeRef->eTypeClass == typelib_TypeClass_VOID) { + return hash; + } + assert(val != nullptr); + + switch (typeRef->eTypeClass) { + case typelib_TypeClass_INTERFACE: { + return std::nullopt; // not implemented + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: { + TypeDescription typeDescr( typeRef ); + if (!typeDescr.is()) + typeDescr.makeComplete(); + if (!typeDescr.is()) + return std::nullopt; + + typelib_CompoundTypeDescription * compType = + reinterpret_cast< typelib_CompoundTypeDescription * >( + typeDescr.get() ); + sal_Int32 nDescr = compType->nMembers; + + if (compType->pBaseTypeDescription) { + std::optional<size_t> tmpHash = hashValue( + hash, val, reinterpret_cast< + typelib_TypeDescription * >( + compType->pBaseTypeDescription)->pWeakRef); + if(!tmpHash.has_value()) + return std::nullopt; + hash = *tmpHash; + } + + typelib_TypeDescriptionReference ** ppTypeRefs = + compType->ppTypeRefs; + sal_Int32 * memberOffsets = compType->pMemberOffsets; + + for ( sal_Int32 nPos = 0; nPos < nDescr; ++nPos ) + { + TypeDescriptionRef memberType( ppTypeRefs[ nPos ] ); + if (!memberType.is()) + return std::nullopt; + + std::optional<size_t> tmpHash = hashValue( hash, + static_cast< char const * >( + val ) + memberOffsets[ nPos ], + memberType->pWeakRef ); + if(!tmpHash.has_value()) + return std::nullopt; + hash = *tmpHash; + } + break; + } + case typelib_TypeClass_SEQUENCE: { + TypeDescriptionRef typeDescr( typeRef ); + if (!typeDescr.is()) + return std::nullopt; + + typelib_TypeDescriptionReference * elementTypeRef = + reinterpret_cast< + typelib_IndirectTypeDescription * >(typeDescr.get())->pType; + TypeDescriptionRef elementTypeDescr( elementTypeRef ); + if (!elementTypeDescr.is()) + return std::nullopt; + + sal_Int32 nElementSize = elementTypeDescr->nSize; + uno_Sequence * seq = + *static_cast< uno_Sequence * const * >(val); + sal_Int32 nElements = seq->nElements; + + if (nElements > 0) + { + char const * pElements = seq->elements; + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + std::optional<size_t> tmpHash = hashValue( hash, + pElements + (nElementSize * nPos), + elementTypeDescr->pWeakRef ); + if(!tmpHash.has_value()) + return std::nullopt; + hash = *tmpHash; + } + } + break; + } + case typelib_TypeClass_ANY: { + uno_Any const * pAny = static_cast< uno_Any const * >(val); + return hashValue( hash, pAny->pData, pAny->pType ); + } + case typelib_TypeClass_TYPE: { + OUString const & str = OUString::unacquired( + &(*static_cast< + typelib_TypeDescriptionReference * const * >(val) + )->pTypeName ); + o3tl::hash_combine( hash, str.hashCode() ); + break; + } + case typelib_TypeClass_STRING: { + OUString const & str = OUString::unacquired( + static_cast< rtl_uString * const * >(val) ); + o3tl::hash_combine( hash, str.hashCode() ); + break; + } + case typelib_TypeClass_ENUM: { + TypeDescription typeDescr( typeRef ); + if (!typeDescr.is()) + typeDescr.makeComplete(); + if (!typeDescr.is()) + return std::nullopt; + + o3tl::hash_combine( hash, *static_cast< int const * >(val)); + break; + } + case typelib_TypeClass_BOOLEAN: + if (*static_cast< sal_Bool const * >(val)) + o3tl::hash_combine( hash, true ); + else + o3tl::hash_combine( hash, false ); + break; + case typelib_TypeClass_CHAR: { + o3tl::hash_combine( hash, *static_cast< sal_Unicode const * >(val)); + break; + } + case typelib_TypeClass_FLOAT: + o3tl::hash_combine( hash, *static_cast< float const * >(val) ); + break; + case typelib_TypeClass_DOUBLE: + o3tl::hash_combine( hash, *static_cast< double const * >(val) ); + break; + case typelib_TypeClass_BYTE: + o3tl::hash_combine( hash, *static_cast< sal_Int8 const * >(val) ); + break; + case typelib_TypeClass_SHORT: + o3tl::hash_combine( hash, *static_cast< sal_Int16 const * >(val) ); + break; + case typelib_TypeClass_UNSIGNED_SHORT: + o3tl::hash_combine( hash, *static_cast< sal_uInt16 const * >(val) ); + break; + case typelib_TypeClass_LONG: + o3tl::hash_combine( hash, *static_cast< sal_Int32 const * >(val) ); + break; + case typelib_TypeClass_UNSIGNED_LONG: + o3tl::hash_combine( hash, *static_cast< sal_uInt32 const * >(val) ); + break; + case typelib_TypeClass_HYPER: + o3tl::hash_combine( hash, *static_cast< sal_Int64 const * >(val) ); + break; + case typelib_TypeClass_UNSIGNED_HYPER: + o3tl::hash_combine( hash, *static_cast< sal_uInt64 const * >(val) ); + break; +// case typelib_TypeClass_UNKNOWN: +// case typelib_TypeClass_SERVICE: +// case typelib_TypeClass_MODULE: + default: + return std::nullopt; + } + return hash; +} + +} // anon namespace + + +std::optional<size_t> anyToHash( uno::Any const & value ) +{ + size_t hash = 0; + return hashValue( hash, value.getValue(), value.getValueTypeRef()); +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/anytostring.cxx b/comphelper/source/misc/anytostring.cxx new file mode 100644 index 000000000..ebc338b0b --- /dev/null +++ b/comphelper/source/misc/anytostring.cxx @@ -0,0 +1,316 @@ +/* -*- 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 <comphelper/anytostring.hxx> +#include <rtl/ustrbuf.hxx> +#include <typelib/typedescription.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include "typedescriptionref.hxx" + +using namespace ::com::sun::star; +using ::com::sun::star::uno::TypeDescription; +using ::comphelper::detail::TypeDescriptionRef; + +namespace comphelper { +namespace { + +void appendTypeError( + OUStringBuffer & buf, const typelib_TypeDescriptionReference * typeRef ) +{ + buf.append( "<cannot get type description of type " ); + buf.append( OUString::unacquired( &typeRef->pTypeName ) ); + buf.append( '>' ); +} + +void appendChar( OUStringBuffer & buf, sal_Unicode c ) +{ + if (c < ' ' || c > '~') { + buf.append( "\\X" ); + OUString const s( + OUString::number( static_cast< sal_Int32 >(c), 16 ) ); + for ( sal_Int32 f = 4 - s.getLength(); f > 0; --f ) + buf.append( '0' ); + buf.append( s ); + } + else { + buf.append( c ); + } +} + + +void appendValue( OUStringBuffer & buf, + void const * val, typelib_TypeDescriptionReference * typeRef, + bool prependType ) +{ + if (typeRef->eTypeClass == typelib_TypeClass_VOID) { + buf.append( "void" ); + return; + } + assert(val != nullptr); + + if (prependType && + typeRef->eTypeClass != typelib_TypeClass_STRING && + typeRef->eTypeClass != typelib_TypeClass_CHAR && + typeRef->eTypeClass != typelib_TypeClass_BOOLEAN) + { + buf.append( '(' ); + buf.append( OUString::unacquired( &typeRef->pTypeName ) ); + buf.append( ") " ); + } + + switch (typeRef->eTypeClass) { + case typelib_TypeClass_INTERFACE: { + buf.append( '@' ); + buf.append( reinterpret_cast< sal_Int64 >( + *static_cast< void * const * >(val) ), 16 ); + uno::Reference< lang::XServiceInfo > xServiceInfo( + *static_cast< uno::XInterface * const * >(val), + uno::UNO_QUERY ); + if (xServiceInfo.is()) { + buf.append( " (ImplementationName = \"" ); + buf.append( xServiceInfo->getImplementationName() ); + buf.append( "\")" ); + } + break; + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: { + buf.append( "{ " ); + TypeDescription typeDescr( typeRef ); + if (!typeDescr.is()) + typeDescr.makeComplete(); + if (!typeDescr.is()) { + appendTypeError( buf, typeRef ); + } + else { + typelib_CompoundTypeDescription * compType = + reinterpret_cast< typelib_CompoundTypeDescription * >( + typeDescr.get() ); + sal_Int32 nDescr = compType->nMembers; + + if (compType->pBaseTypeDescription) { + appendValue( + buf, val, reinterpret_cast< + typelib_TypeDescription * >( + compType->pBaseTypeDescription)->pWeakRef, false ); + if (nDescr > 0) + buf.append( ", " ); + } + + typelib_TypeDescriptionReference ** ppTypeRefs = + compType->ppTypeRefs; + sal_Int32 * memberOffsets = compType->pMemberOffsets; + rtl_uString ** ppMemberNames = compType->ppMemberNames; + + for ( sal_Int32 nPos = 0; nPos < nDescr; ++nPos ) + { + buf.append( ppMemberNames[ nPos ] ); + buf.append( " = " ); + TypeDescriptionRef memberType( ppTypeRefs[ nPos ] ); + if (!memberType.is()) { + appendTypeError( buf, ppTypeRefs[ nPos ] ); + } + else { + appendValue( buf, + static_cast< char const * >( + val ) + memberOffsets[ nPos ], + memberType->pWeakRef, true ); + } + if (nPos < (nDescr - 1)) + buf.append( ", " ); + } + } + buf.append( " }" ); + break; + } + case typelib_TypeClass_SEQUENCE: { + TypeDescriptionRef typeDescr( typeRef ); + if (!typeDescr.is()) { + appendTypeError( buf,typeRef ); + } + else { + typelib_TypeDescriptionReference * elementTypeRef = + reinterpret_cast< + typelib_IndirectTypeDescription * >(typeDescr.get())->pType; + TypeDescriptionRef elementTypeDescr( elementTypeRef ); + if (!elementTypeDescr.is()) + { + appendTypeError( buf, elementTypeRef ); + } + else + { + sal_Int32 nElementSize = elementTypeDescr->nSize; + uno_Sequence * seq = + *static_cast< uno_Sequence * const * >(val); + sal_Int32 nElements = seq->nElements; + + if (nElements > 0) + { + buf.append( "{ " ); + char const * pElements = seq->elements; + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + appendValue( + buf, pElements + (nElementSize * nPos), + elementTypeDescr->pWeakRef, false ); + if (nPos < (nElements - 1)) + buf.append( ", " ); + } + buf.append( " }" ); + } + else + { + buf.append( "{}" ); + } + } + } + break; + } + case typelib_TypeClass_ANY: { + buf.append( "{ " ); + uno_Any const * pAny = static_cast< uno_Any const * >(val); + appendValue( buf, pAny->pData, pAny->pType, true ); + buf.append( " }" ); + break; + } + case typelib_TypeClass_TYPE: + buf.append( (*static_cast< + typelib_TypeDescriptionReference * const * >(val) + )->pTypeName ); + break; + case typelib_TypeClass_STRING: { + buf.append( '\"' ); + OUString const & str = OUString::unacquired( + static_cast< rtl_uString * const * >(val) ); + sal_Int32 len = str.getLength(); + for ( sal_Int32 pos = 0; pos < len; ++pos ) + { + sal_Unicode c = str[ pos ]; + if (c == '\"') + buf.append( "\\\"" ); + else if (c == '\\') + buf.append( "\\\\" ); + else + appendChar( buf, c ); + } + buf.append( '\"' ); + break; + } + case typelib_TypeClass_ENUM: { + TypeDescription typeDescr( typeRef ); + if (!typeDescr.is()) + typeDescr.makeComplete(); + if (!typeDescr.is()) { + appendTypeError( buf, typeRef ); + } + else + { + sal_Int32 * pValues = + reinterpret_cast< typelib_EnumTypeDescription * >( + typeDescr.get() )->pEnumValues; + sal_Int32 nPos = reinterpret_cast< typelib_EnumTypeDescription * >( + typeDescr.get() )->nEnumValues; + while (nPos--) + { + if (pValues[ nPos ] == *static_cast< int const * >(val)) + break; + } + if (nPos >= 0) + { + buf.append( reinterpret_cast< typelib_EnumTypeDescription * >( + typeDescr.get() )->ppEnumNames[ nPos ] ); + } + else + { + buf.append( "?unknown enum value?" ); + } + } + break; + } + case typelib_TypeClass_BOOLEAN: + if (*static_cast< sal_Bool const * >(val)) + buf.append( "true" ); + else + buf.append( "false" ); + break; + case typelib_TypeClass_CHAR: { + buf.append( '\'' ); + sal_Unicode c = *static_cast< sal_Unicode const * >(val); + if (c == '\'') + buf.append( "\\\'" ); + else if (c == '\\') + buf.append( "\\\\" ); + else + appendChar( buf, c ); + buf.append( '\'' ); + break; + } + case typelib_TypeClass_FLOAT: + buf.append( *static_cast< float const * >(val) ); + break; + case typelib_TypeClass_DOUBLE: + buf.append( *static_cast< double const * >(val) ); + break; + case typelib_TypeClass_BYTE: + buf.append( static_cast< sal_Int32 >( + *static_cast< sal_Int8 const * >(val) ) ); + break; + case typelib_TypeClass_SHORT: + buf.append( static_cast< sal_Int32 >( + *static_cast< sal_Int16 const * >(val) ) ); + break; + case typelib_TypeClass_UNSIGNED_SHORT: + buf.append( static_cast< sal_Int32 >( + *static_cast< sal_uInt16 const * >(val) ) ); + break; + case typelib_TypeClass_LONG: + buf.append( *static_cast< sal_Int32 const * >(val) ); + break; + case typelib_TypeClass_UNSIGNED_LONG: + buf.append( static_cast< sal_Int64 >( + *static_cast< sal_uInt32 const * >(val) ) ); + break; + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + buf.append( *static_cast< sal_Int64 const * >(val) ); + break; +// case typelib_TypeClass_UNKNOWN: +// case typelib_TypeClass_SERVICE: +// case typelib_TypeClass_MODULE: + default: + buf.append( '?' ); + break; + } +} + +} // anon namespace + + +OUString anyToString( uno::Any const & value ) +{ + OUStringBuffer buf; + appendValue( buf, value.getValue(), value.getValueTypeRef(), true ); + return buf.makeStringAndClear(); +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/asyncnotification.cxx b/comphelper/source/misc/asyncnotification.cxx new file mode 100644 index 000000000..cb8a2f251 --- /dev/null +++ b/comphelper/source/misc/asyncnotification.cxx @@ -0,0 +1,259 @@ +/* -*- 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 <comphelper/asyncnotification.hxx> +#include <comphelper/scopeguard.hxx> +#include <mutex> +#include <condition_variable> + +#include <cassert> +#include <stdexcept> +#include <vector> +#include <algorithm> + +namespace comphelper +{ + AnyEvent::AnyEvent() + { + } + + AnyEvent::~AnyEvent() + { + } + + namespace { + + struct ProcessableEvent + { + AnyEventRef aEvent; + ::rtl::Reference< IEventProcessor > xProcessor; + }; + + struct EqualProcessor + { + const ::rtl::Reference< IEventProcessor >& rProcessor; + explicit EqualProcessor( const ::rtl::Reference< IEventProcessor >& _rProcessor ) :rProcessor( _rProcessor ) { } + + bool operator()( const ProcessableEvent& _rEvent ) + { + return _rEvent.xProcessor.get() == rProcessor.get(); + } + }; + + } + + struct EventNotifierImpl + { + std::mutex aMutex; + std::condition_variable aPendingActions; + std::vector< ProcessableEvent > aEvents; + bool bTerminate; + // only used for AsyncEventNotifierAutoJoin + char const* name; + std::shared_ptr<AsyncEventNotifierAutoJoin> pKeepThisAlive; + + EventNotifierImpl() + : bTerminate(false) + , name(nullptr) + { + } + }; + + AsyncEventNotifierBase::AsyncEventNotifierBase() + : m_xImpl(new EventNotifierImpl) + { + } + + + AsyncEventNotifierBase::~AsyncEventNotifierBase() + { + } + + + void AsyncEventNotifierBase::removeEventsForProcessor( const ::rtl::Reference< IEventProcessor >& _xProcessor ) + { + std::scoped_lock aGuard( m_xImpl->aMutex ); + + // remove all events for this processor + m_xImpl->aEvents.erase(std::remove_if( m_xImpl->aEvents.begin(), m_xImpl->aEvents.end(), EqualProcessor( _xProcessor ) ), m_xImpl->aEvents.end()); + } + + + void SAL_CALL AsyncEventNotifierBase::terminate() + { + std::scoped_lock aGuard( m_xImpl->aMutex ); + + // remember the termination request + m_xImpl->bTerminate = true; + + // awake the thread + m_xImpl->aPendingActions.notify_all(); + } + + + void AsyncEventNotifierBase::addEvent( const AnyEventRef& _rEvent, const ::rtl::Reference< IEventProcessor >& _xProcessor ) + { + std::scoped_lock aGuard( m_xImpl->aMutex ); + + // remember this event + m_xImpl->aEvents.emplace_back( ProcessableEvent {_rEvent, _xProcessor} ); + + // awake the thread + m_xImpl->aPendingActions.notify_all(); + } + + + void AsyncEventNotifierBase::execute() + { + for (;;) + { + std::vector< ProcessableEvent > aEvents; + { + std::unique_lock aGuard(m_xImpl->aMutex); + m_xImpl->aPendingActions.wait(aGuard, + [this] { return m_xImpl->bTerminate || !m_xImpl->aEvents.empty(); } ); + if (m_xImpl->bTerminate) + return; + else + std::swap(aEvents, m_xImpl->aEvents); + } + for (ProcessableEvent& rEvent : aEvents) + { + assert(rEvent.xProcessor.is()); + rEvent.xProcessor->processEvent(*rEvent.aEvent); + } + aEvents.clear(); + } + } + + AsyncEventNotifier::AsyncEventNotifier(char const* name) + : salhelper::Thread(name) + { + } + + AsyncEventNotifier::~AsyncEventNotifier() + { + } + + void AsyncEventNotifier::execute() + { + return AsyncEventNotifierBase::execute(); + } + + void AsyncEventNotifier::terminate() + { + return AsyncEventNotifierBase::terminate(); + } + + namespace { + + std::mutex& GetTheNotifiersMutex() + { + static std::mutex MUTEX; + return MUTEX; + } + + } + + static std::vector<std::weak_ptr<AsyncEventNotifierAutoJoin>> g_Notifiers; + + void JoinAsyncEventNotifiers() + { + std::vector<std::weak_ptr<AsyncEventNotifierAutoJoin>> notifiers; + { + std::scoped_lock g(GetTheNotifiersMutex()); + notifiers = g_Notifiers; + } + for (std::weak_ptr<AsyncEventNotifierAutoJoin> const& wNotifier : notifiers) + { + std::shared_ptr<AsyncEventNotifierAutoJoin> const pNotifier( + wNotifier.lock()); + if (pNotifier) + { + pNotifier->terminate(); + pNotifier->join(); + } + } + // note it's possible that g_Notifiers isn't empty now in case of leaks, + // particularly since the UNO service manager isn't disposed yet + } + + AsyncEventNotifierAutoJoin::AsyncEventNotifierAutoJoin(char const* name) + { + m_xImpl->name = name; + } + + AsyncEventNotifierAutoJoin::~AsyncEventNotifierAutoJoin() + { + std::scoped_lock g(GetTheNotifiersMutex()); + // note: this doesn't happen atomically with the refcount + // hence it's possible this deletes > 1 or 0 elements + g_Notifiers.erase( + std::remove_if(g_Notifiers.begin(), g_Notifiers.end(), + [](std::weak_ptr<AsyncEventNotifierAutoJoin> const& w) { + return w.expired(); + } ), + g_Notifiers.end()); + } + + std::shared_ptr<AsyncEventNotifierAutoJoin> + AsyncEventNotifierAutoJoin::newAsyncEventNotifierAutoJoin(char const* name) + { + std::shared_ptr<AsyncEventNotifierAutoJoin> const ret( + new AsyncEventNotifierAutoJoin(name)); + std::scoped_lock g(GetTheNotifiersMutex()); + g_Notifiers.push_back(ret); + return ret; + } + + void AsyncEventNotifierAutoJoin::terminate() + { + return AsyncEventNotifierBase::terminate(); + } + + void AsyncEventNotifierAutoJoin::launch(std::shared_ptr<AsyncEventNotifierAutoJoin> const& xThis) + { + // see salhelper::Thread::launch + xThis->m_xImpl->pKeepThisAlive = xThis; + comphelper::ScopeGuard g([&xThis] { xThis->m_xImpl->pKeepThisAlive.reset(); }); + if (!xThis->create()) { + throw std::runtime_error("osl::Thread::create failed"); + } + g.dismiss(); + } + + void AsyncEventNotifierAutoJoin::run() + { + // see salhelper::Thread::run + comphelper::ScopeGuard g([this] { onTerminated(); }); + setName(m_xImpl->name); + execute(); + g.dismiss(); + } + + void AsyncEventNotifierAutoJoin::onTerminated() + { + // try to delete "this" + m_xImpl->pKeepThisAlive.reset(); + } + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/asyncquithandler.cxx b/comphelper/source/misc/asyncquithandler.cxx new file mode 100644 index 000000000..a04534ec9 --- /dev/null +++ b/comphelper/source/misc/asyncquithandler.cxx @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/frame/Desktop.hpp> +#include <com/sun/star/frame/XDesktop2.hpp> +#include <com/sun/star/uno/Reference.hxx> + +#include <comphelper/asyncquithandler.hxx> +#include <comphelper/processfactory.hxx> + +AsyncQuitHandler::AsyncQuitHandler() {} + +AsyncQuitHandler& AsyncQuitHandler::instance() +{ + static AsyncQuitHandler aInst; + return aInst; +} + +void AsyncQuitHandler::QuitApplication() +{ + css::uno::Reference<css::frame::XDesktop2> xDesktop + = css::frame::Desktop::create(comphelper::getProcessComponentContext()); + xDesktop->terminate(); +} + +IMPL_STATIC_LINK_NOARG(AsyncQuitHandler, OnAsyncQuit, void*, void) { QuitApplication(); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/automationinvokedzone.cxx b/comphelper/source/misc/automationinvokedzone.cxx new file mode 100644 index 000000000..4a71c4a51 --- /dev/null +++ b/comphelper/source/misc/automationinvokedzone.cxx @@ -0,0 +1,33 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include <cassert> + +#include <comphelper/automationinvokedzone.hxx> + +namespace comphelper::Automation +{ +thread_local static int nActiveount = 0; + +bool AutomationInvokedZone::isActive() { return nActiveount > 0; } + +AutomationInvokedZone::AutomationInvokedZone() +{ + assert(nActiveount < 1000); + nActiveount++; +} + +AutomationInvokedZone::~AutomationInvokedZone() +{ + assert(nActiveount > 0); + nActiveount--; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/backupfilehelper.cxx b/comphelper/source/misc/backupfilehelper.cxx new file mode 100644 index 000000000..e9b173a47 --- /dev/null +++ b/comphelper/source/misc/backupfilehelper.cxx @@ -0,0 +1,2504 @@ +/* -*- 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/. + */ + +#include <sal/config.h> +#include <rtl/ustring.hxx> +#include <rtl/bootstrap.hxx> +#include <sal/log.hxx> +#include <osl/file.hxx> +#include <comphelper/backupfilehelper.hxx> +#include <comphelper/DirectoryHelper.hxx> +#include <rtl/crc.h> +#include <algorithm> +#include <deque> +#include <memory> +#include <string_view> +#include <utility> +#include <vector> +#include <zlib.h> + +#include <comphelper/processfactory.hxx> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/ucb/CommandAbortedException.hpp> +#include <com/sun/star/ucb/CommandFailedException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/deployment/DeploymentException.hpp> +#include <com/sun/star/deployment/ExtensionManager.hpp> +#include <com/sun/star/xml/dom/XDocumentBuilder.hpp> +#include <com/sun/star/xml/dom/DocumentBuilder.hpp> +#include <com/sun/star/xml/dom/XElement.hpp> +#include <com/sun/star/xml/dom/XNodeList.hpp> +#include <com/sun/star/xml/dom/XText.hpp> +#include <com/sun/star/xml/sax/XSAXSerializable.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> +#include <com/sun/star/xml/sax/XWriter.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <cppuhelper/exc_hlp.hxx> + +using namespace comphelper; +using namespace css; +using namespace css::xml::dom; + +const sal_uInt32 BACKUP_FILE_HELPER_BLOCK_SIZE = 16384; + +namespace +{ + typedef std::shared_ptr< osl::File > FileSharedPtr; + + sal_uInt32 createCrc32(FileSharedPtr const & rCandidate, sal_uInt32 nOffset) + { + sal_uInt32 nCrc32(0); + + if (rCandidate && osl::File::E_None == rCandidate->open(osl_File_OpenFlag_Read)) + { + sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE]; + sal_uInt64 nBytesTransfer(0); + sal_uInt64 nSize(0); + + rCandidate->getSize(nSize); + + // set offset in source file - should be zero due to crc32 should + // only be needed to be created for new entries, gets loaded with old + // ones + if (osl::File::E_None == rCandidate->setPos(osl_Pos_Absolut, sal_Int64(nOffset))) + { + while (nSize != 0) + { + const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE))); + + if (osl::File::E_None == rCandidate->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) && nBytesTransfer == nToTransfer) + { + // add to crc and reduce size + nCrc32 = rtl_crc32(nCrc32, static_cast<void*>(aArray), static_cast<sal_uInt32>(nBytesTransfer)); + nSize -= nToTransfer; + } + else + { + // error - reset to zero again + nSize = nCrc32 = 0; + } + } + } + + rCandidate->close(); + } + + return nCrc32; + } + + bool read_sal_uInt32(FileSharedPtr const & rFile, sal_uInt32& rTarget) + { + sal_uInt8 aArray[4]; + sal_uInt64 nBaseRead(0); + + // read rTarget + if (osl::File::E_None == rFile->read(static_cast<void*>(aArray), 4, nBaseRead) && 4 == nBaseRead) + { + rTarget = (sal_uInt32(aArray[0]) << 24) + (sal_uInt32(aArray[1]) << 16) + (sal_uInt32(aArray[2]) << 8) + sal_uInt32(aArray[3]); + return true; + } + + return false; + } + + bool write_sal_uInt32(oslFileHandle& rHandle, sal_uInt32 nSource) + { + sal_uInt8 aArray[4]; + sal_uInt64 nBaseWritten(0); + + // write nSource + aArray[0] = sal_uInt8((nSource & 0xff000000) >> 24); + aArray[1] = sal_uInt8((nSource & 0x00ff0000) >> 16); + aArray[2] = sal_uInt8((nSource & 0x0000ff00) >> 8); + aArray[3] = sal_uInt8(nSource & 0x000000ff); + + return osl_File_E_None == osl_writeFile(rHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) && 4 == nBaseWritten; + } + + bool read_OString(FileSharedPtr const & rFile, OString& rTarget) + { + sal_uInt32 nLength(0); + + if (!read_sal_uInt32(rFile, nLength)) + { + return false; + } + + sal_uInt64 nPos; + if (osl::File::E_None != rFile->getPos(nPos)) + return false; + + sal_uInt64 nSize; + if (osl::File::E_None != rFile->getSize(nSize)) + return false; + + const auto nRemainingSize = nSize - nPos; + if (nLength > nRemainingSize) + return false; + + std::vector<char> aTarget(nLength); + sal_uInt64 nBaseRead(0); + + // read rTarget + if (osl::File::E_None == rFile->read(static_cast<void*>(aTarget.data()), nLength, nBaseRead) && nLength == nBaseRead) + { + rTarget = OString(aTarget.data(), static_cast<sal_Int32>(nBaseRead)); + return true; + } + + return false; + } + + bool write_OString(oslFileHandle& rHandle, const OString& rSource) + { + const sal_uInt32 nLength(rSource.getLength()); + + if (!write_sal_uInt32(rHandle, nLength)) + { + return false; + } + + sal_uInt64 nBaseWritten(0); + + return osl_File_E_None == osl_writeFile(rHandle, static_cast<const void*>(rSource.getStr()), nLength, &nBaseWritten) && nLength == nBaseWritten; + } + + OUString createFileURL( + std::u16string_view rURL, std::u16string_view rName, std::u16string_view rExt) + { + OUString aRetval; + + if (!rURL.empty() && !rName.empty()) + { + aRetval = OUString::Concat(rURL) + "/" + rName; + + if (!rExt.empty()) + { + aRetval += OUString::Concat(".") + rExt; + } + } + + return aRetval; + } + + OUString createPackURL(std::u16string_view rURL, std::u16string_view rName) + { + OUString aRetval; + + if (!rURL.empty() && !rName.empty()) + { + aRetval = OUString::Concat(rURL) + "/" + rName + ".pack"; + } + + return aRetval; + } +} + +namespace +{ + enum PackageRepository { USER, SHARED, BUNDLED }; + + class ExtensionInfoEntry + { + private: + OString maName; // extension name + PackageRepository maRepository; // user|shared|bundled + bool mbEnabled; // state + + public: + ExtensionInfoEntry() + : maRepository(USER), + mbEnabled(false) + { + } + + ExtensionInfoEntry(OString aName, bool bEnabled) + : maName(std::move(aName)), + maRepository(USER), + mbEnabled(bEnabled) + { + } + + ExtensionInfoEntry(const uno::Reference< deployment::XPackage >& rxPackage) + : maName(OUStringToOString(rxPackage->getName(), RTL_TEXTENCODING_ASCII_US)), + maRepository(USER), + mbEnabled(false) + { + // check maRepository + const OString aRepName(OUStringToOString(rxPackage->getRepositoryName(), RTL_TEXTENCODING_ASCII_US)); + + if (aRepName == "shared") + { + maRepository = SHARED; + } + else if (aRepName == "bundled") + { + maRepository = BUNDLED; + } + + // check mbEnabled + const beans::Optional< beans::Ambiguous< sal_Bool > > option( + rxPackage->isRegistered(uno::Reference< task::XAbortChannel >(), + uno::Reference< ucb::XCommandEnvironment >())); + + if (option.IsPresent) + { + ::beans::Ambiguous< sal_Bool > const& reg = option.Value; + + if (!reg.IsAmbiguous) + { + mbEnabled = reg.Value; + } + } + } + + bool isSameExtension(const ExtensionInfoEntry& rComp) const + { + return (maRepository == rComp.maRepository && maName == rComp.maName); + } + + bool operator<(const ExtensionInfoEntry& rComp) const + { + if (maRepository == rComp.maRepository) + { + if (maName == rComp.maName) + { + return mbEnabled < rComp.mbEnabled; + } + else + { + return 0 > maName.compareTo(rComp.maName); + } + } + else + { + return maRepository < rComp.maRepository; + } + } + + bool read_entry(FileSharedPtr const & rFile) + { + // read maName + if (!read_OString(rFile, maName)) + { + return false; + } + + // read maRepository + sal_uInt32 nState(0); + + if (read_sal_uInt32(rFile, nState)) + { + maRepository = static_cast< PackageRepository >(nState); + } + else + { + return false; + } + + // read mbEnabled + if (read_sal_uInt32(rFile, nState)) + { + mbEnabled = static_cast< bool >(nState); + } + else + { + return false; + } + + return true; + } + + bool write_entry(oslFileHandle& rHandle) const + { + // write maName; + if (!write_OString(rHandle, maName)) + { + return false; + } + + // write maRepository + sal_uInt32 nState(maRepository); + + if (!write_sal_uInt32(rHandle, nState)) + { + return false; + } + + // write mbEnabled + nState = static_cast< sal_uInt32 >(mbEnabled); + + return write_sal_uInt32(rHandle, nState); + } + + const OString& getName() const + { + return maName; + } + + bool isEnabled() const + { + return mbEnabled; + } + }; + + typedef std::vector< ExtensionInfoEntry > ExtensionInfoEntryVector; + + constexpr OUStringLiteral gaRegPath { u"/registry/com.sun.star.comp.deployment.bundle.PackageRegistryBackend/backenddb.xml" }; + + class ExtensionInfo + { + private: + ExtensionInfoEntryVector maEntries; + + public: + ExtensionInfo() + { + } + + const ExtensionInfoEntryVector& getExtensionInfoEntryVector() const + { + return maEntries; + } + + void reset() + { + // clear all data + maEntries.clear(); + } + + void createUsingXExtensionManager() + { + // clear all data + reset(); + + // create content from current extension configuration + uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages; + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + uno::Reference< deployment::XExtensionManager > m_xExtensionManager = deployment::ExtensionManager::get(xContext); + + try + { + xAllPackages = m_xExtensionManager->getAllExtensions(uno::Reference< task::XAbortChannel >(), + uno::Reference< ucb::XCommandEnvironment >()); + } + catch (const deployment::DeploymentException &) + { + return; + } + catch (const ucb::CommandFailedException &) + { + return; + } + catch (const ucb::CommandAbortedException &) + { + return; + } + catch (const lang::IllegalArgumentException & e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( e.Message, + e.Context, anyEx ); + } + + for (const uno::Sequence< uno::Reference< deployment::XPackage > > & xPackageList : std::as_const(xAllPackages)) + { + for (const uno::Reference< deployment::XPackage > & xPackage : xPackageList) + { + if (xPackage.is()) + { + maEntries.emplace_back(xPackage); + } + } + } + + if (!maEntries.empty()) + { + // sort the list + std::sort(maEntries.begin(), maEntries.end()); + } + } + + private: + void visitNodesXMLRead(const uno::Reference< xml::dom::XElement >& rElement) + { + if (!rElement.is()) + return; + + const OUString aTagName(rElement->getTagName()); + + if (aTagName == "extension") + { + OUString aAttrUrl(rElement->getAttribute("url")); + const OUString aAttrRevoked(rElement->getAttribute("revoked")); + + if (!aAttrUrl.isEmpty()) + { + const sal_Int32 nIndex(aAttrUrl.lastIndexOf('/')); + + if (nIndex > 0 && aAttrUrl.getLength() > nIndex + 1) + { + aAttrUrl = aAttrUrl.copy(nIndex + 1); + } + + const bool bEnabled(aAttrRevoked.isEmpty() || !aAttrRevoked.toBoolean()); + maEntries.emplace_back( + OUStringToOString(aAttrUrl, RTL_TEXTENCODING_ASCII_US), + bEnabled); + } + } + else + { + uno::Reference< xml::dom::XNodeList > aList = rElement->getChildNodes(); + + if (aList.is()) + { + const sal_Int32 nLength(aList->getLength()); + + for (sal_Int32 a(0); a < nLength; a++) + { + const uno::Reference< xml::dom::XElement > aChild(aList->item(a), uno::UNO_QUERY); + + if (aChild.is()) + { + visitNodesXMLRead(aChild); + } + } + } + } + } + + public: + void createUserExtensionRegistryEntriesFromXML(std::u16string_view rUserConfigWorkURL) + { + const OUString aPath( + OUString::Concat(rUserConfigWorkURL) + "/uno_packages/cache" + gaRegPath); + createExtensionRegistryEntriesFromXML(aPath); + } + + void createSharedExtensionRegistryEntriesFromXML(std::u16string_view rUserConfigWorkURL) + { + const OUString aPath( + OUString::Concat(rUserConfigWorkURL) + "/extensions/shared" + gaRegPath); + createExtensionRegistryEntriesFromXML(aPath); + } + + void createBundledExtensionRegistryEntriesFromXML(std::u16string_view rUserConfigWorkURL) + { + const OUString aPath( + OUString::Concat(rUserConfigWorkURL) + "/extensions/bundled" + gaRegPath); + createExtensionRegistryEntriesFromXML(aPath); + } + + + void createExtensionRegistryEntriesFromXML(const OUString& aPath) + { + if (DirectoryHelper::fileExists(aPath)) + { + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + uno::Reference< xml::dom::XDocumentBuilder > xBuilder(xml::dom::DocumentBuilder::create(xContext)); + uno::Reference< xml::dom::XDocument > aDocument = xBuilder->parseURI(aPath); + + if (aDocument.is()) + { + visitNodesXMLRead(aDocument->getDocumentElement()); + } + } + + if (!maEntries.empty()) + { + // sort the list + std::sort(maEntries.begin(), maEntries.end()); + } + } + + private: + static bool visitNodesXMLChange( + const OUString& rTagToSearch, + const uno::Reference< xml::dom::XElement >& rElement, + const ExtensionInfoEntryVector& rToBeEnabled, + const ExtensionInfoEntryVector& rToBeDisabled) + { + bool bChanged(false); + + if (rElement.is()) + { + const OUString aTagName(rElement->getTagName()); + + if (aTagName == rTagToSearch) + { + const OString aAttrUrl(OUStringToOString(rElement->getAttribute("url"), RTL_TEXTENCODING_ASCII_US)); + const OUString aAttrRevoked(rElement->getAttribute("revoked")); + const bool bEnabled(aAttrRevoked.isEmpty() || !aAttrRevoked.toBoolean()); + + if (!aAttrUrl.isEmpty()) + { + for (const auto& enable : rToBeEnabled) + { + if (-1 != aAttrUrl.indexOf(enable.getName())) + { + if (!bEnabled) + { + // needs to be enabled + rElement->removeAttribute("revoked"); + bChanged = true; + } + } + } + + for (const auto& disable : rToBeDisabled) + { + if (-1 != aAttrUrl.indexOf(disable.getName())) + { + if (bEnabled) + { + // needs to be disabled + rElement->setAttribute("revoked", "true"); + bChanged = true; + } + } + } + } + } + else + { + uno::Reference< xml::dom::XNodeList > aList = rElement->getChildNodes(); + + if (aList.is()) + { + const sal_Int32 nLength(aList->getLength()); + + for (sal_Int32 a(0); a < nLength; a++) + { + const uno::Reference< xml::dom::XElement > aChild(aList->item(a), uno::UNO_QUERY); + + if (aChild.is()) + { + bChanged |= visitNodesXMLChange( + rTagToSearch, + aChild, + rToBeEnabled, + rToBeDisabled); + } + } + } + } + } + + return bChanged; + } + + static void visitNodesXMLChangeOneCase( + const OUString& rUnoPackagReg, + const OUString& rTagToSearch, + const ExtensionInfoEntryVector& rToBeEnabled, + const ExtensionInfoEntryVector& rToBeDisabled) + { + if (!DirectoryHelper::fileExists(rUnoPackagReg)) + return; + + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + uno::Reference< xml::dom::XDocumentBuilder > xBuilder = xml::dom::DocumentBuilder::create(xContext); + uno::Reference< xml::dom::XDocument > aDocument = xBuilder->parseURI(rUnoPackagReg); + + if (!aDocument.is()) + return; + + if (!visitNodesXMLChange( + rTagToSearch, + aDocument->getDocumentElement(), + rToBeEnabled, + rToBeDisabled)) + return; + + // did change - write back + uno::Reference< xml::sax::XSAXSerializable > xSerializer(aDocument, uno::UNO_QUERY); + + if (!xSerializer.is()) + return; + + // create a SAXWriter + uno::Reference< xml::sax::XWriter > const xSaxWriter = xml::sax::Writer::create(xContext); + uno::Reference< io::XTempFile > xTempFile = io::TempFile::create(xContext); + uno::Reference< io::XOutputStream > xOutStrm = xTempFile->getOutputStream(); + + // set output stream and do the serialization + xSaxWriter->setOutputStream(xOutStrm); + xSerializer->serialize(xSaxWriter, uno::Sequence< beans::StringPair >()); + + // get URL from temp file + OUString aTempURL = xTempFile->getUri(); + + // copy back file + if (aTempURL.isEmpty() || !DirectoryHelper::fileExists(aTempURL)) + return; + + if (DirectoryHelper::fileExists(rUnoPackagReg)) + { + osl::File::remove(rUnoPackagReg); + } + +#if OSL_DEBUG_LEVEL > 1 + SAL_WARN_IF(osl::FileBase::E_None != osl::File::move(aTempURL, rUnoPackagReg), "comphelper.backupfilehelper", "could not copy back modified Extension configuration file"); +#else + osl::File::move(aTempURL, rUnoPackagReg); +#endif + } + + public: + static void changeEnableDisableStateInXML( + std::u16string_view rUserConfigWorkURL, + const ExtensionInfoEntryVector& rToBeEnabled, + const ExtensionInfoEntryVector& rToBeDisabled) + { + static const OUStringLiteral aRegPathFront(u"/uno_packages/cache/registry/com.sun.star.comp.deployment."); + static const OUStringLiteral aRegPathBack(u".PackageRegistryBackend/backenddb.xml"); + // first appearance to check + { + const OUString aUnoPackagReg(OUString::Concat(rUserConfigWorkURL) + aRegPathFront + "bundle" + aRegPathBack); + + visitNodesXMLChangeOneCase( + aUnoPackagReg, + "extension", + rToBeEnabled, + rToBeDisabled); + } + + // second appearance to check + { + const OUString aUnoPackagReg(OUString::Concat(rUserConfigWorkURL) + aRegPathFront + "configuration" + aRegPathBack); + + visitNodesXMLChangeOneCase( + aUnoPackagReg, + "configuration", + rToBeEnabled, + rToBeDisabled); + } + + // third appearance to check + { + const OUString aUnoPackagReg(OUString::Concat(rUserConfigWorkURL) + aRegPathFront + "script" + aRegPathBack); + + visitNodesXMLChangeOneCase( + aUnoPackagReg, + "script", + rToBeEnabled, + rToBeDisabled); + } + } + + bool read_entries(FileSharedPtr const & rFile) + { + // read NumExtensionEntries + sal_uInt32 nExtEntries(0); + + if (!read_sal_uInt32(rFile, nExtEntries)) + { + return false; + } + + // coverity#1373663 Untrusted loop bound, check file size + // isn't utterly broken + sal_uInt64 nFileSize(0); + rFile->getSize(nFileSize); + if (nFileSize < nExtEntries) + return false; + + for (sal_uInt32 a(0); a < nExtEntries; a++) + { + ExtensionInfoEntry aNewEntry; + + if (aNewEntry.read_entry(rFile)) + { + maEntries.push_back(aNewEntry); + } + else + { + return false; + } + } + + return true; + } + + bool write_entries(oslFileHandle& rHandle) const + { + const sal_uInt32 nExtEntries(maEntries.size()); + + if (!write_sal_uInt32(rHandle, nExtEntries)) + { + return false; + } + + for (const auto& a : maEntries) + { + if (!a.write_entry(rHandle)) + { + return false; + } + } + + return true; + } + + bool createTempFile(OUString& rTempFileName) + { + oslFileHandle aHandle; + bool bRetval(false); + + // create current configuration + if (maEntries.empty()) + { + createUsingXExtensionManager(); + } + + // open target temp file and write current configuration to it - it exists until deleted + if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &rTempFileName)) + { + bRetval = write_entries(aHandle); + + // close temp file - it exists until deleted + osl_closeFile(aHandle); + } + + return bRetval; + } + + bool areThereEnabledExtensions() const + { + for (const auto& a : maEntries) + { + if (a.isEnabled()) + { + return true; + } + } + + return false; + } + }; +} + +namespace +{ + class PackedFileEntry + { + private: + sal_uInt32 mnFullFileSize; // size in bytes of unpacked original file + sal_uInt32 mnPackFileSize; // size in bytes in file backup package (smaller if compressed, same if not) + sal_uInt32 mnOffset; // offset in File (zero identifies new file) + sal_uInt32 mnCrc32; // checksum + FileSharedPtr maFile; // file where to find the data (at offset) + bool const mbDoCompress; // flag if this file is scheduled to be compressed when written + + bool copy_content_straight(oslFileHandle& rTargetHandle) + { + if (!maFile || osl::File::E_None != maFile->open(osl_File_OpenFlag_Read)) + return false; + + sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE]; + sal_uInt64 nBytesTransfer(0); + sal_uInt64 nSize(getPackFileSize()); + + // set offset in source file - when this is zero, a new file is to be added + if (osl::File::E_None == maFile->setPos(osl_Pos_Absolut, sal_Int64(getOffset()))) + { + while (nSize != 0) + { + const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE))); + + if (osl::File::E_None != maFile->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) || nBytesTransfer != nToTransfer) + { + break; + } + + if (osl_File_E_None != osl_writeFile(rTargetHandle, static_cast<const void*>(aArray), nToTransfer, &nBytesTransfer) || nBytesTransfer != nToTransfer) + { + break; + } + + nSize -= nToTransfer; + } + } + + maFile->close(); + return (0 == nSize); + } + + bool copy_content_compress(oslFileHandle& rTargetHandle) + { + if (!maFile || osl::File::E_None != maFile->open(osl_File_OpenFlag_Read)) + return false; + + sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE]; + sal_uInt8 aBuffer[BACKUP_FILE_HELPER_BLOCK_SIZE]; + sal_uInt64 nBytesTransfer(0); + sal_uInt64 nSize(getPackFileSize()); + z_stream zstream; + memset(&zstream, 0, sizeof(zstream)); + + if (Z_OK == deflateInit(&zstream, Z_BEST_COMPRESSION)) + { + // set offset in source file - when this is zero, a new file is to be added + if (osl::File::E_None == maFile->setPos(osl_Pos_Absolut, sal_Int64(getOffset()))) + { + bool bOkay(true); + + while (bOkay && nSize != 0) + { + const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE))); + + if (osl::File::E_None != maFile->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) || nBytesTransfer != nToTransfer) + { + break; + } + + zstream.avail_in = nToTransfer; + zstream.next_in = reinterpret_cast<unsigned char*>(aArray); + + do { + zstream.avail_out = BACKUP_FILE_HELPER_BLOCK_SIZE; + zstream.next_out = reinterpret_cast<unsigned char*>(aBuffer); +#if !defined Z_PREFIX + const sal_Int64 nRetval(deflate(&zstream, nSize == nToTransfer ? Z_FINISH : Z_NO_FLUSH)); +#else + const sal_Int64 nRetval(z_deflate(&zstream, nSize == nToTransfer ? Z_FINISH : Z_NO_FLUSH)); +#endif + if (Z_STREAM_ERROR == nRetval) + { + bOkay = false; + } + else + { + const sal_uInt64 nAvailable(BACKUP_FILE_HELPER_BLOCK_SIZE - zstream.avail_out); + + if (osl_File_E_None != osl_writeFile(rTargetHandle, static_cast<const void*>(aBuffer), nAvailable, &nBytesTransfer) || nBytesTransfer != nAvailable) + { + bOkay = false; + } + } + } while (bOkay && 0 == zstream.avail_out); + + if (!bOkay) + { + break; + } + + nSize -= nToTransfer; + } + +#if !defined Z_PREFIX + deflateEnd(&zstream); +#else + z_deflateEnd(&zstream); +#endif + } + } + + maFile->close(); + + // get compressed size and add to entry + if (mnFullFileSize == mnPackFileSize && mnFullFileSize == zstream.total_in) + { + mnPackFileSize = zstream.total_out; + } + + return (0 == nSize); + } + + bool copy_content_uncompress(oslFileHandle& rTargetHandle) + { + if (!maFile || osl::File::E_None != maFile->open(osl_File_OpenFlag_Read)) + return false; + + sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE]; + sal_uInt8 aBuffer[BACKUP_FILE_HELPER_BLOCK_SIZE]; + sal_uInt64 nBytesTransfer(0); + sal_uInt64 nSize(getPackFileSize()); + z_stream zstream; + memset(&zstream, 0, sizeof(zstream)); + + if (Z_OK == inflateInit(&zstream)) + { + // set offset in source file - when this is zero, a new file is to be added + if (osl::File::E_None == maFile->setPos(osl_Pos_Absolut, sal_Int64(getOffset()))) + { + bool bOkay(true); + + while (bOkay && nSize != 0) + { + const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE))); + + if (osl::File::E_None != maFile->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) || nBytesTransfer != nToTransfer) + { + break; + } + + zstream.avail_in = nToTransfer; + zstream.next_in = reinterpret_cast<unsigned char*>(aArray); + + do { + zstream.avail_out = BACKUP_FILE_HELPER_BLOCK_SIZE; + zstream.next_out = reinterpret_cast<unsigned char*>(aBuffer); +#if !defined Z_PREFIX + const sal_Int64 nRetval(inflate(&zstream, Z_NO_FLUSH)); +#else + const sal_Int64 nRetval(z_inflate(&zstream, Z_NO_FLUSH)); +#endif + if (Z_STREAM_ERROR == nRetval) + { + bOkay = false; + } + else + { + const sal_uInt64 nAvailable(BACKUP_FILE_HELPER_BLOCK_SIZE - zstream.avail_out); + + if (osl_File_E_None != osl_writeFile(rTargetHandle, static_cast<const void*>(aBuffer), nAvailable, &nBytesTransfer) || nBytesTransfer != nAvailable) + { + bOkay = false; + } + } + } while (bOkay && 0 == zstream.avail_out); + + if (!bOkay) + { + break; + } + + nSize -= nToTransfer; + } + +#if !defined Z_PREFIX + deflateEnd(&zstream); +#else + z_deflateEnd(&zstream); +#endif + } + } + + maFile->close(); + return (0 == nSize); + } + + + public: + // create new, uncompressed entry + PackedFileEntry( + sal_uInt32 nFullFileSize, + sal_uInt32 nCrc32, + FileSharedPtr xFile, + bool bDoCompress) + : mnFullFileSize(nFullFileSize), + mnPackFileSize(nFullFileSize), + mnOffset(0), + mnCrc32(nCrc32), + maFile(std::move(xFile)), + mbDoCompress(bDoCompress) + { + } + + // create entry to be loaded as header (read_header) + PackedFileEntry() + : mnFullFileSize(0), + mnPackFileSize(0), + mnOffset(0), + mnCrc32(0), + mbDoCompress(false) + { + } + + sal_uInt32 getFullFileSize() const + { + return mnFullFileSize; + } + + sal_uInt32 getPackFileSize() const + { + return mnPackFileSize; + } + + sal_uInt32 getOffset() const + { + return mnOffset; + } + + void setOffset(sal_uInt32 nOffset) + { + mnOffset = nOffset; + } + + static sal_uInt32 getEntrySize() + { + return 12; + } + + sal_uInt32 getCrc32() const + { + return mnCrc32; + } + + bool read_header(FileSharedPtr const & rFile) + { + if (!rFile) + { + return false; + } + + maFile = rFile; + + // read and compute full file size + if (!read_sal_uInt32(rFile, mnFullFileSize)) + { + return false; + } + + // read and compute entry crc32 + if (!read_sal_uInt32(rFile, mnCrc32)) + { + return false; + } + + // read and compute packed size + if (!read_sal_uInt32(rFile, mnPackFileSize)) + { + return false; + } + + return true; + } + + bool write_header(oslFileHandle& rHandle) const + { + // write full file size + if (!write_sal_uInt32(rHandle, mnFullFileSize)) + { + return false; + } + + // write crc32 + if (!write_sal_uInt32(rHandle, mnCrc32)) + { + return false; + } + + // write packed file size + if (!write_sal_uInt32(rHandle, mnPackFileSize)) + { + return false; + } + + return true; + } + + bool copy_content(oslFileHandle& rTargetHandle, bool bUncompress) + { + if (bUncompress) + { + if (getFullFileSize() == getPackFileSize()) + { + // not compressed, just copy + return copy_content_straight(rTargetHandle); + } + else + { + // compressed, need to uncompress on copy + return copy_content_uncompress(rTargetHandle); + } + } + else if (0 == getOffset()) + { + if (mbDoCompress) + { + // compressed wanted, need to compress on copy + return copy_content_compress(rTargetHandle); + } + else + { + // not compressed, straight copy + return copy_content_straight(rTargetHandle); + } + } + else + { + return copy_content_straight(rTargetHandle); + } + } + }; +} + +namespace +{ + class PackedFile + { + private: + const OUString maURL; + std::deque< PackedFileEntry > + maPackedFileEntryVector; + bool mbChanged; + + public: + PackedFile(const OUString& rURL) + : maURL(rURL), + mbChanged(false) + { + FileSharedPtr aSourceFile = std::make_shared<osl::File>(rURL); + + if (osl::File::E_None == aSourceFile->open(osl_File_OpenFlag_Read)) + { + sal_uInt64 nBaseLen(0); + aSourceFile->getSize(nBaseLen); + + // we need at least File_ID and num entries -> 8byte + if (8 < nBaseLen) + { + sal_uInt8 aArray[4]; + sal_uInt64 nBaseRead(0); + + // read and check File_ID + if (osl::File::E_None == aSourceFile->read(static_cast< void* >(aArray), 4, nBaseRead) && 4 == nBaseRead) + { + if ('P' == aArray[0] && 'A' == aArray[1] && 'C' == aArray[2] && 'K' == aArray[3]) + { + // read and compute num entries in this file + if (osl::File::E_None == aSourceFile->read(static_cast<void*>(aArray), 4, nBaseRead) && 4 == nBaseRead) + { + sal_uInt32 nEntries((sal_uInt32(aArray[0]) << 24) + (sal_uInt32(aArray[1]) << 16) + (sal_uInt32(aArray[2]) << 8) + sal_uInt32(aArray[3])); + + // if there are entries (and less than max), read them + if (nEntries >= 1 && nEntries <= 10) + { + for (sal_uInt32 a(0); a < nEntries; a++) + { + // create new entry, read header (size, crc and PackedSize), + // set offset and source file + PackedFileEntry aEntry; + + if (aEntry.read_header(aSourceFile)) + { + // add to local data + maPackedFileEntryVector.push_back(aEntry); + } + else + { + // error + nEntries = 0; + } + } + + if (0 == nEntries) + { + // on read error clear local data + maPackedFileEntryVector.clear(); + } + else + { + // calculate and set offsets to file binary content + sal_uInt32 nHeaderSize(8); + + nHeaderSize += maPackedFileEntryVector.size() * PackedFileEntry::getEntrySize(); + + sal_uInt32 nOffset(nHeaderSize); + + for (auto& b : maPackedFileEntryVector) + { + b.setOffset(nOffset); + nOffset += b.getPackFileSize(); + } + } + } + } + } + } + } + + aSourceFile->close(); + } + + if (maPackedFileEntryVector.empty()) + { + // on error or no data get rid of pack file + osl::File::remove(maURL); + } + } + + void flush() + { + bool bRetval(true); + + if (maPackedFileEntryVector.empty()) + { + // get rid of (now?) empty pack file + osl::File::remove(maURL); + } + else if (mbChanged) + { + // need to create a new pack file, do this in a temp file to which data + // will be copied from local file (so keep it here until this is done) + oslFileHandle aHandle = nullptr; + OUString aTempURL; + + // open target temp file - it exists until deleted + if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL)) + { + sal_uInt8 aArray[4]; + sal_uInt64 nBaseWritten(0); + + aArray[0] = 'P'; + aArray[1] = 'A'; + aArray[2] = 'C'; + aArray[3] = 'K'; + + // write File_ID + if (osl_File_E_None == osl_writeFile(aHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) && 4 == nBaseWritten) + { + const sal_uInt32 nSize(maPackedFileEntryVector.size()); + + // write number of entries + if (write_sal_uInt32(aHandle, nSize)) + { + // write placeholder for headers. Due to the fact that + // PackFileSize for newly added files gets set during + // writing the content entry, write headers after content + // is written. To do so, write placeholders here + sal_uInt32 nWriteSize(0); + + nWriteSize += maPackedFileEntryVector.size() * PackedFileEntry::getEntrySize(); + + aArray[0] = aArray[1] = aArray[2] = aArray[3] = 0; + + for (sal_uInt32 a(0); bRetval && a < nWriteSize; a++) + { + if (osl_File_E_None != osl_writeFile(aHandle, static_cast<const void*>(aArray), 1, &nBaseWritten) || 1 != nBaseWritten) + { + bRetval = false; + } + } + + if (bRetval) + { + // write contents - this may adapt PackFileSize for new + // files + for (auto& candidate : maPackedFileEntryVector) + { + if (!candidate.copy_content(aHandle, false)) + { + bRetval = false; + break; + } + } + } + + if (bRetval) + { + // seek back to header start (at position 8) + if (osl_File_E_None != osl_setFilePos(aHandle, osl_Pos_Absolut, sal_Int64(8))) + { + bRetval = false; + } + } + + if (bRetval) + { + // write headers + for (const auto& candidate : maPackedFileEntryVector) + { + if (!candidate.write_header(aHandle)) + { + // error + bRetval = false; + break; + } + } + } + } + } + } + + // close temp file (in all cases) - it exists until deleted + osl_closeFile(aHandle); + + if (bRetval) + { + // copy over existing file by first deleting original + // and moving the temp file to old original + osl::File::remove(maURL); + osl::File::move(aTempURL, maURL); + } + + // delete temp file (in all cases - it may be moved already) + osl::File::remove(aTempURL); + } + } + + bool tryPush(FileSharedPtr const & rFileCandidate, bool bCompress) + { + sal_uInt64 nFileSize(0); + + if (rFileCandidate && osl::File::E_None == rFileCandidate->open(osl_File_OpenFlag_Read)) + { + rFileCandidate->getSize(nFileSize); + rFileCandidate->close(); + } + + if (0 == nFileSize) + { + // empty file offered + return false; + } + + bool bNeedToAdd(false); + sal_uInt32 nCrc32(0); + + if (maPackedFileEntryVector.empty()) + { + // no backup yet, add as 1st backup + bNeedToAdd = true; + } + else + { + // already backups there, check if different from last entry + const PackedFileEntry& aLastEntry = maPackedFileEntryVector.back(); + + // check if file is different + if (aLastEntry.getFullFileSize() != static_cast<sal_uInt32>(nFileSize)) + { + // different size, different file + bNeedToAdd = true; + } + else + { + // same size, check crc32 + nCrc32 = createCrc32(rFileCandidate, 0); + + if (nCrc32 != aLastEntry.getCrc32()) + { + // different crc, different file + bNeedToAdd = true; + } + } + } + + if (bNeedToAdd) + { + // create crc32 if not yet done + if (0 == nCrc32) + { + nCrc32 = createCrc32(rFileCandidate, 0); + } + + // create a file entry for a new file. Offset is set automatically + // to 0 to mark the entry as new file entry + maPackedFileEntryVector.emplace_back( + static_cast< sal_uInt32 >(nFileSize), + nCrc32, + rFileCandidate, + bCompress); + + mbChanged = true; + } + + return bNeedToAdd; + } + + bool tryPop(oslFileHandle& rHandle) + { + if (maPackedFileEntryVector.empty()) + return false; + + // already backups there, check if different from last entry + PackedFileEntry& aLastEntry = maPackedFileEntryVector.back(); + + // here the uncompress flag has to be determined, true + // means to add the file compressed, false means to add it + // uncompressed + bool bRetval = aLastEntry.copy_content(rHandle, true); + + if (bRetval) + { + maPackedFileEntryVector.pop_back(); + mbChanged = true; + } + + return bRetval; + } + + void tryReduceToNumBackups(sal_uInt16 nNumBackups) + { + while (maPackedFileEntryVector.size() > nNumBackups) + { + maPackedFileEntryVector.pop_front(); + mbChanged = true; + } + } + + bool empty() const + { + return maPackedFileEntryVector.empty(); + } + }; +} + +namespace comphelper +{ + sal_uInt16 BackupFileHelper::mnMaxAllowedBackups = 10; + bool BackupFileHelper::mbExitWasCalled = false; + bool BackupFileHelper::mbSafeModeDirExists = false; + OUString BackupFileHelper::maInitialBaseURL; + OUString BackupFileHelper::maUserConfigBaseURL; + OUString BackupFileHelper::maUserConfigWorkURL; + OUString BackupFileHelper::maRegModName; + OUString BackupFileHelper::maExt; + + const OUString& BackupFileHelper::getInitialBaseURL() + { + if (maInitialBaseURL.isEmpty()) + { + // try to access user layer configuration file URL, the one that + // points to registrymodifications.xcu + OUString conf("${CONFIGURATION_LAYERS}"); + rtl::Bootstrap::expandMacros(conf); + static const OUStringLiteral aTokenUser(u"user:"); + sal_Int32 nStart(conf.indexOf(aTokenUser)); + + if (-1 != nStart) + { + nStart += aTokenUser.getLength(); + sal_Int32 nEnd(conf.indexOf(' ', nStart)); + + if (-1 == nEnd) + { + nEnd = conf.getLength(); + } + + maInitialBaseURL = conf.copy(nStart, nEnd - nStart); + (void)maInitialBaseURL.startsWith("!", &maInitialBaseURL); + } + + if (!maInitialBaseURL.isEmpty()) + { + // split URL at extension and at last path separator + maUserConfigBaseURL = DirectoryHelper::splitAtLastToken( + DirectoryHelper::splitAtLastToken(maInitialBaseURL, '.', maExt), '/', + maRegModName); + } + + if (!maUserConfigBaseURL.isEmpty()) + { + // check if SafeModeDir exists + mbSafeModeDirExists = DirectoryHelper::dirExists(maUserConfigBaseURL + "/" + getSafeModeName()); + } + + maUserConfigWorkURL = maUserConfigBaseURL; + + if (mbSafeModeDirExists) + { + // adapt work URL to do all repair op's in the correct directory + maUserConfigWorkURL += "/" + getSafeModeName(); + } + } + + return maInitialBaseURL; + } + + const OUString& BackupFileHelper::getSafeModeName() + { + static const OUString aSafeMode("SafeMode"); + + return aSafeMode; + } + + BackupFileHelper::BackupFileHelper() + : mnNumBackups(2), + mnMode(1), + mbActive(false), + mbExtensions(true), + mbCompress(true) + { + OUString sTokenOut; + + // read configuration item 'SecureUserConfig' -> bool on/off + if (rtl::Bootstrap::get("SecureUserConfig", sTokenOut)) + { + mbActive = sTokenOut.toBoolean(); + } + + if (mbActive) + { + // ensure existence + getInitialBaseURL(); + + // if not found, we are out of business (maExt may be empty) + mbActive = !maInitialBaseURL.isEmpty() && !maUserConfigBaseURL.isEmpty() && !maRegModName.isEmpty(); + } + + if (mbActive && rtl::Bootstrap::get("SecureUserConfigNumCopies", sTokenOut)) + { + const sal_uInt16 nConfigNumCopies(static_cast<sal_uInt16>(sTokenOut.toUInt32())); + + // limit to range [1..mnMaxAllowedBackups] + mnNumBackups = std::clamp(mnNumBackups, nConfigNumCopies, mnMaxAllowedBackups); + } + + if (mbActive && rtl::Bootstrap::get("SecureUserConfigMode", sTokenOut)) + { + const sal_uInt16 nMode(static_cast<sal_uInt16>(sTokenOut.toUInt32())); + + // limit to range [0..2] + mnMode = std::min(nMode, sal_uInt16(2)); + } + + if (mbActive && rtl::Bootstrap::get("SecureUserConfigExtensions", sTokenOut)) + { + mbExtensions = sTokenOut.toBoolean(); + } + + if (mbActive && rtl::Bootstrap::get("SecureUserConfigCompress", sTokenOut)) + { + mbCompress = sTokenOut.toBoolean(); + } + } + + void BackupFileHelper::setExitWasCalled() + { + mbExitWasCalled = true; + } + + bool BackupFileHelper::getExitWasCalled() + { + return mbExitWasCalled; + } + + void BackupFileHelper::reactOnSafeMode(bool bSafeMode) + { + // ensure existence of needed paths + getInitialBaseURL(); + + if (maUserConfigBaseURL.isEmpty()) + return; + + if (bSafeMode) + { + if (!mbSafeModeDirExists) + { + std::set< OUString > aExcludeList; + + // do not move SafeMode directory itself + aExcludeList.insert(getSafeModeName()); + + // init SafeMode by creating the 'SafeMode' directory and moving + // all stuff there. All repairs will happen there. Both Dirs have to exist. + // extend maUserConfigWorkURL as needed + maUserConfigWorkURL = maUserConfigBaseURL + "/" + getSafeModeName(); + + osl::Directory::createPath(maUserConfigWorkURL); + DirectoryHelper::moveDirContent(maUserConfigBaseURL, maUserConfigWorkURL, aExcludeList); + + // switch local flag, maUserConfigWorkURL is already reset + mbSafeModeDirExists = true; + } + } + else + { + if (mbSafeModeDirExists) + { + // SafeMode has ended, return to normal mode by moving all content + // from 'SafeMode' directory back to UserDirectory and deleting it. + // Both Dirs have to exist + std::set< OUString > aExcludeList; + + DirectoryHelper::moveDirContent(maUserConfigWorkURL, maUserConfigBaseURL, aExcludeList); + osl::Directory::remove(maUserConfigWorkURL); + + // switch local flag and reset maUserConfigWorkURL + mbSafeModeDirExists = false; + maUserConfigWorkURL = maUserConfigBaseURL; + } + } + } + + void BackupFileHelper::tryPush() + { + // no push when SafeModeDir exists, it may be Office's exit after SafeMode + // where SafeMode flag is already deleted, but SafeModeDir cleanup is not + // done yet (is done at next startup) + if (!mbActive || mbSafeModeDirExists) + return; + + const OUString aPackURL(getPackURL()); + + // ensure dir and file vectors + fillDirFileInfo(); + + // process all files in question recursively + if (!maDirs.empty() || !maFiles.empty()) + { + tryPush_Files( + maDirs, + maFiles, + maUserConfigWorkURL, + aPackURL); + } + } + + void BackupFileHelper::tryPushExtensionInfo() + { + // no push when SafeModeDir exists, it may be Office's exit after SafeMode + // where SafeMode flag is already deleted, but SafeModeDir cleanup is not + // done yet (is done at next startup) + if (mbActive && mbExtensions && !mbSafeModeDirExists) + { + const OUString aPackURL(getPackURL()); + + tryPush_extensionInfo(aPackURL); + } + } + + bool BackupFileHelper::isPopPossible() + { + bool bPopPossible(false); + + if (mbActive) + { + const OUString aPackURL(getPackURL()); + + // ensure dir and file vectors + fillDirFileInfo(); + + // process all files in question recursively + if (!maDirs.empty() || !maFiles.empty()) + { + bPopPossible = isPopPossible_files( + maDirs, + maFiles, + maUserConfigWorkURL, + aPackURL); + } + } + + return bPopPossible; + } + + void BackupFileHelper::tryPop() + { + if (!mbActive) + return; + + bool bDidPop(false); + const OUString aPackURL(getPackURL()); + + // ensure dir and file vectors + fillDirFileInfo(); + + // process all files in question recursively + if (!maDirs.empty() || !maFiles.empty()) + { + bDidPop = tryPop_files( + maDirs, + maFiles, + maUserConfigWorkURL, + aPackURL); + } + + if (bDidPop) + { + // try removal of evtl. empty directory + osl::Directory::remove(aPackURL); + } + } + + bool BackupFileHelper::isPopPossibleExtensionInfo() const + { + bool bPopPossible(false); + + if (mbActive && mbExtensions) + { + const OUString aPackURL(getPackURL()); + + bPopPossible = isPopPossible_extensionInfo(aPackURL); + } + + return bPopPossible; + } + + void BackupFileHelper::tryPopExtensionInfo() + { + if (!(mbActive && mbExtensions)) + return; + + bool bDidPop(false); + const OUString aPackURL(getPackURL()); + + bDidPop = tryPop_extensionInfo(aPackURL); + + if (bDidPop) + { + // try removal of evtl. empty directory + osl::Directory::remove(aPackURL); + } + } + + bool BackupFileHelper::isTryDisableAllExtensionsPossible() + { + // check if there are still enabled extension which can be disabled, + // but as we are now in SafeMode, use XML infos for this since the + // extensions are not loaded from XExtensionManager + class ExtensionInfo aExtensionInfo; + + aExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL); + + return aExtensionInfo.areThereEnabledExtensions(); + } + + void BackupFileHelper::tryDisableAllExtensions() + { + // disable all still enabled extensions, + // but as we are now in SafeMode, use XML infos for this since the + // extensions are not loaded from XExtensionManager + ExtensionInfo aCurrentExtensionInfo; + const ExtensionInfoEntryVector aToBeEnabled{}; + ExtensionInfoEntryVector aToBeDisabled; + + aCurrentExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL); + + const ExtensionInfoEntryVector& rCurrentVector = aCurrentExtensionInfo.getExtensionInfoEntryVector(); + + for (const auto& rCurrentInfo : rCurrentVector) + { + if (rCurrentInfo.isEnabled()) + { + aToBeDisabled.push_back(rCurrentInfo); + } + } + + ExtensionInfo::changeEnableDisableStateInXML(maUserConfigWorkURL, aToBeEnabled, aToBeDisabled); + } + + bool BackupFileHelper::isTryDeinstallUserExtensionsPossible() + { + // check if there are User Extensions installed. + class ExtensionInfo aExtensionInfo; + + aExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL); + + return !aExtensionInfo.getExtensionInfoEntryVector().empty(); + } + + void BackupFileHelper::tryDeinstallUserExtensions() + { + // delete User Extension installs + DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/uno_packages"); + } + + bool BackupFileHelper::isTryResetSharedExtensionsPossible() + { + // check if there are shared Extensions installed + class ExtensionInfo aExtensionInfo; + + aExtensionInfo.createSharedExtensionRegistryEntriesFromXML(maUserConfigWorkURL); + + return !aExtensionInfo.getExtensionInfoEntryVector().empty(); + } + + void BackupFileHelper::tryResetSharedExtensions() + { + // reset shared extension info + DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/extensions/shared"); + } + + bool BackupFileHelper::isTryResetBundledExtensionsPossible() + { + // check if there are shared Extensions installed + class ExtensionInfo aExtensionInfo; + + aExtensionInfo.createBundledExtensionRegistryEntriesFromXML(maUserConfigWorkURL); + + return !aExtensionInfo.getExtensionInfoEntryVector().empty(); + } + + void BackupFileHelper::tryResetBundledExtensions() + { + // reset shared extension info + DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/extensions/bundled"); + } + + const std::vector< OUString >& BackupFileHelper::getCustomizationDirNames() + { + static std::vector< OUString > aDirNames = + { + "config", // UI config stuff + "registry", // most of the registry stuff + "psprint", // not really needed, can be abandoned + "store", // not really needed, can be abandoned + "temp", // not really needed, can be abandoned + "pack" // own backup dir + }; + + return aDirNames; + } + + const std::vector< OUString >& BackupFileHelper::getCustomizationFileNames() + { + static std::vector< OUString > aFileNames = + { + "registrymodifications.xcu" // personal registry stuff + }; + + return aFileNames; + } + + namespace { + uno::Reference<XElement> lcl_getConfigElement(const uno::Reference<XDocument>& xDocument, const OUString& rPath, + const OUString& rKey, const OUString& rValue) + { + uno::Reference< XElement > itemElement = xDocument->createElement("item"); + itemElement->setAttribute("oor:path", rPath); + + uno::Reference< XElement > propElement = xDocument->createElement("prop"); + propElement->setAttribute("oor:name", rKey); + propElement->setAttribute("oor:op", "replace"); // Replace any other options + + uno::Reference< XElement > valueElement = xDocument->createElement("value"); + uno::Reference< XText > textElement = xDocument->createTextNode(rValue); + + valueElement->appendChild(textElement); + propElement->appendChild(valueElement); + itemElement->appendChild(propElement); + + return itemElement; + } + } + + void BackupFileHelper::tryDisableHWAcceleration() + { + const OUString aRegistryModifications(maUserConfigWorkURL + "/registrymodifications.xcu"); + if (!DirectoryHelper::fileExists(aRegistryModifications)) + return; + + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + uno::Reference< XDocumentBuilder > xBuilder = DocumentBuilder::create(xContext); + uno::Reference< XDocument > xDocument = xBuilder->parseURI(aRegistryModifications); + uno::Reference< XElement > xRootElement = xDocument->getDocumentElement(); + + xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/VCL", + "DisableOpenGL", "true")); + xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/Misc", + "UseOpenCL", "false")); + // Do not disable Skia entirely, just force its CPU-based raster mode. + xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/VCL", + "ForceSkia", "false")); + xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/VCL", + "ForceSkiaRaster", "true")); + + OUString aTempURL; + { + // use the scope to make sure that the temp file gets properly closed before move + + // write back + uno::Reference< xml::sax::XSAXSerializable > xSerializer(xDocument, uno::UNO_QUERY); + + if (!xSerializer.is()) + return; + + // create a SAXWriter + uno::Reference< xml::sax::XWriter > const xSaxWriter = xml::sax::Writer::create(xContext); + uno::Reference< io::XTempFile > xTempFile = io::TempFile::create(xContext); + xTempFile->setRemoveFile(false); // avoid removal of tempfile when leaving the scope + uno::Reference< io::XOutputStream > xOutStrm = xTempFile->getOutputStream(); + + // set output stream and do the serialization + xSaxWriter->setOutputStream(xOutStrm); + xSerializer->serialize(xSaxWriter, uno::Sequence< beans::StringPair >()); + + // get URL from temp file + aTempURL = xTempFile->getUri(); + } + + // copy back file + if (aTempURL.isEmpty() || !DirectoryHelper::fileExists(aTempURL)) + return; + + if (DirectoryHelper::fileExists(aRegistryModifications)) + { + osl::File::remove(aRegistryModifications); + } + + int result = osl::File::move(aTempURL, aRegistryModifications); + SAL_WARN_IF(result != osl::FileBase::E_None, "comphelper.backupfilehelper", "could not copy back modified Extension configuration file"); + } + + bool BackupFileHelper::isTryResetCustomizationsPossible() + { + // return true if not all of the customization selection dirs or files are deleted + const std::vector< OUString >& rDirs = getCustomizationDirNames(); + + for (const auto& a : rDirs) + { + if (DirectoryHelper::dirExists(maUserConfigWorkURL + "/" + a)) + { + return true; + } + } + + const std::vector< OUString >& rFiles = getCustomizationFileNames(); + + for (const auto& b : rFiles) + { + if (DirectoryHelper::fileExists(maUserConfigWorkURL + "/" + b)) + { + return true; + } + } + + return false; + } + + void BackupFileHelper::tryResetCustomizations() + { + // delete all of the customization selection dirs + const std::vector< OUString >& rDirs = getCustomizationDirNames(); + + for (const auto& a : rDirs) + { + DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/" + a); + } + + const std::vector< OUString >& rFiles = getCustomizationFileNames(); + + for (const auto& b : rFiles) + { + osl::File::remove(maUserConfigWorkURL + "/" + b); + } + } + + void BackupFileHelper::tryResetUserProfile() + { + // completely delete the current UserProfile + DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL); + } + + const OUString& BackupFileHelper::getUserProfileURL() + { + return maUserConfigBaseURL; + } + + const OUString& BackupFileHelper::getUserProfileWorkURL() + { + return maUserConfigWorkURL; + } + + /////////////////// helpers /////////////////////// + + OUString BackupFileHelper::getPackURL() + { + return OUString(maUserConfigWorkURL + "/pack"); + } + + /////////////////// file push helpers /////////////////////// + + bool BackupFileHelper::tryPush_Files( + const std::set< OUString >& rDirs, + const std::set< std::pair< OUString, OUString > >& rFiles, + std::u16string_view rSourceURL, // source dir without trailing '/' + const OUString& rTargetURL // target dir without trailing '/' + ) + { + bool bDidPush(false); + osl::Directory::createPath(rTargetURL); + + // process files + for (const auto& file : rFiles) + { + bDidPush |= tryPush_file( + rSourceURL, + rTargetURL, + file.first, + file.second); + } + + // process dirs + for (const auto& dir : rDirs) + { + OUString aNewSourceURL(OUString::Concat(rSourceURL) + "/" + dir); + OUString aNewTargetURL(rTargetURL + "/" + dir); + std::set< OUString > aNewDirs; + std::set< std::pair< OUString, OUString > > aNewFiles; + + DirectoryHelper::scanDirsAndFiles( + aNewSourceURL, + aNewDirs, + aNewFiles); + + if (!aNewDirs.empty() || !aNewFiles.empty()) + { + bDidPush |= tryPush_Files( + aNewDirs, + aNewFiles, + aNewSourceURL, + aNewTargetURL); + } + } + + if (!bDidPush) + { + // try removal of evtl. empty directory + osl::Directory::remove(rTargetURL); + } + + return bDidPush; + } + + bool BackupFileHelper::tryPush_file( + std::u16string_view rSourceURL, // source dir without trailing '/' + std::u16string_view rTargetURL, // target dir without trailing '/' + std::u16string_view rName, // filename + std::u16string_view rExt // extension (or empty) + ) + { + const OUString aFileURL(createFileURL(rSourceURL, rName, rExt)); + + if (DirectoryHelper::fileExists(aFileURL)) + { + const OUString aPackURL(createPackURL(rTargetURL, rName)); + PackedFile aPackedFile(aPackURL); + FileSharedPtr aBaseFile = std::make_shared<osl::File>(aFileURL); + + if (aPackedFile.tryPush(aBaseFile, mbCompress)) + { + // reduce to allowed number and flush + aPackedFile.tryReduceToNumBackups(mnNumBackups); + aPackedFile.flush(); + + return true; + } + } + + return false; + } + + /////////////////// file pop possibilities helper /////////////////////// + + bool BackupFileHelper::isPopPossible_files( + const std::set< OUString >& rDirs, + const std::set< std::pair< OUString, OUString > >& rFiles, + std::u16string_view rSourceURL, // source dir without trailing '/' + std::u16string_view rTargetURL // target dir without trailing '/' + ) + { + bool bPopPossible(false); + + // process files + for (const auto& file : rFiles) + { + bPopPossible |= isPopPossible_file( + rSourceURL, + rTargetURL, + file.first, + file.second); + } + + // process dirs + for (const auto& dir : rDirs) + { + OUString aNewSourceURL(OUString::Concat(rSourceURL) + "/" + dir); + OUString aNewTargetURL(OUString::Concat(rTargetURL) + "/" + dir); + std::set< OUString > aNewDirs; + std::set< std::pair< OUString, OUString > > aNewFiles; + + DirectoryHelper::scanDirsAndFiles( + aNewSourceURL, + aNewDirs, + aNewFiles); + + if (!aNewDirs.empty() || !aNewFiles.empty()) + { + bPopPossible |= isPopPossible_files( + aNewDirs, + aNewFiles, + aNewSourceURL, + aNewTargetURL); + } + } + + return bPopPossible; + } + + bool BackupFileHelper::isPopPossible_file( + std::u16string_view rSourceURL, // source dir without trailing '/' + std::u16string_view rTargetURL, // target dir without trailing '/' + std::u16string_view rName, // filename + std::u16string_view rExt // extension (or empty) + ) + { + const OUString aFileURL(createFileURL(rSourceURL, rName, rExt)); + + if (DirectoryHelper::fileExists(aFileURL)) + { + const OUString aPackURL(createPackURL(rTargetURL, rName)); + PackedFile aPackedFile(aPackURL); + + return !aPackedFile.empty(); + } + + return false; + } + + /////////////////// file pop helpers /////////////////////// + + bool BackupFileHelper::tryPop_files( + const std::set< OUString >& rDirs, + const std::set< std::pair< OUString, OUString > >& rFiles, + std::u16string_view rSourceURL, // source dir without trailing '/' + const OUString& rTargetURL // target dir without trailing '/' + ) + { + bool bDidPop(false); + + // process files + for (const auto& file : rFiles) + { + bDidPop |= tryPop_file( + rSourceURL, + rTargetURL, + file.first, + file.second); + } + + // process dirs + for (const auto& dir : rDirs) + { + OUString aNewSourceURL(OUString::Concat(rSourceURL) + "/" + dir); + OUString aNewTargetURL(rTargetURL + "/" + dir); + std::set< OUString > aNewDirs; + std::set< std::pair< OUString, OUString > > aNewFiles; + + DirectoryHelper::scanDirsAndFiles( + aNewSourceURL, + aNewDirs, + aNewFiles); + + if (!aNewDirs.empty() || !aNewFiles.empty()) + { + bDidPop |= tryPop_files( + aNewDirs, + aNewFiles, + aNewSourceURL, + aNewTargetURL); + } + } + + if (bDidPop) + { + // try removal of evtl. empty directory + osl::Directory::remove(rTargetURL); + } + + return bDidPop; + } + + bool BackupFileHelper::tryPop_file( + std::u16string_view rSourceURL, // source dir without trailing '/' + std::u16string_view rTargetURL, // target dir without trailing '/' + std::u16string_view rName, // filename + std::u16string_view rExt // extension (or empty) + ) + { + const OUString aFileURL(createFileURL(rSourceURL, rName, rExt)); + + if (!DirectoryHelper::fileExists(aFileURL)) + return false; + + // try Pop for base file + const OUString aPackURL(createPackURL(rTargetURL, rName)); + PackedFile aPackedFile(aPackURL); + + if (aPackedFile.empty()) + return false; + + oslFileHandle aHandle; + OUString aTempURL; + + // open target temp file - it exists until deleted + if (osl::File::E_None != osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL)) + return false; + + bool bRetval(aPackedFile.tryPop(aHandle)); + + // close temp file (in all cases) - it exists until deleted + osl_closeFile(aHandle); + + if (bRetval) + { + // copy over existing file by first deleting original + // and moving the temp file to old original + osl::File::remove(aFileURL); + osl::File::move(aTempURL, aFileURL); + + // reduce to allowed number and flush + aPackedFile.tryReduceToNumBackups(mnNumBackups); + aPackedFile.flush(); + } + + // delete temp file (in all cases - it may be moved already) + osl::File::remove(aTempURL); + + return bRetval; + } + + /////////////////// ExtensionInfo helpers /////////////////////// + + bool BackupFileHelper::tryPush_extensionInfo( + std::u16string_view rTargetURL // target dir without trailing '/' + ) + { + ExtensionInfo aExtensionInfo; + OUString aTempURL; + bool bRetval(false); + + // create current configuration and write to temp file - it exists until deleted + if (aExtensionInfo.createTempFile(aTempURL)) + { + const OUString aPackURL(createPackURL(rTargetURL, u"ExtensionInfo")); + PackedFile aPackedFile(aPackURL); + FileSharedPtr aBaseFile = std::make_shared<osl::File>(aTempURL); + + if (aPackedFile.tryPush(aBaseFile, mbCompress)) + { + // reduce to allowed number and flush + aPackedFile.tryReduceToNumBackups(mnNumBackups); + aPackedFile.flush(); + bRetval = true; + } + } + + // delete temp file (in all cases) + osl::File::remove(aTempURL); + return bRetval; + } + + bool BackupFileHelper::isPopPossible_extensionInfo( + std::u16string_view rTargetURL // target dir without trailing '/' + ) + { + // extensionInfo always exists internally, no test needed + const OUString aPackURL(createPackURL(rTargetURL, u"ExtensionInfo")); + PackedFile aPackedFile(aPackURL); + + return !aPackedFile.empty(); + } + + bool BackupFileHelper::tryPop_extensionInfo( + std::u16string_view rTargetURL // target dir without trailing '/' + ) + { + // extensionInfo always exists internally, no test needed + const OUString aPackURL(createPackURL(rTargetURL, u"ExtensionInfo")); + PackedFile aPackedFile(aPackURL); + + if (aPackedFile.empty()) + return false; + + oslFileHandle aHandle; + OUString aTempURL; + + // open target temp file - it exists until deleted + if (osl::File::E_None != osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL)) + return false; + + bool bRetval(aPackedFile.tryPop(aHandle)); + + // close temp file (in all cases) - it exists until deleted + osl_closeFile(aHandle); + + if (bRetval) + { + // last config is in temp file, load it to ExtensionInfo + ExtensionInfo aLoadedExtensionInfo; + FileSharedPtr aBaseFile = std::make_shared<osl::File>(aTempURL); + + if (osl::File::E_None == aBaseFile->open(osl_File_OpenFlag_Read)) + { + if (aLoadedExtensionInfo.read_entries(aBaseFile)) + { + // get current extension info, but from XML config files + ExtensionInfo aCurrentExtensionInfo; + + aCurrentExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL); + + // now we have loaded last_working (aLoadedExtensionInfo) and + // current (aCurrentExtensionInfo) ExtensionInfo and may react on + // differences by de/activating these as needed + const ExtensionInfoEntryVector& aUserEntries = aCurrentExtensionInfo.getExtensionInfoEntryVector(); + const ExtensionInfoEntryVector& rLoadedVector = aLoadedExtensionInfo.getExtensionInfoEntryVector(); + ExtensionInfoEntryVector aToBeDisabled; + ExtensionInfoEntryVector aToBeEnabled; + + for (const auto& rCurrentInfo : aUserEntries) + { + const ExtensionInfoEntry* pLoadedInfo = nullptr; + + for (const auto& rLoadedInfo : rLoadedVector) + { + if (rCurrentInfo.isSameExtension(rLoadedInfo)) + { + pLoadedInfo = &rLoadedInfo; + break; + } + } + + if (nullptr != pLoadedInfo) + { + // loaded info contains information about the Extension rCurrentInfo + const bool bCurrentEnabled(rCurrentInfo.isEnabled()); + const bool bLoadedEnabled(pLoadedInfo->isEnabled()); + + if (bCurrentEnabled && !bLoadedEnabled) + { + aToBeDisabled.push_back(rCurrentInfo); + } + else if (!bCurrentEnabled && bLoadedEnabled) + { + aToBeEnabled.push_back(rCurrentInfo); + } + } + else + { + // There is no loaded info about the Extension rCurrentInfo. + // It needs to be disabled + if (rCurrentInfo.isEnabled()) + { + aToBeDisabled.push_back(rCurrentInfo); + } + } + } + + if (!aToBeDisabled.empty() || !aToBeEnabled.empty()) + { + ExtensionInfo::changeEnableDisableStateInXML(maUserConfigWorkURL, aToBeEnabled, aToBeDisabled); + } + + bRetval = true; + } + } + + // reduce to allowed number and flush + aPackedFile.tryReduceToNumBackups(mnNumBackups); + aPackedFile.flush(); + } + + // delete temp file (in all cases - it may be moved already) + osl::File::remove(aTempURL); + + return bRetval; + } + + /////////////////// FileDirInfo helpers /////////////////////// + + void BackupFileHelper::fillDirFileInfo() + { + if (!maDirs.empty() || !maFiles.empty()) + { + // already done + return; + } + + // Information about the configuration and the role/purpose of directories in + // the UserConfiguration is taken from: https://wiki.documentfoundation.org/UserProfile + + // fill dir and file info list to work with dependent on work mode + switch (mnMode) + { + case 0: + { + // simple mode: add just registrymodifications + // (the orig file in maInitialBaseURL) + maFiles.insert(std::pair< OUString, OUString >(maRegModName, maExt)); + break; + } + case 1: + { + // defined mode: Add a selection of dirs containing User-Defined and thus + // valuable configuration information. + // This is clearly discussable in every single point and may be adapted/corrected + // over time. Main focus is to secure User-Defined/adapted values + + // add registrymodifications (the orig file in maInitialBaseURL) + maFiles.insert(std::pair< OUString, OUString >(maRegModName, maExt)); + + // User-defined substitution table (Tools/AutoCorrect) + maDirs.insert("autocorr"); + + // User-Defined AutoText (Edit/AutoText) + maDirs.insert("autotext"); + + // User-defined Macros + maDirs.insert("basic"); + + // User-adapted toolbars for modules + maDirs.insert("config"); + + // Initial and User-defined Databases + maDirs.insert("database"); + + // most part of registry files + maDirs.insert("registry"); + + // User-Defined Scripts + maDirs.insert("Scripts"); + + // Template files + maDirs.insert("template"); + + // Custom Dictionaries + maDirs.insert("wordbook"); + + // Questionable - where and how is Extension stuff held and how + // does this interact with enabled/disabled states which are extra handled? + // Keep out of business until deeper evaluated + // + // maDirs.insert("extensions"); + // maDirs.insert("uno-packages"); + break; + } + case 2: + { + // whole directory. To do so, scan directory and exclude some dirs + // from which we know they do not need to be secured explicitly. This + // should already include registrymodifications, too. + DirectoryHelper::scanDirsAndFiles( + maUserConfigWorkURL, + maDirs, + maFiles); + + // should not exist, but for the case an error occurred and it got + // copied somehow, avoid further recursive copying/saving + maDirs.erase("SafeMode"); + + // not really needed, can be abandoned + maDirs.erase("psprint"); + + // not really needed, can be abandoned + maDirs.erase("store"); + + // not really needed, can be abandoned + maDirs.erase("temp"); + + // exclude own backup dir to avoid recursion + maDirs.erase("pack"); + + break; + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/base64.cxx b/comphelper/source/misc/base64.cxx new file mode 100644 index 000000000..5e3e3ca0a --- /dev/null +++ b/comphelper/source/misc/base64.cxx @@ -0,0 +1,220 @@ +/* -*- 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 <sal/config.h> + +#include <cstddef> + +#include <comphelper/base64.hxx> + +#include <com/sun/star/uno/Sequence.hxx> + +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> + +using namespace com::sun::star; + +namespace comphelper { + +const + char aBase64EncodeTable[] = + { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; + +const + sal_uInt8 aBase64DecodeTable[] = + { 62,255,255,255, 63, // 43-47 +// + / + + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255, // 48-63 +// 0 1 2 3 4 5 6 7 8 9 = + + 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79 +// A B C D E F G H I J K L M N O + + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, // 80-95 +// P Q R S T U V W X Y Z + + 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111 +// a b c d e f g h i j k l m n o + + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; // 112-123 +// p q r s t u v w x y z + + +static void ThreeByteToFourByte(const sal_Int8* pBuffer, const sal_Int32 nStart, const sal_Int32 nFullLen, char* aCharBuffer) +{ + sal_Int32 nLen(nFullLen - nStart); + if (nLen > 3) + nLen = 3; + if (nLen == 0) + { + return; + } + + sal_Int32 nBinaer; + switch (nLen) + { + case 1: + { + nBinaer = static_cast<sal_uInt8>(pBuffer[nStart + 0]) << 16; + } + break; + case 2: + { + nBinaer = (static_cast<sal_uInt8>(pBuffer[nStart + 0]) << 16) + + (static_cast<sal_uInt8>(pBuffer[nStart + 1]) << 8); + } + break; + default: + { + nBinaer = (static_cast<sal_uInt8>(pBuffer[nStart + 0]) << 16) + + (static_cast<sal_uInt8>(pBuffer[nStart + 1]) << 8) + + static_cast<sal_uInt8>(pBuffer[nStart + 2]); + } + break; + } + + aCharBuffer[0] = aCharBuffer[1] = aCharBuffer[2] = aCharBuffer[3] = '='; + + sal_uInt8 nIndex (static_cast<sal_uInt8>((nBinaer & 0xFC0000) >> 18)); + aCharBuffer[0] = aBase64EncodeTable [nIndex]; + + nIndex = static_cast<sal_uInt8>((nBinaer & 0x3F000) >> 12); + aCharBuffer[1] = aBase64EncodeTable [nIndex]; + if (nLen > 1) + { + nIndex = static_cast<sal_uInt8>((nBinaer & 0xFC0) >> 6); + aCharBuffer[2] = aBase64EncodeTable [nIndex]; + if (nLen > 2) + { + nIndex = static_cast<sal_uInt8>((nBinaer & 0x3F)); + aCharBuffer[3] = aBase64EncodeTable [nIndex]; + } + } +} + +void Base64::encode(OStringBuffer& aStrBuffer, const uno::Sequence<sal_Int8>& aPass) +{ + sal_Int32 i(0); + sal_Int32 nBufferLength(aPass.getLength()); + const sal_Int8* pBuffer = aPass.getConstArray(); + while (i < nBufferLength) + { + char aCharBuffer[4]; + ThreeByteToFourByte(pBuffer, i, nBufferLength, aCharBuffer); + aStrBuffer.append(aCharBuffer, std::size(aCharBuffer)); + i += 3; + } +} + +void Base64::encode(OUStringBuffer& aStrBuffer, const uno::Sequence<sal_Int8>& aPass) +{ + sal_Int32 i(0); + sal_Int32 nBufferLength(aPass.getLength()); + const sal_Int8* pBuffer = aPass.getConstArray(); + while (i < nBufferLength) + { + char aCharBuffer[4]; + ThreeByteToFourByte(pBuffer, i, nBufferLength, aCharBuffer); + aStrBuffer.appendAscii(aCharBuffer, std::size(aCharBuffer)); + i += 3; + } +} + +void Base64::decode(uno::Sequence<sal_Int8>& aBuffer, std::u16string_view sBuffer) +{ + std::size_t nCharsDecoded = decodeSomeChars( aBuffer, sBuffer ); + OSL_ENSURE( nCharsDecoded == sBuffer.size(), "some bytes left in base64 decoding!" ); +} + +std::size_t Base64::decodeSomeChars(uno::Sequence<sal_Int8>& rOutBuffer, std::u16string_view rInBuffer) +{ + std::size_t nInBufferLen = rInBuffer.size(); + std::size_t nMinOutBufferLen = (nInBufferLen / 4) * 3; + if( o3tl::make_unsigned(rOutBuffer.getLength()) < nMinOutBufferLen ) + rOutBuffer.realloc( nMinOutBufferLen ); + + const sal_Unicode *pInBuffer = rInBuffer.data(); + sal_Int8 *pOutBuffer = rOutBuffer.getArray(); + sal_Int8 *pOutBufferStart = pOutBuffer; + std::size_t nCharsDecoded = 0; + + sal_uInt8 aDecodeBuffer[4]; + sal_Int32 nBytesToDecode = 0; + sal_Int32 nBytesGotFromDecoding = 3; + std::size_t nInBufferPos= 0; + while( nInBufferPos < nInBufferLen ) + { + sal_Unicode cChar = *pInBuffer; + if( cChar >= '+' && cChar <= 'z' ) + { + sal_uInt8 nByte = aBase64DecodeTable[cChar-'+']; + if( nByte != 255 ) + { + // We have found a valid character! + aDecodeBuffer[nBytesToDecode++] = nByte; + + // One '=' character at the end means 2 out bytes + // Two '=' characters at the end mean 1 out bytes + if( '=' == cChar && nBytesToDecode > 2 ) + nBytesGotFromDecoding--; + if( 4 == nBytesToDecode ) + { + // Four characters found, so we may convert now! + sal_uInt32 nOut = (aDecodeBuffer[0] << 18) + + (aDecodeBuffer[1] << 12) + + (aDecodeBuffer[2] << 6) + + aDecodeBuffer[3]; + + *pOutBuffer++ = static_cast<sal_Int8>((nOut & 0xff0000) >> 16); + if( nBytesGotFromDecoding > 1 ) + *pOutBuffer++ = static_cast<sal_Int8>((nOut & 0xff00) >> 8); + if( nBytesGotFromDecoding > 2 ) + *pOutBuffer++ = static_cast<sal_Int8>(nOut & 0xff); + nCharsDecoded = nInBufferPos + 1; + nBytesToDecode = 0; + nBytesGotFromDecoding = 3; + } + } + else + { + nCharsDecoded++; + } + } + else + { + nCharsDecoded++; + } + + nInBufferPos++; + pInBuffer++; + } + if( (pOutBuffer - pOutBufferStart) != rOutBuffer.getLength() ) + rOutBuffer.realloc( pOutBuffer - pOutBufferStart ); + + return nCharsDecoded; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/compbase.cxx b/comphelper/source/misc/compbase.cxx new file mode 100644 index 000000000..f8a8897b7 --- /dev/null +++ b/comphelper/source/misc/compbase.cxx @@ -0,0 +1,231 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include <comphelper/compbase.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +namespace comphelper +{ +WeakComponentImplHelperBase::~WeakComponentImplHelperBase() {} + +// css::lang::XComponent +void SAL_CALL WeakComponentImplHelperBase::dispose() +{ + std::unique_lock aGuard(m_aMutex); + if (m_bDisposed) + return; + m_bDisposed = true; + disposing(aGuard); + if (!aGuard.owns_lock()) + aGuard.lock(); + css::lang::EventObject aEvt(static_cast<OWeakObject*>(this)); + maEventListeners.disposeAndClear(aGuard, aEvt); +} + +void WeakComponentImplHelperBase::disposing(std::unique_lock<std::mutex>&) {} + +void SAL_CALL WeakComponentImplHelperBase::addEventListener( + css::uno::Reference<css::lang::XEventListener> const& rxListener) +{ + std::unique_lock aGuard(m_aMutex); + if (m_bDisposed) + return; + maEventListeners.addInterface(aGuard, rxListener); +} + +void SAL_CALL WeakComponentImplHelperBase::removeEventListener( + css::uno::Reference<css::lang::XEventListener> const& rxListener) +{ + std::unique_lock aGuard(m_aMutex); + maEventListeners.removeInterface(aGuard, rxListener); +} + +css::uno::Any SAL_CALL WeakComponentImplHelperBase::queryInterface(css::uno::Type const& rType) +{ + css::uno::Any aReturn = ::cppu::queryInterface(rType, static_cast<css::uno::XWeak*>(this), + static_cast<css::lang::XComponent*>(this)); + if (aReturn.hasValue()) + return aReturn; + return OWeakObject::queryInterface(rType); +} + +static void checkInterface(css::uno::Type const& rType) +{ + if (css::uno::TypeClass_INTERFACE != rType.getTypeClass()) + { + OUString msg("querying for interface \"" + rType.getTypeName() + "\": no interface type!"); + SAL_WARN("cppuhelper", msg); + throw css::uno::RuntimeException(msg); + } +} + +static bool isXInterface(rtl_uString* pStr) +{ + return OUString::unacquired(&pStr) == "com.sun.star.uno.XInterface"; +} + +static bool td_equals(typelib_TypeDescriptionReference const* pTDR1, + typelib_TypeDescriptionReference const* pTDR2) +{ + return ((pTDR1 == pTDR2) + || OUString::unacquired(&pTDR1->pTypeName) == OUString::unacquired(&pTDR2->pTypeName)); +} + +static cppu::type_entry* getTypeEntries(cppu::class_data* cd) +{ + cppu::type_entry* pEntries = cd->m_typeEntries; + if (!cd->m_storedTypeRefs) // not inited? + { + static std::mutex aMutex; + std::scoped_lock guard(aMutex); + if (!cd->m_storedTypeRefs) // not inited? + { + // get all types + for (sal_Int32 n = cd->m_nTypes; n--;) + { + cppu::type_entry* pEntry = &pEntries[n]; + css::uno::Type const& rType = (*pEntry->m_type.getCppuType)(nullptr); + OSL_ENSURE(rType.getTypeClass() == css::uno::TypeClass_INTERFACE, + "### wrong helper init: expected interface!"); + OSL_ENSURE( + !isXInterface(rType.getTypeLibType()->pTypeName), + "### want to implement XInterface: template argument is XInterface?!?!?!"); + if (rType.getTypeClass() != css::uno::TypeClass_INTERFACE) + { + OUString msg("type \"" + rType.getTypeName() + "\" is no interface type!"); + SAL_WARN("cppuhelper", msg); + throw css::uno::RuntimeException(msg); + } + // ref is statically held by getCppuType() + pEntry->m_type.typeRef = rType.getTypeLibType(); + } + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + cd->m_storedTypeRefs = true; + } + } + else + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + } + return pEntries; +} + +static void* makeInterface(sal_IntPtr nOffset, void* that) +{ + return (static_cast<char*>(that) + nOffset); +} + +static bool recursivelyFindType(typelib_TypeDescriptionReference const* demandedType, + typelib_InterfaceTypeDescription const* type, sal_IntPtr* offset) +{ + // This code assumes that the vtables of a multiple-inheritance class (the + // offset amount by which to adjust the this pointer) follow one another in + // the object layout, and that they contain slots for the inherited classes + // in a specific order. In theory, that need not hold for any given + // platform; in practice, it seems to work well on all supported platforms: +next: + for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) + { + if (i > 0) + { + *offset += sizeof(void*); + } + typelib_InterfaceTypeDescription const* base = type->ppBaseTypes[i]; + // ignore XInterface: + if (base->nBaseTypes > 0) + { + if (td_equals(reinterpret_cast<typelib_TypeDescriptionReference const*>(base), + demandedType)) + { + return true; + } + // Profiling showed that it is important to speed up the common case + // of only one base: + if (type->nBaseTypes == 1) + { + type = base; + goto next; + } + if (recursivelyFindType(demandedType, base, offset)) + { + return true; + } + } + } + return false; +} + +static void* queryDeepNoXInterface(typelib_TypeDescriptionReference const* pDemandedTDR, + cppu::class_data* cd, void* that) +{ + cppu::type_entry* pEntries = getTypeEntries(cd); + sal_Int32 nTypes = cd->m_nTypes; + sal_Int32 n; + + // try top interfaces without getting td + for (n = 0; n < nTypes; ++n) + { + if (td_equals(pEntries[n].m_type.typeRef, pDemandedTDR)) + { + return makeInterface(pEntries[n].m_offset, that); + } + } + // query deep getting td + for (n = 0; n < nTypes; ++n) + { + typelib_TypeDescription* pTD = nullptr; + TYPELIB_DANGER_GET(&pTD, pEntries[n].m_type.typeRef); + if (pTD) + { + // exclude top (already tested) and bottom (XInterface) interface + OSL_ENSURE(reinterpret_cast<typelib_InterfaceTypeDescription*>(pTD)->nBaseTypes > 0, + "### want to implement XInterface:" + " template argument is XInterface?!?!?!"); + sal_IntPtr offset = pEntries[n].m_offset; + bool found = recursivelyFindType( + pDemandedTDR, reinterpret_cast<typelib_InterfaceTypeDescription*>(pTD), &offset); + TYPELIB_DANGER_RELEASE(pTD); + if (found) + { + return makeInterface(offset, that); + } + } + else + { + OUString msg("cannot get type description for type \"" + + OUString::unacquired(&pEntries[n].m_type.typeRef->pTypeName) + "\"!"); + SAL_WARN("cppuhelper", msg); + throw css::uno::RuntimeException(msg); + } + } + return nullptr; +} + +css::uno::Any WeakComponentImplHelper_query(css::uno::Type const& rType, cppu::class_data* cd, + WeakComponentImplHelperBase* pBase) +{ + checkInterface(rType); + typelib_TypeDescriptionReference* pTDR = rType.getTypeLibType(); + + // shortcut XInterface to WeakComponentImplHelperBase + if (!isXInterface(pTDR->pTypeName)) + { + void* p = queryDeepNoXInterface(pTDR, cd, pBase); + if (p) + { + return css::uno::Any(&p, pTDR); + } + } + return pBase->comphelper::WeakComponentImplHelperBase::queryInterface(rType); +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/comphelper/source/misc/componentbase.cxx b/comphelper/source/misc/componentbase.cxx new file mode 100644 index 000000000..9baec2363 --- /dev/null +++ b/comphelper/source/misc/componentbase.cxx @@ -0,0 +1,59 @@ +/* -*- 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 <comphelper/componentbase.hxx> + +#include <com/sun/star/lang/NotInitializedException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + + +namespace comphelper +{ + + + using ::com::sun::star::lang::NotInitializedException; + using ::com::sun::star::lang::DisposedException; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XInterface; + + void ComponentBase::checkDisposed( GuardAccess ) const + { + if ( m_rBHelper.bDisposed ) + throw DisposedException( OUString(), getComponent() ); + } + + + void ComponentBase::checkInitialized( GuardAccess ) const + { + if ( !m_bInitialized ) + throw NotInitializedException( OUString(), getComponent() ); + } + + + Reference< XInterface > ComponentBase::getComponent() + { + return nullptr; + } + + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/configuration.cxx b/comphelper/source/misc/configuration.cxx new file mode 100644 index 000000000..59631dbcc --- /dev/null +++ b/comphelper/source/misc/configuration.cxx @@ -0,0 +1,279 @@ +/* -*- 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/. + */ + +#include <sal/config.h> + +#include <cassert> +#include <map> +#include <memory> +#include <mutex> +#include <string_view> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/configuration/ReadOnlyAccess.hpp> +#include <com/sun/star/configuration/ReadWriteAccess.hpp> +#include <com/sun/star/configuration/XReadWriteAccess.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/container/XHierarchicalNameReplace.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/lang/XLocalizable.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <comphelper/solarmutex.hxx> +#include <comphelper/configuration.hxx> +#include <comphelper/configurationlistener.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <i18nlangtag/languagetag.hxx> + +namespace com::sun::star::uno { class XComponentContext; } + +namespace { + +OUString getDefaultLocale( + css::uno::Reference< css::uno::XComponentContext > const & context) +{ + return LanguageTag( + css::uno::Reference< css::lang::XLocalizable >( + css::configuration::theDefaultProvider::get(context), + css::uno::UNO_QUERY_THROW)-> + getLocale()).getBcp47(false); +} + +OUString extendLocalizedPath(std::u16string_view path, OUString const & locale) { + SAL_WARN_IF( + locale.match("*"), "comphelper", + "Locale \"" << locale << "\" starts with \"*\""); + assert(locale.indexOf('&') == -1); + assert(locale.indexOf('"') == -1); + assert(locale.indexOf('\'') == -1); + return OUString::Concat(path) + "/['*" + locale + "']"; +} + +} + +std::shared_ptr< comphelper::ConfigurationChanges > +comphelper::ConfigurationChanges::create() +{ + return detail::ConfigurationWrapper::get().createChanges(); +} + +comphelper::ConfigurationChanges::~ConfigurationChanges() {} + +void comphelper::ConfigurationChanges::commit() const { + access_->commitChanges(); +} + +comphelper::ConfigurationChanges::ConfigurationChanges( + css::uno::Reference< css::uno::XComponentContext > const & context): + access_( + css::configuration::ReadWriteAccess::create( + context, getDefaultLocale(context))) +{} + +void comphelper::ConfigurationChanges::setPropertyValue( + OUString const & path, css::uno::Any const & value) const +{ + access_->replaceByHierarchicalName(path, value); +} + +css::uno::Reference< css::container::XHierarchicalNameReplace > +comphelper::ConfigurationChanges::getGroup(OUString const & path) const +{ + return css::uno::Reference< css::container::XHierarchicalNameReplace >( + access_->getByHierarchicalName(path), css::uno::UNO_QUERY_THROW); +} + +css::uno::Reference< css::container::XNameContainer > +comphelper::ConfigurationChanges::getSet(OUString const & path) const +{ + return css::uno::Reference< css::container::XNameContainer >( + access_->getByHierarchicalName(path), css::uno::UNO_QUERY_THROW); +} + +comphelper::detail::ConfigurationWrapper const & +comphelper::detail::ConfigurationWrapper::get() +{ + static comphelper::detail::ConfigurationWrapper WRAPPER; + return WRAPPER; +} + +comphelper::detail::ConfigurationWrapper::ConfigurationWrapper(): + context_(comphelper::getProcessComponentContext()), + access_(css::configuration::ReadWriteAccess::create(context_, "*")) +{} + +comphelper::detail::ConfigurationWrapper::~ConfigurationWrapper() {} + +bool comphelper::detail::ConfigurationWrapper::isReadOnly(OUString const & path) + const +{ + return + (access_->getPropertyByHierarchicalName(path).Attributes + & css::beans::PropertyAttribute::READONLY) + != 0; +} + +css::uno::Any comphelper::detail::ConfigurationWrapper::getPropertyValue(std::u16string_view path) const +{ + // Cache the configuration access, since some of the keys are used in hot code. + // Note that this cache is only used by the officecfg:: auto-generated code, using it for anything + // else would be unwise because the cache could end up containing stale entries. + static std::mutex gMutex; + static std::map<OUString, css::uno::Reference< css::container::XNameAccess >> gAccessMap; + + sal_Int32 idx = path.rfind('/'); + assert(idx!=-1); + OUString parentPath(path.substr(0, idx)); + OUString childName(path.substr(idx+1)); + + std::scoped_lock aGuard(gMutex); + + // check cache + auto it = gAccessMap.find(parentPath); + if (it == gAccessMap.end()) + { + // not in the cache, look it up + css::uno::Reference<css::container::XNameAccess> access( + access_->getByHierarchicalName(parentPath), css::uno::UNO_QUERY_THROW); + it = gAccessMap.emplace(parentPath, access).first; + } + return it->second->getByName(childName); +} + +void comphelper::detail::ConfigurationWrapper::setPropertyValue( + std::shared_ptr< ConfigurationChanges > const & batch, + OUString const & path, css::uno::Any const & value) +{ + assert(batch); + batch->setPropertyValue(path, value); +} + +css::uno::Any +comphelper::detail::ConfigurationWrapper::getLocalizedPropertyValue( + std::u16string_view path) const +{ + return access_->getByHierarchicalName( + extendLocalizedPath(path, getDefaultLocale(context_))); +} + +void comphelper::detail::ConfigurationWrapper::setLocalizedPropertyValue( + std::shared_ptr< ConfigurationChanges > const & batch, + OUString const & path, css::uno::Any const & value) +{ + assert(batch); + batch->setPropertyValue(path, value); +} + +css::uno::Reference< css::container::XHierarchicalNameAccess > +comphelper::detail::ConfigurationWrapper::getGroupReadOnly( + OUString const & path) const +{ + return css::uno::Reference< css::container::XHierarchicalNameAccess >( + (css::configuration::ReadOnlyAccess::create( + context_, getDefaultLocale(context_))-> + getByHierarchicalName(path)), + css::uno::UNO_QUERY_THROW); +} + +css::uno::Reference< css::container::XHierarchicalNameReplace > +comphelper::detail::ConfigurationWrapper::getGroupReadWrite( + std::shared_ptr< ConfigurationChanges > const & batch, + OUString const & path) +{ + assert(batch); + return batch->getGroup(path); +} + +css::uno::Reference< css::container::XNameAccess > +comphelper::detail::ConfigurationWrapper::getSetReadOnly( + OUString const & path) const +{ + return css::uno::Reference< css::container::XNameAccess >( + (css::configuration::ReadOnlyAccess::create( + context_, getDefaultLocale(context_))-> + getByHierarchicalName(path)), + css::uno::UNO_QUERY_THROW); +} + +css::uno::Reference< css::container::XNameContainer > +comphelper::detail::ConfigurationWrapper::getSetReadWrite( + std::shared_ptr< ConfigurationChanges > const & batch, + OUString const & path) +{ + assert(batch); + return batch->getSet(path); +} + +std::shared_ptr< comphelper::ConfigurationChanges > +comphelper::detail::ConfigurationWrapper::createChanges() const { + return std::shared_ptr< ConfigurationChanges >( + new ConfigurationChanges(context_)); +} + +void comphelper::ConfigurationListener::addListener(ConfigurationListenerPropertyBase *pListener) +{ + maListeners.push_back( pListener ); + mxConfig->addPropertyChangeListener( pListener->maName, this ); + pListener->setProperty( mxConfig->getPropertyValue( pListener->maName ) ); +} + +void comphelper::ConfigurationListener::removeListener(ConfigurationListenerPropertyBase *pListener) +{ + auto it = std::find( maListeners.begin(), maListeners.end(), pListener ); + if ( it != maListeners.end() ) + { + maListeners.erase( it ); + mxConfig->removePropertyChangeListener( pListener->maName, this ); + } +} + +void comphelper::ConfigurationListener::dispose() +{ + for (auto const& listener : maListeners) + { + mxConfig->removePropertyChangeListener( listener->maName, this ); + listener->dispose(); + } + maListeners.clear(); + mbDisposed = true; +} + +void SAL_CALL comphelper::ConfigurationListener::disposing(css::lang::EventObject const &) +{ + dispose(); +} + +void SAL_CALL comphelper::ConfigurationListener::propertyChange( + css::beans::PropertyChangeEvent const &rEvt ) +{ + // Code is commonly used inside the SolarMutexGuard + // so to avoid concurrent writes to the property, + // and allow fast, lock-less access, guard here. + // + // Note that we are abusing rtl::Reference here to do acquire/release because, + // unlike osl::Guard, it is tolerant of null pointers, and on some code paths, the + // SolarMutex does not exist. + rtl::Reference<comphelper::SolarMutex> xMutexGuard( comphelper::SolarMutex::get() ); + + assert( rEvt.Source == mxConfig ); + for (auto const& listener : maListeners) + { + if ( listener->maName == rEvt.PropertyName ) + { + // ignore rEvt.NewValue - in theory it could be stale => not set. + css::uno::Any aValue = mxConfig->getPropertyValue( listener->maName ); + listener->setProperty( aValue ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/configurationhelper.cxx b/comphelper/source/misc/configurationhelper.cxx new file mode 100644 index 000000000..f3853baef --- /dev/null +++ b/comphelper/source/misc/configurationhelper.cxx @@ -0,0 +1,170 @@ +/* -*- 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 <comphelper/configurationhelper.hxx> +#include <comphelper/sequence.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/util/XChangesBatch.hpp> + + +namespace comphelper{ + + +css::uno::Reference< css::uno::XInterface > ConfigurationHelper::openConfig(const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const OUString& sPackage, + EConfigurationModes eMode ) +{ + css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider( + css::configuration::theDefaultProvider::get( rxContext ) ); + + std::vector< css::uno::Any > lParams; + css::beans::PropertyValue aParam ; + + // set root path + aParam.Name = "nodepath"; + aParam.Value <<= sPackage; + lParams.emplace_back(aParam); + + // enable all locales mode + if (eMode & EConfigurationModes::AllLocales) + { + aParam.Name = "locale"; + aParam.Value <<= OUString("*"); + lParams.emplace_back(aParam); + } + + // open it + css::uno::Reference< css::uno::XInterface > xCFG; + + bool bReadOnly(eMode & EConfigurationModes::ReadOnly); + if (bReadOnly) + xCFG = xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + comphelper::containerToSequence(lParams)); + else + xCFG = xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationUpdateAccess", + comphelper::containerToSequence(lParams)); + + return xCFG; +} + + +css::uno::Any ConfigurationHelper::readRelativeKey(const css::uno::Reference< css::uno::XInterface >& xCFG , + const OUString& sRelPath, + const OUString& sKey ) +{ + css::uno::Reference< css::container::XHierarchicalNameAccess > xAccess(xCFG, css::uno::UNO_QUERY_THROW); + + css::uno::Reference< css::beans::XPropertySet > xProps; + xAccess->getByHierarchicalName(sRelPath) >>= xProps; + if (!xProps.is()) + { + throw css::container::NoSuchElementException( + "The requested path \"" + sRelPath + "\" does not exist."); + } + return xProps->getPropertyValue(sKey); +} + + +void ConfigurationHelper::writeRelativeKey(const css::uno::Reference< css::uno::XInterface >& xCFG , + const OUString& sRelPath, + const OUString& sKey , + const css::uno::Any& aValue ) +{ + css::uno::Reference< css::container::XHierarchicalNameAccess > xAccess(xCFG, css::uno::UNO_QUERY_THROW); + + css::uno::Reference< css::beans::XPropertySet > xProps; + xAccess->getByHierarchicalName(sRelPath) >>= xProps; + if (!xProps.is()) + { + throw css::container::NoSuchElementException( + "The requested path \"" + sRelPath + "\" does not exist."); + } + xProps->setPropertyValue(sKey, aValue); +} + + +css::uno::Reference< css::uno::XInterface > ConfigurationHelper::makeSureSetNodeExists(const css::uno::Reference< css::uno::XInterface >& xCFG , + const OUString& sRelPathToSet, + const OUString& sSetNode ) +{ + css::uno::Reference< css::container::XHierarchicalNameAccess > xAccess(xCFG, css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::container::XNameAccess > xSet; + xAccess->getByHierarchicalName(sRelPathToSet) >>= xSet; + if (!xSet.is()) + { + throw css::container::NoSuchElementException( + "The requested path \"" + sRelPathToSet + "\" does not exist." ); + } + + css::uno::Reference< css::uno::XInterface > xNode; + if (xSet->hasByName(sSetNode)) + xSet->getByName(sSetNode) >>= xNode; + else + { + css::uno::Reference< css::lang::XSingleServiceFactory > xNodeFactory(xSet, css::uno::UNO_QUERY_THROW); + xNode = xNodeFactory->createInstance(); + css::uno::Reference< css::container::XNameContainer > xSetReplace(xSet, css::uno::UNO_QUERY_THROW); + xSetReplace->insertByName(sSetNode, css::uno::Any(xNode)); + } + + return xNode; +} + + +css::uno::Any ConfigurationHelper::readDirectKey(const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const OUString& sPackage, + const OUString& sRelPath, + const OUString& sKey , + EConfigurationModes eMode ) +{ + css::uno::Reference< css::uno::XInterface > xCFG = ConfigurationHelper::openConfig(rxContext, sPackage, eMode); + return ConfigurationHelper::readRelativeKey(xCFG, sRelPath, sKey); +} + + +void ConfigurationHelper::writeDirectKey(const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const OUString& sPackage, + const OUString& sRelPath, + const OUString& sKey , + const css::uno::Any& aValue , + EConfigurationModes eMode ) +{ + css::uno::Reference< css::uno::XInterface > xCFG = ConfigurationHelper::openConfig(rxContext, sPackage, eMode); + ConfigurationHelper::writeRelativeKey(xCFG, sRelPath, sKey, aValue); + ConfigurationHelper::flush(xCFG); +} + + +void ConfigurationHelper::flush(const css::uno::Reference< css::uno::XInterface >& xCFG) +{ + css::uno::Reference< css::util::XChangesBatch > xBatch(xCFG, css::uno::UNO_QUERY_THROW); + xBatch->commitChanges(); +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/debuggerinfo.cxx b/comphelper/source/misc/debuggerinfo.cxx new file mode 100644 index 000000000..1e7116a55 --- /dev/null +++ b/comphelper/source/misc/debuggerinfo.cxx @@ -0,0 +1,91 @@ +/* -*- 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/. + */ + +#include <comphelper/debuggerinfo.hxx> + +#include <cassert> +#include <cstring> +#include <ctype.h> + +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#elif defined MACOSX +#include <unistd.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#elif defined UNX +#include <unistd.h> +#include <fcntl.h> +#endif + +namespace comphelper +{ +#if defined DBG_UTIL +bool isDebuggerAttached() +{ +#if defined(_WIN32) + return IsDebuggerPresent(); +#elif defined MACOSX + // https://developer.apple.com/library/archive/qa/qa1361/_index.html + int junk; + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0); + assert(junk == 0); + + // We're being debugged if the P_TRACED flag is set. + + return ((info.kp_proc.p_flag & P_TRACED) != 0); +#elif defined LINUX + char buf[4096]; + int fd = open("/proc/self/status", O_RDONLY); + if (fd < 0) + return false; + int size = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (size < 0) + return false; + assert(size < int(sizeof(buf)) - 1); + buf[sizeof(buf) - 1] = '\0'; + // "TracerPid: <pid>" for pid != 0 means something is attached + const char* pos = strstr(buf, "TracerPid:"); + if (pos == nullptr) + return false; + pos += strlen("TracerPid:"); + while (*pos != '\n' && isspace(*pos)) + ++pos; + return *pos != '\n' && *pos != '0'; +#else + return false; // feel free to add your platform +#endif +} +#endif + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/dispatchcommand.cxx b/comphelper/source/misc/dispatchcommand.cxx new file mode 100644 index 000000000..d7b723c72 --- /dev/null +++ b/comphelper/source/misc/dispatchcommand.cxx @@ -0,0 +1,81 @@ +/* -*- 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 <comphelper/dispatchcommand.hxx> +#include <comphelper/processfactory.hxx> + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XNotifyingDispatch.hpp> +#include <com/sun/star/util/URL.hpp> +#include <com/sun/star/util/URLTransformer.hpp> + +using namespace css; + +namespace comphelper { + +bool dispatchCommand(const OUString& rCommand, const uno::Reference<css::frame::XFrame>& rFrame, const css::uno::Sequence<css::beans::PropertyValue>& rArguments, const uno::Reference<css::frame::XDispatchResultListener>& rListener) +{ + uno::Reference<frame::XDispatchProvider> xDispatchProvider(rFrame, uno::UNO_QUERY); + if (!xDispatchProvider.is()) + return false; + + util::URL aCommandURL; + aCommandURL.Complete = rCommand; + uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext(); + uno::Reference<util::XURLTransformer> xParser = util::URLTransformer::create(xContext); + xParser->parseStrict(aCommandURL); + + uno::Reference<frame::XDispatch> xDisp = xDispatchProvider->queryDispatch(aCommandURL, OUString(), 0); + if (!xDisp.is()) + return false; + + // And do the work... + if (rListener.is()) + { + uno::Reference<frame::XNotifyingDispatch> xNotifyingDisp(xDisp, uno::UNO_QUERY); + if (xNotifyingDisp.is()) + { + xNotifyingDisp->dispatchWithNotification(aCommandURL, rArguments, rListener); + return true; + } + } + + xDisp->dispatch(aCommandURL, rArguments); + + return true; +} + +bool dispatchCommand(const OUString& rCommand, const css::uno::Sequence<css::beans::PropertyValue>& rArguments, const uno::Reference<css::frame::XDispatchResultListener>& rListener) +{ + // Target where we will execute the .uno: command + uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext(); + uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(xContext); + + uno::Reference<frame::XFrame> xFrame(xDesktop->getActiveFrame()); + if (!xFrame.is()) + xFrame = xDesktop; + + return dispatchCommand(rCommand, xFrame, rArguments, rListener); +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/docpasswordhelper.cxx b/comphelper/source/misc/docpasswordhelper.cxx new file mode 100644 index 000000000..5edb3949c --- /dev/null +++ b/comphelper/source/misc/docpasswordhelper.cxx @@ -0,0 +1,741 @@ +/* -*- 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_gpgme.h> + +#include <algorithm> +#include <string_view> + +#include <comphelper/docpasswordhelper.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/hash.hxx> +#include <comphelper/base64.hxx> +#include <comphelper/sequence.hxx> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> + +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <rtl/digest.h> +#include <rtl/random.h> +#include <string.h> + +#if HAVE_FEATURE_GPGME +# include <context.h> +# include <data.h> +# include <decryptionresult.h> +#endif + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::task::PasswordRequestMode; +using ::com::sun::star::task::PasswordRequestMode_PASSWORD_ENTER; +using ::com::sun::star::task::PasswordRequestMode_PASSWORD_REENTER; +using ::com::sun::star::task::XInteractionHandler; +using ::com::sun::star::task::XInteractionRequest; + +using namespace ::com::sun::star; + +namespace comphelper { + + +static uno::Sequence< sal_Int8 > GeneratePBKDF2Hash( std::u16string_view aPassword, const uno::Sequence< sal_Int8 >& aSalt, sal_Int32 nCount, sal_Int32 nHashLength ) +{ + uno::Sequence< sal_Int8 > aResult; + + if ( !aPassword.empty() && aSalt.hasElements() && nCount && nHashLength ) + { + OString aBytePass = OUStringToOString( aPassword, RTL_TEXTENCODING_UTF8 ); + // FIXME this is subject to the SHA1-bug tdf#114939 - see also + // RequestPassword() in filedlghelper.cxx + aResult.realloc( 16 ); + rtl_digest_PBKDF2( reinterpret_cast < sal_uInt8 * > ( aResult.getArray() ), + aResult.getLength(), + reinterpret_cast < const sal_uInt8 * > ( aBytePass.getStr() ), + aBytePass.getLength(), + reinterpret_cast < const sal_uInt8 * > ( aSalt.getConstArray() ), + aSalt.getLength(), + nCount ); + } + + return aResult; +} + + +IDocPasswordVerifier::~IDocPasswordVerifier() +{ +} + + +uno::Sequence< beans::PropertyValue > DocPasswordHelper::GenerateNewModifyPasswordInfo( std::u16string_view aPassword ) +{ + uno::Sequence< beans::PropertyValue > aResult; + + uno::Sequence< sal_Int8 > aSalt = GenerateRandomByteSequence( 16 ); + sal_Int32 const nPBKDF2IterationCount = 100000; + + uno::Sequence< sal_Int8 > aNewHash = GeneratePBKDF2Hash(aPassword, aSalt, nPBKDF2IterationCount, 16); + if ( aNewHash.hasElements() ) + { + aResult = { comphelper::makePropertyValue("algorithm-name", OUString( "PBKDF2" )), + comphelper::makePropertyValue("salt", aSalt), + comphelper::makePropertyValue("iteration-count", nPBKDF2IterationCount), + comphelper::makePropertyValue("hash", aNewHash) }; + } + + return aResult; +} + + +uno::Sequence<beans::PropertyValue> +DocPasswordHelper::GenerateNewModifyPasswordInfoOOXML(std::u16string_view aPassword) +{ + uno::Sequence<beans::PropertyValue> aResult; + + if (!aPassword.empty()) + { + uno::Sequence<sal_Int8> aSalt = GenerateRandomByteSequence(16); + OUStringBuffer aBuffer(22); + comphelper::Base64::encode(aBuffer, aSalt); + OUString sSalt = aBuffer.makeStringAndClear(); + + sal_Int32 const nIterationCount = 100000; + OUString sAlgorithm("SHA-512"); + + const OUString sHash(GetOoxHashAsBase64(OUString(aPassword), sSalt, nIterationCount, + comphelper::Hash::IterCount::APPEND, sAlgorithm)); + + if (!sHash.isEmpty()) + { + aResult = { comphelper::makePropertyValue("algorithm-name", sAlgorithm), + comphelper::makePropertyValue("salt", sSalt), + comphelper::makePropertyValue("iteration-count", nIterationCount), + comphelper::makePropertyValue("hash", sHash) }; + } + } + + return aResult; +} + + +uno::Sequence< beans::PropertyValue > DocPasswordHelper::ConvertPasswordInfo( const uno::Sequence< beans::PropertyValue >& aInfo ) +{ + uno::Sequence< beans::PropertyValue > aResult; + OUString sAlgorithm, sHash, sSalt, sCount; + sal_Int32 nAlgorithm = 0; + + for ( const auto & prop : aInfo ) + { + if ( prop.Name == "cryptAlgorithmSid" ) + { + prop.Value >>= sAlgorithm; + nAlgorithm = sAlgorithm.toInt32(); + } + else if ( prop.Name == "salt" ) + prop.Value >>= sSalt; + else if ( prop.Name == "cryptSpinCount" ) + prop.Value >>= sCount; + else if ( prop.Name == "hash" ) + prop.Value >>= sHash; + } + + if (nAlgorithm == 1) + sAlgorithm = "MD2"; + else if (nAlgorithm == 2) + sAlgorithm = "MD4"; + else if (nAlgorithm == 3) + sAlgorithm = "MD5"; + else if (nAlgorithm == 4) + sAlgorithm = "SHA-1"; + else if (nAlgorithm == 5) + sAlgorithm = "MAC"; + else if (nAlgorithm == 6) + sAlgorithm = "RIPEMD"; + else if (nAlgorithm == 7) + sAlgorithm = "RIPEMD-160"; + else if (nAlgorithm == 9) + sAlgorithm = "HMAC"; + else if (nAlgorithm == 12) + sAlgorithm = "SHA-256"; + else if (nAlgorithm == 13) + sAlgorithm = "SHA-384"; + else if (nAlgorithm == 14) + sAlgorithm = "SHA-512"; + + if ( !sCount.isEmpty() ) + { + sal_Int32 nCount = sCount.toInt32(); + aResult = { comphelper::makePropertyValue("algorithm-name", sAlgorithm), + comphelper::makePropertyValue("salt", sSalt), + comphelper::makePropertyValue("iteration-count", nCount), + comphelper::makePropertyValue("hash", sHash) }; + } + + return aResult; +} + + +bool DocPasswordHelper::IsModifyPasswordCorrect( std::u16string_view aPassword, const uno::Sequence< beans::PropertyValue >& aInfo ) +{ + bool bResult = false; + if ( !aPassword.empty() && aInfo.hasElements() ) + { + OUString sAlgorithm; + uno::Any aSalt, aHash; + sal_Int32 nCount = 0; + + for ( const auto & prop : aInfo ) + { + if ( prop.Name == "algorithm-name" ) + prop.Value >>= sAlgorithm; + else if ( prop.Name == "salt" ) + aSalt = prop.Value; + else if ( prop.Name == "iteration-count" ) + prop.Value >>= nCount; + else if ( prop.Name == "hash" ) + aHash = prop.Value; + } + + if ( sAlgorithm == "PBKDF2" ) + { + uno::Sequence<sal_Int8> aIntSalt, aIntHash; + aSalt >>= aIntSalt; + aHash >>= aIntHash; + if (aIntSalt.hasElements() && nCount > 0 && aIntHash.hasElements()) + { + uno::Sequence<sal_Int8> aNewHash + = GeneratePBKDF2Hash(aPassword, aIntSalt, nCount, aIntHash.getLength()); + for (sal_Int32 nInd = 0; nInd < aNewHash.getLength() && nInd < aIntHash.getLength() + && aNewHash[nInd] == aIntHash[nInd]; + nInd++) + { + if (nInd == aNewHash.getLength() - 1 && nInd == aIntHash.getLength() - 1) + bResult = true; + } + } + } + else if (nCount > 0) + { + OUString sSalt, sHash; + aSalt >>= sSalt; + aHash >>= sHash; + if (!sSalt.isEmpty() && !sHash.isEmpty()) + { + const OUString aNewHash(GetOoxHashAsBase64(OUString(aPassword), sSalt, nCount, + comphelper::Hash::IterCount::APPEND, + sAlgorithm)); + if (!aNewHash.isEmpty()) + bResult = aNewHash == sHash; + } + } + } + + return bResult; +} + + +sal_uInt32 DocPasswordHelper::GetWordHashAsUINT32( + std::u16string_view aUString ) +{ + static const sal_uInt16 pInitialCode[] = { + 0xE1F0, // 1 + 0x1D0F, // 2 + 0xCC9C, // 3 + 0x84C0, // 4 + 0x110C, // 5 + 0x0E10, // 6 + 0xF1CE, // 7 + 0x313E, // 8 + 0x1872, // 9 + 0xE139, // 10 + 0xD40F, // 11 + 0x84F9, // 12 + 0x280C, // 13 + 0xA96A, // 14 + 0x4EC3 // 15 + }; + + static const sal_uInt16 pEncryptionMatrix[15][7] = { + { 0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09}, // last-14 + { 0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF}, // last-13 + { 0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0}, // last-12 + { 0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40}, // last-11 + { 0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5}, // last-10 + { 0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A}, // last-9 + { 0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9}, // last-8 + { 0x47D3, 0x8FA6, 0x8FA6, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0}, // last-7 + { 0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC}, // last-6 + { 0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10}, // last-5 + { 0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168}, // last-4 + { 0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C}, // last-3 + { 0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD}, // last-2 + { 0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC}, // last-1 + { 0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4} // last + }; + + sal_uInt32 nResult = 0; + size_t nLen = aUString.size(); + + if ( nLen ) + { + if ( nLen > 15 ) + nLen = 15; + + sal_uInt16 nHighResult = pInitialCode[nLen - 1]; + sal_uInt16 nLowResult = 0; + + for ( size_t nInd = 0; nInd < nLen; nInd++ ) + { + // NO Encoding during conversion! + // The specification says that the low byte should be used in case it is not NULL + char nHighChar = static_cast<char>( aUString[nInd] >> 8 ); + char nLowChar = static_cast<char>( aUString[nInd] & 0xFF ); + char nChar = nLowChar ? nLowChar : nHighChar; + + for ( int nMatrixInd = 0; nMatrixInd < 7; ++nMatrixInd ) + { + if ( ( nChar & ( 1 << nMatrixInd ) ) != 0 ) + nHighResult = nHighResult ^ pEncryptionMatrix[15 - nLen + nInd][nMatrixInd]; + } + + nLowResult = ( ( ( nLowResult >> 14 ) & 0x0001 ) | ( ( nLowResult << 1 ) & 0x7FFF ) ) ^ nChar; + } + + nLowResult = static_cast<sal_uInt16>( ( ( ( nLowResult >> 14 ) & 0x001 ) | ( ( nLowResult << 1 ) & 0x7FF ) ) ^ nLen ^ 0xCE4B ); + + nResult = ( nHighResult << 16 ) | nLowResult; + } + + return nResult; +} + + +sal_uInt16 DocPasswordHelper::GetXLHashAsUINT16( + std::u16string_view aUString, + rtl_TextEncoding nEnc ) +{ + sal_uInt16 nResult = 0; + + OString aString = OUStringToOString( aUString, nEnc ); + + if ( !aString.isEmpty() && aString.getLength() <= SAL_MAX_UINT16 ) + { + for ( sal_Int32 nInd = aString.getLength() - 1; nInd >= 0; nInd-- ) + { + nResult = ( ( nResult >> 14 ) & 0x01 ) | ( ( nResult << 1 ) & 0x7FFF ); + nResult ^= aString[nInd]; + } + + nResult = ( ( nResult >> 14 ) & 0x01 ) | ( ( nResult << 1 ) & 0x7FFF ); + nResult ^= ( 0x8000 | ( 'N' << 8 ) | 'K' ); + nResult ^= aString.getLength(); + } + + return nResult; +} + + +Sequence< sal_Int8 > DocPasswordHelper::GetXLHashAsSequence( + std::u16string_view aUString ) +{ + sal_uInt16 nHash = GetXLHashAsUINT16( aUString ); + return {sal_Int8(nHash >> 8), sal_Int8(nHash & 0xFF)}; +} + + +std::vector<unsigned char> DocPasswordHelper::GetOoxHashAsVector( + const OUString& rPassword, + const std::vector<unsigned char>& rSaltValue, + sal_uInt32 nSpinCount, + comphelper::Hash::IterCount eIterCount, + std::u16string_view rAlgorithmName) +{ + comphelper::HashType eType; + if (rAlgorithmName == u"SHA-512" || rAlgorithmName == u"SHA512") + eType = comphelper::HashType::SHA512; + else if (rAlgorithmName == u"SHA-256" || rAlgorithmName == u"SHA256") + eType = comphelper::HashType::SHA256; + else if (rAlgorithmName == u"SHA-1" || rAlgorithmName == u"SHA1") // "SHA1" might be in the wild + eType = comphelper::HashType::SHA1; + else if (rAlgorithmName == u"MD5") + eType = comphelper::HashType::MD5; + else + return std::vector<unsigned char>(); + + return comphelper::Hash::calculateHash( rPassword, rSaltValue, nSpinCount, eIterCount, eType); +} + + +css::uno::Sequence<sal_Int8> DocPasswordHelper::GetOoxHashAsSequence( + const OUString& rPassword, + std::u16string_view rSaltValue, + sal_uInt32 nSpinCount, + comphelper::Hash::IterCount eIterCount, + std::u16string_view rAlgorithmName) +{ + std::vector<unsigned char> aSaltVec; + if (!rSaltValue.empty()) + { + css::uno::Sequence<sal_Int8> aSaltSeq; + comphelper::Base64::decode( aSaltSeq, rSaltValue); + aSaltVec = comphelper::sequenceToContainer<std::vector<unsigned char>>( aSaltSeq); + } + + std::vector<unsigned char> hash( GetOoxHashAsVector( rPassword, aSaltVec, nSpinCount, eIterCount, rAlgorithmName)); + + return comphelper::containerToSequence<sal_Int8>( hash); +} + +OUString DocPasswordHelper::GetOoxHashAsBase64( + const OUString& rPassword, + std::u16string_view rSaltValue, + sal_uInt32 nSpinCount, + comphelper::Hash::IterCount eIterCount, + std::u16string_view rAlgorithmName) +{ + css::uno::Sequence<sal_Int8> aSeq( GetOoxHashAsSequence( rPassword, rSaltValue, nSpinCount, + eIterCount, rAlgorithmName)); + + OUStringBuffer aBuf((aSeq.getLength()+2)/3*4); + comphelper::Base64::encode( aBuf, aSeq); + return aBuf.makeStringAndClear(); +} + + +/*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateRandomByteSequence( sal_Int32 nLength ) +{ + uno::Sequence< sal_Int8 > aResult( nLength ); + + rtlRandomPool aRandomPool = rtl_random_createPool (); + rtl_random_getBytes ( aRandomPool, aResult.getArray(), nLength ); + rtl_random_destroyPool ( aRandomPool ); + + return aResult; +} + + +/*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateStd97Key( std::u16string_view aPassword, const uno::Sequence< sal_Int8 >& aDocId ) +{ + uno::Sequence< sal_Int8 > aResultKey; + if ( !aPassword.empty() && aDocId.getLength() == 16 ) + { + sal_uInt16 pPassData[16] = {}; + + sal_Int32 nPassLen = std::min< sal_Int32 >( aPassword.size(), 15 ); + memcpy( pPassData, aPassword.data(), nPassLen * sizeof(pPassData[0]) ); + + aResultKey = GenerateStd97Key( pPassData, aDocId ); + } + + return aResultKey; +} + + +/*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateStd97Key( const sal_uInt16 pPassData[16], const uno::Sequence< sal_Int8 >& aDocId ) +{ + uno::Sequence< sal_Int8 > aResultKey; + + if ( aDocId.getLength() == 16 ) + aResultKey = GenerateStd97Key(pPassData, reinterpret_cast<const sal_uInt8*>(aDocId.getConstArray())); + + return aResultKey; +} + + +/*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateStd97Key( const sal_uInt16 pPassData[16], const sal_uInt8 pDocId[16] ) +{ + uno::Sequence< sal_Int8 > aResultKey; + if ( pPassData[0] ) + { + sal_uInt8 pKeyData[64] = {}; + + sal_Int32 nInd = 0; + + // Fill PassData into KeyData. + for ( nInd = 0; nInd < 16 && pPassData[nInd]; nInd++) + { + pKeyData[2*nInd] = sal::static_int_cast< sal_uInt8 >( (pPassData[nInd] >> 0) & 0xff ); + pKeyData[2*nInd + 1] = sal::static_int_cast< sal_uInt8 >( (pPassData[nInd] >> 8) & 0xff ); + } + + pKeyData[2*nInd] = 0x80; + pKeyData[56] = sal::static_int_cast< sal_uInt8 >( nInd << 4 ); + + // Fill raw digest of KeyData into KeyData. + rtlDigest hDigest = rtl_digest_create ( rtl_Digest_AlgorithmMD5 ); + (void)rtl_digest_updateMD5 ( + hDigest, pKeyData, sizeof(pKeyData)); + (void)rtl_digest_rawMD5 ( + hDigest, pKeyData, RTL_DIGEST_LENGTH_MD5); + + // Update digest with KeyData and Unique. + for ( nInd = 0; nInd < 16; nInd++ ) + { + rtl_digest_updateMD5( hDigest, pKeyData, 5 ); + rtl_digest_updateMD5( hDigest, pDocId, 16 ); + } + + // Update digest with padding. + pKeyData[16] = 0x80; + memset( pKeyData + 17, 0, sizeof(pKeyData) - 17 ); + pKeyData[56] = 0x80; + pKeyData[57] = 0x0a; + + rtl_digest_updateMD5( hDigest, &(pKeyData[16]), sizeof(pKeyData) - 16 ); + + // Fill raw digest of above updates + aResultKey.realloc( RTL_DIGEST_LENGTH_MD5 ); + rtl_digest_rawMD5 ( hDigest, reinterpret_cast<sal_uInt8*>(aResultKey.getArray()), aResultKey.getLength() ); + + // Erase KeyData array and leave. + rtl_secureZeroMemory (pKeyData, sizeof(pKeyData)); + + rtl_digest_destroy(hDigest); + } + + return aResultKey; +} + + +/*static*/ css::uno::Sequence< css::beans::NamedValue > DocPasswordHelper::requestAndVerifyDocPassword( + IDocPasswordVerifier& rVerifier, + const css::uno::Sequence< css::beans::NamedValue >& rMediaEncData, + const OUString& rMediaPassword, + const Reference< XInteractionHandler >& rxInteractHandler, + const OUString& rDocumentUrl, + DocPasswordRequestType eRequestType, + const std::vector< OUString >* pDefaultPasswords, + bool* pbIsDefaultPassword ) +{ + css::uno::Sequence< css::beans::NamedValue > aEncData; + OUString aPassword; + DocPasswordVerifierResult eResult = DocPasswordVerifierResult::WrongPassword; + + sal_Int32 nMediaEncDataCount = rMediaEncData.getLength(); + + // tdf#93389: if the document is being restored from autorecovery, we need to add encryption + // data also for real document type. + // TODO: get real filter name here (from CheckPasswd_Impl), to only add necessary data + bool bForSalvage = false; + if (nMediaEncDataCount) + { + for (auto& val : rMediaEncData) + { + if (val.Name == "ForSalvage") + { + --nMediaEncDataCount; // don't consider this element below + val.Value >>= bForSalvage; + break; + } + } + } + + // first, try provided default passwords + if( pbIsDefaultPassword ) + *pbIsDefaultPassword = false; + if( pDefaultPasswords ) + { + for( const auto& rPassword : *pDefaultPasswords ) + { + OSL_ENSURE( !rPassword.isEmpty(), "DocPasswordHelper::requestAndVerifyDocPassword - unexpected empty default password" ); + if( !rPassword.isEmpty() ) + { + eResult = rVerifier.verifyPassword( rPassword, aEncData ); + if (eResult == DocPasswordVerifierResult::OK) + { + aPassword = rPassword; + if (pbIsDefaultPassword) + *pbIsDefaultPassword = true; + } + if( eResult != DocPasswordVerifierResult::WrongPassword ) + break; + } + } + } + + // try media encryption data (skip, if result is OK or ABORT) + if( eResult == DocPasswordVerifierResult::WrongPassword ) + { + if (nMediaEncDataCount) + { + eResult = rVerifier.verifyEncryptionData( rMediaEncData ); + if( eResult == DocPasswordVerifierResult::OK ) + aEncData = rMediaEncData; + } + } + + // try media password (skip, if result is OK or ABORT) + if( eResult == DocPasswordVerifierResult::WrongPassword ) + { + if( !rMediaPassword.isEmpty() ) + { + eResult = rVerifier.verifyPassword( rMediaPassword, aEncData ); + if (eResult == DocPasswordVerifierResult::OK) + aPassword = rMediaPassword; + } + } + + // request a password (skip, if result is OK or ABORT) + if( (eResult == DocPasswordVerifierResult::WrongPassword) && rxInteractHandler.is() ) try + { + PasswordRequestMode eRequestMode = PasswordRequestMode_PASSWORD_ENTER; + while( eResult == DocPasswordVerifierResult::WrongPassword ) + { + rtl::Reference<DocPasswordRequest> pRequest = new DocPasswordRequest( eRequestType, eRequestMode, rDocumentUrl ); + rxInteractHandler->handle( pRequest ); + if( pRequest->isPassword() ) + { + if( !pRequest->getPassword().isEmpty() ) + eResult = rVerifier.verifyPassword( pRequest->getPassword(), aEncData ); + if (eResult == DocPasswordVerifierResult::OK) + aPassword = pRequest->getPassword(); + } + else + { + eResult = DocPasswordVerifierResult::Abort; + } + eRequestMode = PasswordRequestMode_PASSWORD_REENTER; + } + } + catch( Exception& ) + { + } + + if (eResult == DocPasswordVerifierResult::OK && !aPassword.isEmpty()) + { + if (std::find_if(std::cbegin(aEncData), std::cend(aEncData), + [](const css::beans::NamedValue& val) { + return val.Name == PACKAGE_ENCRYPTIONDATA_SHA256UTF8; + }) + == std::cend(aEncData)) + { + // tdf#118639: We need ODF encryption data for autorecovery, where password + // will already be unavailable, so generate and append it here + aEncData = comphelper::concatSequences( + aEncData, OStorageHelper::CreatePackageEncryptionData(aPassword)); + } + + if (bForSalvage) + { + // TODO: add individual methods for different target filter, and only call what's needed + + // 1. Prepare binary MS formats encryption data + auto aUniqueID = GenerateRandomByteSequence(16); + auto aEnc97Key = GenerateStd97Key(aPassword, aUniqueID); + // 2. Add MS binary and OOXML encryption data to result + aEncData = comphelper::concatSequences( + aEncData, std::initializer_list<beans::NamedValue>{ + { "STD97EncryptionKey", css::uno::Any(aEnc97Key) }, + { "STD97UniqueID", css::uno::Any(aUniqueID) }, + { "OOXPassword", css::uno::Any(aPassword) }, + }); + } + } + + return (eResult == DocPasswordVerifierResult::OK) ? aEncData : uno::Sequence< beans::NamedValue >(); +} + +/*static*/ uno::Sequence< css::beans::NamedValue > + DocPasswordHelper::decryptGpgSession( + const uno::Sequence< uno::Sequence< beans::NamedValue > >& rGpgProperties ) +{ +#if HAVE_FEATURE_GPGME + if ( !rGpgProperties.hasElements() ) + return uno::Sequence< beans::NamedValue >(); + + uno::Sequence< beans::NamedValue > aEncryptionData; + std::unique_ptr<GpgME::Context> ctx; + GpgME::initializeLibrary(); + GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP); + if (err) + throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); + + ctx.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP) ); + if (ctx == nullptr) + throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); + ctx->setArmor(false); + + const uno::Sequence < beans::NamedValue > *pSequence = rGpgProperties.getConstArray(); + const sal_Int32 nLength = rGpgProperties.getLength(); + for ( sal_Int32 i = 0; i < nLength ; i++, pSequence++ ) + { + const beans::NamedValue *pValues = pSequence->getConstArray(); + if ( pSequence->getLength() == 3 ) + { + // take CipherValue and try to decrypt that - stop after + // the first successful decryption + + // ctx is setup now, let's decrypt the lot! + uno::Sequence < sal_Int8 > aVector; + pValues[2].Value >>= aVector; + + GpgME::Data cipher( + reinterpret_cast<const char*>(aVector.getConstArray()), + size_t(aVector.getLength()), false); + GpgME::Data plain; + + GpgME::DecryptionResult crypt_res = ctx->decrypt( + cipher, plain); + + // NO_SECKEY -> skip + // BAD_PASSPHRASE -> retry? + + off_t result = plain.seek(0,SEEK_SET); + (void) result; + assert(result == 0); + int len=0, curr=0; char buf; + while( (curr=plain.read(&buf, 1)) ) + len += curr; + + if(crypt_res.error() || !len) + continue; // can't use this key, take next one + + uno::Sequence < sal_Int8 > aKeyValue(len); + result = plain.seek(0,SEEK_SET); + assert(result == 0); + if( plain.read(aKeyValue.getArray(), len) != len ) + throw uno::RuntimeException("The GpgME library failed to read the encrypted value."); + + SAL_INFO("comphelper.crypto", "Extracted gpg session key of length: " << len); + + aEncryptionData = { { PACKAGE_ENCRYPTIONDATA_SHA256UTF8, uno::Any(aKeyValue) } }; + break; + } + } + + if ( aEncryptionData.hasElements() ) + { + uno::Sequence< beans::NamedValue > aContainer{ + { "GpgInfos", uno::Any(rGpgProperties) }, { "EncryptionKey", uno::Any(aEncryptionData) } + }; + + return aContainer; + } +#else + (void)rGpgProperties; +#endif + return uno::Sequence< beans::NamedValue >(); +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/docpasswordrequest.cxx b/comphelper/source/misc/docpasswordrequest.cxx new file mode 100644 index 000000000..6f644336e --- /dev/null +++ b/comphelper/source/misc/docpasswordrequest.cxx @@ -0,0 +1,179 @@ +/* -*- 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 <comphelper/docpasswordrequest.hxx> +#include <com/sun/star/task/DocumentMSPasswordRequest2.hpp> +#include <com/sun/star/task/DocumentPasswordRequest2.hpp> +#include <com/sun/star/task/PasswordRequest.hpp> +#include <com/sun/star/task/XInteractionAbort.hpp> +#include <com/sun/star/task/XInteractionPassword2.hpp> +#include <cppuhelper/implbase.hxx> + +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::task::InteractionClassification_QUERY; +using ::com::sun::star::task::DocumentMSPasswordRequest2; +using ::com::sun::star::task::DocumentPasswordRequest2; +using ::com::sun::star::task::PasswordRequest; +using ::com::sun::star::task::PasswordRequestMode; +using ::com::sun::star::task::XInteractionAbort; +using ::com::sun::star::task::XInteractionContinuation; +using ::com::sun::star::task::XInteractionPassword2; + +namespace comphelper { + +namespace { + +class AbortContinuation : public ::cppu::WeakImplHelper< XInteractionAbort > +{ +public: + virtual void SAL_CALL select() override {} +}; + +} + +class PasswordContinuation : public ::cppu::WeakImplHelper< XInteractionPassword2 > +{ +public: + explicit PasswordContinuation() : mbReadOnly( false ), mbSelected( false ) {} + + bool isSelected() const { return mbSelected; } + + virtual void SAL_CALL select() override { mbSelected = true; } + + virtual void SAL_CALL setPassword( const OUString& rPass ) override { maPassword = rPass; } + virtual OUString SAL_CALL getPassword() override { return maPassword; } + + virtual void SAL_CALL setPasswordToModify( const OUString& rPass ) override { maModifyPassword = rPass; } + virtual OUString SAL_CALL getPasswordToModify() override { return maModifyPassword; } + + virtual void SAL_CALL setRecommendReadOnly( sal_Bool bReadOnly ) override { mbReadOnly = bReadOnly; } + virtual sal_Bool SAL_CALL getRecommendReadOnly() override { return mbReadOnly; } + +private: + OUString maPassword; + OUString maModifyPassword; + bool mbReadOnly; + bool mbSelected; +}; + + +SimplePasswordRequest::SimplePasswordRequest() +{ + PasswordRequest aRequest( OUString(), Reference< XInterface >(), + InteractionClassification_QUERY, css::task::PasswordRequestMode_PASSWORD_CREATE ); + maRequest <<= aRequest; + + mxAbort = new AbortContinuation; + mxPassword = new PasswordContinuation; +} + +SimplePasswordRequest::~SimplePasswordRequest() +{ +} + +bool SimplePasswordRequest::isPassword() const +{ + return mxPassword->isSelected(); +} + +OUString SimplePasswordRequest::getPassword() const +{ + return mxPassword->getPassword(); +} + +Any SAL_CALL SimplePasswordRequest::getRequest() +{ + return maRequest; +} + +Sequence< Reference< XInteractionContinuation > > SAL_CALL SimplePasswordRequest::getContinuations() +{ + return { mxAbort, mxPassword }; +} + + +DocPasswordRequest::DocPasswordRequest( DocPasswordRequestType eType, + PasswordRequestMode eMode, const OUString& rDocumentUrl, bool bPasswordToModify ) +{ + switch( eType ) + { + case DocPasswordRequestType::Standard: + { + DocumentPasswordRequest2 aRequest( OUString(), Reference< XInterface >(), + InteractionClassification_QUERY, eMode, rDocumentUrl, bPasswordToModify ); + maRequest <<= aRequest; + } + break; + case DocPasswordRequestType::MS: + { + DocumentMSPasswordRequest2 aRequest( OUString(), Reference< XInterface >(), + InteractionClassification_QUERY, eMode, rDocumentUrl, bPasswordToModify ); + maRequest <<= aRequest; + } + break; + /* no 'default', so compilers will complain about missing + implementation of a new enum value. */ + } + + mxAbort = new AbortContinuation; + mxPassword = new PasswordContinuation; +} + +DocPasswordRequest::~DocPasswordRequest() +{ +} + +bool DocPasswordRequest::isPassword() const +{ + return mxPassword->isSelected(); +} + +OUString DocPasswordRequest::getPassword() const +{ + return mxPassword->getPassword(); +} + +OUString DocPasswordRequest::getPasswordToModify() const +{ + return mxPassword->getPasswordToModify(); +} + +bool DocPasswordRequest::getRecommendReadOnly() const +{ + return mxPassword->getRecommendReadOnly(); +} + +Any SAL_CALL DocPasswordRequest::getRequest() +{ + return maRequest; +} + +Sequence< Reference< XInteractionContinuation > > SAL_CALL DocPasswordRequest::getContinuations() +{ + return { mxAbort, mxPassword }; +} + + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/documentinfo.cxx b/comphelper/source/misc/documentinfo.cxx new file mode 100644 index 000000000..21425524e --- /dev/null +++ b/comphelper/source/misc/documentinfo.cxx @@ -0,0 +1,177 @@ +/* -*- 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 <comphelper/documentinfo.hxx> +#include <comphelper/namedvaluecollection.hxx> + +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/frame/XTitle.hpp> + +#include <cppuhelper/exc_hlp.hxx> + +#include <sal/log.hxx> + +namespace comphelper { + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::frame::XModel; + using ::com::sun::star::frame::XTitle; + using ::com::sun::star::frame::XController; + using ::com::sun::star::document::XDocumentPropertiesSupplier; + using ::com::sun::star::document::XDocumentProperties; + using ::com::sun::star::frame::XStorable; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::frame::XFrame; + + namespace + { + OUString lcl_getTitle( const Reference< XInterface >& _rxComponent ) + { + Reference< XTitle > xTitle( _rxComponent, UNO_QUERY ); + if ( xTitle.is() ) + return xTitle->getTitle(); + return OUString(); + } + } + + OUString DocumentInfo::getDocumentTitle( const Reference< XModel >& _rxDocument ) + { + OUString sTitle; + + if ( !_rxDocument.is() ) + return sTitle; + + try + { + // 1. ask the model and the controller for their XTitle::getTitle + sTitle = lcl_getTitle( _rxDocument ); + if ( !sTitle.isEmpty() ) + return sTitle; + + Reference< XController > xController( _rxDocument->getCurrentController() ); + sTitle = lcl_getTitle( xController ); + if ( !sTitle.isEmpty() ) + return sTitle; + + // work around a problem with embedded objects, which sometimes return + // private:object as URL + OUString sDocURL = _rxDocument->getURL(); + if ( sDocURL.startsWithIgnoreAsciiCase( "private:" ) ) + sDocURL.clear(); + + // 2. if the document is not saved, yet, check the frame title + if ( sDocURL.isEmpty() ) + { + Reference< XFrame > xFrame; + if ( xController.is() ) + xFrame.set( xController->getFrame() ); + sTitle = lcl_getTitle( xFrame ); + if ( !sTitle.isEmpty() ) + return sTitle; + } + + // 3. try the UNO XDocumentProperties + Reference< XDocumentPropertiesSupplier > xDPS( _rxDocument, UNO_QUERY ); + if ( xDPS.is() ) + { + Reference< XDocumentProperties > xDocProps ( + xDPS->getDocumentProperties(), css::uno::UNO_SET_THROW ); + sTitle = xDocProps->getTitle(); + if ( !sTitle.isEmpty() ) + return sTitle; + } + + // 4. try model arguments + sTitle = NamedValueCollection::getOrDefault( _rxDocument->getArgs(), u"Title", sTitle ); + if ( !sTitle.isEmpty() ) + return sTitle; + + // 5. try the last segment of the document URL + // this formerly was an INetURLObject::getName( LAST_SEGMENT, true, DecodeMechanism::WithCharset ), + // but since we moved this code to comphelper, we do not have access to an INetURLObject anymore + // This heuristics here should be sufficient - finally, we will get a UNO title API in a not + // too distant future (hopefully), then this complete class is superfluous) + if ( sDocURL.isEmpty() ) + { + Reference< XStorable > xDocStorable( _rxDocument, UNO_QUERY_THROW ); + sDocURL = xDocStorable->getLocation(); + } + sal_Int32 nLastSepPos = sDocURL.lastIndexOf( '/' ); + if ( ( nLastSepPos != -1 ) && ( nLastSepPos == sDocURL.getLength() - 1 ) ) + { + sDocURL = sDocURL.copy( 0, nLastSepPos ); + nLastSepPos = sDocURL.lastIndexOf( '/' ); + } + sTitle = sDocURL.copy( nLastSepPos + 1 ); + + if ( !sTitle.isEmpty() ) + return sTitle; + + // 5. + // <-- #i88104# (05-16-08) TKR: use the new XTitle Interface to get the Title --> + + Reference< XTitle > xTitle( _rxDocument, UNO_QUERY ); + if ( xTitle.is() ) + { + if ( !xTitle->getTitle().isEmpty() ) + return xTitle->getTitle(); + } + } + catch ( const Exception& ) + { + // Cannot use tools::exceptionToString here, because the tools module depends on the comphelper module + css::uno::Any caught( ::cppu::getCaughtException() ); + css::uno::Exception exception; + caught >>= exception; + SAL_WARN( "comphelper", "caught an exception!\ntype : " << caught.getValueTypeName() + << "\nmessage: " << exception + << "\nin function:\n" << __func__); + } + + return sTitle; + } + + void DocumentInfo::notifyMacroEventRead(const css::uno::Reference<css::frame::XModel>& rModel) + { + if (!rModel.is()) + return; + + // like BreakMacroSignature of XMLScriptContext use XModel::attachResource + // to propagate this notification + css::uno::Sequence<css::beans::PropertyValue> aMedDescr = rModel->getArgs(); + sal_Int32 nNewLen = aMedDescr.getLength() + 1; + aMedDescr.realloc(nNewLen); + auto pMedDescr = aMedDescr.getArray(); + pMedDescr[nNewLen-1].Name = "MacroEventRead"; + pMedDescr[nNewLen-1].Value <<= true; + rModel->attachResource(rModel->getURL(), aMedDescr); + } + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/evtlistenerhlp.cxx b/comphelper/source/misc/evtlistenerhlp.cxx new file mode 100644 index 000000000..2eac31580 --- /dev/null +++ b/comphelper/source/misc/evtlistenerhlp.cxx @@ -0,0 +1,37 @@ +/* -*- 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 <comphelper/evtlistenerhlp.hxx> + +namespace comphelper +{ + OEventListenerHelper::OEventListenerHelper(const css::uno::Reference< css::lang::XEventListener>& + _rxListener) : m_xListener(_rxListener) + { + } + void SAL_CALL OEventListenerHelper::disposing( const css::lang::EventObject& Source ) + { + css::uno::Reference< css::lang::XEventListener> xRef = m_xListener; + if(xRef.is()) + xRef->disposing(Source); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/evtmethodhelper.cxx b/comphelper/source/misc/evtmethodhelper.cxx new file mode 100644 index 000000000..5f60d92d6 --- /dev/null +++ b/comphelper/source/misc/evtmethodhelper.cxx @@ -0,0 +1,59 @@ +/* -*- 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 <comphelper/evtmethodhelper.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Type; + +namespace comphelper +{ + + Sequence< OUString> getEventMethodsForType(const Type& type) + { + typelib_InterfaceTypeDescription *pType=nullptr; + type.getDescription(reinterpret_cast<typelib_TypeDescription**>(&pType)); + + if(!pType) + return Sequence< OUString>(); + + Sequence< OUString> aNames(pType->nMembers); + OUString* pNames = aNames.getArray(); + for(sal_Int32 i=0;i<pType->nMembers;i++,++pNames) + { + // the description reference + typelib_TypeDescriptionReference* pMemberDescriptionReference = pType->ppMembers[i]; + // the description for the reference + typelib_TypeDescription* pMemberDescription = nullptr; + typelib_typedescriptionreference_getDescription(&pMemberDescription, pMemberDescriptionReference); + if (pMemberDescription) + { + typelib_InterfaceMemberTypeDescription* pRealMemberDescription = + reinterpret_cast<typelib_InterfaceMemberTypeDescription*>(pMemberDescription); + *pNames = pRealMemberDescription->pMemberName; + } + } + typelib_typedescription_release( &pType->aBase ); + return aNames; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/fileurl.cxx b/comphelper/source/misc/fileurl.cxx new file mode 100644 index 000000000..4f361ab4b --- /dev/null +++ b/comphelper/source/misc/fileurl.cxx @@ -0,0 +1,27 @@ +/* -*- 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 <sal/config.h> + +#include <comphelper/fileurl.hxx> +#include <rtl/ustring.hxx> + +bool comphelper::isFileUrl(OUString const& url) { return url.startsWithIgnoreAsciiCase("file:"); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/getexpandeduri.cxx b/comphelper/source/misc/getexpandeduri.cxx new file mode 100644 index 000000000..853e5dbd3 --- /dev/null +++ b/comphelper/source/misc/getexpandeduri.cxx @@ -0,0 +1,32 @@ +/* -*- 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/. + */ + +#include <sal/config.h> + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarExpandUrlReference.hpp> +#include <com/sun/star/util/theMacroExpander.hpp> +#include <comphelper/getexpandeduri.hxx> +#include <rtl/ustring.hxx> + +namespace com::sun::star::uno { class XComponentContext; } + +OUString comphelper::getExpandedUri( + css::uno::Reference<css::uno::XComponentContext> const & context, + OUString const & uri) +{ + css::uno::Reference<css::uri::XVndSunStarExpandUrlReference> ref( + css::uri::UriReferenceFactory::create(context)->parse(uri), + css::uno::UNO_QUERY); + return ref.is() + ? ref->expand(css::util::theMacroExpander::get(context)) : uri; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/graphicmimetype.cxx b/comphelper/source/misc/graphicmimetype.cxx new file mode 100644 index 000000000..bfa7e5c6a --- /dev/null +++ b/comphelper/source/misc/graphicmimetype.cxx @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 <comphelper/graphicmimetype.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/uno/Reference.hxx> + +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> + +using namespace css; +using namespace css::beans; +using namespace css::graphic; +using namespace css::io; +using namespace css::uno; + +namespace comphelper +{ +OUString GraphicMimeTypeHelper::GetMimeTypeForExtension(std::string_view rExt) +{ + struct XMLGraphicMimeTypeMapper + { + const char* pExt; + const char* pMimeType; + }; + + static const XMLGraphicMimeTypeMapper aMapper[] + = { { "gif", "image/gif" }, { "png", "image/png" }, { "jpg", "image/jpeg" }, + { "tif", "image/tiff" }, { "svg", "image/svg+xml" }, { "pdf", "application/pdf" }, + { "wmf", "image/x-wmf" }, { "emf", "image/x-emf" }, { "eps", "image/x-eps" }, + { "bmp", "image/bmp" }, { "pct", "image/x-pict" }, { "svm", "image/x-svm" } }; + + OUString aMimeType; + + tools::Long const nCount = std::size(aMapper); + for (tools::Long i = 0; (i < nCount) && aMimeType.isEmpty(); ++i) + { + if (rExt == aMapper[i].pExt) + aMimeType = OUString(aMapper[i].pMimeType, strlen(aMapper[i].pMimeType), + RTL_TEXTENCODING_ASCII_US); + } + + return aMimeType; +} + +OUString GraphicMimeTypeHelper::GetMimeTypeForXGraphic(const Reference<XGraphic>& xGraphic) +{ + OUString aSourceMimeType; + Reference<XPropertySet> const xGraphicPropertySet(xGraphic, UNO_QUERY); + if (xGraphicPropertySet.is() && // it's null if it's an external link + (xGraphicPropertySet->getPropertyValue("MimeType") >>= aSourceMimeType)) + { + return aSourceMimeType; + } + return ""; +} + +OUString +GraphicMimeTypeHelper::GetMimeTypeForImageStream(const Reference<XInputStream>& xInputStream) +{ + // Create the graphic to retrieve the mimetype from it + Reference<XGraphicProvider> xProvider + = css::graphic::GraphicProvider::create(comphelper::getProcessComponentContext()); + Sequence<PropertyValue> aMediaProperties{ comphelper::makePropertyValue("InputStream", + xInputStream) }; + Reference<XGraphic> xGraphic(xProvider->queryGraphic(aMediaProperties)); + + return GetMimeTypeForXGraphic(xGraphic); +} + +OUString GraphicMimeTypeHelper::GetMimeTypeForConvertDataFormat(ConvertDataFormat convertDataFormat) +{ + switch (convertDataFormat) + { + case ConvertDataFormat::BMP: + return "image/bmp"; + case ConvertDataFormat::GIF: + return "image/gif"; + case ConvertDataFormat::JPG: + return "image/jpeg"; + case ConvertDataFormat::PCT: + return "image/x-pict"; + case ConvertDataFormat::PNG: + return "image/png"; + case ConvertDataFormat::SVM: + return "image/x-svm"; + case ConvertDataFormat::TIF: + return "image/tiff"; + case ConvertDataFormat::WMF: + return "image/x-wmf"; + case ConvertDataFormat::EMF: + return "image/x-emf"; + case ConvertDataFormat::SVG: + return "image/svg+xml"; + case ConvertDataFormat::MET: // What is this? + case ConvertDataFormat::Unknown: + default: + return ""; + } +} + +char const* GraphicMimeTypeHelper::GetExtensionForConvertDataFormat(ConvertDataFormat nFormat) +{ + char const* pExt = nullptr; + // create extension + if (nFormat != ConvertDataFormat::Unknown) + { + switch (nFormat) + { + case ConvertDataFormat::BMP: + pExt = ".bmp"; + break; + case ConvertDataFormat::GIF: + pExt = ".gif"; + break; + case ConvertDataFormat::JPG: + pExt = ".jpg"; + break; + case ConvertDataFormat::MET: + pExt = ".met"; + break; + case ConvertDataFormat::PCT: + pExt = ".pct"; + break; + case ConvertDataFormat::PNG: + pExt = ".png"; + break; + case ConvertDataFormat::SVM: + pExt = ".svm"; + break; + case ConvertDataFormat::TIF: + pExt = ".tif"; + break; + case ConvertDataFormat::WMF: + pExt = ".wmf"; + break; + case ConvertDataFormat::EMF: + pExt = ".emf"; + break; + + default: + pExt = ".grf"; + break; + } + } + return pExt; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/hash.cxx b/comphelper/source/misc/hash.cxx new file mode 100644 index 000000000..50fd8cd0f --- /dev/null +++ b/comphelper/source/misc/hash.cxx @@ -0,0 +1,257 @@ +/* -*- 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/. + */ + +#include <sal/config.h> + +#include <com/sun/star/uno/RuntimeException.hpp> +#include <comphelper/hash.hxx> +#include <rtl/ustring.hxx> +#include <rtl/alloc.h> +#include <osl/endian.h> +#include <config_oox.h> + +#if USE_TLS_NSS +#include <nss.h> +#include <sechash.h> +#elif USE_TLS_OPENSSL +#include <openssl/evp.h> +#include <openssl/sha.h> +#endif // USE_TLS_OPENSSL + +namespace comphelper { + +struct HashImpl +{ + +#if USE_TLS_NSS + HASHContext* mpContext; + + HASH_HashType getNSSType() const + { + switch (meType) + { + case HashType::MD5: + return HASH_AlgMD5; + case HashType::SHA1: + return HASH_AlgSHA1; + case HashType::SHA256: + return HASH_AlgSHA256; + case HashType::SHA512: + return HASH_AlgSHA512; + } + + return HASH_AlgNULL; + } +#elif USE_TLS_OPENSSL + EVP_MD_CTX* mpContext; + + const EVP_MD* getOpenSSLType() const + { + switch (meType) + { + case HashType::MD5: + return EVP_md5(); + case HashType::SHA1: + return EVP_sha1(); + case HashType::SHA256: + return EVP_sha256(); + case HashType::SHA512: + return EVP_sha512(); + } + + return nullptr; + } +#endif + + HashType const meType; + + HashImpl(HashType eType): + meType(eType) + { + +#if USE_TLS_NSS + auto const e = NSS_NoDB_Init(nullptr); + if (e != SECSuccess) { + throw css::uno::RuntimeException("NSS_NoDB_Init failed with " + OUString::number(e)); + } + mpContext = HASH_Create(getNSSType()); + HASH_Begin(mpContext); +#elif USE_TLS_OPENSSL + mpContext = EVP_MD_CTX_create(); + EVP_DigestInit_ex(mpContext, getOpenSSLType(), nullptr); +#endif + } + + ~HashImpl() + { +#if USE_TLS_NSS + HASH_Destroy(mpContext); +#elif USE_TLS_OPENSSL + EVP_MD_CTX_destroy(mpContext); +#endif + } +}; + +Hash::Hash(HashType eType): + mpImpl(new HashImpl(eType)) +{ +} + +Hash::~Hash() +{ +} + +void Hash::update(const unsigned char* pInput, size_t length) +{ +#if USE_TLS_NSS + HASH_Update(mpImpl->mpContext, pInput, length); +#elif USE_TLS_OPENSSL + EVP_DigestUpdate(mpImpl->mpContext, pInput, length); +#else + (void)pInput; + (void)length; +#endif +} + +std::vector<unsigned char> Hash::finalize() +{ + std::vector<unsigned char> hash(getLength(), 0); + unsigned int digestWrittenLength; +#if USE_TLS_NSS + HASH_End(mpImpl->mpContext, hash.data(), &digestWrittenLength, getLength()); +#elif USE_TLS_OPENSSL + EVP_DigestFinal_ex(mpImpl->mpContext, hash.data(), &digestWrittenLength); +#else + (void)digestWrittenLength; +#endif + + return hash; +} + +size_t Hash::getLength() const +{ + switch (mpImpl->meType) + { + case HashType::MD5: + return MD5_HASH_LENGTH; + case HashType::SHA1: + return SHA1_HASH_LENGTH; + case HashType::SHA256: + return SHA256_HASH_LENGTH; + case HashType::SHA512: + return SHA512_HASH_LENGTH; + } + + return 0; +} + +std::vector<unsigned char> Hash::calculateHash(const unsigned char* pInput, size_t length, HashType eType) +{ + Hash aHash(eType); + aHash.update(pInput, length); + return aHash.finalize(); +} + +std::vector<unsigned char> Hash::calculateHash( + const unsigned char* pInput, size_t nLength, + const unsigned char* pSalt, size_t nSaltLen, + sal_uInt32 nSpinCount, + IterCount eIterCount, + HashType eType) +{ + if (!pSalt) + nSaltLen = 0; + + if (!nSaltLen && !nSpinCount) + return calculateHash( pInput, nLength, eType); + + Hash aHash(eType); + if (nSaltLen) + { + std::vector<unsigned char> initialData( nSaltLen + nLength); + std::copy( pSalt, pSalt + nSaltLen, initialData.begin()); + std::copy( pInput, pInput + nLength, initialData.begin() + nSaltLen); + aHash.update( initialData.data(), initialData.size()); + rtl_secureZeroMemory( initialData.data(), initialData.size()); + } + else + { + aHash.update( pInput, nLength); + } + std::vector<unsigned char> hash( aHash.finalize()); + + if (nSpinCount) + { + // https://msdn.microsoft.com/en-us/library/dd920692 + // says the iteration is concatenated after the hash. + // https://msdn.microsoft.com/en-us/library/dd924776 and + // https://msdn.microsoft.com/en-us/library/dd925430 + // say the iteration is prepended to the hash. + const size_t nAddIter = (eIterCount == IterCount::NONE ? 0 : 4); + const size_t nIterPos = (eIterCount == IterCount::APPEND ? hash.size() : 0); + const size_t nHashPos = (eIterCount == IterCount::PREPEND ? nAddIter : 0); + std::vector<unsigned char> data( hash.size() + nAddIter, 0); + for (sal_uInt32 i = 0; i < nSpinCount; ++i) + { + std::copy( hash.begin(), hash.end(), data.begin() + nHashPos); + if (nAddIter) + { +#ifdef OSL_BIGENDIAN + sal_uInt32 be = i; + sal_uInt8* p = reinterpret_cast<sal_uInt8*>(&be); + std::swap( p[0], p[3] ); + std::swap( p[1], p[2] ); + memcpy( data.data() + nIterPos, &be, nAddIter); +#else + memcpy( data.data() + nIterPos, &i, nAddIter); +#endif + } + /* TODO: isn't there something better than + * creating/finalizing/destroying on each iteration? */ + Hash aReHash(eType); + aReHash.update( data.data(), data.size()); + hash = aReHash.finalize(); + } + } + + return hash; +} + +std::vector<unsigned char> Hash::calculateHash( + const OUString& rPassword, + const std::vector<unsigned char>& rSaltValue, + sal_uInt32 nSpinCount, + IterCount eIterCount, + HashType eType) +{ + const unsigned char* pPassBytes = reinterpret_cast<const unsigned char*>(rPassword.getStr()); + const size_t nPassBytesLen = rPassword.getLength() * 2; +#ifdef OSL_BIGENDIAN + // Swap UTF16-BE to UTF16-LE + std::vector<unsigned char> vPass; + if (nPassBytesLen) + { + vPass.resize( nPassBytesLen); + std::copy( pPassBytes, pPassBytes + nPassBytesLen, vPass.begin()); + unsigned char* p = vPass.data(); + unsigned char const * const pEnd = p + nPassBytesLen; + for ( ; p < pEnd; p += 2 ) + { + std::swap( p[0], p[1] ); + } + pPassBytes = vPass.data(); + } +#endif + return calculateHash( pPassBytes, nPassBytesLen, rSaltValue.data(), rSaltValue.size(), nSpinCount, + eIterCount, eType); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/instancelocker.cxx b/comphelper/source/misc/instancelocker.cxx new file mode 100644 index 000000000..84c8054ec --- /dev/null +++ b/comphelper/source/misc/instancelocker.cxx @@ -0,0 +1,445 @@ +/* -*- 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 <cppuhelper/supportsservice.hxx> + +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/util/XCloseBroadcaster.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/frame/XDesktop.hpp> +#include <com/sun/star/frame/TerminationVetoException.hpp> +#include <com/sun/star/frame/DoubleInitializationException.hpp> +#include <com/sun/star/embed/Actions.hpp> +#include <com/sun/star/embed/XActionsApproval.hpp> +#include <utility> + +#include "instancelocker.hxx" + +namespace com::sun::star::uno { class XComponentContext; } + +using namespace ::com::sun::star; + + +// OInstanceLocker + + +OInstanceLocker::OInstanceLocker() +: m_bDisposed( false ) +, m_bInitialized( false ) +{ +} + + +OInstanceLocker::~OInstanceLocker() +{ + if ( !m_bDisposed ) + { + osl_atomic_increment(&m_refCount); // to call dispose + try { + dispose(); + } + catch ( uno::RuntimeException& ) + {} + } +} + +// XComponent + +void SAL_CALL OInstanceLocker::dispose() +{ + std::unique_lock aGuard( m_aMutex ); + + if ( m_bDisposed ) + throw lang::DisposedException(); + + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >(this) ); + m_aListenersContainer.disposeAndClear( aGuard, aSource ); + if ( m_xLockListener.is() ) + { + auto tmp = std::move(m_xLockListener); + aGuard.unlock(); + tmp->Dispose(); + aGuard.lock(); + } + + m_bDisposed = true; +} + + +void SAL_CALL OInstanceLocker::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + m_aListenersContainer.addInterface( aGuard, xListener ); +} + + +void SAL_CALL OInstanceLocker::removeEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + m_aListenersContainer.removeInterface( aGuard, xListener ); +} + +// XInitialization + +void SAL_CALL OInstanceLocker::initialize( const uno::Sequence< uno::Any >& aArguments ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( m_bInitialized ) + throw frame::DoubleInitializationException(); + + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_refCount ) + throw uno::RuntimeException(); // the object must be refcounted already! + + uno::Reference< uno::XInterface > xInstance; + uno::Reference< embed::XActionsApproval > xApproval; + + try + { + sal_Int32 nLen = aArguments.getLength(); + if ( nLen < 2 || nLen > 3 ) + throw lang::IllegalArgumentException( + "Wrong count of parameters!", + uno::Reference< uno::XInterface >(), + 0 ); + + if ( !( aArguments[0] >>= xInstance ) || !xInstance.is() ) + throw lang::IllegalArgumentException( + "Nonempty reference is expected as the first argument!", + uno::Reference< uno::XInterface >(), + 0 ); + + sal_Int32 nModes = 0; + if ( + !( aArguments[1] >>= nModes ) || + ( + !( nModes & embed::Actions::PREVENT_CLOSE ) && + !( nModes & embed::Actions::PREVENT_TERMINATION ) + ) + ) + { + throw lang::IllegalArgumentException( + "The correct modes set is expected as the second argument!", + uno::Reference< uno::XInterface >(), + 0 ); + } + + if ( nLen == 3 && !( aArguments[2] >>= xApproval ) ) + throw lang::IllegalArgumentException( + "If the third argument is provided, it must be XActionsApproval implementation!", + uno::Reference< uno::XInterface >(), + 0 ); + + m_xLockListener = new OLockListener( uno::Reference< lang::XComponent > ( static_cast< lang::XComponent* >( this ) ), + xInstance, + nModes, + xApproval ); + m_xLockListener->Init(); + } + catch( uno::Exception& ) + { + aGuard.unlock(); + dispose(); + throw; + } + + m_bInitialized = true; +} + +// XServiceInfo +OUString SAL_CALL OInstanceLocker::getImplementationName( ) +{ + return "com.sun.star.comp.embed.InstanceLocker"; +} + +sal_Bool SAL_CALL OInstanceLocker::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL OInstanceLocker::getSupportedServiceNames() +{ + return { "com.sun.star.embed.InstanceLocker" }; +} + +// OLockListener + + +OLockListener::OLockListener( uno::WeakReference< lang::XComponent > xWrapper, + uno::Reference< uno::XInterface > xInstance, + sal_Int32 nMode, + uno::Reference< embed::XActionsApproval > xApproval ) +: m_xInstance(std::move( xInstance )) +, m_xApproval(std::move( xApproval )) +, m_xWrapper(std::move( xWrapper )) +, m_bDisposed( false ) +, m_bInitialized( false ) +, m_nMode( nMode ) +{ +} + + +OLockListener::~OLockListener() +{ +} + + +void OLockListener::Dispose() +{ + std::unique_lock aGuard( m_aMutex ); + + if ( m_bDisposed ) + return; + + auto xInstance = std::move(m_xInstance); + auto nMode = m_nMode; + m_bDisposed = true; + aGuard.unlock(); + + if ( nMode & embed::Actions::PREVENT_CLOSE ) + { + try + { + uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( xInstance, uno::UNO_QUERY ); + if ( xCloseBroadcaster.is() ) + xCloseBroadcaster->removeCloseListener( static_cast< util::XCloseListener* >( this ) ); + + uno::Reference< util::XCloseable > xCloseable( xInstance, uno::UNO_QUERY ); + if ( xCloseable.is() ) + xCloseable->close( true ); + } + catch( uno::Exception& ) + {} + } + + if ( nMode & embed::Actions::PREVENT_TERMINATION ) + { + try + { + uno::Reference< frame::XDesktop > xDesktop( xInstance, uno::UNO_QUERY_THROW ); + xDesktop->removeTerminateListener( static_cast< frame::XTerminateListener* >( this ) ); + } + catch( uno::Exception& ) + {} + } +} + +// XEventListener + +void SAL_CALL OLockListener::disposing( const lang::EventObject& aEvent ) +{ + std::unique_lock aGuard( m_aMutex ); + + // object is disposed + if ( aEvent.Source != m_xInstance ) + return; + + // the object does not listen for anything any more + m_nMode = 0; + + // dispose the wrapper; + uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY ); + aGuard.unlock(); + if ( xComponent.is() ) + { + try { xComponent->dispose(); } + catch( uno::Exception& ){} + } +} + + +// XCloseListener + +void SAL_CALL OLockListener::queryClosing( const lang::EventObject& aEvent, sal_Bool ) +{ + // GetsOwnership parameter is always ignored, the user of the service must close the object always + std::unique_lock aGuard( m_aMutex ); + if ( !(!m_bDisposed && aEvent.Source == m_xInstance && ( m_nMode & embed::Actions::PREVENT_CLOSE )) ) + return; + + try + { + uno::Reference< embed::XActionsApproval > xApprove = m_xApproval; + + // unlock the mutex here + aGuard.unlock(); + + if ( xApprove.is() && xApprove->approveAction( embed::Actions::PREVENT_CLOSE ) ) + throw util::CloseVetoException(); + } + catch( util::CloseVetoException& ) + { + // rethrow this exception + throw; + } + catch( uno::Exception& ) + { + // no action should be done + } +} + + +void SAL_CALL OLockListener::notifyClosing( const lang::EventObject& aEvent ) +{ + std::unique_lock aGuard( m_aMutex ); + + // object is closed, no reason to listen + if ( aEvent.Source != m_xInstance ) + return; + + uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( aEvent.Source, uno::UNO_QUERY ); + if ( !xCloseBroadcaster.is() ) + return; + + xCloseBroadcaster->removeCloseListener( static_cast< util::XCloseListener* >( this ) ); + m_nMode &= ~embed::Actions::PREVENT_CLOSE; + if ( !m_nMode ) + { + // dispose the wrapper; + uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY ); + aGuard.unlock(); + if ( xComponent.is() ) + { + try { xComponent->dispose(); } + catch( uno::Exception& ){} + } + } +} + + +// XTerminateListener + +void SAL_CALL OLockListener::queryTermination( const lang::EventObject& aEvent ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( !(aEvent.Source == m_xInstance && ( m_nMode & embed::Actions::PREVENT_TERMINATION )) ) + return; + + try + { + uno::Reference< embed::XActionsApproval > xApprove = m_xApproval; + + // unlock the mutex here + aGuard.unlock(); + + if ( xApprove.is() && xApprove->approveAction( embed::Actions::PREVENT_TERMINATION ) ) + throw frame::TerminationVetoException(); + } + catch( frame::TerminationVetoException& ) + { + // rethrow this exception + throw; + } + catch( uno::Exception& ) + { + // no action should be done + } +} + + +void SAL_CALL OLockListener::notifyTermination( const lang::EventObject& aEvent ) +{ + std::unique_lock aGuard( m_aMutex ); + + // object is terminated, no reason to listen + if ( aEvent.Source != m_xInstance ) + return; + + uno::Reference< frame::XDesktop > xDesktop( aEvent.Source, uno::UNO_QUERY ); + if ( !xDesktop.is() ) + return; + + try + { + xDesktop->removeTerminateListener( static_cast< frame::XTerminateListener* >( this ) ); + m_nMode &= ~embed::Actions::PREVENT_TERMINATION; + if ( !m_nMode ) + { + // dispose the wrapper; + uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY ); + aGuard.unlock(); + if ( xComponent.is() ) + { + try { xComponent->dispose(); } + catch( uno::Exception& ){} + } + } + } + catch( uno::Exception& ) + {} +} + + +// XInitialization + +void OLockListener::Init() +{ + std::unique_lock aGuard( m_aMutex ); + + if ( m_bDisposed || m_bInitialized ) + return; + + try + { + if ( m_nMode & embed::Actions::PREVENT_CLOSE ) + { + uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xInstance, uno::UNO_QUERY_THROW ); + xCloseBroadcaster->addCloseListener( static_cast< util::XCloseListener* >( this ) ); + } + + if ( m_nMode & embed::Actions::PREVENT_TERMINATION ) + { + uno::Reference< frame::XDesktop > xDesktop( m_xInstance, uno::UNO_QUERY_THROW ); + xDesktop->addTerminateListener( static_cast< frame::XTerminateListener* >( this ) ); + } + } + catch( uno::Exception& ) + { + // dispose the wrapper; + uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY ); + aGuard.unlock(); + if ( xComponent.is() ) + { + try { xComponent->dispose(); } + catch( uno::Exception& ){} + } + + throw; + } + + m_bInitialized = true; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_embed_InstanceLocker( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new OInstanceLocker()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/instancelocker.hxx b/comphelper/source/misc/instancelocker.hxx new file mode 100644 index 000000000..6a050c7f4 --- /dev/null +++ b/comphelper/source/misc/instancelocker.hxx @@ -0,0 +1,111 @@ +/* -*- 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/lang/XComponent.hpp> +#include <com/sun/star/util/XCloseListener.hpp> +#include <com/sun/star/frame/XTerminateListener.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <comphelper/interfacecontainer4.hxx> +#include <cppuhelper/weakref.hxx> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> +#include <mutex> + +namespace com::sun::star::embed { class XActionsApproval; } + + +class OLockListener; + +// the service is implemented as a wrapper to be able to die by refcount +// the disposing mechanics is required for java related scenarios +class OInstanceLocker : public ::cppu::WeakImplHelper< css::lang::XComponent, + css::lang::XInitialization, + css::lang::XServiceInfo > +{ + std::mutex m_aMutex; + + rtl::Reference< OLockListener > m_xLockListener; + + comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_aListenersContainer; // list of listeners + + bool m_bDisposed; + bool m_bInitialized; + +public: + explicit OInstanceLocker(); + virtual ~OInstanceLocker() 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; + +// XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) 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; + +}; + + +class OLockListener : public ::cppu::WeakImplHelper< css::util::XCloseListener, + css::frame::XTerminateListener > +{ + std::mutex m_aMutex; + css::uno::Reference< css::uno::XInterface > m_xInstance; + css::uno::Reference< css::embed::XActionsApproval > m_xApproval; + + css::uno::WeakReference< css::lang::XComponent > m_xWrapper; + + bool m_bDisposed; + bool m_bInitialized; + + sal_Int32 m_nMode; + +public: + OLockListener( css::uno::WeakReference< css::lang::XComponent > xWrapper, + css::uno::Reference< css::uno::XInterface > xInstance, + sal_Int32 nMode, + css::uno::Reference< css::embed::XActionsApproval > xApproval ); + + virtual ~OLockListener() override; + + void Init(); + void Dispose(); + +// XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + +// XCloseListener + virtual void SAL_CALL queryClosing( const css::lang::EventObject& Source, sal_Bool GetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const css::lang::EventObject& Source ) override; + +// XTerminateListener + virtual void SAL_CALL queryTermination( const css::lang::EventObject& Event ) override; + virtual void SAL_CALL notifyTermination( const css::lang::EventObject& Event ) override; + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/interaction.cxx b/comphelper/source/misc/interaction.cxx new file mode 100644 index 000000000..9e7b1706d --- /dev/null +++ b/comphelper/source/misc/interaction.cxx @@ -0,0 +1,71 @@ +/* -*- 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 <comphelper/interaction.hxx> + +#include <comphelper/sequence.hxx> +#include <utility> +#include <osl/diagnose.h> + + +namespace comphelper +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::task; + + OInteractionRequest::OInteractionRequest(Any _aRequestDescription) + :m_aRequest(std::move(_aRequestDescription)) + { + } + + OInteractionRequest::OInteractionRequest(Any aRequestDescription, + std::vector<Reference<XInteractionContinuation>>&& rContinuations) + : m_aRequest(std::move(aRequestDescription)) + , m_aContinuations(std::move(rContinuations)) + { + } + + void OInteractionRequest::addContinuation(const Reference< XInteractionContinuation >& _rxContinuation) + { + OSL_ENSURE(_rxContinuation.is(), "OInteractionRequest::addContinuation: invalid argument!"); + if (_rxContinuation.is()) + { + m_aContinuations.push_back(_rxContinuation); + } + } + + + Any SAL_CALL OInteractionRequest::getRequest( ) + { + return m_aRequest; + } + + + Sequence< Reference< XInteractionContinuation > > SAL_CALL OInteractionRequest::getContinuations( ) + { + return comphelper::containerToSequence(m_aContinuations); + } + + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/logging.cxx b/comphelper/source/misc/logging.cxx new file mode 100644 index 000000000..b656389de --- /dev/null +++ b/comphelper/source/misc/logging.cxx @@ -0,0 +1,159 @@ +/* -*- 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 <comphelper/logging.hxx> + +#include <com/sun/star/logging/LoggerPool.hpp> + +#include <osl/diagnose.h> + + +namespace comphelper +{ + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::logging::XLoggerPool; + using ::com::sun::star::logging::LoggerPool; + using ::com::sun::star::logging::XLogger; + using ::com::sun::star::uno::Exception; + + class EventLogger_Impl + { + private: + Reference< XComponentContext > m_aContext; + Reference< XLogger > m_xLogger; + + public: + EventLogger_Impl( const Reference< XComponentContext >& _rxContext, const OUString& _rLoggerName ); + + bool isValid() const { return m_xLogger.is(); } + const Reference< XLogger >& getLogger() const { return m_xLogger; } + }; + + EventLogger_Impl::EventLogger_Impl( const Reference< XComponentContext >& _rxContext, const OUString& _rLoggerName ) + :m_aContext( _rxContext ) + { + try + { + Reference< XLoggerPool > xPool( LoggerPool::get( m_aContext ) ); + if ( !_rLoggerName.isEmpty() ) + m_xLogger = xPool->getNamedLogger( _rLoggerName ); + else + m_xLogger = xPool->getDefaultLogger(); + } + catch( const Exception& ) + { + OSL_FAIL( "EventLogger_Impl::impl_createLogger_nothrow: caught an exception!" ); + } + } + + EventLogger::EventLogger( const Reference< XComponentContext >& _rxContext, const char* _pAsciiLoggerName ) + :m_pImpl( std::make_shared<EventLogger_Impl>( _rxContext, OUString::createFromAscii( _pAsciiLoggerName ) ) ) + { + } + + bool EventLogger::isLoggable( const sal_Int32 _nLogLevel ) const + { + if ( !m_pImpl->isValid() ) + return false; + + try + { + return m_pImpl->getLogger()->isLoggable( _nLogLevel ); + } + catch( const Exception& ) + { + OSL_FAIL( "EventLogger::isLoggable: caught an exception!" ); + } + + return false; + } + + const css::uno::Reference<css::logging::XLogger> & EventLogger::getLogger() const + { + return m_pImpl->getLogger(); + } + + + namespace + { + void lcl_replaceParameter( OUString& _inout_Message, const char* _rPlaceHolder, std::u16string_view _rReplacement ) + { + sal_Int32 nPlaceholderPosition = _inout_Message.indexOfAsciiL( _rPlaceHolder, strlen(_rPlaceHolder) ); + OSL_ENSURE( nPlaceholderPosition >= 0, "lcl_replaceParameter: placeholder not found!" ); + if ( nPlaceholderPosition < 0 ) + return; + + _inout_Message = _inout_Message.replaceAt( nPlaceholderPosition, strlen(_rPlaceHolder), _rReplacement ); + } + } + + + void EventLogger::impl_log( const sal_Int32 _nLogLevel, + const char* _pSourceClass, const char* _pSourceMethod, const OUString& _rMessage, + const OptionalString& _rArgument1, const OptionalString& _rArgument2, + const OptionalString& _rArgument3, const OptionalString& _rArgument4, + const OptionalString& _rArgument5, const OptionalString& _rArgument6 ) const + { + OUString sMessage( _rMessage ); + if ( !!_rArgument1 ) + lcl_replaceParameter( sMessage, "$1$", *_rArgument1 ); + + if ( !!_rArgument2 ) + lcl_replaceParameter( sMessage, "$2$", *_rArgument2 ); + + if ( !!_rArgument3 ) + lcl_replaceParameter( sMessage, "$3$", *_rArgument3 ); + + if ( !!_rArgument4 ) + lcl_replaceParameter( sMessage, "$4$", *_rArgument4 ); + + if ( !!_rArgument5 ) + lcl_replaceParameter( sMessage, "$5$", *_rArgument5 ); + + if ( !!_rArgument6 ) + lcl_replaceParameter( sMessage, "$6$", *_rArgument6 ); + + try + { + Reference< XLogger > xLogger( m_pImpl->getLogger() ); + OSL_PRECOND( xLogger.is(), "EventLogger::impl_log: should never be called without a logger!" ); + if ( _pSourceClass && _pSourceMethod ) + { + xLogger->logp( + _nLogLevel, + OUString::createFromAscii( _pSourceClass ), + OUString::createFromAscii( _pSourceMethod ), + sMessage + ); + } + else + { + xLogger->log( _nLogLevel, sMessage ); + } + } + catch( const Exception& ) + { + OSL_FAIL( "EventLogger::impl_log: caught an exception!" ); + } + } +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/lok.cxx b/comphelper/source/misc/lok.cxx new file mode 100644 index 000000000..b11bf4e83 --- /dev/null +++ b/comphelper/source/misc/lok.cxx @@ -0,0 +1,287 @@ +/* -*- 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/. + */ + +#include <comphelper/lok.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <sal/log.hxx> + +#include <iostream> + +namespace comphelper::LibreOfficeKit +{ + +static bool g_bActive(false); + +static bool g_bPartInInvalidation(false); + +static bool g_bTiledPainting(false); + +static bool g_bDialogPainting(false); + +static bool g_bTiledAnnotations(true); + +static bool g_bRangeHeaders(false); + +static bool g_bViewIdForVisCursorInvalidation(false); + +static bool g_bLocalRendering(false); + +static Compat g_eCompatFlags(Compat::none); + +namespace +{ + +class LanguageAndLocale +{ +private: + LanguageTag maLanguageTag; + LanguageTag maLocaleLanguageTag; + +public: + + LanguageAndLocale() + : maLanguageTag(LANGUAGE_NONE) + , maLocaleLanguageTag(LANGUAGE_NONE) + {} + + const LanguageTag& getLanguage() const + { + return maLanguageTag; + } + + void setLanguage(const LanguageTag& rLanguageTag) + { + if (maLanguageTag != rLanguageTag) + { + SAL_INFO("comphelper.lok", "Setting language from " << maLanguageTag.getBcp47() << " to " << rLanguageTag.getBcp47()); + maLanguageTag = rLanguageTag; + } + } + + const LanguageTag& getLocale() const + { + return maLocaleLanguageTag; + } + + void setLocale(const LanguageTag& rLocaleLanguageTag) + { + if (maLocaleLanguageTag != rLocaleLanguageTag) + { + SAL_INFO("comphelper.lok", "Setting locale from " << maLocaleLanguageTag.getBcp47() << " to " << rLocaleLanguageTag.getBcp47()); + maLocaleLanguageTag = rLocaleLanguageTag; + } + } + +}; + +} + +static LanguageAndLocale g_aLanguageAndLocale; + +/// Scaling of the cairo canvas painting for hi-dpi +static double g_fDPIScale(1.0); + +void setActive(bool bActive) +{ + g_bActive = bActive; +} + +bool isActive() +{ + return g_bActive; +} + +void setPartInInvalidation(bool bPartInInvalidation) +{ + g_bPartInInvalidation = bPartInInvalidation; +} + +bool isPartInInvalidation() +{ + return g_bPartInInvalidation; +} + +void setTiledPainting(bool bTiledPainting) +{ + g_bTiledPainting = bTiledPainting; +} + +bool isTiledPainting() +{ + return g_bTiledPainting; +} + +void setDialogPainting(bool bDialogPainting) +{ + g_bDialogPainting = bDialogPainting; +} + +bool isDialogPainting() +{ + return g_bDialogPainting; +} + +void setDPIScale(double fDPIScale) +{ + g_fDPIScale = fDPIScale; +} + +double getDPIScale() +{ + return g_fDPIScale; +} + +void setTiledAnnotations(bool bTiledAnnotations) +{ + g_bTiledAnnotations = bTiledAnnotations; +} + +bool isTiledAnnotations() +{ + return g_bTiledAnnotations; +} + +void setRangeHeaders(bool bRangeHeaders) +{ + g_bRangeHeaders = bRangeHeaders; +} + +void setViewIdForVisCursorInvalidation(bool bViewIdForVisCursorInvalidation) +{ + g_bViewIdForVisCursorInvalidation = bViewIdForVisCursorInvalidation; +} + +bool isViewIdForVisCursorInvalidation() +{ + return g_bViewIdForVisCursorInvalidation; +} + +bool isRangeHeaders() +{ + return g_bRangeHeaders; +} + +void setLocalRendering(bool bLocalRendering) +{ + g_bLocalRendering = bLocalRendering; +} + +bool isLocalRendering() +{ + return g_bLocalRendering; +} + +void setCompatFlag(Compat flag) { g_eCompatFlags = static_cast<Compat>(g_eCompatFlags | flag); } + +bool isCompatFlagSet(Compat flag) { return (g_eCompatFlags & flag) == flag; } + +void setLocale(const LanguageTag& rLanguageTag) +{ + g_aLanguageAndLocale.setLocale(rLanguageTag); +} + +const LanguageTag& getLocale() +{ + const LanguageTag& rLocale = g_aLanguageAndLocale.getLocale(); + SAL_INFO_IF(rLocale.getLanguageType() == LANGUAGE_NONE, "comphelper.lok", "Locale not set"); + return rLocale; +} + +void setLanguageTag(const LanguageTag& rLanguageTag) +{ + g_aLanguageAndLocale.setLanguage(rLanguageTag); +} + +const LanguageTag& getLanguageTag() +{ + const LanguageTag& rLanguage = g_aLanguageAndLocale.getLanguage(); + SAL_INFO_IF(rLanguage.getLanguageType() == LANGUAGE_NONE, "comphelper.lok", "Language not set"); + return rLanguage; +} + +bool isAllowlistedLanguage(const OUString& lang) +{ + if (!isActive()) + return true; + +#if defined ANDROID || defined IOS + (void) lang; + return true; +#else + static const std::vector<OUString> aAllowlist = [] { + std::vector<OUString> aList; + // coverity[tainted_data] - we trust the contents of this variable + const char* pAllowlist = getenv("LOK_ALLOWLIST_LANGUAGES"); + if (pAllowlist) + { + std::stringstream stream(pAllowlist); + std::string s; + + std::cerr << "Allowlisted languages: "; + while (getline(stream, s, ' ')) { + if (s.length() == 0) + continue; + + std::cerr << s << " "; + aList.emplace_back(OStringToOUString(s.c_str(), RTL_TEXTENCODING_UTF8)); + } + std::cerr << std::endl; + } + + if (aList.empty()) + std::cerr << "No language allowlisted, turning off the language support." << std::endl; + + return aList; + }(); + + if (aAllowlist.empty()) + return false; + + for (const auto& entry : aAllowlist) + { + if (lang.startsWith(entry)) + return true; + if (lang.startsWith(entry.replace('_', '-'))) + return true; + } + + return false; +#endif +} + +static void (*pStatusIndicatorCallback)(void *data, statusIndicatorCallbackType type, int percent, const char* pText)(nullptr); +static void *pStatusIndicatorCallbackData(nullptr); + +void setStatusIndicatorCallback(void (*callback)(void *data, statusIndicatorCallbackType type, int percent, const char* pText), void *data) +{ + pStatusIndicatorCallback = callback; + pStatusIndicatorCallbackData = data; +} + +void statusIndicatorStart(const OUString& sText) +{ + if (pStatusIndicatorCallback) + pStatusIndicatorCallback(pStatusIndicatorCallbackData, statusIndicatorCallbackType::Start, 0, sText.toUtf8().getStr()); +} + +void statusIndicatorSetValue(int percent) +{ + if (pStatusIndicatorCallback) + pStatusIndicatorCallback(pStatusIndicatorCallbackData, statusIndicatorCallbackType::SetValue, percent, nullptr); +} + +void statusIndicatorFinish() +{ + if (pStatusIndicatorCallback) + pStatusIndicatorCallback(pStatusIndicatorCallbackData, statusIndicatorCallbackType::Finish, 0, nullptr); +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/mimeconfighelper.cxx b/comphelper/source/misc/mimeconfighelper.cxx new file mode 100644 index 000000000..7f402b635 --- /dev/null +++ b/comphelper/source/misc/mimeconfighelper.cxx @@ -0,0 +1,909 @@ +/* -*- 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/PropertyValue.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/container/XContainerQuery.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/embed/VerbDescriptor.hpp> +#include <com/sun/star/document/XTypeDetection.hpp> + +#include <osl/diagnose.h> + +#include <comphelper/fileformat.h> +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/classids.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/documentconstants.hxx> +#include <comphelper/propertysequence.hxx> +#include <rtl/ustrbuf.hxx> +#include <utility> + + +using namespace ::com::sun::star; +using namespace comphelper; + + +MimeConfigurationHelper::MimeConfigurationHelper( uno::Reference< uno::XComponentContext > xContext ) +: m_xContext(std::move( xContext )) +{ + if ( !m_xContext.is() ) + throw uno::RuntimeException(); +} + + +OUString MimeConfigurationHelper::GetStringClassIDRepresentation( const uno::Sequence< sal_Int8 >& aClassID ) +{ + OUStringBuffer aResult; + + if ( aClassID.getLength() == 16 ) + { + for ( sal_Int32 nInd = 0; nInd < aClassID.getLength(); nInd++ ) + { + if ( nInd == 4 || nInd == 6 || nInd == 8 || nInd == 10 ) + aResult.append("-"); + + sal_Int32 nDigit1 = static_cast<sal_Int32>( static_cast<sal_uInt8>(aClassID[nInd]) / 16 ); + sal_Int32 nDigit2 = static_cast<sal_uInt8>(aClassID[nInd]) % 16; + aResult.append( OUString::number(nDigit1, 16) + OUString::number( nDigit2, 16 ) ); + } + } + + return aResult.makeStringAndClear(); +} + + +static sal_uInt8 GetDigit_Impl( char aChar ) +{ + if ( aChar >= '0' && aChar <= '9' ) + return aChar - '0'; + else if ( aChar >= 'a' && aChar <= 'f' ) + return aChar - 'a' + 10; + else if ( aChar >= 'A' && aChar <= 'F' ) + return aChar - 'A' + 10; + else + return 16; +} + + +uno::Sequence< sal_Int8 > MimeConfigurationHelper::GetSequenceClassIDRepresentation( std::u16string_view aClassID ) +{ + size_t nLength = aClassID.size(); + if ( nLength == 36 ) + { + OString aCharClassID = OUStringToOString( aClassID, RTL_TEXTENCODING_ASCII_US ); + uno::Sequence< sal_Int8 > aResult( 16 ); + auto pResult = aResult.getArray(); + + size_t nStrPointer = 0; + sal_Int32 nSeqInd = 0; + while( nSeqInd < 16 && nStrPointer + 1U < nLength ) + { + sal_uInt8 nDigit1 = GetDigit_Impl( aCharClassID[nStrPointer++] ); + sal_uInt8 nDigit2 = GetDigit_Impl( aCharClassID[nStrPointer++] ); + + if ( nDigit1 > 15 || nDigit2 > 15 ) + break; + + pResult[nSeqInd++] = static_cast<sal_Int8>( nDigit1 * 16 + nDigit2 ); + + if ( nStrPointer < nLength && aCharClassID[nStrPointer] == '-' ) + nStrPointer++; + } + + if ( nSeqInd == 16 && nStrPointer == nLength ) + return aResult; + } + + return uno::Sequence< sal_Int8 >(); +} + + +uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetConfigurationByPathImpl( const OUString& aPath ) +{ + uno::Reference< container::XNameAccess > xConfig; + + try + { + if ( !m_xConfigProvider.is() ) + m_xConfigProvider = configuration::theDefaultProvider::get( m_xContext ); + + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(aPath)} + })); + xConfig.set( m_xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArgs ), + uno::UNO_QUERY ); + } + catch( uno::Exception& ) + {} + + return xConfig; +} + + +uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetObjConfiguration() +{ + std::unique_lock aGuard( m_aMutex ); + + if ( !m_xObjectConfig.is() ) + m_xObjectConfig = GetConfigurationByPathImpl( + "/org.openoffice.Office.Embedding/Objects" ); + + return m_xObjectConfig; +} + + +uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetVerbsConfiguration() +{ + std::unique_lock aGuard( m_aMutex ); + + if ( !m_xVerbsConfig.is() ) + m_xVerbsConfig = GetConfigurationByPathImpl( + "/org.openoffice.Office.Embedding/Verbs"); + + return m_xVerbsConfig; +} + + +uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetMediaTypeConfiguration() +{ + std::unique_lock aGuard( m_aMutex ); + + if ( !m_xMediaTypeConfig.is() ) + m_xMediaTypeConfig = GetConfigurationByPathImpl( + "/org.openoffice.Office.Embedding/MimeTypeClassIDRelations"); + + return m_xMediaTypeConfig; +} + + +uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetFilterFactory() +{ + std::unique_lock aGuard( m_aMutex ); + + if ( !m_xFilterFactory.is() ) + m_xFilterFactory.set( + m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", m_xContext), + uno::UNO_QUERY ); + + return m_xFilterFactory; +} + + +OUString MimeConfigurationHelper::GetDocServiceNameFromFilter( const OUString& aFilterName ) +{ + OUString aDocServiceName; + + try + { + uno::Reference< container::XNameAccess > xFilterFactory( + GetFilterFactory(), + uno::UNO_SET_THROW ); + + uno::Any aFilterAnyData = xFilterFactory->getByName( aFilterName ); + uno::Sequence< beans::PropertyValue > aFilterData; + if ( aFilterAnyData >>= aFilterData ) + { + for ( const auto & prop : std::as_const(aFilterData) ) + if ( prop.Name == "DocumentService" ) + prop.Value >>= aDocServiceName; + } + } + catch( uno::Exception& ) + {} + + return aDocServiceName; +} + + +OUString MimeConfigurationHelper::GetDocServiceNameFromMediaType( const OUString& aMediaType ) +{ + uno::Reference< container::XContainerQuery > xTypeCFG( + m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", m_xContext), + uno::UNO_QUERY ); + + if ( xTypeCFG.is() ) + { + try + { + // make query for all types matching the properties + uno::Sequence < beans::NamedValue > aSeq { { "MediaType", css::uno::Any(aMediaType) } }; + + uno::Reference < container::XEnumeration > xEnum = xTypeCFG->createSubSetEnumerationByProperties( aSeq ); + while ( xEnum->hasMoreElements() ) + { + uno::Sequence< beans::PropertyValue > aType; + if ( xEnum->nextElement() >>= aType ) + { + for ( const auto & prop : std::as_const(aType) ) + { + OUString aFilterName; + if ( prop.Name == "PreferredFilter" + && ( prop.Value >>= aFilterName ) && !aFilterName.isEmpty() ) + { + OUString aDocumentName = GetDocServiceNameFromFilter( aFilterName ); + if ( !aDocumentName.isEmpty() ) + return aDocumentName; + } + } + } + } + } + catch( uno::Exception& ) + {} + } + + return OUString(); +} + + +bool MimeConfigurationHelper::GetVerbByShortcut( const OUString& aVerbShortcut, + embed::VerbDescriptor& aDescriptor ) +{ + bool bResult = false; + + uno::Reference< container::XNameAccess > xVerbsConfig = GetVerbsConfiguration(); + uno::Reference< container::XNameAccess > xVerbsProps; + try + { + if ( xVerbsConfig.is() && ( xVerbsConfig->getByName( aVerbShortcut ) >>= xVerbsProps ) && xVerbsProps.is() ) + { + embed::VerbDescriptor aTempDescr; + static constexpr OUStringLiteral sVerbID = u"VerbID"; + static constexpr OUStringLiteral sVerbUIName = u"VerbUIName"; + static constexpr OUStringLiteral sVerbFlags = u"VerbFlags"; + static constexpr OUStringLiteral sVerbAttributes = u"VerbAttributes"; + if ( ( xVerbsProps->getByName(sVerbID) >>= aTempDescr.VerbID ) + && ( xVerbsProps->getByName(sVerbUIName) >>= aTempDescr.VerbName ) + && ( xVerbsProps->getByName(sVerbFlags) >>= aTempDescr.VerbFlags ) + && ( xVerbsProps->getByName(sVerbAttributes) >>= aTempDescr.VerbAttributes ) ) + { + aDescriptor = aTempDescr; + bResult = true; + } + } + } + catch( uno::Exception& ) + { + } + + return bResult; +} + + +uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjPropsFromConfigEntry( + const uno::Sequence< sal_Int8 >& aClassID, + const uno::Reference< container::XNameAccess >& xObjectProps ) +{ + uno::Sequence< beans::NamedValue > aResult; + + if ( aClassID.getLength() == 16 ) + { + try + { + const uno::Sequence< OUString > aObjPropNames = xObjectProps->getElementNames(); + + aResult.realloc( aObjPropNames.getLength() + 1 ); + auto pResult = aResult.getArray(); + pResult[0].Name = "ClassID"; + pResult[0].Value <<= aClassID; + + for ( sal_Int32 nInd = 0; nInd < aObjPropNames.getLength(); nInd++ ) + { + pResult[nInd + 1].Name = aObjPropNames[nInd]; + + if ( aObjPropNames[nInd] == "ObjectVerbs" ) + { + uno::Sequence< OUString > aVerbShortcuts; + if ( !(xObjectProps->getByName( aObjPropNames[nInd] ) >>= aVerbShortcuts) ) + throw uno::RuntimeException(); + uno::Sequence< embed::VerbDescriptor > aVerbDescriptors( aVerbShortcuts.getLength() ); + auto aVerbDescriptorsRange = asNonConstRange(aVerbDescriptors); + for ( sal_Int32 nVerbI = 0; nVerbI < aVerbShortcuts.getLength(); nVerbI++ ) + if ( !GetVerbByShortcut( aVerbShortcuts[nVerbI], aVerbDescriptorsRange[nVerbI] ) ) + throw uno::RuntimeException(); + + pResult[nInd+1].Value <<= aVerbDescriptors; + } + else + pResult[nInd+1].Value = xObjectProps->getByName( aObjPropNames[nInd] ); + } + } + catch( uno::Exception& ) + { + aResult.realloc( 0 ); + } + } + + return aResult; +} + + +OUString MimeConfigurationHelper::GetExplicitlyRegisteredObjClassID( const OUString& aMediaType ) +{ + OUString aStringClassID; + + uno::Reference< container::XNameAccess > xMediaTypeConfig = GetMediaTypeConfiguration(); + try + { + if ( xMediaTypeConfig.is() ) + xMediaTypeConfig->getByName( aMediaType ) >>= aStringClassID; + } + catch( uno::Exception& ) + { + } + + return aStringClassID; + +} + + +uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByStringClassID( + const OUString& aStringClassID ) +{ + uno::Sequence< beans::NamedValue > aObjProps; + + uno::Sequence< sal_Int8 > aClassID = GetSequenceClassIDRepresentation( aStringClassID ); + if ( ClassIDsEqual( aClassID, GetSequenceClassID( SO3_DUMMY_CLASSID ) ) ) + { + aObjProps = { { "ObjectFactory", + uno::Any(OUString("com.sun.star.embed.OOoSpecialEmbeddedObjectFactory")) }, + { "ClassID", uno::Any(aClassID) } }; + return aObjProps; + } + + if ( aClassID.getLength() == 16 ) + { + uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration(); + uno::Reference< container::XNameAccess > xObjectProps; + try + { + // TODO/LATER: allow to provide ClassID string in any format, only digits are counted + if ( xObjConfig.is() && ( xObjConfig->getByName( aStringClassID.toAsciiUpperCase() ) >>= xObjectProps ) && xObjectProps.is() ) + aObjProps = GetObjPropsFromConfigEntry( aClassID, xObjectProps ); + } + catch( uno::Exception& ) + { + } + } + + return aObjProps; +} + + +uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByClassID( + const uno::Sequence< sal_Int8 >& aClassID ) +{ + uno::Sequence< beans::NamedValue > aObjProps; + if ( ClassIDsEqual( aClassID, GetSequenceClassID( SO3_DUMMY_CLASSID ) ) ) + { + aObjProps = { { "ObjectFactory", + uno::Any(OUString("com.sun.star.embed.OOoSpecialEmbeddedObjectFactory")) }, + { "ClassID", uno::Any(aClassID) } }; + } + + OUString aStringClassID = GetStringClassIDRepresentation( aClassID ); + if ( !aStringClassID.isEmpty() ) + { + uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration(); + uno::Reference< container::XNameAccess > xObjectProps; + try + { + if ( xObjConfig.is() && ( xObjConfig->getByName( aStringClassID.toAsciiUpperCase() ) >>= xObjectProps ) && xObjectProps.is() ) + aObjProps = GetObjPropsFromConfigEntry( aClassID, xObjectProps ); + } + catch( uno::Exception& ) + { + } + } + + return aObjProps; +} + + +uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByMediaType( const OUString& aMediaType ) +{ + uno::Sequence< beans::NamedValue > aObject = + GetObjectPropsByStringClassID( GetExplicitlyRegisteredObjClassID( aMediaType ) ); + if ( aObject.hasElements() ) + return aObject; + + OUString aDocumentName = GetDocServiceNameFromMediaType( aMediaType ); + if ( !aDocumentName.isEmpty() ) + return GetObjectPropsByDocumentName( aDocumentName ); + + return uno::Sequence< beans::NamedValue >(); +} + + +uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByFilter( const OUString& aFilterName ) +{ + OUString aDocumentName = GetDocServiceNameFromFilter( aFilterName ); + if ( !aDocumentName.isEmpty() ) + return GetObjectPropsByDocumentName( aDocumentName ); + + return uno::Sequence< beans::NamedValue >(); +} + + +uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByDocumentName( std::u16string_view aDocName ) +{ + if ( !aDocName.empty() ) + { + uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration(); + if ( xObjConfig.is() ) + { + try + { + const uno::Sequence< OUString > aClassIDs = xObjConfig->getElementNames(); + for ( const OUString & id : aClassIDs ) + { + uno::Reference< container::XNameAccess > xObjectProps; + OUString aEntryDocName; + + if ( ( xObjConfig->getByName( id ) >>= xObjectProps ) && xObjectProps.is() + && ( xObjectProps->getByName("ObjectDocumentServiceName") >>= aEntryDocName ) + && aEntryDocName == aDocName ) + { + return GetObjPropsFromConfigEntry( GetSequenceClassIDRepresentation( id ), + xObjectProps ); + } + } + } + catch( uno::Exception& ) + {} + } + } + + return uno::Sequence< beans::NamedValue >(); +} + + +OUString MimeConfigurationHelper::GetFactoryNameByClassID( const uno::Sequence< sal_Int8 >& aClassID ) +{ + return GetFactoryNameByStringClassID( GetStringClassIDRepresentation( aClassID ) ); +} + + +OUString MimeConfigurationHelper::GetFactoryNameByStringClassID( const OUString& aStringClassID ) +{ + OUString aResult; + + if ( !aStringClassID.isEmpty() ) + { + uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration(); + uno::Reference< container::XNameAccess > xObjectProps; + try + { + if ( xObjConfig.is() && ( xObjConfig->getByName( aStringClassID.toAsciiUpperCase() ) >>= xObjectProps ) && xObjectProps.is() ) + xObjectProps->getByName("ObjectFactory") >>= aResult; + } + catch( uno::Exception& ) + { + uno::Sequence< sal_Int8 > aClassID = GetSequenceClassIDRepresentation( aStringClassID ); + if ( ClassIDsEqual( aClassID, GetSequenceClassID( SO3_DUMMY_CLASSID ) ) ) + return "com.sun.star.embed.OOoSpecialEmbeddedObjectFactory"; + } + } + + return aResult; +} + + +OUString MimeConfigurationHelper::GetFactoryNameByDocumentName( std::u16string_view aDocName ) +{ + OUString aResult; + + if ( !aDocName.empty() ) + { + uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration(); + if ( xObjConfig.is() ) + { + try + { + const uno::Sequence< OUString > aClassIDs = xObjConfig->getElementNames(); + for ( const OUString & id : aClassIDs ) + { + uno::Reference< container::XNameAccess > xObjectProps; + OUString aEntryDocName; + + if ( ( xObjConfig->getByName( id ) >>= xObjectProps ) && xObjectProps.is() + && ( xObjectProps->getByName( "ObjectDocumentServiceName" ) >>= aEntryDocName ) + && aEntryDocName == aDocName ) + { + xObjectProps->getByName("ObjectFactory") >>= aResult; + break; + } + } + } + catch( uno::Exception& ) + {} + } + } + + return aResult; +} + + +OUString MimeConfigurationHelper::GetFactoryNameByMediaType( const OUString& aMediaType ) +{ + OUString aResult = GetFactoryNameByStringClassID( GetExplicitlyRegisteredObjClassID( aMediaType ) ); + + if ( aResult.isEmpty() ) + { + OUString aDocumentName = GetDocServiceNameFromMediaType( aMediaType ); + if ( !aDocumentName.isEmpty() ) + aResult = GetFactoryNameByDocumentName( aDocumentName ); + } + + return aResult; +} + + +OUString MimeConfigurationHelper::UpdateMediaDescriptorWithFilterName( + uno::Sequence< beans::PropertyValue >& aMediaDescr, + bool bIgnoreType ) +{ + OUString aFilterName; + + for ( const auto & prop : std::as_const(aMediaDescr) ) + if ( prop.Name == "FilterName" ) + prop.Value >>= aFilterName; + + if ( aFilterName.isEmpty() ) + { + // filter name is not specified, so type detection should be done + + uno::Reference< document::XTypeDetection > xTypeDetection( + m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", m_xContext), + uno::UNO_QUERY_THROW ); + + // typedetection can change the mode, add a stream and so on, thus a copy should be used + uno::Sequence< beans::PropertyValue > aTempMD( aMediaDescr ); + + // get TypeName + OUString aTypeName = xTypeDetection->queryTypeByDescriptor( aTempMD, true ); + + // get FilterName + for ( const auto & prop : std::as_const(aTempMD) ) + if ( prop.Name == "FilterName" ) + prop.Value >>= aFilterName; + + if ( !aFilterName.isEmpty() ) + { + sal_Int32 nOldLen = aMediaDescr.getLength(); + aMediaDescr.realloc( nOldLen + 1 ); + auto pMediaDescr = aMediaDescr.getArray(); + pMediaDescr[nOldLen].Name = "FilterName"; + pMediaDescr[ nOldLen ].Value <<= aFilterName; + + } + else if ( !aTypeName.isEmpty() && !bIgnoreType ) + { + uno::Reference< container::XNameAccess > xNameAccess( xTypeDetection, uno::UNO_QUERY ); + uno::Sequence< beans::PropertyValue > aTypes; + + if ( xNameAccess.is() && ( xNameAccess->getByName( aTypeName ) >>= aTypes ) ) + { + for ( const auto & prop : std::as_const(aTypes) ) + { + if ( prop.Name == "PreferredFilter" && ( prop.Value >>= aFilterName ) ) + { + sal_Int32 nOldLen = aMediaDescr.getLength(); + aMediaDescr.realloc( nOldLen + 1 ); + auto pMediaDescr = aMediaDescr.getArray(); + pMediaDescr[nOldLen].Name = "FilterName"; + pMediaDescr[ nOldLen ].Value = prop.Value; + break; + } + } + } + } + } + + return aFilterName; +} + +OUString MimeConfigurationHelper::UpdateMediaDescriptorWithFilterName( + uno::Sequence< beans::PropertyValue >& aMediaDescr, + uno::Sequence< beans::NamedValue >& aObject ) +{ + OUString aDocName; + for ( const auto & nv : std::as_const(aObject) ) + if ( nv.Name == "ObjectDocumentServiceName" ) + { + nv.Value >>= aDocName; + break; + } + + OSL_ENSURE( !aDocName.isEmpty(), "The name must exist at this point!" ); + + + bool bNeedsAddition = true; + for ( sal_Int32 nMedInd = 0; nMedInd < aMediaDescr.getLength(); nMedInd++ ) + if ( aMediaDescr[nMedInd].Name == "DocumentService" ) + { + aMediaDescr.getArray()[nMedInd].Value <<= aDocName; + bNeedsAddition = false; + break; + } + + if ( bNeedsAddition ) + { + sal_Int32 nOldLen = aMediaDescr.getLength(); + aMediaDescr.realloc( nOldLen + 1 ); + auto pMediaDescr = aMediaDescr.getArray(); + pMediaDescr[nOldLen].Name = "DocumentService"; + pMediaDescr[nOldLen].Value <<= aDocName; + } + + return UpdateMediaDescriptorWithFilterName( aMediaDescr, true ); +} + +#ifdef _WIN32 + +SfxFilterFlags MimeConfigurationHelper::GetFilterFlags( const OUString& aFilterName ) +{ + SfxFilterFlags nFlags = SfxFilterFlags::NONE; + try + { + if ( !aFilterName.isEmpty() ) + { + uno::Reference< container::XNameAccess > xFilterFactory( + GetFilterFactory(), + uno::UNO_SET_THROW ); + + uno::Any aFilterAny = xFilterFactory->getByName( aFilterName ); + uno::Sequence< beans::PropertyValue > aData; + if ( aFilterAny >>= aData ) + { + SequenceAsHashMap aFilterHM( aData ); + nFlags = static_cast<SfxFilterFlags>(aFilterHM.getUnpackedValueOrDefault( "Flags", sal_Int32(0) )); + } + } + } catch( uno::Exception& ) + {} + + return nFlags; +} + +bool MimeConfigurationHelper::AddFilterNameCheckOwnFile( + uno::Sequence< beans::PropertyValue >& aMediaDescr ) +{ + OUString aFilterName = UpdateMediaDescriptorWithFilterName( aMediaDescr, false ); + if ( !aFilterName.isEmpty() ) + { + SfxFilterFlags nFlags = GetFilterFlags( aFilterName ); + // check the OWN flag + return bool(nFlags & SfxFilterFlags::OWN); + } + + return false; +} + +#endif + +OUString MimeConfigurationHelper::GetDefaultFilterFromServiceName( const OUString& aServiceName, sal_Int32 nVersion ) +{ + OUString aResult; + + if ( !aServiceName.isEmpty() && nVersion ) + try + { + uno::Reference< container::XContainerQuery > xFilterQuery( + GetFilterFactory(), + uno::UNO_QUERY_THROW ); + + uno::Sequence< beans::NamedValue > aSearchRequest + { + { "DocumentService", css::uno::Any(aServiceName) }, + { "FileFormatVersion", css::uno::Any(nVersion) } + }; + + uno::Reference< container::XEnumeration > xFilterEnum = + xFilterQuery->createSubSetEnumerationByProperties( aSearchRequest ); + + // use the first filter that is found + if ( xFilterEnum.is() ) + while ( xFilterEnum->hasMoreElements() ) + { + uno::Sequence< beans::PropertyValue > aProps; + if ( xFilterEnum->nextElement() >>= aProps ) + { + SfxFilterFlags nFlags = SfxFilterFlags::NONE; + OUString sName; + for (const auto & rPropVal : aProps) + { + if (rPropVal.Name == "Flags") + { + sal_Int32 nTmp(0); + if (rPropVal.Value >>= nTmp) + nFlags = static_cast<SfxFilterFlags>(nTmp); + } + else if (rPropVal.Name == "Name") + rPropVal.Value >>= sName; + } + + // that should be import, export, own filter and not a template filter ( TemplatePath flag ) + SfxFilterFlags const nRequired = SfxFilterFlags::OWN + // fdo#78159 for OOoXML, there is code to convert + // to ODF in OCommonEmbeddedObject::store* + // so accept it even though there's no export + | (SOFFICE_FILEFORMAT_60 == nVersion ? SfxFilterFlags::NONE : SfxFilterFlags::EXPORT) + | SfxFilterFlags::IMPORT; + if ( ( ( nFlags & nRequired ) == nRequired ) && !( nFlags & SfxFilterFlags::TEMPLATEPATH ) ) + { + // if there are more than one filter the preferred one should be used + // if there is no preferred filter the first one will be used + if ( aResult.isEmpty() || ( nFlags & SfxFilterFlags::PREFERED ) ) + aResult = sName; + if ( nFlags & SfxFilterFlags::PREFERED ) + break; // the preferred filter was found + } + } + } + } + catch( uno::Exception& ) + {} + + return aResult; +} + + +OUString MimeConfigurationHelper::GetExportFilterFromImportFilter( const OUString& aImportFilterName ) +{ + OUString aExportFilterName; + + try + { + if ( !aImportFilterName.isEmpty() ) + { + uno::Reference< container::XNameAccess > xFilterFactory( + GetFilterFactory(), + uno::UNO_SET_THROW ); + + uno::Any aImpFilterAny = xFilterFactory->getByName( aImportFilterName ); + uno::Sequence< beans::PropertyValue > aImpData; + if ( aImpFilterAny >>= aImpData ) + { + SequenceAsHashMap aImpFilterHM( aImpData ); + SfxFilterFlags nFlags = static_cast<SfxFilterFlags>(aImpFilterHM.getUnpackedValueOrDefault( "Flags", sal_Int32(0) )); + + if ( !( nFlags & SfxFilterFlags::IMPORT ) ) + { + OSL_FAIL( "This is no import filter!" ); + throw uno::Exception("this is no import filter", nullptr); + } + + if ( nFlags & SfxFilterFlags::EXPORT ) + { + aExportFilterName = aImportFilterName; + } + else + { + OUString aDocumentServiceName = aImpFilterHM.getUnpackedValueOrDefault( "DocumentService", OUString() ); + OUString aTypeName = aImpFilterHM.getUnpackedValueOrDefault( "Type", OUString() ); + + OSL_ENSURE( !aDocumentServiceName.isEmpty() && !aTypeName.isEmpty(), "Incomplete filter data!" ); + if ( !(aDocumentServiceName.isEmpty() || aTypeName.isEmpty()) ) + { + uno::Sequence< beans::NamedValue > aSearchRequest + { + { "Type", css::uno::Any(aTypeName) }, + { "DocumentService", css::uno::Any(aDocumentServiceName) } + }; + + uno::Sequence< beans::PropertyValue > aExportFilterProps = SearchForFilter( + uno::Reference< container::XContainerQuery >( xFilterFactory, uno::UNO_QUERY_THROW ), + aSearchRequest, + SfxFilterFlags::EXPORT, + SfxFilterFlags::INTERNAL ); + + if ( aExportFilterProps.hasElements() ) + { + SequenceAsHashMap aExpPropsHM( aExportFilterProps ); + aExportFilterName = aExpPropsHM.getUnpackedValueOrDefault( "Name", OUString() ); + } + } + } + } + } + } + catch( uno::Exception& ) + {} + + return aExportFilterName; +} + + +// static +uno::Sequence< beans::PropertyValue > MimeConfigurationHelper::SearchForFilter( + const uno::Reference< container::XContainerQuery >& xFilterQuery, + const uno::Sequence< beans::NamedValue >& aSearchRequest, + SfxFilterFlags nMustFlags, + SfxFilterFlags nDontFlags ) +{ + uno::Sequence< beans::PropertyValue > aFilterProps; + uno::Reference< container::XEnumeration > xFilterEnum = + xFilterQuery->createSubSetEnumerationByProperties( aSearchRequest ); + + // the first default filter will be taken, + // if there is no filter with flag default the first acceptable filter will be taken + if ( xFilterEnum.is() ) + { + while ( xFilterEnum->hasMoreElements() ) + { + uno::Sequence< beans::PropertyValue > aProps; + if ( xFilterEnum->nextElement() >>= aProps ) + { + SequenceAsHashMap aPropsHM( aProps ); + SfxFilterFlags nFlags = static_cast<SfxFilterFlags>(aPropsHM.getUnpackedValueOrDefault("Flags", + sal_Int32(0) )); + if ( ( ( nFlags & nMustFlags ) == nMustFlags ) && !( nFlags & nDontFlags ) ) + { + if ( ( nFlags & SfxFilterFlags::DEFAULT ) == SfxFilterFlags::DEFAULT ) + { + aFilterProps = aProps; + break; + } + else if ( !aFilterProps.hasElements() ) + aFilterProps = aProps; + } + } + } + } + + return aFilterProps; +} + + +bool MimeConfigurationHelper::ClassIDsEqual( const uno::Sequence< sal_Int8 >& aClassID1, const uno::Sequence< sal_Int8 >& aClassID2 ) +{ + return aClassID1 == aClassID2; +} + + +uno::Sequence< sal_Int8 > MimeConfigurationHelper::GetSequenceClassID( sal_uInt32 n1, sal_uInt16 n2, sal_uInt16 n3, + sal_uInt8 b8, sal_uInt8 b9, sal_uInt8 b10, sal_uInt8 b11, + sal_uInt8 b12, sal_uInt8 b13, sal_uInt8 b14, sal_uInt8 b15 ) +{ + uno::Sequence< sal_Int8 > aResult{ /* [ 0] */ static_cast<sal_Int8>( n1 >> 24 ), + /* [ 1] */ static_cast<sal_Int8>( ( n1 << 8 ) >> 24 ), + /* [ 2] */ static_cast<sal_Int8>( ( n1 << 16 ) >> 24 ), + /* [ 3] */ static_cast<sal_Int8>( ( n1 << 24 ) >> 24 ), + /* [ 4] */ static_cast<sal_Int8>( n2 >> 8 ), + /* [ 5] */ static_cast<sal_Int8>( ( n2 << 8 ) >> 8 ), + /* [ 6] */ static_cast<sal_Int8>( n3 >> 8 ), + /* [ 7] */ static_cast<sal_Int8>( ( n3 << 8 ) >> 8 ), + /* [ 8] */ static_cast<sal_Int8>( b8 ), + /* [ 9] */ static_cast<sal_Int8>( b9 ), + /* [10] */ static_cast<sal_Int8>( b10 ), + /* [11] */ static_cast<sal_Int8>( b11 ), + /* [12] */ static_cast<sal_Int8>( b12 ), + /* [13] */ static_cast<sal_Int8>( b13 ), + /* [14] */ static_cast<sal_Int8>( b14 ), + /* [15] */ static_cast<sal_Int8>( b15 ) }; + + return aResult; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/namedvaluecollection.cxx b/comphelper/source/misc/namedvaluecollection.cxx new file mode 100644 index 000000000..11ef15b30 --- /dev/null +++ b/comphelper/source/misc/namedvaluecollection.cxx @@ -0,0 +1,308 @@ +/* -*- 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 <comphelper/namedvaluecollection.hxx> + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/beans/PropertyState.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <sal/log.hxx> + +#include <algorithm> +#include <unordered_map> + +namespace comphelper +{ + + + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::beans::PropertyValue; + using ::com::sun::star::beans::NamedValue; + using ::com::sun::star::uno::Type; + using ::com::sun::star::uno::cpp_acquire; + using ::com::sun::star::uno::cpp_release; + using ::com::sun::star::uno::cpp_queryInterface; + using ::com::sun::star::lang::IllegalArgumentException; + using ::com::sun::star::beans::PropertyState_DIRECT_VALUE; + + NamedValueCollection::NamedValueCollection( const Any& _rElements ) + { + impl_assign( _rElements ); + } + + + NamedValueCollection::NamedValueCollection( const Sequence< Any >& _rArguments ) + { + impl_assign( _rArguments ); + } + + + NamedValueCollection::NamedValueCollection( const Sequence< PropertyValue >& _rArguments ) + { + impl_assign( _rArguments ); + } + + + NamedValueCollection::NamedValueCollection( const Sequence< NamedValue >& _rArguments ) + { + impl_assign( _rArguments ); + } + + + bool NamedValueCollection::canExtractFrom( css::uno::Any const & i_value ) + { + Type const & aValueType = i_value.getValueType(); + return aValueType.equals( ::cppu::UnoType< PropertyValue >::get() ) + || aValueType.equals( ::cppu::UnoType< NamedValue >::get() ) + || aValueType.equals( ::cppu::UnoType< Sequence< PropertyValue > >::get() ) + || aValueType.equals( ::cppu::UnoType< Sequence< NamedValue > >::get() ); + } + + + NamedValueCollection& NamedValueCollection::merge( const NamedValueCollection& _rAdditionalValues, bool _bOverwriteExisting ) + { + for (auto const& value : _rAdditionalValues.maValues) + { + if ( _bOverwriteExisting || !impl_has( value.first ) ) + impl_put( value.first, value.second ); + } + + return *this; + } + + + size_t NamedValueCollection::size() const + { + return maValues.size(); + } + + + bool NamedValueCollection::empty() const + { + return maValues.empty(); + } + + + std::vector< OUString > NamedValueCollection::getNames() const + { + std::vector< OUString > aNames; + for (auto const& value : maValues) + { + aNames.push_back( value.first ); + } + return aNames; + } + + + void NamedValueCollection::impl_assign( const Any& i_rWrappedElements ) + { + Sequence< NamedValue > aNamedValues; + Sequence< PropertyValue > aPropertyValues; + NamedValue aNamedValue; + PropertyValue aPropertyValue; + + if ( i_rWrappedElements >>= aNamedValues ) + impl_assign( aNamedValues ); + else if ( i_rWrappedElements >>= aPropertyValues ) + impl_assign( aPropertyValues ); + else if ( i_rWrappedElements >>= aNamedValue ) + impl_assign( Sequence< NamedValue >( &aNamedValue, 1 ) ); + else if ( i_rWrappedElements >>= aPropertyValue ) + impl_assign( Sequence< PropertyValue >( &aPropertyValue, 1 ) ); + else + SAL_WARN_IF( i_rWrappedElements.hasValue(), "comphelper", "NamedValueCollection::impl_assign(Any): unsupported type!" ); + } + + + void NamedValueCollection::impl_assign( const Sequence< Any >& _rArguments ) + { + maValues.clear(); + + PropertyValue aPropertyValue; + NamedValue aNamedValue; + + for ( auto const & argument : _rArguments ) + { + if ( argument >>= aPropertyValue ) + maValues[ aPropertyValue.Name ] = aPropertyValue.Value; + else if ( argument >>= aNamedValue ) + maValues[ aNamedValue.Name ] = aNamedValue.Value; + else + { + SAL_WARN_IF( + argument.hasValue(), "comphelper", + ("NamedValueCollection::impl_assign: encountered a value" + " type which I cannot handle: " + + argument.getValueTypeName())); + } + } + } + + + void NamedValueCollection::impl_assign( const Sequence< PropertyValue >& _rArguments ) + { + maValues.clear(); + + for ( auto const & argument : _rArguments ) + maValues[ argument.Name ] = argument.Value; + } + + + void NamedValueCollection::impl_assign( const Sequence< NamedValue >& _rArguments ) + { + maValues.clear(); + + for ( auto const & argument : _rArguments ) + maValues[ argument.Name ] = argument.Value; + } + + + bool NamedValueCollection::get_ensureType( const OUString& _rValueName, void* _pValueLocation, const Type& _rExpectedValueType ) const + { + auto pos = maValues.find( _rValueName ); + if ( pos == maValues.end() ) + // argument does not exist + return false; + + if ( uno_type_assignData( + _pValueLocation, _rExpectedValueType.getTypeLibType(), + const_cast< void* >( pos->second.getValue() ), pos->second.getValueType().getTypeLibType(), + reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ), + reinterpret_cast< uno_AcquireFunc >( cpp_acquire ), + reinterpret_cast< uno_ReleaseFunc >( cpp_release ) + ) ) + // argument exists, and could be extracted + return true; + + // argument exists, but is of wrong type + throw IllegalArgumentException( + "Invalid value type for '" + _rValueName + + "'.\nExpected: " + _rExpectedValueType.getTypeName() + + "\nFound: " + pos->second.getValueType().getTypeName(), + nullptr, 0 ); + } + + // static + bool NamedValueCollection::get_ensureType( const css::uno::Sequence<css::beans::PropertyValue>& rPropSeq, + std::u16string_view _rValueName, void* _pValueLocation, const Type& _rExpectedValueType ) + { + for (const css::beans::PropertyValue& rPropVal : rPropSeq) + { + if (rPropVal.Name == _rValueName) + { + if ( uno_type_assignData( + _pValueLocation, _rExpectedValueType.getTypeLibType(), + const_cast< void* >( rPropVal.Value.getValue() ), rPropVal.Value.getValueType().getTypeLibType(), + reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ), + reinterpret_cast< uno_AcquireFunc >( cpp_acquire ), + reinterpret_cast< uno_ReleaseFunc >( cpp_release ) + ) ) + // argument exists, and could be extracted + return true; + + // argument exists, but is of wrong type + throw IllegalArgumentException( + OUString::Concat("Invalid value type for '") + _rValueName + + "'.\nExpected: " + _rExpectedValueType.getTypeName() + + "\nFound: " + rPropVal.Value.getValueType().getTypeName(), + nullptr, 0 ); + } + } + // argument does not exist + return false; + } + + // static + const css::uno::Any& NamedValueCollection::get( const css::uno::Sequence<css::beans::PropertyValue>& rPropSeq, + std::u16string_view _rValueName ) + { + static const Any theEmptyDefault; + for (const css::beans::PropertyValue& rPropVal : rPropSeq) + { + if (rPropVal.Name == _rValueName) + { + return rPropVal.Value; + } + } + return theEmptyDefault; + } + + const Any& NamedValueCollection::impl_get( const OUString& _rValueName ) const + { + static const Any theEmptyDefault; + auto pos = maValues.find( _rValueName ); + if ( pos != maValues.end() ) + return pos->second; + + return theEmptyDefault; + } + + + bool NamedValueCollection::impl_has( const OUString& _rValueName ) const + { + auto pos = maValues.find( _rValueName ); + return ( pos != maValues.end() ); + } + + + bool NamedValueCollection::impl_put( const OUString& _rValueName, const Any& _rValue ) + { + bool bHas = impl_has( _rValueName ); + maValues[ _rValueName ] = _rValue; + return bHas; + } + + + bool NamedValueCollection::impl_remove( const OUString& _rValueName ) + { + auto pos = maValues.find( _rValueName ); + if ( pos == maValues.end() ) + return false; + maValues.erase( pos ); + return true; + } + + + sal_Int32 NamedValueCollection::operator >>= ( Sequence< PropertyValue >& _out_rValues ) const + { + _out_rValues.realloc( maValues.size() ); + std::transform( maValues.begin(), maValues.end(), _out_rValues.getArray(), + [](const std::pair< OUString, css::uno::Any >& _rValue) + { return PropertyValue( _rValue.first, 0, _rValue.second, PropertyState_DIRECT_VALUE ); } ); + return _out_rValues.getLength(); + } + + + sal_Int32 NamedValueCollection::operator >>= ( Sequence< NamedValue >& _out_rValues ) const + { + _out_rValues.realloc( maValues.size() ); + std::transform( maValues.begin(), maValues.end(), _out_rValues.getArray(), + [](const std::pair< OUString, css::uno::Any >& _rValue) + { return NamedValue( _rValue.first, _rValue.second ); } ); + return _out_rValues.getLength(); + } + + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/numberedcollection.cxx b/comphelper/source/misc/numberedcollection.cxx new file mode 100644 index 000000000..80d33776c --- /dev/null +++ b/comphelper/source/misc/numberedcollection.cxx @@ -0,0 +1,213 @@ +/* -*- 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 <algorithm> +#include <comphelper/numberedcollection.hxx> +#include <com/sun/star/frame/UntitledNumbersConst.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +namespace comphelper{ + +constexpr OUStringLiteral ERRMSG_INVALID_COMPONENT_PARAM = u"NULL as component reference not allowed."; + + +NumberedCollection::NumberedCollection() +{ +} + + +NumberedCollection::~NumberedCollection() +{ +} + + +void NumberedCollection::setOwner(const css::uno::Reference< css::uno::XInterface >& xOwner) +{ + // SYNCHRONIZED -> + std::scoped_lock aLock(m_aMutex); + + m_xOwner = xOwner; + + // <- SYNCHRONIZED +} + + +void NumberedCollection::setUntitledPrefix(const OUString& sPrefix) +{ + // SYNCHRONIZED -> + std::scoped_lock aLock(m_aMutex); + + m_sUntitledPrefix = sPrefix; + + // <- SYNCHRONIZED +} + + +::sal_Int32 SAL_CALL NumberedCollection::leaseNumber(const css::uno::Reference< css::uno::XInterface >& xComponent) +{ + // SYNCHRONIZED -> + std::scoped_lock aLock(m_aMutex); + + if ( ! xComponent.is ()) + throw css::lang::IllegalArgumentException(ERRMSG_INVALID_COMPONENT_PARAM, m_xOwner.get(), 1); + + sal_IntPtr pComponent = reinterpret_cast<sal_IntPtr>( xComponent.get() ); + TNumberedItemHash::const_iterator pIt = m_lComponents.find (pComponent); + + // a) component already exists - return its number directly + if (pIt != m_lComponents.end()) + return pIt->second.nNumber; + + // b) component must be added new to this container + + // b1) collection is full - no further components possible + // -> return INVALID_NUMBER + ::sal_Int32 nFreeNumber = impl_searchFreeNumber(); + if (nFreeNumber == css::frame::UntitledNumbersConst::INVALID_NUMBER) + return css::frame::UntitledNumbersConst::INVALID_NUMBER; + + // b2) add component to collection and return its number + TNumberedItem aItem; + aItem.xItem = css::uno::WeakReference< css::uno::XInterface >(xComponent); + aItem.nNumber = nFreeNumber; + m_lComponents[pComponent] = aItem; + + return nFreeNumber; + + // <- SYNCHRONIZED +} + + +void SAL_CALL NumberedCollection::releaseNumber(::sal_Int32 nNumber) +{ + // SYNCHRONIZED -> + std::scoped_lock aLock(m_aMutex); + + if (nNumber == css::frame::UntitledNumbersConst::INVALID_NUMBER) + throw css::lang::IllegalArgumentException ("Special value INVALID_NUMBER not allowed as input parameter.", m_xOwner.get(), 1); + + TDeadItemList lDeadItems; + TNumberedItemHash::iterator pComponent; + + for ( pComponent = m_lComponents.begin (); + pComponent != m_lComponents.end (); + ++pComponent ) + { + const TNumberedItem& rItem = pComponent->second; + const css::uno::Reference< css::uno::XInterface > xItem = rItem.xItem.get(); + + if ( ! xItem.is ()) + { + lDeadItems.push_back(pComponent->first); + continue; + } + + if (rItem.nNumber == nNumber) + { + m_lComponents.erase (pComponent); + break; + } + } + + impl_cleanUpDeadItems(m_lComponents, lDeadItems); + + // <- SYNCHRONIZED +} + + +void SAL_CALL NumberedCollection::releaseNumberForComponent(const css::uno::Reference< css::uno::XInterface >& xComponent) +{ + // SYNCHRONIZED -> + std::scoped_lock aLock(m_aMutex); + + if ( ! xComponent.is ()) + throw css::lang::IllegalArgumentException(ERRMSG_INVALID_COMPONENT_PARAM, m_xOwner.get(), 1); + + sal_IntPtr pComponent = reinterpret_cast<sal_IntPtr>( xComponent.get() ); + TNumberedItemHash::iterator pIt = m_lComponents.find (pComponent); + + // a) component exists and will be removed + if (pIt != m_lComponents.end()) + m_lComponents.erase(pIt); + + // else + // b) component does not exists - nothing todo here (ignore request!) + + // <- SYNCHRONIZED +} + + +OUString SAL_CALL NumberedCollection::getUntitledPrefix() +{ + // SYNCHRONIZED -> + std::scoped_lock aLock(m_aMutex); + + return m_sUntitledPrefix; + + // <- SYNCHRONIZED +} + + +/** create an ordered list of all possible numbers ... + e.g. {1,2,3,...,N} Max size of these list will be + current size of component list + 1 . + + "+1" ... because in case all numbers in range 1..n + are in use we need a new number n+1 :-) + + Every item which is already used as unique number + will be removed. At the end a list of e.g. {3,6,...,M} + exists where the first item represent the lowest free + number (in this example 3). + */ +::sal_Int32 NumberedCollection::impl_searchFreeNumber () +{ + // create bitset, where each position represents one possible number. + std::vector<bool> aUsedNumbers((m_lComponents.size() * 2) + 1, false); + + for (const auto& rPair : m_lComponents) + { + // numbers start at 1 + sal_Int32 pos = rPair.second.nNumber - 1; + if (pos >= static_cast<sal_Int32>(aUsedNumbers.size())) + aUsedNumbers.resize(pos * 2, false); // should be rare + aUsedNumbers[pos] = true; + } + + // a) non free numbers ... return INVALID_NUMBER + auto it = std::find(aUsedNumbers.begin(), aUsedNumbers.end(), false); + if (it == aUsedNumbers.end()) + return css::frame::UntitledNumbersConst::INVALID_NUMBER; + + // b) return first free number + return it - aUsedNumbers.begin() + 1; +} + +void NumberedCollection::impl_cleanUpDeadItems ( TNumberedItemHash& lItems , + const TDeadItemList& lDeadItems) +{ + for (const sal_IntPtr& rDeadItem : lDeadItems) + { + lItems.erase(rDeadItem); + } +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/numbers.cxx b/comphelper/source/misc/numbers.cxx new file mode 100644 index 000000000..f3b609392 --- /dev/null +++ b/comphelper/source/misc/numbers.cxx @@ -0,0 +1,120 @@ +/* -*- 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 <comphelper/numbers.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <com/sun/star/util/NumberFormat.hpp> +#include <com/sun/star/util/XNumberFormatter.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + + +namespace comphelper +{ + +sal_Int16 getNumberFormatType(const css::uno::Reference<css::util::XNumberFormats>& xFormats, sal_Int32 nKey) +{ + sal_Int16 nReturn(css::util::NumberFormat::UNDEFINED); + if (xFormats.is()) + { + try + { + css::uno::Reference<css::beans::XPropertySet> xFormat(xFormats->getByKey(nKey)); + if (xFormat.is()) + xFormat->getPropertyValue("Type") >>= nReturn; + } + catch(...) + { + SAL_WARN("comphelper", "getNumberFormatType : invalid key! (maybe created with another formatter ?)"); + } + } + return nReturn; +} + + +sal_Int16 getNumberFormatType(const css::uno::Reference<css::util::XNumberFormatter>& xFormatter, sal_Int32 nKey) +{ + OSL_ENSURE(xFormatter.is(), "getNumberFormatType : the formatter isn't valid !"); + css::uno::Reference<css::util::XNumberFormatsSupplier> xSupplier( xFormatter->getNumberFormatsSupplier()); + OSL_ENSURE(xSupplier.is(), "getNumberFormatType : the formatter doesn't implement a supplier !"); + css::uno::Reference<css::util::XNumberFormats> xFormats( xSupplier->getNumberFormats()); + return getNumberFormatType(xFormats, nKey); +} + + +css::uno::Any getNumberFormatDecimals(const css::uno::Reference<css::util::XNumberFormats>& xFormats, sal_Int32 nKey) +{ + if (xFormats.is()) + { + try + { + css::uno::Reference<css::beans::XPropertySet> xFormat( xFormats->getByKey(nKey)); + if (xFormat.is()) + { + return xFormat->getPropertyValue( "Decimals" ); + } + } + catch(...) + { + SAL_WARN("comphelper", "getNumberFormatDecimals : invalid key! (may be created with another formatter ?)"); + } + } + return css::uno::Any(sal_Int16(0)); +} + + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::beans; + + +Any getNumberFormatProperty( const Reference< XNumberFormatter >& _rxFormatter, sal_Int32 _nKey, const OUString& _rPropertyName ) +{ + Any aReturn; + + OSL_ENSURE( _rxFormatter.is() && !_rPropertyName.isEmpty(), "getNumberFormatProperty: invalid arguments!" ); + try + { + Reference< XNumberFormatsSupplier > xSupplier; + Reference< XNumberFormats > xFormats; + Reference< XPropertySet > xFormatProperties; + + if ( _rxFormatter.is() ) + xSupplier = _rxFormatter->getNumberFormatsSupplier(); + if ( xSupplier.is() ) + xFormats = xSupplier->getNumberFormats(); + if ( xFormats.is() ) + xFormatProperties = xFormats->getByKey( _nKey ); + + if ( xFormatProperties.is() ) + aReturn = xFormatProperties->getPropertyValue( _rPropertyName ); + } + catch( const Exception& ) + { + OSL_FAIL( "::getNumberFormatProperty: caught an exception (did you create the key with another formatter?)!" ); + } + + return aReturn; +} + + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/officerestartmanager.cxx b/comphelper/source/misc/officerestartmanager.cxx new file mode 100644 index 000000000..39c61a4e5 --- /dev/null +++ b/comphelper/source/misc/officerestartmanager.cxx @@ -0,0 +1,157 @@ +/* -*- 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/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/awt/XRequestCallback.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <cppuhelper/supportsservice.hxx> +#include "officerestartmanager.hxx" + +using namespace ::com::sun::star; + +namespace comphelper +{ + +// XRestartManager + +void SAL_CALL OOfficeRestartManager::requestRestart( const uno::Reference< task::XInteractionHandler >& /* xInteractionHandler */ ) +{ + if ( !m_xContext.is() ) + throw uno::RuntimeException(); + + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // if the restart already running there is no need to trigger it again + if ( m_bRestartRequested ) + return; + + m_bRestartRequested = true; + + // the office is still not initialized, no need to terminate, changing the state is enough + if ( !m_bOfficeInitialized ) + return; + } + + // TODO: use InteractionHandler to report errors + try + { + // register itself as a job that should be executed asynchronously + uno::Reference< lang::XMultiComponentFactory > xFactory( m_xContext->getServiceManager(), uno::UNO_SET_THROW ); + + uno::Reference< awt::XRequestCallback > xRequestCallback( + xFactory->createInstanceWithContext( + "com.sun.star.awt.AsyncCallback", + m_xContext ), + uno::UNO_QUERY_THROW ); + + xRequestCallback->addCallback( this, uno::Any() ); + } + catch ( uno::Exception& ) + { + // the try to request restart has failed + m_bRestartRequested = false; + } +} + + +sal_Bool SAL_CALL OOfficeRestartManager::isRestartRequested( sal_Bool bOfficeInitialized ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( bOfficeInitialized && !m_bOfficeInitialized ) + m_bOfficeInitialized = bOfficeInitialized; + + return m_bRestartRequested; +} + +// XCallback + +void SAL_CALL OOfficeRestartManager::notify( const uno::Any& /* aData */ ) +{ + try + { + bool bSuccess = false; + + if ( m_xContext.is() ) + { + uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(m_xContext); + + // Turn Quickstarter veto off + uno::Reference< beans::XPropertySet > xPropertySet( xDesktop, uno::UNO_QUERY_THROW ); + OUString aVetoPropName( "SuspendQuickstartVeto" ); + uno::Any aValue; + aValue <<= true; + xPropertySet->setPropertyValue( aVetoPropName, aValue ); + + try + { + bSuccess = xDesktop->terminate(); + } catch( uno::Exception& ) + {} + + if ( !bSuccess ) + { + aValue <<= false; + xPropertySet->setPropertyValue( aVetoPropName, aValue ); + } + } + + if ( !bSuccess ) + m_bRestartRequested = false; + } + catch( uno::Exception& ) + { + // the try to restart has failed + m_bRestartRequested = false; + } +} + +// XServiceInfo + +OUString SAL_CALL OOfficeRestartManager::getImplementationName() +{ + return "com.sun.star.comp.task.OfficeRestartManager"; +} + +sal_Bool SAL_CALL OOfficeRestartManager::supportsService( const OUString& aServiceName ) +{ + return cppu::supportsService(this, aServiceName); +} + +uno::Sequence< OUString > SAL_CALL OOfficeRestartManager::getSupportedServiceNames() +{ + return { "com.sun.star.comp.task.OfficeRestartManager" }; +} + +} // namespace comphelper + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_task_OfficeRestartManager( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new comphelper::OOfficeRestartManager(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/officerestartmanager.hxx b/comphelper/source/misc/officerestartmanager.hxx new file mode 100644 index 000000000..bc95668b9 --- /dev/null +++ b/comphelper/source/misc/officerestartmanager.hxx @@ -0,0 +1,67 @@ +/* -*- 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/task/XRestartManager.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/awt/XCallback.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <osl/mutex.hxx> +#include <cppuhelper/implbase.hxx> +#include <utility> + +namespace comphelper +{ + +class OOfficeRestartManager : public ::cppu::WeakImplHelper< css::task::XRestartManager + , css::awt::XCallback + , css::lang::XServiceInfo > +{ + ::osl::Mutex m_aMutex; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + bool m_bOfficeInitialized; + bool m_bRestartRequested; + +public: + explicit OOfficeRestartManager( css::uno::Reference< css::uno::XComponentContext > xContext ) + : m_xContext(std::move( xContext )) + , m_bOfficeInitialized( false ) + , m_bRestartRequested( false ) + {} + +// XRestartManager + virtual void SAL_CALL requestRestart( const css::uno::Reference< css::task::XInteractionHandler >& xInteractionHandler ) override; + virtual sal_Bool SAL_CALL isRestartRequested( sal_Bool bInitialized ) override; + +// XCallback + virtual void SAL_CALL notify( const css::uno::Any& aData ) 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; + +}; + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/proxyaggregation.cxx b/comphelper/source/misc/proxyaggregation.cxx new file mode 100644 index 000000000..db580bea4 --- /dev/null +++ b/comphelper/source/misc/proxyaggregation.cxx @@ -0,0 +1,243 @@ +/* -*- 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 <sal/config.h> + +#include <cassert> + +#include <comphelper/proxyaggregation.hxx> +#include <com/sun/star/reflection/ProxyFactory.hpp> + + +namespace comphelper +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::reflection; + + OProxyAggregation::OProxyAggregation( const Reference< XComponentContext >& _rxContext ) + :m_xContext( _rxContext ) + { + } + + + void OProxyAggregation::baseAggregateProxyFor( const Reference< XInterface >& _rxComponent, oslInterlockedCount& _rRefCount, + ::cppu::OWeakObject& _rDelegator ) + { + // first a factory for the proxy + Reference< XProxyFactory > xFactory = ProxyFactory::create( m_xContext ); + + // then the proxy itself + { // i36686 OJ: achieve the destruction of the temporary -> otherwise it leads to _rRefCount -= 2 + m_xProxyAggregate = xFactory->createProxy( _rxComponent ); + } + if ( m_xProxyAggregate.is() ) + m_xProxyAggregate->queryAggregation( cppu::UnoType<decltype(m_xProxyTypeAccess)>::get() ) >>= m_xProxyTypeAccess; + + // aggregate the proxy + osl_atomic_increment( &_rRefCount ); + if ( m_xProxyAggregate.is() ) + { + // At this point in time, the proxy has a ref count of exactly two - in m_xControlContextProxy, + // and in m_xProxyTypeAccess. + // Remember to _not_ reset these members unless the delegator of the proxy has been reset, too! + m_xProxyAggregate->setDelegator( _rDelegator ); + } + osl_atomic_decrement( &_rRefCount ); + } + + + Any SAL_CALL OProxyAggregation::queryAggregation( const Type& _rType ) + { + return m_xProxyAggregate.is() ? m_xProxyAggregate->queryAggregation( _rType ) : Any(); + } + + + Sequence< Type > SAL_CALL OProxyAggregation::getTypes( ) + { + Sequence< Type > aTypes; + if ( m_xProxyAggregate.is() ) + { + if ( m_xProxyTypeAccess.is() ) + aTypes = m_xProxyTypeAccess->getTypes(); + } + return aTypes; + } + + + OProxyAggregation::~OProxyAggregation() + { + if ( m_xProxyAggregate.is() ) + m_xProxyAggregate->setDelegator( nullptr ); + m_xProxyAggregate.clear(); + m_xProxyTypeAccess.clear(); + // this should remove the _two_only_ "real" references (means not delegated to + // ourself) to this proxy, and thus delete it + } + + OComponentProxyAggregationHelper::OComponentProxyAggregationHelper( const Reference< XComponentContext >& _rxContext, + ::cppu::OBroadcastHelper& _rBHelper ) + :OProxyAggregation( _rxContext ) + ,m_rBHelper( _rBHelper ) + { + OSL_ENSURE( _rxContext.is(), "OComponentProxyAggregationHelper::OComponentProxyAggregationHelper: invalid arguments!" ); + } + + + void OComponentProxyAggregationHelper::componentAggregateProxyFor( + const Reference< XComponent >& _rxComponent, oslInterlockedCount& _rRefCount, + ::cppu::OWeakObject& _rDelegator ) + { + OSL_ENSURE( _rxComponent.is(), "OComponentProxyAggregationHelper::componentAggregateProxyFor: invalid inner component!" ); + m_xInner = _rxComponent; + + // aggregate a proxy for the object + baseAggregateProxyFor( m_xInner, _rRefCount, _rDelegator ); + + // add as event listener to the inner context, because we want to be notified of disposals + osl_atomic_increment( &_rRefCount ); + { + if ( m_xInner.is() ) + m_xInner->addEventListener( this ); + } + osl_atomic_decrement( &_rRefCount ); + } + + + Any SAL_CALL OComponentProxyAggregationHelper::queryInterface( const Type& _rType ) + { + Any aReturn( BASE::queryInterface( _rType ) ); + if ( !aReturn.hasValue() ) + aReturn = OProxyAggregation::queryAggregation( _rType ); + return aReturn; + } + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( OComponentProxyAggregationHelper, BASE, OProxyAggregation ) + + + OComponentProxyAggregationHelper::~OComponentProxyAggregationHelper( ) + { + OSL_ENSURE( m_rBHelper.bDisposed, "OComponentProxyAggregationHelper::~OComponentProxyAggregationHelper: you should dispose your derived class in the dtor, if necessary!" ); + // if this asserts, add the following to your derived class dtor: + + // if ( !m_rBHelper.bDisposed ) + // { + // acquire(); // to prevent duplicate dtor calls + // dispose(); + // } + + m_xInner.clear(); + } + + + void SAL_CALL OComponentProxyAggregationHelper::disposing( const EventObject& _rSource ) + { + if ( _rSource.Source == m_xInner ) + { // it's our inner context which is dying -> dispose ourself + if ( !m_rBHelper.bDisposed && !m_rBHelper.bInDispose ) + { // (if necessary only, of course) + dispose(); + } + } + } + + + void SAL_CALL OComponentProxyAggregationHelper::dispose() + { + ::osl::MutexGuard aGuard( m_rBHelper.rMutex ); + + // dispose our inner context + // before we do this, remove ourself as listener - else in disposing( EventObject ), we + // would dispose ourself a second time + if ( m_xInner.is() ) + { + m_xInner->removeEventListener( this ); + m_xInner->dispose(); + m_xInner.clear(); + } + } + + OComponentProxyAggregation::OComponentProxyAggregation( const Reference< XComponentContext >& _rxContext, + const Reference< XComponent >& _rxComponent ) + :WeakComponentImplHelperBase( m_aMutex ) + ,OComponentProxyAggregationHelper( _rxContext, rBHelper ) + { + OSL_ENSURE( _rxComponent.is(), "OComponentProxyAggregation::OComponentProxyAggregation: accessible is no XComponent!" ); + if ( _rxComponent.is() ) + componentAggregateProxyFor( _rxComponent, m_refCount, *this ); + } + + + OComponentProxyAggregation::~OComponentProxyAggregation() + { + if ( !rBHelper.bDisposed ) + { + acquire(); // to prevent duplicate dtor calls + dispose(); + } + } + + + IMPLEMENT_FORWARD_XINTERFACE2( OComponentProxyAggregation, WeakComponentImplHelperBase, OComponentProxyAggregationHelper ) + + + IMPLEMENT_GET_IMPLEMENTATION_ID( OComponentProxyAggregation ) + + + Sequence< Type > SAL_CALL OComponentProxyAggregation::getTypes( ) + { + return comphelper::concatSequences( + OComponentProxyAggregationHelper::getTypes(), + // append XComponent, coming from WeakComponentImplHelperBase + Sequence { cppu::UnoType<XComponent>::get() }); + } + + + void SAL_CALL OComponentProxyAggregation::disposing( const EventObject& _rSource ) + { + // Simply disambiguate---this is necessary for MSVC to distinguish + // "disposing(EventObject)" from "disposing()"; but it is also a good + // place to check for recursive calls that would be caused by an object + // being registered as an XEventListener at itself (cf. rhbz#928568): + assert(_rSource.Source != static_cast< cppu::OWeakObject * >(this)); + OComponentProxyAggregationHelper::disposing( _rSource ); + } + + + void SAL_CALL OComponentProxyAggregation::disposing() + { + // call the dispose-functionality of the base, which will dispose our aggregated component + OComponentProxyAggregationHelper::dispose(); + } + + + void SAL_CALL OComponentProxyAggregation::dispose() + { + // simply disambiguate + WeakComponentImplHelperBase::dispose(); + } + + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/random.cxx b/comphelper/source/misc/random.cxx new file mode 100644 index 000000000..96d466641 --- /dev/null +++ b/comphelper/source/misc/random.cxx @@ -0,0 +1,123 @@ +/* -*- 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/. + * + * Contributor(s): + * Copyright (C) 2012 Tino Kluge <tino.kluge@hrz.tu-chemnitz.de> + */ + +#include <comphelper/random.hxx> +#include <sal/log.hxx> +#include <assert.h> +#include <time.h> +#include <mutex> +#include <random> +#include <stdexcept> +#if defined HAVE_VALGRIND_HEADERS +#include <valgrind/memcheck.h> +#endif + +// this is nothing but a simple wrapper around +// the std::random generators + +namespace comphelper::rng +{ +// underlying random number generator +// std::mt19937 implements the Mersenne twister algorithm which +// is fast and has good statistical properties, it produces integers +// in the range of [0, 2^32-1] internally +// memory requirement: 625*sizeof(uint32_t) +// http://en.wikipedia.org/wiki/Mersenne_twister +#define STD_RNG_ALGO std::mt19937 + +namespace +{ +struct RandomNumberGenerator +{ + std::mutex mutex; + STD_RNG_ALGO global_rng; + RandomNumberGenerator() + { + // make RR easier to use, breaks easily without the RNG being repeatable + bool bRepeatable = (getenv("SAL_RAND_REPEATABLE") != nullptr) || (getenv("RR") != nullptr); + // valgrind on some platforms (e.g.Ubuntu16.04) does not support the new Intel RDRAND instructions, + // which leads to "Illegal Opcode" errors, so just turn off randomness. +#if defined HAVE_VALGRIND_HEADERS + if (RUNNING_ON_VALGRIND) + bRepeatable = true; +#endif + if (bRepeatable) + { + global_rng.seed(42); + return; + } + + try + { + std::random_device rd; + // initialises the state of the global random number generator + // should only be called once. + // (note, a few std::variate_generator<> (like normal) have their + // own state which would need a reset as well to guarantee identical + // sequence of numbers, e.g. via myrand.distribution().reset()) + global_rng.seed(rd() ^ time(nullptr)); + } + catch (std::runtime_error& e) + { + SAL_WARN("comphelper", "Using std::random_device failed: " << e.what()); + global_rng.seed(time(nullptr)); + } + } +}; + +RandomNumberGenerator& GetTheRandomNumberGenerator() +{ + static RandomNumberGenerator RANDOM; + return RANDOM; +} +} + +// uniform ints [a,b] distribution +int uniform_int_distribution(int a, int b) +{ + std::uniform_int_distribution<int> dist(a, b); + auto& gen = GetTheRandomNumberGenerator(); + std::scoped_lock<std::mutex> g(gen.mutex); + return dist(gen.global_rng); +} + +// uniform ints [a,b] distribution +unsigned int uniform_uint_distribution(unsigned int a, unsigned int b) +{ + std::uniform_int_distribution<unsigned int> dist(a, b); + auto& gen = GetTheRandomNumberGenerator(); + std::scoped_lock<std::mutex> g(gen.mutex); + return dist(gen.global_rng); +} + +// uniform size_t [a,b] distribution +size_t uniform_size_distribution(size_t a, size_t b) +{ + std::uniform_int_distribution<size_t> dist(a, b); + auto& gen = GetTheRandomNumberGenerator(); + std::scoped_lock<std::mutex> g(gen.mutex); + return dist(gen.global_rng); +} + +// uniform size_t [a,b) distribution +double uniform_real_distribution(double a, double b) +{ + assert(a < b); + std::uniform_real_distribution<double> dist(a, b); + auto& gen = GetTheRandomNumberGenerator(); + std::scoped_lock<std::mutex> g(gen.mutex); + return dist(gen.global_rng); +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/sequenceashashmap.cxx b/comphelper/source/misc/sequenceashashmap.cxx new file mode 100644 index 000000000..34a6a0c8a --- /dev/null +++ b/comphelper/source/misc/sequenceashashmap.cxx @@ -0,0 +1,378 @@ +/* -*- 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 <sal/config.h> + +#include <boost/property_tree/json_parser.hpp> + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/reflection/XIdlField.hpp> +#include <com/sun/star/reflection/theCoreReflection.hpp> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <sal/log.hxx> +#include <o3tl/string_view.hxx> + +using namespace com::sun::star; + +namespace +{ +uno::Any jsonToUnoAny(const boost::property_tree::ptree& aTree) +{ + uno::Any aAny; + uno::Any aValue; + sal_Int32 nFields; + uno::Reference<reflection::XIdlField> aField; + boost::property_tree::ptree aNodeNull, aNodeValue, aNodeField; + const std::string& rType = aTree.get<std::string>("type", ""); + const std::string& rValue = aTree.get<std::string>("value", ""); + uno::Sequence<uno::Reference<reflection::XIdlField>> aFields; + uno::Reference<reflection::XIdlClass> xIdlClass + = css::reflection::theCoreReflection::get(comphelper::getProcessComponentContext()) + ->forName(OUString::fromUtf8(rType.c_str())); + if (xIdlClass.is()) + { + uno::TypeClass aTypeClass = xIdlClass->getTypeClass(); + xIdlClass->createObject(aAny); + aFields = xIdlClass->getFields(); + nFields = aFields.getLength(); + aNodeValue = aTree.get_child("value", aNodeNull); + if (nFields > 0 && aNodeValue != aNodeNull) + { + for (sal_Int32 itField = 0; itField < nFields; ++itField) + { + aField = aFields[itField]; + aNodeField = aNodeValue.get_child(aField->getName().toUtf8().getStr(), aNodeNull); + if (aNodeField != aNodeNull) + { + aValue = jsonToUnoAny(aNodeField); + aField->set(aAny, aValue); + } + } + } + else if (!rValue.empty()) + { + if (aTypeClass == uno::TypeClass_VOID) + aAny.clear(); + else if (aTypeClass == uno::TypeClass_BYTE) + aAny <<= static_cast<sal_Int8>(o3tl::toInt32(rValue)); + else if (aTypeClass == uno::TypeClass_BOOLEAN) + aAny <<= OString(rValue.c_str()).toBoolean(); + else if (aTypeClass == uno::TypeClass_SHORT) + aAny <<= static_cast<sal_Int16>(o3tl::toInt32(rValue)); + else if (aTypeClass == uno::TypeClass_UNSIGNED_SHORT) + aAny <<= static_cast<sal_uInt16>(o3tl::toUInt32(rValue)); + else if (aTypeClass == uno::TypeClass_LONG) + aAny <<= o3tl::toInt32(rValue); + else if (aTypeClass == uno::TypeClass_UNSIGNED_LONG) + aAny <<= static_cast<sal_uInt32>(o3tl::toInt32(rValue)); + else if (aTypeClass == uno::TypeClass_FLOAT) + aAny <<= OString(rValue.c_str()).toFloat(); + else if (aTypeClass == uno::TypeClass_DOUBLE) + aAny <<= o3tl::toDouble(rValue); + else if (aTypeClass == uno::TypeClass_STRING) + aAny <<= OUString::fromUtf8(rValue.c_str()); + } + } + return aAny; +} +} + +namespace comphelper{ + +SequenceAsHashMap::SequenceAsHashMap() +{ +} + +SequenceAsHashMap::SequenceAsHashMap(const css::uno::Any& aSource) +{ + (*this) << aSource; +} + + +SequenceAsHashMap::SequenceAsHashMap(const css::uno::Sequence< css::uno::Any >& lSource) +{ + (*this) << lSource; +} + +SequenceAsHashMap::SequenceAsHashMap(const css::uno::Sequence< css::beans::PropertyValue >& lSource) +{ + (*this) << lSource; +} + +SequenceAsHashMap::SequenceAsHashMap(const css::uno::Sequence< css::beans::NamedValue >& lSource) +{ + (*this) << lSource; +} + +void SequenceAsHashMap::operator<<(const css::uno::Any& aSource) +{ + // An empty Any reset this instance! + if (!aSource.hasValue()) + { + clear(); + return; + } + + css::uno::Sequence< css::beans::NamedValue > lN; + if (aSource >>= lN) + { + (*this) << lN; + return; + } + + css::uno::Sequence< css::beans::PropertyValue > lP; + if (aSource >>= lP) + { + (*this) << lP; + return; + } + + throw css::lang::IllegalArgumentException( + "Any contains wrong type.", css::uno::Reference<css::uno::XInterface>(), + -1); +} + + +void SequenceAsHashMap::operator<<(const css::uno::Sequence< css::uno::Any >& lSource) +{ + sal_Int32 c = lSource.getLength(); + sal_Int32 i = 0; + + m_aMap.reserve(c); + for (i=0; i<c; ++i) + { + css::beans::PropertyValue lP; + if (lSource[i] >>= lP) + { + if ( + (lP.Name.isEmpty()) || + (!lP.Value.hasValue()) + ) + throw css::lang::IllegalArgumentException( + "PropertyValue struct contains no useful information.", + css::uno::Reference<css::uno::XInterface>(), -1); + (*this)[lP.Name] = lP.Value; + continue; + } + + css::beans::NamedValue lN; + if (lSource[i] >>= lN) + { + if ( + (lN.Name.isEmpty()) || + (!lN.Value.hasValue()) + ) + throw css::lang::IllegalArgumentException( + "NamedValue struct contains no useful information.", + css::uno::Reference<css::uno::XInterface>(), -1); + (*this)[lN.Name] = lN.Value; + continue; + } + + // ignore VOID Any ... but reject wrong filled ones! + if (lSource[i].hasValue()) + throw css::lang::IllegalArgumentException( + "Any contains wrong type.", + css::uno::Reference<css::uno::XInterface>(), -1); + } +} + +void SequenceAsHashMap::operator<<(const css::uno::Sequence< css::beans::PropertyValue >& lSource) +{ + clear(); + + sal_Int32 c = lSource.getLength(); + const css::beans::PropertyValue* pSource = lSource.getConstArray(); + + m_aMap.reserve(c); + for (sal_Int32 i=0; i<c; ++i) + (*this)[pSource[i].Name] = pSource[i].Value; +} + +void SequenceAsHashMap::operator<<(const css::uno::Sequence< css::beans::NamedValue >& lSource) +{ + clear(); + + sal_Int32 c = lSource.getLength(); + const css::beans::NamedValue* pSource = lSource.getConstArray(); + + m_aMap.reserve(c); + for (sal_Int32 i=0; i<c; ++i) + (*this)[pSource[i].Name] = pSource[i].Value; +} + +void SequenceAsHashMap::operator>>(css::uno::Sequence< css::beans::PropertyValue >& lDestination) const +{ + sal_Int32 c = static_cast<sal_Int32>(size()); + lDestination.realloc(c); + css::beans::PropertyValue* pDestination = lDestination.getArray(); + + sal_Int32 i = 0; + for (const_iterator pThis = begin(); + pThis != end() ; + ++pThis ) + { + pDestination[i].Name = pThis->first.maString; + pDestination[i].Value = pThis->second; + ++i; + } +} + +void SequenceAsHashMap::operator>>(css::uno::Sequence< css::beans::NamedValue >& lDestination) const +{ + sal_Int32 c = static_cast<sal_Int32>(size()); + lDestination.realloc(c); + css::beans::NamedValue* pDestination = lDestination.getArray(); + + sal_Int32 i = 0; + for (const_iterator pThis = begin(); + pThis != end() ; + ++pThis ) + { + pDestination[i].Name = pThis->first.maString; + pDestination[i].Value = pThis->second; + ++i; + } +} + +css::uno::Any SequenceAsHashMap::getAsConstAny(bool bAsPropertyValueList) const +{ + css::uno::Any aDestination; + if (bAsPropertyValueList) + aDestination <<= getAsConstPropertyValueList(); + else + aDestination <<= getAsConstNamedValueList(); + return aDestination; +} + +css::uno::Sequence< css::beans::NamedValue > SequenceAsHashMap::getAsConstNamedValueList() const +{ + css::uno::Sequence< css::beans::NamedValue > lReturn; + (*this) >> lReturn; + return lReturn; +} + +css::uno::Sequence< css::beans::PropertyValue > SequenceAsHashMap::getAsConstPropertyValueList() const +{ + css::uno::Sequence< css::beans::PropertyValue > lReturn; + (*this) >> lReturn; + return lReturn; +} + +bool SequenceAsHashMap::match(const SequenceAsHashMap& rCheck) const +{ + for (auto const& elem : rCheck) + { + const OUString& sCheckName = elem.first.maString; + const css::uno::Any& aCheckValue = elem.second; + const_iterator pFound = find(sCheckName); + + if (pFound == end()) + return false; + + const css::uno::Any& aFoundValue = pFound->second; + if (aFoundValue != aCheckValue) + return false; + } + + return true; +} + +void SequenceAsHashMap::update(const SequenceAsHashMap& rUpdate) +{ + m_aMap.reserve(std::max(size(), rUpdate.size())); + for (auto const& elem : rUpdate.m_aMap) + { + m_aMap[elem.first] = elem.second; + } +} + +std::vector<css::beans::PropertyValue> JsonToPropertyValues(const OString& rJson) +{ + std::vector<beans::PropertyValue> aArguments; + boost::property_tree::ptree aTree, aNodeNull, aNodeValue; + std::stringstream aStream(rJson.getStr()); + boost::property_tree::read_json(aStream, aTree); + + for (const auto& rPair : aTree) + { + const std::string& rType = rPair.second.get<std::string>("type", ""); + const std::string& rValue = rPair.second.get<std::string>("value", ""); + + beans::PropertyValue aValue; + aValue.Name = OUString::fromUtf8(rPair.first.c_str()); + if (rType == "string") + aValue.Value <<= OUString::fromUtf8(rValue.c_str()); + else if (rType == "boolean") + aValue.Value <<= OString(rValue.c_str()).toBoolean(); + else if (rType == "float") + aValue.Value <<= OString(rValue.c_str()).toFloat(); + else if (rType == "long") + aValue.Value <<= o3tl::toInt32(rValue); + else if (rType == "short") + aValue.Value <<= sal_Int16(o3tl::toInt32(rValue)); + else if (rType == "unsigned short") + aValue.Value <<= sal_uInt16(o3tl::toUInt32(rValue)); + else if (rType == "int64") + aValue.Value <<= o3tl::toInt64(rValue); + else if (rType == "int32") + aValue.Value <<= o3tl::toInt32(rValue); + else if (rType == "int16") + aValue.Value <<= sal_Int16(o3tl::toInt32(rValue)); + else if (rType == "uint64") + aValue.Value <<= OString(rValue.c_str()).toUInt64(); + else if (rType == "uint32") + aValue.Value <<= o3tl::toUInt32(rValue); + else if (rType == "uint16") + aValue.Value <<= sal_uInt16(o3tl::toUInt32(rValue)); + else if (rType == "[]byte") + { + aNodeValue = rPair.second.get_child("value", aNodeNull); + if (aNodeValue != aNodeNull && aNodeValue.size() == 0) + { + uno::Sequence<sal_Int8> aSeqByte(reinterpret_cast<const sal_Int8*>(rValue.c_str()), + rValue.size()); + aValue.Value <<= aSeqByte; + } + } + else if (rType == "[]any") + { + aNodeValue = rPair.second.get_child("value", aNodeNull); + if (aNodeValue != aNodeNull && !aNodeValue.empty()) + { + uno::Sequence<uno::Any> aSeq(aNodeValue.size()); + std::transform(aNodeValue.begin(), aNodeValue.end(), aSeq.getArray(), + [](const auto& rSeqPair) { return jsonToUnoAny(rSeqPair.second); }); + aValue.Value <<= aSeq; + } + } + else + SAL_WARN("comphelper", "JsonToPropertyValues: unhandled type '" << rType << "'"); + aArguments.push_back(aValue); + } + return aArguments; +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/servicedecl.cxx b/comphelper/source/misc/servicedecl.cxx new file mode 100644 index 000000000..2b16539d1 --- /dev/null +++ b/comphelper/source/misc/servicedecl.cxx @@ -0,0 +1,161 @@ +/* -*- 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 <comphelper/servicedecl.hxx> +#include <o3tl/string_view.hxx> +#include <rtl/string.hxx> +#include <cppuhelper/implbase.hxx> +#include <comphelper/sequence.hxx> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <cassert> +#include <vector> + +using namespace com::sun::star; + +namespace comphelper::service_decl { + +const char cDelim = ';'; + +class ServiceDecl::Factory : + public cppu::WeakImplHelper<lang::XSingleComponentFactory, + lang::XServiceInfo> +{ +public: + explicit Factory( ServiceDecl const& rServiceDecl ) + : m_rServiceDecl(rServiceDecl) {} + // noncopyable + Factory(const Factory&) = delete; + const Factory& operator=(const Factory&) = delete; + + // XServiceInfo: + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( OUString const& name ) override; + virtual uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + // XSingleComponentFactory: + virtual uno::Reference<uno::XInterface> SAL_CALL createInstanceWithContext( + uno::Reference<uno::XComponentContext> const& xContext ) override; + virtual uno::Reference<uno::XInterface> SAL_CALL + createInstanceWithArgumentsAndContext( + uno::Sequence<uno::Any> const& args, + uno::Reference<uno::XComponentContext> const& xContext ) override; + +private: + virtual ~Factory() override; + + ServiceDecl const& m_rServiceDecl; +}; + +ServiceDecl::Factory::~Factory() +{ +} + +// XServiceInfo: +OUString ServiceDecl::Factory::getImplementationName() +{ + return m_rServiceDecl.getImplementationName(); +} + +sal_Bool ServiceDecl::Factory::supportsService( OUString const& name ) +{ + return m_rServiceDecl.supportsService(name); +} + +uno::Sequence<OUString> ServiceDecl::Factory::getSupportedServiceNames() +{ + return m_rServiceDecl.getSupportedServiceNames(); +} + +// XSingleComponentFactory: +uno::Reference<uno::XInterface> ServiceDecl::Factory::createInstanceWithContext( + uno::Reference<uno::XComponentContext> const& xContext ) +{ + return m_rServiceDecl.m_createFunc( + m_rServiceDecl, uno::Sequence<uno::Any>(), xContext ); +} + +uno::Reference<uno::XInterface> +ServiceDecl::Factory::createInstanceWithArgumentsAndContext( + uno::Sequence<uno::Any > const& args, + uno::Reference<uno::XComponentContext> const& xContext ) +{ + return m_rServiceDecl.m_createFunc( + m_rServiceDecl, args, xContext ); +} + +void * ServiceDecl::getFactory( char const* pImplName ) const +{ + if (rtl_str_compare(m_pImplName, pImplName) == 0) { + lang::XSingleComponentFactory * const pFac( new Factory(*this) ); + pFac->acquire(); + return pFac; + } + return nullptr; +} + +uno::Sequence<OUString> ServiceDecl::getSupportedServiceNames() const +{ + std::vector<OUString> vec; + + OString const str(m_pServiceNames); + sal_Int32 nIndex = 0; + do { + std::string_view const token( o3tl::getToken(str, 0, cDelim, nIndex ) ); + vec.emplace_back( token.data(), token.size(), + RTL_TEXTENCODING_ASCII_US ); + } + while (nIndex >= 0); + + return comphelper::containerToSequence(vec); +} + +bool ServiceDecl::supportsService( std::u16string_view name ) const +{ + OString const str(m_pServiceNames); + sal_Int32 nIndex = 0; + do { + std::string_view const token( o3tl::getToken(str, 0, cDelim, nIndex ) ); + if (o3tl::equalsAscii(name, token)) + return true; + } + while (nIndex >= 0); + return false; +} + +OUString ServiceDecl::getImplementationName() const +{ + return OUString::createFromAscii(m_pImplName); +} + +void* component_getFactoryHelper( const char* pImplName, + std::initializer_list<ServiceDecl const *> args ) +{ + for (auto const i: args) { + assert(i != nullptr); + void * fac = i->getFactory(pImplName); + if (fac != nullptr) { + return fac; + } + } + return nullptr; +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/sharedmutex.cxx b/comphelper/source/misc/sharedmutex.cxx new file mode 100644 index 000000000..58ae35df5 --- /dev/null +++ b/comphelper/source/misc/sharedmutex.cxx @@ -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 . + */ + + +#include <comphelper/sharedmutex.hxx> +#include <comphelper/refcountedmutex.hxx> + + +namespace comphelper +{ + + SharedMutex::SharedMutex() + :m_pMutexImpl( std::make_shared<::osl::Mutex >()) + { + } + + + RefCountedMutex::~RefCountedMutex() + { + } + + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/simplefileaccessinteraction.cxx b/comphelper/source/misc/simplefileaccessinteraction.cxx new file mode 100644 index 000000000..8cd77af7d --- /dev/null +++ b/comphelper/source/misc/simplefileaccessinteraction.cxx @@ -0,0 +1,120 @@ +/* -*- 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/. + */ + +#include <comphelper/simplefileaccessinteraction.hxx> +#include <com/sun/star/task/XInteractionAbort.hpp> +#include <com/sun/star/task/XInteractionApprove.hpp> +#include <com/sun/star/ucb/AuthenticationRequest.hpp> +#include <com/sun/star/ucb/CertificateValidationRequest.hpp> +#include <com/sun/star/ucb/InteractiveIOException.hpp> +#include <com/sun/star/ucb/InteractiveNetworkException.hpp> +#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp> + +namespace comphelper +{ +/// Will handle com::sun::star::ucb::InteractiveIOException and derived classes +const sal_Int32 HANDLE_INTERACTIVEIOEXCEPTION = 0; +/// Will handle com::sun::star::ucb::UnsupportedDataSinkException +const sal_Int32 HANDLE_UNSUPPORTEDDATASINKEXCEPTION = 1; +/// Will handle com::sun::star::ucb::InteractiveNetworkException +const sal_Int32 HANDLE_INTERACTIVENETWORKEXCEPTION = 2; +/// Will handle com::sun::star::ucb::CertificateValidationRequest +const sal_Int32 HANDLE_CERTIFICATEREQUEST = 3; +/// Will handle com::sun::star::ucb::AuthenticationRequest +const sal_Int32 HANDLE_AUTHENTICATIONREQUEST = 4; + +SimpleFileAccessInteraction::SimpleFileAccessInteraction( + const css::uno::Reference<css::task::XInteractionHandler>& xHandler) +{ + std::vector<::ucbhelper::InterceptedInteraction::InterceptedRequest> lInterceptions{ + { //intercept standard IO error exception (local file and WebDAV) + css::uno::Any(css::ucb::InteractiveIOException()), + cppu::UnoType<css::task::XInteractionAbort>::get(), HANDLE_INTERACTIVEIOEXCEPTION }, + { //intercept internal error + css::uno::Any(css::ucb::UnsupportedDataSinkException()), + cppu::UnoType<css::task::XInteractionAbort>::get(), HANDLE_UNSUPPORTEDDATASINKEXCEPTION }, + { + //intercept network error exception (WebDAV ucp provider) + css::uno::Any(css::ucb::InteractiveNetworkException()), + cppu::UnoType<css::task::XInteractionAbort>::get(), + HANDLE_INTERACTIVENETWORKEXCEPTION, + }, + { //intercept certificate validation request (WebDAV ucp provider) + css::uno::Any(css::ucb::CertificateValidationRequest()), + cppu::UnoType<css::task::XInteractionAbort>::get(), HANDLE_CERTIFICATEREQUEST }, + { //intercept authentication request (WebDAV ucp provider) + css::uno::Any(css::ucb::AuthenticationRequest()), + cppu::UnoType<css::task::XInteractionApprove>::get(), HANDLE_AUTHENTICATIONREQUEST } + }; + + setInterceptedHandler(xHandler); + setInterceptions(std::move(lInterceptions)); +} + +SimpleFileAccessInteraction::~SimpleFileAccessInteraction() {} + +ucbhelper::InterceptedInteraction::EInterceptionState SimpleFileAccessInteraction::intercepted( + const ::ucbhelper::InterceptedInteraction::InterceptedRequest& aRequest, + const css::uno::Reference<css::task::XInteractionRequest>& xRequest) +{ + bool bAbort = false; + switch (aRequest.Handle) + { + case HANDLE_UNSUPPORTEDDATASINKEXCEPTION: + case HANDLE_INTERACTIVENETWORKEXCEPTION: + case HANDLE_INTERACTIVEIOEXCEPTION: + { + bAbort = true; + } + break; + + case HANDLE_CERTIFICATEREQUEST: + { + // use default internal handler. + if (m_xInterceptedHandler.is()) + { + m_xInterceptedHandler->handle(xRequest); + return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED; + } + else + bAbort = true; + break; + } + + case HANDLE_AUTHENTICATIONREQUEST: + { + // use default internal handler. + if (m_xInterceptedHandler.is()) + { + m_xInterceptedHandler->handle(xRequest); + return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED; + } + else //simply abort + bAbort = true; + } + break; + } + + // handle interaction by ourself, by not doing + // any selection... + if (bAbort) + { + css::uno::Reference<css::task::XInteractionContinuation> xAbort + = ::ucbhelper::InterceptedInteraction::extractContinuation( + xRequest->getContinuations(), cppu::UnoType<css::task::XInteractionAbort>::get()); + if (!xAbort.is()) + return ::ucbhelper::InterceptedInteraction::E_NO_CONTINUATION_FOUND; + return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED; + } + + return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/solarmutex.cxx b/comphelper/source/misc/solarmutex.cxx new file mode 100644 index 000000000..5d1052327 --- /dev/null +++ b/comphelper/source/misc/solarmutex.cxx @@ -0,0 +1,103 @@ +/* -*- 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 <sal/config.h> + +#include <comphelper/solarmutex.hxx> +#include <osl/thread.hxx> + +#include <assert.h> +#include <cstdlib> + +namespace comphelper { + +namespace { + SolarMutex* g_pSolarMutex = nullptr; +} + +SolarMutex *SolarMutex::get() +{ + return g_pSolarMutex; +} + +SolarMutex::SolarMutex() + : m_nCount( 0 ) + , m_nThreadId( 0 ) + , m_aBeforeReleaseHandler( nullptr ) +{ + assert(!g_pSolarMutex); + g_pSolarMutex = this; +} + +SolarMutex::~SolarMutex() +{ + g_pSolarMutex = nullptr; +} + +void SolarMutex::doAcquire( const sal_uInt32 nLockCount ) +{ + for ( sal_uInt32 n = nLockCount; n ; --n ) + m_aMutex.acquire(); + m_nThreadId = osl::Thread::getCurrentIdentifier(); + m_nCount += nLockCount; +} + +sal_uInt32 SolarMutex::doRelease( bool bUnlockAll ) +{ + if ( !IsCurrentThread() ) + std::abort(); + if ( m_nCount == 0 ) + std::abort(); + + const sal_uInt32 nCount = bUnlockAll ? m_nCount : 1; + m_nCount -= nCount; + + if ( 0 == m_nCount ) + { + if ( m_aBeforeReleaseHandler ) + m_aBeforeReleaseHandler(); + m_nThreadId = 0; + } + + for ( sal_uInt32 n = nCount ; n ; --n ) + m_aMutex.release(); + + return nCount; +} + +bool SolarMutex::IsCurrentThread() const +{ + return m_nThreadId == osl::Thread::getCurrentIdentifier(); +} + +bool SolarMutex::tryToAcquire() +{ + if ( m_aMutex.tryToAcquire() ) + { + m_nThreadId = osl::Thread::getCurrentIdentifier(); + m_nCount++; + return true; + } + else + return false; +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/stillreadwriteinteraction.cxx b/comphelper/source/misc/stillreadwriteinteraction.cxx new file mode 100644 index 000000000..88bc25bc4 --- /dev/null +++ b/comphelper/source/misc/stillreadwriteinteraction.cxx @@ -0,0 +1,156 @@ +/* -*- 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 <comphelper/stillreadwriteinteraction.hxx> + +#include <com/sun/star/ucb/InteractiveIOException.hpp> + +#include <com/sun/star/task/XInteractionAbort.hpp> + +#include <com/sun/star/task/XInteractionApprove.hpp> + +#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp> + +#include <com/sun/star/ucb/AuthenticationRequest.hpp> + +#include <com/sun/star/ucb/CertificateValidationRequest.hpp> +#include <utility> + +namespace comphelper{ + +StillReadWriteInteraction::StillReadWriteInteraction(const css::uno::Reference< css::task::XInteractionHandler >& xHandler, + css::uno::Reference< css::task::XInteractionHandler > xAuxiliaryHandler) + : m_bUsed (false) + , m_bHandledByMySelf (false) + , m_xAuxiliaryHandler(std::move(xAuxiliaryHandler)) +{ + std::vector< ::ucbhelper::InterceptedInteraction::InterceptedRequest > lInterceptions; + lInterceptions.reserve(4); + ::ucbhelper::InterceptedInteraction::InterceptedRequest aInterceptedRequest; + + aInterceptedRequest.Handle = HANDLE_INTERACTIVEIOEXCEPTION; + aInterceptedRequest.Request <<= css::ucb::InteractiveIOException(); + aInterceptedRequest.Continuation = cppu::UnoType<css::task::XInteractionAbort>::get(); + lInterceptions.push_back(aInterceptedRequest); + + aInterceptedRequest.Handle = HANDLE_UNSUPPORTEDDATASINKEXCEPTION; + aInterceptedRequest.Request <<= css::ucb::UnsupportedDataSinkException(); + aInterceptedRequest.Continuation = cppu::UnoType<css::task::XInteractionAbort>::get(); + lInterceptions.push_back(aInterceptedRequest); + + aInterceptedRequest.Handle = HANDLE_AUTHENTICATIONREQUESTEXCEPTION; + aInterceptedRequest.Request <<= css::ucb::AuthenticationRequest(); + aInterceptedRequest.Continuation = cppu::UnoType<css::task::XInteractionApprove>::get(); + lInterceptions.push_back(aInterceptedRequest); + + aInterceptedRequest.Handle = HANDLE_CERTIFICATEVALIDATIONREQUESTEXCEPTION; + aInterceptedRequest.Request <<= css::ucb::CertificateValidationRequest(); + aInterceptedRequest.Continuation = cppu::UnoType<css::task::XInteractionApprove>::get(); + lInterceptions.push_back(aInterceptedRequest); + + setInterceptedHandler(xHandler); + setInterceptions(std::move(lInterceptions)); +} + +void StillReadWriteInteraction::resetInterceptions() +{ + setInterceptions(std::vector< ::ucbhelper::InterceptedInteraction::InterceptedRequest >()); +} + +void StillReadWriteInteraction::resetErrorStates() +{ + m_bUsed = false; + m_bHandledByMySelf = false; +} + + +ucbhelper::InterceptedInteraction::EInterceptionState StillReadWriteInteraction::intercepted(const ::ucbhelper::InterceptedInteraction::InterceptedRequest& aRequest, + const css::uno::Reference< css::task::XInteractionRequest >& xRequest) +{ + // we are used! + m_bUsed = true; + + // check if it's a real interception - might some parameters are not the right ones ... + bool bAbort = false; + switch(aRequest.Handle) + { + case HANDLE_INTERACTIVEIOEXCEPTION: + { + css::ucb::InteractiveIOException exIO; + xRequest->getRequest() >>= exIO; + bAbort = ( + (exIO.Code == css::ucb::IOErrorCode_ACCESS_DENIED ) + || (exIO.Code == css::ucb::IOErrorCode_LOCKING_VIOLATION ) + || (exIO.Code == css::ucb::IOErrorCode_NOT_EXISTING ) + // At least on Linux, a request to open some fuse-mounted file O_RDWR may fail with + // EOPNOTSUPP (mapped to osl_File_E_NOSYS to IOErrorCode_NOT_SUPPORTED) when opening + // it O_RDONLY would succeed: + || (exIO.Code == css::ucb::IOErrorCode_NOT_SUPPORTED ) +#ifdef MACOSX + // this is a workaround for MAC, on this platform if the file is locked + // the returned error code looks to be wrong + || (exIO.Code == css::ucb::IOErrorCode_GENERAL ) +#endif + ); + } + break; + + case HANDLE_UNSUPPORTEDDATASINKEXCEPTION: + { + bAbort = true; + } + break; + case HANDLE_CERTIFICATEVALIDATIONREQUESTEXCEPTION: + case HANDLE_AUTHENTICATIONREQUESTEXCEPTION: + { +//use internal auxiliary handler and return + if (m_xAuxiliaryHandler.is()) + { + m_xAuxiliaryHandler->handle(xRequest); + return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED; + } + else //simply abort + bAbort = true; + } + break; + } + + // handle interaction by ourself + if (bAbort) + { + m_bHandledByMySelf = true; + css::uno::Reference< css::task::XInteractionContinuation > xAbort = ::ucbhelper::InterceptedInteraction::extractContinuation( + xRequest->getContinuations(), + cppu::UnoType<css::task::XInteractionAbort>::get() ); + if (!xAbort.is()) + return ::ucbhelper::InterceptedInteraction::E_NO_CONTINUATION_FOUND; + xAbort->select(); + return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED; + } + + // Otherwise use internal handler. + if (m_xInterceptedHandler.is()) + { + m_xInterceptedHandler->handle(xRequest); + } + return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/storagehelper.cxx b/comphelper/source/misc/storagehelper.cxx new file mode 100644 index 000000000..5f8527bcf --- /dev/null +++ b/comphelper/source/misc/storagehelper.cxx @@ -0,0 +1,713 @@ +/* -*- 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_gpgme.h> + +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XEncryptionProtectedStorage.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/embed/StorageFactory.hpp> +#include <com/sun/star/embed/FileSystemStorageFactory.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/IllegalTypeException.hpp> +#include <com/sun/star/xml/crypto/NSSInitializer.hpp> +#include <com/sun/star/xml/crypto/XDigestContext.hpp> +#include <com/sun/star/xml/crypto/DigestID.hpp> +#include <com/sun/star/security/DocumentDigitalSignatures.hpp> +#include <com/sun/star/security/XCertificate.hpp> + +#include <vector> + +#include <rtl/digest.h> +#include <rtl/random.h> +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include <ucbhelper/content.hxx> + +#include <comphelper/bytereader.hxx> +#include <comphelper/fileformat.h> +#include <comphelper/hash.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/documentconstants.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <o3tl/string_view.hxx> + +#if HAVE_FEATURE_GPGME +# include <context.h> +# include <encryptionresult.h> +# include <key.h> +# include <data.h> +#endif + +using namespace ::com::sun::star; + +namespace comphelper { + + +uno::Reference< lang::XSingleServiceFactory > OStorageHelper::GetStorageFactory( + const uno::Reference< uno::XComponentContext >& rxContext ) +{ + uno::Reference< uno::XComponentContext> xContext = rxContext.is() ? rxContext : ::comphelper::getProcessComponentContext(); + + return embed::StorageFactory::create( xContext ); +} + + +uno::Reference< lang::XSingleServiceFactory > OStorageHelper::GetFileSystemStorageFactory( + const uno::Reference< uno::XComponentContext >& rxContext ) +{ + return embed::FileSystemStorageFactory::create(rxContext); +} + + +uno::Reference< embed::XStorage > OStorageHelper::GetTemporaryStorage( + const uno::Reference< uno::XComponentContext >& rxContext ) +{ + uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstance(), + uno::UNO_QUERY_THROW ); + return xTempStorage; +} + + +uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromURL( + const OUString& aURL, + sal_Int32 nStorageMode, + const uno::Reference< uno::XComponentContext >& rxContext ) +{ + uno::Sequence< uno::Any > aArgs{ uno::Any(aURL), uno::Any(nStorageMode) }; + uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ), + uno::UNO_QUERY_THROW ); + return xTempStorage; +} + + +uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromURL2( + const OUString& aURL, + sal_Int32 nStorageMode, + const uno::Reference< uno::XComponentContext >& rxContext ) +{ + uno::Sequence< uno::Any > aArgs{ uno::Any(aURL), uno::Any(nStorageMode) }; + + uno::Reference< lang::XSingleServiceFactory > xFact; + css::uno::Any anyEx; + try { + ::ucbhelper::Content aCntnt( aURL, + uno::Reference< css::ucb::XCommandEnvironment > (), + getProcessComponentContext() ); + if (aCntnt.isDocument()) { + xFact = GetStorageFactory( rxContext ); + } else { + xFact = GetFileSystemStorageFactory( rxContext ); + } + } catch (uno::Exception &) + { + anyEx = cppu::getCaughtException(); + } + + if (!xFact.is()) + { + if (anyEx.hasValue()) + throw css::lang::WrappedTargetRuntimeException( "", nullptr, anyEx ); + else + throw uno::RuntimeException(); + } + + uno::Reference< embed::XStorage > xTempStorage( + xFact->createInstanceWithArguments( aArgs ), uno::UNO_QUERY_THROW ); + return xTempStorage; +} + + +uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromInputStream( + const uno::Reference < io::XInputStream >& xStream, + const uno::Reference< uno::XComponentContext >& rxContext ) +{ + uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), uno::Any(embed::ElementModes::READ) }; + uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ), + uno::UNO_QUERY_THROW ); + return xTempStorage; +} + + +uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromStream( + const uno::Reference < io::XStream >& xStream, + sal_Int32 nStorageMode, + const uno::Reference< uno::XComponentContext >& rxContext ) +{ + uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), uno::Any(nStorageMode) }; + uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ), + uno::UNO_QUERY_THROW ); + return xTempStorage; +} + + +void OStorageHelper::CopyInputToOutput( + const uno::Reference< io::XInputStream >& xInput, + const uno::Reference< io::XOutputStream >& xOutput ) +{ + static const sal_Int32 nConstBufferSize = 32000; + + uno::Reference< css::lang::XUnoTunnel > xInputTunnel( xInput, uno::UNO_QUERY ); + comphelper::ByteReader* pByteReader = nullptr; + comphelper::ByteWriter* pByteWriter = nullptr; + if (xInputTunnel) + pByteReader = reinterpret_cast< comphelper::ByteReader* >( xInputTunnel->getSomething( comphelper::ByteReader::getUnoTunnelId() ) ); + if (pByteReader) + { + uno::Reference< css::lang::XUnoTunnel > xOutputTunnel( xOutput, uno::UNO_QUERY ); + if (xOutputTunnel) + pByteWriter = reinterpret_cast< comphelper::ByteWriter* >( xOutputTunnel->getSomething( comphelper::ByteWriter::getUnoTunnelId() ) ); + } + + if (pByteWriter) + { + sal_Int32 nRead; + sal_Int8 aTempBuf[ nConstBufferSize ]; + do + { + nRead = pByteReader->readSomeBytes ( aTempBuf, nConstBufferSize ); + pByteWriter->writeSomeBytes ( aTempBuf, nRead ); + } + while ( nRead == nConstBufferSize ); + } + else + { + sal_Int32 nRead; + uno::Sequence < sal_Int8 > aSequence ( nConstBufferSize ); + + do + { + nRead = xInput->readBytes ( aSequence, nConstBufferSize ); + if ( nRead < nConstBufferSize ) + { + uno::Sequence < sal_Int8 > aTempBuf ( aSequence.getConstArray(), nRead ); + xOutput->writeBytes ( aTempBuf ); + } + else + xOutput->writeBytes ( aSequence ); + } + while ( nRead == nConstBufferSize ); + } +} + + +uno::Reference< io::XInputStream > OStorageHelper::GetInputStreamFromURL( + const OUString& aURL, + const uno::Reference< uno::XComponentContext >& context ) +{ + uno::Reference< io::XInputStream > xInputStream = ucb::SimpleFileAccess::create(context)->openFileRead( aURL ); + if ( !xInputStream.is() ) + throw uno::RuntimeException(); + + return xInputStream; +} + + +void OStorageHelper::SetCommonStorageEncryptionData( + const uno::Reference< embed::XStorage >& xStorage, + const uno::Sequence< beans::NamedValue >& aEncryptionData ) +{ + uno::Reference< embed::XEncryptionProtectedStorage > xEncrSet( xStorage, uno::UNO_QUERY ); + if ( !xEncrSet.is() ) + throw io::IOException("no XEncryptionProtectedStorage"); // TODO + + if ( aEncryptionData.getLength() == 2 && + aEncryptionData[0].Name == "GpgInfos" && + aEncryptionData[1].Name == "EncryptionKey" ) + { + xEncrSet->setGpgProperties( + aEncryptionData[0].Value.get< uno::Sequence< uno::Sequence< beans::NamedValue > > >() ); + xEncrSet->setEncryptionData( + aEncryptionData[1].Value.get< uno::Sequence< beans::NamedValue > >() ); + } + else + xEncrSet->setEncryptionData( aEncryptionData ); +} + + +sal_Int32 OStorageHelper::GetXStorageFormat( + const uno::Reference< embed::XStorage >& xStorage ) +{ + uno::Reference< beans::XPropertySet > xStorProps( xStorage, uno::UNO_QUERY_THROW ); + + OUString aMediaType; + xStorProps->getPropertyValue("MediaType") >>= aMediaType; + + sal_Int32 nResult = 0; + + // TODO/LATER: the filter configuration could be used to detect it later, or better a special service + if ( + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_WRITER_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_WRITER_WEB_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_WRITER_GLOBAL_ASCII) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_DRAW_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_IMPRESS_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_CALC_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_CHART_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_MATH_ASCII ) + ) + { + nResult = SOFFICE_FILEFORMAT_60; + } + else if ( + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_WEB_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_ASCII) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_CHART_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_REPORT_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_REPORT_CHART_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_TEMPLATE_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_TEMPLATE_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_TEMPLATE_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE_ASCII) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_TEMPLATE_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_CHART_TEMPLATE_ASCII ) || + aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_TEMPLATE_ASCII ) + ) + { + nResult = SOFFICE_FILEFORMAT_8; + } + else + { + // the mediatype is not known + OUString aMsg = __func__ + + OUString::Concat(u":") + + OUString::number(__LINE__) + + ": unknown media type '" + + aMediaType + + "'"; + throw beans::IllegalTypeException(aMsg); + } + + return nResult; +} + + +uno::Reference< embed::XStorage > OStorageHelper::GetStorageOfFormatFromURL( + const OUString& aFormat, + const OUString& aURL, + sal_Int32 nStorageMode, + const uno::Reference< uno::XComponentContext >& rxContext ) +{ + uno::Sequence< beans::PropertyValue > aProps{ comphelper::makePropertyValue("StorageFormat", + aFormat) }; + + uno::Sequence< uno::Any > aArgs{ uno::Any(aURL), uno::Any(nStorageMode), uno::Any(aProps) }; + uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ), + uno::UNO_QUERY_THROW ); + return xTempStorage; +} + + +uno::Reference< embed::XStorage > OStorageHelper::GetStorageOfFormatFromInputStream( + const OUString& aFormat, + const uno::Reference < io::XInputStream >& xStream, + const uno::Reference< uno::XComponentContext >& rxContext, + bool bRepairStorage ) +{ + uno::Sequence< beans::PropertyValue > aProps( bRepairStorage ? 2 : 1 ); + auto pProps = aProps.getArray(); + pProps[0].Name = "StorageFormat"; + pProps[0].Value <<= aFormat; + if ( bRepairStorage ) + { + pProps[1].Name = "RepairPackage"; + pProps[1].Value <<= bRepairStorage; + } + + uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), uno::Any(embed::ElementModes::READ), uno::Any(aProps) }; + uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ), + uno::UNO_QUERY_THROW ); + return xTempStorage; +} + + +uno::Reference< embed::XStorage > OStorageHelper::GetStorageOfFormatFromStream( + const OUString& aFormat, + const uno::Reference < io::XStream >& xStream, + sal_Int32 nStorageMode, + const uno::Reference< uno::XComponentContext >& rxContext, + bool bRepairStorage ) +{ + uno::Sequence< beans::PropertyValue > aProps( bRepairStorage ? 2 : 1 ); + auto pProps = aProps.getArray(); + pProps[0].Name = "StorageFormat"; + pProps[0].Value <<= aFormat; + if ( bRepairStorage ) + { + pProps[1].Name = "RepairPackage"; + pProps[1].Value <<= bRepairStorage; + } + + uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), uno::Any(nStorageMode), uno::Any(aProps) }; + uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ), + uno::UNO_QUERY_THROW ); + return xTempStorage; +} + + +uno::Sequence< beans::NamedValue > OStorageHelper::CreatePackageEncryptionData( std::u16string_view aPassword ) +{ + // TODO/LATER: Should not the method be part of DocPasswordHelper? + uno::Sequence< beans::NamedValue > aEncryptionData; + if ( !aPassword.empty() ) + { + sal_Int32 nSha1Ind = 0; + // generate SHA256 start key + try + { + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + + uno::Reference< css::xml::crypto::XNSSInitializer > xDigestContextSupplier = css::xml::crypto::NSSInitializer::create(xContext); + uno::Reference< css::xml::crypto::XDigestContext > xDigestContext( xDigestContextSupplier->getDigestContext( css::xml::crypto::DigestID::SHA256, uno::Sequence< beans::NamedValue >() ), uno::UNO_SET_THROW ); + + OString aUTF8Password( OUStringToOString( aPassword, RTL_TEXTENCODING_UTF8 ) ); + xDigestContext->updateDigest( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( aUTF8Password.getStr() ), aUTF8Password.getLength() ) ); + uno::Sequence< sal_Int8 > aDigest = xDigestContext->finalizeDigestAndDispose(); + + ++nSha1Ind; + aEncryptionData = { { PACKAGE_ENCRYPTIONDATA_SHA256UTF8, uno::Any(aDigest) } }; + } + catch ( uno::Exception& ) + { + OSL_ENSURE( false, "Can not create SHA256 digest!" ); + } + + // MS_1252 encoding was used for SO60 document format password encoding, + // this encoding supports only a minor subset of nonascii characters, + // but for compatibility reasons it has to be used for old document formats + aEncryptionData.realloc( nSha1Ind + 3 ); + auto pEncryptionData = aEncryptionData.getArray(); + // these are StarOffice not-quite-SHA1 + pEncryptionData[nSha1Ind].Name = PACKAGE_ENCRYPTIONDATA_SHA1UTF8; + pEncryptionData[nSha1Ind + 1].Name = PACKAGE_ENCRYPTIONDATA_SHA1MS1252; + + rtl_TextEncoding const pEncoding[2] = { RTL_TEXTENCODING_UTF8, RTL_TEXTENCODING_MS_1252 }; + + for ( sal_Int32 nInd = 0; nInd < 2; nInd++ ) + { + OString aByteStrPass = OUStringToOString( aPassword, pEncoding[nInd] ); + + sal_uInt8 pBuffer[RTL_DIGEST_LENGTH_SHA1]; + rtlDigestError nError = rtl_digest_SHA1( aByteStrPass.getStr(), + aByteStrPass.getLength(), + pBuffer, + RTL_DIGEST_LENGTH_SHA1 ); + + if ( nError != rtl_Digest_E_None ) + { + aEncryptionData.realloc( nSha1Ind ); + return aEncryptionData; + } + + // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence + pEncryptionData[nSha1Ind+nInd].Value <<= uno::Sequence< sal_Int8 >( reinterpret_cast<sal_Int8*>(pBuffer), RTL_DIGEST_LENGTH_SHA1 ); + } + + // actual SHA1 + pEncryptionData[nSha1Ind + 2].Name = PACKAGE_ENCRYPTIONDATA_SHA1CORRECT; + OString aByteStrPass = OUStringToOString(aPassword, RTL_TEXTENCODING_UTF8); + std::vector<unsigned char> const sha1(::comphelper::Hash::calculateHash( + reinterpret_cast<unsigned char const*>(aByteStrPass.getStr()), aByteStrPass.getLength(), + ::comphelper::HashType::SHA1)); + pEncryptionData[nSha1Ind + 2].Value <<= uno::Sequence<sal_Int8>( + reinterpret_cast<sal_Int8 const*>(sha1.data()), sha1.size()); + } + + return aEncryptionData; +} + +uno::Sequence< beans::NamedValue > OStorageHelper::CreateGpgPackageEncryptionData() +{ +#if HAVE_FEATURE_GPGME + // generate session key + // -------------------- + + rtlRandomPool aRandomPool = rtl_random_createPool(); + + // get 32 random chars out of it + uno::Sequence < sal_Int8 > aVector(32); + rtl_random_getBytes( aRandomPool, aVector.getArray(), aVector.getLength() ); + + rtl_random_destroyPool(aRandomPool); + + std::vector< uno::Sequence< beans::NamedValue > > aGpgEncryptions; + + uno::Reference< security::XDocumentDigitalSignatures > xSigner( + // here none of the version-dependent methods are called + security::DocumentDigitalSignatures::createDefault( + comphelper::getProcessComponentContext())); + + // fire up certificate chooser dialog - user can multi-select! + const uno::Sequence< uno::Reference< security::XCertificate > > xSignCertificates= + xSigner->chooseEncryptionCertificate(); + + if (!xSignCertificates.hasElements()) + return uno::Sequence< beans::NamedValue >(); // user cancelled + + // generate one encrypted key entry for each recipient + // --------------------------------------------------- + + std::unique_ptr<GpgME::Context> ctx; + GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP); + if (err) + throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); + + ctx.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP) ); + if (ctx == nullptr) + throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); + ctx->setArmor(false); + + for (const auto & cert : xSignCertificates) + { + uno::Sequence < sal_Int8 > aKeyID; + if (cert.is()) + aKeyID = cert->getSHA1Thumbprint(); + + std::vector<GpgME::Key> keys + { + ctx->key( + reinterpret_cast<const char*>(aKeyID.getConstArray()), + err, false) + }; + + // ctx is setup now, let's encrypt the lot! + GpgME::Data plain( + reinterpret_cast<const char*>(aVector.getConstArray()), + size_t(aVector.getLength()), false); + GpgME::Data cipher; + + GpgME::EncryptionResult crypt_res = ctx->encrypt( + keys, plain, + cipher, GpgME::Context::NoCompress); + + off_t result = cipher.seek(0,SEEK_SET); + (void) result; + assert(result == 0); + int len=0, curr=0; char buf; + while( (curr=cipher.read(&buf, 1)) ) + len += curr; + + if(crypt_res.error() || !len) + throw lang::IllegalArgumentException( + "Not a suitable key, or failed to encrypt.", + css::uno::Reference<css::uno::XInterface>(), -1); + + uno::Sequence < sal_Int8 > aCipherValue(len); + result = cipher.seek(0,SEEK_SET); + assert(result == 0); + if( cipher.read(aCipherValue.getArray(), len) != len ) + throw uno::RuntimeException("The GpgME library failed to read the encrypted value."); + + SAL_INFO("comphelper.crypto", "Generated gpg crypto of length: " << len); + + uno::Sequence< beans::NamedValue > aGpgEncryptionEntry{ + { "KeyId", uno::Any(aKeyID) }, + { "KeyPacket", uno::Any(aKeyID) }, + { "CipherValue", uno::Any(aCipherValue) } + }; + + aGpgEncryptions.push_back(aGpgEncryptionEntry); + } + + uno::Sequence<beans::NamedValue> aEncryptionData + = { { PACKAGE_ENCRYPTIONDATA_SHA256UTF8, uno::Any(aVector) } }; + + uno::Sequence<beans::NamedValue> aContainer + = { { "GpgInfos", uno::Any(comphelper::containerToSequence(aGpgEncryptions)) }, + { "EncryptionKey", uno::Any(aEncryptionData) } }; + + return aContainer; +#else + return uno::Sequence< beans::NamedValue >(); +#endif +} + +bool OStorageHelper::IsValidZipEntryFileName( std::u16string_view aName, bool bSlashAllowed ) +{ + return IsValidZipEntryFileName( aName.data(), aName.size(), bSlashAllowed ); +} + + +bool OStorageHelper::IsValidZipEntryFileName( + const sal_Unicode *pChar, sal_Int32 nLength, bool bSlashAllowed ) +{ + for ( sal_Int32 i = 0; i < nLength; i++ ) + { + switch ( pChar[i] ) + { + case '\\': + case '?': + case '<': + case '>': + case '\"': + case '|': + case ':': + return false; + case '/': + if ( !bSlashAllowed ) + return false; + break; + default: + if ( pChar[i] < 32 || (pChar[i] >= 0xD800 && pChar[i] <= 0xDFFF) ) + return false; + } + } + return true; +} + + +bool OStorageHelper::PathHasSegment( std::u16string_view aPath, std::u16string_view aSegment ) +{ + bool bResult = false; + const size_t nPathLen = aPath.size(); + const size_t nSegLen = aSegment.size(); + + if ( !aSegment.empty() && nPathLen >= nSegLen ) + { + OUString aEndSegment = OUString::Concat("/") + aSegment; + OUString aInternalSegment = aEndSegment + "/"; + + if ( aPath.find( aInternalSegment ) != std::u16string_view::npos ) + bResult = true; + + if ( !bResult && o3tl::starts_with(aPath, aSegment ) ) + { + if ( nPathLen == nSegLen || aPath[nSegLen] == '/' ) + bResult = true; + } + + if ( !bResult && nPathLen > nSegLen && aPath.substr( nPathLen - nSegLen - 1, nSegLen + 1 ) == aEndSegment ) + bResult = true; + } + + return bResult; +} + +class LifecycleProxy::Impl + : public std::vector< uno::Reference< embed::XStorage > > {}; +LifecycleProxy::LifecycleProxy() + : m_xBadness( new Impl ) { } +LifecycleProxy::~LifecycleProxy() { } + +void LifecycleProxy::commitStorages() +{ + std::for_each(m_xBadness->rbegin(), m_xBadness->rend(), // reverse order (outwards) + [](Impl::reference rxItem) { + uno::Reference<embed::XTransactedObject> const xTransaction(rxItem, uno::UNO_QUERY); + if (xTransaction.is()) + { + xTransaction->commit(); + } + }); +} + +static void splitPath( std::vector<OUString> &rElems, std::u16string_view rPath ) +{ + for (sal_Int32 i = 0; i >= 0;) + rElems.push_back( OUString(o3tl::getToken(rPath, 0, '/', i )) ); +} + +static uno::Reference< embed::XStorage > LookupStorageAtPath( + const uno::Reference< embed::XStorage > &xParentStorage, + std::vector<OUString> &rElems, sal_uInt32 nOpenMode, + LifecycleProxy const &rNastiness ) +{ + uno::Reference< embed::XStorage > xStorage( xParentStorage ); + rNastiness.m_xBadness->push_back( xStorage ); + for( size_t i = 0; i < rElems.size() && xStorage.is(); i++ ) + { + xStorage = xStorage->openStorageElement( rElems[i], nOpenMode ); + rNastiness.m_xBadness->push_back( xStorage ); + } + return xStorage; +} + +uno::Reference< embed::XStorage > OStorageHelper::GetStorageAtPath( + const uno::Reference< embed::XStorage > &xStorage, + std::u16string_view rPath, sal_uInt32 nOpenMode, + LifecycleProxy const &rNastiness ) +{ + std::vector<OUString> aElems; + splitPath( aElems, rPath ); + return LookupStorageAtPath( xStorage, aElems, nOpenMode, rNastiness ); +} + +uno::Reference< io::XStream > OStorageHelper::GetStreamAtPath( + const uno::Reference< embed::XStorage > &xParentStorage, + std::u16string_view rPath, sal_uInt32 nOpenMode, + LifecycleProxy const &rNastiness ) +{ + std::vector<OUString> aElems; + splitPath( aElems, rPath ); + OUString aName( aElems.back() ); + aElems.pop_back(); + sal_uInt32 nStorageMode = nOpenMode & ~embed::ElementModes::TRUNCATE; + uno::Reference< embed::XStorage > xStorage( + LookupStorageAtPath( xParentStorage, aElems, nStorageMode, rNastiness ), + uno::UNO_SET_THROW ); + return xStorage->openStreamElement( aName, nOpenMode ); +} + +uno::Reference< io::XStream > OStorageHelper::GetStreamAtPackageURL( + uno::Reference< embed::XStorage > const& xParentStorage, + const OUString& rURL, sal_uInt32 const nOpenMode, + LifecycleProxy const & rNastiness) +{ + OUString path; + if (rURL.startsWithIgnoreAsciiCase("vnd.sun.star.Package:", &path)) + { + return GetStreamAtPath(xParentStorage, path, nOpenMode, rNastiness); + } + return nullptr; +} + +OUString OStorageHelper::GetODFVersionFromStorage(const uno::Reference<embed::XStorage>& xStorage) +{ + OUString aODFVersion; + try + { + uno::Reference<beans::XPropertySet> xPropSet(xStorage, uno::UNO_QUERY_THROW); + xPropSet->getPropertyValue("Version") >>= aODFVersion; + } + catch (uno::Exception&) + { + } + return aODFVersion; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/string.cxx b/comphelper/source/misc/string.cxx new file mode 100644 index 000000000..d40b9136b --- /dev/null +++ b/comphelper/source/misc/string.cxx @@ -0,0 +1,623 @@ +/* -*- 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 <sal/config.h> + +#include <cstddef> +#include <string_view> +#include <utility> +#include <vector> +#include <algorithm> + +#include <o3tl/safeint.hxx> +#include <o3tl/string_view.hxx> +#include <rtl/character.hxx> +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/string.hxx> +#include <rtl/strbuf.hxx> +#include <sal/types.h> + +#include <comphelper/string.hxx> +#include <comphelper/stl_types.hxx> +#include <comphelper/sequence.hxx> + +#include <com/sun/star/i18n/BreakIterator.hpp> +#include <com/sun/star/i18n/CharType.hpp> +#include <com/sun/star/i18n/Collator.hpp> + + +namespace comphelper::string { + +namespace +{ + template <typename T, typename C> T tmpl_stripStart(const T &rIn, + const C cRemove) + { + if (rIn.empty()) + return rIn; + + typename T::size_type i = 0; + + while (i < rIn.size()) + { + if (rIn[i] != cRemove) + break; + ++i; + } + + return rIn.substr(i); + } + template <typename T, typename C> T tmpl_stripStartString(const T &rIn, + const C cRemove) + { + if (rIn.isEmpty()) + return rIn; + + sal_Int32 i = 0; + + while (i < rIn.getLength()) + { + if (rIn[i] != cRemove) + break; + ++i; + } + + return rIn.copy(i); + } +} + +OString stripStart(const OString& rIn, char c) +{ + return tmpl_stripStartString<OString, char>(rIn, c); +} + +std::string_view stripStart(std::string_view rIn, char c) +{ + return tmpl_stripStart<std::string_view, char>(rIn, c); +} + +OUString stripStart(const OUString& rIn, sal_Unicode c) +{ + return tmpl_stripStartString<OUString, sal_Unicode>(rIn, c); +} + +std::u16string_view stripStart(std::u16string_view rIn, sal_Unicode c) +{ + return tmpl_stripStart<std::u16string_view, sal_Unicode>(rIn, c); +} + +namespace +{ + template <typename T, typename C> T tmpl_stripEnd(const T &rIn, + const C cRemove) + { + if (rIn.empty()) + return rIn; + + typename T::size_type i = rIn.size(); + + while (i > 0) + { + if (rIn[i-1] != cRemove) + break; + --i; + } + + return rIn.substr(0, i); + } + template <typename T, typename C> T tmpl_stripEndString(const T &rIn, + const C cRemove) + { + if (rIn.isEmpty()) + return rIn; + + sal_Int32 i = rIn.getLength(); + + while (i > 0) + { + if (rIn[i-1] != cRemove) + break; + --i; + } + + return rIn.copy(0, i); + } +} + +OString stripEnd(const OString& rIn, char c) +{ + return tmpl_stripEndString<OString, char>(rIn, c); +} + +std::string_view stripEnd(std::string_view rIn, char c) +{ + return tmpl_stripEnd<std::string_view, char>(rIn, c); +} + +OUString stripEnd(const OUString& rIn, sal_Unicode c) +{ + return tmpl_stripEndString<OUString, sal_Unicode>(rIn, c); +} + +std::u16string_view stripEnd(std::u16string_view rIn, sal_Unicode c) +{ + return tmpl_stripEnd<std::u16string_view, sal_Unicode>(rIn, c); +} + +OString strip(const OString& rIn, char c) +{ + auto x = tmpl_stripStartString<OString, char>(rIn, c); + return stripEnd(x, c); +} + +std::string_view strip(std::string_view rIn, char c) +{ + auto x = tmpl_stripStart<std::string_view, char>(rIn, c); + return stripEnd(x, c); +} + +OUString strip(const OUString& rIn, sal_Unicode c) +{ + auto x = tmpl_stripStartString<OUString, sal_Unicode>(rIn, c); + return stripEnd(x, c); +} + +std::u16string_view strip(std::u16string_view rIn, sal_Unicode c) +{ + auto x = tmpl_stripStart<std::u16string_view, sal_Unicode>(rIn, c); + return stripEnd(x, c); +} + +namespace +{ + template <typename T, typename C> sal_Int32 tmpl_getTokenCount( T rIn, + C cTok) + { + // Empty String: TokenCount by Definition is 0 + if (rIn.empty()) + return 0; + + sal_Int32 nTokCount = 1; + for (typename T::size_type i = 0; i < rIn.size(); ++i) + { + if (rIn[i] == cTok) + ++nTokCount; + } + return nTokCount; + } +} + +sal_Int32 getTokenCount(std::string_view rIn, char cTok) +{ + return tmpl_getTokenCount<std::string_view, char>(rIn, cTok); +} + +sal_Int32 getTokenCount(std::u16string_view rIn, sal_Unicode cTok) +{ + return tmpl_getTokenCount<std::u16string_view, sal_Unicode>(rIn, cTok); +} + +static sal_uInt32 decimalStringToNumber( + OUString const & str, sal_Int32 nStart, sal_Int32 nLength ) +{ + sal_uInt32 result = 0; + for( sal_Int32 i = nStart; i < nStart + nLength; ) + { + sal_uInt32 c = str.iterateCodePoints(&i); + sal_uInt32 value = 0; + if( c <= 0x0039) // ASCII decimal digits, most common + value = c - 0x0030; + else if( c >= 0x1D7F6 ) // mathematical monospace digits + value = c - 0x1D7F6; + else if( c >= 0x1D7EC ) // mathematical sans-serif bold digits + value = c - 0x1D7EC; + else if( c >= 0x1D7E2 ) // mathematical sans-serif digits + value = c - 0x1D7E2; + else if( c >= 0x1D7D8 ) // mathematical double-struck digits + value = c - 0x1D7D8; + else if( c >= 0x1D7CE ) // mathematical bold digits + value = c - 0x1D7CE; + else if( c >= 0x11066 ) // brahmi digits + value = c - 0x11066; + else if( c >= 0x104A0 ) // osmanya digits + value = c - 0x104A0; + else if( c >= 0xFF10 ) // fullwidth digits + value = c - 0xFF10; + else if( c >= 0xABF0 ) // meetei mayek digits + value = c - 0xABF0; + else if( c >= 0xAA50 ) // cham digits + value = c - 0xAA50; + else if( c >= 0xA9D0 ) // javanese digits + value = c - 0xA9D0; + else if( c >= 0xA900 ) // kayah li digits + value = c - 0xA900; + else if( c >= 0xA8D0 ) // saurashtra digits + value = c - 0xA8D0; + else if( c >= 0xA620 ) // vai digits + value = c - 0xA620; + else if( c >= 0x1C50 ) // ol chiki digits + value = c - 0x1C50; + else if( c >= 0x1C40 ) // lepcha digits + value = c - 0x1C40; + else if( c >= 0x1BB0 ) // sundanese digits + value = c - 0x1BB0; + else if( c >= 0x1B50 ) // balinese digits + value = c - 0x1B50; + else if( c >= 0x1A90 ) // tai tham tham digits + value = c - 0x1A90; + else if( c >= 0x1A80 ) // tai tham hora digits + value = c - 0x1A80; + else if( c >= 0x19D0 ) // new tai lue digits + value = c - 0x19D0; + else if( c >= 0x1946 ) // limbu digits + value = c - 0x1946; + else if( c >= 0x1810 ) // mongolian digits + value = c - 0x1810; + else if( c >= 0x17E0 ) // khmer digits + value = c - 0x17E0; + else if( c >= 0x1090 ) // myanmar shan digits + value = c - 0x1090; + else if( c >= 0x1040 ) // myanmar digits + value = c - 0x1040; + else if( c >= 0x0F20 ) // tibetan digits + value = c - 0x0F20; + else if( c >= 0x0ED0 ) // lao digits + value = c - 0x0ED0; + else if( c >= 0x0E50 ) // thai digits + value = c - 0x0E50; + else if( c >= 0x0D66 ) // malayalam digits + value = c - 0x0D66; + else if( c >= 0x0CE6 ) // kannada digits + value = c - 0x0CE6; + else if( c >= 0x0C66 ) // telugu digits + value = c - 0x0C66; + else if( c >= 0x0BE6 ) // tamil digits + value = c - 0x0BE6; + else if( c >= 0x0B66 ) // odia digits + value = c - 0x0B66; + else if( c >= 0x0AE6 ) // gujarati digits + value = c - 0x0AE6; + else if( c >= 0x0A66 ) // gurmukhi digits + value = c - 0x0A66; + else if( c >= 0x09E6 ) // bengali digits + value = c - 0x09E6; + else if( c >= 0x0966 ) // devanagari digit + value = c - 0x0966; + else if( c >= 0x07C0 ) // nko digits + value = c - 0x07C0; + else if( c >= 0x06F0 ) // extended arabic-indic digits + value = c - 0x06F0; + else if( c >= 0x0660 ) // arabic-indic digits + value = c - 0x0660; + result = result * 10 + value; + } + return result; +} + +sal_uInt32 decimalStringToNumber( + OUString const & str ) +{ + return decimalStringToNumber(str, 0, str.getLength()); +} + +using namespace ::com::sun::star; + +// convert between sequence of string and comma separated string + +OUString convertCommaSeparated( + uno::Sequence< OUString > const& i_rSeq) +{ + OUStringBuffer buf; + ::comphelper::intersperse( + i_rSeq.begin(), i_rSeq.end(), ::comphelper::OUStringBufferAppender(buf), OUString( ", " )); + return buf.makeStringAndClear(); +} + +std::vector<OUString> + split(std::u16string_view rStr, sal_Unicode cSeparator) +{ + std::vector< OUString > vec; + std::size_t idx = 0; + do + { + std::u16string_view kw = o3tl::getToken(rStr, cSeparator, idx); + kw = o3tl::trim(kw); + if (!kw.empty()) + { + vec.push_back(OUString(kw)); + } + + } while (idx != std::u16string_view::npos); + + return vec; +} + +uno::Sequence< OUString > + convertCommaSeparated( std::u16string_view i_rString ) +{ + std::vector< OUString > vec = split(i_rString, ','); + return comphelper::containerToSequence(vec); +} + +OString join(std::string_view rSeparator, const std::vector<OString>& rSequence) +{ + OStringBuffer aBuffer; + for (size_t i = 0; i < rSequence.size(); ++i) + { + if (i != 0) + aBuffer.append(rSeparator); + aBuffer.append(rSequence[i]); + } + return aBuffer.makeStringAndClear(); +} + +sal_Int32 compareNatural( const OUString & rLHS, const OUString & rRHS, + const uno::Reference< i18n::XCollator > &rCollator, + const uno::Reference< i18n::XBreakIterator > &rBI, + const lang::Locale &rLocale ) +{ + sal_Int32 nRet = 0; + + sal_Int32 nLHSLastNonDigitPos = 0; + sal_Int32 nRHSLastNonDigitPos = 0; + sal_Int32 nLHSFirstDigitPos = 0; + sal_Int32 nRHSFirstDigitPos = 0; + + // Check if the string starts with a digit + sal_Int32 nStartsDigitLHS = rBI->endOfCharBlock(rLHS, nLHSFirstDigitPos, rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER); + sal_Int32 nStartsDigitRHS = rBI->endOfCharBlock(rRHS, nRHSFirstDigitPos, rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER); + + if (nStartsDigitLHS > 0 && nStartsDigitRHS > 0) + { + sal_uInt32 nLHS = comphelper::string::decimalStringToNumber(rLHS, 0, nStartsDigitLHS); + sal_uInt32 nRHS = comphelper::string::decimalStringToNumber(rRHS, 0, nStartsDigitRHS); + + if (nLHS != nRHS) + return nLHS < nRHS ? -1 : 1; + nLHSLastNonDigitPos = nStartsDigitLHS; + nRHSLastNonDigitPos = nStartsDigitRHS; + } + else if (nStartsDigitLHS > 0) + return -1; + else if (nStartsDigitRHS > 0) + return 1; + + while (nLHSFirstDigitPos < rLHS.getLength() || nRHSFirstDigitPos < rRHS.getLength()) + { + sal_Int32 nLHSChunkLen; + sal_Int32 nRHSChunkLen; + + //Compare non digit block as normal strings + nLHSFirstDigitPos = rBI->nextCharBlock(rLHS, nLHSLastNonDigitPos, rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER); + nRHSFirstDigitPos = rBI->nextCharBlock(rRHS, nRHSLastNonDigitPos, rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER); + + if (nLHSFirstDigitPos == -1) + nLHSFirstDigitPos = rLHS.getLength(); + + if (nRHSFirstDigitPos == -1) + nRHSFirstDigitPos = rRHS.getLength(); + + nLHSChunkLen = nLHSFirstDigitPos - nLHSLastNonDigitPos; + nRHSChunkLen = nRHSFirstDigitPos - nRHSLastNonDigitPos; + + nRet = rCollator->compareSubstring(rLHS, nLHSLastNonDigitPos, nLHSChunkLen, rRHS, nRHSLastNonDigitPos, nRHSChunkLen); + if (nRet != 0) + break; + + //Compare digit block as one number vs another + nLHSLastNonDigitPos = rBI->endOfCharBlock(rLHS, nLHSFirstDigitPos, rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER); + nRHSLastNonDigitPos = rBI->endOfCharBlock(rRHS, nRHSFirstDigitPos, rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER); + if (nLHSLastNonDigitPos == -1) + nLHSLastNonDigitPos = rLHS.getLength(); + if (nRHSLastNonDigitPos == -1) + nRHSLastNonDigitPos = rRHS.getLength(); + nLHSChunkLen = nLHSLastNonDigitPos - nLHSFirstDigitPos; + nRHSChunkLen = nRHSLastNonDigitPos - nRHSFirstDigitPos; + + //To-Do: Possibly scale down those unicode codepoints that relate to + //numbers outside of the normal 0-9 range, e.g. see GetLocalizedChar in + //vcl + + sal_uInt32 nLHS = comphelper::string::decimalStringToNumber(rLHS, nLHSFirstDigitPos, nLHSChunkLen); + sal_uInt32 nRHS = comphelper::string::decimalStringToNumber(rRHS, nRHSFirstDigitPos, nRHSChunkLen); + + if (nLHS != nRHS) + { + nRet = (nLHS < nRHS) ? -1 : 1; + break; + } + } + + return nRet; +} + +NaturalStringSorter::NaturalStringSorter( + const uno::Reference< uno::XComponentContext > &rContext, + lang::Locale aLocale) : m_aLocale(std::move(aLocale)) +{ + m_xCollator = i18n::Collator::create( rContext ); + m_xCollator->loadDefaultCollator(m_aLocale, 0); + m_xBI = i18n::BreakIterator::create( rContext ); +} + +bool isdigitAsciiString(std::string_view rString) +{ + return std::all_of( + rString.data(), rString.data() + rString.size(), + [](unsigned char c){ return rtl::isAsciiDigit(c); }); +} + +bool isdigitAsciiString(std::u16string_view rString) +{ + return std::all_of( + rString.data(), rString.data() + rString.size(), + [](sal_Unicode c){ return rtl::isAsciiDigit(c); }); +} + +namespace +{ + template <typename T, typename I, typename O> T tmpl_reverseString(I rIn) + { + if (rIn.empty()) + return T(); + + typename I::size_type i = rIn.size(); + O sBuf(static_cast<sal_Int32>(i)); + while (i) + sBuf.append(rIn[--i]); + return sBuf.makeStringAndClear(); + } +} + +OUString reverseString(std::u16string_view rStr) +{ + return tmpl_reverseString<OUString, std::u16string_view, OUStringBuffer>(rStr); +} + +OString reverseString(std::string_view rStr) +{ + return tmpl_reverseString<OString, std::string_view, OStringBuffer>(rStr); +} + +OUString reverseCodePoints(OUString const & str) { + auto const len = str.getLength(); + OUStringBuffer buf(len); + for (auto i = len; i != 0;) { + buf.appendUtf32(str.iterateCodePoints(&i, -1)); + } + return buf.makeStringAndClear(); +} + +sal_Int32 indexOfAny(std::u16string_view rIn, + sal_Unicode const*const pChars, sal_Int32 const nPos) +{ + for (std::u16string_view::size_type i = nPos; i < rIn.size(); ++i) + { + sal_Unicode const c = rIn[i]; + for (sal_Unicode const* pChar = pChars; *pChar; ++pChar) + { + if (c == *pChar) + { + return i; + } + } + } + return -1; +} + +OUString removeAny(std::u16string_view rIn, + sal_Unicode const*const pChars) +{ + OUStringBuffer buf; + bool isFound(false); + for (std::u16string_view::size_type i = 0; i < rIn.size(); ++i) + { + sal_Unicode const c = rIn[i]; + bool removeC(false); + for (sal_Unicode const* pChar = pChars; *pChar; ++pChar) + { + if (c == *pChar) + { + removeC = true; + break; + } + } + if (removeC) + { + if (!isFound) + { + if (i > 0) + { + buf.append(rIn.substr(0, i)); + } + isFound = true; + } + } + else if (isFound) + { + buf.append(c); + } + } + return isFound ? buf.makeStringAndClear() : OUString(rIn); +} + +OUString setToken(const OUString& rIn, sal_Int32 nToken, sal_Unicode cTok, + std::u16string_view rNewToken) +{ + sal_Int32 nLen = rIn.getLength(); + sal_Int32 nTok = 0; + sal_Int32 nFirstChar = 0; + sal_Int32 i = 0; + + // Determine token position and length + while ( i < nLen ) + { + // Increase token count if match + if (rIn[i] == cTok) + { + ++nTok; + + if (nTok == nToken) + nFirstChar = i+1; + else if (nTok > nToken) + break; + } + + ++i; + } + + if (nTok >= nToken) + return rIn.replaceAt(nFirstChar, i-nFirstChar, rNewToken); + return rIn; +} + +/** Similar to OUString::replaceAt, but for an OUStringBuffer. + + Replace n = count characters + from position index in this string with newStr. + */ +void replaceAt(OUStringBuffer& rIn, sal_Int32 nIndex, sal_Int32 nCount, std::u16string_view newStr ) +{ + assert(nIndex >= 0 && nIndex <= rIn.getLength()); + assert(nCount >= 0); + assert(nCount <= rIn.getLength() - nIndex); + + /* Append? */ + const sal_Int32 nOldLength = rIn.getLength(); + if ( nIndex == nOldLength ) + { + rIn.append(newStr); + return; + } + + sal_Int32 nNewLength = nOldLength + newStr.size() - nCount; + if (newStr.size() > o3tl::make_unsigned(nCount)) + rIn.ensureCapacity(nOldLength + newStr.size() - nCount); + + sal_Unicode* pStr = const_cast<sal_Unicode*>(rIn.getStr()); + memmove(pStr + nIndex + newStr.size(), pStr + nIndex + nCount, nOldLength - nIndex + nCount); + memcpy(pStr + nIndex, newStr.data(), newStr.size()); + + rIn.setLength(nNewLength); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/synchronousdispatch.cxx b/comphelper/source/misc/synchronousdispatch.cxx new file mode 100644 index 000000000..1602c8963 --- /dev/null +++ b/comphelper/source/misc/synchronousdispatch.cxx @@ -0,0 +1,81 @@ +/* -*- 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/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XSynchronousDispatch.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/util/URLTransformer.hpp> + +#include <comphelper/synchronousdispatch.hxx> +#include <comphelper/processfactory.hxx> +#include <sal/log.hxx> + +namespace comphelper +{ + + +using namespace ::com::sun::star; + +uno::Reference< lang::XComponent > SynchronousDispatch::dispatch( + const uno::Reference< uno::XInterface > &xStartPoint, + const OUString &sURL, + const OUString &sTarget, + const uno::Sequence< beans::PropertyValue > &lArguments ) +{ + util::URL aURL; + aURL.Complete = sURL; + uno::Reference < util::XURLTransformer > xTrans = util::URLTransformer::create( ::comphelper::getProcessComponentContext() ); + xTrans->parseStrict( aURL ); + + uno::Reference < frame::XDispatch > xDispatcher; + uno::Reference < frame::XDispatchProvider > xProvider( xStartPoint, uno::UNO_QUERY ); + + if ( xProvider.is() ) + xDispatcher = xProvider->queryDispatch( aURL, sTarget, 0 ); + + uno::Reference < lang::XComponent > aComponent; + + if ( xDispatcher.is() ) + { + try + { + uno::Any aRet; + uno::Reference < frame::XSynchronousDispatch > xSyncDisp( xDispatcher, uno::UNO_QUERY_THROW ); + + aRet = xSyncDisp->dispatchWithReturnValue( aURL, lArguments ); + + aRet >>= aComponent; + } + catch ( uno::Exception& ) + { + // can't use TOOLS_WARN_EXCEPTION, as comphelper is used by libtl! + SAL_WARN("comphelper", "SynchronousDispatch::dispatch(): error while dispatching '" + << sURL << "' for '" << sTarget << "'!"); + } + } + + return aComponent; +} + + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/syntaxhighlight.cxx b/comphelper/source/misc/syntaxhighlight.cxx new file mode 100644 index 000000000..3ce8086e6 --- /dev/null +++ b/comphelper/source/misc/syntaxhighlight.cxx @@ -0,0 +1,740 @@ +/* -*- 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 <sal/config.h> + +#include <cassert> + +#include <rtl/character.hxx> +#include <unicode/uchar.h> +#include <comphelper/syntaxhighlight.hxx> +#include <o3tl/typed_flags_set.hxx> + +namespace { + +// Flags for character properties +enum class CharFlags { + StartIdentifier = 0x0001, + InIdentifier = 0x0002, + StartNumber = 0x0004, + InNumber = 0x0008, + InHexNumber = 0x0010, + InOctNumber = 0x0020, + StartString = 0x0040, + Operator = 0x0080, + Space = 0x0100, + EOL = 0x0200 +}; + +} + +namespace o3tl { + template<> struct typed_flags<CharFlags> : is_typed_flags<CharFlags, 0x03ff> {}; +} + +// ########################################################################## +// ATTENTION: all these words need to be in lower case +// ########################################################################## +static const char* strListBasicKeyWords[] = { + "access", + "alias", + "and", + "any", + "append", + "as", + "attribute", + "base", + "binary", + "boolean", + "byref", + "byte", + "byval", + "call", + "case", + "cdecl", + "classmodule", + "close", + "compare", + "compatible", + "const", + "currency", + "date", + "declare", + "defbool", + "defcur", + "defdate", + "defdbl", + "deferr", + "defint", + "deflng", + "defobj", + "defsng", + "defstr", + "defvar", + "dim", + "do", + "doevents", + "double", + "each", + "else", + "elseif", + "end", + "end enum", + "end function", + "end if", + "end property", + "end select", + "end sub", + "end type", + "endif", + "enum", + "eqv", + "erase", + "error", + "exit", + "explicit", + "for", + "function", + "get", + "global", + "gosub", + "goto", + "if", + "imp", + "implements", + "in", + "input", + "integer", + "is", + "let", + "lib", + "like", + "line", + "line input", + "local", + "lock", + "long", + "loop", + "lprint", + "lset", + "mod", + "name", + "new", + "next", + "not", + "object", + "on", + "open", + "option", + "optional", + "or", + "output", + "paramarray", + "preserve", + "print", + "private", + "property", + "public", + "random", + "read", + "redim", + "rem", + "resume", + "return", + "rset", + "select", + "set", + "shared", + "single", + "static", + "step", + "stop", + "string", + "sub", + "system", + "text", + "then", + "to", + "type", + "typeof", + "until", + "variant", + "vbasupport", + "wend", + "while", + "with", + "withevents", + "write", + "xor" +}; + + +static const char* strListSqlKeyWords[] = { + "all", + "and", + "any", + "as", + "asc", + "avg", + "between", + "by", + "cast", + "corresponding", + "count", + "create", + "cross", + "delete", + "desc", + "distinct", + "drop", + "escape", + "except", + "exists", + "false", + "from", + "full", + "global", + "group", + "having", + "in", + "inner", + "insert", + "intersect", + "into", + "is", + "join", + "left", + "like", + "limit", + "local", + "match", + "max", + "min", + "natural", + "not", + "null", + "on", + "or", + "order", + "outer", + "right", + "select", + "set", + "some", + "sum", + "table", + "temporary", + "true", + "union", + "unique", + "unknown", + "update", + "using", + "values", + "where" +}; + + +extern "C" { + +static int compare_strings( const void *arg1, const void *arg2 ) +{ + return strcmp( static_cast<char const *>(arg1), *static_cast<char * const *>(arg2) ); +} + +} + +namespace +{ + bool isAlpha(sal_Unicode c) + { + if (rtl::isAsciiAlpha(c)) + return true; + return u_isalpha(c); + } +} + +class SyntaxHighlighter::Tokenizer +{ + // Character information tables + CharFlags aCharTypeTab[256] = {}; + + // Auxiliary function: testing of the character flags + bool testCharFlags(sal_Unicode c, CharFlags nTestFlags) const; + + // Get new token, EmptyString == nothing more over there + bool getNextToken(std::u16string_view::const_iterator& pos, std::u16string_view::const_iterator end, /*out*/TokenType& reType, + /*out*/std::u16string_view::const_iterator& rpStartPos, /*out*/std::u16string_view::const_iterator& rpEndPos) const; + + const char** ppListKeyWords; + sal_uInt16 nKeyWordCount; + +public: + HighlighterLanguage const aLanguage; + + explicit Tokenizer( HighlighterLanguage aLang ); + + void getHighlightPortions(std::u16string_view rLine, + /*out*/std::vector<HighlightPortion>& portions) const; + void setKeyWords( const char** ppKeyWords, sal_uInt16 nCount ); +}; + +// Helper function: test character flag +bool SyntaxHighlighter::Tokenizer::testCharFlags(sal_Unicode c, CharFlags nTestFlags) const +{ + bool bRet = false; + if( c != 0 && c <= 255 ) + { + bRet = bool(aCharTypeTab[c] & nTestFlags); + } + else if( c > 255 ) + { + bRet = (( CharFlags::StartIdentifier | CharFlags::InIdentifier ) & nTestFlags) + && isAlpha(c); + } + return bRet; +} + +void SyntaxHighlighter::Tokenizer::setKeyWords( const char** ppKeyWords, sal_uInt16 nCount ) +{ + ppListKeyWords = ppKeyWords; + nKeyWordCount = nCount; +} + +bool SyntaxHighlighter::Tokenizer::getNextToken(std::u16string_view::const_iterator& pos, std::u16string_view::const_iterator end, + /*out*/TokenType& reType, + /*out*/std::u16string_view::const_iterator& rpStartPos, /*out*/std::u16string_view::const_iterator& rpEndPos) const +{ + reType = TokenType::Unknown; + + rpStartPos = pos; + + if( pos == end ) + return false; + + sal_Unicode c = *pos; + ++pos; + + //*** Go through all possibilities *** + // Space? + if ( testCharFlags( c, CharFlags::Space ) ) + { + while( pos != end && testCharFlags( *pos, CharFlags::Space ) ) + ++pos; + + reType = TokenType::Whitespace; + } + + // Identifier? + else if ( testCharFlags( c, CharFlags::StartIdentifier ) ) + { + bool bIdentifierChar; + do + { + if (pos == end) + break; + // Fetch next character + c = *pos; + bIdentifierChar = testCharFlags( c, CharFlags::InIdentifier ); + if( bIdentifierChar ) + ++pos; + } + while( bIdentifierChar ); + + reType = TokenType::Identifier; + + // Keyword table + if (ppListKeyWords != nullptr) + { + int nCount = pos - rpStartPos; + + // No keyword if string contains char > 255 + bool bCanBeKeyword = true; + for( int i = 0 ; i < nCount ; i++ ) + { + if( rpStartPos[i] > 255 ) + { + bCanBeKeyword = false; + break; + } + } + + if( bCanBeKeyword ) + { + std::u16string_view aKWString(&*rpStartPos, nCount); + OString aByteStr = OUStringToOString(aKWString, + RTL_TEXTENCODING_ASCII_US).toAsciiLowerCase(); + if ( bsearch( aByteStr.getStr(), ppListKeyWords, nKeyWordCount, sizeof( char* ), + compare_strings ) ) + { + reType = TokenType::Keywords; + + if( aByteStr == "rem" ) + { + // Remove all characters until end of line or EOF + for (;;) + { + if (pos == end) + break; + sal_Unicode cPeek = *pos; + if ( testCharFlags( cPeek, CharFlags::EOL ) ) + break; + ++pos; + } + + reType = TokenType::Comment; + } + } + } + } + } + + // Operator? + // only for BASIC '\'' should be a comment, otherwise it is a normal string and handled there + else if ( testCharFlags( c, CharFlags::Operator ) || ( (c == '\'') && (aLanguage==HighlighterLanguage::Basic)) ) + { + // parameters for SQL view + if (((c==':') || (c=='?')) && (aLanguage == HighlighterLanguage::SQL)) + { + if (c!='?') + { + bool bIdentifierChar; + do + { + // Get next character + if (pos == end) + break; + c = *pos; + bIdentifierChar = isAlpha(c); + if( bIdentifierChar ) + ++pos; + } + while( bIdentifierChar ); + } + reType = TokenType::Parameter; + } + else if ((c=='-') && (aLanguage == HighlighterLanguage::SQL)) + { + if (pos != end && *pos=='-') + { + // Remove all characters until end of line or EOF + while( pos != end && !testCharFlags( *pos, CharFlags::EOL ) ) + { + ++pos; + } + reType = TokenType::Comment; + } + else + reType = TokenType::Operator; + } + else if ((c=='/') && (aLanguage == HighlighterLanguage::SQL)) + { + if (pos != end && *pos=='/') + { + // Remove all characters until end of line or EOF + while( pos != end && !testCharFlags( *pos, CharFlags::EOL ) ) + { + ++pos; + } + reType = TokenType::Comment; + } + else + reType = TokenType::Operator; + } + else + { + // Apostrophe is Basic comment + if (( c == '\'') && (aLanguage == HighlighterLanguage::Basic)) + { + // Skip all characters until end of input or end of line: + for (;;) { + if (pos == end) + break; + c = *pos; + if (testCharFlags(c, CharFlags::EOL)) { + break; + } + ++pos; + } + + reType = TokenType::Comment; + } + + // The real operator; can be easily used since not the actual + // operator (e.g. +=) is concerned, but the fact that it is one + if( reType != TokenType::Comment ) + { + reType = TokenType::Operator; + } + + } + } + + // Object separator? Must be handled before Number + else if( c == '.' && ( pos == end || *pos < '0' || *pos > '9' ) ) + { + reType = TokenType::Operator; + } + + // Number? + else if( testCharFlags( c, CharFlags::StartNumber ) ) + { + reType = TokenType::Number; + + // Number system, 10 = normal, it is changed for Oct/Hex + int nRadix = 10; + + // Is it an Oct or a Hex number? + if( c == '&' ) + { + // Octal? + if( pos != end && (*pos == 'o' || *pos == 'O' )) + { + // remove o + ++pos; + nRadix = 8; // Octal base + + // Read all numbers + while( pos != end && testCharFlags( *pos, CharFlags::InOctNumber ) ) + ++pos; + } + // Hexadecimal? + else if( pos != end && (*pos == 'h' || *pos == 'H' )) + { + // remove x + ++pos; + nRadix = 16; // Hexadecimal base + + // Read all numbers + while( pos != end && testCharFlags( *pos, CharFlags::InHexNumber ) ) + ++pos; + } + else + { + reType = TokenType::Operator; + } + } + + // When it is not Oct or Hex, then it is double + if( reType == TokenType::Number && nRadix == 10 ) + { + // Flag if the last character is an exponent + bool bAfterExpChar = false; + + // Read all numbers + while( pos != end && (testCharFlags( *pos, CharFlags::InNumber ) || + (bAfterExpChar && *pos == '+' ) || + (bAfterExpChar && *pos == '-' ) )) + // After exponent +/- are OK, too + { + c = *pos++; + bAfterExpChar = ( c == 'e' || c == 'E' ); + } + } + } + + // String? + else if( testCharFlags( c, CharFlags::StartString ) ) + { + // Remember which character has opened the string + sal_Unicode cEndString = c; + if( c == '[' ) + cEndString = ']'; + + // Read all characters + while( pos == end || *pos != cEndString ) + { + // Detect EOF before reading next char, so we do not lose EOF + if( pos == end ) + { + // ERROR: unterminated string literal + reType = TokenType::Error; + break; + } + c = *pos++; + if( testCharFlags( c, CharFlags::EOL ) ) + { + // ERROR: unterminated string literal + reType = TokenType::Error; + break; + } + } + + if( reType != TokenType::Error ) + { + ++pos; + if( cEndString == ']' ) + reType = TokenType::Identifier; + else + reType = TokenType::String; + } + } + + // End of line? + else if( testCharFlags( c, CharFlags::EOL ) ) + { + // If another EOL character comes, read it + if (pos != end) + { + sal_Unicode cNext = *pos; + if( cNext != c && testCharFlags( cNext, CharFlags::EOL ) ) + ++pos; + } + + reType = TokenType::EOL; + } + + // All other will remain TokenType::Unknown + + // Save end position + rpEndPos = pos; + return true; +} + +SyntaxHighlighter::Tokenizer::Tokenizer( HighlighterLanguage aLang ): aLanguage(aLang) +{ + // Fill character table + sal_uInt16 i; + + // Allowed characters for identifiers + CharFlags nHelpMask = CharFlags::StartIdentifier | CharFlags::InIdentifier; + for( i = 'a' ; i <= 'z' ; i++ ) + aCharTypeTab[i] |= nHelpMask; + for( i = 'A' ; i <= 'Z' ; i++ ) + aCharTypeTab[i] |= nHelpMask; + aCharTypeTab[int('_')] |= nHelpMask; + aCharTypeTab[int('$')] |= nHelpMask; + + // Digit (can be identifier and number) + nHelpMask = CharFlags::InIdentifier | CharFlags::StartNumber | + CharFlags::InNumber | CharFlags::InHexNumber; + for( i = '0' ; i <= '9' ; i++ ) + aCharTypeTab[i] |= nHelpMask; + + // Add e, E, . and & here manually + aCharTypeTab[int('e')] |= CharFlags::InNumber; + aCharTypeTab[int('E')] |= CharFlags::InNumber; + aCharTypeTab[int('.')] |= CharFlags::InNumber | CharFlags::StartNumber; + aCharTypeTab[int('&')] |= CharFlags::StartNumber; + + // Hexadecimal digit + for( i = 'a' ; i <= 'f' ; i++ ) + aCharTypeTab[i] |= CharFlags::InHexNumber; + for( i = 'A' ; i <= 'F' ; i++ ) + aCharTypeTab[i] |= CharFlags::InHexNumber; + + // Octal digit + for( i = '0' ; i <= '7' ; i++ ) + aCharTypeTab[i] |= CharFlags::InOctNumber; + + // String literal start/end characters + aCharTypeTab[int('\'')] |= CharFlags::StartString; + aCharTypeTab[int('\"')] |= CharFlags::StartString; + aCharTypeTab[int('[')] |= CharFlags::StartString; + aCharTypeTab[int('`')] |= CharFlags::StartString; + + // Operator characters + aCharTypeTab[int('!')] |= CharFlags::Operator; + aCharTypeTab[int('%')] |= CharFlags::Operator; + // aCharTypeTab[(int)'&'] |= CharFlags::Operator; Removed because of #i14140 + aCharTypeTab[int('(')] |= CharFlags::Operator; + aCharTypeTab[int(')')] |= CharFlags::Operator; + aCharTypeTab[int('*')] |= CharFlags::Operator; + aCharTypeTab[int('+')] |= CharFlags::Operator; + aCharTypeTab[int(',')] |= CharFlags::Operator; + aCharTypeTab[int('-')] |= CharFlags::Operator; + aCharTypeTab[int('/')] |= CharFlags::Operator; + aCharTypeTab[int(':')] |= CharFlags::Operator; + aCharTypeTab[int('<')] |= CharFlags::Operator; + aCharTypeTab[int('=')] |= CharFlags::Operator; + aCharTypeTab[int('>')] |= CharFlags::Operator; + aCharTypeTab[int('?')] |= CharFlags::Operator; + aCharTypeTab[int('^')] |= CharFlags::Operator; + aCharTypeTab[int('|')] |= CharFlags::Operator; + aCharTypeTab[int('~')] |= CharFlags::Operator; + aCharTypeTab[int('{')] |= CharFlags::Operator; + aCharTypeTab[int('}')] |= CharFlags::Operator; + // aCharTypeTab[(int)'['] |= CharFlags::Operator; Removed because of #i17826 + aCharTypeTab[int(']')] |= CharFlags::Operator; + aCharTypeTab[int(';')] |= CharFlags::Operator; + + // Space + aCharTypeTab[int(' ') ] |= CharFlags::Space; + aCharTypeTab[int('\t')] |= CharFlags::Space; + + // End of line characters + aCharTypeTab[int('\r')] |= CharFlags::EOL; + aCharTypeTab[int('\n')] |= CharFlags::EOL; + + ppListKeyWords = nullptr; + nKeyWordCount = 0; +} + +void SyntaxHighlighter::Tokenizer::getHighlightPortions(std::u16string_view rLine, + /*out*/std::vector<HighlightPortion>& portions) const +{ + // Set the position to the beginning of the source string + auto pos = rLine.begin(); + + // Variables for the out parameter + TokenType eType; + std::u16string_view::const_iterator pStartPos; + std::u16string_view::const_iterator pEndPos; + + // Loop over all the tokens + while( getNextToken( pos, rLine.end(), eType, pStartPos, pEndPos ) ) + { + portions.emplace_back( + pStartPos - rLine.begin(), pEndPos - rLine.begin(), eType); + } +} + + +SyntaxHighlighter::SyntaxHighlighter(HighlighterLanguage language): + m_tokenizer(new SyntaxHighlighter::Tokenizer(language)) +{ + switch (language) + { + case HighlighterLanguage::Basic: + m_tokenizer->setKeyWords( strListBasicKeyWords, + std::size( strListBasicKeyWords )); + break; + case HighlighterLanguage::SQL: + m_tokenizer->setKeyWords( strListSqlKeyWords, + std::size( strListSqlKeyWords )); + break; + default: + assert(false); // this cannot happen + } +} + +SyntaxHighlighter::~SyntaxHighlighter() {} + +void SyntaxHighlighter::getHighlightPortions(std::u16string_view rLine, + /*out*/std::vector<HighlightPortion>& portions) const +{ + m_tokenizer->getHighlightPortions( rLine, portions ); +} + +HighlighterLanguage SyntaxHighlighter::GetLanguage() const +{ + return m_tokenizer->aLanguage; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/threadpool.cxx b/comphelper/source/misc/threadpool.cxx new file mode 100644 index 000000000..f0a71eb05 --- /dev/null +++ b/comphelper/source/misc/threadpool.cxx @@ -0,0 +1,394 @@ +/* -*- 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/. + */ + +#include <comphelper/threadpool.hxx> + +#include <com/sun/star/uno/Exception.hpp> +#include <config_options.h> +#include <o3tl/safeint.hxx> +#include <sal/config.h> +#include <sal/log.hxx> +#include <salhelper/thread.hxx> +#include <algorithm> +#include <memory> +#include <thread> +#include <chrono> +#include <cstddef> +#include <comphelper/debuggerinfo.hxx> +#include <utility> + +#if defined HAVE_VALGRIND_HEADERS +#include <valgrind/memcheck.h> +#endif + +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + +namespace comphelper { + +/** prevent waiting for a task from inside a task */ +#if defined DBG_UTIL && (defined LINUX || defined _WIN32) +static thread_local bool gbIsWorkerThread; +#endif + +// used to group thread-tasks for waiting in waitTillDone() +class ThreadTaskTag +{ + std::mutex maMutex; + sal_Int32 mnTasksWorking; + std::condition_variable maTasksComplete; + +public: + ThreadTaskTag(); + bool isDone(); + void waitUntilDone(); + void onTaskWorkerDone(); + void onTaskPushed(); +}; + + +class ThreadPool::ThreadWorker : public salhelper::Thread +{ + ThreadPool *mpPool; +public: + + explicit ThreadWorker( ThreadPool *pPool ) : + salhelper::Thread("thread-pool"), + mpPool( pPool ) + { + } + + virtual void execute() override + { +#if defined DBG_UTIL && (defined LINUX || defined _WIN32) + gbIsWorkerThread = true; +#endif + std::unique_lock< std::mutex > aGuard( mpPool->maMutex ); + + while( !mpPool->mbTerminate ) + { + std::unique_ptr<ThreadTask> pTask = mpPool->popWorkLocked( aGuard, true ); + if( pTask ) + { + std::shared_ptr<ThreadTaskTag> pTag(pTask->mpTag); + mpPool->incBusyWorker(); + aGuard.unlock(); + + pTask->exec(); + pTask.reset(); + + aGuard.lock(); + mpPool->decBusyWorker(); + pTag->onTaskWorkerDone(); + } + } + } +}; + +ThreadPool::ThreadPool(std::size_t nWorkers) + : mbTerminate(true) + , mnMaxWorkers(nWorkers) + , mnBusyWorkers(0) +{ +} + +ThreadPool::~ThreadPool() +{ + // note: calling shutdown from global variable dtor blocks forever on Win7 + // note2: there isn't enough MSVCRT left on exit to call assert() properly + // so these asserts just print something to stderr but exit status is + // still 0, but hopefully they will be more helpful on non-WNT platforms + assert(mbTerminate); + assert(maTasks.empty()); + assert(mnBusyWorkers == 0); +} + +namespace { + +std::shared_ptr< ThreadPool >& GetStaticThreadPool() +{ + static std::shared_ptr< ThreadPool > POOL = + []() + { + const std::size_t nThreads = ThreadPool::getPreferredConcurrency(); + return std::make_shared< ThreadPool >( nThreads ); + }(); + return POOL; +} + +} + +ThreadPool& ThreadPool::getSharedOptimalPool() +{ + return *GetStaticThreadPool(); +} + +std::size_t ThreadPool::getPreferredConcurrency() +{ + static std::size_t ThreadCount = []() + { + const std::size_t nHardThreads = o3tl::clamp_to_unsigned<std::size_t>( + std::max(std::thread::hardware_concurrency(), 1U)); + std::size_t nThreads = nHardThreads; + const char *pEnv = getenv("MAX_CONCURRENCY"); + if (pEnv != nullptr) + { + // Override with user/admin preference. + nThreads = o3tl::clamp_to_unsigned<std::size_t>(rtl_str_toInt32(pEnv, 10)); + } + + nThreads = std::min(nHardThreads, nThreads); + return std::max<std::size_t>(nThreads, 1); + }(); + + return ThreadCount; +} + +// Used to order shutdown, and to ensure there are no lingering +// threads after LibreOfficeKit pre-init. +void ThreadPool::shutdown() +{ +// if (mbTerminate) +// return; + + std::unique_lock< std::mutex > aGuard( maMutex ); + shutdownLocked(aGuard); +} + +void ThreadPool::shutdownLocked(std::unique_lock<std::mutex>& aGuard) +{ + if( maWorkers.empty() ) + { // no threads at all -> execute the work in-line + std::unique_ptr<ThreadTask> pTask; + while ( ( pTask = popWorkLocked(aGuard, false) ) ) + { + std::shared_ptr<ThreadTaskTag> pTag(pTask->mpTag); + pTask->exec(); + pTag->onTaskWorkerDone(); + } + } + else + { + while( !maTasks.empty() ) + { + maTasksChanged.wait( aGuard ); + // In the (unlikely but possible?) case pushTask() gets called meanwhile, + // its notify_one() call is meant to wake a up a thread and process the task. + // But if this code gets woken up instead, it could lead to a deadlock. + // Pass on the notification. + maTasksChanged.notify_one(); + } + } + assert( maTasks.empty() ); + + // coverity[missing_lock] - on purpose + mbTerminate = true; + + maTasksChanged.notify_all(); + + decltype(maWorkers) aWorkers; + std::swap(maWorkers, aWorkers); + aGuard.unlock(); + + while (!aWorkers.empty()) + { + rtl::Reference<ThreadWorker> xWorker = aWorkers.back(); + aWorkers.pop_back(); + assert(std::find(aWorkers.begin(), aWorkers.end(), xWorker) + == aWorkers.end()); + { + xWorker->join(); + xWorker.clear(); + } + } +} + +void ThreadPool::pushTask( std::unique_ptr<ThreadTask> pTask ) +{ + std::scoped_lock< std::mutex > aGuard( maMutex ); + + mbTerminate = false; + + // Worked on tasks are already removed from maTasks, so include the count of busy workers. + if (maWorkers.size() < mnMaxWorkers && maWorkers.size() <= maTasks.size() + mnBusyWorkers) + { + maWorkers.push_back( new ThreadWorker( this ) ); + maWorkers.back()->launch(); + } + + pTask->mpTag->onTaskPushed(); + maTasks.insert( maTasks.begin(), std::move(pTask) ); + + maTasksChanged.notify_one(); +} + +std::unique_ptr<ThreadTask> ThreadPool::popWorkLocked( std::unique_lock< std::mutex > & rGuard, bool bWait ) +{ + do + { + if( !maTasks.empty() ) + { + std::unique_ptr<ThreadTask> pTask = std::move(maTasks.back()); + maTasks.pop_back(); + return pTask; + } + else if (!bWait || mbTerminate) + return nullptr; + + maTasksChanged.wait( rGuard ); + + } while (!mbTerminate); + + return nullptr; +} + +void ThreadPool::incBusyWorker() +{ + ++mnBusyWorkers; +} + +void ThreadPool::decBusyWorker() +{ + assert(mnBusyWorkers >= 1); + --mnBusyWorkers; +} + +void ThreadPool::waitUntilDone(const std::shared_ptr<ThreadTaskTag>& rTag, bool bJoin) +{ +#if defined DBG_UTIL && (defined LINUX || defined _WIN32) + assert(!gbIsWorkerThread && "cannot wait for tasks from inside a task"); +#endif + { + std::unique_lock< std::mutex > aGuard( maMutex ); + + if( maWorkers.empty() ) + { // no threads at all -> execute the work in-line + while (!rTag->isDone()) + { + std::unique_ptr<ThreadTask> pTask = popWorkLocked(aGuard, false); + if (!pTask) + break; + std::shared_ptr<ThreadTaskTag> pTag(pTask->mpTag); + pTask->exec(); + pTag->onTaskWorkerDone(); + } + } + } + + rTag->waitUntilDone(); + + if (bJoin) + joinThreadsIfIdle(); +} + +void ThreadPool::joinThreadsIfIdle() +{ + std::unique_lock< std::mutex > aGuard( maMutex ); + if (isIdle()) // check if there are still tasks from another tag + { + shutdownLocked(aGuard); + } +} + +std::shared_ptr<ThreadTaskTag> ThreadPool::createThreadTaskTag() +{ + return std::make_shared<ThreadTaskTag>(); +} + +bool ThreadPool::isTaskTagDone(const std::shared_ptr<ThreadTaskTag>& pTag) +{ + return pTag->isDone(); +} + +ThreadTask::ThreadTask(std::shared_ptr<ThreadTaskTag> xTag) + : mpTag(std::move(xTag)) +{ +} + +void ThreadTask::exec() +{ + try { + doWork(); + } + catch (const std::exception &e) + { + SAL_WARN("comphelper", "exception in thread worker while calling doWork(): " << e.what()); + } + catch (const css::uno::Exception &e) + { + SAL_WARN("comphelper", "exception in thread worker while calling doWork(): " << e); + } + catch (...) + { + SAL_WARN("comphelper", "unknown exception in thread worker while calling doWork()"); + } +} + +ThreadTaskTag::ThreadTaskTag() : mnTasksWorking(0) +{ +} + +void ThreadTaskTag::onTaskPushed() +{ + std::scoped_lock< std::mutex > aGuard( maMutex ); + mnTasksWorking++; + assert( mnTasksWorking < 65536 ); // sanity checking +} + +void ThreadTaskTag::onTaskWorkerDone() +{ + std::scoped_lock< std::mutex > aGuard( maMutex ); + mnTasksWorking--; + assert(mnTasksWorking >= 0); + if (mnTasksWorking == 0) + maTasksComplete.notify_all(); +} + +bool ThreadTaskTag::isDone() +{ + std::scoped_lock< std::mutex > aGuard( maMutex ); + return mnTasksWorking == 0; +} + +void ThreadTaskTag::waitUntilDone() +{ + std::unique_lock< std::mutex > aGuard( maMutex ); + while( mnTasksWorking > 0 ) + { +#if defined DBG_UTIL && !defined NDEBUG + // 10 minute timeout in debug mode, unless the code is built with + // sanitizers or debugged in valgrind or gdb, in which case the threads + // should not time out in the middle of a debugging session + int maxTimeout = 10 * 60; +#if !ENABLE_RUNTIME_OPTIMIZATIONS + maxTimeout = 30 * 60; +#endif +#if defined HAVE_VALGRIND_HEADERS + if( RUNNING_ON_VALGRIND ) + maxTimeout = 30 * 60; +#endif + if( isDebuggerAttached()) + maxTimeout = 300 * 60; + std::cv_status result = maTasksComplete.wait_for( + aGuard, std::chrono::seconds( maxTimeout )); + assert(result != std::cv_status::timeout); +#else + // 10 minute timeout in production so the app eventually throws some kind of error + if (maTasksComplete.wait_for( + aGuard, std::chrono::seconds( 10 * 60 )) == std::cv_status::timeout) + throw std::runtime_error("timeout waiting for threadpool tasks"); +#endif + } +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/traceevent.cxx b/comphelper/source/misc/traceevent.cxx new file mode 100644 index 000000000..fb07e1caa --- /dev/null +++ b/comphelper/source/misc/traceevent.cxx @@ -0,0 +1,144 @@ +/* -*- 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/. + */ + +#include <sal/config.h> + +#include <atomic> +#include <mutex> +#include <iostream> + +#include <comphelper/profilezone.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/traceevent.hxx> + +namespace comphelper +{ +#ifdef DBG_UTIL +std::atomic<bool> TraceEvent::s_bRecording = (getenv("TRACE_EVENT_RECORDING") != nullptr); +#else +std::atomic<bool> TraceEvent::s_bRecording = false; +#endif + +std::size_t TraceEvent::s_nBufferSize = 0; +void (*TraceEvent::s_pBufferFullCallback)() = nullptr; + +int AsyncEvent::s_nIdCounter = 0; + +static thread_local int nProfileZoneNesting = 0; // Level of Nested Profile Zones + +namespace +{ +std::vector<OUString> g_aRecording; // recorded data +std::mutex g_aMutex; +} + +void TraceEvent::addRecording(const OUString& sObject) +{ + std::lock_guard aGuard(g_aMutex); + + g_aRecording.emplace_back(sObject); + + if (s_nBufferSize > 0 && g_aRecording.size() >= s_nBufferSize) + { + if (s_pBufferFullCallback != nullptr) + (*s_pBufferFullCallback)(); + } +} + +void TraceEvent::addInstantEvent(const char* sName, const std::map<OUString, OUString>& args) +{ + long long nNow = getNow(); + + int nPid = 0; + oslProcessInfo aProcessInfo; + aProcessInfo.Size = sizeof(oslProcessInfo); + if (osl_getProcessInfo(nullptr, osl_Process_IDENTIFIER, &aProcessInfo) == osl_Process_E_None) + nPid = aProcessInfo.Ident; + + addRecording("{" + "\"name:\"" + + OUString(sName, strlen(sName), RTL_TEXTENCODING_UTF8) + + "\"," + "\"ph\":\"i\"" + + createArgsString(args) + ",\"ts\":" + OUString::number(nNow) + + "," + "\"pid\":" + + OUString::number(nPid) + + "," + "\"tid\":" + + OUString::number(osl_getThreadIdentifier(nullptr)) + "},"); +} + +void TraceEvent::startRecording() +{ + std::lock_guard aGuard(g_aMutex); + s_bRecording = true; +} + +void TraceEvent::stopRecording() { s_bRecording = false; } + +void TraceEvent::setBufferSizeAndCallback(std::size_t bufferSize, void (*bufferFullCallback)()) +{ + s_nBufferSize = bufferSize; + s_pBufferFullCallback = bufferFullCallback; +} + +std::vector<OUString> TraceEvent::getEventVectorAndClear() +{ + bool bRecording; + std::vector<OUString> aRecording; + { + std::lock_guard aGuard(g_aMutex); + bRecording = s_bRecording; + stopRecording(); + aRecording.swap(g_aRecording); + } + // reset start time and nesting level + if (bRecording) + startRecording(); + return aRecording; +} + +css::uno::Sequence<OUString> TraceEvent::getRecordingAndClear() +{ + return comphelper::containerToSequence(getEventVectorAndClear()); +} + +void ProfileZone::addRecording() +{ + assert(s_bRecording); + + long long nNow = getNow(); + + // Generate a single "Complete Event" (type X) + TraceEvent::addRecording("{" + "\"name\":\"" + + OUString(m_sName, strlen(m_sName), RTL_TEXTENCODING_UTF8) + + "\"," + "\"ph\":\"X\"," + "\"ts\":" + + OUString::number(m_nCreateTime) + + "," + "\"dur\":" + + OUString::number(nNow - m_nCreateTime) + m_sArgs + + "," + "\"pid\":" + + OUString::number(m_nPid) + + "," + "\"tid\":" + + OUString::number(osl_getThreadIdentifier(nullptr)) + "},"); +} + +int ProfileZone::getNestingLevel() { return nProfileZoneNesting; } + +void ProfileZone::setNestingLevel(int nNestingLevel) { nProfileZoneNesting = nNestingLevel; } + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/typedescriptionref.hxx b/comphelper/source/misc/typedescriptionref.hxx new file mode 100644 index 000000000..f4580cb2e --- /dev/null +++ b/comphelper/source/misc/typedescriptionref.hxx @@ -0,0 +1,51 @@ +/* -*- 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 <typelib/typedescription.h> + +namespace comphelper::detail +{ +// This is like com::sun::star::uno::TypeDescription, but it uses TYPELIB_DANGER_GET +// (which the code used originally, but it's easier to have a class to handle ownership). +class TypeDescriptionRef +{ +public: + TypeDescriptionRef(typelib_TypeDescriptionReference* typeDef) + { + TYPELIB_DANGER_GET(&typeDescr, typeDef); + } + ~TypeDescriptionRef() { TYPELIB_DANGER_RELEASE(typeDescr); } + typelib_TypeDescription* get() { return typeDescr; } + typelib_TypeDescription* operator->() { return typeDescr; } + bool is() { return typeDescr != nullptr; } + bool equals(const TypeDescriptionRef& other) const + { + return typeDescr && other.typeDescr + && typelib_typedescription_equals(typeDescr, other.typeDescr); + } + +private: + typelib_TypeDescription* typeDescr = nullptr; +}; + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/types.cxx b/comphelper/source/misc/types.cxx new file mode 100644 index 000000000..8887d5b5a --- /dev/null +++ b/comphelper/source/misc/types.cxx @@ -0,0 +1,146 @@ +/* -*- 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 <comphelper/types.hxx> +#include <comphelper/extract.hxx> +#include <com/sun/star/awt/FontUnderline.hpp> +#include <com/sun/star/awt/FontStrikeout.hpp> +#include <com/sun/star/awt/FontDescriptor.hpp> +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <typelib/typedescription.hxx> +#include <sal/log.hxx> + +namespace comphelper +{ +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; + +sal_Int64 getINT64(const Any& _rAny) +{ + sal_Int64 nReturn = 0; + if (!(_rAny >>= nReturn)) + SAL_WARN("comphelper", "conversion from Any to sal_Int64 failed"); + return nReturn; +} + +sal_Int32 getINT32(const Any& _rAny) +{ + sal_Int32 nReturn = 0; + if (!(_rAny >>= nReturn)) + SAL_WARN("comphelper", "conversion from Any to sal_Int32 failed"); + return nReturn; +} + +sal_Int16 getINT16(const Any& _rAny) +{ + sal_Int16 nReturn = 0; + if (!(_rAny >>= nReturn)) + SAL_WARN("comphelper", "conversion from Any to sal_Int16 failed"); + return nReturn; +} + +double getDouble(const Any& _rAny) +{ + double nReturn = 0.0; + if (!(_rAny >>= nReturn)) + SAL_WARN("comphelper", "conversion from Any to double failed"); + return nReturn; +} + +float getFloat(const Any& _rAny) +{ + float nReturn = 0.0; + if (!(_rAny >>= nReturn)) + SAL_WARN("comphelper", "conversion from Any to float failed"); + return nReturn; +} + +OUString getString(const Any& _rAny) +{ + OUString nReturn; + if (!(_rAny >>= nReturn)) + SAL_WARN("comphelper", "conversion from Any to OUString failed"); + return nReturn; +} + +bool getBOOL(const Any& _rAny) +{ + bool bReturn = false; + if (auto b = o3tl::tryAccess<bool>(_rAny)) + bReturn = *b; + else + OSL_FAIL("comphelper::getBOOL : invalid argument !"); + return bReturn; +} + +sal_Int32 getEnumAsINT32(const Any& _rAny) +{ + sal_Int32 nReturn = 0; + if (!::cppu::enum2int(nReturn, _rAny)) + throw IllegalArgumentException("enum2int failed", + css::uno::Reference<css::uno::XInterface>(), -1); + return nReturn; +} + +FontDescriptor getDefaultFont() +{ + FontDescriptor aReturn; + aReturn.Slant = FontSlant_DONTKNOW; + aReturn.Underline = FontUnderline::DONTKNOW; + aReturn.Strikeout = com::sun::star::awt::FontStrikeout::DONTKNOW; + return aReturn; +} + +bool isAssignableFrom(const Type& _rAssignable, const Type& _rFrom) +{ + // get the type lib descriptions + typelib_TypeDescription* pAssignable = nullptr; + _rAssignable.getDescription(&pAssignable); + + typelib_TypeDescription* pFrom = nullptr; + _rFrom.getDescription(&pFrom); + + // and ask the type lib + return typelib_typedescription_isAssignableFrom(pAssignable, pFrom); +} + +Type getSequenceElementType(const Type& _rSequenceType) +{ + OSL_ENSURE(_rSequenceType.getTypeClass() == TypeClass_SEQUENCE, + "getSequenceElementType: must be called with a sequence type!"); + + if (_rSequenceType.getTypeClass() != TypeClass_SEQUENCE) + return Type(); + + TypeDescription aTD(_rSequenceType); + typelib_IndirectTypeDescription* pSequenceTD + = reinterpret_cast<typelib_IndirectTypeDescription*>(aTD.get()); + + OSL_ASSERT(pSequenceTD && pSequenceTD->pType); + if (pSequenceTD && pSequenceTD->pType) + return Type(pSequenceTD->pType); + + return Type(); +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/weak.cxx b/comphelper/source/misc/weak.cxx new file mode 100644 index 000000000..02cf40e2b --- /dev/null +++ b/comphelper/source/misc/weak.cxx @@ -0,0 +1,57 @@ +/* -*- 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 <comphelper/weak.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; + +namespace comphelper +{ + +OWeakTypeObject::OWeakTypeObject() +{ +} + +OWeakTypeObject::~OWeakTypeObject() +{ +} + +Any SAL_CALL OWeakTypeObject::queryInterface(const Type & rType ) +{ + if( rType == cppu::UnoType<XTypeProvider>::get() ) + return Any( Reference< XTypeProvider >(this) ); + else + return ::cppu::OWeakObject::queryInterface( rType ); +} + +Sequence< Type > SAL_CALL OWeakTypeObject::getTypes( ) +{ + return Sequence< Type >(); +} + +Sequence< ::sal_Int8 > SAL_CALL OWeakTypeObject::getImplementationId( ) +{ + return Sequence< ::sal_Int8 >(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/weakeventlistener.cxx b/comphelper/source/misc/weakeventlistener.cxx new file mode 100644 index 000000000..e8ce46590 --- /dev/null +++ b/comphelper/source/misc/weakeventlistener.cxx @@ -0,0 +1,74 @@ +/* -*- 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 <comphelper/weakeventlistener.hxx> +#include <osl/diagnose.h> + + +namespace comphelper +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + + OWeakListenerAdapterBase::~OWeakListenerAdapterBase() + { + } + + OWeakEventListenerAdapter::OWeakEventListenerAdapter( Reference< XWeak > const & _rxListener, Reference< XComponent > const & _rxBroadcaster ) + :OWeakEventListenerAdapter_Base( _rxListener, _rxBroadcaster ) + { + // add ourself as listener to the broadcaster + OSL_ENSURE( _rxBroadcaster.is(), "OWeakEventListenerAdapter::OWeakEventListenerAdapter: invalid broadcaster!" ); + if ( _rxBroadcaster.is() ) + { + osl_atomic_increment( &m_refCount ); + { + _rxBroadcaster->addEventListener( this ); + } + osl_atomic_decrement( &m_refCount ); + OSL_ENSURE( m_refCount > 0, "OWeakEventListenerAdapter::OWeakEventListenerAdapter: oops - not to be used with implementations which hold their listeners weak!" ); + // the one and only reason for this adapter class (A) is to add as listener to a component (C) which + // holds its listeners hard, and forward all calls then to another listener (L) which is + // held weak by A. + // Now if C holds listeners weak, then we do not need A, we can add L directly to C. + } + + OSL_ENSURE( getListener().is(), "OWeakEventListenerAdapter::OWeakEventListenerAdapter: invalid listener (does not support the XEventListener interface)!" ); + } + + + void SAL_CALL OWeakEventListenerAdapter::disposing( ) + { + Reference< XComponent > xBroadcaster( getBroadcaster( ), UNO_QUERY ); + OSL_ENSURE( xBroadcaster.is(), "OWeakEventListenerAdapter::disposing: broadcaster is invalid in the meantime! How this?" ); + if ( xBroadcaster.is() ) + { + xBroadcaster->removeEventListener( this ); + } + + resetListener(); + } + + +} // namespace comphelper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/xmlsechelper.cxx b/comphelper/source/misc/xmlsechelper.cxx new file mode 100644 index 000000000..5b1a438ab --- /dev/null +++ b/comphelper/source/misc/xmlsechelper.cxx @@ -0,0 +1,319 @@ +/* -*- 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 <comphelper/xmlsechelper.hxx> + +#include <rtl/ustrbuf.hxx> +#include <osl/diagnose.h> +#include <o3tl/string_view.hxx> + +#include <utility> +#include <vector> + +namespace comphelper::xmlsec +{ + OUString GetCertificateKind( const css::security::CertificateKind &rKind ) + { + switch (rKind) + { + case css::security::CertificateKind_X509: + return "X.509"; + case css::security::CertificateKind_OPENPGP: + return "OpenPGP"; + default: + return OUString(); + } + } + + /* + Creates two strings based on the distinguished name which are displayed in the + certificate details view. The first string contains only the values of the attribute + and values pairs, which are separated by commas. All escape characters ('"') are + removed. + The second string is for the details view at the bottom. It shows the attribute/value + pairs on different lines. All escape characters ('"') are removed. + */ + std::pair< OUString, OUString> GetDNForCertDetailsView( std::u16string_view rRawString) + { + std::vector< std::pair< OUString, OUString > > vecAttrValueOfDN = parseDN(rRawString); + OUStringBuffer s1, s2; + for (auto i = vecAttrValueOfDN.cbegin(); i < vecAttrValueOfDN.cend(); ++i) + { + if (i != vecAttrValueOfDN.cbegin()) + { + s1.append(','); + s2.append('\n'); + } + s1.append(i->second); + s2.append(i->first + " = " + i->second); + } + return std::make_pair(s1.makeStringAndClear(), s2.makeStringAndClear()); + } + +/* + Whenever the attribute value contains special characters, such as '"' or ',' (without '') + then the value will be enclosed in double quotes by the respective Windows or NSS function + which we use to retrieve, for example, the subject name. If double quotes appear in the value then + they are escaped with a double quote. This function removes the escape characters. +*/ +#ifdef _WIN32 +std::vector< std::pair< OUString, OUString> > parseDN(std::u16string_view rRawString) +{ + std::vector< std::pair<OUString, OUString> > retVal; + bool bInEscape = false; + bool bInValue = false; + bool bInType = true; + sal_Int32 nTypeNameStart = 0; + std::u16string_view sType; + OUStringBuffer sbufValue; + size_t length = rRawString.size(); + + for (size_t i = 0; i < length; i++) + { + sal_Unicode c = rRawString[i]; + + if (c == '=') + { + if (! bInValue) + { + sType = rRawString.substr(nTypeNameStart, i - nTypeNameStart); + sType = o3tl::trim(sType); + bInType = false; + } + else + { + sbufValue.append(c); + } + } + else if (c == '"') + { + if (!bInEscape) + { + //If this is the quote is the first of the couple which enclose the + //whole value, because the value contains special characters + //then we just drop it. That is, this character must be followed by + //a character which is not '"'. + if ( i + 1 < length && rRawString[i+1] == '"') + bInEscape = true; + else + bInValue = !bInValue; //value is enclosed in " " + } + else + { + //This quote is escaped by a preceding quote and therefore is + //part of the value + sbufValue.append(c); + bInEscape = false; + } + } + else if (c == ',' || c == '+') + { + //The comma separate the attribute value pairs. + //If the comma is not part of a value (the value would then be enclosed in '"'), + //then we have reached the end of the value + if (!bInValue) + { + OSL_ASSERT(!sType.empty()); + retVal.push_back(std::make_pair(OUString(sType), sbufValue.makeStringAndClear())); + sType = {}; + //The next char is the start of the new type + nTypeNameStart = i + 1; + bInType = true; + } + else + { + //The whole string is enclosed because it contains special characters. + //The enclosing '"' are not part of certificate but will be added by + //the function (Windows or NSS) which retrieves DN + sbufValue.append(c); + } + } + else + { + if (!bInType) + sbufValue.append(c); + } + } + if (sbufValue.getLength()) + { + OSL_ASSERT(!sType.empty()); + retVal.push_back(std::make_pair(OUString(sType), sbufValue.makeStringAndClear())); + } + return retVal; + } +#else +std::vector< std::pair< OUString, OUString> > parseDN(std::u16string_view rRawString) + { + std::vector< std::pair<OUString, OUString> > retVal; + //bInEscape == true means that the preceding character is an escape character + bool bInEscape = false; + bool bInValue = false; + bool bInType = true; + sal_Int32 nTypeNameStart = 0; + std::u16string_view sType; + OUStringBuffer sbufValue; + size_t length = rRawString.size(); + + for (size_t i = 0; i < length; i++) + { + sal_Unicode c = rRawString[i]; + + if (c == '=') + { + if (! bInValue) + { + sType = rRawString.substr(nTypeNameStart, i - nTypeNameStart); + sType = o3tl::trim(sType); + bInType = false; + } + else + { + sbufValue.append(c); + } + } + else if (c == '\\') + { + if (!bInEscape) + { + bInEscape = true; + } + else + { // bInEscape is true + sbufValue.append(c); + bInEscape = false; + } + } + else if (c == '"') + { + //an unescaped '"' is either at the beginning or end of the value + if (!bInEscape) + { + if ( !bInValue) + bInValue = true; + else if (bInValue) + bInValue = false; + } + else + { + //This quote is escaped by a preceding quote and therefore is + //part of the value + sbufValue.append(c); + bInEscape = false; + } + } + else if (c == ',' || c == '+') + { + //The comma separate the attribute value pairs. + //If the comma is not part of a value (the value would then be enclosed in '"'), + //then we have reached the end of the value + if (!bInValue) + { + OSL_ASSERT(!sType.empty()); + retVal.emplace_back(sType, sbufValue.makeStringAndClear()); + sType = {}; + //The next char is the start of the new type + nTypeNameStart = i + 1; + bInType = true; + } + else + { + //The whole string is enclosed because it contains special characters. + //The enclosing '"' are not part of certificate but will be added by + //the function (Windows or NSS) which retrieves DN + sbufValue.append(c); + } + } + else + { + if (!bInType) + { + sbufValue.append(c); + bInEscape = false; + } + } + } + if (!sbufValue.isEmpty()) + { + OSL_ASSERT(!sType.empty()); + retVal.emplace_back(sType, sbufValue.makeStringAndClear()); + } + return retVal; + } + +#endif + + OUString GetContentPart( const OUString& _rRawString, const css::security::CertificateKind &rKind ) + { + char const * aIDs[] = { "CN", "OU", "O", "E", nullptr }; + + // tdf#131733 Don't process OpenPGP certs, only X509 + if (rKind == css::security::CertificateKind_OPENPGP ) + return _rRawString; + + OUString retVal; + int i = 0; + std::vector< std::pair< OUString, OUString > > vecAttrValueOfDN = parseDN(_rRawString); + while ( aIDs[i] ) + { + OUString sPartId = OUString::createFromAscii( aIDs[i++] ); + auto idn = std::find_if(vecAttrValueOfDN.cbegin(), vecAttrValueOfDN.cend(), + [&sPartId](const std::pair< OUString, OUString >& dn) { return dn.first == sPartId; }); + if (idn != vecAttrValueOfDN.cend()) + retVal = idn->second; + if (!retVal.isEmpty()) + break; + } + return retVal.isEmpty() ? _rRawString : retVal; + } + + OUString GetHexString( const css::uno::Sequence< sal_Int8 >& _rSeq, const char* _pSep, sal_uInt16 _nLineBreak ) + { + const sal_Int8* pSerNumSeq = _rSeq.getConstArray(); + int nCnt = _rSeq.getLength(); + OUStringBuffer aStr; + const char pHexDigs[ 17 ] = "0123456789ABCDEF"; + char pBuffer[ 3 ] = " "; + sal_uInt8 nNum; + sal_uInt16 nBreakStart = _nLineBreak? _nLineBreak : 1; + sal_uInt16 nBreak = nBreakStart; + for( int i = 0 ; i < nCnt ; ++i ) + { + nNum = sal_uInt8( pSerNumSeq[ i ] ); + + // exchange the buffer[0] and buffer[1], which make it consistent with Mozilla and Windows + pBuffer[ 1 ] = pHexDigs[ nNum & 0x0F ]; + nNum >>= 4; + pBuffer[ 0 ] = pHexDigs[ nNum ]; + aStr.appendAscii( pBuffer ); + + --nBreak; + if( nBreak ) + aStr.appendAscii( _pSep ); + else + { + nBreak = nBreakStart; + aStr.append( '\n' ); + } + } + + return aStr.makeStringAndClear(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |