/* -*- 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_features.h>
 
#include <sal/config.h>
#include <sal/log.hxx>
 
#if HAVE_FEATURE_MACOSX_SANDBOX
#include <sys/stat.h>
#endif
 
#include <com/sun/star/beans/IllegalTypeException.hpp>
#include <com/sun/star/beans/NotRemoveableException.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/beans/PropertyExistException.hpp>
#include <com/sun/star/io/BufferSizeExceededException.hpp>
#include <com/sun/star/io/NotConnectedException.hpp>
#include <com/sun/star/io/IOException.hpp>
#include <com/sun/star/lang/IllegalAccessException.hpp>
#include <com/sun/star/task/InteractionClassification.hpp>
#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
#include <com/sun/star/ucb/DuplicateCommandIdentifierException.hpp>
#include <com/sun/star/ucb/IOErrorCode.hpp>
#include <com/sun/star/ucb/InsertCommandArgument.hpp>
#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
#include <com/sun/star/ucb/NameClash.hpp>
#include <com/sun/star/ucb/OpenCommandArgument.hpp>
#include <com/sun/star/ucb/Store.hpp>
#include <com/sun/star/ucb/TransferInfo.hpp>
#include <comphelper/propertysequence.hxx>
#include <osl/diagnose.h>
#include <rtl/ref.hxx>
#include <rtl/uri.hxx>
 
#include "filtask.hxx"
#include "filcmd.hxx"
#include "filglob.hxx"
#include "filinpstr.hxx"
#include "filprp.hxx"
#include "filrset.hxx"
#include "filstr.hxx"
#include "prov.hxx"
 
/******************************************************************************/
/*                                                                            */
/*                              TaskHandling                                  */
/*                                                                            */
/******************************************************************************/
 
 
using namespace fileaccess;
using namespace com::sun::star;
using namespace com::sun::star::uno;
using namespace com::sun::star::ucb;
 
#if OSL_DEBUG_LEVEL > 0
#define THROW_WHERE SAL_WHERE
#else
#define THROW_WHERE ""
#endif
 
TaskManager::UnqPathData::UnqPathData() = default;
 
TaskManager::UnqPathData::UnqPathData(TaskManager::UnqPathData&&) = default;
 
 
TaskManager::UnqPathData::~UnqPathData()
{
}
 
TaskManager::MyProperty::MyProperty( const OUString&                         thePropertyName )
    : PropertyName( thePropertyName )
    , Handle(-1)
    , isNative(false)
    , State(beans::PropertyState_AMBIGUOUS_VALUE)
    , Attributes(0)
{
    // empty
}
 
TaskManager::MyProperty::MyProperty( bool                               theisNative,
                               const OUString&                    thePropertyName,
                               sal_Int32                          theHandle,
                               const css::uno::Type&              theTyp,
                               const css::uno::Any&               theValue,
                               const css::beans::PropertyState&   theState,
                               sal_Int16                          theAttributes )
    : PropertyName( thePropertyName ),
      Handle( theHandle ),
      isNative( theisNative ),
      Typ( theTyp ),
      Value( theValue ),
      State( theState ),
      Attributes( theAttributes )
{
    // empty
}
 
#include "filinl.hxx"
 
        // Default properties
 
constexpr OUString Title( u"Title"_ustr );
constexpr OUString CasePreservingURL( u"CasePreservingURL"_ustr );
constexpr OUString IsDocument( u"IsDocument"_ustr );
constexpr OUString IsFolder( u"IsFolder"_ustr );
constexpr OUString DateModified( u"DateModified"_ustr );
constexpr OUString Size( u"Size"_ustr );
constexpr OUString IsVolume( u"IsVolume"_ustr );
constexpr OUString IsRemoveable( u"IsRemoveable"_ustr );
constexpr OUString IsRemote( u"IsRemote"_ustr );
constexpr OUString IsCompactDisc( u"IsCompactDisc"_ustr );
constexpr OUString IsFloppy( u"IsFloppy"_ustr );
constexpr OUString IsHidden( u"IsHidden"_ustr );
constexpr OUString ContentType( u"ContentType"_ustr );
constexpr OUString IsReadOnly( u"IsReadOnly"_ustr );
constexpr OUString CreatableContentsInfo( u"CreatableContentsInfo"_ustr );
 
TaskManager::TaskManager( const uno::Reference< uno::XComponentContext >& rxContext,
              FileProvider* pProvider, bool bWithConfig )
    : m_nCommandId( 0 ),
      m_pProvider( pProvider ),
      m_xContext( rxContext ),
      // Commands
      m_sCommandInfo{
          { /* Name    */ u"getCommandInfo"_ustr,
            /* Handle  */ -1,
            /* ArgType */ cppu::UnoType<void>::get() },
 
          { /* Name    */ u"getPropertySetInfo"_ustr,
            /* Handle  */ -1,
            /* ArgType */ cppu::UnoType<void>::get() },
 
          { /* Name    */ u"getPropertyValues"_ustr,
            /* Handle  */ -1,
            /* ArgType */ cppu::UnoType<uno::Sequence< beans::Property >>::get() },
 
          { /* Name    */ u"setPropertyValues"_ustr,
            /* Handle  */ -1,
            /* ArgType */ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get() },
 
          { /* Name    */ u"open"_ustr,
            /* Handle  */ -1,
            /* ArgType */ cppu::UnoType<OpenCommandArgument>::get() },
 
          { /* Name    */ u"transfer"_ustr,
            /* Handle  */ -1,
            /* ArgType */ cppu::UnoType<TransferInfo>::get() },
 
          { /* Name    */ u"delete"_ustr,
            /* Handle  */ -1,
            /* ArgType */ cppu::UnoType<sal_Bool>::get() },
 
          { /* Name    */ u"insert"_ustr,
            /* Handle  */ -1,
            /* ArgType */ cppu::UnoType<InsertCommandArgument>::get() },
 
          { /* Name    */ u"createNewContent"_ustr,
            /* Handle  */ -1,
            /* ArgType */ cppu::UnoType<ucb::ContentInfo>::get() } }
{
    // Title
    m_aDefaultProperties.insert( MyProperty( true,
                                             Title,
                                             -1 ,
                                             cppu::UnoType<OUString>::get(),
                                             uno::Any(),
                                             beans::PropertyState_DEFAULT_VALUE,
                                             beans::PropertyAttribute::MAYBEVOID
                                             | beans::PropertyAttribute::BOUND ) );
 
    // CasePreservingURL
    m_aDefaultProperties.insert(
        MyProperty( true,
                    CasePreservingURL,
                    -1 ,
                    cppu::UnoType<OUString>::get(),
                    uno::Any(),
                    beans::PropertyState_DEFAULT_VALUE,
                    beans::PropertyAttribute::MAYBEVOID
                    | beans::PropertyAttribute::BOUND
                    | beans::PropertyAttribute::READONLY ) );
 
 
    // IsFolder
    m_aDefaultProperties.insert( MyProperty( true,
                                             IsFolder,
                                             -1 ,
                                             cppu::UnoType<sal_Bool>::get(),
                                             uno::Any(),
                                             beans::PropertyState_DEFAULT_VALUE,
                                             beans::PropertyAttribute::MAYBEVOID
                                             | beans::PropertyAttribute::BOUND
                                             | beans::PropertyAttribute::READONLY ) );
 
 
    // IsDocument
    m_aDefaultProperties.insert( MyProperty( true,
                                             IsDocument,
                                             -1 ,
                                             cppu::UnoType<sal_Bool>::get(),
                                             uno::Any(),
                                             beans::PropertyState_DEFAULT_VALUE,
                                             beans::PropertyAttribute::MAYBEVOID
                                             | beans::PropertyAttribute::BOUND
                                             | beans::PropertyAttribute::READONLY ) );
 
    // Removable
    m_aDefaultProperties.insert( MyProperty( true,
                                             IsVolume,
                                             -1 ,
                                             cppu::UnoType<sal_Bool>::get(),
                                             uno::Any(),
                                             beans::PropertyState_DEFAULT_VALUE,
                                             beans::PropertyAttribute::MAYBEVOID
                                             | beans::PropertyAttribute::BOUND
                                             | beans::PropertyAttribute::READONLY ) );
 
 
    // Removable
    m_aDefaultProperties.insert( MyProperty( true,
                                             IsRemoveable,
                                             -1 ,
                                             cppu::UnoType<sal_Bool>::get(),
                                             uno::Any(),
                                             beans::PropertyState_DEFAULT_VALUE,
                                             beans::PropertyAttribute::MAYBEVOID
                                             | beans::PropertyAttribute::BOUND
                                             | beans::PropertyAttribute::READONLY ) );
 
    // Remote
    m_aDefaultProperties.insert( MyProperty( true,
                                             IsRemote,
                                             -1 ,
                                             cppu::UnoType<sal_Bool>::get(),
                                             uno::Any(),
                                             beans::PropertyState_DEFAULT_VALUE,
                                             beans::PropertyAttribute::MAYBEVOID
                                             | beans::PropertyAttribute::BOUND
                                             | beans::PropertyAttribute::READONLY ) );
 
    // CompactDisc
    m_aDefaultProperties.insert( MyProperty( true,
                                             IsCompactDisc,
                                             -1 ,
                                             cppu::UnoType<sal_Bool>::get(),
                                             uno::Any(),
                                             beans::PropertyState_DEFAULT_VALUE,
                                             beans::PropertyAttribute::MAYBEVOID
                                             | beans::PropertyAttribute::BOUND
                                             | beans::PropertyAttribute::READONLY ) );
 
    // Floppy
    m_aDefaultProperties.insert( MyProperty( true,
                                             IsFloppy,
                                             -1 ,
                                             cppu::UnoType<sal_Bool>::get(),
                                             uno::Any(),
                                             beans::PropertyState_DEFAULT_VALUE,
                                             beans::PropertyAttribute::MAYBEVOID
                                             | beans::PropertyAttribute::BOUND
                                             | beans::PropertyAttribute::READONLY ) );
 
    // Hidden
    m_aDefaultProperties.insert(
        MyProperty(
            true,
            IsHidden,
            -1 ,
            cppu::UnoType<sal_Bool>::get(),
            uno::Any(),
            beans::PropertyState_DEFAULT_VALUE,
            beans::PropertyAttribute::MAYBEVOID
            | beans::PropertyAttribute::BOUND
#if defined(_WIN32)
        ));
#else
    | beans::PropertyAttribute::READONLY)); // under unix/linux only readable
#endif
 
 
    // ContentType
    m_aDefaultProperties.insert( MyProperty( false,
                                             ContentType,
                                             -1 ,
                                             cppu::UnoType<OUString>::get(),
                                             uno::Any(OUString()),
                                             beans::PropertyState_DEFAULT_VALUE,
                                             beans::PropertyAttribute::MAYBEVOID
                                             | beans::PropertyAttribute::BOUND
                                             | beans::PropertyAttribute::READONLY ) );
 
 
    // DateModified
    m_aDefaultProperties.insert( MyProperty( true,
                                             DateModified,
                                             -1 ,
                                             cppu::UnoType<util::DateTime>::get(),
                                             uno::Any(),
                                             beans::PropertyState_DEFAULT_VALUE,
                                             beans::PropertyAttribute::MAYBEVOID
                                             | beans::PropertyAttribute::BOUND ) );
 
    // Size
    m_aDefaultProperties.insert( MyProperty( true,
                                             Size,
                                             -1,
                                             cppu::UnoType<sal_Int64>::get(),
                                             uno::Any(),
                                             beans::PropertyState_DEFAULT_VALUE,
                                             beans::PropertyAttribute::MAYBEVOID
                                             | beans::PropertyAttribute::BOUND ) );
 
    // IsReadOnly
    m_aDefaultProperties.insert( MyProperty( true,
                                             IsReadOnly,
                                             -1 ,
                                             cppu::UnoType<sal_Bool>::get(),
                                             uno::Any(),
                                             beans::PropertyState_DEFAULT_VALUE,
                                             beans::PropertyAttribute::MAYBEVOID
                                             | beans::PropertyAttribute::BOUND ) );
 
 
    // CreatableContentsInfo
    m_aDefaultProperties.insert( MyProperty( true,
                                             CreatableContentsInfo,
                                             -1 ,
                                             cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
                                             uno::Any(),
                                             beans::PropertyState_DEFAULT_VALUE,
                                             beans::PropertyAttribute::MAYBEVOID
                                             | beans::PropertyAttribute::BOUND
                                             | beans::PropertyAttribute::READONLY ) );
 
    if(bWithConfig)
    {
        uno::Reference< XPropertySetRegistryFactory > xRegFac = ucb::Store::create( m_xContext );
        // Open/create a registry
        m_xFileRegistry = xRegFac->createPropertySetRegistry( OUString() );
    }
}
 
 
TaskManager::~TaskManager()
{
}
 
 
void
TaskManager::startTask(
    sal_Int32 CommandId,
    const uno::Reference< XCommandEnvironment >& xCommandEnv )
{
    std::unique_lock aGuard( m_aMutex );
    TaskMap::iterator it = m_aTaskMap.find( CommandId );
    if( it != m_aTaskMap.end() )
    {
        throw DuplicateCommandIdentifierException( OSL_LOG_PREFIX );
    }
    m_aTaskMap.emplace( CommandId, TaskHandling( xCommandEnv ));
}
 
 
void
TaskManager::endTask( sal_Int32 CommandId,
                      const OUString& aUncPath,
                      BaseContent* pContent)
{
    std::unique_lock aGuard( m_aMutex );
    TaskMap::iterator it = m_aTaskMap.find( CommandId );
    if( it == m_aTaskMap.end() )
        return;
 
    TaskHandlerErr ErrorCode = it->second.getInstalledError();
    sal_Int32 MinorCode = it->second.getMinorErrorCode();
    bool isHandled = it->second.isHandled();
 
    Reference< XCommandEnvironment > xComEnv
        = it->second.getCommandEnvironment();
 
    m_aTaskMap.erase( it );
 
    aGuard.unlock();
 
    if( ErrorCode != TaskHandlerErr::NO_ERROR )
        throw_handler(
            ErrorCode,
            MinorCode,
            xComEnv,
            aUncPath,
            pContent,
            isHandled);
}
 
 
void TaskManager::clearError( sal_Int32 CommandId )
{
    std::unique_lock aGuard( m_aMutex );
    TaskMap::iterator it = m_aTaskMap.find( CommandId );
    if( it != m_aTaskMap.end() )
        it->second.clearError();
}
 
 
void TaskManager::retrieveError( sal_Int32 CommandId,
                                          TaskHandlerErr &ErrorCode,
                                          sal_Int32 &minorCode)
{
    std::unique_lock aGuard( m_aMutex );
    TaskMap::iterator it = m_aTaskMap.find( CommandId );
    if( it != m_aTaskMap.end() )
    {
        ErrorCode = it->second.getInstalledError();
        minorCode = it->second. getMinorErrorCode();
    }
}
 
 
void TaskManager::installError( sal_Int32 CommandId,
                                         TaskHandlerErr ErrorCode,
                                         sal_Int32 MinorCode )
{
    std::unique_lock aGuard( m_aMutex );
    TaskMap::iterator it = m_aTaskMap.find( CommandId );
    if( it != m_aTaskMap.end() )
        it->second.installError( ErrorCode,MinorCode );
}
 
 
sal_Int32
TaskManager::getCommandId()
{
    std::unique_lock aGuard( m_aMutex );
    return ++m_nCommandId;
}
 
 
void TaskManager::handleTask(
    sal_Int32 CommandId,
    const uno::Reference< task::XInteractionRequest >& request )
{
    std::unique_lock aGuard( m_aMutex );
    TaskMap::iterator it = m_aTaskMap.find( CommandId );
    uno::Reference< task::XInteractionHandler > xInt;
    if( it != m_aTaskMap.end() )
    {
        xInt = it->second.getInteractionHandler();
        if( xInt.is() )
            xInt->handle( request );
        it->second.setHandled();
    }
}
 
/*********************************************************************************/
/*                                                                               */
/*                     de/registerNotifier-Implementation                        */
/*                                                                               */
/*********************************************************************************/
 
 
//  This two methods register and deregister a change listener for the content belonging
//  to URL aUnqPath
 
 
void
TaskManager::registerNotifier( const OUString& aUnqPath, Notifier* pNotifier )
{
    std::unique_lock aGuard( m_aMutex );
 
    ContentMap::iterator it =
        m_aContent.emplace( aUnqPath, UnqPathData() ).first;
 
    std::vector< Notifier* >& nlist = it->second.notifier;
 
    std::vector<Notifier*>::iterator it1 = std::find(nlist.begin(), nlist.end(), pNotifier);
    if( it1 != nlist.end() )               // Every "Notifier" only once
    {
        return;
    }
    nlist.push_back( pNotifier );
}
 
 
void
TaskManager::deregisterNotifier( const OUString& aUnqPath,Notifier* pNotifier )
{
    std::unique_lock aGuard( m_aMutex );
 
    ContentMap::iterator it = m_aContent.find( aUnqPath );
    if( it == m_aContent.end() )
        return;
 
    std::erase(it->second.notifier, pNotifier);
 
    if( it->second.notifier.empty() )
        m_aContent.erase( it );
}
 
 
/*********************************************************************************/
/*                                                                               */
/*                     de/associate-Implementation                               */
/*                                                                               */
/*********************************************************************************/
 
//  Used to associate and deassociate a new property with
//  the content belonging to URL UnqPath.
//  The default value and the attributes are input
 
 
void
TaskManager::associate( const OUString& aUnqPath,
                  const OUString& PropertyName,
                  const uno::Any& DefaultValue,
                  const sal_Int16 Attributes )
{
    MyProperty newProperty( false,
                            PropertyName,
                            -1,
                            DefaultValue.getValueType(),
                            DefaultValue,
                            beans::PropertyState_DEFAULT_VALUE,
                            Attributes );
 
    auto it1 = m_aDefaultProperties.find( newProperty );
    if( it1 != m_aDefaultProperties.end() )
        throw beans::PropertyExistException( THROW_WHERE );
 
    {
        std::unique_lock aGuard( m_aMutex );
 
        ContentMap::iterator it = m_aContent.emplace( aUnqPath,UnqPathData() ).first;
 
        // Load the XPersistentPropertySetInfo and create it, if it does not exist
        load( it,true );
 
        PropertySet& properties = it->second.properties;
        it1 = properties.find( newProperty );
        if( it1 != properties.end() )
            throw beans::PropertyExistException(THROW_WHERE );
 
        // Property does not exist
        properties.insert( newProperty );
        it->second.xC->addProperty( PropertyName,Attributes,DefaultValue );
    }
    notifyPropertyAdded( getPropertySetListeners( aUnqPath ), PropertyName );
}
 
 
void
TaskManager::deassociate( const OUString& aUnqPath,
            const OUString& PropertyName )
{
    MyProperty oldProperty( PropertyName );
 
    auto it1 = m_aDefaultProperties.find( oldProperty );
    if( it1 != m_aDefaultProperties.end() )
        throw beans::NotRemoveableException( THROW_WHERE );
 
    std::unique_lock aGuard( m_aMutex );
 
    ContentMap::iterator it = m_aContent.emplace( aUnqPath,UnqPathData() ).first;
 
    load( it, false );
 
    PropertySet& properties = it->second.properties;
 
    it1 = properties.find( oldProperty );
    if( it1 == properties.end() )
        throw beans::UnknownPropertyException( PropertyName );
 
    properties.erase( it1 );
 
    if( it->second.xC.is() )
        it->second.xC->removeProperty( PropertyName );
 
    if( properties.size() == 9 )
    {
        MyProperty ContentTProperty( ContentType );
 
        if( properties.find( ContentTProperty )->getState() == beans::PropertyState_DEFAULT_VALUE )
        {
            it->second.xS = nullptr;
            it->second.xC = nullptr;
            it->second.xA = nullptr;
            if(m_xFileRegistry.is())
                m_xFileRegistry->removePropertySet( aUnqPath );
        }
    }
    aGuard.unlock();
    notifyPropertyRemoved( getPropertySetListeners( aUnqPath ), PropertyName );
}
 
 
/*********************************************************************************/
/*                                                                               */
/*                     page-Implementation                                       */
/*                                                                               */
/*********************************************************************************/
 
//  Given an xOutputStream, this method writes the content of the file belonging to
//  URL aUnqPath into the XOutputStream
 
 
void TaskManager::page( sal_Int32 CommandId,
                           const OUString& aUnqPath,
                           const uno::Reference< io::XOutputStream >& xOutputStream )
{
    osl::File aFile( aUnqPath );
    osl::FileBase::RC err = aFile.open( osl_File_OpenFlag_Read );
 
    if( err != osl::FileBase::E_None )
    {
        aFile.close();
        installError( CommandId,
                      TaskHandlerErr::OPEN_FILE_FOR_PAGING,
                      err );
        return;
    }
 
    const sal_uInt64 bfz = 4*1024;
    sal_Int8 BFF[bfz];
    sal_uInt64 nrc;  // Retrieved number of Bytes;
 
    do
    {
        err = aFile.read( static_cast<void*>(BFF),bfz,nrc );
        if(  err == osl::FileBase::E_None )
        {
            // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
            uno::Sequence< sal_Int8 > seq( BFF, static_cast<sal_uInt32>(nrc) );
            try
            {
                xOutputStream->writeBytes( seq );
            }
            catch (const io::NotConnectedException&)
            {
                installError( CommandId,
                              TaskHandlerErr::NOTCONNECTED_FOR_PAGING );
                break;
            }
            catch (const io::BufferSizeExceededException&)
            {
                installError( CommandId,
                              TaskHandlerErr::BUFFERSIZEEXCEEDED_FOR_PAGING );
                break;
            }
            catch (const io::IOException&)
            {
                installError( CommandId,
                              TaskHandlerErr::IOEXCEPTION_FOR_PAGING );
                break;
            }
        }
        else
        {
            installError( CommandId,
                          TaskHandlerErr::READING_FILE_FOR_PAGING,
                          err );
            break;
        }
    } while( nrc == bfz );
 
 
    aFile.close();
 
 
    try
    {
        xOutputStream->closeOutput();
    }
    catch (const io::NotConnectedException&)
    {
    }
    catch (const io::BufferSizeExceededException&)
    {
    }
    catch (const io::IOException&)
    {
    }
}
 
 
/*********************************************************************************/
/*                                                                               */
/*                     open-Implementation                                       */
/*                                                                               */
/*********************************************************************************/
 
//  Given a file URL aUnqPath, this methods returns a XInputStream which reads from the open file.
 
 
uno::Reference< io::XInputStream >
TaskManager::open( sal_Int32 CommandId,
             const OUString& aUnqPath,
             bool bLock )
{
    rtl::Reference<XInputStream_impl> pInputStream(new XInputStream_impl( aUnqPath, bLock )); // from filinpstr.hxx
 
    TaskHandlerErr ErrorCode = pInputStream->CtorSuccess();
 
    if( ErrorCode != TaskHandlerErr::NO_ERROR )
    {
        installError( CommandId,
                      ErrorCode,
                      pInputStream->getMinorError() );
 
        pInputStream.clear();
    }
 
    return pInputStream;
}
 
 
/*********************************************************************************/
/*                                                                               */
/*                     open for read/write access-Implementation                 */
/*                                                                               */
/*********************************************************************************/
 
//  Given a file URL aUnqPath, this methods returns a XStream which can be used
//  to read and write from/to the file.
 
 
uno::Reference< io::XStream >
TaskManager::open_rw( sal_Int32 CommandId,
                const OUString& aUnqPath,
                bool bLock )
{
    rtl::Reference<XStream_impl> pStream(new XStream_impl( aUnqPath, bLock ));  // from filstr.hxx
 
    TaskHandlerErr ErrorCode = pStream->CtorSuccess();
 
    if( ErrorCode != TaskHandlerErr::NO_ERROR )
    {
        installError( CommandId,
                      ErrorCode,
                      pStream->getMinorError() );
 
        pStream.clear();
    }
    return pStream;
}
 
 
/*********************************************************************************/
/*                                                                               */
/*                       ls-Implementation                                       */
/*                                                                               */
/*********************************************************************************/
 
//  This method returns the result set containing the children of the directory belonging
//  to file URL aUnqPath
 
 
uno::Reference< XDynamicResultSet >
TaskManager::ls( sal_Int32 CommandId,
           const OUString& aUnqPath,
           const sal_Int32 OpenMode,
           const uno::Sequence< beans::Property >& seq,
           const uno::Sequence< NumberedSortingInfo >& seqSort )
{
    rtl::Reference<XResultSet_impl> p(new XResultSet_impl( this,aUnqPath,OpenMode,seq,seqSort ));
 
    TaskHandlerErr ErrorCode = p->CtorSuccess();
 
    if( ErrorCode != TaskHandlerErr::NO_ERROR )
    {
        installError( CommandId,
                      ErrorCode,
                      p->getMinorError() );
 
        p.clear();
    }
 
    return p;
}
 
 
/*********************************************************************************/
/*                                                                               */
/*                          info_c implementation                                */
/*                                                                               */
/*********************************************************************************/
// Info for commands
 
uno::Reference< XCommandInfo >
TaskManager::info_c()
{
    return new XCommandInfo_impl( this );
}
 
 
/*********************************************************************************/
/*                                                                               */
/*                     info_p-Implementation                                     */
/*                                                                               */
/*********************************************************************************/
// Info for the properties
 
uno::Reference< beans::XPropertySetInfo >
TaskManager::info_p( const OUString& aUnqPath )
{
    std::unique_lock aGuard( m_aMutex );
    return new XPropertySetInfo_impl( this,aUnqPath );
}
 
 
/*********************************************************************************/
/*                                                                               */
/*                     setv-Implementation                                       */
/*                                                                               */
/*********************************************************************************/
 
//  Sets the values of the properties belonging to fileURL aUnqPath
 
 
uno::Sequence< uno::Any >
TaskManager::setv( const OUString& aUnqPath,
             const uno::Sequence< beans::PropertyValue >& values )
{
    std::unique_lock aGuard( m_aMutex );
 
    sal_Int32 propChanged = 0;
    uno::Sequence< uno::Any > ret( values.getLength() );
    auto retRange = asNonConstRange(ret);
    uno::Sequence< beans::PropertyChangeEvent > seqChanged( values.getLength() );
    auto seqChangedRange = asNonConstRange(seqChanged);
 
    TaskManager::ContentMap::iterator it = m_aContent.find( aUnqPath );
    PropertySet& properties = it->second.properties;
    TaskManager::PropertySet::const_iterator it1;
    uno::Any aAny;
 
    for( sal_Int32 i = 0; i < values.getLength(); ++i )
    {
        MyProperty toset( values[i].Name );
        it1 = properties.find( toset );
        if( it1 == properties.end() )
        {
            retRange[i] <<= beans::UnknownPropertyException( THROW_WHERE );
            continue;
        }
 
        aAny = it1->getValue();
        if( aAny == values[i].Value )
            continue;  // nothing needs to be changed
 
        if( it1->getAttributes() & beans::PropertyAttribute::READONLY )
        {
            retRange[i] <<= lang::IllegalAccessException( THROW_WHERE );
            continue;
        }
 
        seqChangedRange[ propChanged   ].PropertyName = values[i].Name;
        seqChangedRange[ propChanged   ].PropertyHandle   = -1;
        seqChangedRange[ propChanged   ].Further   = false;
        seqChangedRange[ propChanged   ].OldValue = aAny;
        seqChangedRange[ propChanged++ ].NewValue = values[i].Value;
 
        it1->setValue( values[i].Value );  // Put the new value into the local cash
 
        if( ! it1->IsNative() )
        {
            // Also put logical properties into storage
            if( !it->second.xS.is() )
                load( it, true );
 
            if( ( values[i].Name == ContentType ) &&
                it1->getState() == beans::PropertyState_DEFAULT_VALUE )
            {   // Special logic for ContentType
                //  09.07.01: Not reached anymore, because ContentType is readonly
                it1->setState( beans::PropertyState_DIRECT_VALUE );
                it->second.xC->addProperty( values[i].Name,
                                            beans::PropertyAttribute::MAYBEVOID,
                                            values[i].Value );
            }
 
            try
            {
                it->second.xS->setPropertyValue( values[i].Name,values[i].Value );
            }
            catch (const uno::Exception&e)
            {
                --propChanged; // unsuccessful setting
                retRange[i] <<= e;
            }
        }
        else
        {
            // native properties
            // Setting of physical file properties
            if( values[i].Name == Size )
            {
                sal_Int64 newSize = 0;
                if( values[i].Value >>= newSize )
                {   // valid value for the size
                    osl::File aFile(aUnqPath);
                    bool err =
                        aFile.open(osl_File_OpenFlag_Write) != osl::FileBase::E_None ||
                        aFile.setSize(sal_uInt64(newSize)) != osl::FileBase::E_None ||
                        aFile.close() != osl::FileBase::E_None;
 
                    if( err )
                    {
                        --propChanged; // unsuccessful setting
                        uno::Sequence<uno::Any> names(comphelper::InitAnyPropertySequence(
                        {
                            {"Uri", uno::Any(aUnqPath)}
                        }));
                        retRange[i] <<= InteractiveAugmentedIOException(
                            OUString(),
                            nullptr,
                            task::InteractionClassification_ERROR,
                            IOErrorCode_GENERAL,
                            names );
                    }
                }
                else
                    retRange[i] <<= beans::IllegalTypeException( THROW_WHERE );
            }
            else if(values[i].Name == IsReadOnly ||
                    values[i].Name == IsHidden)
            {
                bool value = false;
                if( values[i].Value >>= value )
                {
                    osl::DirectoryItem aDirItem;
                    osl::FileBase::RC err =
                        osl::DirectoryItem::get(aUnqPath,aDirItem);
                    sal_uInt64 nAttributes(0);
                    if(err == osl::FileBase::E_None)
                    {
                        osl::FileStatus aFileStatus(osl_FileStatus_Mask_Attributes);
                        err = aDirItem.getFileStatus(aFileStatus);
                        if(err == osl::FileBase::E_None &&
                           aFileStatus.isValid(osl_FileStatus_Mask_Attributes))
                            nAttributes = aFileStatus.getAttributes();
                    }
                    // now we have the attributes provided all went well.
                    if(err == osl::FileBase::E_None) {
                        if(values[i].Name == IsReadOnly)
                        {
                            nAttributes &= ~(osl_File_Attribute_OwnWrite |
                                             osl_File_Attribute_GrpWrite |
                                             osl_File_Attribute_OthWrite |
                                             osl_File_Attribute_ReadOnly);
                            if(value)
                                nAttributes |= osl_File_Attribute_ReadOnly;
                            else
                                nAttributes |= (
                                    osl_File_Attribute_OwnWrite |
                                    osl_File_Attribute_GrpWrite |
                                    osl_File_Attribute_OthWrite);
                        }
                        else if(values[i].Name == IsHidden)
                        {
                            nAttributes &= ~(osl_File_Attribute_Hidden);
                            if(value)
                                nAttributes |= osl_File_Attribute_Hidden;
                        }
                        err = osl::File::setAttributes(
                            aUnqPath,nAttributes);
                    }
 
                    if( err != osl::FileBase::E_None )
                    {
                        --propChanged; // unsuccessful setting
                        uno::Sequence<uno::Any> names(comphelper::InitAnyPropertySequence(
                        {
                            {"Uri", uno::Any(aUnqPath)}
                        }));
                        IOErrorCode ioError;
                        switch( err )
                        {
                        case osl::FileBase::E_NOMEM:
                            // not enough memory for allocating structures <br>
                            ioError = IOErrorCode_OUT_OF_MEMORY;
                            break;
                        case osl::FileBase::E_INVAL:
                            // the format of the parameters was not valid<p>
                            ioError = IOErrorCode_INVALID_PARAMETER;
                            break;
                        case osl::FileBase::E_NAMETOOLONG:
                            // File name too long<br>
                            ioError = IOErrorCode_NAME_TOO_LONG;
                            break;
                        case osl::FileBase::E_NOENT:
                            // No such file or directory<br>
                        case osl::FileBase::E_NOLINK:
                            // Link has been severed<br>
                            ioError = IOErrorCode_NOT_EXISTING;
                            break;
                        case osl::FileBase::E_ROFS:
                            // #i4735# handle ROFS transparently
                            // as ACCESS_DENIED
                        case  osl::FileBase::E_PERM:
                        case osl::FileBase::E_ACCES:
                            // permission denied<br>
                            ioError = IOErrorCode_ACCESS_DENIED;
                            break;
                        case osl::FileBase::E_LOOP:
                            // Too many symbolic links encountered<br>
                        case osl::FileBase::E_FAULT:
                            // Bad address<br>
                        case osl::FileBase::E_IO:
                            // I/O error<br>
                        case osl::FileBase::E_NOSYS:
                            // Function not implemented<br>
                        case osl::FileBase::E_MULTIHOP:
                            // Multihop attempted<br>
                        case osl::FileBase::E_INTR:
                            // function call was interrupted<p>
                        default:
                            ioError = IOErrorCode_GENERAL;
                            break;
                        }
                        retRange[i] <<= InteractiveAugmentedIOException(
                            OUString(),
                            nullptr,
                            task::InteractionClassification_ERROR,
                            ioError,
                            names );
                    }
                }
                else
                    retRange[i] <<= beans::IllegalTypeException( THROW_WHERE );
            }
        }
    }   // end for
 
    aGuard.unlock();
    if( propChanged )
    {
        seqChanged.realloc( propChanged );
        notifyPropertyChanges( getPropertyChangeNotifier( aUnqPath ), seqChanged );
    }
 
    return ret;
}
 
/*********************************************************************************/
/*                                                                               */
/*                     getv-Implementation                                       */
/*                                                                               */
/*********************************************************************************/
 
//  Reads the values of the properties belonging to fileURL aUnqPath;
//  Returns an XRow object containing the values in the requested order.
 
 
uno::Reference< sdbc::XRow >
TaskManager::getv( sal_Int32 CommandId,
             const OUString& aUnqPath,
             const uno::Sequence< beans::Property >& properties )
{
    uno::Sequence< uno::Any > seq( properties.getLength() );
 
    sal_Int32 n_Mask;
    getMaskFromProperties( n_Mask,properties );
    osl::FileStatus aFileStatus( n_Mask );
 
    osl::DirectoryItem aDirItem;
    osl::FileBase::RC nError1 = osl::DirectoryItem::get( aUnqPath,aDirItem );
    if( nError1 != osl::FileBase::E_None )
        installError(CommandId,
                     TaskHandlerErr::OPEN_FILE_FOR_PAGING, // BEAWARE, REUSED
                     nError1);
 
    osl::FileBase::RC nError2 = aDirItem.getFileStatus( aFileStatus );
    if( nError1 == osl::FileBase::E_None &&
        nError2 != osl::FileBase::E_None )
        installError(CommandId,
                     TaskHandlerErr::OPEN_FILE_FOR_PAGING, // BEAWARE, REUSED
                     nError2);
 
    {
        std::unique_lock aGuard( m_aMutex );
 
        TaskManager::ContentMap::iterator it = m_aContent.find( aUnqPath );
        commit( aGuard, it, aFileStatus );
 
        PropertySet& propset = it->second.properties;
 
        std::transform(properties.begin(), properties.end(), seq.getArray(),
            [&propset](const beans::Property& rProp) -> uno::Any {
                MyProperty readProp( rProp.Name );
                auto it1 = propset.find( readProp );
                if( it1 == propset.end() )
                    return uno::Any();
                return it1->getValue();
            });
    }
 
    return new XRow_impl( this,seq );
}
 
 
/********************************************************************************/
/*                                                                              */
/*                         transfer-commandos                                   */
/*                                                                              */
/********************************************************************************/
 
 
/********************************************************************************/
/*                                                                              */
/*                         move-implementation                                  */
/*                                                                              */
/********************************************************************************/
 
//  Moves the content belonging to fileURL srcUnqPath to fileURL dstUnqPath.
 
 
void
TaskManager::move( sal_Int32 CommandId,
             const OUString& srcUnqPath,
             const OUString& dstUnqPathIn,
             const sal_Int32 NameClash )
{
    // --> #i88446# Method notifyContentExchanged( getContentExchangedEventListeners( srcUnqPath,dstUnqPath,!isDocument ) ); crashes if
    // srcUnqPath and dstUnqPathIn are equal
    if( srcUnqPath == dstUnqPathIn )
        return;
 
    osl::FileBase::RC nError;
    OUString dstUnqPath( dstUnqPathIn );
 
    switch( NameClash )
    {
    case NameClash::KEEP:
        {
            nError = osl_File_move( srcUnqPath,dstUnqPath,true );
            if( nError != osl::FileBase::E_None && nError != osl::FileBase::E_EXIST )
            {
                installError( CommandId,
                              TaskHandlerErr::KEEPERROR_FOR_MOVE,
                              nError );
                return;
            }
            break;
        }
    case NameClash::OVERWRITE:
        {
            // stat to determine whether we have a symlink
            OUString targetPath(dstUnqPath);
 
            osl::FileStatus aStatus(osl_FileStatus_Mask_Type|osl_FileStatus_Mask_LinkTargetURL);
            osl::DirectoryItem aItem;
            (void)osl::DirectoryItem::get(dstUnqPath,aItem);
            (void)aItem.getFileStatus(aStatus);
 
            if( aStatus.isValid(osl_FileStatus_Mask_Type)          &&
                aStatus.isValid(osl_FileStatus_Mask_LinkTargetURL) &&
                aStatus.getFileType() == osl::FileStatus::Link )
                targetPath = aStatus.getLinkTargetURL();
 
            // Will do nothing if file does not exist.
            osl::File::remove( targetPath );
 
            nError = osl_File_move( srcUnqPath,targetPath );
            if( nError != osl::FileBase::E_None )
            {
                installError( CommandId,
                              TaskHandlerErr::OVERWRITE_FOR_MOVE,
                              nError );
                return;
            }
            break;
        }
    case NameClash::RENAME:
        {
            OUString newDstUnqPath;
            nError = osl_File_move( srcUnqPath,dstUnqPath,true );
            if( nError == osl::FileBase::E_EXIST )
            {
                // "invent" a new valid title.
 
                sal_Int32 nPos = -1;
                sal_Int32 nLastDot = dstUnqPath.lastIndexOf( '.' );
                sal_Int32 nLastSlash = dstUnqPath.lastIndexOf( '/' );
                if( ( nLastSlash < nLastDot )                  // dot is part of last(!) path segment
                    && ( nLastSlash != ( nLastDot - 1 ) ) )    // file name does not start with a dot
                    nPos = nLastDot;
                else
                    nPos = dstUnqPath.getLength();
 
                sal_Int32 nTry = 0;
 
                do
                {
                    newDstUnqPath = dstUnqPath;
 
                    OUString aPostfix =  "_" + OUString::number( ++nTry );
 
                    newDstUnqPath = newDstUnqPath.replaceAt( nPos, 0, aPostfix );
 
                    nError = osl_File_move( srcUnqPath,newDstUnqPath,true );
                }
                while( ( nError == osl::FileBase::E_EXIST ) && ( nTry < 10000 ) );
            }
 
            if( nError == osl::FileBase::E_EXIST )
            {
                installError( CommandId,
                              TaskHandlerErr::RENAME_FOR_MOVE );
                return;
            }
            else if( nError != osl::FileBase::E_None )
            {
                installError( CommandId,
                              TaskHandlerErr::RENAMEMOVE_FOR_MOVE,
                              nError );
                return;
            }
            else
                dstUnqPath = newDstUnqPath;
 
            break;
        }
    case NameClash::ERROR:
        {
            nError = osl_File_move( srcUnqPath,dstUnqPath,true );
            if( nError == osl::FileBase::E_EXIST )
            {
                installError( CommandId,
                              TaskHandlerErr::NAMECLASH_FOR_MOVE );
                return;
            }
            else if( nError != osl::FileBase::E_None )
            {
                installError( CommandId,
                              TaskHandlerErr::NAMECLASHMOVE_FOR_MOVE,
                              nError );
                return;
            }
            break;
        }
        case NameClash::ASK:
        default:
        {
            nError = osl_File_move( srcUnqPath,dstUnqPath,true );
            if( nError == osl::FileBase::E_EXIST )
            {
                installError( CommandId,
                              TaskHandlerErr::NAMECLASHSUPPORT_FOR_MOVE,
                              NameClash::ASK);
                return;
            }
        }
        break;
    }
 
    // Determine, whether we have moved a file or a folder
    osl::DirectoryItem aItem;
    nError = osl::DirectoryItem::get( dstUnqPath,aItem );
    if( nError != osl::FileBase::E_None )
    {
        installError( CommandId,
                      TaskHandlerErr::TRANSFER_BY_MOVE_SOURCE,
                      nError );
        return;
    }
    osl::FileStatus aStatus( osl_FileStatus_Mask_Type );
    nError = aItem.getFileStatus( aStatus );
    if( nError != osl::FileBase::E_None || ! aStatus.isValid( osl_FileStatus_Mask_Type ) )
    {
        installError( CommandId,
                      TaskHandlerErr::TRANSFER_BY_MOVE_SOURCESTAT,
                      nError );
        return;
    }
    bool isDocument = ( aStatus.getFileType() == osl::FileStatus::Regular );
 
 
    copyPersistentSet( srcUnqPath,dstUnqPath,!isDocument );
 
    OUString aDstParent = getParentName( dstUnqPath );
    OUString aSrcParent = getParentName( srcUnqPath );
 
    notifyInsert( getContentEventListeners( aDstParent ),dstUnqPath );
    if(  aDstParent != aSrcParent )
        notifyContentRemoved( getContentEventListeners( aSrcParent ),srcUnqPath );
 
    notifyContentExchanged( getContentExchangedEventListeners( srcUnqPath,dstUnqPath,!isDocument ) );
    erasePersistentSet( srcUnqPath,!isDocument );
}
 
 
/********************************************************************************/
/*                                                                              */
/*                         copy-implementation                                  */
/*                                                                              */
/********************************************************************************/
 
//  Copies the content belonging to fileURL srcUnqPath to fileURL dstUnqPath ( files and directories )
 
 
namespace {
 
bool getType(
    TaskManager & task, sal_Int32 id, OUString const & fileUrl,
    osl::DirectoryItem * item, osl::FileStatus::Type * type)
{
    assert(item != nullptr && type != nullptr);
    osl::FileBase::RC err = osl::DirectoryItem::get(fileUrl, *item);
    if (err != osl::FileBase::E_None) {
        task.installError(id, TaskHandlerErr::TRANSFER_BY_COPY_SOURCE, err);
        return false;
    }
    osl::FileStatus stat(osl_FileStatus_Mask_Type);
    err = item->getFileStatus(stat);
    if (err != osl::FileBase::E_None) {
        task.installError(id, TaskHandlerErr::TRANSFER_BY_COPY_SOURCESTAT, err);
        return false;
    }
    *type = stat.getFileType();
    return true;
}
 
}
 
void
TaskManager::copy(
    sal_Int32 CommandId,
    const OUString& srcUnqPath,
    const OUString& dstUnqPathIn,
    sal_Int32 NameClash )
{
    osl::FileBase::RC nError;
    OUString dstUnqPath( dstUnqPathIn );
 
    // Resolve symbolic links within the source path.  If srcUnqPath denotes a
    // symbolic link (targeting either a file or a folder), the contents of the
    // target is copied (recursively, in the case of a folder).  However, if
    // recursively copying the contents of a folder causes a symbolic link to be
    // copied, the symbolic link itself is copied.
    osl::DirectoryItem item;
    osl::FileStatus::Type type;
    if (!getType(*this, CommandId, srcUnqPath, &item, &type)) {
        return;
    }
    OUString rslvdSrcUnqPath;
    if (type == osl::FileStatus::Link) {
        osl::FileStatus stat(osl_FileStatus_Mask_LinkTargetURL);
        nError = item.getFileStatus(stat);
        if (nError != osl::FileBase::E_None) {
            installError(
                CommandId, TaskHandlerErr::TRANSFER_BY_COPY_SOURCESTAT, nError);
            return;
        }
        rslvdSrcUnqPath = stat.getLinkTargetURL();
        if (!getType(*this, CommandId, srcUnqPath, &item, &type)) {
            return;
        }
    } else {
        rslvdSrcUnqPath = srcUnqPath;
    }
 
    bool isDocument
        = type != osl::FileStatus::Directory && type != osl::FileStatus::Volume;
    FileUrlType IsWhat = isDocument ? FileUrlType::File : FileUrlType::Folder;
 
    switch( NameClash )
    {
        case NameClash::KEEP:
        {
            nError = copy_recursive( rslvdSrcUnqPath,dstUnqPath,IsWhat,true );
            if( nError != osl::FileBase::E_None && nError != osl::FileBase::E_EXIST )
            {
                installError( CommandId,
                              TaskHandlerErr::KEEPERROR_FOR_COPY,
                              nError );
                return;
            }
            break;
        }
        case NameClash::OVERWRITE:
        {
            // remove (..., MustExist = sal_False).
            remove( CommandId, dstUnqPath, IsWhat, false );
 
            // copy.
            nError = copy_recursive( rslvdSrcUnqPath,dstUnqPath,IsWhat,false );
            if( nError != osl::FileBase::E_None )
            {
                installError( CommandId,
                              TaskHandlerErr::OVERWRITE_FOR_COPY,
                              nError );
                return;
            }
            break;
        }
        case NameClash::RENAME:
        {
            OUString newDstUnqPath = dstUnqPath;
            nError = copy_recursive( rslvdSrcUnqPath,dstUnqPath,IsWhat,true );
 
            if( nError == osl::FileBase::E_EXIST )
            {
                // "invent" a new valid title.
 
                sal_Int32 nPos = -1;
                sal_Int32 nLastDot = dstUnqPath.lastIndexOf( '.' );
                sal_Int32 nLastSlash = dstUnqPath.lastIndexOf( '/' );
                if ( ( nLastSlash < nLastDot ) // dot is part of last(!) path segment
                     && ( nLastSlash != ( nLastDot - 1 ) ) ) // file name does not start with a dot
                    nPos = nLastDot;
                else
                    nPos = dstUnqPath.getLength();
 
                sal_Int32 nTry = 0;
 
                do
                {
                    newDstUnqPath = dstUnqPath;
 
                    OUString aPostfix =  "_" + OUString::number( ++nTry );
 
                    newDstUnqPath = newDstUnqPath.replaceAt( nPos, 0, aPostfix );
 
                    nError = copy_recursive( rslvdSrcUnqPath,newDstUnqPath,IsWhat,true );
                }
                while( ( nError == osl::FileBase::E_EXIST ) && ( nTry < 10000 ) );
            }
 
            if( nError == osl::FileBase::E_EXIST )
            {
                installError( CommandId,
                              TaskHandlerErr::RENAME_FOR_COPY );
                return;
            }
            else if( nError != osl::FileBase::E_None )
            {
                installError( CommandId,
                              TaskHandlerErr::RENAMEMOVE_FOR_COPY,
                              nError );
                return;
            }
            else
                dstUnqPath = newDstUnqPath;
 
            break;
        }
        case NameClash::ERROR:
        {
            nError = copy_recursive( rslvdSrcUnqPath,dstUnqPath,IsWhat,true );
 
            if( nError == osl::FileBase::E_EXIST )
            {
                installError( CommandId,
                              TaskHandlerErr::NAMECLASH_FOR_COPY );
                return;
            }
            else if( nError != osl::FileBase::E_None )
            {
                installError( CommandId,
                              TaskHandlerErr::NAMECLASHMOVE_FOR_COPY,
                              nError );
                return;
            }
            break;
        }
        case NameClash::ASK:
        default:
        {
            nError = copy_recursive( rslvdSrcUnqPath,dstUnqPath,IsWhat,true );
 
            if( nError == osl::FileBase::E_EXIST )
            {
                installError( CommandId,
                              TaskHandlerErr::NAMECLASHSUPPORT_FOR_COPY,
                              NameClash);
                return;
            }
            break;
        }
    }
 
    copyPersistentSet( srcUnqPath,dstUnqPath, !isDocument );
    notifyInsert( getContentEventListeners( getParentName( dstUnqPath ) ),dstUnqPath );
}
 
 
/********************************************************************************/
/*                                                                              */
/*                         remove-implementation                                */
/*                                                                              */
/********************************************************************************/
 
//  Deletes the content belonging to fileURL aUnqPath( recursively in case of directory )
//  Return: success of operation
 
 
bool
TaskManager::remove( sal_Int32 CommandId,
               const OUString& aUnqPath,
               FileUrlType IsWhat,
               bool  MustExist )
{
    sal_Int32 nMask = osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL;
 
    osl::DirectoryItem aItem;
    osl::FileStatus aStatus( nMask );
    osl::FileBase::RC nError;
 
    if( IsWhat == FileUrlType::Unknown ) // Determine whether we are removing a directory or a file
    {
        nError = osl::DirectoryItem::get( aUnqPath, aItem );
        if( nError != osl::FileBase::E_None )
        {
            if (MustExist)
            {
                installError( CommandId,
                              TaskHandlerErr::NOSUCHFILEORDIR_FOR_REMOVE,
                              nError );
            }
            return (!MustExist);
        }
 
        nError = aItem.getFileStatus( aStatus );
        if( nError != osl::FileBase::E_None || ! aStatus.isValid( nMask ) )
        {
            installError( CommandId,
                          TaskHandlerErr::VALIDFILESTATUS_FOR_REMOVE,
                          nError != osl::FileBase::E_None ? nError : 0 );
            return false;
        }
 
        if( aStatus.getFileType() == osl::FileStatus::Regular ||
            aStatus.getFileType() == osl::FileStatus::Link )
            IsWhat = FileUrlType::File;
        else if(  aStatus.getFileType() == osl::FileStatus::Directory ||
                  aStatus.getFileType() == osl::FileStatus::Volume )
            IsWhat = FileUrlType::Folder;
    }
 
 
    if( IsWhat == FileUrlType::File )
    {
        nError = osl::File::remove( aUnqPath );
        if( nError != osl::FileBase::E_None )
        {
            if (MustExist)
            {
                installError( CommandId,
                              TaskHandlerErr::DELETEFILE_FOR_REMOVE,
                              nError );
            }
            return (!MustExist);
        }
        else
        {
            notifyContentDeleted( getContentDeletedEventListeners(aUnqPath) );
            erasePersistentSet( aUnqPath ); // Removes from XPersistentPropertySet
        }
    }
    else if( IsWhat == FileUrlType::Folder )
    {
        osl::Directory aDirectory( aUnqPath );
 
        nError = aDirectory.open();
        if( nError != osl::FileBase::E_None )
        {
            if (MustExist)
            {
                installError( CommandId,
                              TaskHandlerErr::OPENDIRECTORY_FOR_REMOVE,
                              nError );
            }
            return (!MustExist);
        }
 
        bool whileSuccess = true;
        FileUrlType recurse = FileUrlType::Unknown;
        OUString name;
 
        nError = aDirectory.getNextItem( aItem );
        while( nError == osl::FileBase::E_None )
        {
            nError = aItem.getFileStatus( aStatus );
            if( nError != osl::FileBase::E_None || ! aStatus.isValid( nMask ) )
            {
                installError( CommandId,
                              TaskHandlerErr::VALIDFILESTATUSWHILE_FOR_REMOVE,
                              nError != osl::FileBase::E_None ? nError : 0 );
                whileSuccess = false;
                break;
            }
 
            if( aStatus.getFileType() == osl::FileStatus::Regular ||
                aStatus.getFileType() == osl::FileStatus::Link )
                recurse = FileUrlType::File;
            else if( aStatus.getFileType() == osl::FileStatus::Directory ||
                     aStatus.getFileType() == osl::FileStatus::Volume )
                recurse = FileUrlType::Folder;
 
            name = aStatus.getFileURL();
            whileSuccess = remove( CommandId, name, recurse, MustExist );
            if( !whileSuccess )
                break;
 
            nError = aDirectory.getNextItem( aItem );
        }
 
        aDirectory.close();
 
        if( ! whileSuccess )
            return false;     // error code is installed
 
        if( nError != osl::FileBase::E_NOENT )
        {
            installError( CommandId,
                          TaskHandlerErr::DIRECTORYEXHAUSTED_FOR_REMOVE,
                          nError );
            return false;
        }
 
        nError = osl::Directory::remove( aUnqPath );
        if( nError != osl::FileBase::E_None )
        {
            if (MustExist)
            {
                installError( CommandId,
                              TaskHandlerErr::DELETEDIRECTORY_FOR_REMOVE,
                              nError );
            }
            return (!MustExist);
        }
        else
        {
            notifyContentDeleted( getContentDeletedEventListeners(aUnqPath) );
            erasePersistentSet( aUnqPath );
        }
    }
    else   // Don't know what to remove
    {
        installError( CommandId,
                      TaskHandlerErr::FILETYPE_FOR_REMOVE );
        return false;
    }
 
    return true;
}
 
 
/********************************************************************************/
/*                                                                              */
/*                         mkdir-implementation                                 */
/*                                                                              */
/********************************************************************************/
 
//  Creates new directory with given URL, recursively if necessary
//  Return:: success of operation
 
 
bool
TaskManager::mkdir( sal_Int32 CommandId,
              const OUString& rUnqPath,
              bool OverWrite )
{
    OUString aUnqPath;
 
    // remove trailing slash
    if ( rUnqPath.endsWith("/") )
        aUnqPath = rUnqPath.copy( 0, rUnqPath.getLength() - 1 );
    else
        aUnqPath = rUnqPath;
 
    osl::FileBase::RC nError = osl::Directory::create( aUnqPath );
 
    switch ( nError )
    {
        case osl::FileBase::E_EXIST:   // Directory cannot be overwritten
        {
            if( !OverWrite )
            {
                installError( CommandId,
                              TaskHandlerErr::FOLDER_EXISTS_MKDIR );
                return false;
            }
            else
                return true;
        }
        case osl::FileBase::E_INVAL:
        {
            installError(CommandId,
                         TaskHandlerErr::INVALID_NAME_MKDIR);
            return false;
        }
        case osl::FileBase::E_None:
        {
            OUString aPrtPath = getParentName( aUnqPath );
            notifyInsert( getContentEventListeners( aPrtPath ),aUnqPath );
            return true;
        }
        default:
            return ensuredir(
                CommandId,
                aUnqPath,
                TaskHandlerErr::CREATEDIRECTORY_MKDIR );
    }
}
 
 
/********************************************************************************/
/*                                                                              */
/*                         mkfil-implementation                                 */
/*                                                                              */
/********************************************************************************/
 
//  Creates new file with given URL.
//  The content of aInputStream becomes the content of the file
//  Return:: success of operation
 
 
bool
TaskManager::mkfil( sal_Int32 CommandId,
              const OUString& aUnqPath,
              bool Overwrite,
              const uno::Reference< io::XInputStream >& aInputStream )
{
    // return value unimportant
    bool bSuccess = write( CommandId,
                               aUnqPath,
                               Overwrite,
                               aInputStream );
    if ( bSuccess )
    {
        OUString aPrtPath = getParentName( aUnqPath );
        notifyInsert( getContentEventListeners( aPrtPath ),aUnqPath );
    }
    return bSuccess;
}
 
 
/********************************************************************************/
/*                                                                              */
/*                         write-implementation                                 */
/*                                                                              */
/********************************************************************************/
 
//  writes to the file with given URL.
//  The content of aInputStream becomes the content of the file
//  Return:: success of operation
 
 
bool
TaskManager::write( sal_Int32 CommandId,
              const OUString& aUnqPath,
              bool OverWrite,
              const uno::Reference< io::XInputStream >& aInputStream )
{
    if( ! aInputStream.is() )
    {
        installError( CommandId,
                      TaskHandlerErr::INPUTSTREAM_FOR_WRITE );
        return false;
    }
 
    // Create parent path, if necessary.
    if ( ! ensuredir( CommandId,
                      getParentName( aUnqPath ),
                      TaskHandlerErr::ENSUREDIR_FOR_WRITE ) )
        return false;
 
    osl::FileBase::RC err;
    osl::File aFile( aUnqPath );
 
    if( OverWrite )
    {
        err = aFile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
 
        if( err != osl::FileBase::E_None )
        {
            aFile.close();
            err = aFile.open( osl_File_OpenFlag_Write );
 
            if( err != osl::FileBase::E_None )
            {
                installError( CommandId,
                              TaskHandlerErr::NO_OPEN_FILE_FOR_OVERWRITE,
                              err );
                return false;
            }
 
            // the existing file was just opened and should be overwritten now,
            // truncate it first
 
            err = aFile.setSize( 0 );
            if( err != osl::FileBase::E_None  )
            {
                installError( CommandId,
                              TaskHandlerErr::FILESIZE_FOR_WRITE,
                              err );
                return false;
            }
        }
    }
    else
    {
        err = aFile.open( osl_File_OpenFlag_Read | osl_File_OpenFlag_NoLock );
        if( err == osl::FileBase::E_None )  // The file exists and shall not be overwritten
        {
            installError( CommandId,
                          TaskHandlerErr::NOREPLACE_FOR_WRITE,  // Now an exception
                          err );
 
            aFile.close();
            return false;
        }
 
        // as a temporary solution the creation does not lock the file at all
        // in future it should be possible to create the file without lock explicitly
        err = aFile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create | osl_File_OpenFlag_NoLock );
 
        if( err != osl::FileBase::E_None )
        {
            aFile.close();
            installError( CommandId,
                          TaskHandlerErr::NO_OPEN_FILE_FOR_WRITE,
                          err );
            return false;
        }
    }
 
    bool bSuccess = true;
 
    sal_uInt64 nWrittenBytes;
    sal_Int32 nReadBytes = 0, nRequestedBytes = 32768 /*32k*/;
    uno::Sequence< sal_Int8 > seq( nRequestedBytes );
 
    do
    {
        try
        {
            nReadBytes = aInputStream->readBytes( seq,
                                                  nRequestedBytes );
        }
        catch( const io::NotConnectedException& )
        {
            installError( CommandId,
                          TaskHandlerErr::NOTCONNECTED_FOR_WRITE );
            bSuccess = false;
            break;
        }
        catch( const io::BufferSizeExceededException& )
        {
            installError( CommandId,
                          TaskHandlerErr::BUFFERSIZEEXCEEDED_FOR_WRITE );
            bSuccess = false;
            break;
        }
        catch( const io::IOException& )
        {
            installError( CommandId,
                          TaskHandlerErr::IOEXCEPTION_FOR_WRITE );
            bSuccess = false;
            break;
        }
 
        if( nReadBytes )
        {
            const sal_Int8* p = seq.getConstArray();
 
            err = aFile.write( static_cast<void const *>(p),
                               sal_uInt64( nReadBytes ),
                               nWrittenBytes );
 
            if( err != osl::FileBase::E_None )
            {
                installError( CommandId,
                              TaskHandlerErr::FILEIOERROR_FOR_WRITE,
                              err );
                bSuccess = false;
                break;
            }
            else if( nWrittenBytes != sal_uInt64( nReadBytes ) )
            {
                installError( CommandId,
                              TaskHandlerErr::FILEIOERROR_FOR_NO_SPACE );
                bSuccess = false;
                break;
            }
        }
    } while( nReadBytes == nRequestedBytes );
 
    err = aFile.close();
    if( err != osl::FileBase::E_None  )
    {
        installError( CommandId,
                      TaskHandlerErr::FILEIOERROR_FOR_WRITE,
                      err );
        bSuccess = false;
    }
 
    return bSuccess;
}
 
 
/*********************************************************************************/
/*                                                                               */
/*                 insertDefaultProperties-Implementation                        */
/*                                                                               */
/*********************************************************************************/
 
 
void TaskManager::insertDefaultProperties( const OUString& aUnqPath )
{
    std::unique_lock aGuard(m_aMutex);
    insertDefaultProperties(aGuard, aUnqPath);
}
 
void TaskManager::insertDefaultProperties( std::unique_lock<std::mutex>& /*rGuard*/, const OUString& aUnqPath )
{
    ContentMap::iterator it =
        m_aContent.emplace( aUnqPath,UnqPathData() ).first;
 
    load( it, false );
 
    MyProperty ContentTProperty( ContentType );
 
    PropertySet& properties = it->second.properties;
    bool ContentNotDefau = properties.find( ContentTProperty ) != properties.end();
 
    properties.reserve(properties.size() + m_aDefaultProperties.size());
    for (auto const& defaultprop : m_aDefaultProperties)
    {
        if( !ContentNotDefau || defaultprop.getPropertyName() != ContentType )
            properties.insert( defaultprop );
    }
}
 
 
/******************************************************************************/
/*                                                                            */
/*                          mapping of file urls                              */
/*                          to uncpath and vice versa                         */
/*                                                                            */
/******************************************************************************/
 
 
bool TaskManager::getUnqFromUrl( const OUString& Url, OUString& Unq )
{
    if ( Url == "file:///" || Url == "file://localhost/" || Url == "file://127.0.0.1/" )
    {
        Unq = "file:///";
        return false;
    }
 
    bool err = osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL( Url,Unq );
 
    Unq = Url;
 
    sal_Int32 l = Unq.getLength()-1;
    if( ! err && Unq.endsWith("/") &&
        Unq.indexOf( '/', RTL_CONSTASCII_LENGTH("//") ) != -1 )
        Unq = Unq.copy(0, l);
 
    return err;
}
 
 
bool TaskManager::getUrlFromUnq( const OUString& Unq,OUString& Url )
{
    bool err = osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL( Unq,Url );
 
    Url = Unq;
 
    return err;
}
 
 
// Helper function for public copy
 
osl::FileBase::RC
TaskManager::copy_recursive( const OUString& srcUnqPath,
                       const OUString& dstUnqPath,
                       FileUrlType TypeToCopy,
                       bool testExistBeforeCopy )
{
    osl::FileBase::RC err = osl::FileBase::E_None;
 
    if( TypeToCopy == FileUrlType::File ) // Document
    {
        err = osl_File_copy( srcUnqPath,dstUnqPath,testExistBeforeCopy );
    }
    else if( TypeToCopy == FileUrlType::Folder )
    {
        osl::Directory aDir( srcUnqPath );
        (void)aDir.open();
 
        err = osl::Directory::create( dstUnqPath );
        osl::FileBase::RC next = err;
        if( err == osl::FileBase::E_None )
        {
            sal_Int32 const n_Mask = osl_FileStatus_Mask_FileURL | osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_Type;
 
            osl::DirectoryItem aDirItem;
 
            while( err == osl::FileBase::E_None )
            {
                next = aDir.getNextItem( aDirItem );
                if (next != osl::FileBase::E_None )
                    break;
                bool IsDoc = false;
                osl::FileStatus aFileStatus( n_Mask );
                aDirItem.getFileStatus( aFileStatus );
                if( aFileStatus.isValid( osl_FileStatus_Mask_Type ) )
                    IsDoc = aFileStatus.getFileType() == osl::FileStatus::Regular;
 
                // Getting the information for the next recursive copy
                FileUrlType newTypeToCopy = IsDoc ? FileUrlType::File : FileUrlType::Folder;
 
                OUString newSrcUnqPath;
                if( aFileStatus.isValid( osl_FileStatus_Mask_FileURL ) )
                    newSrcUnqPath = aFileStatus.getFileURL();
 
                OUString newDstUnqPath = dstUnqPath;
                OUString tit;
                if( aFileStatus.isValid( osl_FileStatus_Mask_FileName ) )
                    tit = rtl::Uri::encode( aFileStatus.getFileName(),
                                          rtl_UriCharClassPchar,
                                          rtl_UriEncodeIgnoreEscapes,
                                          RTL_TEXTENCODING_UTF8 );
 
                if( !newDstUnqPath.endsWith( "/" ) )
                    newDstUnqPath += "/";
 
                newDstUnqPath += tit;
 
                if ( newSrcUnqPath != dstUnqPath )
                    err = copy_recursive( newSrcUnqPath,newDstUnqPath,newTypeToCopy,false );
            }
 
            if( err == osl::FileBase::E_None && next != osl::FileBase::E_NOENT )
                err = next;
        }
        aDir.close();
    }
 
    return err;
}
 
 
// Helper function for mkfil,mkdir and write
// Creates whole path
// returns success of the operation
 
 
bool TaskManager::ensuredir( sal_Int32 CommandId,
                                    const OUString& rUnqPath,
                                    TaskHandlerErr errorCode )
{
    OUString aPath;
 
    if ( rUnqPath.isEmpty() )
        return false;
 
    if ( rUnqPath.endsWith("/") )
        aPath = rUnqPath.copy( 0, rUnqPath.getLength() - 1 );
    else
        aPath = rUnqPath;
 
#if HAVE_FEATURE_MACOSX_SANDBOX
 
    // Avoid annoying sandbox messages in the system.log from the
    // below aDirectory.open(), which ends up calling opendir().
    // Surely it is easier to just call stat()? Calling stat() on an
    // arbitrary (?) directory does not seem to cause any sandbox
    // violation, while opendir() does. (Sorry I could not be bothered
    // to use some complex cross-platform abstraction over stat() here
    // in this macOS specific code block.)
 
    OUString aDirName;
    struct stat s;
    if( osl::FileBase::getSystemPathFromFileURL( aPath, aDirName ) == osl::FileBase::E_None &&
        stat(OUStringToOString( aDirName, RTL_TEXTENCODING_UTF8).getStr(), &s ) == 0 &&
        S_ISDIR( s.st_mode ) )
        return sal_True;
#endif
 
    // HACK: create directory on a mount point with nobrowse option
    // returns ENOSYS in any case !!
    osl::Directory aDirectory( aPath );
    osl::FileBase::RC nError = aDirectory.open();
    aDirectory.close();
 
    if( nError == osl::File::E_None )
        return true;
 
    nError = osl::Directory::create( aPath );
 
    if( nError == osl::File::E_None )
        notifyInsert( getContentEventListeners( getParentName( aPath ) ),aPath );
 
    bool  bSuccess = ( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
 
    if( ! bSuccess )
    {
        OUString aParentDir = getParentName( aPath );
 
        if ( aParentDir != aPath )
        {   // Create first the parent directory
            bSuccess = ensuredir( CommandId,
                                  getParentName( aPath ),
                                  errorCode );
 
            // After parent directory structure exists try it one's more
 
            if ( bSuccess )
            {   // Parent directory exists, retry creation of directory
                nError = osl::Directory::create( aPath );
 
                if( nError == osl::File::E_None )
                    notifyInsert( getContentEventListeners( getParentName( aPath ) ),aPath );
 
                bSuccess =( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
            }
        }
    }
 
    if( ! bSuccess )
        installError( CommandId,
                      errorCode,
                      nError );
 
    return bSuccess;
}
 
 
//  Given a sequence of properties seq, this method determines the mask
//  used to instantiate an osl::FileStatus, so that a call to
//  osl::DirectoryItem::getFileStatus fills the required fields.
 
 
void
TaskManager::getMaskFromProperties(
    sal_Int32& n_Mask,
    const uno::Sequence< beans::Property >& seq )
{
    n_Mask = 0;
    for(const auto& rProp : seq) {
        if(rProp.Name == Title)
            n_Mask |= osl_FileStatus_Mask_FileName;
        else if(rProp.Name == CasePreservingURL)
            n_Mask |= osl_FileStatus_Mask_FileURL;
        else if(rProp.Name == IsDocument ||
                rProp.Name == IsFolder ||
                rProp.Name == IsVolume ||
                rProp.Name == IsRemoveable ||
                rProp.Name == IsRemote ||
                rProp.Name == IsCompactDisc ||
                rProp.Name == IsFloppy ||
                rProp.Name == ContentType)
            n_Mask |= (osl_FileStatus_Mask_Type | osl_FileStatus_Mask_LinkTargetURL);
        else if(rProp.Name == Size)
            n_Mask |= (osl_FileStatus_Mask_FileSize |
                      osl_FileStatus_Mask_Type |
                      osl_FileStatus_Mask_LinkTargetURL);
        else if(rProp.Name == IsHidden ||
                rProp.Name == IsReadOnly)
            n_Mask |= osl_FileStatus_Mask_Attributes;
        else if(rProp.Name == DateModified)
            n_Mask |= osl_FileStatus_Mask_ModifyTime;
    }
}
 
 
/*********************************************************************************/
/*                                                                               */
/*                     load-Implementation                                       */
/*                                                                               */
/*********************************************************************************/
 
//  Load the properties from configuration, if create == true create them.
//  The Properties are stored under the url belonging to it->first.
 
 
void
TaskManager::load( const ContentMap::iterator& it, bool create )
{
    if(  ( it->second.xS.is() && it->second.xC.is() && it->second.xA.is() )
        || !m_xFileRegistry.is() )
        return;
 
 
    uno::Reference< ucb::XPersistentPropertySet > xS = m_xFileRegistry->openPropertySet( it->first,create );
    if( xS.is() )
    {
        it->second.xS = xS;
        it->second.xC.set(xS, uno::UNO_QUERY);
        it->second.xA.set(xS, uno::UNO_QUERY);
 
        // Now put in all values in the storage in the local hash;
 
        PropertySet& properties = it->second.properties;
        const uno::Sequence< beans::Property > seq = xS->getPropertySetInfo()->getProperties();
 
        for( const auto& rProp : seq )
        {
            MyProperty readProp( false,
                                 rProp.Name,
                                 rProp.Handle,
                                 rProp.Type,
                                 xS->getPropertyValue( rProp.Name ),
                                 beans::PropertyState_DIRECT_VALUE,
                                 rProp.Attributes );
            properties.insert( readProp );
        }
    }
    else if( create )
    {
        // Catastrophic error
    }
}
 
 
/*********************************************************************************/
/*                                                                               */
/*                     commit-Implementation                                     */
/*                                                                               */
/*********************************************************************************/
// Commit inserts the determined properties in the filestatus object into
// the internal map, so that is possible to determine on a subsequent
// setting of file properties which properties have changed without filestat
 
 
void
TaskManager::commit( std::unique_lock<std::mutex>& rGuard,
               const TaskManager::ContentMap::iterator& it,
               const osl::FileStatus& aFileStatus )
{
    TaskManager::PropertySet::const_iterator it1;
 
    if( it->second.properties.empty() )
    {
        OUString aPath = it->first;
        insertDefaultProperties( rGuard, aPath );
    }
 
    PropertySet& properties = it->second.properties;
 
    it1 = properties.find( MyProperty( Title ) );
    if( it1 != properties.end() )
    {
        if( aFileStatus.isValid( osl_FileStatus_Mask_FileName ) )
        {
            it1->setValue( uno::Any(aFileStatus.getFileName()) );
        }
    }
 
    it1 = properties.find( MyProperty( CasePreservingURL ) );
    if( it1 != properties.end() )
    {
        if( aFileStatus.isValid( osl_FileStatus_Mask_FileURL ) )
        {
            it1->setValue( uno::Any(aFileStatus.getFileURL()) );
        }
    }
 
 
    bool isDirectory;
 
    sal_Int64 dirSize = 0;
 
    if( aFileStatus.isValid( osl_FileStatus_Mask_FileSize ) )
        dirSize = aFileStatus.getFileSize();
 
    if( aFileStatus.isValid( osl_FileStatus_Mask_Type ) )
    {
        bool isFile,isVolume;
        if( osl::FileStatus::Link == aFileStatus.getFileType() &&
            aFileStatus.isValid( osl_FileStatus_Mask_LinkTargetURL ) )
        {
            osl::DirectoryItem aDirItem;
            osl::FileStatus aFileStatus2( osl_FileStatus_Mask_Type );
            if( osl::FileBase::E_None == osl::DirectoryItem::get( aFileStatus.getLinkTargetURL(),aDirItem ) &&
                osl::FileBase::E_None == aDirItem.getFileStatus( aFileStatus2 )    &&
                aFileStatus2.isValid( osl_FileStatus_Mask_Type ) )
            {
                isVolume = osl::FileStatus::Volume == aFileStatus2.getFileType();
                isDirectory =
                    osl::FileStatus::Volume == aFileStatus2.getFileType() ||
                    osl::FileStatus::Directory == aFileStatus2.getFileType();
                isFile =
                    osl::FileStatus::Regular == aFileStatus2.getFileType();
 
                if( aFileStatus2.isValid( osl_FileStatus_Mask_FileSize ) )
                    dirSize = aFileStatus2.getFileSize();
            }
            else
            {
                // extremely ugly, but otherwise default construction
                // of aDirItem and aFileStatus2
                // before the preceding if
                isVolume = osl::FileStatus::Volume == aFileStatus.getFileType();
                isDirectory =
                    osl::FileStatus::Volume == aFileStatus.getFileType() ||
                    osl::FileStatus::Directory == aFileStatus.getFileType();
                isFile =
                    osl::FileStatus::Regular == aFileStatus.getFileType();
            }
        }
        else
        {
            isVolume = osl::FileStatus::Volume == aFileStatus.getFileType();
            isDirectory =
                osl::FileStatus::Volume == aFileStatus.getFileType() ||
                osl::FileStatus::Directory == aFileStatus.getFileType();
            isFile =
                osl::FileStatus::Regular == aFileStatus.getFileType();
        }
 
        it1 = properties.find( MyProperty( IsVolume ) );
        if( it1 != properties.end() )
            it1->setValue( uno::Any( isVolume ) );
 
        it1 = properties.find( MyProperty( IsFolder ) );
        if( it1 != properties.end() )
            it1->setValue( uno::Any( isDirectory ) );
 
        it1 = properties.find( MyProperty( IsDocument ) );
        if( it1 != properties.end() )
            it1->setValue( uno::Any( isFile ) );
 
        osl::VolumeInfo aVolumeInfo( osl_VolumeInfo_Mask_Attributes );
        if( isVolume &&
            osl::FileBase::E_None == osl::Directory::getVolumeInfo( it->first,aVolumeInfo ) &&
            aVolumeInfo.isValid( osl_VolumeInfo_Mask_Attributes ) )
        {
            // Retrieve the flags;
            bool isRemote = aVolumeInfo.getRemoteFlag();
            bool isRemoveable = aVolumeInfo.getRemoveableFlag();
            bool isCompactDisc = aVolumeInfo.getCompactDiscFlag();
            bool isFloppy = aVolumeInfo.getFloppyDiskFlag();
 
            it1 = properties.find( MyProperty( IsRemote ) );
            if( it1 != properties.end() )
                it1->setValue( uno::Any( isRemote ) );
 
            it1 = properties.find( MyProperty( IsRemoveable ) );
            if( it1 != properties.end() )
                it1->setValue( uno::Any( isRemoveable ) );
 
            it1 = properties.find( MyProperty( IsCompactDisc ) );
            if( it1 != properties.end() )
                it1->setValue( uno::Any( isCompactDisc ) );
 
            it1 = properties.find( MyProperty( IsFloppy ) );
            if( it1 != properties.end() )
                it1->setValue( uno::Any( isFloppy ) );
        }
        else
        {
            uno::Any aAny(false);
            it1 = properties.find( MyProperty( IsRemote ) );
            if( it1 != properties.end() )
                it1->setValue( aAny );
 
            it1 = properties.find( MyProperty( IsRemoveable ) );
            if( it1 != properties.end() )
                it1->setValue( aAny );
 
            it1 = properties.find( MyProperty( IsCompactDisc ) );
            if( it1 != properties.end() )
                it1->setValue( aAny );
 
            it1 = properties.find( MyProperty( IsFloppy ) );
            if( it1 != properties.end() )
                it1->setValue( aAny );
        }
    }
    else
    {
        isDirectory = false;
    }
 
    it1 = properties.find( MyProperty( Size ) );
    if( it1 != properties.end() )
        it1->setValue( uno::Any( dirSize ) );
 
    it1 = properties.find( MyProperty( IsReadOnly ) );
    if( it1 != properties.end() )
    {
        if( aFileStatus.isValid( osl_FileStatus_Mask_Attributes ) )
        {
            sal_uInt64 Attr = aFileStatus.getAttributes();
            bool readonly = ( Attr & osl_File_Attribute_ReadOnly ) != 0;
            it1->setValue( uno::Any( readonly ) );
        }
    }
 
    it1 = properties.find( MyProperty( IsHidden ) );
    if( it1 != properties.end() )
    {
        if( aFileStatus.isValid( osl_FileStatus_Mask_Attributes ) )
        {
            sal_uInt64 Attr = aFileStatus.getAttributes();
            bool ishidden = ( Attr & osl_File_Attribute_Hidden ) != 0;
            it1->setValue( uno::Any( ishidden ) );
        }
    }
 
    it1 = properties.find( MyProperty( DateModified ) );
    if( it1 != properties.end() )
    {
        if( aFileStatus.isValid( osl_FileStatus_Mask_ModifyTime ) )
        {
            TimeValue temp = aFileStatus.getModifyTime();
 
            // Convert system time to local time (for EA)
            TimeValue myLocalTime;
            if (!osl_getLocalTimeFromSystemTime( &temp, &myLocalTime ))
            {
                SAL_WARN(
                    "ucb.ucp.file",
                    "cannot convert (" << temp.Seconds << ", " << temp.Nanosec
                        << ") to local time");
                myLocalTime = temp;
            }
 
            oslDateTime myDateTime;
            osl_getDateTimeFromTimeValue( &myLocalTime, &myDateTime );
            util::DateTime aDateTime;
 
            aDateTime.NanoSeconds = myDateTime.NanoSeconds;
            aDateTime.Seconds = myDateTime.Seconds;
            aDateTime.Minutes = myDateTime.Minutes;
            aDateTime.Hours = myDateTime.Hours;
            aDateTime.Day = myDateTime.Day;
            aDateTime.Month = myDateTime.Month;
            aDateTime.Year = myDateTime.Year;
            it1->setValue( uno::Any( aDateTime ) );
        }
    }
 
    it1 = properties.find( MyProperty( CreatableContentsInfo ) );
    if( it1 != properties.end() )
        it1->setValue( uno::Any(
            isDirectory || !aFileStatus.isValid( osl_FileStatus_Mask_Type )
                ? queryCreatableContentsInfo()
                : uno::Sequence< ucb::ContentInfo >() ) );
}
 
 
// Special optimized method for getting the properties of a
// directoryitem, which is returned by osl::DirectoryItem::getNextItem()
 
 
bool
TaskManager::getv(
    const uno::Sequence< beans::Property >& properties,
    osl::DirectoryItem& aDirItem,
    OUString& aUnqPath,
    bool& aIsRegular,
    uno::Reference< sdbc::XRow > & row )
{
    uno::Sequence< uno::Any > seq( properties.getLength() );
 
    sal_Int32 n_Mask;
    getMaskFromProperties( n_Mask,properties );
 
    // Always retrieve the type and the target URL because item might be a link
    osl::FileStatus aFileStatus( n_Mask |
                                 osl_FileStatus_Mask_FileURL |
                                 osl_FileStatus_Mask_Type |
                                 osl_FileStatus_Mask_LinkTargetURL );
 
    osl::FileBase::RC aRes = aDirItem.getFileStatus( aFileStatus );
    if ( aRes != osl::FileBase::E_None )
    {
        SAL_WARN(
            "ucb.ucp.file",
            "osl::DirectoryItem::getFileStatus failed with " << +aRes);
        return false;
    }
 
    aUnqPath = aFileStatus.getFileURL();
 
    // If the directory item type is a link retrieve the type of the target
 
    if ( aFileStatus.getFileType() == osl::FileStatus::Link )
    {
        // Assume failure
        aIsRegular = false;
        osl::DirectoryItem aTargetItem;
        (void)osl::DirectoryItem::get( aFileStatus.getLinkTargetURL(), aTargetItem );
        if ( aTargetItem.is() )
        {
            osl::FileStatus aTargetStatus( osl_FileStatus_Mask_Type );
 
            if ( osl::FileBase::E_None == aTargetItem.getFileStatus( aTargetStatus ) )
                aIsRegular =
                    aTargetStatus.getFileType() == osl::FileStatus::Regular;
        }
    }
    else
        aIsRegular = aFileStatus.getFileType() == osl::FileStatus::Regular;
 
    {
        std::unique_lock aGuard( m_aMutex );
 
        insertDefaultProperties( aGuard, aUnqPath );
 
        TaskManager::ContentMap::iterator it = m_aContent.find( aUnqPath );
        commit( aGuard, it, aFileStatus );
 
        PropertySet& propset = it->second.properties;
 
        std::transform(properties.begin(), properties.end(), seq.getArray(),
            [&propset](const beans::Property& rProp) -> uno::Any {
                MyProperty readProp( rProp.Name );
                auto it1 = propset.find( readProp );
                if( it1 == propset.end() )
                    return uno::Any();
                return it1->getValue();
            });
    }
 
    row = new XRow_impl( this,seq );
    return true;
}
 
 
// EventListener
 
 
std::vector< ContentEventNotifier >
TaskManager::getContentEventListeners( const OUString& aName )
{
    std::vector< ContentEventNotifier > listeners;
    {
        std::unique_lock aGuard( m_aMutex );
        TaskManager::ContentMap::iterator it = m_aContent.find( aName );
        if( it != m_aContent.end() && !it->second.notifier.empty() )
        {
            std::vector<Notifier*>& listOfNotifiers = it->second.notifier;
            for (auto const& pointer : listOfNotifiers)
            {
                std::optional<ContentEventNotifier> notifier = pointer->cCEL();
                if( notifier )
                    listeners.push_back( std::move(*notifier) );
            }
        }
    }
    return listeners;
}
 
 
std::vector< ContentEventNotifier >
TaskManager::getContentDeletedEventListeners( const OUString& aName )
{
    std::vector< ContentEventNotifier > listeners;
    {
        std::unique_lock aGuard( m_aMutex );
        TaskManager::ContentMap::iterator it = m_aContent.find( aName );
        if( it != m_aContent.end() && !it->second.notifier.empty() )
        {
            std::vector<Notifier*>& listOfNotifiers = it->second.notifier;
            for (auto const& pointer : listOfNotifiers)
            {
                std::optional<ContentEventNotifier> notifier = pointer->cDEL();
                if( notifier )
                    listeners.push_back( std::move(*notifier) );
            }
        }
    }
    return listeners;
}
 
void TaskManager::notifyInsert(const std::vector<ContentEventNotifier>& listeners,
                               const OUString& aChildName)
{
    for (const auto & l : listeners )
    {
        l.notifyChildInserted( aChildName );
    }
}
 
void TaskManager::notifyContentDeleted(
    const std::vector<ContentEventNotifier>& listeners)
{
    for( auto const & l : listeners )
    {
        l.notifyDeleted();
    }
}
 
void TaskManager::notifyContentRemoved(
    const std::vector<ContentEventNotifier>& listeners, const OUString& aChildName)
{
    for( auto const & l : listeners )
    {
        l.notifyRemoved( aChildName );
    }
}
 
 
std::vector< PropertySetInfoChangeNotifier >
TaskManager::getPropertySetListeners( const OUString& aName )
{
    std::vector< PropertySetInfoChangeNotifier > listeners;
    {
        std::unique_lock aGuard( m_aMutex );
        TaskManager::ContentMap::iterator it = m_aContent.find( aName );
        if( it != m_aContent.end() && !it->second.notifier.empty() )
        {
            std::vector<Notifier*>& listOfNotifiers = it->second.notifier;
            for (auto const& pointer : listOfNotifiers)
            {
                std::optional<PropertySetInfoChangeNotifier> notifier = pointer->cPSL();
                if( notifier )
                    listeners.push_back( std::move(*notifier) );
            }
        }
    }
    return listeners;
}
 
void TaskManager::notifyPropertyAdded(
    const std::vector<PropertySetInfoChangeNotifier>& listeners,
    const OUString& aPropertyName)
{
    for( auto const & l : listeners )
    {
        l.notifyPropertyAdded( aPropertyName );
    }
}
 
void TaskManager::notifyPropertyRemoved(
    const std::vector<PropertySetInfoChangeNotifier>& listeners,
    const OUString& aPropertyName)
{
    for( auto const & l : listeners )
    {
        l.notifyPropertyRemoved( aPropertyName );
    }
}
 
 
std::vector< ContentEventNotifier >
TaskManager::getContentExchangedEventListeners( const OUString& aOldPrefix,
                                          const OUString& aNewPrefix,
                                          bool withChildren )
{
    std::vector< ContentEventNotifier > aVector;
 
    sal_Int32 count;
    OUString aOldName;
    OUString aNewName;
    std::vector< OUString > oldChildList;
 
    {
        std::unique_lock aGuard( m_aMutex );
 
        if( ! withChildren )
        {
            aOldName = aOldPrefix;
            aNewName = aNewPrefix;
            count = 1;
        }
        else
        {
            for (auto const& content : m_aContent)
            {
                if( isChild( aOldPrefix, content.first ) )
                {
                    oldChildList.push_back( content.first );
                }
            }
            count = oldChildList.size();
        }
 
 
        for( sal_Int32 j = 0; j < count; ++j )
        {
            if( withChildren )
            {
                aOldName = oldChildList[j];
                aNewName = newName( aNewPrefix,aOldPrefix,aOldName );
            }
 
            TaskManager::ContentMap::iterator itold = m_aContent.find( aOldName );
            if( itold != m_aContent.end() )
            {
                TaskManager::ContentMap::iterator itnew = m_aContent.emplace(
                    aNewName,UnqPathData() ).first;
 
                // copy Ownership also
                itnew->second.properties = std::move(itold->second.properties);
 
                // copy existing list
                std::vector< Notifier* > copyList;
                std::swap(copyList, itnew->second.notifier);
                itnew->second.notifier = std::move(itold->second.notifier);
 
                m_aContent.erase( itold );
 
                if (itnew != m_aContent.end())
                {
                    if (!itnew->second.notifier.empty())
                    {
                        std::vector<Notifier*>& listOfNotifiers = itnew->second.notifier;
                        for (auto const& pointer : listOfNotifiers)
                        {
                            std::optional<ContentEventNotifier> notifier = pointer->cEXC( aNewName );
                            if( notifier )
                                aVector.push_back( std::move(*notifier) );
                        }
                    }
 
                    // Merge with preexisting notifiers
                    // However, these may be in status BaseContent::Deleted
                    itnew->second.notifier.insert(itnew->second.notifier.end(),
                        copyList.begin(), copyList.end() );
                }
            }
        }
    }
 
    return aVector;
}
 
void TaskManager::notifyContentExchanged(
    const std::vector<ContentEventNotifier>& listeners_vec)
{
    for( auto & l : listeners_vec)
    {
        l.notifyExchanged();
    }
}
 
 
std::vector< PropertyChangeNotifier >
TaskManager::getPropertyChangeNotifier( const OUString& aName )
{
    std::vector< PropertyChangeNotifier > listeners;
    {
        std::unique_lock aGuard( m_aMutex );
        TaskManager::ContentMap::iterator it = m_aContent.find( aName );
        if( it != m_aContent.end() && !it->second.notifier.empty() )
        {
            std::vector<Notifier*>& listOfNotifiers = it->second.notifier;
            for (auto const& pointer : listOfNotifiers)
            {
                std::optional<PropertyChangeNotifier> notifier = pointer->cPCL();
                if( notifier )
                    listeners.push_back( std::move(*notifier) );
            }
        }
    }
    return listeners;
}
 
void TaskManager::notifyPropertyChanges(
    const std::vector<PropertyChangeNotifier>& listeners,
    const uno::Sequence<beans::PropertyChangeEvent>& seqChanged)
{
    for( auto const & l : listeners )
    {
        l.notifyPropertyChanged( seqChanged );
    }
}
 
 
/********************************************************************************/
/*                       remove persistent propertyset                          */
/********************************************************************************/
 
void
TaskManager::erasePersistentSetWithoutChildren( const OUString& aUnqPath )
{
    {
        // Release possible references
        std::unique_lock aGuard( m_aMutex );
        ContentMap::iterator it = m_aContent.find( aUnqPath );
        if( it != m_aContent.end() )
        {
            it->second.xS = nullptr;
            it->second.xC = nullptr;
            it->second.xA = nullptr;
 
            it->second.properties.clear();
        }
    }
 
    m_xFileRegistry->removePropertySet( aUnqPath );
}
 
void
TaskManager::erasePersistentSet( const OUString& aUnqPath,
                           bool withChildren )
{
    if( ! m_xFileRegistry.is() )
    {
        OSL_ASSERT( m_xFileRegistry.is() );
        return;
    }
 
    if( ! withChildren )
    {
        erasePersistentSetWithoutChildren(aUnqPath);
        return;
    }
 
    uno::Reference< container::XNameAccess > xName( m_xFileRegistry,uno::UNO_QUERY );
    const uno::Sequence< OUString > seqNames = xName->getElementNames();
 
    OUString old_Name = aUnqPath;
 
    for( const auto& rName : seqNames )
    {
        if( ! ( isChild( old_Name,rName ) ) )
            continue;
 
        old_Name = rName;
 
        erasePersistentSetWithoutChildren(old_Name);
    }
}
 
 
/********************************************************************************/
/*                       copy persistent propertyset                            */
/*                       from srcUnqPath to dstUnqPath                          */
/********************************************************************************/
 
void
TaskManager::copyPersistentSetWithoutChildren( const OUString& srcUnqPath,
                          const OUString& dstUnqPath )
{
    uno::Reference< XPersistentPropertySet > x_src =
            m_xFileRegistry->openPropertySet( srcUnqPath,false );
    m_xFileRegistry->removePropertySet( dstUnqPath );
 
    if( ! x_src.is() )
        return;
 
    const uno::Sequence< beans::Property > seqProperty =
        x_src->getPropertySetInfo()->getProperties();
 
    if( ! seqProperty.hasElements() )
        return;
 
    uno::Reference< XPersistentPropertySet >
        x_dstS = m_xFileRegistry->openPropertySet( dstUnqPath,true );
    uno::Reference< beans::XPropertyContainer >
        x_dstC( x_dstS,uno::UNO_QUERY );
 
    for( const auto& rProperty : seqProperty )
    {
        x_dstC->addProperty( rProperty.Name,
                             rProperty.Attributes,
                             x_src->getPropertyValue( rProperty.Name ) );
    }
}
 
void
TaskManager::copyPersistentSet( const OUString& srcUnqPath,
                          const OUString& dstUnqPath,
                          bool withChildren )
{
    if( ! m_xFileRegistry.is() )
    {
        OSL_ASSERT( m_xFileRegistry.is() );
        return;
    }
 
    if( ! withChildren )
    {
        copyPersistentSetWithoutChildren(srcUnqPath, dstUnqPath);
        return;
    }
 
    uno::Reference< container::XNameAccess > xName( m_xFileRegistry,uno::UNO_QUERY );
    const uno::Sequence< OUString > seqNames = xName->getElementNames();
 
    OUString new_Name;
 
    for( const auto& rName : seqNames )
    {
        if( ! ( isChild( srcUnqPath,rName ) ) )
            continue;
 
        new_Name = newName( dstUnqPath,srcUnqPath,rName );
 
        copyPersistentSetWithoutChildren(rName, new_Name);
    }
}
 
uno::Sequence< ucb::ContentInfo > TaskManager::queryCreatableContentsInfo()
{
 
 
    uno::Sequence< beans::Property > props
    {
        { u"Title"_ustr, -1, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID | beans::PropertyAttribute::BOUND }
    };
    return
    {
        { FileContentType, ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM | ucb::ContentInfoAttribute::KIND_DOCUMENT, props },
        { FolderContentType, ucb::ContentInfoAttribute::KIND_FOLDER, props }
    };
}
 
/*******************************************************************************/
/*                                                                             */
/*                 some miscellaneous static functions                        */
/*                                                                             */
/*******************************************************************************/
 
void
TaskManager::getScheme( OUString& Scheme )
{
  Scheme = "file";
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V783 Dereferencing of the invalid iterator 'itnew' might take place. Check lines: 2709, 2718.

V547 Expression 'value' is always false.

V547 Expression 'value' is always false.

V1051 Consider checking for misprints. It's possible that the 'rslvdSrcUnqPath' should be used inside 'getType' function.