/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include constexpr OUString SERVICE_NAME = u"com.sun.star.ucb.SimpleFileAccess"_ustr; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::io; using namespace ::com::sun::star::ucb; using namespace ::com::sun::star::sdbc; using namespace ::com::sun::star::task; using namespace ::com::sun::star::util; using namespace ::com::sun::star::beans; using ::std::vector; namespace { // Implementation XSimpleFileAccess typedef cppu::WeakImplHelper FileAccessHelper; class OCommandEnvironment; class OFileAccess : public FileAccessHelper { Reference< XComponentContext > m_xContext; rtl::Reference mxEnvironment; /// @throws CommandAbortedException /// @throws Exception /// @throws RuntimeException void transferImpl( const OUString& rSource, std::u16string_view rDest, bool bMoveData ); /// @throws Exception bool createNewFile( const OUString & rParentURL, const OUString & rTitle, const Reference< XInputStream >& data ); public: explicit OFileAccess( const Reference< XComponentContext > & xContext ) : m_xContext( xContext) {} // Methods virtual void SAL_CALL copy( const OUString& SourceURL, const OUString& DestURL ) override; virtual void SAL_CALL move( const OUString& SourceURL, const OUString& DestURL ) override; virtual void SAL_CALL kill( const OUString& FileURL ) override; virtual sal_Bool SAL_CALL isFolder( const OUString& FileURL ) override; virtual sal_Bool SAL_CALL isReadOnly( const OUString& FileURL ) override; virtual void SAL_CALL setReadOnly( const OUString& FileURL, sal_Bool bReadOnly ) override; virtual void SAL_CALL createFolder( const OUString& NewFolderURL ) override; virtual sal_Int32 SAL_CALL getSize( const OUString& FileURL ) override; virtual OUString SAL_CALL getContentType( const OUString& FileURL ) override; virtual css::util::DateTime SAL_CALL getDateTimeModified( const OUString& FileURL ) override; virtual css::uno::Sequence< OUString > SAL_CALL getFolderContents( const OUString& FolderURL, sal_Bool bIncludeFolders ) override; virtual sal_Bool SAL_CALL exists( const OUString& FileURL ) override; virtual css::uno::Reference< css::io::XInputStream > SAL_CALL openFileRead( const OUString& FileURL ) override; virtual css::uno::Reference< css::io::XOutputStream > SAL_CALL openFileWrite( const OUString& FileURL ) override; virtual css::uno::Reference< css::io::XStream > SAL_CALL openFileReadWrite( const OUString& FileURL ) override; virtual void SAL_CALL setInteractionHandler( const css::uno::Reference< css::task::XInteractionHandler >& Handler ) override; virtual void SAL_CALL writeFile( const OUString& FileURL, const css::uno::Reference< css::io::XInputStream >& data ) override; virtual sal_Bool SAL_CALL isHidden( const OUString& FileURL ) override; virtual void SAL_CALL setHidden( const OUString& FileURL, sal_Bool bHidden ) override; OUString SAL_CALL getImplementationName() override { return u"com.sun.star.comp.ucb.SimpleFileAccess"_ustr; } sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override { return cppu::supportsService(this, ServiceName); } css::uno::Sequence SAL_CALL getSupportedServiceNames() override { return { SERVICE_NAME }; } }; // Implementation XActiveDataSink class OActiveDataSink : public cppu::WeakImplHelper< XActiveDataSink > { Reference< XInputStream > mxStream; public: // Methods virtual void SAL_CALL setInputStream( const Reference< XInputStream >& aStream ) override; virtual Reference< XInputStream > SAL_CALL getInputStream( ) override; }; // Implementation XActiveDataStreamer class OActiveDataStreamer : public cppu::WeakImplHelper< XActiveDataStreamer > { Reference< XStream > mxStream; public: // Methods virtual void SAL_CALL setStream( const Reference< XStream >& aStream ) override; virtual Reference< XStream > SAL_CALL getStream() override; }; // Implementation XCommandEnvironment class OCommandEnvironment : public cppu::WeakImplHelper< XCommandEnvironment > { Reference< XInteractionHandler > mxInteraction; public: void setHandler( const Reference< XInteractionHandler >& xInteraction_ ) { mxInteraction = xInteraction_; } // Methods virtual Reference< XInteractionHandler > SAL_CALL getInteractionHandler() override; virtual Reference< XProgressHandler > SAL_CALL getProgressHandler() override; }; void OActiveDataSink::setInputStream( const Reference< XInputStream >& aStream ) { mxStream = aStream; } Reference< XInputStream > OActiveDataSink::getInputStream() { return mxStream; } void OActiveDataStreamer::setStream( const Reference< XStream >& aStream ) { mxStream = aStream; } Reference< XStream > OActiveDataStreamer::getStream() { return mxStream; } Reference< XInteractionHandler > OCommandEnvironment::getInteractionHandler() { return mxInteraction; } Reference< XProgressHandler > OCommandEnvironment::getProgressHandler() { Reference< XProgressHandler > xRet; return xRet; } void OFileAccess::transferImpl( const OUString& rSource, std::u16string_view rDest, bool bMoveData ) { // SfxContentHelper::Transfer_Impl INetURLObject aSourceObj( rSource, INetProtocol::File ); INetURLObject aDestObj( rDest, INetProtocol::File ); OUString aName = aDestObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); OUString aDestURL; OUString aSourceURL = aSourceObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); if ( aDestObj.removeSegment() ) { // hierarchical URL. aDestObj.setFinalSlash(); aDestURL = aDestObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); } else { // non-hierarchical URL // #i29648# if ( aDestObj.GetProtocol() == INetProtocol::VndSunStarExpand ) { // Hack: Expand destination URL using Macro Expander and try again // with the hopefully hierarchical expanded URL... try { Reference< XMacroExpander > xExpander = theMacroExpander::get(m_xContext); aDestURL = xExpander->expandMacros( aDestObj.GetURLPath( INetURLObject::DecodeMechanism::WithCharset ) ); } catch ( Exception const & ) { css::uno::Any anyEx = cppu::getCaughtException(); throw css::lang::WrappedTargetRuntimeException( u"OFileAccess::transferrImpl - Unable to obtain destination folder URL!"_ustr, getXWeak(), anyEx ); } transferImpl( rSource, aDestURL, bMoveData ); return; } throw RuntimeException( u"OFileAccess::transferrImpl - Unable to obtain destination folder URL!"_ustr, getXWeak() ); } ucbhelper::Content aDestPath( aDestURL, mxEnvironment, comphelper::getProcessComponentContext() ); ucbhelper::Content aSrc ( aSourceURL, mxEnvironment, comphelper::getProcessComponentContext() ); try { aDestPath.transferContent(aSrc, bMoveData ? ucbhelper::InsertOperation::Move : ucbhelper::InsertOperation::Copy, aName, css::ucb::NameClash::OVERWRITE); } catch ( css::ucb::CommandFailedException const & ) { // Interaction Handler already handled the error that has occurred... } } void OFileAccess::copy( const OUString& SourceURL, const OUString& DestURL ) { transferImpl( SourceURL, DestURL, false ); } void OFileAccess::move( const OUString& SourceURL, const OUString& DestURL ) { transferImpl( SourceURL, DestURL, true ); } void OFileAccess::kill( const OUString& FileURL ) { // SfxContentHelper::Kill INetURLObject aDeleteObj( FileURL, INetProtocol::File ); ucbhelper::Content aCnt( aDeleteObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); try { aCnt.executeCommand( u"delete"_ustr, Any( true ) ); } catch ( css::ucb::CommandFailedException const & ) { // Interaction Handler already handled the error that has occurred... } } sal_Bool OFileAccess::isFolder( const OUString& FileURL ) { bool bRet = false; try { INetURLObject aURLObj( FileURL, INetProtocol::File ); ucbhelper::Content aCnt( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); bRet = aCnt.isFolder(); } catch (const Exception &) {} return bRet; } sal_Bool OFileAccess::isReadOnly( const OUString& FileURL ) { INetURLObject aURLObj( FileURL, INetProtocol::File ); ucbhelper::Content aCnt( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); Any aRetAny = aCnt.getPropertyValue(u"IsReadOnly"_ustr); bool bRet = false; aRetAny >>= bRet; return bRet; } void OFileAccess::setReadOnly( const OUString& FileURL, sal_Bool bReadOnly ) { INetURLObject aURLObj( FileURL, INetProtocol::File ); ucbhelper::Content aCnt( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); aCnt.setPropertyValue(u"IsReadOnly"_ustr, Any(bReadOnly) ); } void OFileAccess::createFolder( const OUString& NewFolderURL ) { // Does the folder already exist? if( NewFolderURL.isEmpty() || isFolder( NewFolderURL ) ) return; // SfxContentHelper::MakeFolder INetURLObject aURL( NewFolderURL, INetProtocol::File ); OUString aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); if ( !aTitle.isEmpty() ) { aURL.removeSegment(); // Does the base folder exist? Otherwise create it first OUString aBaseFolderURLStr = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); if( !isFolder( aBaseFolderURLStr ) ) { createFolder( aBaseFolderURLStr ); } } ucbhelper::Content aCnt( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); const Sequence< ContentInfo > aInfo = aCnt.queryCreatableContentsInfo(); for ( const ContentInfo & rCurr : aInfo ) { // Simply look for the first KIND_FOLDER... if ( rCurr.Attributes & ContentInfoAttribute::KIND_FOLDER ) { // Make sure the only required bootstrap property is "Title", const Sequence< Property > & rProps = rCurr.Properties; if ( rProps.getLength() != 1 ) continue; if ( rProps[ 0 ].Name != "Title" ) continue; ucbhelper::Content aNew; try { if ( !aCnt.insertNewContent( rCurr.Type, { u"Title"_ustr }, { Any(aTitle) }, aNew ) ) continue; // Success. We're done. return; } catch ( css::ucb::CommandFailedException const & ) { // Interaction Handler already handled the error that has occurred... continue; } } } } sal_Int32 OFileAccess::getSize( const OUString& FileURL ) { // SfxContentHelper::GetSize sal_Int32 nSize = 0; sal_Int64 nTemp = 0; INetURLObject aObj( FileURL, INetProtocol::File ); ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); aCnt.getPropertyValue( u"Size"_ustr ) >>= nTemp; nSize = static_cast(nTemp); return nSize; } OUString OFileAccess::getContentType( const OUString& FileURL ) { INetURLObject aObj( FileURL, INetProtocol::File ); ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); Reference< XContent > xContent = aCnt.get(); OUString aTypeStr = xContent->getContentType(); return aTypeStr; } css::util::DateTime OFileAccess::getDateTimeModified( const OUString& FileURL ) { INetURLObject aFileObj( FileURL, INetProtocol::File ); css::util::DateTime aDateTime; Reference< XCommandEnvironment > aCmdEnv; ucbhelper::Content aYoung( aFileObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aCmdEnv, comphelper::getProcessComponentContext() ); aYoung.getPropertyValue(u"DateModified"_ustr) >>= aDateTime; return aDateTime; } Sequence< OUString > OFileAccess::getFolderContents( const OUString& FolderURL, sal_Bool bIncludeFolders ) { // SfxContentHelper::GetFolderContents std::vector aFiles; INetURLObject aFolderObj( FolderURL, INetProtocol::File ); ucbhelper::Content aCnt( aFolderObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); Reference< XResultSet > xResultSet; ucbhelper::ResultSetInclude eInclude = bIncludeFolders ? ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS : ucbhelper::INCLUDE_DOCUMENTS_ONLY; try { xResultSet = aCnt.createCursor( {}, eInclude ); } catch ( css::ucb::CommandFailedException const & ) { // Interaction Handler already handled the error that has occurred... } if ( xResultSet.is() ) { Reference< css::ucb::XContentAccess > xContentAccess( xResultSet, UNO_QUERY ); while ( xResultSet->next() ) { OUString aId = xContentAccess->queryContentIdentifierString(); INetURLObject aURL( aId, INetProtocol::File ); aFiles.push_back( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); } } return comphelper::containerToSequence(aFiles); } sal_Bool OFileAccess::exists( const OUString& FileURL ) { bool bRet = false; try { bRet = isFolder( FileURL ); if( !bRet ) { Reference< XInputStream > xStream = openFileRead( FileURL ); bRet = xStream.is(); if( bRet ) xStream->closeInput(); } } catch (const Exception &) {} return bRet; } Reference< XInputStream > OFileAccess::openFileRead( const OUString& FileURL ) { Reference< XInputStream > xRet; INetURLObject aObj( FileURL, INetProtocol::File ); ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); Reference xSink = new OActiveDataSink; try { bool bRet = aCnt.openStream( xSink ); if( bRet ) xRet = xSink->getInputStream(); } catch ( css::ucb::CommandFailedException const & ) { // Interaction Handler already handled the error that has occurred... } return xRet; } Reference< XOutputStream > OFileAccess::openFileWrite( const OUString& FileURL ) { Reference< XOutputStream > xRet; Reference< XStream > xStream = OFileAccess::openFileReadWrite( FileURL ); if( xStream.is() ) xRet = xStream->getOutputStream(); return xRet; } Reference< XStream > OFileAccess::openFileReadWrite( const OUString& FileURL ) { Reference xSink = new OActiveDataStreamer; OpenCommandArgument2 aArg; aArg.Mode = OpenMode::DOCUMENT; aArg.Priority = 0; // unused aArg.Sink = xSink; aArg.Properties = Sequence< Property >( 0 ); // unused Any aCmdArg; aCmdArg <<= aArg; INetURLObject aFileObj( FileURL, INetProtocol::File ); ucbhelper::Content aCnt( aFileObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); // Be silent... Reference< XInteractionHandler > xIH; if ( mxEnvironment.is() ) { xIH = mxEnvironment->getInteractionHandler(); mxEnvironment->setHandler( nullptr ); } try { aCnt.executeCommand( u"open"_ustr, aCmdArg ); } catch ( InteractiveIOException const & e ) { if ( xIH.is() && mxEnvironment.is() ) mxEnvironment->setHandler( xIH ); if ( e.Code == IOErrorCode_NOT_EXISTING ) { // Create file... SvMemoryStream aStream(0,0); rtl::Reference<::utl::OInputStreamWrapper> pInput = new ::utl::OInputStreamWrapper( aStream ); InsertCommandArgument aInsertArg; aInsertArg.Data = pInput; aInsertArg.ReplaceExisting = false; aCmdArg <<= aInsertArg; aCnt.executeCommand( u"insert"_ustr, aCmdArg ); // Retry... return openFileReadWrite( FileURL ); } throw; } if ( xIH.is() && mxEnvironment.is() ) mxEnvironment->setHandler( xIH ); Reference< XStream > xRet = xSink->getStream(); return xRet; } void OFileAccess::setInteractionHandler( const Reference< XInteractionHandler >& Handler ) { if( !mxEnvironment.is() ) { mxEnvironment = new OCommandEnvironment; } mxEnvironment->setHandler( Handler ); } bool OFileAccess::createNewFile( const OUString & rParentURL, const OUString & rTitle, const Reference< XInputStream >& data ) { ucbhelper::Content aParentCnt( rParentURL, mxEnvironment, comphelper::getProcessComponentContext() ); const Sequence< ContentInfo > aInfo = aParentCnt.queryCreatableContentsInfo(); for ( const ContentInfo & rCurr : aInfo ) { if ( ( rCurr.Attributes & ContentInfoAttribute::KIND_DOCUMENT ) && ( rCurr.Attributes & ContentInfoAttribute::INSERT_WITH_INPUTSTREAM ) ) { // Make sure the only required bootstrap property is // "Title", const Sequence< Property > & rProps = rCurr.Properties; if ( rProps.getLength() != 1 ) continue; if ( rProps[ 0 ].Name != "Title" ) continue; try { ucbhelper::Content aNew; if ( aParentCnt.insertNewContent( rCurr.Type, { u"Title"_ustr }, { Any(rTitle) }, data, aNew ) ) return true; // success. else continue; } catch ( CommandFailedException const & ) { // Interaction Handler already handled the // error that has occurred... continue; } } } return false; } void SAL_CALL OFileAccess::writeFile( const OUString& FileURL, const Reference< XInputStream >& data ) { INetURLObject aURL( FileURL, INetProtocol::File ); try { ucbhelper::Content aCnt( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); try { aCnt.writeStream( data, true /* bReplaceExisting */ ); } catch ( CommandFailedException const & ) { // Interaction Handler already handled the error that has occurred... } } catch ( ContentCreationException const & e ) { // Most probably file does not exist. Try to create. if ( e.eError == ContentCreationError_CONTENT_CREATION_FAILED ) { INetURLObject aParentURLObj( aURL ); if ( aParentURLObj.removeSegment() ) { OUString aParentURL = aParentURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); // ensure all parent folders exist. createFolder( aParentURL ); // create the new file... OUString aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); if ( createNewFile( aParentURL, aTitle, data ) ) { // success return; } } } throw; } } sal_Bool OFileAccess::isHidden( const OUString& FileURL ) { INetURLObject aURLObj( FileURL, INetProtocol::File ); ucbhelper::Content aCnt( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); Any aRetAny = aCnt.getPropertyValue(u"IsHidden"_ustr); bool bRet = false; aRetAny >>= bRet; return bRet; } void OFileAccess::setHidden( const OUString& FileURL, sal_Bool bHidden ) { INetURLObject aURLObj( FileURL, INetProtocol::File ); ucbhelper::Content aCnt( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); aCnt.setPropertyValue(u"IsHidden"_ustr, Any(bHidden) ); } extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* ucb_OFileAccess_get_implementation( css::uno::XComponentContext* context , css::uno::Sequence const&) { return cppu::acquire(new OFileAccess(context)); } }; // namespace end /* vim:set shiftwidth=4 softtabstop=4 expandtab: */