/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_ucb.hxx"
#include <osl/security.hxx>
#include <osl/file.hxx>
#include <osl/socket.h>
#include <cppuhelper/factory.hxx>
#ifndef _COM_SUN_STAR_BEANS_PROPERTYATTRIBBUTE_HPP_
#include <com/sun/star/beans/PropertyAttribute.hpp>
#endif
#include <com/sun/star/ucb/FileSystemNotation.hpp>
#include <com/sun/star/beans/PropertyState.hpp>
#include "filglob.hxx"
#include "filid.hxx"
#include "shell.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;

//=========================================================================
extern "C" void SAL_CALL component_getImplementationEnvironment(
	const sal_Char ** ppEnvTypeName, uno_Environment ** )
{
	*ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
}

//=========================================================================
extern "C" void * SAL_CALL component_getFactory(
	const sal_Char * pImplName, void * pServiceManager, void * )
{
	void * pRet = 0;

	Reference< XMultiServiceFactory > xSMgr(
			reinterpret_cast< XMultiServiceFactory * >( pServiceManager ) );
	Reference< XSingleServiceFactory > xFactory;

	//////////////////////////////////////////////////////////////////////
	// File Content Provider.
	//////////////////////////////////////////////////////////////////////

	if ( fileaccess::shell::getImplementationName_static().
			compareToAscii( pImplName ) == 0 )
	{
		xFactory = FileProvider::createServiceFactory( xSMgr );
	}

	//////////////////////////////////////////////////////////////////////

	if ( xFactory.is() )
	{
		xFactory->acquire();
		pRet = xFactory.get();
	}

	return pRet;
}

/****************************************************************************/
/*                                                                          */
/*                                                                          */
/*                        FileProvider                                      */
/*                                                                          */
/*                                                                          */
/****************************************************************************/



FileProvider::FileProvider( const Reference< XMultiServiceFactory >& xMultiServiceFactory )
	: m_xMultiServiceFactory( xMultiServiceFactory ),
	  m_pMyShell( 0 )
{
}


FileProvider::~FileProvider()
{
	if( m_pMyShell )
		delete m_pMyShell;
}


//////////////////////////////////////////////////////////////////////////
// XInterface
//////////////////////////////////////////////////////////////////////////

void SAL_CALL
FileProvider::acquire(
	void )
	throw()
{
  OWeakObject::acquire();
}


void SAL_CALL
FileProvider::release(
	void )
  throw()
{
  OWeakObject::release();
}


Any SAL_CALL
FileProvider::queryInterface(
	const Type& rType )
	throw( RuntimeException )
{
	Any aRet = cppu::queryInterface( 
        rType,
        SAL_STATIC_CAST( XContentProvider*, this ),
        SAL_STATIC_CAST( XInitialization*, this ),
        SAL_STATIC_CAST( XContentIdentifierFactory*, this ),
        SAL_STATIC_CAST( XServiceInfo*,     this ),
        SAL_STATIC_CAST( XTypeProvider*,    this ),
        SAL_STATIC_CAST( XFileIdentifierConverter*,this ),
        SAL_STATIC_CAST( XPropertySet*, this ) );
	return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
}

///////////////////////////////////////////////////////////////////////////////
// XInitialization

void SAL_CALL FileProvider::init()
{
    if( ! m_pMyShell )
        m_pMyShell = new shell( m_xMultiServiceFactory, this, sal_True );
}


void SAL_CALL
FileProvider::initialize( 
    const Sequence< Any >& aArguments ) 
    throw (Exception, RuntimeException) 
{
    if( ! m_pMyShell ) {
        rtl::OUString config;
        if( aArguments.getLength() > 0 && 
            (aArguments[0] >>= config) &&
            config.compareToAscii("NoConfig") == 0 )
            m_pMyShell = new shell( m_xMultiServiceFactory, this, sal_False );
        else
            m_pMyShell = new shell( m_xMultiServiceFactory, this, sal_True );
    }
}


////////////////////////////////////////////////////////////////////////////////
//
// XTypeProvider methods.


XTYPEPROVIDER_IMPL_7( FileProvider,
				   	  XTypeProvider,
				   	  XServiceInfo,
                      XInitialization,
                      XContentIdentifierFactory,
					  XPropertySet,
  					  XFileIdentifierConverter,
  				   	  XContentProvider )


////////////////////////////////////////////////////////////////////////////////
// XServiceInfo methods.

rtl::OUString SAL_CALL
FileProvider::getImplementationName()
	throw( RuntimeException )
{
	return fileaccess::shell::getImplementationName_static();
}


sal_Bool SAL_CALL
FileProvider::supportsService(
			      const rtl::OUString& ServiceName )
  throw( RuntimeException )
{
  return ServiceName == rtl::OUString::createFromAscii( "com.sun.star.ucb.FileContentProvider" );
}


Sequence< rtl::OUString > SAL_CALL
FileProvider::getSupportedServiceNames(
				       void )
  throw( RuntimeException )
{
	return fileaccess::shell::getSupportedServiceNames_static();
}



Reference< XSingleServiceFactory > SAL_CALL
FileProvider::createServiceFactory(
				   const Reference< XMultiServiceFactory >& rxServiceMgr )
{
  /**
   * Create a single service factory.<BR>
   * Note: The function pointer ComponentInstantiation points to a function throws Exception.
   *
   * @param rServiceManager		the service manager used by the implementation.
   * @param rImplementationName	the implementation name. An empty string is possible.
   * @param ComponentInstantiation the function pointer to create an object.
   * @param rServiceNames			the service supported by the implementation.
   * @return a factory that support the interfaces XServiceProvider, XServiceInfo
   *			XSingleServiceFactory and XComponent.
   *
   * @see createOneInstanceFactory
   */
  /*
   *  Reference< ::com::sun::star::XSingleServiceFactory > createSingleFactory
   *  (
   *  const ::com::sun::star::Reference< ::com::sun::star::XMultiServiceFactory > & rServiceManager,
   *  const ::rtl::OUString & rImplementationName,
   *  ComponentInstantiation pCreateFunction,

   *  const ::com::sun::star::Sequence< ::rtl::OUString > & rServiceNames
   *  );
   */

	return Reference< XSingleServiceFactory > ( cppu::createSingleFactory(
		rxServiceMgr,
		fileaccess::shell::getImplementationName_static(),
		FileProvider::CreateInstance,
		fileaccess::shell::getSupportedServiceNames_static() ) );
}

Reference< XInterface > SAL_CALL
FileProvider::CreateInstance(
	const Reference< XMultiServiceFactory >& xMultiServiceFactory )
{
	XServiceInfo* xP = (XServiceInfo*) new FileProvider( xMultiServiceFactory );
	return Reference< XInterface >::query( xP );
}



////////////////////////////////////////////////////////////////////////////////
// XContent
////////////////////////////////////////////////////////////////////////////////


Reference< XContent > SAL_CALL
FileProvider::queryContent(
	const Reference< XContentIdentifier >& xIdentifier )
	throw( IllegalIdentifierException,
		   RuntimeException)
{
    init();
	rtl::OUString aUnc;
	sal_Bool err = m_pMyShell->getUnqFromUrl( xIdentifier->getContentIdentifier(),
											  aUnc );

	if(  err )
		throw IllegalIdentifierException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ), uno::Reference< uno::XInterface >() );

    return Reference< XContent >( new BaseContent( m_pMyShell,xIdentifier,aUnc ) );
}



sal_Int32 SAL_CALL
FileProvider::compareContentIds(
				const Reference< XContentIdentifier >& Id1,
				const Reference< XContentIdentifier >& Id2 )
  throw( RuntimeException )
{
    init();
	rtl::OUString aUrl1 = Id1->getContentIdentifier();
	rtl::OUString aUrl2 = Id2->getContentIdentifier();

	sal_Int32	iComp = aUrl1.compareTo( aUrl2 );

	if ( 0 != iComp )
	{
        rtl::OUString aPath1, aPath2;

        m_pMyShell->getUnqFromUrl( aUrl1, aPath1 );
        m_pMyShell->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( FileStatusMask_FileURL );
		osl::FileStatus	aStatus2( FileStatusMask_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	WNT
			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 = rtl_ustr_compareIgnoreAsciiCase( aPath1.getStr(), aPath2.getStr() );
			}
#endif
		}
	}

	return iComp;
}



Reference< XContentIdentifier > SAL_CALL
FileProvider::createContentIdentifier(
				      const rtl::OUString& ContentId )
  throw( RuntimeException )
{
    init();
	FileContentIdentifier* p = new FileContentIdentifier( m_pMyShell,ContentId,false );
	return Reference< XContentIdentifier >( p );
}



//XPropertySetInfoImpl

class XPropertySetInfoImpl2
	: public cppu::OWeakObject,
	  public XPropertySetInfo
{
public:
	XPropertySetInfoImpl2();
	~XPropertySetInfoImpl2();

	// XInterface
	virtual Any SAL_CALL
	queryInterface(
		const Type& aType )
		throw( RuntimeException);

	virtual void SAL_CALL
	acquire(
		void )
		throw();

	virtual void SAL_CALL
	release(
		void )
		throw();


	virtual Sequence< Property > SAL_CALL
	getProperties(
		void )
		throw( RuntimeException );

	virtual Property SAL_CALL
	getPropertyByName(
		const rtl::OUString& aName )
		throw( UnknownPropertyException,
			   RuntimeException);

	virtual sal_Bool SAL_CALL
	hasPropertyByName( const rtl::OUString& Name )
		throw( RuntimeException );


private:
	Sequence< Property > m_seq;
};


XPropertySetInfoImpl2::XPropertySetInfoImpl2()
	: m_seq( 3 )
{
	m_seq[0] = Property( rtl::OUString::createFromAscii( "HostName" ),
						 -1,
						 getCppuType( static_cast< rtl::OUString* >( 0 ) ),
						 PropertyAttribute::READONLY );

	m_seq[1] = Property( rtl::OUString::createFromAscii( "HomeDirectory" ),
						 -1,
						 getCppuType( static_cast< rtl::OUString* >( 0 ) ),
						 PropertyAttribute::READONLY );

	m_seq[2] = Property( rtl::OUString::createFromAscii( "FileSystemNotation" ),
						 -1,
						 getCppuType( static_cast< sal_Int32* >( 0 ) ),
						 PropertyAttribute::READONLY );
}


XPropertySetInfoImpl2::~XPropertySetInfoImpl2()
{
	// nothing
}


void SAL_CALL
XPropertySetInfoImpl2::acquire(
	void )
	throw()
{
	OWeakObject::acquire();
}


void SAL_CALL
XPropertySetInfoImpl2::release(
	void )
	throw()
{
	OWeakObject::release();
}


Any SAL_CALL
XPropertySetInfoImpl2::queryInterface(
	const Type& rType )
	throw( RuntimeException )
{
	Any aRet = cppu::queryInterface( rType,
										  SAL_STATIC_CAST( XPropertySetInfo*,this) );
	return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
}


Property SAL_CALL
XPropertySetInfoImpl2::getPropertyByName(
	const rtl::OUString& aName )
	throw( UnknownPropertyException,
		   RuntimeException)
{
	for( sal_Int32 i = 0; i < m_seq.getLength(); ++i )
		if( m_seq[i].Name == aName )
			return m_seq[i];

	throw UnknownPropertyException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ), uno::Reference< uno::XInterface >() );
}



Sequence< Property > SAL_CALL
XPropertySetInfoImpl2::getProperties(
	void )
	throw( RuntimeException )
{
	return m_seq;
}


sal_Bool SAL_CALL
XPropertySetInfoImpl2::hasPropertyByName(
	const rtl::OUString& aName )
	throw( RuntimeException )
{
	for( sal_Int32 i = 0; i < m_seq.getLength(); ++i )
		if( m_seq[i].Name == aName )
			return true;
	return false;
}





void SAL_CALL FileProvider::initProperties( void )
{
	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 ) || defined( OS2 )
		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 = Reference< XPropertySetInfo >( p );
	}
}


// XPropertySet

Reference< XPropertySetInfo > SAL_CALL
FileProvider::getPropertySetInfo(  )
	throw( RuntimeException )
{
	initProperties();
	return m_xPropertySetInfo;
}


void SAL_CALL
FileProvider::setPropertyValue( const rtl::OUString& aPropertyName,
								const Any& )
	throw( UnknownPropertyException,
		   PropertyVetoException,
		   IllegalArgumentException,
		   WrappedTargetException,
		   RuntimeException )
{
	if( aPropertyName.compareToAscii( "FileSystemNotation" ) == 0 ||
		aPropertyName.compareToAscii( "HomeDirectory" ) == 0      ||
		aPropertyName.compareToAscii( "HostName" ) == 0 )
		return;
	else
		throw UnknownPropertyException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ), uno::Reference< uno::XInterface >() );
}



Any SAL_CALL
FileProvider::getPropertyValue(
	const rtl::OUString& aPropertyName )
	throw( UnknownPropertyException,
		   WrappedTargetException,
		   RuntimeException )
{
	initProperties();
	if( aPropertyName.compareToAscii( "FileSystemNotation" ) == 0 )
	{
		Any aAny;
		aAny <<= m_FileSystemNotation;
		return aAny;
	}
	else if( aPropertyName.compareToAscii( "HomeDirectory" ) == 0 )
	{
		Any aAny;
		aAny <<= m_HomeDirectory;
		return aAny;
	}
	else if( aPropertyName.compareToAscii( "HostName" ) == 0 )
	{
		Any aAny;
		aAny <<= m_HostName;
		return aAny;
	}
	else
		throw UnknownPropertyException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ), uno::Reference< uno::XInterface >() );
}


void SAL_CALL
FileProvider::addPropertyChangeListener(
	const rtl::OUString&,
	const Reference< XPropertyChangeListener >& )
	throw( UnknownPropertyException,
		   WrappedTargetException,
		   RuntimeException)
{
	return;
}


void SAL_CALL
FileProvider::removePropertyChangeListener(
	const rtl::OUString&,
	const Reference< XPropertyChangeListener >& )
	throw( UnknownPropertyException,
		   WrappedTargetException,
		   RuntimeException )
{
	return;
}

void SAL_CALL
FileProvider::addVetoableChangeListener(
	const rtl::OUString&,
	const Reference< XVetoableChangeListener >& )
	throw( UnknownPropertyException,
		   WrappedTargetException,
		   RuntimeException )
{
	return;
}


void SAL_CALL
FileProvider::removeVetoableChangeListener(
	const rtl::OUString&,
	const Reference< XVetoableChangeListener >& )
	throw( UnknownPropertyException,
		   WrappedTargetException,
		   RuntimeException)
{
	return;
}



// XFileIdentifierConverter

sal_Int32 SAL_CALL
FileProvider::getFileProviderLocality( const rtl::OUString& BaseURL )
	throw( RuntimeException )
{
	// If the base URL is a 'file' URL, return 10 (very 'local'), otherwise
	// return -1 (missmatch).  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;
}

rtl::OUString SAL_CALL FileProvider::getFileURLFromSystemPath( const rtl::OUString&,
															   const rtl::OUString& SystemPath )
	throw( RuntimeException )
{
	rtl::OUString aNormalizedPath;
	if ( osl::FileBase::getFileURLFromSystemPath( SystemPath,aNormalizedPath ) != osl::FileBase::E_None )
		return rtl::OUString();
    
    return aNormalizedPath;
}

rtl::OUString SAL_CALL FileProvider::getSystemPathFromFileURL( const rtl::OUString& URL )
	throw( RuntimeException )
{
	rtl::OUString aSystemPath;
    if (osl::FileBase::getSystemPathFromFileURL( URL,aSystemPath ) != osl::FileBase::E_None )
		return rtl::OUString();

	return aSystemPath;
}

