/* -*- 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 <osl/security.hxx>
#include <osl/file.hxx>
#include <osl/socket.h>
#include <sal/log.hxx>
#include <cppuhelper/queryinterface.hxx>
#include <comphelper/processfactory.hxx>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/ucb/FileSystemNotation.hpp>
#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
#include <com/sun/star/beans/PropertyState.hpp>
#include <cppuhelper/factory.hxx>
#include <cppuhelper/supportsservice.hxx>
#include "filglob.hxx"
#include "filid.hxx"
#include "filtask.hxx"
#include "bc.hxx"
#include "prov.hxx"
 
using namespace fileaccess;
using namespace com::sun::star;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::beans;
using namespace com::sun::star::ucb;
using namespace com::sun::star::container;
 
#if OSL_DEBUG_LEVEL > 0
#define THROW_WHERE SAL_WHERE
#else
#define THROW_WHERE ""
#endif
 
 
extern "C" SAL_DLLPUBLIC_EXPORT void * ucpfile_component_getFactory(
    const sal_Char * pImplName, void * pServiceManager, void * )
{
    void * pRet = nullptr;
 
    Reference< XMultiServiceFactory > xSMgr(
            static_cast< XMultiServiceFactory * >( pServiceManager ) );
    Reference< XSingleServiceFactory > xFactory;
 
 
    // File Content Provider.
 
 
    if ( fileaccess::TaskManager::getImplementationName_static().
            equalsAscii( pImplName ) )
    {
        xFactory = FileProvider::createServiceFactory( xSMgr );
    }
 
 
    if ( xFactory.is() )
    {
        xFactory->acquire();
        pRet = xFactory.get();
    }
 
    return pRet;
}
 
/****************************************************************************/
/*                                                                          */
/*                                                                          */
/*                        FileProvider                                      */
/*                                                                          */
/*                                                                          */
/****************************************************************************/
FileProvider::FileProvider( const Reference< XComponentContext >& rxContext )
    : m_xContext(rxContext)
    , m_FileSystemNotation(FileSystemNotation::UNKNOWN_NOTATION)
{
}
 
FileProvider::~FileProvider()
{
}
 
// XInitialization
void FileProvider::init()
{
    if( ! m_pMyShell )
        m_pMyShell.reset( new TaskManager( m_xContext, this, true ) );
}
 
 
void SAL_CALL
FileProvider::initialize(
    const Sequence< Any >& aArguments )
{
    if( ! m_pMyShell ) {
        OUString config;
        if( aArguments.getLength() > 0 &&
            (aArguments[0] >>= config) &&
            config == "NoConfig" )
            m_pMyShell.reset( new TaskManager( m_xContext, this, false ) );
        else
            m_pMyShell.reset( new TaskManager( m_xContext, this, true ) );
    }
}
 
// XServiceInfo methods.
OUString SAL_CALL
FileProvider::getImplementationName()
{
    return fileaccess::TaskManager::getImplementationName_static();
}
 
sal_Bool SAL_CALL FileProvider::supportsService(const OUString& ServiceName )
{
    return cppu::supportsService(this, ServiceName);
}
 
Sequence< OUString > SAL_CALL
FileProvider::getSupportedServiceNames()
{
    return fileaccess::TaskManager::getSupportedServiceNames_static();
}
 
Reference< XSingleServiceFactory >
FileProvider::createServiceFactory(
                   const Reference< XMultiServiceFactory >& rxServiceMgr )
{
    return cppu::createSingleFactory(
        rxServiceMgr,
        fileaccess::TaskManager::getImplementationName_static(),
        FileProvider::CreateInstance,
        fileaccess::TaskManager::getSupportedServiceNames_static() );
}
 
Reference< XInterface > SAL_CALL
FileProvider::CreateInstance(
    const Reference< XMultiServiceFactory >& xMultiServiceFactory )
{
    XServiceInfo* xP = static_cast<XServiceInfo*>(new FileProvider( comphelper::getComponentContext(xMultiServiceFactory) ));
    return Reference< XInterface >::query( xP );
}
 
 
// XContent
 
 
Reference< XContent > SAL_CALL
FileProvider::queryContent(
    const Reference< XContentIdentifier >& xIdentifier )
{
    init();
    OUString aUnc;
    bool err = fileaccess::TaskManager::getUnqFromUrl( xIdentifier->getContentIdentifier(),
                                              aUnc );
 
    if(  err )
        throw IllegalIdentifierException( THROW_WHERE );
 
    return Reference< XContent >( new BaseContent( m_pMyShell.get(), xIdentifier, aUnc ) );
}
 
 
sal_Int32 SAL_CALL
FileProvider::compareContentIds(
                const Reference< XContentIdentifier >& Id1,
                const Reference< XContentIdentifier >& Id2 )
{
    init();
    OUString aUrl1 = Id1->getContentIdentifier();
    OUString aUrl2 = Id2->getContentIdentifier();
 
    sal_Int32   iComp = aUrl1.compareTo( aUrl2 );
 
    if ( 0 != iComp )
    {
        OUString aPath1, aPath2;
 
        fileaccess::TaskManager::getUnqFromUrl( aUrl1, aPath1 );
        fileaccess::TaskManager::getUnqFromUrl( aUrl2, aPath2 );
 
        osl::FileBase::RC   error;
        osl::DirectoryItem  aItem1, aItem2;
 
        error = osl::DirectoryItem::get( aPath1, aItem1 );
        if ( error == osl::FileBase::E_None )
            error = osl::DirectoryItem::get( aPath2, aItem2 );
 
        if ( error != osl::FileBase::E_None )
            return iComp;
 
        osl::FileStatus aStatus1( osl_FileStatus_Mask_FileURL );
        osl::FileStatus aStatus2( osl_FileStatus_Mask_FileURL );
        error = aItem1.getFileStatus( aStatus1 );
        if ( error == osl::FileBase::E_None )
            error = aItem2.getFileStatus( aStatus2 );
 
        if ( error == osl::FileBase::E_None )
        {
            iComp = aStatus1.getFileURL().compareTo( aStatus2.getFileURL() );
 
// Quick hack for Windows to threat all file systems as case insensitive
#ifdef _WIN32
            if ( 0 != iComp )
            {
                error = osl::FileBase::getSystemPathFromFileURL( aStatus1.getFileURL(), aPath1 );
                if ( error == osl::FileBase::E_None )
                    error = osl::FileBase::getSystemPathFromFileURL( aStatus2.getFileURL(), aPath2 );
 
                if ( error == osl::FileBase::E_None )
                    iComp = aPath1.compareToIgnoreAsciiCase( aPath2 );
            }
#endif
        }
    }
 
    return iComp;
}
 
 
Reference< XContentIdentifier > SAL_CALL
FileProvider::createContentIdentifier(
                      const OUString& ContentId )
{
    init();
    FileContentIdentifier* p = new FileContentIdentifier( ContentId,false );
    return Reference< XContentIdentifier >( p );
}
 
 
//XPropertySetInfoImpl
 
class XPropertySetInfoImpl2
    : public cppu::OWeakObject,
      public XPropertySetInfo
{
public:
    XPropertySetInfoImpl2();
 
    // XInterface
    virtual Any SAL_CALL
    queryInterface( const Type& aType ) override;
 
    virtual void SAL_CALL
    acquire()
        throw() override;
 
    virtual void SAL_CALL
    release()
        throw() override;
 
 
    virtual Sequence< Property > SAL_CALL
    getProperties() override;
 
    virtual Property SAL_CALL
    getPropertyByName( const OUString& aName ) override;
 
    virtual sal_Bool SAL_CALL
    hasPropertyByName( const OUString& Name ) override;
 
 
private:
    Sequence< Property > m_seq;
};
 
 
XPropertySetInfoImpl2::XPropertySetInfoImpl2()
    : m_seq( 3 )
{
    m_seq[0] = Property( "HostName",
                         -1,
                         cppu::UnoType<OUString>::get(),
                         PropertyAttribute::READONLY );
 
    m_seq[1] = Property( "HomeDirectory",
                         -1,
                         cppu::UnoType<OUString>::get(),
                         PropertyAttribute::READONLY );
 
    m_seq[2] = Property( "FileSystemNotation",
                         -1,
                         cppu::UnoType<sal_Int32>::get(),
                         PropertyAttribute::READONLY );
}
 
void SAL_CALL
XPropertySetInfoImpl2::acquire()
    throw()
{
    OWeakObject::acquire();
}
 
 
void SAL_CALL
XPropertySetInfoImpl2::release()
    throw()
{
    OWeakObject::release();
}
 
 
Any SAL_CALL
XPropertySetInfoImpl2::queryInterface( const Type& rType )
{
    Any aRet = cppu::queryInterface( rType,
                                     static_cast< XPropertySetInfo* >(this) );
    return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
}
 
 
Property SAL_CALL
XPropertySetInfoImpl2::getPropertyByName( const OUString& aName )
{
    for( sal_Int32 i = 0; i < m_seq.getLength(); ++i )
        if( m_seq[i].Name == aName )
            return m_seq[i];
 
    throw UnknownPropertyException( THROW_WHERE );
}
 
 
Sequence< Property > SAL_CALL
XPropertySetInfoImpl2::getProperties()
{
    return m_seq;
}
 
 
sal_Bool SAL_CALL
XPropertySetInfoImpl2::hasPropertyByName(
    const OUString& aName )
{
    for( sal_Int32 i = 0; i < m_seq.getLength(); ++i )
        if( m_seq[i].Name == aName )
            return true;
    return false;
}
 
 
void FileProvider::initProperties()
{
    osl::MutexGuard aGuard( m_aMutex );
    if( ! m_xPropertySetInfo.is() )
    {
        osl_getLocalHostname( &m_HostName.pData );
 
#if defined ( UNX )
        m_FileSystemNotation = FileSystemNotation::UNIX_NOTATION;
#elif defined( WNT )
        m_FileSystemNotation = FileSystemNotation::DOS_NOTATION;
#else
        m_FileSystemNotation = FileSystemNotation::UNKNOWN_NOTATION;
#endif
        osl::Security aSecurity;
        aSecurity.getHomeDir( m_HomeDirectory );
 
        // static const sal_Int32 UNKNOWN_NOTATION = (sal_Int32)0;
        // static const sal_Int32 UNIX_NOTATION = (sal_Int32)1;
        // static const sal_Int32 DOS_NOTATION = (sal_Int32)2;
        // static const sal_Int32 MAC_NOTATION = (sal_Int32)3;
 
        XPropertySetInfoImpl2* p = new XPropertySetInfoImpl2();
        m_xPropertySetInfo.set( p );
    }
}
 
 
// XPropertySet
 
Reference< XPropertySetInfo > SAL_CALL
FileProvider::getPropertySetInfo(  )
{
    initProperties();
    return m_xPropertySetInfo;
}
 
 
void SAL_CALL
FileProvider::setPropertyValue( const OUString& aPropertyName,
                                const Any& )
{
    if( !(aPropertyName == "FileSystemNotation" ||
        aPropertyName == "HomeDirectory"      ||
        aPropertyName == "HostName") )
        throw UnknownPropertyException( THROW_WHERE );
}
 
 
Any SAL_CALL
FileProvider::getPropertyValue(
    const OUString& aPropertyName )
{
    initProperties();
    if( aPropertyName == "FileSystemNotation" )
    {
        return Any(m_FileSystemNotation);
    }
    else if( aPropertyName == "HomeDirectory" )
    {
        return Any(m_HomeDirectory);
    }
    else if( aPropertyName == "HostName" )
    {
        return Any(m_HostName);
    }
    else
        throw UnknownPropertyException( THROW_WHERE );
}
 
 
void SAL_CALL
FileProvider::addPropertyChangeListener(
    const OUString&,
    const Reference< XPropertyChangeListener >& )
{
}
 
 
void SAL_CALL
FileProvider::removePropertyChangeListener(
    const OUString&,
    const Reference< XPropertyChangeListener >& )
{
}
 
void SAL_CALL
FileProvider::addVetoableChangeListener(
    const OUString&,
    const Reference< XVetoableChangeListener >& )
{
}
 
 
void SAL_CALL
FileProvider::removeVetoableChangeListener(
    const OUString&,
    const Reference< XVetoableChangeListener >& )
{
}
 
 
// XFileIdentifierConverter
 
sal_Int32 SAL_CALL
FileProvider::getFileProviderLocality( const OUString& BaseURL )
{
    // If the base URL is a 'file' URL, return 10 (very 'local'), otherwise
    // return -1 (mismatch).  What is missing is a fast comparison to ASCII,
    // ignoring case:
    return BaseURL.getLength() >= 5
           && (BaseURL[0] == 'F' || BaseURL[0] == 'f')
           && (BaseURL[1] == 'I' || BaseURL[1] == 'i')
           && (BaseURL[2] == 'L' || BaseURL[2] == 'l')
           && (BaseURL[3] == 'E' || BaseURL[3] == 'e')
           && BaseURL[4] == ':' ?
               10 : -1;
}
 
OUString SAL_CALL FileProvider::getFileURLFromSystemPath( const OUString&,
                                                               const OUString& SystemPath )
{
    OUString aNormalizedPath;
    if ( osl::FileBase::getFileURLFromSystemPath( SystemPath,aNormalizedPath ) != osl::FileBase::E_None )
        return OUString();
 
    return aNormalizedPath;
}
 
OUString SAL_CALL FileProvider::getSystemPathFromFileURL( const OUString& URL )
{
    OUString aSystemPath;
    if (osl::FileBase::getSystemPathFromFileURL( URL,aSystemPath ) != osl::FileBase::E_None )
        return OUString();
 
    return aSystemPath;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1019 Compound assignment expression 'aArguments[0] >>= config' is used inside condition.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.