diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /ucb/source/ucp/gio | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream/4%7.4.7.tar.xz libreoffice-upstream/4%7.4.7.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ucb/source/ucp/gio')
-rw-r--r-- | ucb/source/ucp/gio/gio_content.cxx | 1345 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_content.hxx | 187 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_datasupplier.cxx | 256 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_datasupplier.hxx | 88 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_inputstream.cxx | 91 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_inputstream.hxx | 58 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_mount.cxx | 211 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_mount.hxx | 88 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_outputstream.cxx | 78 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_outputstream.hxx | 62 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_provider.cxx | 139 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_provider.hxx | 52 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_resultset.cxx | 53 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_resultset.hxx | 46 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_seekable.cxx | 126 | ||||
-rw-r--r-- | ucb/source/ucp/gio/gio_seekable.hxx | 60 | ||||
-rw-r--r-- | ucb/source/ucp/gio/ucpgio.component | 26 |
17 files changed, 2966 insertions, 0 deletions
diff --git a/ucb/source/ucp/gio/gio_content.cxx b/ucb/source/ucp/gio/gio_content.cxx new file mode 100644 index 000000000..fc31965a9 --- /dev/null +++ b/ucb/source/ucp/gio/gio_content.cxx @@ -0,0 +1,1345 @@ +/* -*- 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 <sal/config.h> + +#include <rtl/uri.hxx> +#include <utility> + +#include <string.h> +#include <sys/types.h> +#include <sal/macros.h> +#include <osl/time.h> +#include <sal/log.hxx> + +#include <com/sun/star/beans/IllegalTypeException.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/io/IOException.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/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/ucb/ContentInfoAttribute.hpp> +#include <com/sun/star/ucb/InsertCommandArgument.hpp> +#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp> +#include <com/sun/star/ucb/InteractiveNetworkGeneralException.hpp> +#include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp> +#include <com/sun/star/ucb/NameClashException.hpp> +#include <com/sun/star/ucb/OpenMode.hpp> +#include <com/sun/star/ucb/XCommandInfo.hpp> +#include <com/sun/star/ucb/MissingInputStreamException.hpp> +#include <com/sun/star/ucb/UnsupportedCommandException.hpp> +#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp> +#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp> +#include <com/sun/star/ucb/XDynamicResultSet.hpp> +#include <com/sun/star/ucb/XContentCreator.hpp> + +#include <comphelper/seekableinput.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <ucbhelper/contentidentifier.hxx> +#include <ucbhelper/propertyvalueset.hxx> +#include <ucbhelper/cancelcommandexecution.hxx> +#include <ucbhelper/macros.hxx> +#include <vcl/svapp.hxx> + +#include "gio_content.hxx" +#include "gio_provider.hxx" +#include "gio_resultset.hxx" +#include "gio_inputstream.hxx" +#include "gio_outputstream.hxx" +#include "gio_mount.hxx" + +namespace gio +{ + +Content::Content( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + ContentProvider* pProvider, + const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier) + : ContentImplHelper( rxContext, pProvider, Identifier ), + m_pProvider( pProvider ), mpFile (nullptr), mpInfo( nullptr ), mbTransient(false) +{ + SAL_INFO("ucb.ucp.gio", "New Content ('" << m_xIdentifier->getContentIdentifier() << "')"); +} + +Content::Content( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + ContentProvider* pProvider, + const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier, + bool bIsFolder) + : ContentImplHelper( rxContext, pProvider, Identifier ), + m_pProvider( pProvider ), mpFile (nullptr), mpInfo( nullptr ), mbTransient(true) +{ + SAL_INFO("ucb.ucp.gio", "Create Content ('" << m_xIdentifier->getContentIdentifier() << "')"); + mpInfo = g_file_info_new(); + g_file_info_set_file_type(mpInfo, bIsFolder ? G_FILE_TYPE_DIRECTORY : G_FILE_TYPE_REGULAR); +} + +Content::~Content() +{ + if (mpInfo) g_object_unref(mpInfo); + if (mpFile) g_object_unref(mpFile); +} + +OUString Content::getParentURL() +{ + OUString sURL; + if (GFile* pFile = g_file_get_parent(getGFile())) + { + char* pPath = g_file_get_uri(pFile); + g_object_unref(pFile); + sURL = OUString::createFromAscii(pPath); + g_free(pPath); + } + return sURL; +} + +void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ ) +{ + //TODO + //stick a map from each CommandId to a new GCancellable and propagate + //it throughout the g_file_* calls +} + +OUString SAL_CALL Content::getContentType() +{ + return isFolder(css::uno::Reference< css::ucb::XCommandEnvironment >()) + ? OUString( GIO_FOLDER_TYPE ) + : OUString( GIO_FILE_TYPE ); +} + +#define EXCEPT(aExcept) \ +do { \ + if (bThrow) throw aExcept;\ + aRet <<= aExcept;\ +} while(false) + +css::uno::Any convertToException(GError *pError, const css::uno::Reference< css::uno::XInterface >& rContext, bool bThrow) +{ + css::uno::Any aRet; + + gint eCode = pError->code; + OUString sMessage(pError->message, strlen(pError->message), RTL_TEXTENCODING_UTF8); + g_error_free(pError); + + OUString sName; + + css::uno::Sequence< css::uno::Any > aArgs{ css::uno::Any(sName) }; + + switch (eCode) + { + case G_IO_ERROR_FAILED: + { css::io::IOException aExcept(sMessage, rContext); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_NOT_MOUNTED: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NOT_EXISTING_PATH, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_NOT_FOUND: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NOT_EXISTING, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_EXISTS: + { css::ucb::NameClashException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, sName); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_INVALID_ARGUMENT: + { css::lang::IllegalArgumentException aExcept(sMessage, rContext, -1 ); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_PERMISSION_DENIED: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_ACCESS_DENIED, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_IS_DIRECTORY: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NO_FILE, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_NOT_REGULAR_FILE: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NO_FILE, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_NOT_DIRECTORY: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NO_DIRECTORY, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_FILENAME_TOO_LONG: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NAME_TOO_LONG, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_FAILED_HANDLED: /* Operation failed and a helper program + has already interacted with the user. Do not display any error + dialog */ + case G_IO_ERROR_PENDING: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_PENDING, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_CLOSED: + case G_IO_ERROR_CANCELLED: + case G_IO_ERROR_TOO_MANY_LINKS: + case G_IO_ERROR_WRONG_ETAG: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_GENERAL, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_NOT_SUPPORTED: + case G_IO_ERROR_CANT_CREATE_BACKUP: + case G_IO_ERROR_WOULD_MERGE: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NOT_SUPPORTED, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_NO_SPACE: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_OUT_OF_DISK_SPACE, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_INVALID_FILENAME: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_INVALID_CHARACTER, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_READ_ONLY: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_WRITE_PROTECTED, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_TIMED_OUT: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_DEVICE_NOT_READY, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_WOULD_RECURSE: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_RECURSIVE, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_BUSY: + case G_IO_ERROR_WOULD_BLOCK: + { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_LOCKING_VIOLATION, aArgs); + EXCEPT(aExcept); } + break; + case G_IO_ERROR_HOST_NOT_FOUND: + { css::ucb::InteractiveNetworkResolveNameException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR, OUString()); + EXCEPT(aExcept);} + break; + default: + case G_IO_ERROR_ALREADY_MOUNTED: + case G_IO_ERROR_NOT_EMPTY: + case G_IO_ERROR_NOT_SYMBOLIC_LINK: + case G_IO_ERROR_NOT_MOUNTABLE_FILE: + { css::ucb::InteractiveNetworkGeneralException aExcept(sMessage, rContext, + css::task::InteractionClassification_ERROR); + EXCEPT(aExcept);} + break; + } + return aRet; +} + +void convertToIOException(GError *pError, const css::uno::Reference< css::uno::XInterface >& rContext) +{ + try + { + convertToException(pError, rContext); + } + catch (const css::io::IOException&) + { + throw; + } + catch (const css::uno::RuntimeException&) + { + throw; + } + catch (const css::uno::Exception& e) + { + css::uno::Any a(cppu::getCaughtException()); + throw css::lang::WrappedTargetRuntimeException( + "wrapped Exception " + e.Message, + css::uno::Reference<css::uno::XInterface>(), a); + } +} + +css::uno::Any Content::mapGIOError( GError *pError ) +{ + if (!pError) + return getBadArgExcept(); + + return convertToException(pError, static_cast< cppu::OWeakObject * >(this), false); +} + +css::uno::Any Content::getBadArgExcept() +{ + return css::uno::Any( css::lang::IllegalArgumentException( + "Wrong argument type!", + static_cast< cppu::OWeakObject * >( this ), -1) ); +} + +namespace { + +class MountOperation +{ + ucb::ucp::gio::glib::MainContextRef mContext; + GMainLoop *mpLoop; + GMountOperation *mpAuthentication; + GError *mpError; + static void Completed(GObject *source, GAsyncResult *res, gpointer user_data); +public: + explicit MountOperation(const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv); + ~MountOperation(); + GError *Mount(GFile *pFile); +}; + +} + +MountOperation::MountOperation(const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv) : mpError(nullptr) +{ + ucb::ucp::gio::glib::MainContextRef oldContext(g_main_context_ref_thread_default()); + mContext.reset(g_main_context_new()); + mpLoop = g_main_loop_new(mContext.get(), FALSE); + g_main_context_push_thread_default(mContext.get()); + mpAuthentication = ooo_mount_operation_new(std::move(oldContext), xEnv); +} + +void MountOperation::Completed(GObject *source, GAsyncResult *res, gpointer user_data) +{ + MountOperation *pThis = static_cast<MountOperation*>(user_data); + g_file_mount_enclosing_volume_finish(G_FILE(source), res, &(pThis->mpError)); + g_main_loop_quit(pThis->mpLoop); +} + +GError *MountOperation::Mount(GFile *pFile) +{ + g_file_mount_enclosing_volume(pFile, G_MOUNT_MOUNT_NONE, mpAuthentication, nullptr, MountOperation::Completed, this); + { + //HACK: At least the gdk_threads_set_lock_functions(GdkThreadsEnter, + // GdkThreadsLeave) call in vcl/unx/gtk/app/gtkinst.cxx will lead to + // GdkThreadsLeave unlock the SolarMutex down to zero at the end of + // g_main_loop_run, so we need ~SolarMutexReleaser to raise it back to + // the original value again: + if (comphelper::SolarMutex::get()->IsCurrentThread()) + { + SolarMutexReleaser rel; + g_main_loop_run(mpLoop); + } + else + { + g_main_loop_run(mpLoop); + } + } + return mpError; +} + +MountOperation::~MountOperation() +{ + g_object_unref(mpAuthentication); + g_main_context_pop_thread_default(mContext.get()); + g_main_loop_unref(mpLoop); +} + +GFileInfo* Content::getGFileInfo(const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv, GError **ppError) +{ + GError * err = nullptr; + if (mpInfo == nullptr && !mbTransient) { + for (bool retried = false;; retried = true) { + mpInfo = g_file_query_info( + getGFile(), "*", G_FILE_QUERY_INFO_NONE, nullptr, &err); + if (mpInfo != nullptr) { + break; + } + assert(err != nullptr); + if (err->code != G_IO_ERROR_NOT_MOUNTED || retried) { + break; + } + SAL_INFO( + "ucb.ucp.gio", + "G_IO_ERROR_NOT_MOUNTED \"" << err->message + << "\", trying to mount"); + g_error_free(err); + err = MountOperation(xEnv).Mount(getGFile()); + if (err != nullptr) { + break; + } + } + } + if (ppError != nullptr) { + *ppError = err; + } else if (err != nullptr) { + SAL_WARN( + "ucb.ucp.gio", + "ignoring GError \"" << err->message << "\" for <" + << m_xIdentifier->getContentIdentifier() << ">"); + g_error_free(err); + } + return mpInfo; +} + +GFile* Content::getGFile() +{ + if (!mpFile) + mpFile = g_file_new_for_uri(OUStringToOString(m_xIdentifier->getContentIdentifier(), RTL_TEXTENCODING_UTF8).getStr()); + return mpFile; +} + +bool Content::isFolder(const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv) +{ + GFileInfo *pInfo = getGFileInfo(xEnv); + return pInfo && (g_file_info_get_file_type(pInfo) == G_FILE_TYPE_DIRECTORY); +} + +static css::util::DateTime getDateFromUnix (time_t t) +{ + TimeValue tv; + tv.Nanosec = 0; + tv.Seconds = t; + oslDateTime dt; + + if ( osl_getDateTimeFromTimeValue( &tv, &dt ) ) + return css::util::DateTime( 0, dt.Seconds, dt.Minutes, dt.Hours, + dt.Day, dt.Month, dt.Year, false); + else + return css::util::DateTime(); +} + +css::uno::Reference< css::sdbc::XRow > Content::getPropertyValues( + const css::uno::Sequence< css::beans::Property >& rProperties, + const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv ) +{ + rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( m_xContext ); + + GFileInfo *pInfo = nullptr; + for( const css::beans::Property& rProp : rProperties ) + { + if ( rProp.Name == "IsDocument" ) + { + getFileInfo(xEnv, &pInfo, true); + if (pInfo != nullptr && g_file_info_has_attribute(pInfo, G_FILE_ATTRIBUTE_STANDARD_TYPE)) + xRow->appendBoolean( rProp, ( g_file_info_get_file_type( pInfo ) == G_FILE_TYPE_REGULAR || + g_file_info_get_file_type( pInfo ) == G_FILE_TYPE_UNKNOWN ) ); + else + xRow->appendVoid( rProp ); + } + else if ( rProp.Name == "IsFolder" ) + { + getFileInfo(xEnv, &pInfo, true); + if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_STANDARD_TYPE) ) + xRow->appendBoolean( rProp, ( g_file_info_get_file_type( pInfo ) == G_FILE_TYPE_DIRECTORY )); + else + xRow->appendVoid( rProp ); + } + else if ( rProp.Name == "Title" ) + { + getFileInfo(xEnv, &pInfo, false); + if (pInfo != nullptr && g_file_info_has_attribute(pInfo, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME)) + { + const char *pName = g_file_info_get_display_name(pInfo); + xRow->appendString( rProp, OUString(pName, strlen(pName), RTL_TEXTENCODING_UTF8) ); + } + else + xRow->appendVoid(rProp); + } + else if ( rProp.Name == "IsReadOnly" ) + { + getFileInfo(xEnv, &pInfo, true); + if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE ) ) + xRow->appendBoolean( rProp, !g_file_info_get_attribute_boolean( pInfo, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE) ); + else + xRow->appendVoid( rProp ); + } + else if ( rProp.Name == "DateCreated" ) + { + getFileInfo(xEnv, &pInfo, true); + if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_TIME_CREATED ) ) + xRow->appendTimestamp( rProp, getDateFromUnix(g_file_info_get_attribute_uint64(pInfo, G_FILE_ATTRIBUTE_TIME_CREATED)) ); + else + xRow->appendVoid( rProp ); + } + else if ( rProp.Name == "DateModified" ) + { + getFileInfo(xEnv, &pInfo, true); + if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_TIME_CHANGED ) ) + xRow->appendTimestamp( rProp, getDateFromUnix(g_file_info_get_attribute_uint64(pInfo, G_FILE_ATTRIBUTE_TIME_CHANGED)) ); + else + xRow->appendVoid( rProp ); + } + else if ( rProp.Name == "Size" ) + { + getFileInfo(xEnv, &pInfo, true); + if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_STANDARD_SIZE) ) + xRow->appendLong( rProp, ( g_file_info_get_size( pInfo ) )); + else + xRow->appendVoid( rProp ); + } + else if ( rProp.Name == "IsVolume" ) + { + //What do we use this for ? + xRow->appendBoolean( rProp, false ); + } + else if ( rProp.Name == "IsCompactDisc" ) + { + getFileInfo(xEnv, &pInfo, true); + if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT ) ) + xRow->appendBoolean( rProp, g_file_info_get_attribute_boolean(pInfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT) ); + else + xRow->appendVoid( rProp ); + } + else if ( rProp.Name == "IsRemoveable" ) + { + getFileInfo(xEnv, &pInfo, true); + if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT ) ) + xRow->appendBoolean( rProp, g_file_info_get_attribute_boolean(pInfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT ) ); + else + xRow->appendVoid( rProp ); + } + else if ( rProp.Name == "IsFloppy" ) + { + xRow->appendBoolean( rProp, false ); + } + else if ( rProp.Name == "IsHidden" ) + { + getFileInfo(xEnv, &pInfo, true); + if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN) ) + xRow->appendBoolean( rProp, ( g_file_info_get_is_hidden ( pInfo ) ) ); + else + xRow->appendVoid( rProp ); + } + else if ( rProp.Name == "CreatableContentsInfo" ) + { + xRow->appendObject( rProp, css::uno::Any( queryCreatableContentsInfo( xEnv ) ) ); + } + else + { + SAL_WARN( + "ucb.ucp.gio", + "Looking for unsupported property " << rProp.Name); + xRow->appendVoid(rProp); + } + } + + return xRow; +} + +static css::lang::IllegalAccessException +getReadOnlyException( const css::uno::Reference< css::uno::XInterface >& rContext ) +{ + return css::lang::IllegalAccessException ("Property is read-only!", rContext ); +} + +void Content::queryChildren( ContentRefList& rChildren ) +{ + // Obtain a list with a snapshot of all currently instantiated contents + // from provider and extract the contents which are direct children + // of this content. + + ucbhelper::ContentRefList aAllContents; + m_xProvider->queryExistingContents( aAllContents ); + + OUString aURL = m_xIdentifier->getContentIdentifier(); + sal_Int32 nURLPos = aURL.lastIndexOf( '/' ); + + if ( nURLPos != ( aURL.getLength() - 1 ) ) + aURL += "/"; + + sal_Int32 nLen = aURL.getLength(); + + for ( const auto& rContent : aAllContents ) + { + ucbhelper::ContentImplHelperRef xChild = rContent; + OUString aChildURL = xChild->getIdentifier()->getContentIdentifier(); + + // Is aURL a prefix of aChildURL? + if ( ( aChildURL.getLength() > nLen ) && aChildURL.startsWith( aURL ) ) + { + sal_Int32 nPos = aChildURL.indexOf( '/', nLen ); + + if ( ( nPos == -1 ) || ( nPos == ( aChildURL.getLength() - 1 ) ) ) + { + // No further slashes / only a final slash. It's a child! + rChildren.emplace_back(static_cast< ::gio::Content * >(xChild.get() ) ); + } + } + } +} + +bool Content::exchangeIdentity( const css::uno::Reference< css::ucb::XContentIdentifier >& xNewId ) +{ + if ( !xNewId.is() ) + return false; + + css::uno::Reference< css::ucb::XContent > xThis = this; + + if ( mbTransient ) + { + m_xIdentifier = xNewId; + return false; + } + + OUString aOldURL = m_xIdentifier->getContentIdentifier(); + + // Exchange own identity. + if ( exchange( xNewId ) ) + { + // Process instantiated children... + ContentRefList aChildren; + queryChildren( aChildren ); + + for ( const auto& rChild : aChildren ) + { + ContentRef xChild = rChild; + + // Create new content identifier for the child... + css::uno::Reference< css::ucb::XContentIdentifier > xOldChildId = xChild->getIdentifier(); + OUString aOldChildURL = xOldChildId->getContentIdentifier(); + OUString aNewChildURL = aOldChildURL.replaceAt( + 0, aOldURL.getLength(), xNewId->getContentIdentifier() ); + + css::uno::Reference< css::ucb::XContentIdentifier > xNewChildId + = new ::ucbhelper::ContentIdentifier( aNewChildURL ); + + if ( !xChild->exchangeIdentity( xNewChildId ) ) + return false; + } + return true; + } + + return false; +} + +void Content::getFileInfo( + css::uno::Reference<css::ucb::XCommandEnvironment> const & env, GFileInfo ** info, bool fail) +{ + assert(info != nullptr); + if (*info != nullptr) + return; + + GError * err = nullptr; + *info = getGFileInfo(env, &err); + if (*info == nullptr && !mbTransient && fail) + { + ucbhelper::cancelCommandExecution(mapGIOError(err), env); + } + else if (err != nullptr) + { + g_error_free(err); + } +} + +css::uno::Sequence< css::uno::Any > Content::setPropertyValues( + const css::uno::Sequence< css::beans::PropertyValue >& rValues, + const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv ) +{ + GError *pError=nullptr; + GFileInfo *pNewInfo=nullptr; + GFileInfo *pInfo = getGFileInfo(xEnv, &pError); + if (pInfo) + pNewInfo = g_file_info_dup(pInfo); + else + { + if (!mbTransient) + ucbhelper::cancelCommandExecution(mapGIOError(pError), xEnv); + else + { + if (pError) + g_error_free(pError); + pNewInfo = g_file_info_new(); + } + } + + sal_Int32 nCount = rValues.getLength(); + + css::beans::PropertyChangeEvent aEvent; + aEvent.Source = static_cast< cppu::OWeakObject * >( this ); + aEvent.Further = false; + aEvent.PropertyHandle = -1; + + sal_Int32 nChanged = 0, nTitlePos = -1; + OUString aNewTitle; + css::uno::Sequence< css::beans::PropertyChangeEvent > aChanges(nCount); + auto aChangesRange = asNonConstRange(aChanges); + + css::uno::Sequence< css::uno::Any > aRet( nCount ); + auto aRetRange = asNonConstRange(aRet); + const css::beans::PropertyValue* pValues = rValues.getConstArray(); + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const css::beans::PropertyValue& rValue = pValues[ n ]; + SAL_INFO("ucb.ucp.gio", "Set prop '" << rValue.Name << "'"); + if ( rValue.Name == "ContentType" || + rValue.Name == "MediaType" || + rValue.Name == "IsDocument" || + rValue.Name == "IsFolder" || + rValue.Name == "Size" || + rValue.Name == "CreatableContentsInfo" ) + { + aRetRange[ n ] <<= getReadOnlyException( static_cast< cppu::OWeakObject * >(this) ); + } + else if ( rValue.Name == "Title" ) + { + if (!( rValue.Value >>= aNewTitle )) + { + aRetRange[ n ] <<= css::beans::IllegalTypeException + ( "Property value has wrong type!", + static_cast< cppu::OWeakObject * >( this ) ); + continue; + } + + if ( aNewTitle.isEmpty() ) + { + aRetRange[ n ] <<= css::lang::IllegalArgumentException + ( "Empty title not allowed!", + static_cast< cppu::OWeakObject * >( this ), -1 ); + continue; + + } + + OString sNewTitle = OUStringToOString(aNewTitle, RTL_TEXTENCODING_UTF8); + const char *newName = sNewTitle.getStr(); + const char *oldName = g_file_info_get_name( pInfo); + + if (!newName || !oldName || strcmp(newName, oldName)) + { + SAL_INFO("ucb.ucp.gio", "Set new name to '" << newName << "'"); + + aEvent.PropertyName = "Title"; + if (oldName) + aEvent.OldValue <<= OUString(oldName, strlen(oldName), RTL_TEXTENCODING_UTF8); + aEvent.NewValue <<= aNewTitle; + aChangesRange[ nChanged ] = aEvent; + nTitlePos = nChanged++; + + g_file_info_set_name(pNewInfo, newName); + } + } + else + { + SAL_WARN("ucb.ucp.gio", "Unknown property " << rValue.Name); + aRetRange[ n ] <<= getReadOnlyException( static_cast< cppu::OWeakObject * >(this) ); + } + } + + if (nChanged) + { + bool bOk = true; + if (!mbTransient) + { + if ((bOk = doSetFileInfo(pNewInfo))) + { + for (sal_Int32 i = 0; i < nChanged; ++i) + aRetRange[ i ] = getBadArgExcept(); + } + } + + if (bOk) + { + if (nTitlePos > -1) + { + OUString aNewURL = getParentURL(); + if (!aNewURL.isEmpty() && aNewURL[aNewURL.getLength() - 1] != '/') + aNewURL += "/"; + aNewURL += aNewTitle; + + css::uno::Reference< css::ucb::XContentIdentifier > xNewId + = new ::ucbhelper::ContentIdentifier( aNewURL ); + + if (!exchangeIdentity( xNewId ) ) + { + aRetRange[ nTitlePos ] <<= css::uno::Exception + ( "Exchange failed!", + static_cast< cppu::OWeakObject * >( this ) ); + } + } + + if (!mbTransient) //Discard and refetch + { + g_object_unref(mpInfo); + mpInfo = nullptr; + } + + if (mpInfo) + { + g_file_info_copy_into(pNewInfo, mpInfo); + g_object_unref(pNewInfo); + } + else + mpInfo = pNewInfo; + + pNewInfo = nullptr; + + if (mpFile) //Discard and refetch + { + g_object_unref(mpFile); + mpFile = nullptr; + } + } + + aChanges.realloc( nChanged ); + notifyPropertiesChange( aChanges ); + } + + if (pNewInfo) + g_object_unref(pNewInfo); + + return aRet; +} + +bool Content::doSetFileInfo(GFileInfo *pNewInfo) +{ + g_assert (!mbTransient); + + bool bOk = true; + GFile *pFile = getGFile(); + if(!g_file_set_attributes_from_info(pFile, pNewInfo, G_FILE_QUERY_INFO_NONE, nullptr, nullptr)) + bOk = false; + return bOk; +} + +const int TRANSFER_BUFFER_SIZE = 65536; + +void Content::copyData( const css::uno::Reference< css::io::XInputStream >& xIn, + const css::uno::Reference< css::io::XOutputStream >& xOut ) +{ + css::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(); +} + +bool Content::feedSink( const css::uno::Reference< css::uno::XInterface >& xSink ) +{ + if ( !xSink.is() ) + return false; + + css::uno::Reference< css::io::XOutputStream > xOut(xSink, css::uno::UNO_QUERY ); + css::uno::Reference< css::io::XActiveDataSink > xDataSink(xSink, css::uno::UNO_QUERY ); + + if ( !xOut.is() && !xDataSink.is() ) + return false; + + GError *pError=nullptr; + GFileInputStream *pStream = g_file_read(getGFile(), nullptr, &pError); + if (!pStream) + convertToException(pError, static_cast< cppu::OWeakObject * >(this)); + + css::uno::Reference< css::io::XInputStream > xIn( + new comphelper::OSeekableInputWrapper( + new ::gio::InputStream(pStream), m_xContext)); + + if ( xOut.is() ) + copyData( xIn, xOut ); + + if ( xDataSink.is() ) + xDataSink->setInputStream( xIn ); + + return true; +} + +css::uno::Any Content::open(const css::ucb::OpenCommandArgument2 & rOpenCommand, + const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) +{ + bool bIsFolder = isFolder(xEnv); + + if (!g_file_query_exists(getGFile(), nullptr)) + { + css::uno::Sequence< css::uno::Any > aArgs{ css::uno::Any( + m_xIdentifier->getContentIdentifier()) }; + css::uno::Any aErr( + css::ucb::InteractiveAugmentedIOException(OUString(), static_cast< cppu::OWeakObject * >( this ), + css::task::InteractionClassification_ERROR, + bIsFolder ? css::ucb::IOErrorCode_NOT_EXISTING_PATH : css::ucb::IOErrorCode_NOT_EXISTING, aArgs) + ); + + ucbhelper::cancelCommandExecution(aErr, xEnv); + } + + css::uno::Any aRet; + + bool bOpenFolder = ( + ( rOpenCommand.Mode == css::ucb::OpenMode::ALL ) || + ( rOpenCommand.Mode == css::ucb::OpenMode::FOLDERS ) || + ( rOpenCommand.Mode == css::ucb::OpenMode::DOCUMENTS ) + ); + + if ( bOpenFolder && bIsFolder ) + { + css::uno::Reference< css::ucb::XDynamicResultSet > xSet + = new DynamicResultSet( m_xContext, this, rOpenCommand, xEnv ); + aRet <<= xSet; + } + else if ( rOpenCommand.Sink.is() ) + { + if ( + ( rOpenCommand.Mode == css::ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) || + ( rOpenCommand.Mode == css::ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) + ) + { + ucbhelper::cancelCommandExecution( + css::uno::Any ( css::ucb::UnsupportedOpenModeException + ( OUString(), static_cast< cppu::OWeakObject * >( this ), + sal_Int16( rOpenCommand.Mode ) ) ), + xEnv ); + } + + if ( !feedSink( rOpenCommand.Sink ) ) + { + // Note: rOpenCommand.Sink may contain an XStream + // implementation. Support for this type of + // sink is optional... + SAL_WARN("ucb.ucp.gio", "Failed to load data from '" << m_xIdentifier->getContentIdentifier() << "'"); + + ucbhelper::cancelCommandExecution( + css::uno::Any (css::ucb::UnsupportedDataSinkException + ( OUString(), static_cast< cppu::OWeakObject * >( this ), + rOpenCommand.Sink ) ), + xEnv ); + } + } + else + SAL_INFO("ucb.ucp.gio", "Open falling through ..."); + return aRet; +} + +css::uno::Any SAL_CALL Content::execute( + const css::ucb::Command& aCommand, + sal_Int32 /*CommandId*/, + const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv ) +{ + SAL_INFO("ucb.ucp.gio", "Content::execute " << aCommand.Name); + css::uno::Any aRet; + + if ( aCommand.Name == "getPropertyValues" ) + { + css::uno::Sequence< css::beans::Property > Properties; + if ( !( aCommand.Argument >>= Properties ) ) + ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv ); + aRet <<= getPropertyValues( Properties, xEnv ); + } + else if ( aCommand.Name == "getPropertySetInfo" ) + aRet <<= getPropertySetInfo( xEnv, false ); + else if ( aCommand.Name == "getCommandInfo" ) + aRet <<= getCommandInfo( xEnv, false ); + else if ( aCommand.Name == "open" ) + { + css::ucb::OpenCommandArgument2 aOpenCommand; + if ( !( aCommand.Argument >>= aOpenCommand ) ) + ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv ); + aRet = open( aOpenCommand, xEnv ); + } + else if ( aCommand.Name == "transfer" ) + { + css::ucb::TransferInfo transferArgs; + if ( !( aCommand.Argument >>= transferArgs ) ) + ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv ); + transfer( transferArgs, xEnv ); + } + else if ( aCommand.Name == "setPropertyValues" ) + { + css::uno::Sequence< css::beans::PropertyValue > aProperties; + if ( !( aCommand.Argument >>= aProperties ) || !aProperties.hasElements() ) + ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv ); + aRet <<= setPropertyValues( aProperties, xEnv ); + } + else if (aCommand.Name == "createNewContent" + && isFolder( xEnv ) ) + { + css::ucb::ContentInfo arg; + if ( !( aCommand.Argument >>= arg ) ) + ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv ); + aRet <<= createNewContent( arg ); + } + else if ( aCommand.Name == "insert" ) + { + css::ucb::InsertCommandArgument arg; + if ( !( aCommand.Argument >>= arg ) ) + ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv ); + insert( arg.Data, arg.ReplaceExisting, xEnv ); + } + else if ( aCommand.Name == "delete" ) + { + bool bDeletePhysical = false; + aCommand.Argument >>= bDeletePhysical; + + //If no delete physical, try and trashcan it, if that doesn't work go + //ahead and try and delete it anyway + if (!bDeletePhysical && !g_file_trash(getGFile(), nullptr, nullptr)) + bDeletePhysical = true; + + if (bDeletePhysical) + { + GError *pError = nullptr; + if (!g_file_delete( getGFile(), nullptr, &pError)) + ucbhelper::cancelCommandExecution(mapGIOError(pError), xEnv); + } + + destroy( bDeletePhysical ); + } + else + { + SAL_WARN("ucb.ucp.gio", "Unknown command " << aCommand.Name); + + ucbhelper::cancelCommandExecution + ( css::uno::Any( css::ucb::UnsupportedCommandException + ( OUString(), + static_cast< cppu::OWeakObject * >( this ) ) ), + xEnv ); + } + + return aRet; +} + +void Content::destroy( bool bDeletePhysical ) +{ + css::uno::Reference< css::ucb::XContent > xThis = this; + + deleted(); + + ::gio::Content::ContentRefList aChildren; + queryChildren( aChildren ); + + for ( auto& rChild : aChildren ) + { + rChild->destroy( bDeletePhysical ); + } +} + +void Content::insert(const css::uno::Reference< css::io::XInputStream > &xInputStream, + bool bReplaceExisting, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv ) +{ + GError *pError = nullptr; + GFileInfo *pInfo = getGFileInfo(xEnv); + + if ( pInfo && + g_file_info_has_attribute(pInfo, G_FILE_ATTRIBUTE_STANDARD_TYPE) && + g_file_info_get_file_type(pInfo) == G_FILE_TYPE_DIRECTORY ) + { + SAL_INFO("ucb.ucp.gio", "Make directory"); + if( !g_file_make_directory( getGFile(), nullptr, &pError)) + ucbhelper::cancelCommandExecution(mapGIOError(pError), xEnv); + return; + } + + if ( !xInputStream.is() ) + { + ucbhelper::cancelCommandExecution( css::uno::Any + ( css::ucb::MissingInputStreamException + ( OUString(), static_cast< cppu::OWeakObject * >( this ) ) ), + xEnv ); + } + + GFileOutputStream* pOutStream = nullptr; + if ( bReplaceExisting ) + { + if (!(pOutStream = g_file_replace(getGFile(), nullptr, false, G_FILE_CREATE_PRIVATE, nullptr, &pError))) + ucbhelper::cancelCommandExecution(mapGIOError(pError), xEnv); + } + else + { + if (!(pOutStream = g_file_create (getGFile(), G_FILE_CREATE_PRIVATE, nullptr, &pError))) + ucbhelper::cancelCommandExecution(mapGIOError(pError), xEnv); + } + + css::uno::Reference < css::io::XOutputStream > xOutput = new ::gio::OutputStream(pOutStream); + copyData( xInputStream, xOutput ); + + if (mbTransient) + { + mbTransient = false; + inserted(); + } +} + +const GFileCopyFlags DEFAULT_COPYDATA_FLAGS = + static_cast<GFileCopyFlags>(G_FILE_COPY_OVERWRITE|G_FILE_COPY_TARGET_DEFAULT_PERMS); + +void Content::transfer( const css::ucb::TransferInfo& aTransferInfo, const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv ) +{ + OUString sDest = m_xIdentifier->getContentIdentifier(); + if (!sDest.endsWith("/")) { + sDest += "/"; + } + if (!aTransferInfo.NewTitle.isEmpty()) + { + sDest += rtl::Uri::encode( aTransferInfo.NewTitle, + rtl_UriCharClassPchar, + rtl_UriEncodeIgnoreEscapes, + RTL_TEXTENCODING_UTF8 ); + } + else + sDest += OUString::createFromAscii(g_file_get_basename(getGFile())); + + GFile *pDest = g_file_new_for_uri(OUStringToOString(sDest, RTL_TEXTENCODING_UTF8).getStr()); + GFile *pSource = g_file_new_for_uri(OUStringToOString(aTransferInfo.SourceURL, RTL_TEXTENCODING_UTF8).getStr()); + + bool bSuccess = false; + GError *pError = nullptr; + if (aTransferInfo.MoveData) + bSuccess = g_file_move(pSource, pDest, G_FILE_COPY_OVERWRITE, nullptr, nullptr, nullptr, &pError); + else + bSuccess = g_file_copy(pSource, pDest, DEFAULT_COPYDATA_FLAGS, nullptr, nullptr, nullptr, &pError); + g_object_unref(pSource); + g_object_unref(pDest); + if (!bSuccess) { + SAL_INFO( + "ucb.ucp.gio", + "transfer <" << aTransferInfo.SourceURL << "> to <" << sDest << "> (MoveData = " + << int(aTransferInfo.MoveData) << ") failed with \"" << pError->message << "\""); + ucbhelper::cancelCommandExecution(mapGIOError(pError), xEnv); + } +} + +css::uno::Sequence< css::ucb::ContentInfo > Content::queryCreatableContentsInfo( + const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv) +{ + if ( isFolder( xEnv ) ) + { + + // Minimum set of props we really need + css::uno::Sequence< css::beans::Property > props + { + { "Title", -1, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::BOUND } + }; + + return + { + { GIO_FILE_TYPE, ( css::ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM | css::ucb::ContentInfoAttribute::KIND_DOCUMENT ), props }, + { GIO_FOLDER_TYPE, css::ucb::ContentInfoAttribute::KIND_FOLDER, props } + }; + } + else + { + return {}; + } +} + +css::uno::Sequence< css::ucb::ContentInfo > SAL_CALL Content::queryCreatableContentsInfo() +{ + return queryCreatableContentsInfo( css::uno::Reference< css::ucb::XCommandEnvironment >() ); +} + +css::uno::Reference< css::ucb::XContent > + SAL_CALL Content::createNewContent( const css::ucb::ContentInfo& Info ) +{ + bool create_document; + const char *name; + + if ( Info.Type == GIO_FILE_TYPE ) + create_document = true; + else if ( Info.Type == GIO_FOLDER_TYPE ) + create_document = false; + else + { + SAL_WARN("ucb.ucp.gio", "Failed to create new content '" << Info.Type << "'"); + return css::uno::Reference< css::ucb::XContent >(); + } + + SAL_INFO("ucb.ucp.gio", "createNewContent (" << create_document << ")"); + OUString aURL = m_xIdentifier->getContentIdentifier(); + + if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() ) + aURL += "/"; + + name = create_document ? "[New_Content]" : "[New_Collection]"; + aURL += OUString::createFromAscii( name ); + + css::uno::Reference< css::ucb::XContentIdentifier > xId(new ::ucbhelper::ContentIdentifier(aURL)); + + try + { + return new ::gio::Content( m_xContext, m_pProvider, xId, !create_document ); + } catch ( css::ucb::ContentCreationException & ) + { + return css::uno::Reference< css::ucb::XContent >(); + } +} + +css::uno::Sequence< css::uno::Type > SAL_CALL Content::getTypes() +{ + if ( isFolder( css::uno::Reference< css::ucb::XCommandEnvironment >() ) ) + { + static cppu::OTypeCollection s_aFolderCollection + (CPPU_TYPE_REF( css::lang::XTypeProvider ), + CPPU_TYPE_REF( css::lang::XServiceInfo ), + CPPU_TYPE_REF( css::lang::XComponent ), + CPPU_TYPE_REF( css::ucb::XContent ), + CPPU_TYPE_REF( css::ucb::XCommandProcessor ), + CPPU_TYPE_REF( css::beans::XPropertiesChangeNotifier ), + CPPU_TYPE_REF( css::ucb::XCommandInfoChangeNotifier ), + CPPU_TYPE_REF( css::beans::XPropertyContainer ), + CPPU_TYPE_REF( css::beans::XPropertySetInfoChangeNotifier ), + CPPU_TYPE_REF( css::container::XChild ), + CPPU_TYPE_REF( css::ucb::XContentCreator ) ); + return s_aFolderCollection.getTypes(); + } + else + { + static cppu::OTypeCollection s_aFileCollection + (CPPU_TYPE_REF( css::lang::XTypeProvider ), + CPPU_TYPE_REF( css::lang::XServiceInfo ), + CPPU_TYPE_REF( css::lang::XComponent ), + CPPU_TYPE_REF( css::ucb::XContent ), + CPPU_TYPE_REF( css::ucb::XCommandProcessor ), + CPPU_TYPE_REF( css::beans::XPropertiesChangeNotifier ), + CPPU_TYPE_REF( css::ucb::XCommandInfoChangeNotifier ), + CPPU_TYPE_REF( css::beans::XPropertyContainer ), + CPPU_TYPE_REF( css::beans::XPropertySetInfoChangeNotifier ), + CPPU_TYPE_REF( css::container::XChild ) ); + + return s_aFileCollection.getTypes(); + } +} + +css::uno::Sequence< css::beans::Property > Content::getProperties( + const css::uno::Reference< css::ucb::XCommandEnvironment > & /*xEnv*/ ) +{ + static const css::beans::Property aGenericProperties[] = + { + css::beans::Property( "IsDocument", + -1, cppu::UnoType<bool>::get(), + css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( "IsFolder", + -1, cppu::UnoType<bool>::get(), + css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( "Title", + -1, cppu::UnoType<OUString>::get(), + css::beans::PropertyAttribute::BOUND ), + css::beans::Property( "IsReadOnly", + -1, cppu::UnoType<bool>::get(), + css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( "DateCreated", + -1, cppu::UnoType<css::util::DateTime>::get(), + css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( "DateModified", + -1, cppu::UnoType<css::util::DateTime>::get(), + css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( "Size", + -1, cppu::UnoType<sal_Int64>::get(), + css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( "IsVolume", + 1, cppu::UnoType<bool>::get(), + css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( "IsCompactDisc", + -1, cppu::UnoType<bool>::get(), + css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( "IsRemoveable", + -1, cppu::UnoType<bool>::get(), + css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( "IsHidden", + -1, cppu::UnoType<bool>::get(), + css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( "CreatableContentsInfo", + -1, cppu::UnoType<css::uno::Sequence< css::ucb::ContentInfo >>::get(), + css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ) + }; + + const int nProps = SAL_N_ELEMENTS(aGenericProperties); + return css::uno::Sequence< css::beans::Property > ( aGenericProperties, nProps ); +} + +css::uno::Sequence< css::ucb::CommandInfo > Content::getCommands( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv) +{ + static const css::ucb::CommandInfo aCommandInfoTable[] = + { + // Required commands + css::ucb::CommandInfo + ( "getCommandInfo", + -1, cppu::UnoType<void>::get() ), + css::ucb::CommandInfo + ( "getPropertySetInfo", + -1, cppu::UnoType<void>::get() ), + css::ucb::CommandInfo + ( "getPropertyValues", + -1, cppu::UnoType<css::uno::Sequence< css::beans::Property >>::get() ), + css::ucb::CommandInfo + ( "setPropertyValues", + -1, cppu::UnoType<css::uno::Sequence< css::beans::PropertyValue >>::get() ), + + // Optional standard commands + css::ucb::CommandInfo + ( "delete", + -1, cppu::UnoType<bool>::get() ), + css::ucb::CommandInfo + ( "insert", + -1, cppu::UnoType<css::ucb::InsertCommandArgument>::get() ), + css::ucb::CommandInfo + ( "open", + -1, cppu::UnoType<css::ucb::OpenCommandArgument2>::get() ), + + // Folder Only, omitted if not a folder + css::ucb::CommandInfo + ( "transfer", + -1, cppu::UnoType<css::ucb::TransferInfo>::get() ), + css::ucb::CommandInfo + ( "createNewContent", + -1, cppu::UnoType<css::ucb::ContentInfo>::get() ) + }; + + const int nProps = SAL_N_ELEMENTS(aCommandInfoTable); + return css::uno::Sequence< css::ucb::CommandInfo >(aCommandInfoTable, isFolder(xEnv) ? nProps : nProps - 2); +} + +XTYPEPROVIDER_COMMON_IMPL( Content ); + +void SAL_CALL Content::acquire() noexcept +{ + ContentImplHelper::acquire(); +} + +void SAL_CALL Content::release() noexcept +{ + ContentImplHelper::release(); +} + +css::uno::Any SAL_CALL Content::queryInterface( const css::uno::Type & rType ) +{ + css::uno::Any aRet = cppu::queryInterface( rType, static_cast< css::ucb::XContentCreator * >( this ) ); + return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface(rType); +} + +OUString SAL_CALL Content::getImplementationName() +{ + return "com.sun.star.comp.GIOContent"; +} + +css::uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames() +{ + css::uno::Sequence<OUString> aSNS { "com.sun.star.ucb.GIOContent" }; + return aSNS; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_content.hxx b/ucb/source/ucp/gio/gio_content.hxx new file mode 100644 index 000000000..0c80da5cd --- /dev/null +++ b/ucb/source/ucp/gio/gio_content.hxx @@ -0,0 +1,187 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/ucb/OpenCommandArgument2.hpp> +#include <com/sun/star/ucb/TransferInfo.hpp> +#include <com/sun/star/ucb/XContentCreator.hpp> +#include <ucbhelper/contenthelper.hxx> +#include <gio/gio.h> + +#include <vector> + +namespace com::sun::star { + namespace beans { + struct Property; + struct PropertyValue; + } + namespace sdbc { + class XRow; + } +} +namespace ucbhelper +{ + class Content; +} + + +namespace gio +{ + + +inline constexpr OUStringLiteral GIO_FILE_TYPE = u"application/vnd.sun.staroffice.gio-file"; +inline constexpr OUStringLiteral GIO_FOLDER_TYPE = u"application/vnd.sun.staroffice.gio-folder"; + +css::uno::Any convertToException(GError *pError, + const css::uno::Reference< css::uno::XInterface >& rContext, bool bThrow=true); +/// @throws css::io::IOException +/// @throws css::uno::RuntimeException +void convertToIOException(GError *pError, + const css::uno::Reference< css::uno::XInterface >& rContext); + +class ContentProvider; +class Content : public ::ucbhelper::ContentImplHelper, public css::ucb::XContentCreator +{ +private: + ContentProvider *m_pProvider; + GFile* mpFile; + GFileInfo *mpInfo; + bool mbTransient; + + GFileInfo *getGFileInfo(const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv, + GError **ppError=nullptr); + bool isFolder(const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv); + + css::uno::Any mapGIOError( GError *error ); + css::uno::Any getBadArgExcept(); + + css::uno::Reference< css::sdbc::XRow > + getPropertyValues( + const css::uno::Sequence< css::beans::Property >& rProperties, + const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv ); +private: + typedef rtl::Reference< Content > ContentRef; + typedef std::vector< ContentRef > ContentRefList; + + void queryChildren( ContentRefList& rChildren ); + + bool doSetFileInfo ( GFileInfo *pNewInfo ); + + /// @throws css::uno::Exception + css::uno::Any open(const css::ucb::OpenCommandArgument2 & rArg, + const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ); + + /// @throws css::uno::Exception + void transfer( const css::ucb::TransferInfo& rTransferInfo, + const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ); + + /// @throws css::uno::Exception + void insert( const css::uno::Reference< css::io::XInputStream > & xInputStream, + bool bReplaceExisting, const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv ); + + /// @throws css::uno::Exception + void destroy( bool bDeletePhysical ); + + static void copyData( const css::uno::Reference< css::io::XInputStream >& xIn, + const css::uno::Reference< css::io::XOutputStream >& xOut ); + + css::uno::Sequence< css::uno::Any > + setPropertyValues( const css::uno::Sequence< + css::beans::PropertyValue >& rValues, + const css::uno::Reference< + css::ucb::XCommandEnvironment >& xEnv ); + + bool feedSink( const css::uno::Reference< css::uno::XInterface>& aSink ); + + bool exchangeIdentity(const css::uno::Reference< css::ucb::XContentIdentifier >& xNewId); + + void getFileInfo( + css::uno::Reference<css::ucb::XCommandEnvironment> const & env, GFileInfo ** info, + bool fail); + +public: + /// @throws css::ucb::ContentCreationException + Content( const css::uno::Reference< + css::uno::XComponentContext >& rxContext, ContentProvider *pProvider, + const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier); + + /// @throws css::ucb::ContentCreationException + Content( const css::uno::Reference< + css::uno::XComponentContext >& rxContext, ContentProvider *pProvider, + const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier, + bool bIsFolder); + + virtual ~Content() override; + + virtual css::uno::Sequence< css::beans::Property > + getProperties( const css::uno::Reference< + css::ucb::XCommandEnvironment > & xEnv ) override; + + virtual css::uno::Sequence< css::ucb::CommandInfo > + getCommands( const css::uno::Reference< + css::ucb::XCommandEnvironment > & xEnv ) override; + + virtual OUString getParentURL() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() + noexcept override; + virtual void SAL_CALL release() + noexcept override; + + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + + virtual OUString SAL_CALL + getImplementationName() override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + virtual OUString SAL_CALL + getContentType() override; + + virtual css::uno::Any SAL_CALL + execute( const css::ucb::Command& aCommand, + sal_Int32 CommandId, + const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ) override; + + virtual void SAL_CALL abort( sal_Int32 CommandId ) override; + + virtual css::uno::Sequence< css::ucb::ContentInfo > + SAL_CALL queryCreatableContentsInfo() override; + virtual css::uno::Reference< css::ucb::XContent > + SAL_CALL createNewContent( const css::ucb::ContentInfo& Info ) override; + + /// @throws css::uno::RuntimeException + css::uno::Sequence< css::ucb::ContentInfo > + queryCreatableContentsInfo( + const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv); + + GFile* getGFile(); +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_datasupplier.cxx b/ucb/source/ucp/gio/gio_datasupplier.cxx new file mode 100644 index 000000000..fd4335dc5 --- /dev/null +++ b/ucb/source/ucp/gio/gio_datasupplier.cxx @@ -0,0 +1,256 @@ +/* -*- 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 <ucbhelper/contentidentifier.hxx> +#include <ucbhelper/providerhelper.hxx> +#include <com/sun/star/ucb/IllegalIdentifierException.hpp> +#include <com/sun/star/ucb/OpenMode.hpp> + +#include "gio_datasupplier.hxx" +#include "gio_content.hxx" + +using namespace gio; + +namespace gio +{ + +DataSupplier::DataSupplier( const rtl::Reference< ::gio::Content >& rContent, sal_Int32 nOpenMode ) + : mxContent(rContent), mnOpenMode(nOpenMode), mbCountFinal(false) +{ +} + +bool DataSupplier::getData() +{ + if (mbCountFinal) + return true; + + GFile *pFile = mxContent->getGFile(); + + GFileEnumerator* pEnumerator = g_file_enumerate_children(pFile, "*", + G_FILE_QUERY_INFO_NONE, nullptr, nullptr); + + if (!pEnumerator) + return false; + + GFileInfo *pInfo = nullptr; + while ((pInfo = g_file_enumerator_next_file (pEnumerator, nullptr, nullptr))) + { + switch ( mnOpenMode ) + { + case css::ucb::OpenMode::FOLDERS: + if (g_file_info_get_file_type(pInfo) != G_FILE_TYPE_DIRECTORY) + continue; + break; + case css::ucb::OpenMode::DOCUMENTS: + if (g_file_info_get_file_type(pInfo) != G_FILE_TYPE_REGULAR) + continue; + break; + case css::ucb::OpenMode::ALL: + default: + break; + } + + maResults.emplace_back( new ResultListEntry( pInfo ) ); + g_object_unref(pInfo); + } + + mbCountFinal = true; + + g_file_enumerator_close(pEnumerator, nullptr, nullptr); + return true; +} + +DataSupplier::~DataSupplier() +{ +} + +OUString DataSupplier::queryContentIdentifierString( sal_uInt32 nIndex ) +{ + if ( nIndex < maResults.size() ) + { + OUString aId = maResults[ nIndex ]->aId; + if ( aId.getLength() ) + { + // Already cached. + return aId; + } + } + + if ( getResult( nIndex ) ) + { + GFile *pFile = mxContent->getGFile(); + char* parent = g_file_get_uri(pFile); + OUString aId = OUString::createFromAscii( parent ); + g_free(parent); + + char *escaped_name = + g_uri_escape_string( g_file_info_get_name(maResults[ nIndex ]->pInfo) , nullptr, false); + + if ( ( aId.lastIndexOf( '/' ) + 1 ) != aId.getLength() ) + aId += "/"; + + aId += OUString::createFromAscii( escaped_name ); + + g_free( escaped_name ); + + maResults[ nIndex ]->aId = aId; + return aId; + } + + return OUString(); +} + +css::uno::Reference< css::ucb::XContentIdentifier > DataSupplier::queryContentIdentifier( sal_uInt32 nIndex ) +{ + if ( nIndex < maResults.size() ) + { + css::uno::Reference< css::ucb::XContentIdentifier > xId = maResults[ nIndex ]->xId; + if ( xId.is() ) + { + // Already cached. + return xId; + } + } + + OUString aId = queryContentIdentifierString( nIndex ); + if ( aId.getLength() ) + { + css::uno::Reference< css::ucb::XContentIdentifier > xId = new ucbhelper::ContentIdentifier( aId ); + maResults[ nIndex ]->xId = xId; + return xId; + } + + return css::uno::Reference< css::ucb::XContentIdentifier >(); +} + +css::uno::Reference< css::ucb::XContent > DataSupplier::queryContent( sal_uInt32 nIndex ) +{ + if ( nIndex < maResults.size() ) + { + css::uno::Reference< css::ucb::XContent > xContent = maResults[ nIndex ]->xContent; + if ( xContent.is() ) + { + // Already cached. + return xContent; + } + } + + css::uno::Reference< css::ucb::XContentIdentifier > xId = queryContentIdentifier( nIndex ); + if ( xId.is() ) + { + try + { + css::uno::Reference< css::ucb::XContent > xContent = mxContent->getProvider()->queryContent( xId ); + maResults[ nIndex ]->xContent = xContent; + return xContent; + } + catch ( css::ucb::IllegalIdentifierException& ) + { + } + } + return css::uno::Reference< css::ucb::XContent >(); +} + +bool DataSupplier::getResult( sal_uInt32 nIndex ) +{ + if ( maResults.size() > nIndex ) // Result already present. + return true; + + if ( getData() && maResults.size() > nIndex ) + return true; + + return false; +} + +sal_uInt32 DataSupplier::totalCount() +{ + getData(); + return maResults.size(); +} + +sal_uInt32 DataSupplier::currentCount() +{ + return maResults.size(); +} + +bool DataSupplier::isCountFinal() +{ + return mbCountFinal; +} + +css::uno::Reference< css::sdbc::XRow > DataSupplier::queryPropertyValues( sal_uInt32 nIndex ) +{ + if ( nIndex < maResults.size() ) + { + css::uno::Reference< css::sdbc::XRow > xRow = maResults[ nIndex ]->xRow; + if ( xRow.is() ) + { + // Already cached. + return xRow; + } + } + + if ( getResult( nIndex ) ) + { + css::uno::Reference< css::ucb::XContent > xContent( queryContent( nIndex ) ); + if ( xContent.is() ) + { + try + { + css::uno::Reference< css::ucb::XCommandProcessor > xCmdProc( + xContent, css::uno::UNO_QUERY_THROW ); + sal_Int32 nCmdId( xCmdProc->createCommandIdentifier() ); + css::ucb::Command aCmd; + aCmd.Name = "getPropertyValues"; + aCmd.Handle = -1; + aCmd.Argument <<= getResultSet()->getProperties(); + css::uno::Any aResult( xCmdProc->execute( + aCmd, nCmdId, getResultSet()->getEnvironment() ) ); + css::uno::Reference< css::sdbc::XRow > xRow; + if ( aResult >>= xRow ) + { + maResults[ nIndex ]->xRow = xRow; + return xRow; + } + } + catch ( css::uno::Exception const & ) + { + } + } + } + return css::uno::Reference< css::sdbc::XRow >(); +} + +void DataSupplier::releasePropertyValues( sal_uInt32 nIndex ) +{ + if ( nIndex < maResults.size() ) + maResults[ nIndex ]->xRow.clear(); +} + +void DataSupplier::close() +{ +} + +void DataSupplier::validate() +{ +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_datasupplier.hxx b/ucb/source/ucp/gio/gio_datasupplier.hxx new file mode 100644 index 000000000..4a0e0b36e --- /dev/null +++ b/ucb/source/ucp/gio/gio_datasupplier.hxx @@ -0,0 +1,88 @@ +/* -*- 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 . + */ + +#pragma once + +#include <ucbhelper/resultset.hxx> +#include "gio_content.hxx" +#include <memory> +#include <vector> + +namespace gio +{ + +class Content; + +struct ResultListEntry +{ + OUString aId; + css::uno::Reference< css::ucb::XContentIdentifier > xId; + css::uno::Reference< css::ucb::XContent > xContent; + css::uno::Reference< css::sdbc::XRow > xRow; + GFileInfo *pInfo; + + explicit ResultListEntry( GFileInfo *pInInfo ) : pInfo(pInInfo) + { + g_object_ref( pInfo ); + } + + ~ResultListEntry() + { + g_object_unref( pInfo ); + } +}; + +typedef std::vector< std::unique_ptr<ResultListEntry> > ResultList; + +class DataSupplier : public ucbhelper::ResultSetDataSupplier +{ +private: + rtl::Reference< ::gio::Content > mxContent; + sal_Int32 mnOpenMode; + bool mbCountFinal; + bool getData(); + ResultList maResults; +public: + DataSupplier( const rtl::Reference< Content >& rContent, sal_Int32 nOpenMode ); + virtual ~DataSupplier() override; + + virtual OUString queryContentIdentifierString( sal_uInt32 nIndex ) override; + virtual css::uno::Reference< css::ucb::XContentIdentifier > + queryContentIdentifier( sal_uInt32 nIndex ) override; + virtual css::uno::Reference< css::ucb::XContent > + queryContent( sal_uInt32 nIndex ) override; + + virtual bool getResult( sal_uInt32 nIndex ) override; + + virtual sal_uInt32 totalCount() override; + virtual sal_uInt32 currentCount() override; + virtual bool isCountFinal() override; + + virtual css::uno::Reference< css::sdbc::XRow > + queryPropertyValues( sal_uInt32 nIndex ) override; + virtual void releasePropertyValues( sal_uInt32 nIndex ) override; + + virtual void close() override; + + virtual void validate() override; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_inputstream.cxx b/ucb/source/ucp/gio/gio_inputstream.cxx new file mode 100644 index 000000000..c6df4572b --- /dev/null +++ b/ucb/source/ucp/gio/gio_inputstream.cxx @@ -0,0 +1,91 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/io/BufferSizeExceededException.hpp> +#include <com/sun/star/io/NotConnectedException.hpp> + +#include "gio_inputstream.hxx" +#include "gio_content.hxx" + +namespace gio +{ + +InputStream::InputStream(GFileInputStream *pStream): mpStream(pStream) +{ + if (!mpStream) + throw css::io::NotConnectedException(); +} + +InputStream::~InputStream() +{ + closeInput(); +} + +sal_Int32 SAL_CALL InputStream::available() +{ + return 0; +} + +void SAL_CALL InputStream::closeInput() +{ + if (mpStream) + g_input_stream_close(G_INPUT_STREAM(mpStream), nullptr, nullptr); +} + +void SAL_CALL InputStream::skipBytes( sal_Int32 nBytesToSkip ) +{ + // Conservatively call readBytes and discard the read data, but given this + // InputStream will always be wrapped in comphelper::OSeekableInputWrapper, + // this function will never be called anyway: + css::uno::Sequence<sal_Int8> data; + readBytes(data, nBytesToSkip); +} + +sal_Int32 SAL_CALL InputStream::readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) +{ + if (!mpStream) + throw css::io::NotConnectedException(); + + try + { + aData.realloc( nBytesToRead ); + } + catch ( const css::uno::Exception & ) + { + throw css::io::BufferSizeExceededException(); + } + + gsize nBytesRead = 0; + GError *pError=nullptr; + if (!g_input_stream_read_all(G_INPUT_STREAM(mpStream), aData.getArray(), nBytesToRead, &nBytesRead, nullptr, &pError)) + convertToIOException(pError, static_cast< cppu::OWeakObject * >(this)); + aData.realloc(nBytesRead); + return nBytesRead; +} + +sal_Int32 SAL_CALL InputStream::readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) +{ + return readBytes(aData, nMaxBytesToRead); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_inputstream.hxx b/ucb/source/ucp/gio/gio_inputstream.hxx new file mode 100644 index 000000000..9608bc5b3 --- /dev/null +++ b/ucb/source/ucp/gio/gio_inputstream.hxx @@ -0,0 +1,58 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <rtl/ustring.hxx> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/io/XInputStream.hpp> + +#include <gio/gio.h> + +namespace gio +{ + +class InputStream final : public cppu::WeakImplHelper<css::io::XInputStream> +{ +private: + GFileInputStream *mpStream; + +public: + explicit InputStream ( GFileInputStream *pStream ); + virtual ~InputStream() override; + + // XInputStream + virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence< sal_Int8 > & aData, + sal_Int32 nBytesToRead ) override; + + virtual sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< sal_Int8 > & aData, + sal_Int32 nMaxBytesToRead ) override; + + virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override; + + virtual sal_Int32 SAL_CALL available() override; + + virtual void SAL_CALL closeInput() override; +}; + +} // namespace gio + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_mount.cxx b/ucb/source/ucp/gio/gio_mount.cxx new file mode 100644 index 000000000..1d2dbcea8 --- /dev/null +++ b/ucb/source/ucp/gio/gio_mount.cxx @@ -0,0 +1,211 @@ +/* -*- 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 <sal/config.h> + +#include <utility> + +#include "gio_mount.hxx" +#include <ucbhelper/simpleauthenticationrequest.hxx> +#include <string.h> + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#if defined __clang__ +#if __has_warning("-Wdeprecated-volatile") +#pragma clang diagnostic ignored "-Wdeprecated-volatile" +#endif +#endif +#endif +G_DEFINE_TYPE (OOoMountOperation, ooo_mount_operation, G_TYPE_MOUNT_OPERATION); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +static void ooo_mount_operation_ask_password (GMountOperation *op, + const char *message, const char *default_user, const char *default_domain, + GAskPasswordFlags flags); + +static void ooo_mount_operation_init (OOoMountOperation *op) +{ + op->m_pPrevPassword = nullptr; + op->m_pPrevUsername = nullptr; +} + +static void ooo_mount_operation_finalize (GObject *object) +{ + OOoMountOperation *mount_op = OOO_MOUNT_OPERATION (object); + if (mount_op->m_pPrevUsername) + free(mount_op->m_pPrevUsername); + if (mount_op->m_pPrevPassword) + free(mount_op->m_pPrevPassword); + mount_op->context.reset(); + + G_OBJECT_CLASS (ooo_mount_operation_parent_class)->finalize (object); +} + +static void ooo_mount_operation_class_init (OOoMountOperationClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = ooo_mount_operation_finalize; + + GMountOperationClass *mount_op_class = G_MOUNT_OPERATION_CLASS (klass); + mount_op_class->ask_password = ooo_mount_operation_ask_password; +} + +namespace { + +// Temporarily undo the g_main_context_push_thread_default done in the surrounding MountOperation +// ctor (in ucb/source/ucp/gio/gio_content.cxx): +struct GlibThreadDefaultMainContextScope { +public: + GlibThreadDefaultMainContextScope(GMainContext * context): context_(context) + { g_main_context_push_thread_default(context_); } + + ~GlibThreadDefaultMainContextScope() { g_main_context_pop_thread_default(context_); } + +private: + GMainContext * context_; +}; + +} + +static void ooo_mount_operation_ask_password (GMountOperation *op, + const char * /*message*/, const char *default_user, + const char *default_domain, GAskPasswordFlags flags) +{ + css::uno::Reference< css::task::XInteractionHandler > xIH; + + OOoMountOperation *pThis = reinterpret_cast<OOoMountOperation*>(op); + GlibThreadDefaultMainContextScope scope(pThis->context.get()); + + const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv = *(pThis->pEnv); + + if (xEnv.is()) + xIH = xEnv->getInteractionHandler(); + + if (!xIH.is()) + { + g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED); + return; + } + + OUString aDomain, aUserName, aPassword; + + if (default_user) + aUserName = OUString(default_user, strlen(default_user), RTL_TEXTENCODING_UTF8); + + ucbhelper::SimpleAuthenticationRequest::EntityType eUserName = + (flags & G_ASK_PASSWORD_NEED_USERNAME) + ? ucbhelper::SimpleAuthenticationRequest::ENTITY_MODIFY + : aUserName.isEmpty() ? ucbhelper::SimpleAuthenticationRequest::ENTITY_NA + : ucbhelper::SimpleAuthenticationRequest::ENTITY_FIXED; + + ucbhelper::SimpleAuthenticationRequest::EntityType ePassword = + (flags & G_ASK_PASSWORD_NEED_PASSWORD) + ? ucbhelper::SimpleAuthenticationRequest::ENTITY_MODIFY + : ucbhelper::SimpleAuthenticationRequest::ENTITY_NA; + + OUString aPrevPassword, aPrevUsername; + if (pThis->m_pPrevUsername) + aPrevUsername = OUString(pThis->m_pPrevUsername, strlen(pThis->m_pPrevUsername), RTL_TEXTENCODING_UTF8); + if (pThis->m_pPrevPassword) + aPrevPassword = OUString(pThis->m_pPrevPassword, strlen(pThis->m_pPrevPassword), RTL_TEXTENCODING_UTF8); + + //The damn dialog is stupidly broken, so do like webdav, i.e. "#102871#" + if ( aUserName.isEmpty() ) + aUserName = aPrevUsername; + + if ( aPassword.isEmpty() ) + aPassword = aPrevPassword; + + ucbhelper::SimpleAuthenticationRequest::EntityType eDomain = + (flags & G_ASK_PASSWORD_NEED_DOMAIN) + ? ucbhelper::SimpleAuthenticationRequest::ENTITY_MODIFY + : ucbhelper::SimpleAuthenticationRequest::ENTITY_NA; + + if (default_domain) + aDomain = OUString(default_domain, strlen(default_domain), RTL_TEXTENCODING_UTF8); + + rtl::Reference< ucbhelper::SimpleAuthenticationRequest > xRequest + = new ucbhelper::SimpleAuthenticationRequest (OUString() /* FIXME: provide URL here */, OUString(), eDomain, aDomain, eUserName, aUserName, ePassword, aPassword); + + xIH->handle( xRequest ); + + rtl::Reference< ucbhelper::InteractionContinuation > xSelection = xRequest->getSelection(); + + if ( !xSelection.is() ) + { + g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED); + return; + } + + css::uno::Reference< css::task::XInteractionAbort > xAbort(xSelection.get(), css::uno::UNO_QUERY ); + if ( xAbort.is() ) + { + g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED); + return; + } + + const rtl::Reference< ucbhelper::InteractionSupplyAuthentication > & xSupp = xRequest->getAuthenticationSupplier(); + aUserName = xSupp->getUserName(); + aPassword = xSupp->getPassword(); + + if (flags & G_ASK_PASSWORD_NEED_USERNAME) + g_mount_operation_set_username(op, OUStringToOString(aUserName, RTL_TEXTENCODING_UTF8).getStr()); + + if (flags & G_ASK_PASSWORD_NEED_PASSWORD) + g_mount_operation_set_password(op, OUStringToOString(aPassword, RTL_TEXTENCODING_UTF8).getStr()); + + if (flags & G_ASK_PASSWORD_NEED_DOMAIN) + g_mount_operation_set_domain(op, OUStringToOString(xSupp->getRealm(), RTL_TEXTENCODING_UTF8).getStr()); + + switch (xSupp->getRememberPasswordMode()) + { + default: + case css::ucb::RememberAuthentication_NO: + g_mount_operation_set_password_save(op, G_PASSWORD_SAVE_NEVER); + break; + case css::ucb::RememberAuthentication_SESSION: + g_mount_operation_set_password_save(op, G_PASSWORD_SAVE_FOR_SESSION); + break; + case css::ucb::RememberAuthentication_PERSISTENT: + g_mount_operation_set_password_save(op, G_PASSWORD_SAVE_PERMANENTLY); + break; + } + + if (pThis->m_pPrevPassword) + free(pThis->m_pPrevPassword); + pThis->m_pPrevPassword = strdup(OUStringToOString(aPassword, RTL_TEXTENCODING_UTF8).getStr()); + if (pThis->m_pPrevUsername) + free(pThis->m_pPrevUsername); + pThis->m_pPrevUsername = strdup(OUStringToOString(aUserName, RTL_TEXTENCODING_UTF8).getStr()); + g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); +} + +GMountOperation *ooo_mount_operation_new(ucb::ucp::gio::glib::MainContextRef && context, const css::uno::Reference< css::ucb::XCommandEnvironment >& rEnv) +{ + OOoMountOperation *pRet = static_cast<OOoMountOperation*>(g_object_new (OOO_TYPE_MOUNT_OPERATION, nullptr)); + pRet->context = std::move(context); + pRet->pEnv = &rEnv; + return &pRet->parent_instance; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_mount.hxx b/ucb/source/ucp/gio/gio_mount.hxx new file mode 100644 index 000000000..2ddcaebf5 --- /dev/null +++ b/ucb/source/ucp/gio/gio_mount.hxx @@ -0,0 +1,88 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <memory> + +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define OOO_TYPE_MOUNT_OPERATION (ooo_mount_operation_get_type ()) +#define OOO_MOUNT_OPERATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OOO_TYPE_MOUNT_OPERATION, OOoMountOperation)) +#define OOO_MOUNT_OPERATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), OOO_TYPE_MOUNT_OPERATION, OOoMountOperationClass)) +#define OOO_IS_MOUNT_OPERATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OOO_TYPE_MOUNT_OPERATION)) +#define OOO_IS_MOUNT_OPERATION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OOO_TYPE_MOUNT_OPERATION)) +#define OOO_MOUNT_OPERATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OOO_TYPE_MOUNT_OPERATION, OOoMountOperationClass)) + +namespace ucb::ucp::gio::glib { + +namespace detail { + +struct MainContextUnref { + void operator ()(GMainContext * context) { + if (context != nullptr) { + g_main_context_unref(context); + } + } +}; + +} + +using MainContextRef = std::unique_ptr<GMainContext, detail::MainContextUnref>; + +} + +struct OOoMountOperation +{ + GMountOperation parent_instance; + + ucb::ucp::gio::glib::MainContextRef context; + const css::uno::Reference< css::ucb::XCommandEnvironment > *pEnv; + char *m_pPrevUsername; + char *m_pPrevPassword; + +private: + // Managed via ooo_mount_operation_new and ooo_mount_operation_finalize: + OOoMountOperation() = delete; + ~OOoMountOperation() = delete; +}; + +struct OOoMountOperationClass +{ + GMountOperationClass parent_class; + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); +}; + + +GType ooo_mount_operation_get_type(); +GMountOperation *ooo_mount_operation_new(ucb::ucp::gio::glib::MainContextRef && context, const css::uno::Reference< css::ucb::XCommandEnvironment >& rEnv); + +G_END_DECLS + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_outputstream.cxx b/ucb/source/ucp/gio/gio_outputstream.cxx new file mode 100644 index 000000000..1f334f4df --- /dev/null +++ b/ucb/source/ucp/gio/gio_outputstream.cxx @@ -0,0 +1,78 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/io/NotConnectedException.hpp> +#include <cppuhelper/queryinterface.hxx> + +#include "gio_outputstream.hxx" +#include "gio_content.hxx" + +namespace gio +{ + +OutputStream::OutputStream(GFileOutputStream *pStream) : Seekable(G_SEEKABLE(pStream)), mpStream(pStream) +{ + if (!mpStream) + throw css::io::NotConnectedException(); +} + +OutputStream::~OutputStream() +{ + closeOutput(); +} + +void SAL_CALL OutputStream::writeBytes( const css::uno::Sequence< sal_Int8 >& rData ) +{ + if (!mpStream) + throw css::io::NotConnectedException(); + + GError *pError=nullptr; + if (!g_output_stream_write_all(G_OUTPUT_STREAM(mpStream), rData.getConstArray(), rData.getLength(), nullptr, nullptr, &pError)) + convertToIOException(pError, static_cast< cppu::OWeakObject * >(this)); +} + +void SAL_CALL OutputStream::flush() +{ + if (!mpStream) + throw css::io::NotConnectedException(); + + GError *pError=nullptr; + if (!g_output_stream_flush(G_OUTPUT_STREAM(mpStream), nullptr, &pError)) + convertToIOException(pError, static_cast< cppu::OWeakObject * >(this)); +} + +void SAL_CALL OutputStream::closeOutput() +{ + if (mpStream) + g_output_stream_close(G_OUTPUT_STREAM(mpStream), nullptr, nullptr); +} + +css::uno::Any OutputStream::queryInterface( const css::uno::Type &type ) +{ + css::uno::Any aRet = ::cppu::queryInterface ( type, + static_cast< XOutputStream * >( this ) ); + + return aRet.hasValue() ? aRet : Seekable::queryInterface( type ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_outputstream.hxx b/ucb/source/ucp/gio/gio_outputstream.hxx new file mode 100644 index 000000000..40bdbcfc4 --- /dev/null +++ b/ucb/source/ucp/gio/gio_outputstream.hxx @@ -0,0 +1,62 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <rtl/ustring.hxx> +#include <cppuhelper/weak.hxx> + +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XTruncate.hpp> +#include <com/sun/star/io/XSeekable.hpp> + +#include "gio_seekable.hxx" + +namespace gio +{ + +class OutputStream final : + public css::io::XOutputStream, + public Seekable +{ +private: + GFileOutputStream *mpStream; + +public: + explicit OutputStream ( GFileOutputStream *pStream ); + virtual ~OutputStream() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type & type ) override; + virtual void SAL_CALL acquire() noexcept override { OWeakObject::acquire(); } + virtual void SAL_CALL release() noexcept override { OWeakObject::release(); } + + // XOutputStream + virtual void SAL_CALL writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) override; + + virtual void SAL_CALL flush() override; + + + virtual void SAL_CALL closeOutput() override; +}; + +} // namespace gio + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_provider.cxx b/ucb/source/ucp/gio/gio_provider.cxx new file mode 100644 index 000000000..b6dd31fbb --- /dev/null +++ b/ucb/source/ucp/gio/gio_provider.cxx @@ -0,0 +1,139 @@ +/* -*- 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 <sal/log.hxx> +#include <ucbhelper/contenthelper.hxx> +#include <ucbhelper/macros.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/weak.hxx> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/ucb/IllegalIdentifierException.hpp> +#include "gio_provider.hxx" +#include "gio_content.hxx" + +namespace gio +{ +css::uno::Reference< css::ucb::XContent > SAL_CALL +ContentProvider::queryContent( + const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier ) +{ + SAL_INFO("ucb.ucp.gio", "QueryContent: " << Identifier->getContentIdentifier()); + osl::MutexGuard aGuard( m_aMutex ); + + // Check, if a content with given id already exists... + css::uno::Reference< css::ucb::XContent > xContent = queryExistingContent( Identifier ); + if ( xContent.is() ) + return xContent; + + try + { + xContent = new ::gio::Content(m_xContext, this, Identifier); + } + catch ( css::ucb::ContentCreationException const & ) + { + throw css::ucb::IllegalIdentifierException(); + } + + if ( !xContent->getIdentifier().is() ) + throw css::ucb::IllegalIdentifierException(); + + return xContent; +} + +ContentProvider::ContentProvider( + const css::uno::Reference< css::uno::XComponentContext >& rxContext ) +: ::ucbhelper::ContentProviderImplHelper( rxContext ) +{ +} + +ContentProvider::~ContentProvider() +{ +} + +// XInterface +void SAL_CALL ContentProvider::acquire() + noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL ContentProvider::release() + noexcept +{ + OWeakObject::release(); +} + +css::uno::Any SAL_CALL ContentProvider::queryInterface( const css::uno::Type & rType ) +{ + css::uno::Any aRet = cppu::queryInterface( rType, + static_cast< css::lang::XTypeProvider* >(this), + static_cast< css::lang::XServiceInfo* >(this), + static_cast< css::ucb::XContentProvider* >(this) + ); + return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ); +} + +XTYPEPROVIDER_IMPL_3( ContentProvider, + css::lang::XTypeProvider, + css::lang::XServiceInfo, + css::ucb::XContentProvider ); + +css::uno::Sequence< OUString > SAL_CALL ContentProvider::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.GIOContentProvider" }; +} + +OUString SAL_CALL ContentProvider::getImplementationName() +{ + return "com.sun.star.comp.GIOContentProvider"; +} + +sal_Bool SAL_CALL ContentProvider::supportsService(const OUString& aServiceName) +{ + return cppu::supportsService(this, aServiceName); +} + + +} + +// gio creates threads we don't want in online's forkit +static bool isDisabled() +{ + const char *pDisable = getenv("UNODISABLELIBRARY"); + if (!pDisable) + return false; + OString aDisable(pDisable, strlen(pDisable)); + return aDisable.indexOf("ucpgio1") >= 0; +} + + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ucb_gio_ContentProvider_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + if (isDisabled()) + return nullptr; +#if !GLIB_CHECK_VERSION(2,36,0) + g_type_init(); +#endif + return cppu::acquire(new gio::ContentProvider(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_provider.hxx b/ucb/source/ucp/gio/gio_provider.hxx new file mode 100644 index 000000000..f90d09805 --- /dev/null +++ b/ucb/source/ucp/gio/gio_provider.hxx @@ -0,0 +1,52 @@ +/* -*- 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 . + */ + +#pragma once + +#include <ucbhelper/providerhelper.hxx> + +namespace gio +{ +class ContentProvider : public ::ucbhelper::ContentProviderImplHelper +{ +public: + explicit ContentProvider(const css::uno::Reference<css::uno::XComponentContext>& rxContext); + virtual ~ContentProvider() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type& rType) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider + virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override; + virtual css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // XContentProvider + virtual css::uno::Reference<css::ucb::XContent> SAL_CALL + queryContent(const css::uno::Reference<css::ucb::XContentIdentifier>& Identifier) override; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_resultset.cxx b/ucb/source/ucp/gio/gio_resultset.cxx new file mode 100644 index 000000000..ee836bd0e --- /dev/null +++ b/ucb/source/ucp/gio/gio_resultset.cxx @@ -0,0 +1,53 @@ +/* -*- 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 "gio_datasupplier.hxx" +#include "gio_resultset.hxx" + +using namespace com::sun::star::lang; +using namespace com::sun::star::ucb; +using namespace com::sun::star::uno; + +using namespace gio; + +DynamicResultSet::DynamicResultSet( + const Reference< XComponentContext >& rxContext, + const rtl::Reference< Content >& rxContent, + const OpenCommandArgument2& rCommand, + const Reference< XCommandEnvironment >& rxEnv ) + : ResultSetImplHelper( rxContext, rCommand ), + m_xContent( rxContent ), + m_xEnv( rxEnv ) +{ +} + +void DynamicResultSet::initStatic() +{ + m_xResultSet1 = new ::ucbhelper::ResultSet( + m_xContext, m_aCommand.Properties, + new DataSupplier( m_xContent, m_aCommand.Mode ), m_xEnv ); +} + +void DynamicResultSet::initDynamic() +{ + initStatic(); + m_xResultSet2 = m_xResultSet1; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_resultset.hxx b/ucb/source/ucp/gio/gio_resultset.hxx new file mode 100644 index 000000000..f5ea95e14 --- /dev/null +++ b/ucb/source/ucp/gio/gio_resultset.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include <ucbhelper/resultsethelper.hxx> +#include "gio_content.hxx" + +namespace gio +{ + + class DynamicResultSet : public ::ucbhelper::ResultSetImplHelper + { + rtl::Reference< Content > m_xContent; + css::uno::Reference< css::ucb::XCommandEnvironment > m_xEnv; + + private: + virtual void initStatic() override; + virtual void initDynamic() override; + + public: + DynamicResultSet( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const rtl::Reference< Content >& rxContent, + const css::ucb::OpenCommandArgument2& rCommand, + const css::uno::Reference< css::ucb::XCommandEnvironment >& rxEnv ); + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_seekable.cxx b/ucb/source/ucp/gio/gio_seekable.cxx new file mode 100644 index 000000000..1f1da5948 --- /dev/null +++ b/ucb/source/ucp/gio/gio_seekable.cxx @@ -0,0 +1,126 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/NotConnectedException.hpp> +#include <cppuhelper/queryinterface.hxx> + +#include "gio_seekable.hxx" +#include "gio_content.hxx" + +namespace gio +{ + +Seekable::Seekable(GSeekable *pStream) : mpStream(pStream) +{ + if (!mpStream) + throw css::io::NotConnectedException(); +} + +Seekable::~Seekable() +{ +} + +void SAL_CALL Seekable::truncate() +{ + if (!mpStream) + throw css::io::NotConnectedException(); + + if (!g_seekable_can_truncate(mpStream)) + throw css::io::IOException("Truncate unsupported", + static_cast< cppu::OWeakObject * >(this)); + + GError *pError=nullptr; + if (!g_seekable_truncate(mpStream, 0, nullptr, &pError)) + convertToIOException(pError, static_cast< cppu::OWeakObject * >(this)); +} + +void SAL_CALL Seekable::seek( sal_Int64 location ) +{ + if (!mpStream) + throw css::io::NotConnectedException(); + + if (!g_seekable_can_seek(mpStream)) + throw css::io::IOException("Seek unsupported", + static_cast< cppu::OWeakObject * >(this)); + + GError *pError=nullptr; + if (!g_seekable_seek(mpStream, location, G_SEEK_SET, nullptr, &pError)) + convertToIOException(pError, static_cast< cppu::OWeakObject * >(this)); +} + +sal_Int64 SAL_CALL Seekable::getPosition() +{ + if (!mpStream) + throw css::io::NotConnectedException(); + + return g_seekable_tell(mpStream); +} + +sal_Int64 SAL_CALL Seekable::getLength() +{ + if (!mpStream) + throw css::io::NotConnectedException(); + + bool bOk = false; + sal_uInt64 nSize = 0; + + GFileInfo* pInfo = G_IS_FILE_INPUT_STREAM(mpStream) + ? g_file_input_stream_query_info(G_FILE_INPUT_STREAM(mpStream), G_FILE_ATTRIBUTE_STANDARD_SIZE, nullptr, nullptr) + : g_file_output_stream_query_info(G_FILE_OUTPUT_STREAM(mpStream), G_FILE_ATTRIBUTE_STANDARD_SIZE, nullptr, nullptr); + + if (pInfo) + { + if (g_file_info_has_attribute(pInfo, G_FILE_ATTRIBUTE_STANDARD_SIZE)) + { + nSize = g_file_info_get_size(pInfo); + bOk = true; + } + g_object_unref(pInfo); + } + + if (!bOk) + { + GError *pError=nullptr; + sal_Int64 nCurr = getPosition(); + if (!g_seekable_seek(mpStream, 0, G_SEEK_END, nullptr, &pError)) + convertToIOException(pError, static_cast< cppu::OWeakObject * >(this)); + nSize = getPosition(); + seek(nCurr); + } + + return nSize; +} + +css::uno::Any Seekable::queryInterface( const css::uno::Type &type ) +{ + css::uno::Any aRet = ::cppu::queryInterface ( type, + static_cast< XSeekable * >( this ) ); + + if (!aRet.hasValue() && g_seekable_can_truncate(mpStream)) + aRet = ::cppu::queryInterface ( type, static_cast< XTruncate * >( this ) ); + + return aRet.hasValue() ? aRet : OWeakObject::queryInterface( type ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/gio_seekable.hxx b/ucb/source/ucp/gio/gio_seekable.hxx new file mode 100644 index 000000000..9bf284b89 --- /dev/null +++ b/ucb/source/ucp/gio/gio_seekable.hxx @@ -0,0 +1,60 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <rtl/ustring.hxx> +#include <cppuhelper/weak.hxx> + +#include <com/sun/star/io/XTruncate.hpp> +#include <com/sun/star/io/XSeekable.hpp> + +#include <gio/gio.h> + +namespace gio +{ +class Seekable : public css::io::XTruncate, public css::io::XSeekable, public ::cppu::OWeakObject +{ +private: + GSeekable* mpStream; + +public: + explicit Seekable(GSeekable* pStream); + virtual ~Seekable() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type& type) override; + virtual void SAL_CALL acquire() noexcept override { OWeakObject::acquire(); } + virtual void SAL_CALL release() noexcept override { OWeakObject::release(); } + + // XSeekable + virtual void SAL_CALL seek(sal_Int64 location) override; + + virtual sal_Int64 SAL_CALL getPosition() override; + + virtual sal_Int64 SAL_CALL getLength() override; + + // XTruncate + virtual void SAL_CALL truncate() override; +}; + +} // namespace gio + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/gio/ucpgio.component b/ucb/source/ucp/gio/ucpgio.component new file mode 100644 index 000000000..7d128067a --- /dev/null +++ b/ucb/source/ucp/gio/ucpgio.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.GIOContentProvider" + constructor="ucb_gio_ContentProvider_get_implementation" single-instance="true"> + <service name="com.sun.star.ucb.GIOContentProvider"/> + </implementation> +</component> |