bump product version to 5.0.4.1
[LibreOffice.git] / ucb / source / ucp / ext / ucpext_content.cxx
blob3ee8ad6a40187278ca95132e0c70071f81c58b22
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "ucpext_content.hxx"
21 #include "ucpext_provider.hxx"
22 #include "ucpext_resultset.hxx"
24 #include <com/sun/star/beans/PropertyAttribute.hpp>
25 #include <com/sun/star/beans/XPropertyAccess.hpp>
26 #include <com/sun/star/lang/IllegalAccessException.hpp>
27 #include <com/sun/star/sdbc/XRow.hpp>
28 #include <com/sun/star/ucb/XCommandInfo.hpp>
29 #include <com/sun/star/ucb/XPersistentPropertySet.hpp>
30 #include <com/sun/star/io/XOutputStream.hpp>
31 #include <com/sun/star/io/XActiveDataSink.hpp>
32 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
33 #include <com/sun/star/ucb/OpenMode.hpp>
34 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
35 #include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
36 #include <com/sun/star/ucb/XDynamicResultSet.hpp>
37 #include <com/sun/star/deployment/PackageInformationProvider.hpp>
39 #include <ucbhelper/contentidentifier.hxx>
40 #include <ucbhelper/propertyvalueset.hxx>
41 #include <ucbhelper/cancelcommandexecution.hxx>
42 #include <ucbhelper/content.hxx>
43 #include <tools/diagnose_ex.h>
44 #include <comphelper/processfactory.hxx>
45 #include <comphelper/string.hxx>
46 #include <rtl/ustrbuf.hxx>
47 #include <rtl/uri.hxx>
48 #include <sal/macros.h>
50 #include <algorithm>
53 namespace ucb { namespace ucp { namespace ext
57 using ::com::sun::star::uno::Reference;
58 using ::com::sun::star::uno::XInterface;
59 using ::com::sun::star::uno::UNO_QUERY;
60 using ::com::sun::star::uno::UNO_QUERY_THROW;
61 using ::com::sun::star::uno::UNO_SET_THROW;
62 using ::com::sun::star::uno::Exception;
63 using ::com::sun::star::uno::RuntimeException;
64 using ::com::sun::star::uno::Any;
65 using ::com::sun::star::uno::makeAny;
66 using ::com::sun::star::uno::Sequence;
67 using ::com::sun::star::uno::Type;
68 using ::com::sun::star::uno::XComponentContext;
69 using ::com::sun::star::lang::XMultiServiceFactory;
70 using ::com::sun::star::ucb::XContentIdentifier;
71 using ::com::sun::star::ucb::IllegalIdentifierException;
72 using ::com::sun::star::ucb::XContent;
73 using ::com::sun::star::ucb::XCommandEnvironment;
74 using ::com::sun::star::ucb::Command;
75 using ::com::sun::star::ucb::CommandAbortedException;
76 using ::com::sun::star::beans::Property;
77 using ::com::sun::star::lang::IllegalArgumentException;
78 using ::com::sun::star::beans::PropertyValue;
79 using ::com::sun::star::ucb::OpenCommandArgument2;
80 using ::com::sun::star::ucb::XDynamicResultSet;
81 using ::com::sun::star::ucb::UnsupportedOpenModeException;
82 using ::com::sun::star::io::XOutputStream;
83 using ::com::sun::star::io::XActiveDataSink;
84 using ::com::sun::star::io::XInputStream;
85 using ::com::sun::star::ucb::UnsupportedDataSinkException;
86 using ::com::sun::star::ucb::UnsupportedCommandException;
87 using ::com::sun::star::sdbc::XRow;
88 using ::com::sun::star::beans::XPropertySet;
89 using ::com::sun::star::beans::PropertyChangeEvent;
90 using ::com::sun::star::lang::IllegalAccessException;
91 using ::com::sun::star::ucb::CommandInfo;
92 using ::com::sun::star::deployment::PackageInformationProvider;
93 using ::com::sun::star::deployment::XPackageInformationProvider;
95 namespace OpenMode = ::com::sun::star::ucb::OpenMode;
96 namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
99 //= helper
101 namespace
104 OUString lcl_compose( const OUString& i_rBaseURL, const OUString& i_rRelativeURL )
106 ENSURE_OR_RETURN( !i_rBaseURL.isEmpty(), "illegal base URL", i_rRelativeURL );
108 OUStringBuffer aComposer( i_rBaseURL );
109 if ( !i_rBaseURL.endsWith("/") )
110 aComposer.append( '/' );
111 aComposer.append( i_rRelativeURL );
112 return aComposer.makeStringAndClear();
116 struct SelectPropertyName : public ::std::unary_function< Property, OUString >
118 const OUString& operator()( const Property& i_rProperty ) const
120 return i_rProperty.Name;
126 //= Content
129 Content::Content( const Reference< XComponentContext >& rxContext, ::ucbhelper::ContentProviderImplHelper* i_pProvider,
130 const Reference< XContentIdentifier >& i_rIdentifier )
131 :Content_Base( rxContext, i_pProvider, i_rIdentifier )
132 ,m_eExtContentType( E_UNKNOWN )
133 ,m_aIsFolder()
134 ,m_aContentType()
135 ,m_sExtensionId()
136 ,m_sPathIntoExtension()
138 const OUString sURL( getIdentifier()->getContentIdentifier() );
139 if ( denotesRootContent( sURL ) )
141 m_eExtContentType = E_ROOT;
143 else
145 const OUString sRelativeURL( sURL.copy( ContentProvider::getRootURL().getLength() ) );
146 const sal_Int32 nSepPos = sRelativeURL.indexOf( '/' );
147 if ( ( nSepPos == -1 ) || ( nSepPos == sRelativeURL.getLength() - 1 ) )
149 m_eExtContentType = E_EXTENSION_ROOT;
151 else
153 m_eExtContentType = E_EXTENSION_CONTENT;
157 if ( m_eExtContentType != E_ROOT )
159 const OUString sRootURL = ContentProvider::getRootURL();
160 m_sExtensionId = sURL.copy( sRootURL.getLength() );
162 const sal_Int32 nNextSep = m_sExtensionId.indexOf( '/' );
163 if ( nNextSep > -1 )
165 m_sPathIntoExtension = m_sExtensionId.copy( nNextSep + 1 );
166 m_sExtensionId = m_sExtensionId.copy( 0, nNextSep );
168 m_sExtensionId = Content::decodeIdentifier( m_sExtensionId );
173 Content::~Content()
178 OUString SAL_CALL Content::getImplementationName() throw( RuntimeException, std::exception )
180 return OUString( "org.openoffice.comp.ucp.ext.Content" );
184 Sequence< OUString > SAL_CALL Content::getSupportedServiceNames() throw( RuntimeException, std::exception )
186 Sequence< OUString > aServiceNames(2);
187 aServiceNames[0] = "com.sun.star.ucb.Content";
188 aServiceNames[1] = "com.sun.star.ucb.ExtensionContent";
189 return aServiceNames;
193 OUString SAL_CALL Content::getContentType() throw( RuntimeException, std::exception )
195 impl_determineContentType();
196 return *m_aContentType;
200 Any SAL_CALL Content::execute( const Command& aCommand, sal_Int32 /* CommandId */, const Reference< XCommandEnvironment >& i_rEvironment )
201 throw( Exception, CommandAbortedException, RuntimeException, std::exception )
203 Any aRet;
205 if ( aCommand.Name == "getPropertyValues" )
207 Sequence< Property > Properties;
208 if ( !( aCommand.Argument >>= Properties ) )
210 ::ucbhelper::cancelCommandExecution( makeAny( IllegalArgumentException(
211 OUString(), *this, -1 ) ),
212 i_rEvironment );
213 // unreachable
216 aRet <<= getPropertyValues( Properties, i_rEvironment );
218 else if ( aCommand.Name == "setPropertyValues" )
220 Sequence< PropertyValue > aProperties;
221 if ( !( aCommand.Argument >>= aProperties ) )
223 ::ucbhelper::cancelCommandExecution( makeAny( IllegalArgumentException(
224 OUString(), *this, -1 ) ),
225 i_rEvironment );
226 // unreachable
229 if ( !aProperties.getLength() )
231 ::ucbhelper::cancelCommandExecution( makeAny( IllegalArgumentException(
232 OUString(), *this, -1 ) ),
233 i_rEvironment );
234 // unreachable
237 aRet <<= setPropertyValues( aProperties, i_rEvironment );
239 else if ( aCommand.Name == "getPropertySetInfo" )
241 // implemented by base class.
242 aRet <<= getPropertySetInfo( i_rEvironment );
244 else if ( aCommand.Name == "getCommandInfo" )
246 // implemented by base class.
247 aRet <<= getCommandInfo( i_rEvironment );
249 else if ( aCommand.Name == "open" )
251 OpenCommandArgument2 aOpenCommand;
252 if ( !( aCommand.Argument >>= aOpenCommand ) )
254 ::ucbhelper::cancelCommandExecution( makeAny( IllegalArgumentException(
255 OUString(), *this, -1 ) ),
256 i_rEvironment );
257 // unreachable
260 bool bOpenFolder =
261 ( ( aOpenCommand.Mode == OpenMode::ALL ) ||
262 ( aOpenCommand.Mode == OpenMode::FOLDERS ) ||
263 ( aOpenCommand.Mode == OpenMode::DOCUMENTS ) );
266 if ( bOpenFolder && impl_isFolder() )
268 Reference< XDynamicResultSet > xSet = new ResultSet( m_xContext, this, aOpenCommand, i_rEvironment );
269 aRet <<= xSet;
272 if ( aOpenCommand.Sink.is() )
274 const OUString sPhysicalContentURL( getPhysicalURL() );
275 ::ucbhelper::Content aRequestedContent( sPhysicalContentURL, i_rEvironment, m_xContext );
276 aRet = aRequestedContent.executeCommand( OUString( "open" ), makeAny( aOpenCommand ) );
280 else
282 ::ucbhelper::cancelCommandExecution( makeAny( UnsupportedCommandException(
283 OUString(), *this ) ),
284 i_rEvironment );
285 // unreachable
288 return aRet;
292 void SAL_CALL Content::abort( sal_Int32 ) throw( RuntimeException, std::exception )
297 OUString Content::encodeIdentifier( const OUString& i_rIdentifier )
299 return ::rtl::Uri::encode( i_rIdentifier, rtl_UriCharClassRegName, rtl_UriEncodeIgnoreEscapes,
300 RTL_TEXTENCODING_UTF8 );
304 OUString Content::decodeIdentifier( const OUString& i_rIdentifier )
306 return ::rtl::Uri::decode( i_rIdentifier, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
310 bool Content::denotesRootContent( const OUString& i_rContentIdentifier )
312 const OUString sRootURL( ContentProvider::getRootURL() );
313 if ( i_rContentIdentifier == sRootURL )
314 return true;
316 // the root URL contains only two trailing /, but we also recognize 3 of them as denoting the root URL
317 if ( i_rContentIdentifier.match( sRootURL )
318 && ( i_rContentIdentifier.getLength() == sRootURL.getLength() + 1 )
319 && ( i_rContentIdentifier[ i_rContentIdentifier.getLength() - 1 ] == '/' )
321 return true;
323 return false;
327 OUString Content::getParentURL()
329 const OUString sRootURL( ContentProvider::getRootURL() );
331 switch ( m_eExtContentType )
333 case E_ROOT:
334 // don't have a parent
335 return sRootURL;
337 case E_EXTENSION_ROOT:
338 // our parent is the root itself
339 return sRootURL;
341 case E_EXTENSION_CONTENT:
343 const OUString sURL = m_xIdentifier->getContentIdentifier();
345 // cut the root URL
346 if ( !sURL.match( sRootURL, 0 ) )
348 SAL_INFO( "ucb.ucp.ext", "illegal URL structure - no root" );
349 break;
352 OUString sRelativeURL( sURL.copy( sRootURL.getLength() ) );
354 // cut the extension ID
355 const OUString sSeparatedExtensionId( encodeIdentifier( m_sExtensionId ) + "/" );
356 if ( !sRelativeURL.match( sSeparatedExtensionId ) )
358 SAL_INFO( "ucb.ucp.ext", "illegal URL structure - no extension ID" );
359 break;
362 sRelativeURL = sRelativeURL.copy( sSeparatedExtensionId.getLength() );
364 // cut the final slash (if any)
365 if ( sRelativeURL.isEmpty() )
367 SAL_INFO( "ucb.ucp.ext", "illegal URL structure - ExtensionContent should have a level below the extension ID" );
368 break;
371 if ( sRelativeURL.endsWith("/") )
372 sRelativeURL = sRelativeURL.copy( 0, sRelativeURL.getLength() - 1 );
374 // remove the last segment
375 const sal_Int32 nLastSep = sRelativeURL.lastIndexOf( '/' );
376 sRelativeURL = sRelativeURL.copy( 0, nLastSep != -1 ? nLastSep : 0 );
378 OUStringBuffer aComposer;
379 aComposer.append( sRootURL );
380 aComposer.append( sSeparatedExtensionId );
381 aComposer.append( sRelativeURL );
382 return aComposer.makeStringAndClear();
385 default:
386 OSL_FAIL( "Content::getParentURL: unhandled case!" );
387 break;
389 return OUString();
393 Reference< XRow > Content::getArtificialNodePropertyValues( const Reference< XComponentContext >& rxContext,
394 const Sequence< Property >& i_rProperties, const OUString& i_rTitle )
396 // note: empty sequence means "get values of all supported properties".
397 ::rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( rxContext );
399 const sal_Int32 nCount = i_rProperties.getLength();
400 if ( nCount )
402 Reference< XPropertySet > xAdditionalPropSet;
404 const Property* pProps = i_rProperties.getConstArray();
405 for ( sal_Int32 n = 0; n < nCount; ++n )
407 const Property& rProp = pProps[ n ];
409 // Process Core properties.
410 if ( rProp.Name == "ContentType" )
412 xRow->appendString ( rProp, ContentProvider::getArtificialNodeContentType() );
414 else if ( rProp.Name == "Title" )
416 xRow->appendString ( rProp, i_rTitle );
418 else if ( rProp.Name == "IsDocument" )
420 xRow->appendBoolean( rProp, false );
422 else if ( rProp.Name == "IsFolder" )
424 xRow->appendBoolean( rProp, true );
426 else
428 // append empty entry.
429 xRow->appendVoid( rProp );
433 else
435 // Append all Core Properties.
436 xRow->appendString ( Property( OUString("ContentType"),
438 cppu::UnoType<OUString>::get(),
439 PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
440 ContentProvider::getArtificialNodeContentType() );
441 xRow->appendString ( Property( OUString("Title"),
443 cppu::UnoType<OUString>::get(),
444 PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
445 i_rTitle );
446 xRow->appendBoolean( Property( OUString("IsDocument"),
448 cppu::UnoType<bool>::get(),
449 PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
450 false );
451 xRow->appendBoolean( Property( OUString("IsFolder"),
453 cppu::UnoType<bool>::get(),
454 PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
455 true );
458 return Reference< XRow >( xRow.get() );
462 OUString Content::getPhysicalURL() const
464 ENSURE_OR_RETURN( m_eExtContentType != E_ROOT, "illegal call", OUString() );
466 // create an ucb::XContent for the physical file within the deployed extension
467 const Reference< XPackageInformationProvider > xPackageInfo = PackageInformationProvider::get(m_xContext);
468 const OUString sPackageLocation( xPackageInfo->getPackageLocation( m_sExtensionId ) );
470 if ( m_sPathIntoExtension.isEmpty() )
471 return sPackageLocation;
472 return lcl_compose( sPackageLocation, m_sPathIntoExtension );
476 Reference< XRow > Content::getPropertyValues( const Sequence< Property >& i_rProperties, const Reference< XCommandEnvironment >& i_rEnv )
478 ::osl::Guard< ::osl::Mutex > aGuard( m_aMutex );
480 switch ( m_eExtContentType )
482 case E_ROOT:
483 return getArtificialNodePropertyValues( m_xContext, i_rProperties, ContentProvider::getRootURL() );
484 case E_EXTENSION_ROOT:
485 return getArtificialNodePropertyValues( m_xContext, i_rProperties, m_sExtensionId );
486 case E_EXTENSION_CONTENT:
488 const OUString sPhysicalContentURL( getPhysicalURL() );
489 ::ucbhelper::Content aRequestedContent( sPhysicalContentURL, i_rEnv, m_xContext );
491 // translate the property request
492 Sequence< OUString > aPropertyNames( i_rProperties.getLength() );
493 ::std::transform(
494 i_rProperties.getConstArray(),
495 i_rProperties.getConstArray() + i_rProperties.getLength(),
496 aPropertyNames.getArray(),
497 SelectPropertyName()
499 const Sequence< Any > aPropertyValues = aRequestedContent.getPropertyValues( aPropertyNames );
500 const ::rtl::Reference< ::ucbhelper::PropertyValueSet > xValueRow = new ::ucbhelper::PropertyValueSet( m_xContext );
501 sal_Int32 i=0;
502 for ( const Any* value = aPropertyValues.getConstArray();
503 value != aPropertyValues.getConstArray() + aPropertyValues.getLength();
504 ++value, ++i
507 xValueRow->appendObject( aPropertyNames[i], *value );
509 return xValueRow.get();
512 default:
513 OSL_FAIL( "Content::getPropertyValues: unhandled case!" );
514 break;
517 OSL_FAIL( "Content::getPropertyValues: unreachable!" );
518 return NULL;
522 Sequence< Any > Content::setPropertyValues( const Sequence< PropertyValue >& i_rValues, const Reference< XCommandEnvironment >& /* xEnv */)
524 ::osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
526 Sequence< Any > aRet( i_rValues.getLength() );
528 PropertyChangeEvent aEvent;
529 aEvent.Source = static_cast< cppu::OWeakObject * >( this );
530 aEvent.Further = sal_False;
531 aEvent.PropertyHandle = -1;
533 const PropertyValue* pValues = i_rValues.getConstArray();
534 const sal_Int32 nCount = i_rValues.getLength();
536 for ( sal_Int32 n = 0; n < nCount; ++n, ++pValues )
538 // all our properties are read-only ...
539 aRet[ n ] <<= IllegalAccessException("property is read-only.", *this );
542 return aRet;
546 Sequence< CommandInfo > Content::getCommands( const Reference< XCommandEnvironment > & /*xEnv*/ )
548 sal_uInt32 nCommandCount = 5;
549 static const CommandInfo aCommandInfoTable[] =
552 // Mandatory commands
554 CommandInfo(
555 OUString( "getCommandInfo" ),
557 cppu::UnoType<void>::get()
559 CommandInfo(
560 OUString( "getPropertySetInfo" ),
562 cppu::UnoType<void>::get()
564 CommandInfo(
565 OUString( "getPropertyValues" ),
567 cppu::UnoType<Sequence< Property >>::get()
569 CommandInfo(
570 OUString( "setPropertyValues" ),
572 cppu::UnoType<Sequence< PropertyValue >>::get()
575 // Optional standard commands
577 , CommandInfo(
578 OUString( "open" ),
580 cppu::UnoType<OpenCommandArgument2>::get()
584 return Sequence< CommandInfo >( aCommandInfoTable, nCommandCount );
588 Sequence< Property > Content::getProperties( const Reference< XCommandEnvironment > & /*xEnv*/ )
590 static const Property aProperties[] =
592 Property(
593 OUString( "ContentType" ),
595 cppu::UnoType<OUString>::get(),
596 PropertyAttribute::BOUND | PropertyAttribute::READONLY
598 Property(
599 OUString( "IsDocument" ),
601 cppu::UnoType<bool>::get(),
602 PropertyAttribute::BOUND | PropertyAttribute::READONLY
604 Property(
605 OUString( "IsFolder" ),
607 cppu::UnoType<bool>::get(),
608 PropertyAttribute::BOUND | PropertyAttribute::READONLY
610 Property(
611 OUString( "Title" ),
613 cppu::UnoType<OUString>::get(),
614 PropertyAttribute::BOUND | PropertyAttribute::READONLY
617 return Sequence< Property >( aProperties, sizeof( aProperties ) / sizeof( aProperties[0] ) );
621 bool Content::impl_isFolder()
623 if ( !!m_aIsFolder )
624 return *m_aIsFolder;
626 bool bIsFolder = false;
629 Sequence< Property > aProps(1);
630 aProps[0].Name = "IsFolder";
631 Reference< XRow > xRow( getPropertyValues( aProps, NULL ), UNO_SET_THROW );
632 bIsFolder = xRow->getBoolean(1);
634 catch( const Exception& )
636 DBG_UNHANDLED_EXCEPTION();
638 m_aIsFolder.reset( bIsFolder );
639 return *m_aIsFolder;
643 void Content::impl_determineContentType()
645 if ( !!m_aContentType )
646 return;
648 m_aContentType.reset( ContentProvider::getArtificialNodeContentType() );
649 if ( m_eExtContentType == E_EXTENSION_CONTENT )
653 Sequence< Property > aProps(1);
654 aProps[0].Name = "ContentType";
655 Reference< XRow > xRow( getPropertyValues( aProps, NULL ), UNO_SET_THROW );
656 m_aContentType.reset( xRow->getString(1) );
658 catch( const Exception& )
660 DBG_UNHANDLED_EXCEPTION();
666 } } } // namespace ucp::ext
669 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */