Quelle DocumentMetadataAccess.cxx
Sprache: 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 <sfx2/DocumentMetadataAccess.hxx>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/embed/XStorage.hpp>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/frame/XTransientDocumentsDocumentContentIdentifierFactory.hpp>
#include <com/sun/star/task/ErrorCodeIOException.hpp>
#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
#include <com/sun/star/rdf/FileFormat.hpp>
#include <com/sun/star/rdf/ParseException.hpp>
#include <com/sun/star/rdf/RepositoryException.hpp>
#include <com/sun/star/rdf/URIs.hpp>
#include <com/sun/star/rdf/Statement.hpp>
#include <com/sun/star/rdf/URI.hpp>
#include <com/sun/star/rdf/Repository.hpp>
#include <rtl/ustrbuf.hxx>
#include <rtl/uri.hxx>
#include <rtl/bootstrap.hxx>
#include <sal/log.hxx>
#include <comphelper/interaction.hxx>
#include <unotools/mediadescriptor.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/storagehelper.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <o3tl/string_view.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/XmlIdRegistry.hxx>
#include <sfx2/objsh.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <libxml/tree.h>
#include <utility>
#include <vector>
#include <set>
#include <string_view>
#include <com/sun/star/uri/XUriReference.hpp>
#include <com/sun/star/uri/UriReferenceFactory.hpp>
/*
Note : in the context of this implementation , all rdf . QueryExceptions and
rdf . RepositoryExceptions are RuntimeExceptions , and will be reported as such .
This implementation assumes that it is only used with ODF documents , not mere
ODF packages . In other words , we enforce that metadata files must not be
called reserved names .
*/
using namespace ::com::sun::star;
namespace sfx2 {
bool isValidNCName(std::u16string_view i_rIdref)
{
const OString id(
OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8) );
return !(xmlValidateNCName(
reinterpret_cast <const unsigned char *>(id.getStr()), 0 ));
}
constexpr OUString s_content = u"content.xml" _ustr;
constexpr OUString s_styles = u"styles.xml" _ustr;
constexpr OUString s_manifest = u"manifest.rdf" _ustr;
const char s_odfmime [] = "application/vnd.oasis.opendocument." ;
static bool isContentFile(std::u16string_view i_rPath)
{
return i_rPath == s_content;
}
static bool isStylesFile (std::u16string_view i_rPath)
{
return i_rPath == s_styles;
}
bool isValidXmlId(std::u16string_view i_rStreamName,
std::u16string_view i_rIdref)
{
return isValidNCName(i_rIdref)
&& (isContentFile(i_rStreamName) || isStylesFile(i_rStreamName));
}
static bool isReservedFile(std::u16string_view i_rPath)
{
return isContentFile(i_rPath) || isStylesFile(i_rPath) || i_rPath == u"meta.xml" || i_rPath == u"settings.xml" ;
}
uno::Reference<rdf::XURI> createBaseURI(
uno::Reference<uno::XComponentContext> const & i_xContext,
uno::Reference<frame::XModel> const & i_xModel,
OUString const & i_rPkgURI, std::u16string_view i_rSubDocument)
{
if (!i_xContext.is() || (!i_xModel.is() && i_rPkgURI.isEmpty())) {
throw uno::RuntimeException();
}
OUString pkgURI(i_rPkgURI);
// tdf#123293 chicken/egg problem when loading from stream: there is no URI,
// and also the model doesn't have a storage yet, so we need to get the
// tdoc URI without a storage...
if (pkgURI.isEmpty())
{
assert(i_xModel.is());
uno::Reference<frame::XTransientDocumentsDocumentContentIdentifierFactory>
const xTDDCIF(
i_xContext->getServiceManager()->createInstanceWithContext(
u"com.sun.star.ucb.TransientDocumentsContentProvider" _ustr,
i_xContext),
uno::UNO_QUERY_THROW);
uno::Reference<ucb::XContentIdentifier> const xContentId(
xTDDCIF->createDocumentContentIdentifier(i_xModel));
SAL_WARN_IF(!xContentId.is(), "sfx" , "createBaseURI: cannot create ContentIdentifier" );
if (!xContentId.is())
{
throw uno::RuntimeException(u"createBaseURI: cannot create ContentIdentifier" _ustr);
}
pkgURI = xContentId->getContentIdentifier();
assert(!pkgURI.isEmpty());
if (!pkgURI.isEmpty() && !pkgURI.endsWith("/" ))
{
pkgURI += "/" ;
}
}
// #i108078# workaround non-hierarchical vnd.sun.star.expand URIs
// this really should be done somewhere else, not here.
if (pkgURI.startsWithIgnoreAsciiCase("vnd.sun.star.expand:" , &pkgURI))
{
// expand it here (makeAbsolute requires hierarchical URI)
if (!pkgURI.isEmpty()) {
pkgURI = ::rtl::Uri::decode(
pkgURI, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8);
if (pkgURI.isEmpty()) {
throw uno::RuntimeException();
}
::rtl::Bootstrap::expandMacros(pkgURI);
}
}
const uno::Reference<uri::XUriReferenceFactory> xUriFactory =
uri::UriReferenceFactory::create( i_xContext);
uno::Reference< uri::XUriReference > xBaseURI;
const uno::Reference< uri::XUriReference > xPkgURI(
xUriFactory->parse(pkgURI), uno::UNO_SET_THROW );
xPkgURI->clearFragment();
// need to know whether the storage is a FileSystemStorage
// XServiceInfo would be better, but it is not implemented
// if ( pkgURI.getLength() && ::utl::UCBContentHelper::IsFolder(pkgURI) )
if (true ) {
xBaseURI.set( xPkgURI, uno::UNO_SET_THROW );
}
OUStringBuffer buf(64 );
if (!xBaseURI->getUriReference().endsWith("/" ))
{
const sal_Int32 count( xBaseURI->getPathSegmentCount() );
if (count > 0 )
{
buf.append(xBaseURI->getPathSegment(count - 1 ));
}
buf.append('/' );
}
if (!i_rSubDocument.empty())
{
buf.append(OUString::Concat(i_rSubDocument) + "/" );
}
if (!buf.isEmpty())
{
const uno::Reference< uri::XUriReference > xPathURI(
xUriFactory->parse(buf.makeStringAndClear()), uno::UNO_SET_THROW );
xBaseURI.set(
xUriFactory->makeAbsolute(xBaseURI, xPathURI,
true , uri::RelativeUriExcessParentSegments_ERROR),
uno::UNO_SET_THROW);
}
return rdf::URI::create(i_xContext, xBaseURI->getUriReference());
}
struct DocumentMetadataAccess_Impl
{
// note: these are all initialized in constructor, and loadFromStorage
const uno::Reference<uno::XComponentContext> m_xContext;
const SfxObjectShell & m_rXmlIdRegistrySupplier;
uno::Reference<rdf::XURI> m_xBaseURI;
uno::Reference<rdf::XRepository> m_xRepository;
uno::Reference<rdf::XNamedGraph> m_xManifest;
DocumentMetadataAccess_Impl(
uno::Reference<uno::XComponentContext> i_xContext,
SfxObjectShell const & i_rRegistrySupplier)
: m_xContext(std::move(i_xContext))
, m_rXmlIdRegistrySupplier(i_rRegistrySupplier)
{
OSL_ENSURE(m_xContext.is(), "context null" );
}
};
// this is... a hack.
template <sal_Int16 Constant>
static uno::Reference<rdf::XURI> const &
getURI(uno::Reference< uno::XComponentContext > const & i_xContext)
{
static uno::Reference< rdf::XURI > xURI(
rdf::URI::createKnown(i_xContext, Constant), uno::UNO_SET_THROW);
return xURI;
}
/** would storing the file to a XStorage succeed? */
static bool isFileNameValid(std::u16string_view i_rFileName)
{
if (i_rFileName.empty()) return false ;
if (i_rFileName[0 ] == '/' ) return false ; // no absolute paths!
sal_Int32 idx(0 );
do {
const OUString segment(
o3tl::getToken(i_rFileName, 0 , u'/' , idx) );
if (segment.isEmpty() || // no empty segments
segment == "." || // no . segments
segment == ".." || // no .. segments
!::comphelper::OStorageHelper::IsValidZipEntryFileName(
segment, false )) // no invalid characters
return false ;
} while (idx >= 0 );
return true ;
}
/** split a uri hierarchy into first segment and rest */
static bool
splitPath(OUString const & i_rPath,
OUString & o_rDir, OUString& o_rRest)
{
const sal_Int32 idx(i_rPath.indexOf(u'/' ));
if (idx < 0 || idx >= i_rPath.getLength()) {
o_rDir.clear();
o_rRest = i_rPath;
return true ;
} else if (idx == 0 || idx == i_rPath.getLength() - 1 ) {
// input must not start or end with '/'
return false ;
} else {
o_rDir = i_rPath.copy(0 , idx);
o_rRest = i_rPath.copy(idx+1 );
return true ;
}
}
static bool
splitXmlId(std::u16string_view i_XmlId,
OUString & o_StreamName, OUString& o_Idref )
{
const size_t idx(i_XmlId.find(u'#' ));
if (idx == std::u16string_view::npos)
return false ;
o_StreamName = i_XmlId.substr(0 , idx);
o_Idref = i_XmlId.substr(idx+1 );
return isValidXmlId(o_StreamName, o_Idref);
}
static uno::Reference<rdf::XURI>
getURIForStream(struct DocumentMetadataAccess_Impl const & i_rImpl,
OUString const & i_rPath)
{
const uno::Reference<rdf::XURI> xURI(
rdf::URI::createNS( i_rImpl.m_xContext,
i_rImpl.m_xBaseURI->getStringValue(), i_rPath),
uno::UNO_SET_THROW);
return xURI;
}
/** add statements declaring i_xResource to be a file of type i_xType with
path i_rPath to manifest, with optional additional types i_pTypes */
static void
addFile(struct DocumentMetadataAccess_Impl const & i_rImpl,
uno::Reference<rdf::XURI> const & i_xType,
OUString const & i_rPath,
const uno::Sequence < uno::Reference< rdf::XURI > > * i_pTypes)
{
try {
const uno::Reference<rdf::XURI> xURI( getURIForStream(
i_rImpl, i_rPath) );
i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
xURI);
i_rImpl.m_xManifest->addStatement(xURI,
getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
i_xType);
if (i_pTypes) {
for (const auto & rType : *i_pTypes) {
i_rImpl.m_xManifest->addStatement(xURI,
getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
rType);
}
}
} catch (const uno::RuntimeException &) {
throw ;
} catch (const uno::Exception &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"addFile: exception" _ustr, /*this*/nullptr, anyEx);
}
}
/** add content.xml or styles.xml to manifest */
static bool
addContentOrStylesFileImpl(struct DocumentMetadataAccess_Impl const & i_rImpl,
const OUString & i_rPath)
{
uno::Reference<rdf::XURI> xType;
if (isContentFile(i_rPath)) {
xType.set(getURI<rdf::URIs::ODF_CONTENTFILE>(i_rImpl.m_xContext));
} else if (isStylesFile(i_rPath)) {
xType.set(getURI<rdf::URIs::ODF_STYLESFILE>(i_rImpl.m_xContext));
} else {
return false ;
}
addFile(i_rImpl, xType, i_rPath, nullptr);
return true ;
}
/** add metadata file to manifest */
static void
addMetadataFileImpl(struct DocumentMetadataAccess_Impl const & i_rImpl,
const OUString & i_rPath,
const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
{
addFile(i_rImpl,
getURI<rdf::URIs::PKG_METADATAFILE>(i_rImpl.m_xContext),
i_rPath, &i_rTypes);
}
/** remove a file from the manifest */
static void
removeFile(struct DocumentMetadataAccess_Impl const & i_rImpl,
uno::Reference<rdf::XURI> const & i_xPart)
{
if (!i_xPart.is()) throw uno::RuntimeException();
try {
i_rImpl.m_xManifest->removeStatements(i_rImpl.m_xBaseURI,
getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
i_xPart);
i_rImpl.m_xManifest->removeStatements(i_xPart,
getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), nullptr);
} catch (const uno::RuntimeException &) {
throw ;
} catch (const uno::Exception &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"removeFile: exception" _ustr,
nullptr, anyEx);
}
}
static ::std::vector< uno::Reference< rdf::XURI > >
getAllParts(struct DocumentMetadataAccess_Impl const & i_rImpl)
{
::std::vector< uno::Reference< rdf::XURI > > ret;
try {
const uno::Reference<container::XEnumeration> xEnum(
i_rImpl.m_xManifest->getStatements( i_rImpl.m_xBaseURI,
getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), nullptr),
uno::UNO_SET_THROW);
while (xEnum->hasMoreElements()) {
rdf::Statement stmt;
if (!(xEnum->nextElement() >>= stmt)) {
throw uno::RuntimeException();
}
const uno::Reference<rdf::XURI> xPart(stmt.Object,
uno::UNO_QUERY);
if (!xPart.is()) continue ;
ret.push_back(xPart);
}
return ret;
} catch (const uno::RuntimeException &) {
throw ;
} catch (const uno::Exception &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"getAllParts: exception" _ustr,
nullptr, anyEx);
}
}
static bool
isPartOfType(struct DocumentMetadataAccess_Impl const & i_rImpl,
uno::Reference<rdf::XURI> const & i_xPart,
uno::Reference<rdf::XURI> const & i_xType)
{
if (!i_xPart.is() || !i_xType.is()) throw uno::RuntimeException();
try {
const uno::Reference<container::XEnumeration> xEnum(
i_rImpl.m_xManifest->getStatements(i_xPart,
getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
i_xType),
uno::UNO_SET_THROW);
return xEnum->hasMoreElements();
} catch (const uno::RuntimeException &) {
throw ;
} catch (const uno::Exception &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"isPartOfType: exception" _ustr,
nullptr, anyEx);
}
}
static ::std::vector<uno::Reference<rdf::XURI>>
getAllParts(struct DocumentMetadataAccess_Impl const & i_rImpl,
const uno::Reference<rdf::XURI>& i_xType)
{
::std::vector<uno::Reference<rdf::XURI>> ret;
try
{
const uno::Reference<container::XEnumeration> xEnum(
i_rImpl.m_xManifest->getStatements(i_rImpl.m_xBaseURI,
getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
nullptr),
uno::UNO_SET_THROW);
while (xEnum->hasMoreElements())
{
rdf::Statement stmt;
if (!(xEnum->nextElement() >>= stmt))
{
throw uno::RuntimeException();
}
const uno::Reference<rdf::XURI> xPart(stmt.Object, uno::UNO_QUERY);
if (!xPart.is())
continue ;
const uno::Reference<container::XEnumeration> xEnum2(
i_rImpl.m_xManifest->getStatements(
xPart, getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), i_xType),
uno::UNO_SET_THROW);
if (xEnum2->hasMoreElements())
ret.emplace_back(xPart);
}
return ret;
}
catch (const uno::RuntimeException&)
{
throw ;
}
catch (const uno::Exception& e)
{
throw lang::WrappedTargetRuntimeException(u"getAllParts: exception" _ustr, nullptr,
uno::Any(e));
}
}
static ucb::InteractiveAugmentedIOException
mkException( OUString const & i_rMessage,
ucb::IOErrorCode const i_ErrorCode,
OUString const & i_rUri, OUString const & i_rResource)
{
const beans::PropertyValue uriProp(u"Uri" _ustr,
-1 , uno::Any(i_rUri), static_cast <beans::PropertyState>(0 ));
const beans::PropertyValue rnProp(
u"ResourceName" _ustr,
-1 , uno::Any(i_rResource), static_cast <beans::PropertyState>(0 ));
return ucb::InteractiveAugmentedIOException(i_rMessage, {},
task::InteractionClassification_ERROR, i_ErrorCode,
{ uno::Any(uriProp), uno::Any(rnProp) });
}
/** error handling policy.
< p > If a handler is given , ask it how to proceed :
< ul > < li > ( default : ) cancel import , raise exception < / li >
< li > ignore the error and continue < / li >
< li > retry the action that led to the error < / li > < / ul > < / p >
N . B . : must not be called before DMA is fully initialized !
@ returns true iff caller should retry
*/
static bool
handleError( ucb::InteractiveAugmentedIOException const & i_rException,
const uno::Reference<task::XInteractionHandler> & i_xHandler)
{
if (!i_xHandler.is()) {
throw lang::WrappedTargetException(
u"DocumentMetadataAccess::loadMetadataFromStorage: exception" _ustr,
/* *this*/ nullptr, uno::Any(i_rException));
}
::rtl::Reference< ::comphelper::OInteractionRequest > pRequest(
new ::comphelper::OInteractionRequest(uno::Any(i_rException)) );
::rtl::Reference< ::comphelper::OInteractionRetry > pRetry(
new ::comphelper::OInteractionRetry );
::rtl::Reference< ::comphelper::OInteractionApprove > pApprove(
new ::comphelper::OInteractionApprove );
::rtl::Reference< ::comphelper::OInteractionAbort > pAbort(
new ::comphelper::OInteractionAbort );
pRequest->addContinuation( pApprove );
pRequest->addContinuation( pAbort );
// actually call the handler
i_xHandler->handle( pRequest );
if (pRetry->wasSelected()) {
return true ;
} else if (pApprove->wasSelected()) {
return false ;
} else {
OSL_ENSURE(pAbort->wasSelected(), "no continuation selected?" );
throw lang::WrappedTargetException(
u"DocumentMetadataAccess::loadMetadataFromStorage: exception" _ustr,
/* *this*/ nullptr, uno::Any(i_rException));
}
}
/** check if storage has content.xml/styles.xml;
e.g. ODB files seem to only have content.xml */
static void
collectFilesFromStorage(uno::Reference<embed::XStorage> const & i_xStorage,
std::set< OUString > & o_rFiles)
{
try {
if (i_xStorage->hasByName(s_content) &&
i_xStorage->isStreamElement(s_content))
{
o_rFiles.insert(s_content);
}
if (i_xStorage->hasByName(s_styles) &&
i_xStorage->isStreamElement(s_styles))
{
o_rFiles.insert(s_styles);
}
} catch (const uno::Exception &) {
TOOLS_WARN_EXCEPTION("sfx" , "collectFilesFromStorage" );
}
}
/** import a metadata file into repository */
static void
readStream(struct DocumentMetadataAccess_Impl & i_rImpl,
uno::Reference< embed::XStorage > const & i_xStorage,
OUString const & i_rPath,
OUString const & i_rBaseURI)
{
try {
OUString dir;
OUString rest;
if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
if (dir.isEmpty()) {
if (!i_xStorage->isStreamElement(i_rPath)) {
throw mkException(
u"readStream: is not a stream" _ustr,
ucb::IOErrorCode_NO_FILE, i_rBaseURI + i_rPath, i_rPath);
}
const uno::Reference<io::XStream> xStream(
i_xStorage->openStreamElement(i_rPath,
embed::ElementModes::READ), uno::UNO_SET_THROW);
const uno::Reference<io::XInputStream> xInStream(
xStream->getInputStream(), uno::UNO_SET_THROW );
const uno::Reference<rdf::XURI> xBaseURI(
rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
const uno::Reference<rdf::XURI> xURI(
rdf::URI::createNS(i_rImpl.m_xContext,
i_rBaseURI, i_rPath));
i_rImpl.m_xRepository->importGraph(rdf::FileFormat::RDF_XML,
xInStream, xURI, xBaseURI);
} else {
if (!i_xStorage->isStorageElement(dir)) {
throw mkException(
u"readStream: is not a directory" _ustr,
ucb::IOErrorCode_NO_DIRECTORY, i_rBaseURI + dir, dir);
}
const uno::Reference<embed::XStorage> xDir(
i_xStorage->openStorageElement(dir,
embed::ElementModes::READ));
const uno::Reference< beans::XPropertySet > xDirProps(xDir,
uno::UNO_QUERY_THROW);
try {
OUString mimeType;
xDirProps->getPropertyValue(
utl::MediaDescriptor::PROP_MEDIATYPE )
>>= mimeType;
if (mimeType.startsWith(s_odfmime)) {
SAL_WARN("sfx" , "readStream: refusing to recurse into embedded document" );
return ;
}
} catch (const uno::Exception &) { }
readStream(i_rImpl, xDir, rest, i_rBaseURI+dir+"/" );
}
} catch (const container::NoSuchElementException & e) {
throw mkException(e.Message, ucb::IOErrorCode_NOT_EXISTING_PATH,
i_rBaseURI + i_rPath, i_rPath);
} catch (const io::IOException & e) {
throw mkException(e.Message, ucb::IOErrorCode_CANT_READ,
i_rBaseURI + i_rPath, i_rPath);
} catch (const rdf::ParseException & e) {
throw mkException(e.Message, ucb::IOErrorCode_WRONG_FORMAT,
i_rBaseURI + i_rPath, i_rPath);
}
}
/** import a metadata file into repository */
static void
importFile(struct DocumentMetadataAccess_Impl & i_rImpl,
uno::Reference<embed::XStorage> const & i_xStorage,
OUString const & i_rBaseURI,
uno::Reference<task::XInteractionHandler> const & i_xHandler,
const OUString& i_rPath)
{
retry:
try {
readStream(i_rImpl, i_xStorage, i_rPath, i_rBaseURI);
} catch (const ucb::InteractiveAugmentedIOException & e) {
if (handleError(e, i_xHandler)) goto retry;
} catch (const uno::RuntimeException &) {
throw ;
} catch (const uno::Exception &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"importFile: exception" _ustr,
nullptr, anyEx);
}
}
/** actually write a metadata file to the storage */
static void
exportStream(struct DocumentMetadataAccess_Impl const & i_rImpl,
uno::Reference< embed::XStorage > const & i_xStorage,
uno::Reference<rdf::XURI> const & i_xGraphName,
OUString const & i_rFileName,
OUString const & i_rBaseURI)
{
const uno::Reference<io::XStream> xStream(
i_xStorage->openStreamElement(i_rFileName,
embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE),
uno::UNO_SET_THROW);
const uno::Reference< beans::XPropertySet > xStreamProps(xStream,
uno::UNO_QUERY);
if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage
xStreamProps->setPropertyValue(
u"MediaType" _ustr,
uno::Any(u"application/rdf+xml" _ustr));
}
const uno::Reference<io::XOutputStream> xOutStream(
xStream->getOutputStream(), uno::UNO_SET_THROW );
const uno::Reference<rdf::XURI> xBaseURI(
rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
i_rImpl.m_xRepository->exportGraph(rdf::FileFormat::RDF_XML,
xOutStream, i_xGraphName, xBaseURI);
}
/** write a metadata file to the storage */
static void
writeStream(struct DocumentMetadataAccess_Impl & i_rImpl,
uno::Reference< embed::XStorage > const & i_xStorage,
uno::Reference<rdf::XURI> const & i_xGraphName,
OUString const & i_rPath,
OUString const & i_rBaseURI)
{
OUString dir;
OUString rest;
if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
try {
if (dir.isEmpty()) {
exportStream(i_rImpl, i_xStorage, i_xGraphName, i_rPath,
i_rBaseURI);
} else {
const uno::Reference<embed::XStorage> xDir(
i_xStorage->openStorageElement(dir,
embed::ElementModes::WRITE));
const uno::Reference< beans::XPropertySet > xDirProps(xDir,
uno::UNO_QUERY_THROW);
try {
OUString mimeType;
xDirProps->getPropertyValue(
utl::MediaDescriptor::PROP_MEDIATYPE )
>>= mimeType;
if (mimeType.startsWith(s_odfmime)) {
SAL_WARN("sfx" , "writeStream: refusing to recurse into embedded document" );
return ;
}
} catch (const uno::Exception &) { }
writeStream(i_rImpl, xDir, i_xGraphName, rest, i_rBaseURI+dir+"/" );
uno::Reference<embed::XTransactedObject> const xTransaction(
xDir, uno::UNO_QUERY);
if (xTransaction.is()) {
xTransaction->commit();
}
}
} catch (const uno::RuntimeException &) {
throw ;
} catch (const io::IOException &) {
throw ;
}
}
static void
initLoading(struct DocumentMetadataAccess_Impl & i_rImpl,
const uno::Reference< embed::XStorage > & i_xStorage,
const uno::Reference<rdf::XURI> & i_xBaseURI,
const uno::Reference<task::XInteractionHandler> & i_xHandler)
{
retry:
// clear old data
i_rImpl.m_xManifest.clear();
// init BaseURI
i_rImpl.m_xBaseURI = i_xBaseURI;
// create repository
i_rImpl.m_xRepository.clear();
i_rImpl.m_xRepository.set(rdf::Repository::create(i_rImpl.m_xContext),
uno::UNO_SET_THROW);
// try to delay raising errors until after initialization is done
uno::Any rterr;
ucb::InteractiveAugmentedIOException iaioe;
bool err(false );
const uno::Reference <rdf::XURI> xManifest(
getURIForStream(i_rImpl, s_manifest));
try {
readStream(i_rImpl, i_xStorage, s_manifest, i_xBaseURI->getStringValue());
} catch (const ucb::InteractiveAugmentedIOException & e) {
// no manifest.rdf: this is not an error in ODF < 1.2
if (ucb::IOErrorCode_NOT_EXISTING_PATH != e.Code) {
iaioe = e;
err = true ;
}
} catch (const uno::Exception & e) {
rterr <<= e;
}
// init manifest graph
const uno::Reference<rdf::XNamedGraph> xManifestGraph(
i_rImpl.m_xRepository->getGraph(xManifest));
i_rImpl.m_xManifest.set(xManifestGraph.is() ? xManifestGraph :
i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_SET_THROW);
// document statement
i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext));
OSL_ENSURE(i_rImpl.m_xBaseURI.is(), "base URI is null" );
OSL_ENSURE(i_rImpl.m_xRepository.is(), "repository is null" );
OSL_ENSURE(i_rImpl.m_xManifest.is(), "manifest is null" );
if (rterr.hasValue()) {
throw lang::WrappedTargetRuntimeException(
u"DocumentMetadataAccess::loadMetadataFromStorage: "
"exception" _ustr, nullptr, rterr);
}
if (err && handleError(iaioe, i_xHandler))
goto retry;
}
/** init Impl struct */
static void init(struct DocumentMetadataAccess_Impl & i_rImpl)
{
try {
i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph(
getURIForStream(i_rImpl, s_manifest)),
uno::UNO_SET_THROW);
// insert the document statement
i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext));
} catch (const uno::Exception &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"init: unexpected exception" _ustr, nullptr,
anyEx);
}
// add top-level content files
if (!addContentOrStylesFileImpl(i_rImpl, s_content)) {
throw uno::RuntimeException();
}
if (!addContentOrStylesFileImpl(i_rImpl, s_styles)) {
throw uno::RuntimeException();
}
}
DocumentMetadataAccess::DocumentMetadataAccess(
uno::Reference< uno::XComponentContext > const & i_xContext,
const SfxObjectShell & i_rRegistrySupplier)
: m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
{
// no initialization: must call loadFrom...
}
DocumentMetadataAccess::DocumentMetadataAccess(
uno::Reference< uno::XComponentContext > const & i_xContext,
const SfxObjectShell & i_rRegistrySupplier,
OUString const & i_rURI)
: m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
{
OSL_ENSURE(!i_rURI.isEmpty(), "DMA::DMA: no URI given!" );
OSL_ENSURE(i_rURI.endsWith("/" ), "DMA::DMA: URI without / given!" );
if (!i_rURI.endsWith("/" )) throw uno::RuntimeException();
m_pImpl->m_xBaseURI.set(rdf::URI::create(m_pImpl->m_xContext, i_rURI));
m_pImpl->m_xRepository.set(rdf::Repository::create(m_pImpl->m_xContext),
uno::UNO_SET_THROW);
// init repository
init(*m_pImpl);
OSL_ENSURE(m_pImpl->m_xBaseURI.is(), "base URI is null" );
OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository is null" );
OSL_ENSURE(m_pImpl->m_xManifest.is(), "manifest is null" );
}
DocumentMetadataAccess::~DocumentMetadataAccess()
{
}
// css::rdf::XRepositorySupplier:
uno::Reference< rdf::XRepository > SAL_CALL
DocumentMetadataAccess::getRDFRepository()
{
OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository not initialized" );
return m_pImpl->m_xRepository;
}
// css::rdf::XNode:
OUString SAL_CALL
DocumentMetadataAccess::getStringValue()
{
return m_pImpl->m_xBaseURI->getStringValue();
}
// css::rdf::XURI:
OUString SAL_CALL
DocumentMetadataAccess::getNamespace()
{
return m_pImpl->m_xBaseURI->getNamespace();
}
OUString SAL_CALL
DocumentMetadataAccess::getLocalName()
{
return m_pImpl->m_xBaseURI->getLocalName();
}
// css::rdf::XDocumentMetadataAccess:
uno::Reference< rdf::XMetadatable > SAL_CALL
DocumentMetadataAccess::getElementByMetadataReference(
const css::beans::StringPair & i_rReference)
{
const IXmlIdRegistry * pReg(
m_pImpl->m_rXmlIdRegistrySupplier.GetXmlIdRegistry() );
if (!pReg) {
throw uno::RuntimeException(
u"DocumentMetadataAccess::getElementByXmlId: no registry" _ustr, *this );
}
return pReg->GetElementByMetadataReference(i_rReference);
}
uno::Reference< rdf::XMetadatable > SAL_CALL
DocumentMetadataAccess::getElementByURI(
const uno::Reference< rdf::XURI > & i_xURI )
{
if (!i_xURI.is()) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::getElementByURI: URI is null" _ustr, *this , 0 );
}
const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
const OUString name( i_xURI->getStringValue() );
if (!name.match(baseURI)) {
return nullptr;
}
OUString path;
OUString idref;
if (!splitXmlId(name.subView(baseURI.getLength()), path, idref)) {
return nullptr;
}
return getElementByMetadataReference( beans::StringPair(path, idref) );
}
uno::Sequence<uno::Reference<rdf::XURI>> SAL_CALL
DocumentMetadataAccess::getMetadataGraphsWithType(const uno::Reference<rdf::XURI>& i_xType)
{
if (!i_xType.is())
{
throw lang::IllegalArgumentException(u"DocumentMetadataAccess::getMetadataGraphsWithType: "
"type is null" _ustr,
*this , 0 );
}
return ::comphelper::containerToSequence(getAllParts(*m_pImpl, i_xType));
}
uno::Reference<rdf::XURI> SAL_CALL
DocumentMetadataAccess::addMetadataFile(const OUString & i_rFileName,
const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
{
if (!isFileNameValid(i_rFileName)) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::addMetadataFile: invalid FileName" _ustr,
*this , 0 );
}
if (isReservedFile(i_rFileName)) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::addMetadataFile:"
"invalid FileName: reserved" _ustr, *this , 0 );
}
if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
[](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::addMetadataFile: "
"null type" _ustr, *this , 2 );
}
const uno::Reference<rdf::XURI> xGraphName(
getURIForStream(*m_pImpl, i_rFileName) );
try {
m_pImpl->m_xRepository->createGraph(xGraphName);
} catch (const rdf::RepositoryException &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"DocumentMetadataAccess::addMetadataFile: exception" _ustr,
*this , anyEx);
// note: all other exceptions are propagated
}
addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
return xGraphName;
}
uno::Reference<rdf::XURI> SAL_CALL
DocumentMetadataAccess::importMetadataFile(::sal_Int16 i_Format,
const uno::Reference< io::XInputStream > & i_xInStream,
const OUString & i_rFileName,
const uno::Reference< rdf::XURI > & i_xBaseURI,
const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
{
if (!isFileNameValid(i_rFileName)) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::importMetadataFile: invalid FileName" _ustr,
*this , 0 );
}
if (isReservedFile(i_rFileName)) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::importMetadataFile:"
"invalid FileName: reserved" _ustr, *this , 0 );
}
if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
[](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::importMetadataFile: null type" _ustr,
*this , 5 );
}
const uno::Reference<rdf::XURI> xGraphName(
getURIForStream(*m_pImpl, i_rFileName) );
try {
m_pImpl->m_xRepository->importGraph(
i_Format, i_xInStream, xGraphName, i_xBaseURI);
} catch (const rdf::RepositoryException &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"DocumentMetadataAccess::importMetadataFile: "
"RepositoryException" _ustr, *this , anyEx);
// note: all other exceptions are propagated
}
// add to manifest
addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
return xGraphName;
}
void SAL_CALL
DocumentMetadataAccess::removeMetadataFile(
const uno::Reference< rdf::XURI > & i_xGraphName)
{
try {
m_pImpl->m_xRepository->destroyGraph(i_xGraphName);
} catch (const rdf::RepositoryException &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"DocumentMetadataAccess::removeMetadataFile: "
"RepositoryException" _ustr, *this , anyEx);
// note: all other exceptions are propagated
}
// remove file from manifest
removeFile(*m_pImpl, i_xGraphName);
}
void SAL_CALL
DocumentMetadataAccess::addContentOrStylesFile(
const OUString & i_rFileName)
{
if (!isFileNameValid(i_rFileName)) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::addContentOrStylesFile: "
"invalid FileName" _ustr, *this , 0 );
}
if (!addContentOrStylesFileImpl(*m_pImpl, i_rFileName)) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::addContentOrStylesFile: "
"invalid FileName: must end with content.xml or styles.xml" _ustr,
*this , 0 );
}
}
void SAL_CALL
DocumentMetadataAccess::removeContentOrStylesFile(
const OUString & i_rFileName)
{
if (!isFileNameValid(i_rFileName)) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::removeContentOrStylesFile: "
"invalid FileName" _ustr, *this , 0 );
}
try {
const uno::Reference<rdf::XURI> xPart(
getURIForStream(*m_pImpl, i_rFileName) );
const uno::Reference<container::XEnumeration> xEnum(
m_pImpl->m_xManifest->getStatements( m_pImpl->m_xBaseURI,
getURI<rdf::URIs::PKG_HASPART>(m_pImpl->m_xContext),
xPart),
uno::UNO_SET_THROW);
if (!xEnum->hasMoreElements()) {
throw container::NoSuchElementException(
"DocumentMetadataAccess::removeContentOrStylesFile: "
"cannot find stream in manifest graph: " + i_rFileName,
*this );
}
// remove file from manifest
removeFile(*m_pImpl, xPart);
} catch (const uno::RuntimeException &) {
throw ;
} catch (const uno::Exception &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"DocumentMetadataAccess::removeContentOrStylesFile: exception" _ustr,
*this , anyEx);
}
}
void SAL_CALL DocumentMetadataAccess::loadMetadataFromStorage(
const uno::Reference< embed::XStorage > & i_xStorage,
const uno::Reference<rdf::XURI> & i_xBaseURI,
const uno::Reference<task::XInteractionHandler> & i_xHandler)
{
if (!i_xStorage.is()) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::loadMetadataFromStorage: "
"storage is null" _ustr, *this , 0 );
}
if (!i_xBaseURI.is()) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::loadMetadataFromStorage: "
"base URI is null" _ustr, *this , 1 );
}
const OUString baseURI( i_xBaseURI->getStringValue());
if (baseURI.indexOf('#' ) >= 0 ) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::loadMetadataFromStorage: "
"base URI not absolute" _ustr, *this , 1 );
}
if (!baseURI.endsWith("/" )) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::loadMetadataFromStorage: "
"base URI does not end with slash" _ustr, *this , 1 );
}
initLoading(*m_pImpl, i_xStorage, i_xBaseURI, i_xHandler);
std::set< OUString > StgFiles;
collectFilesFromStorage(i_xStorage, StgFiles);
std::vector< OUString > MfstMetadataFiles;
try {
const ::std::vector< uno::Reference< rdf::XURI > > parts(
getAllParts(*m_pImpl) );
const uno::Reference<rdf::XURI>& xContentFile(
getURI<rdf::URIs::ODF_CONTENTFILE>(m_pImpl->m_xContext));
const uno::Reference<rdf::XURI>& xStylesFile(
getURI<rdf::URIs::ODF_STYLESFILE>(m_pImpl->m_xContext));
const uno::Reference<rdf::XURI>& xMetadataFile(
getURI<rdf::URIs::PKG_METADATAFILE>(m_pImpl->m_xContext));
const sal_Int32 len( baseURI.getLength() );
for (const auto & rxPart : parts) {
const OUString name(rxPart->getStringValue());
if (!name.match(baseURI)) {
SAL_WARN("sfx" , "loadMetadataFromStorage: graph not in document: " << name);
continue ;
}
const OUString relName( name.copy(len) );
if (relName == s_manifest) {
SAL_WARN("sfx" , "loadMetadataFromStorage: found ourselves a recursive manifest!" );
continue ;
}
// remove found items from StgFiles
StgFiles.erase(relName);
if (isContentFile(relName)) {
if (!isPartOfType(*m_pImpl, rxPart, xContentFile)) {
const uno::Reference <rdf::XURI> xName(
getURIForStream(*m_pImpl, relName) );
// add missing type statement
m_pImpl->m_xManifest->addStatement(xName,
getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
xContentFile);
}
} else if (isStylesFile(relName)) {
if (!isPartOfType(*m_pImpl, rxPart, xStylesFile)) {
const uno::Reference <rdf::XURI> xName(
getURIForStream(*m_pImpl, relName) );
// add missing type statement
m_pImpl->m_xManifest->addStatement(xName,
getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
xStylesFile);
}
} else if (isReservedFile(relName)) {
SAL_WARN("sfx" , "loadMetadataFromStorage: reserved file name in manifest" );
} else {
if (isPartOfType(*m_pImpl, rxPart, xMetadataFile)) {
MfstMetadataFiles.push_back(relName);
}
// do not add statement for MetadataFile; it could be
// something else! just ignore it...
}
}
} catch (const uno::RuntimeException &) {
throw ;
} catch (const uno::Exception &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"DocumentMetadataAccess::loadMetadataFromStorage: "
"exception" _ustr, *this , anyEx);
}
for (const auto & aStgFile : StgFiles)
addContentOrStylesFileImpl(*m_pImpl, aStgFile);
for (const auto & aMfstMetadataFile : MfstMetadataFiles)
importFile(*m_pImpl, i_xStorage, baseURI, i_xHandler, aMfstMetadataFile);
}
void SAL_CALL DocumentMetadataAccess::storeMetadataToStorage(
const uno::Reference< embed::XStorage > & i_xStorage)
{
if (!i_xStorage.is()) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::storeMetadataToStorage: "
"storage is null" _ustr, *this , 0 );
}
// export manifest
const uno::Reference <rdf::XURI> xManifest(
getURIForStream(*m_pImpl, s_manifest) );
const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
try {
writeStream(*m_pImpl, i_xStorage, xManifest, s_manifest, baseURI);
} catch (const uno::RuntimeException &) {
throw ;
} catch (const io::IOException &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetException(
u"storeMetadataToStorage: IO exception" _ustr, *this , anyEx);
} catch (const uno::Exception &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"storeMetadataToStorage: exception" _ustr, *this , anyEx);
}
// export metadata streams
try {
const uno::Sequence<uno::Reference<rdf::XURI> > graphs(
m_pImpl->m_xRepository->getGraphNames());
const sal_Int32 len( baseURI.getLength() );
for (const uno::Reference<rdf::XURI>& xName : graphs) {
const OUString name(xName->getStringValue());
if (!name.match(baseURI)) {
SAL_WARN("sfx" , "storeMetadataToStorage: graph not in document: " << name);
continue ;
}
const OUString relName( name.copy(len) );
if (relName == s_manifest) {
continue ;
}
if (!isFileNameValid(relName) || isReservedFile(relName)) {
SAL_WARN("sfx" , "storeMetadataToStorage: invalid file name: " << relName);
continue ;
}
try {
writeStream(*m_pImpl, i_xStorage, xName, relName, baseURI);
} catch (const uno::RuntimeException &) {
throw ;
} catch (const io::IOException &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetException(
u"storeMetadataToStorage: IO exception" _ustr,
*this , anyEx);
} catch (const uno::Exception &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"storeMetadataToStorage: exception" _ustr,
*this , anyEx);
}
}
} catch (const rdf::RepositoryException &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetRuntimeException(
u"storeMetadataToStorage: exception" _ustr, *this , anyEx);
}
}
void SAL_CALL
DocumentMetadataAccess::loadMetadataFromMedium(
const uno::Sequence< beans::PropertyValue > & i_rMedium)
{
uno::Reference<io::XInputStream> xIn;
utl::MediaDescriptor md(i_rMedium);
OUString URL;
md[ utl::MediaDescriptor::PROP_URL ] >>= URL;
OUString BaseURL;
md[ utl::MediaDescriptor::PROP_DOCUMENTBASEURL ] >>= BaseURL;
if (md.addInputStream()) {
md[ utl::MediaDescriptor::PROP_INPUTSTREAM ] >>= xIn;
}
if (!xIn.is() && URL.isEmpty()) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::loadMetadataFromMedium: "
"invalid medium: no URL, no input stream" _ustr, *this , 0 );
}
uno::Reference<embed::XStorage> xStorage;
try {
if (xIn.is()) {
xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
xIn, m_pImpl->m_xContext);
} else { // fallback to url
xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
URL, embed::ElementModes::READ, m_pImpl->m_xContext);
}
} catch (const uno::RuntimeException &) {
throw ;
} catch (const io::IOException &) {
throw ;
} catch (const uno::Exception &) {
css::uno::Any anyEx = cppu::getCaughtException();
throw lang::WrappedTargetException(
u"DocumentMetadataAccess::loadMetadataFromMedium: "
"exception" _ustr, *this , anyEx);
}
if (!xStorage.is()) {
throw uno::RuntimeException(
u"DocumentMetadataAccess::loadMetadataFromMedium: "
"cannot get Storage" _ustr, *this );
}
uno::Reference<rdf::XURI> xBaseURI;
try {
xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, BaseURL);
} catch (const uno::Exception &) {
// fall back to URL
try {
xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, URL);
} catch (const uno::Exception &) {
OSL_FAIL("cannot create base URI" );
}
}
uno::Reference<task::XInteractionHandler> xIH;
md[ utl::MediaDescriptor::PROP_INTERACTIONHANDLER ] >>= xIH;
loadMetadataFromStorage(xStorage, xBaseURI, xIH);
}
void SAL_CALL
DocumentMetadataAccess::storeMetadataToMedium(
const uno::Sequence< beans::PropertyValue > & i_rMedium)
{
utl::MediaDescriptor md(i_rMedium);
OUString URL;
md[ utl::MediaDescriptor::PROP_URL ] >>= URL;
if (URL.isEmpty()) {
throw lang::IllegalArgumentException(
u"DocumentMetadataAccess::storeMetadataToMedium: "
"invalid medium: no URL" _ustr, *this , 0 );
}
SfxMedium aMedium(i_rMedium);
uno::Reference<embed::XStorage> xStorage(aMedium.GetOutputStorage());
bool sfx(false );
if (xStorage.is()) {
sfx = true ;
} else {
xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
URL, embed::ElementModes::WRITE, m_pImpl->m_xContext);
}
if (!xStorage.is()) {
throw uno::RuntimeException(
u"DocumentMetadataAccess::storeMetadataToMedium: "
"cannot get Storage" _ustr, *this );
}
// set MIME type of the storage
utl::MediaDescriptor::const_iterator iter
= md.find(utl::MediaDescriptor::PROP_MEDIATYPE);
if (iter != md.end()) {
uno::Reference< beans::XPropertySet > xProps(xStorage,
uno::UNO_QUERY_THROW);
try {
// this is NOT supported in FileSystemStorage
xProps->setPropertyValue(
utl::MediaDescriptor::PROP_MEDIATYPE,
iter->second);
} catch (const uno::Exception &) { }
}
storeMetadataToStorage(xStorage);
if (!sfx)
return ;
const bool bOk = aMedium.Commit();
aMedium.Close();
if ( !bOk ) {
ErrCodeMsg nError = aMedium.GetErrorIgnoreWarning();
if ( nError == ERRCODE_NONE ) {
nError = ERRCODE_IO_GENERAL;
}
task::ErrorCodeIOException ex(
"DocumentMetadataAccess::storeMetadataToMedium Commit failed: " + nError.toString(),
uno::Reference< uno::XInterface >(), sal_uInt32(nError.GetCode()));
throw lang::WrappedTargetException(OUString(), *this ,
uno::Any(ex));
}
}
} // namespace sfx2
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=95 H=97 G=95
¤ Dauer der Verarbeitung: 0.26 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland
2026-10-09
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126438
#Domains=1867298