1347 lines
48 KiB
C++
1347 lines
48 KiB
C++
/* -*- 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 >())
|
|
? GIO_FOLDER_TYPE
|
|
: 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, getXWeak(), false);
|
|
}
|
|
|
|
css::uno::Any Content::getBadArgExcept()
|
|
{
|
|
return css::uno::Any( css::lang::IllegalArgumentException(
|
|
u"Wrong argument type!"_ustr,
|
|
getXWeak(), -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;
|
|
// coverity[store_truncates_time_t] - TODO: sal_uInt32 TimeValue::Seconds is only large enough
|
|
// for integer time_t values < 2^32 representing dates until year 2106:
|
|
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 (u"Property is read-only!"_ustr, 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 = getXWeak();
|
|
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( getXWeak() );
|
|
}
|
|
else if ( rValue.Name == "Title" )
|
|
{
|
|
if (!( rValue.Value >>= aNewTitle ))
|
|
{
|
|
aRetRange[ n ] <<= css::beans::IllegalTypeException
|
|
( u"Property value has wrong type!"_ustr,
|
|
getXWeak() );
|
|
continue;
|
|
}
|
|
|
|
if ( aNewTitle.isEmpty() )
|
|
{
|
|
aRetRange[ n ] <<= css::lang::IllegalArgumentException
|
|
( u"Empty title not allowed!"_ustr,
|
|
getXWeak(), -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( getXWeak() );
|
|
}
|
|
}
|
|
|
|
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
|
|
( u"Exchange failed!"_ustr,
|
|
getXWeak() );
|
|
}
|
|
}
|
|
|
|
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, getXWeak());
|
|
|
|
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(), getXWeak(),
|
|
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(), getXWeak(),
|
|
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(), getXWeak(),
|
|
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(),
|
|
getXWeak() ) ),
|
|
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(), getXWeak() ) ),
|
|
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
|
|
{
|
|
{ u"Title"_ustr, -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( u"IsDocument"_ustr,
|
|
-1, cppu::UnoType<bool>::get(),
|
|
css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
|
|
css::beans::Property( u"IsFolder"_ustr,
|
|
-1, cppu::UnoType<bool>::get(),
|
|
css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
|
|
css::beans::Property( u"Title"_ustr,
|
|
-1, cppu::UnoType<OUString>::get(),
|
|
css::beans::PropertyAttribute::BOUND ),
|
|
css::beans::Property( u"IsReadOnly"_ustr,
|
|
-1, cppu::UnoType<bool>::get(),
|
|
css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
|
|
css::beans::Property( u"DateCreated"_ustr,
|
|
-1, cppu::UnoType<css::util::DateTime>::get(),
|
|
css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
|
|
css::beans::Property( u"DateModified"_ustr,
|
|
-1, cppu::UnoType<css::util::DateTime>::get(),
|
|
css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
|
|
css::beans::Property( u"Size"_ustr,
|
|
-1, cppu::UnoType<sal_Int64>::get(),
|
|
css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
|
|
css::beans::Property( u"IsVolume"_ustr,
|
|
1, cppu::UnoType<bool>::get(),
|
|
css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
|
|
css::beans::Property( u"IsCompactDisc"_ustr,
|
|
-1, cppu::UnoType<bool>::get(),
|
|
css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
|
|
css::beans::Property( u"IsRemoveable"_ustr,
|
|
-1, cppu::UnoType<bool>::get(),
|
|
css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
|
|
css::beans::Property( u"IsHidden"_ustr,
|
|
-1, cppu::UnoType<bool>::get(),
|
|
css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
|
|
css::beans::Property( u"CreatableContentsInfo"_ustr,
|
|
-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
|
|
( u"getCommandInfo"_ustr,
|
|
-1, cppu::UnoType<void>::get() ),
|
|
css::ucb::CommandInfo
|
|
( u"getPropertySetInfo"_ustr,
|
|
-1, cppu::UnoType<void>::get() ),
|
|
css::ucb::CommandInfo
|
|
( u"getPropertyValues"_ustr,
|
|
-1, cppu::UnoType<css::uno::Sequence< css::beans::Property >>::get() ),
|
|
css::ucb::CommandInfo
|
|
( u"setPropertyValues"_ustr,
|
|
-1, cppu::UnoType<css::uno::Sequence< css::beans::PropertyValue >>::get() ),
|
|
|
|
// Optional standard commands
|
|
css::ucb::CommandInfo
|
|
( u"delete"_ustr,
|
|
-1, cppu::UnoType<bool>::get() ),
|
|
css::ucb::CommandInfo
|
|
( u"insert"_ustr,
|
|
-1, cppu::UnoType<css::ucb::InsertCommandArgument>::get() ),
|
|
css::ucb::CommandInfo
|
|
( u"open"_ustr,
|
|
-1, cppu::UnoType<css::ucb::OpenCommandArgument2>::get() ),
|
|
|
|
// Folder Only, omitted if not a folder
|
|
css::ucb::CommandInfo
|
|
( u"transfer"_ustr,
|
|
-1, cppu::UnoType<css::ucb::TransferInfo>::get() ),
|
|
css::ucb::CommandInfo
|
|
( u"createNewContent"_ustr,
|
|
-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 u"com.sun.star.comp.GIOContent"_ustr;
|
|
}
|
|
|
|
css::uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
|
|
{
|
|
css::uno::Sequence<OUString> aSNS { u"com.sun.star.ucb.GIOContent"_ustr };
|
|
return aSNS;
|
|
}
|
|
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|