blob: a15b1c815f2822dde8bba2790bcd4c6ed65c859a [file] [log] [blame]
/**************************************************************
*
* 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 <string.h>
#include <unistd.h>
#include <sys/types.h>
#include "osl/time.h"
#include <osl/diagnose.h>
#include "osl/doublecheckedlocking.h"
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/beans/PropertySetInfoChange.hpp>
#include <com/sun/star/beans/PropertySetInfoChangeEvent.hpp>
#include <com/sun/star/io/XActiveDataSink.hpp>
#include <com/sun/star/io/XOutputStream.hpp>
#include <com/sun/star/lang/IllegalAccessException.hpp>
#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
#include <com/sun/star/ucb/InsertCommandArgument.hpp>
#include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
#include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
#include <com/sun/star/ucb/InteractiveNetworkGeneralException.hpp>
#include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
#include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp>
#include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
#include <com/sun/star/ucb/NameClash.hpp>
#include <com/sun/star/ucb/NameClashException.hpp>
#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
#include <com/sun/star/ucb/OpenMode.hpp>
#include <com/sun/star/ucb/PostCommandArgument2.hpp>
#include <com/sun/star/ucb/TransferInfo.hpp>
#include <com/sun/star/ucb/XCommandInfo.hpp>
#include <com/sun/star/ucb/XPersistentPropertySet.hpp>
#include <com/sun/star/ucb/MissingInputStreamException.hpp>
#include <com/sun/star/ucb/MissingPropertiesException.hpp>
#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
#include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
#include <com/sun/star/ucb/NameClashException.hpp>
#include <ucbhelper/contentidentifier.hxx>
#include <ucbhelper/propertyvalueset.hxx>
#include <ucbhelper/interactionrequest.hxx>
#include <ucbhelper/cancelcommandexecution.hxx>
#include <ucbhelper/simpleauthenticationrequest.hxx>
const int TRANSFER_BUFFER_SIZE = 65536;
/*
* NB. Name escaping is done only for URIs
* the 'Title' property is unescaped on set/get
*/
#include <libgnomevfs/gnome-vfs-utils.h>
#include <libgnomevfs/gnome-vfs-result.h>
#include <libgnomevfs/gnome-vfs-standard-callbacks.h>
extern "C" { // missing in the header: doh.
# include <libgnomevfs/gnome-vfs-module-callback.h>
}
#include "gvfs_content.hxx"
#include "gvfs_provider.hxx"
#include "gvfs_directory.hxx"
#include "gvfs_stream.hxx"
using namespace gvfs;
using namespace com::sun::star;
#define CLEAR_INFO(info) memset((info), 0, sizeof ((info)[0]))
static char *
OUStringToGnome( const rtl::OUString &str )
{
rtl::OString aTempStr = rtl::OUStringToOString( str, RTL_TEXTENCODING_UTF8 );
return g_strdup( aTempStr.getStr() );
}
static rtl::OUString
GnomeToOUString( const char *utf8_str)
{
if (!utf8_str)
return rtl::OUString();
else
return rtl::OUString( utf8_str, strlen( utf8_str ), RTL_TEXTENCODING_UTF8 );
}
Content::Content(
const uno::Reference< lang::XMultiServiceFactory >& rxSMgr,
ContentProvider* pProvider,
const uno::Reference< ucb::XContentIdentifier >& Identifier)
throw ( ucb::ContentCreationException )
: ContentImplHelper( rxSMgr, pProvider, Identifier ),
m_pProvider( pProvider ),
m_bTransient( sal_False )
{
CLEAR_INFO (&m_info);
#ifdef DEBUG
g_warning ("New Content ('%s')", getURI());
#endif
}
Content::Content(
const uno::Reference< lang::XMultiServiceFactory >& rxSMgr,
ContentProvider * pProvider,
const uno::Reference< ucb::XContentIdentifier >& Identifier,
sal_Bool IsFolder)
throw ( ucb::ContentCreationException )
: ContentImplHelper( rxSMgr, pProvider, Identifier ),
m_pProvider( pProvider ),
m_bTransient( sal_True )
{
CLEAR_INFO (&m_info);
#ifdef DEBUG
g_warning ("New Transient content ('%s') (%d)", getURI(), IsFolder);
#endif
// m_info.name = FIXME: set name ?
m_info.valid_fields = GNOME_VFS_FILE_INFO_FIELDS_TYPE;
m_info.type = IsFolder ? GNOME_VFS_FILE_TYPE_DIRECTORY :
GNOME_VFS_FILE_TYPE_REGULAR;
}
// virtual
Content::~Content()
{
gnome_vfs_file_info_clear( &m_info );
}
//
// XInterface methods.
//
void SAL_CALL Content::acquire()
throw( )
{
ContentImplHelper::acquire();
}
void SAL_CALL Content::release()
throw( )
{
ContentImplHelper::release();
}
uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType )
throw ( uno::RuntimeException )
{
// Note: isFolder may require network activities! So call it only
// if it is really necessary!!!
uno::Any aRet = cppu::queryInterface( rType,
static_cast< ucb::XContentCreator * >( this ) );
if ( aRet.hasValue() )
return isFolder( uno::Reference< ucb::XCommandEnvironment >() )
? aRet : uno::Any();
else
return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface( rType );
}
//
// XTypeProvider methods.
//
XTYPEPROVIDER_COMMON_IMPL( Content );
uno::Sequence< uno::Type > SAL_CALL Content::getTypes()
throw( uno::RuntimeException )
{
static cppu::OTypeCollection *pFolderCollection = NULL;
static cppu::OTypeCollection *pFileCollection = NULL;
if (!pFolderCollection) {
osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );
if (!pFolderCollection) {
static cppu::OTypeCollection aFolderCollection
(CPPU_TYPE_REF( lang::XTypeProvider ),
CPPU_TYPE_REF( lang::XServiceInfo ),
CPPU_TYPE_REF( lang::XComponent ),
CPPU_TYPE_REF( ucb::XContent ),
CPPU_TYPE_REF( ucb::XCommandProcessor ),
CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
CPPU_TYPE_REF( beans::XPropertyContainer ),
CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
CPPU_TYPE_REF( container::XChild ),
CPPU_TYPE_REF( ucb::XContentCreator ) ); // !!
static cppu::OTypeCollection aFileCollection
(CPPU_TYPE_REF( lang::XTypeProvider ),
CPPU_TYPE_REF( lang::XServiceInfo ),
CPPU_TYPE_REF( lang::XComponent ),
CPPU_TYPE_REF( ucb::XContent ),
CPPU_TYPE_REF( ucb::XCommandProcessor ),
CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
CPPU_TYPE_REF( beans::XPropertyContainer ),
CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
CPPU_TYPE_REF( container::XChild ) );
pFolderCollection = &aFolderCollection;
pFileCollection = &aFileCollection;
OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
}
}
else {
OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
}
if ( isFolder( uno::Reference< ucb::XCommandEnvironment >() ) )
return pFolderCollection->getTypes();
else
return pFileCollection->getTypes();
}
//
// XServiceInfo methods.
//
rtl::OUString SAL_CALL Content::getImplementationName()
throw( uno::RuntimeException )
{
return rtl::OUString::createFromAscii("com.sun.star.comp.GnomeVFSContent" );
}
uno::Sequence< rtl::OUString > SAL_CALL Content::getSupportedServiceNames()
throw( uno::RuntimeException )
{
uno::Sequence< rtl::OUString > aSNS( 1 );
aSNS.getArray()[ 0 ] = rtl::OUString::createFromAscii(
"com.sun.star.ucb.GnomeVFSContent" );
return aSNS;
}
//
// XContent methods.
//
rtl::OUString SAL_CALL Content::getContentType()
throw( uno::RuntimeException )
{
if ( isFolder( uno::Reference< ucb::XCommandEnvironment >() ) )
return rtl::OUString::createFromAscii( GVFS_FOLDER_TYPE );
else
return rtl::OUString::createFromAscii( GVFS_FILE_TYPE );
}
//
// XCommandProcessor methods.
//
uno::Any Content::getBadArgExcept()
{
return uno::makeAny( lang::IllegalArgumentException
( rtl::OUString::createFromAscii( "Wrong argument type!" ),
static_cast< cppu::OWeakObject * >( this ),
-1 ) );
}
#include <stdio.h>
uno::Any SAL_CALL Content::execute(
const ucb::Command& aCommand,
sal_Int32 /*CommandId*/,
const uno::Reference< ucb::XCommandEnvironment >& xEnv )
throw( uno::Exception,
ucb::CommandAbortedException,
uno::RuntimeException )
{
uno::Any aRet;
#ifdef DEBUG
{
uno::Reference< task::XInteractionHandler > xIH;
if ( xEnv.is() )
xIH = xEnv->getInteractionHandler();
g_warning( "Execute command: '%s' with %s interaction env",
OUStringToGnome( aCommand.Name ),
xIH.is() ? "" : "NO" );
}
#endif
#define COMMAND_IS(cmd,name) ( (cmd).Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( name ) ) )
if ( COMMAND_IS( aCommand, "getPropertyValues" ) ) {
uno::Sequence< beans::Property > Properties;
if ( !( aCommand.Argument >>= Properties ) )
ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
aRet <<= getPropertyValues( Properties, xEnv );
} else if ( COMMAND_IS( aCommand, "setPropertyValues" ) ) {
uno::Sequence< beans::PropertyValue > aProperties;
if ( !( aCommand.Argument >>= aProperties ) ||
!aProperties.getLength() )
ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
aRet <<= setPropertyValues( aProperties, xEnv );
} else if ( COMMAND_IS( aCommand, "getPropertySetInfo" ) ) {
aRet <<= getPropertySetInfo( xEnv, sal_False );
} else if ( COMMAND_IS( aCommand, "getCommandInfo" ) ) {
aRet <<= getCommandInfo( xEnv, sal_False );
} else if ( COMMAND_IS( aCommand, "open" ) ) {
rtl::OUString str = m_xIdentifier->getContentIdentifier();
rtl::OString stra(
str.getStr(),
str.getLength(),
RTL_TEXTENCODING_UTF8);
ucb::OpenCommandArgument2 aOpenCommand;
if ( !( aCommand.Argument >>= aOpenCommand ) )
ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
sal_Bool bOpenFolder =
( ( aOpenCommand.Mode == ucb::OpenMode::ALL ) ||
( aOpenCommand.Mode == ucb::OpenMode::FOLDERS ) ||
( aOpenCommand.Mode == ucb::OpenMode::DOCUMENTS ) );
if ( bOpenFolder && isFolder( xEnv ) ) {
uno::Reference< ucb::XDynamicResultSet > xSet
= new DynamicResultSet(m_xSMgr, this, aOpenCommand, xEnv );
aRet <<= xSet;
} else if ( aOpenCommand.Sink.is() ) {
if ( ( aOpenCommand.Mode
== ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
( aOpenCommand.Mode
== ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) ) {
ucbhelper::cancelCommandExecution
( uno::makeAny ( ucb::UnsupportedOpenModeException
( rtl::OUString(),
static_cast< cppu::OWeakObject * >( this ),
sal_Int16( aOpenCommand.Mode ) ) ),
xEnv );
}
if ( !feedSink( aOpenCommand.Sink, xEnv ) ) {
// Note: aOpenCommand.Sink may contain an XStream
// implementation. Support for this type of
// sink is optional...
#ifdef DEBUG
g_warning ("Failed to load data from '%s'", getURI());
#endif
ucbhelper::cancelCommandExecution
( uno::makeAny (ucb::UnsupportedDataSinkException
( rtl::OUString(),
static_cast< cppu::OWeakObject * >( this ),
aOpenCommand.Sink ) ),
xEnv );
}
}
#ifdef DEBUG
else
g_warning ("Open falling through ...");
#endif
} else if ( COMMAND_IS( aCommand, "createNewContent" ) && isFolder( xEnv ) ) {
ucb::ContentInfo arg;
if ( !( aCommand.Argument >>= arg ) )
ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
aRet <<= createNewContent( arg );
} else if ( COMMAND_IS( aCommand, "insert" ) ) {
ucb::InsertCommandArgument arg;
if ( !( aCommand.Argument >>= arg ) )
ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
insert( arg.Data, arg.ReplaceExisting, xEnv );
} else if ( COMMAND_IS( aCommand, "delete" ) ) {
sal_Bool bDeletePhysical = sal_False;
aCommand.Argument >>= bDeletePhysical;
::rtl::OString aURI = getOURI();
GnomeVFSResult result = gnome_vfs_unlink( aURI.getStr());
if (result != GNOME_VFS_OK)
cancelCommandExecution( result, xEnv, sal_True );
destroy( bDeletePhysical );
} else if ( COMMAND_IS( aCommand, "transfer" ) && isFolder( xEnv ) ) {
ucb::TransferInfo transferArgs;
if ( !( aCommand.Argument >>= transferArgs ) )
ucbhelper::cancelCommandExecution( getBadArgExcept(), xEnv );
transfer( transferArgs, xEnv );
} else { // Unsuported
#ifdef DEBUG
g_warning( "Unsupported command: '%s'",
OUStringToGnome( aCommand.Name ) );
#endif
ucbhelper::cancelCommandExecution
( uno::makeAny( ucb::UnsupportedCommandException
( rtl::OUString(),
static_cast< cppu::OWeakObject * >( this ) ) ),
xEnv );
}
#undef COMMAND_IS
return aRet;
}
void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
throw( uno::RuntimeException )
{
// FIXME: we should use the GnomeVFSCancellation APIs here ...
}
//
// XContentCreator methods.
//
uno::Sequence< ucb::ContentInfo > Content::queryCreatableContentsInfo(
const uno::Reference< ucb::XCommandEnvironment >& xEnv)
throw( uno::RuntimeException )
{
if ( isFolder( xEnv ) )
{
uno::Sequence< ucb::ContentInfo > seq(2);
// Minimum set of props we really need
uno::Sequence< beans::Property > props( 1 );
props[0] = beans::Property(
rtl::OUString::createFromAscii( "Title" ),
-1,
getCppuType( static_cast< rtl::OUString* >( 0 ) ),
beans::PropertyAttribute::MAYBEVOID | beans::PropertyAttribute::BOUND );
// file
seq[0].Type = rtl::OUString::createFromAscii( GVFS_FILE_TYPE );
seq[0].Attributes = ( ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM |
ucb::ContentInfoAttribute::KIND_DOCUMENT );
seq[0].Properties = props;
// folder
seq[1].Type = rtl::OUString::createFromAscii( GVFS_FOLDER_TYPE );
seq[1].Attributes = ucb::ContentInfoAttribute::KIND_FOLDER;
seq[1].Properties = props;
return seq;
}
else
{
return uno::Sequence< ucb::ContentInfo >();
}
}
uno::Sequence< ucb::ContentInfo > SAL_CALL Content::queryCreatableContentsInfo()
throw( uno::RuntimeException )
{
return queryCreatableContentsInfo( uno::Reference< ucb::XCommandEnvironment >() );
}
uno::Reference< ucb::XContent > SAL_CALL
Content::createNewContent( const ucb::ContentInfo& Info )
throw( uno::RuntimeException )
{
bool create_document;
const char *name;
if ( Info.Type.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( GVFS_FILE_TYPE ) ) )
create_document = true;
else if ( Info.Type.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( GVFS_FOLDER_TYPE ) ) )
create_document = false;
else {
#ifdef DEBUG
g_warning( "Failed to create new content '%s'",
OUStringToGnome( Info.Type ) );
#endif
return uno::Reference< ucb::XContent >();
}
#ifdef DEBUG
g_warning( "createNewContent (%d)", (int) create_document );
#endif
rtl::OUString aURL = getOUURI();
if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
aURL += rtl::OUString::createFromAscii( "/" );
name = create_document ? "[New_Content]" : "[New_Collection]";
// This looks problematic to me cf. webdav
aURL += rtl::OUString::createFromAscii( name );
uno::Reference< ucb::XContentIdentifier > xId
( new ::ucbhelper::ContentIdentifier( m_xSMgr, aURL ) );
try {
return new ::gvfs::Content( m_xSMgr, m_pProvider, xId, !create_document );
} catch ( ucb::ContentCreationException & ) {
return uno::Reference< ucb::XContent >();
}
}
rtl::OUString Content::getParentURL()
{
rtl::OUString aParentURL;
// <scheme>:// -> ""
// <scheme>://foo -> ""
// <scheme>://foo/ -> ""
// <scheme>://foo/bar -> <scheme>://foo/
// <scheme>://foo/bar/ -> <scheme>://foo/
// <scheme>://foo/bar/abc -> <scheme>://foo/bar/
rtl::OUString aURL = getOUURI();
sal_Int32 nPos = aURL.lastIndexOf( '/' );
if ( nPos == ( aURL.getLength() - 1 ) ) {
// Trailing slash found. Skip.
nPos = aURL.lastIndexOf( '/', nPos );
}
sal_Int32 nPos1 = aURL.lastIndexOf( '/', nPos );
if ( nPos1 != -1 )
nPos1 = aURL.lastIndexOf( '/', nPos1 );
if ( nPos1 != -1 )
aParentURL = rtl::OUString( aURL.copy( 0, nPos + 1 ) );
#ifdef DEBUG
g_warning ("getParentURL '%s' -> '%s'",
getURI(), rtl::OUStringToOString( aParentURL, RTL_TEXTENCODING_UTF8).getStr() );
#endif
return aParentURL;
}
static util::DateTime
getDateFromUnix (time_t t)
{
TimeValue tv;
tv.Nanosec = 0;
tv.Seconds = t;
oslDateTime dt;
if ( osl_getDateTimeFromTimeValue( &tv, &dt ) )
return util::DateTime( 0, dt.Seconds, dt.Minutes, dt.Hours,
dt.Day, dt.Month, dt.Year);
else
return util::DateTime();
}
uno::Reference< sdbc::XRow > Content::getPropertyValues(
const uno::Sequence< beans::Property >& rProperties,
const uno::Reference< ucb::XCommandEnvironment >& xEnv )
{
int nProps;
GnomeVFSResult result;
uno::Sequence< beans::Property > allProperties;
if( ( result = getInfo( xEnv ) ) != GNOME_VFS_OK )
cancelCommandExecution( result, xEnv, sal_False );
const beans::Property* pProps;
if( rProperties.getLength() ) {
nProps = rProperties.getLength();
pProps = rProperties.getConstArray();
} else {
allProperties = getPropertySetInfo( xEnv )->getProperties();
nProps = allProperties.getLength();
pProps = allProperties.getConstArray();
}
rtl::Reference< ::ucbhelper::PropertyValueSet > xRow
= new ::ucbhelper::PropertyValueSet( m_xSMgr );
osl::Guard< osl::Mutex > aGuard( m_aMutex );
for( sal_Int32 n = 0; n < nProps; ++n ) {
const beans::Property& rProp = pProps[ n ];
if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Title" ) ) ) {
if (m_info.name && m_info.name[0] == '/')
g_warning ("Odd NFS title on item '%s' == '%s'",
getURI(), m_info.name);
xRow->appendString( rProp, GnomeToOUString( m_info.name ) );
}
else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "ContentType" ) ) )
xRow->appendString( rProp, getContentType () );
else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsDocument" ) ) ) {
if (m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_TYPE)
xRow->appendBoolean( rProp, ( m_info.type == GNOME_VFS_FILE_TYPE_REGULAR ||
m_info.type == GNOME_VFS_FILE_TYPE_UNKNOWN ) );
else
xRow->appendVoid( rProp );
}
else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsFolder" ) ) ) {
if (m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_TYPE)
xRow->appendBoolean( rProp, ( m_info.type == GNOME_VFS_FILE_TYPE_DIRECTORY ) );
else
xRow->appendVoid( rProp );
}
else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsReadOnly" ) ) ) {
GnomeVFSFileInfo* fileInfo = gnome_vfs_file_info_new ();
::rtl::OString aURI = getOURI();
gnome_vfs_get_file_info( aURI.getStr(), fileInfo,
GNOME_VFS_FILE_INFO_GET_ACCESS_RIGHTS );
if (fileInfo->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_ACCESS) {
bool read_only = true;
if (fileInfo->permissions & GNOME_VFS_PERM_ACCESS_WRITABLE)
read_only = false;
xRow->appendBoolean( rProp, read_only );
} else
xRow->appendVoid( rProp );
gnome_vfs_file_info_unref (fileInfo);
}
else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Size" ) ) ) {
if (m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)
xRow->appendLong( rProp, m_info.size );
else
xRow->appendVoid( rProp );
}
else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsHidden" ) ) )
xRow->appendBoolean( rProp, ( m_info.name && m_info.name[0] == '.' ) );
else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsVolume" ) ) ||
rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsCompactDisk" ) ) )
xRow->appendBoolean( rProp, sal_False );
else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DateCreated" ) ) ) {
if (m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_CTIME)
xRow->appendTimestamp( rProp, getDateFromUnix( m_info.ctime ) );
else
xRow->appendVoid( rProp );
}
else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DateModified" ) ) ) {
if (m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MTIME)
xRow->appendTimestamp( rProp, getDateFromUnix( m_info.mtime ) );
else
xRow->appendVoid( rProp );
}
else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "MediaType" ) ) ) {
// We do this by sniffing in gnome-vfs; rather expensively.
#ifdef DEBUG
g_warning ("FIXME: Requested mime-type - an expensive op. indeed!");
#endif
xRow->appendVoid( rProp );
} else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "CreatableContentsInfo" ) ) )
xRow->appendObject( rProp, uno::makeAny( queryCreatableContentsInfo( xEnv ) ) );
else {
xRow->appendVoid( rProp );
}
}
#ifdef DEBUG
g_warning ("getPropertyValues on '%s' %d properties returned (of %d)",
getURI(), (int)xRow->getLength(), (int)nProps);
#endif
return uno::Reference< sdbc::XRow >( xRow.get() );
}
static lang::IllegalAccessException
getReadOnlyException( Content *ctnt )
{
return lang::IllegalAccessException
( rtl::OUString::createFromAscii( "Property is read-only!" ),
static_cast< cppu::OWeakObject * >( ctnt ) );
}
rtl::OUString
Content::makeNewURL( const char */*newName*/ )
{
rtl::OUString aNewURL = getParentURL();
if ( aNewURL.lastIndexOf( '/' ) != ( aNewURL.getLength() - 1 ) )
aNewURL += rtl::OUString::createFromAscii( "/" );
char *name = gnome_vfs_escape_string( m_info.name );
aNewURL += GnomeToOUString( name );
g_free( name );
return aNewURL;
}
// This is slightly complicated by needing to support either 'move' or 'setname'
GnomeVFSResult
Content::doSetFileInfo( const GnomeVFSFileInfo *newInfo,
GnomeVFSSetFileInfoMask setMask,
const uno::Reference< ucb::XCommandEnvironment >& /*xEnv*/ )
{
GnomeVFSResult result = GNOME_VFS_OK;
g_assert (!m_bTransient);
::rtl::OString aURI = getOURI();
osl::Guard< osl::Mutex > aGuard( m_aMutex );
// The simple approach:
if( setMask != GNOME_VFS_SET_FILE_INFO_NONE )
result = gnome_vfs_set_file_info // missed a const in the API there
( aURI.getStr(), (GnomeVFSFileInfo *)newInfo, setMask );
if ( result == GNOME_VFS_ERROR_NOT_SUPPORTED &&
( setMask & GNOME_VFS_SET_FILE_INFO_NAME ) ) {
// Try a move instead
#ifdef DEBUG
g_warning( "SetFileInfo not supported on '%s'", getURI() );
#endif
char *newURI = OUStringToGnome( makeNewURL( newInfo->name ) );
result = gnome_vfs_move( aURI.getStr(), newURI, FALSE);
g_free (newURI);
}
return result;
}
uno::Sequence< uno::Any > Content::setPropertyValues(
const uno::Sequence< beans::PropertyValue >& rValues,
const uno::Reference< ucb::XCommandEnvironment >& xEnv )
{
rtl::OUString aNewTitle;
GnomeVFSFileInfo newInfo;
int setMask = GNOME_VFS_SET_FILE_INFO_NONE;
getInfo( xEnv );
osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
gnome_vfs_file_info_copy( &newInfo, &m_info );
Authentication aAuth( xEnv );
int nChanged = 0, nTitlePos = 0;
uno::Sequence< uno::Any > aRet( rValues.getLength() );
uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() );
beans::PropertyChangeEvent aEvent;
aEvent.Source = static_cast< cppu::OWeakObject * >( this );
aEvent.Further = sal_False;
aEvent.PropertyHandle = -1;
// aEvent.PropertyName = fill in later ...
// aEvent.OldValue =
// aEvent.NewValue =
int nCount = rValues.getLength();
const beans::PropertyValue* pValues = rValues.getConstArray();
for ( sal_Int32 n = 0; n < nCount; ++n ) {
const beans::PropertyValue& rValue = pValues[ n ];
#ifdef DEBUG
g_warning( "Set prop '%s'", OUStringToGnome( rValue.Name ) );
#endif
if ( rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "ContentType" ) ) ||
rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "MediaType" ) ) ||
rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsDocument" ) ) ||
rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsFolder" ) ) ||
rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Size" ) ) ||
rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "CreatableContentsInfo" ) ) )
aRet[ n ] <<= getReadOnlyException( this );
else if ( rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Title" ) ) ) {
if ( rValue.Value >>= aNewTitle ) {
if ( aNewTitle.getLength() <= 0 )
aRet[ n ] <<= lang::IllegalArgumentException
( rtl::OUString::createFromAscii( "Empty title not allowed!" ),
static_cast< cppu::OWeakObject * >( this ), -1 );
else {
char *newName = OUStringToGnome( aNewTitle );
if( !newName || !m_info.name || strcmp( newName, m_info.name ) ) {
#ifdef DEBUG
g_warning ("Set new name to '%s'", newName);
#endif
aEvent.PropertyName = rtl::OUString::createFromAscii( "Title" );
aEvent.OldValue = uno::makeAny( GnomeToOUString( newInfo.name ) );
aEvent.NewValue = uno::makeAny( aNewTitle );
aChanges.getArray()[ nChanged ] = aEvent;
nTitlePos = nChanged++;
newInfo.name = newName;
setMask |= GNOME_VFS_SET_FILE_INFO_NAME;
} else // same name
g_free (newName);
}
} else
aRet[ n ] <<= beans::IllegalTypeException
( rtl::OUString::createFromAscii( "Property value has wrong type!" ),
static_cast< cppu::OWeakObject * >( this ) );
} else if ( rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DateCreated" ) ) ||
rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DateModified" ) ) ) {
// FIXME: should be able to set the timestamps
aRet[ n ] <<= getReadOnlyException( this );
} else {
#ifdef DEBUG
g_warning( "Unhandled property '%s'", OUStringToGnome( rValue.Name ) );
#endif
aRet[ n ] <<= getReadOnlyException( this );
}
}
GnomeVFSResult result = GNOME_VFS_OK;
if ( !m_bTransient &&
( result = doSetFileInfo( &newInfo,
(GnomeVFSSetFileInfoMask) setMask,
xEnv ) ) != GNOME_VFS_OK ) {
for (int i = 0; i < nChanged; i++)
aRet[ i ] <<= mapVFSException( result, sal_True );
}
if ( result == GNOME_VFS_OK) {
gnome_vfs_file_info_copy( &m_info, &newInfo );
if ( setMask & GNOME_VFS_SET_FILE_INFO_NAME ) {
uno::Reference< ucb::XContentIdentifier > xNewId
= new ::ucbhelper::ContentIdentifier(
m_xSMgr, makeNewURL( newInfo.name ) );
aGuard.clear();
if (!exchangeIdentity( xNewId ) )
aRet[ nTitlePos ] <<= uno::Exception
( rtl::OUString::createFromAscii( "Exchange failed!" ),
static_cast< cppu::OWeakObject * >( this ) );
}
}
gnome_vfs_file_info_clear( &newInfo );
if ( nChanged > 0 ) {
aGuard.clear();
aChanges.realloc( nChanged );
notifyPropertiesChange( aChanges );
}
return aRet;
}
void Content::queryChildren( ContentRefList& rChildren )
{
// Obtain a list with a snapshot of all currently instanciated contents
// from provider and extract the contents which are direct children
// of this content.
::ucbhelper::ContentRefList aAllContents;
m_xProvider->queryExistingContents( aAllContents );
rtl::OUString aURL = getOUURI();
sal_Int32 nURLPos = aURL.lastIndexOf( '/' );
if ( nURLPos != ( aURL.getLength() - 1 ) )
aURL += rtl::OUString::createFromAscii( "/" );
sal_Int32 nLen = aURL.getLength();
::ucbhelper::ContentRefList::const_iterator it = aAllContents.begin();
::ucbhelper::ContentRefList::const_iterator end = aAllContents.end();
while ( it != end ) {
::ucbhelper::ContentImplHelperRef xChild = (*it);
rtl::OUString aChildURL
= xChild->getIdentifier()->getContentIdentifier();
// Is aURL a prefix of aChildURL?
if ( ( aChildURL.getLength() > nLen ) &&
( aChildURL.compareTo( aURL, nLen ) == 0 ) ) {
sal_Int32 nPos = nLen;
nPos = aChildURL.indexOf( '/', nPos );
if ( ( nPos == -1 ) ||
( nPos == ( aChildURL.getLength() - 1 ) ) ) {
// No further slashes / only a final slash. It's a child!
rChildren.push_back( ::gvfs::Content::ContentRef
(static_cast< ::gvfs::Content * >(xChild.get() ) ) );
}
}
++it;
}
}
void Content::insert(
const uno::Reference< io::XInputStream > &xInputStream,
sal_Bool bReplaceExisting,
const uno::Reference< ucb::XCommandEnvironment > &xEnv )
throw( uno::Exception )
{
osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
#ifdef DEBUG
g_warning( "Insert '%s' (%d) (0x%x:%d)", getURI(), bReplaceExisting,
m_info.valid_fields, m_info.type );
#endif
GnomeVFSResult result = getInfo( xEnv );
// a racy design indeed.
if( !bReplaceExisting && !m_bTransient &&
result != GNOME_VFS_ERROR_NOT_FOUND) {
#ifdef DEBUG
g_warning ("Nasty error inserting to '%s' ('%s')",
getURI(), gnome_vfs_result_to_string( result ));
#endif
cancelCommandExecution( GNOME_VFS_ERROR_FILE_EXISTS, xEnv, sal_True );
}
if ( m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_TYPE &&
m_info.type == GNOME_VFS_FILE_TYPE_DIRECTORY ) {
::rtl::OString aURI = getOURI();
int perm;
perm = ( GNOME_VFS_PERM_USER_ALL |
GNOME_VFS_PERM_GROUP_READ |
GNOME_VFS_PERM_OTHER_READ );
#ifdef DEBUG
g_warning ("Make directory");
#endif
result = gnome_vfs_make_directory( aURI.getStr(), perm );
if( result != GNOME_VFS_OK )
cancelCommandExecution( result, xEnv, sal_True );
return;
}
if ( !xInputStream.is() ) {
// FIXME: slightly unclear whether to accept this and create an empty file
ucbhelper::cancelCommandExecution
( uno::makeAny
( ucb::MissingInputStreamException
( rtl::OUString(),
static_cast< cppu::OWeakObject * >( this ) ) ),
xEnv );
}
GnomeVFSHandle *handle = NULL;
::rtl::OString aURI = getOURI();
result = GNOME_VFS_OK;
if ( bReplaceExisting ) {
Authentication aAuth( xEnv );
result = gnome_vfs_open( &handle, aURI.getStr(),
GNOME_VFS_OPEN_WRITE );
}
if ( result != GNOME_VFS_OK ) {
int perm;
Authentication aAuth( xEnv );
perm = ( ( GNOME_VFS_PERM_USER_WRITE | GNOME_VFS_PERM_USER_READ ) |
( GNOME_VFS_PERM_GROUP_WRITE | GNOME_VFS_PERM_GROUP_READ ) );
result = gnome_vfs_create
( &handle, aURI.getStr(), GNOME_VFS_OPEN_WRITE, TRUE, perm );
}
if( result != GNOME_VFS_OK )
cancelCommandExecution( result, xEnv, sal_True );
if ( !xInputStream.is() ) {
result = gnome_vfs_close( handle );
if (result != GNOME_VFS_OK)
cancelCommandExecution( result, xEnv, sal_True );
} else { // copy it over
uno::Reference < io::XOutputStream > xOutput =
new gvfs::Stream( handle, &m_info );
copyData( xInputStream, xOutput );
}
if (m_bTransient) {
m_bTransient = sal_False;
aGuard.clear();
inserted();
}
}
void Content::transfer(const ucb::TransferInfo & /*rArgs*/,
const uno::Reference< ucb::XCommandEnvironment >& xEnv )
throw( uno::Exception )
{
// FIXME: see gnome-vfs-xfer.h - but we need to be able to easily
// detect which are gnome-vfs owned URI types ...
ucbhelper::cancelCommandExecution
( uno::makeAny
( ucb::InteractiveBadTransferURLException
( rtl::OUString::createFromAscii( "Unsupported URL scheme!" ),
static_cast< cppu::OWeakObject * >( this ) ) ),
xEnv );
}
void Content::destroy( sal_Bool bDeletePhysical )
throw( uno::Exception )
{
// @@@ take care about bDeletePhysical -> trashcan support
rtl::OUString aURL = getOUURI();
uno::Reference< ucb::XContent > xThis = this;
deleted();
osl::Guard< osl::Mutex > aGuard( m_aMutex );
// Process instanciated children...
::gvfs::Content::ContentRefList aChildren;
queryChildren( aChildren );
ContentRefList::const_iterator it = aChildren.begin();
ContentRefList::const_iterator end = aChildren.end();
while ( it != end ) {
(*it)->destroy( bDeletePhysical );
++it;
}
}
// Used by the 'setPropertyValues' method for
// propagating the renaming of a Content.
sal_Bool Content::exchangeIdentity(
const uno::Reference< ucb::XContentIdentifier >& xNewId )
{
if ( !xNewId.is() )
return sal_False;
uno::Reference< ucb::XContent > xThis = this;
#ifdef DEBUG
g_warning( "exchangeIdentity from '%s' to '%s'",
getURI(), OUStringToGnome( xNewId->getContentIdentifier() ) );
#endif
if ( m_bTransient ) {
osl::Guard< osl::Mutex > aGuard( m_aMutex );
/* FIXME: can we not screw up an identically named
* Content pointing to ourself here ? */
m_xIdentifier = xNewId;
return sal_False;
}
rtl::OUString aOldURL = getOUURI();
// Exchange own identitity.
if ( exchange( xNewId ) ) {
// Process instanciated children...
ContentRefList aChildren;
queryChildren( aChildren );
ContentRefList::const_iterator it = aChildren.begin();
ContentRefList::const_iterator end = aChildren.end();
while ( it != end ) {
ContentRef xChild = (*it);
// Create new content identifier for the child...
uno::Reference< ucb::XContentIdentifier >
xOldChildId = xChild->getIdentifier();
rtl::OUString aOldChildURL
= xOldChildId->getContentIdentifier();
rtl::OUString aNewChildURL
= aOldChildURL.replaceAt(
0,
aOldURL.getLength(),
xNewId->getContentIdentifier() );
uno::Reference< ucb::XContentIdentifier >
xNewChildId
= new ::ucbhelper::ContentIdentifier( m_xSMgr, aNewChildURL );
if ( !xChild->exchangeIdentity( xNewChildId ) )
return sal_False;
++it;
}
return sal_True;
}
return sal_False;
}
GnomeVFSResult
Content::getInfo( const uno::Reference< ucb::XCommandEnvironment >& xEnv )
{
GnomeVFSResult result;
osl::Guard< osl::Mutex > aGuard( m_aMutex );
if (m_bTransient)
result = GNOME_VFS_OK;
else if ( !m_info.valid_fields ) {
::rtl::OString aURI = getOURI();
Authentication aAuth( xEnv );
result = gnome_vfs_get_file_info
( aURI.getStr(), &m_info, GNOME_VFS_FILE_INFO_DEFAULT );
if (result != GNOME_VFS_OK)
gnome_vfs_file_info_clear( &m_info );
} else
result = GNOME_VFS_OK;
#ifdef DEBUG
g_warning( "getInfo on '%s' returns '%s' (%d) (0x%x)",
getURI(), gnome_vfs_result_to_string( result ),
result, m_info.valid_fields );
#endif
return result;
}
sal_Bool
Content::isFolder(const uno::Reference< ucb::XCommandEnvironment >& xEnv )
{
osl::Guard< osl::Mutex > aGuard( m_aMutex );
getInfo( xEnv );
return (m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_TYPE &&
m_info.type == GNOME_VFS_FILE_TYPE_DIRECTORY);
}
uno::Any Content::mapVFSException( const GnomeVFSResult result, sal_Bool bWrite )
{
uno::Any aException;
const char *gvfs_message;
rtl::OUString message;
uno::Sequence< uno::Any > aArgs( 1 );
#ifdef DEBUG
g_warning ("Map VFS exception '%s' (%d)",
gnome_vfs_result_to_string( result ), result );
#endif
if ((gvfs_message = gnome_vfs_result_to_string (result)))
message = GnomeToOUString( gvfs_message );
switch (result) {
case GNOME_VFS_OK:
g_warning("VFS_OK mapped to exception.");
break;
case GNOME_VFS_ERROR_EOF:
g_warning ("VFS_EOF not handled somewhere.");
break;
case GNOME_VFS_ERROR_NOT_FOUND:
aArgs[ 0 ] <<= m_xIdentifier->getContentIdentifier();
aException <<=
ucb::InteractiveAugmentedIOException
( rtl::OUString::createFromAscii( "Not found!" ),
static_cast< cppu::OWeakObject * >( this ),
task::InteractionClassification_ERROR,
ucb::IOErrorCode_NOT_EXISTING,
aArgs );
break;
case GNOME_VFS_ERROR_BAD_PARAMETERS:
aException <<=
lang::IllegalArgumentException
( rtl::OUString(),
static_cast< cppu::OWeakObject * >( this ),
-1 );
break;
case GNOME_VFS_ERROR_GENERIC:
case GNOME_VFS_ERROR_INTERNAL:
case GNOME_VFS_ERROR_NOT_SUPPORTED:
#ifdef DEBUG
g_warning ("Internal - un-mapped error");
#endif
aException <<= io::IOException();
break;
case GNOME_VFS_ERROR_IO:
if ( bWrite )
aException <<=
ucb::InteractiveNetworkWriteException
( rtl::OUString(),
static_cast< cppu::OWeakObject * >( this ),
task::InteractionClassification_ERROR,
message );
else
aException <<=
ucb::InteractiveNetworkReadException
( rtl::OUString(),
static_cast< cppu::OWeakObject * >( this ),
task::InteractionClassification_ERROR,
message );
break;
case GNOME_VFS_ERROR_HOST_NOT_FOUND:
case GNOME_VFS_ERROR_INVALID_HOST_NAME:
aException <<=
ucb::InteractiveNetworkResolveNameException
( rtl::OUString(),
static_cast< cppu::OWeakObject * >( this ),
task::InteractionClassification_ERROR,
message );
break;
case GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE:
case GNOME_VFS_ERROR_SERVICE_OBSOLETE:
case GNOME_VFS_ERROR_PROTOCOL_ERROR:
case GNOME_VFS_ERROR_NO_MASTER_BROWSER:
aException <<=
ucb::InteractiveNetworkConnectException
( rtl::OUString(),
static_cast< cppu::OWeakObject * >( this ),
task::InteractionClassification_ERROR,
message );
break;
case GNOME_VFS_ERROR_FILE_EXISTS:
aException <<= ucb::NameClashException
( rtl::OUString(),
static_cast< cppu::OWeakObject * >( this ),
task::InteractionClassification_ERROR,
message );
break;
case GNOME_VFS_ERROR_INVALID_OPEN_MODE:
aException <<= ucb::UnsupportedOpenModeException();
break;
case GNOME_VFS_ERROR_CORRUPTED_DATA:
case GNOME_VFS_ERROR_WRONG_FORMAT:
case GNOME_VFS_ERROR_BAD_FILE:
case GNOME_VFS_ERROR_TOO_BIG:
case GNOME_VFS_ERROR_NO_SPACE:
case GNOME_VFS_ERROR_READ_ONLY:
case GNOME_VFS_ERROR_INVALID_URI:
case GNOME_VFS_ERROR_NOT_OPEN:
case GNOME_VFS_ERROR_ACCESS_DENIED:
case GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES:
case GNOME_VFS_ERROR_NOT_A_DIRECTORY:
case GNOME_VFS_ERROR_IN_PROGRESS:
case GNOME_VFS_ERROR_INTERRUPTED:
case GNOME_VFS_ERROR_LOOP:
case GNOME_VFS_ERROR_NOT_PERMITTED:
case GNOME_VFS_ERROR_IS_DIRECTORY:
case GNOME_VFS_ERROR_NO_MEMORY:
case GNOME_VFS_ERROR_HOST_HAS_NO_ADDRESS:
case GNOME_VFS_ERROR_LOGIN_FAILED:
case GNOME_VFS_ERROR_CANCELLED:
case GNOME_VFS_ERROR_DIRECTORY_BUSY:
case GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY:
case GNOME_VFS_ERROR_TOO_MANY_LINKS:
case GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM:
case GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM:
case GNOME_VFS_ERROR_NAME_TOO_LONG:
#ifdef DEBUG
g_warning( "FIXME: Un-mapped VFS exception '%s' (%d)",
gnome_vfs_result_to_string( result ), result );
#endif
default:
aException <<= ucb::InteractiveNetworkGeneralException
( rtl::OUString(),
static_cast< cppu::OWeakObject * >( this ),
task::InteractionClassification_ERROR );
break;
}
return aException;
}
void Content::cancelCommandExecution(
GnomeVFSResult result,
const uno::Reference< ucb::XCommandEnvironment > & xEnv,
sal_Bool bWrite /* = sal_False */ )
throw ( uno::Exception )
{
ucbhelper::cancelCommandExecution( mapVFSException( result, bWrite ), xEnv );
// Unreachable
}
uno::Sequence< beans::Property > Content::getProperties(
const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
{
static const beans::Property aGenericProperties[] = {
beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ContentType" ) ),
-1, getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsDocument" ) ),
-1, getCppuBooleanType(),
beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsFolder" ) ),
-1, getCppuBooleanType(),
beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Title" ) ),
-1, getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
beans::PropertyAttribute::BOUND ),
// Optional ...
beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DateCreated" ) ),
-1, getCppuType( static_cast< const util::DateTime * >( 0 ) ),
beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DateModified" ) ),
-1, getCppuType( static_cast< const util::DateTime * >( 0 ) ),
beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
// FIXME: Too expensive for now (?)
// beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MediaType" ) ),
// -1, getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
// beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Size" ) ),
-1, getCppuType( static_cast< const sal_Int64 * >( 0 ) ),
beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsReadOnly" ) ),
-1, getCppuBooleanType(),
beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsVolume" ) ),
-1, getCppuBooleanType(),
beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsCompactDisk" ) ),
-1, getCppuBooleanType(),
beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsHidden" ) ),
-1, getCppuBooleanType(),
beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CreatableContentsInfo" ) ),
-1, getCppuType( static_cast< const uno::Sequence< ucb::ContentInfo > * >( 0 ) ),
beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY )
};
const int nProps = sizeof (aGenericProperties) / sizeof (aGenericProperties[0]);
return uno::Sequence< beans::Property > ( aGenericProperties, nProps );
}
uno::Sequence< ucb::CommandInfo > Content::getCommands(
const uno::Reference< ucb::XCommandEnvironment > & xEnv )
{
static ucb::CommandInfo aCommandInfoTable[] = {
// Required commands
ucb::CommandInfo
( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "getCommandInfo" ) ),
-1, getCppuVoidType() ),
ucb::CommandInfo
( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "getPropertySetInfo" ) ),
-1, getCppuVoidType() ),
ucb::CommandInfo
( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "getPropertyValues" ) ),
-1, getCppuType( static_cast<uno::Sequence< beans::Property > * >( 0 ) ) ),
ucb::CommandInfo
( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "setPropertyValues" ) ),
-1, getCppuType( static_cast<uno::Sequence< beans::PropertyValue > * >( 0 ) ) ),
// Optional standard commands
ucb::CommandInfo
( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "delete" ) ),
-1, getCppuBooleanType() ),
ucb::CommandInfo
( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "insert" ) ),
-1, getCppuType( static_cast<ucb::InsertCommandArgument * >( 0 ) ) ),
ucb::CommandInfo
( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "open" ) ),
-1, getCppuType( static_cast<ucb::OpenCommandArgument2 * >( 0 ) ) ),
// Folder Only, omitted if not a folder
ucb::CommandInfo
( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "transfer" ) ),
-1, getCppuType( static_cast<ucb::TransferInfo * >( 0 ) ) ),
ucb::CommandInfo
( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "createNewContent" ) ),
-1, getCppuType( static_cast<ucb::ContentInfo * >( 0 ) ) )
};
const int nProps
= sizeof( aCommandInfoTable ) / sizeof( aCommandInfoTable[ 0 ] );
return uno::Sequence< ucb::CommandInfo >(
aCommandInfoTable, isFolder( xEnv ) ? nProps : nProps - 2 );
}
rtl::OUString
Content::getOUURI ()
{
osl::Guard< osl::Mutex > aGuard( m_aMutex );
return m_xIdentifier->getContentIdentifier();
}
rtl::OString
Content::getOURI ()
{
return rtl::OUStringToOString( getOUURI(), RTL_TEXTENCODING_UTF8 );
}
char *
Content::getURI ()
{
return OUStringToGnome( getOUURI() );
}
void
Content::copyData( uno::Reference< io::XInputStream > xIn,
uno::Reference< io::XOutputStream > xOut )
{
uno::Sequence< sal_Int8 > theData( TRANSFER_BUFFER_SIZE );
g_return_if_fail( xIn.is() && xOut.is() );
while ( xIn->readBytes( theData, TRANSFER_BUFFER_SIZE ) > 0 )
xOut->writeBytes( theData );
xOut->closeOutput();
}
// Inherits an authentication context
uno::Reference< io::XInputStream >
Content::createTempStream(
const uno::Reference< ucb::XCommandEnvironment >& xEnv )
throw( uno::Exception )
{
GnomeVFSResult result;
GnomeVFSHandle *handle = NULL;
::rtl::OString aURI = getOURI();
osl::Guard< osl::Mutex > aGuard( m_aMutex );
// Something badly wrong happened - can't seek => stream to a temporary file
const rtl::OUString sServiceName ( RTL_CONSTASCII_USTRINGPARAM ( "com.sun.star.io.TempFile" ) );
uno::Reference < io::XOutputStream > xTempOut =
uno::Reference < io::XOutputStream >
( m_xSMgr->createInstance( sServiceName ), uno::UNO_QUERY );
if ( !xTempOut.is() )
cancelCommandExecution( GNOME_VFS_ERROR_IO, xEnv );
result = gnome_vfs_open( &handle, aURI.getStr(), GNOME_VFS_OPEN_READ );
if (result != GNOME_VFS_OK)
cancelCommandExecution( result, xEnv );
uno::Reference < io::XInputStream > pStream = new ::gvfs::Stream( handle, &m_info );
copyData( pStream, xTempOut );
return uno::Reference < io::XInputStream > ( xTempOut, uno::UNO_QUERY );
}
uno::Reference< io::XInputStream >
Content::createInputStream(
const uno::Reference< ucb::XCommandEnvironment >& xEnv )
throw( uno::Exception )
{
GnomeVFSHandle *handle = NULL;
GnomeVFSResult result;
uno::Reference<io::XInputStream > xIn;
Authentication aAuth( xEnv );
osl::Guard< osl::Mutex > aGuard( m_aMutex );
getInfo( xEnv );
::rtl::OString aURI = getOURI();
if ( !(m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) )
return createTempStream( xEnv );
result = gnome_vfs_open( &handle, aURI.getStr(),
(GnomeVFSOpenMode) (GNOME_VFS_OPEN_READ | GNOME_VFS_OPEN_RANDOM ) );
if (result == GNOME_VFS_ERROR_INVALID_OPEN_MODE ||
result == GNOME_VFS_ERROR_NOT_SUPPORTED)
return createTempStream( xEnv );
if (result != GNOME_VFS_OK)
cancelCommandExecution( result, xEnv );
// Try a seek just to make sure it's Random access: some lie.
result = gnome_vfs_seek( handle, GNOME_VFS_SEEK_START, 0);
if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) {
gnome_vfs_close( handle );
return createTempStream( xEnv );
}
if (result != GNOME_VFS_OK)
cancelCommandExecution( result, xEnv );
if (handle != NULL)
xIn = new ::gvfs::Stream( handle, &m_info );
return xIn;
}
sal_Bool
Content::feedSink( uno::Reference< uno::XInterface > aSink,
const uno::Reference< ucb::XCommandEnvironment >& xEnv )
{
if ( !aSink.is() )
return sal_False;
uno::Reference< io::XOutputStream > xOut
= uno::Reference< io::XOutputStream >(aSink, uno::UNO_QUERY );
uno::Reference< io::XActiveDataSink > xDataSink
= uno::Reference< io::XActiveDataSink >(aSink, uno::UNO_QUERY );
if ( !xOut.is() && !xDataSink.is() )
return sal_False;
uno::Reference< io::XInputStream > xIn = createInputStream( xEnv );
if ( !xIn.is() )
return sal_False;
if ( xOut.is() )
copyData( xIn, xOut );
if ( xDataSink.is() )
xDataSink->setInputStream( xIn );
return sal_True;
}
extern "C" {
#ifndef GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION
# error "We require Gnome VFS 2.6.x to compile (will run fine with < 2.6)"
#endif
static void
vfs_authentication_callback (gconstpointer in_void,
gsize in_size,
gpointer out_void,
gsize out_size,
gpointer callback_data)
{
task::XInteractionHandler *xIH;
#ifdef DEBUG
g_warning ("Full authentication callback (%p) ...", callback_data);
#endif
if( !( xIH = (task::XInteractionHandler *) callback_data ) )
return;
const GnomeVFSModuleCallbackFullAuthenticationIn *in =
(const GnomeVFSModuleCallbackFullAuthenticationIn *) in_void;
GnomeVFSModuleCallbackFullAuthenticationOut *out =
(GnomeVFSModuleCallbackFullAuthenticationOut *) out_void;
g_return_if_fail (in != NULL && out != NULL);
g_return_if_fail (sizeof (GnomeVFSModuleCallbackFullAuthenticationIn) == in_size &&
sizeof (GnomeVFSModuleCallbackFullAuthenticationOut) == out_size);
#ifdef DEBUG
# define NNIL(x) (x?x:"<Null>")
g_warning (" InComing data 0x%x uri '%s' prot '%s' server '%s' object '%s' "
"port %d auth_t '%s' user '%s' domain '%s' "
"def user '%s', def domain '%s'",
(int) in->flags, NNIL(in->uri), NNIL(in->protocol),
NNIL(in->server), NNIL(in->object),
(int) in->port, NNIL(in->authtype), NNIL(in->username), NNIL(in->domain),
NNIL(in->default_user), NNIL(in->default_domain));
# undef NNIL
#endif
ucbhelper::SimpleAuthenticationRequest::EntityType
eDomain, eUserName, ePassword;
::rtl::OUString aHostName, aDomain, aUserName, aPassword;
aHostName = GnomeToOUString( in->server );
if (in->flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_DOMAIN)
{
aDomain = GnomeToOUString( in->domain );
eDomain = ucbhelper::SimpleAuthenticationRequest::ENTITY_MODIFY;
if (!aDomain.getLength())
aDomain = GnomeToOUString( in->default_domain );
}
else // no underlying capability to display realm otherwise
eDomain = ucbhelper::SimpleAuthenticationRequest::ENTITY_NA;
aUserName = GnomeToOUString( in->username );
if (!aUserName.getLength())
aUserName = GnomeToOUString( in->default_user );
eUserName = (in->flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_USERNAME) ?
ucbhelper::SimpleAuthenticationRequest::ENTITY_MODIFY :
(aUserName.getLength() ?
ucbhelper::SimpleAuthenticationRequest::ENTITY_FIXED :
ucbhelper::SimpleAuthenticationRequest::ENTITY_NA);
// No suggested password.
ePassword = (in->flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_PASSWORD) ?
ucbhelper::SimpleAuthenticationRequest::ENTITY_MODIFY :
ucbhelper::SimpleAuthenticationRequest::ENTITY_FIXED;
// Really, really bad things happen if we don't provide
// the same user/password as was entered last time if
// we failed to authenticate - infinite looping / flickering
// madness etc. [ nice infrastructure ! ]
static rtl::OUString aLastUserName, aLastPassword;
if (in->flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_PREVIOUS_ATTEMPT_FAILED)
{
osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );
aUserName = aLastUserName;
aPassword = aLastPassword;
}
rtl::Reference< ucbhelper::SimpleAuthenticationRequest > xRequest
= new ucbhelper::SimpleAuthenticationRequest (GnomeToOUString(in->uri),
aHostName, eDomain, aDomain,
eUserName, aUserName,
ePassword, aPassword);
xIH->handle( xRequest.get() );
rtl::Reference< ucbhelper::InteractionContinuation > xSelection
= xRequest->getSelection();
if ( xSelection.is() ) {
// Handler handled the request.
uno::Reference< task::XInteractionAbort > xAbort(xSelection.get(), uno::UNO_QUERY );
if ( !xAbort.is() ) {
const rtl::Reference<
ucbhelper::InteractionSupplyAuthentication > & xSupp
= xRequest->getAuthenticationSupplier();
aUserName = xSupp->getUserName();
aDomain = xSupp->getRealm();
aPassword = xSupp->getPassword();
{
osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );
aLastUserName = aUserName;
aLastPassword = aPassword;
}
out->username = OUStringToGnome( aUserName );
out->domain = OUStringToGnome( aDomain );
out->password = OUStringToGnome( aPassword );
out->save_password = xSupp->getRememberPasswordMode();
#ifdef DEBUG
g_warning ("Got valid user/domain/password '%s' '%s' '%s', %s password",
out->username, out->domain, out->password,
out->save_password ? "save" : "don't save");
#endif
}
else
out->abort_auth = TRUE;
}
else
out->abort_auth = TRUE;
}
static void
vfs_authentication_old_callback (gconstpointer in_void,
gsize in_size,
gpointer out_void,
gsize out_size,
gpointer callback_data)
{
#ifdef DEBUG
g_warning ("Old authentication callback (%p) [ UNTESTED ] ...", callback_data);
#endif
const GnomeVFSModuleCallbackAuthenticationIn *in =
(const GnomeVFSModuleCallbackAuthenticationIn *) in_void;
GnomeVFSModuleCallbackAuthenticationOut *out =
(GnomeVFSModuleCallbackAuthenticationOut *) out_void;
g_return_if_fail (in != NULL && out != NULL);
g_return_if_fail (sizeof (GnomeVFSModuleCallbackAuthenticationIn) == in_size &&
sizeof (GnomeVFSModuleCallbackAuthenticationOut) == out_size);
GnomeVFSModuleCallbackFullAuthenticationIn mapped_in = {
(GnomeVFSModuleCallbackFullAuthenticationFlags)
(GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_PASSWORD |
GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_USERNAME |
GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_DOMAIN),
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
GnomeVFSModuleCallbackFullAuthenticationOut mapped_out = { 0, 0, 0, 0, 0, 0, 0, 0 };
// Map the old style input auth. data to the new style structure.
if (in->previous_attempt_failed)
mapped_in.flags = (GnomeVFSModuleCallbackFullAuthenticationFlags)
(mapped_in.flags |
GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_PREVIOUS_ATTEMPT_FAILED);
GnomeVFSURI *pURI = NULL;
// Urk - parse all this from the URL ...
mapped_in.uri = in->uri;
if (in->uri)
{
pURI = gnome_vfs_uri_new( in->uri );
mapped_in.protocol = (char *) gnome_vfs_uri_get_scheme (pURI);
mapped_in.server = (char *) gnome_vfs_uri_get_host_name (pURI);
mapped_in.port = gnome_vfs_uri_get_host_port (pURI);
mapped_in.username = (char *) gnome_vfs_uri_get_user_name (pURI);
}
mapped_in.domain = in->realm;
mapped_in.default_user = mapped_in.username;
mapped_in.default_domain = mapped_in.domain;
vfs_authentication_callback ((gconstpointer) &mapped_in,
sizeof (mapped_in),
(gpointer) &mapped_out,
sizeof (mapped_out),
callback_data);
if (pURI)
gnome_vfs_uri_unref (pURI);
// Map the new style auth. out data to the old style out structure.
out->username = mapped_out.username;
out->password = mapped_out.password;
g_free (mapped_out.domain);
g_free (mapped_out.keyring);
}
static void
auth_destroy (gpointer data)
{
task::XInteractionHandler *xIH;
if( ( xIH = ( task::XInteractionHandler * )data ) )
xIH->release();
}
// This sucks, but gnome-vfs doesn't much like
// repeated set / unsets - so we have to compensate.
GPrivate *auth_queue = NULL;
void auth_queue_destroy( gpointer data )
{
GList *l;
GQueue *vq = (GQueue *) data;
for (l = vq->head; l; l = l->next)
auth_destroy (l->data);
g_queue_free (vq);
}
}
static void
refresh_auth( GQueue *vq )
{
GList *l;
gnome_vfs_module_callback_pop( GNOME_VFS_MODULE_CALLBACK_AUTHENTICATION );
gnome_vfs_module_callback_pop( GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION );
for (l = vq->head; l; l = l->next) {
if (l->data) {
gnome_vfs_module_callback_push
( GNOME_VFS_MODULE_CALLBACK_AUTHENTICATION,
vfs_authentication_old_callback, l->data, NULL );
gnome_vfs_module_callback_push
( GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION,
vfs_authentication_callback, l->data, NULL );
break;
}
}
}
gvfs::Authentication::Authentication(
const uno::Reference< ucb::XCommandEnvironment > & xEnv )
{
GQueue *vq;
uno::Reference< task::XInteractionHandler > xIH;
if ( xEnv.is() )
xIH = xEnv->getInteractionHandler();
if ( xIH.is() )
xIH->acquire();
if( !(vq = (GQueue *)g_private_get( auth_queue ) ) ) {
vq = g_queue_new();
g_private_set( auth_queue, vq );
}
g_queue_push_head( vq, (gpointer) xIH.get() );
refresh_auth( vq );
}
gvfs::Authentication::~Authentication()
{
GQueue *vq;
gpointer data;
vq = (GQueue *)g_private_get( auth_queue );
data = g_queue_pop_head( vq );
auth_destroy (data);
refresh_auth( vq );
}