LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sfx2 / source / doc / DocumentMetadataAccess.cxx
blob160dfce000f508aa33abfb95b69a8f5ed0f4dc61
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <sfx2/DocumentMetadataAccess.hxx>
23 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
24 #include <com/sun/star/beans/XPropertySet.hpp>
25 #include <com/sun/star/embed/ElementModes.hpp>
26 #include <com/sun/star/embed/XStorage.hpp>
27 #include <com/sun/star/embed/XTransactedObject.hpp>
28 #include <com/sun/star/frame/XTransientDocumentsDocumentContentIdentifierFactory.hpp>
29 #include <com/sun/star/task/ErrorCodeIOException.hpp>
30 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
31 #include <com/sun/star/rdf/FileFormat.hpp>
32 #include <com/sun/star/rdf/ParseException.hpp>
33 #include <com/sun/star/rdf/RepositoryException.hpp>
34 #include <com/sun/star/rdf/URIs.hpp>
35 #include <com/sun/star/rdf/Statement.hpp>
36 #include <com/sun/star/rdf/URI.hpp>
37 #include <com/sun/star/rdf/Repository.hpp>
39 #include <rtl/ustrbuf.hxx>
40 #include <rtl/uri.hxx>
41 #include <rtl/bootstrap.hxx>
42 #include <sal/log.hxx>
44 #include <comphelper/interaction.hxx>
45 #include <unotools/mediadescriptor.hxx>
46 #include <comphelper/sequence.hxx>
47 #include <comphelper/storagehelper.hxx>
48 #include <cppuhelper/exc_hlp.hxx>
50 #include <sfx2/docfile.hxx>
51 #include <sfx2/XmlIdRegistry.hxx>
52 #include <sfx2/objsh.hxx>
53 #include <tools/diagnose_ex.h>
55 #include <libxml/tree.h>
57 #include <vector>
58 #include <set>
59 #include <string_view>
61 #include <com/sun/star/uri/XUriReference.hpp>
62 #include <com/sun/star/uri/UriReferenceFactory.hpp>
66 Note: in the context of this implementation, all rdf.QueryExceptions and
67 rdf.RepositoryExceptions are RuntimeExceptions, and will be reported as such.
69 This implementation assumes that it is only used with ODF documents, not mere
70 ODF packages. In other words, we enforce that metadata files must not be
71 called reserved names.
74 using namespace ::com::sun::star;
76 namespace sfx2 {
79 bool isValidNCName(std::u16string_view i_rIdref)
81 const OString id(
82 OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8) );
83 return !(xmlValidateNCName(
84 reinterpret_cast<const unsigned char*>(id.getStr()), 0));
88 constexpr OUStringLiteral s_content = u"content.xml";
89 constexpr OUStringLiteral s_styles = u"styles.xml";
90 constexpr OUStringLiteral s_manifest = u"manifest.rdf";
91 const char s_odfmime [] = "application/vnd.oasis.opendocument.";
94 static bool isContentFile(std::u16string_view i_rPath)
96 return i_rPath == s_content;
99 static bool isStylesFile (std::u16string_view i_rPath)
101 return i_rPath == s_styles;
104 bool isValidXmlId(std::u16string_view i_rStreamName,
105 std::u16string_view i_rIdref)
107 return isValidNCName(i_rIdref)
108 && (isContentFile(i_rStreamName) || isStylesFile(i_rStreamName));
111 static bool isReservedFile(std::u16string_view i_rPath)
113 return isContentFile(i_rPath) || isStylesFile(i_rPath) || i_rPath == u"meta.xml" || i_rPath == u"settings.xml";
117 uno::Reference<rdf::XURI> createBaseURI(
118 uno::Reference<uno::XComponentContext> const & i_xContext,
119 uno::Reference<frame::XModel> const & i_xModel,
120 OUString const & i_rPkgURI, std::u16string_view i_rSubDocument)
122 if (!i_xContext.is() || (!i_xModel.is() && i_rPkgURI.isEmpty())) {
123 throw uno::RuntimeException();
126 OUString pkgURI(i_rPkgURI);
128 // tdf#123293 chicken/egg problem when loading from stream: there is no URI,
129 // and also the model doesn't have a storage yet, so we need to get the
130 // tdoc URI without a storage...
131 if (pkgURI.isEmpty())
133 assert(i_xModel.is());
134 uno::Reference<frame::XTransientDocumentsDocumentContentIdentifierFactory>
135 const xTDDCIF(
136 i_xContext->getServiceManager()->createInstanceWithContext(
137 "com.sun.star.ucb.TransientDocumentsContentProvider",
138 i_xContext),
139 uno::UNO_QUERY_THROW);
140 uno::Reference<ucb::XContentIdentifier> const xContentId(
141 xTDDCIF->createDocumentContentIdentifier(i_xModel));
142 SAL_WARN_IF(!xContentId.is(), "sfx", "createBaseURI: cannot create ContentIdentifier");
143 if (!xContentId.is())
145 throw uno::RuntimeException("createBaseURI: cannot create ContentIdentifier");
147 pkgURI = xContentId->getContentIdentifier();
148 assert(!pkgURI.isEmpty());
149 if (!pkgURI.isEmpty() && !pkgURI.endsWith("/"))
151 pkgURI += "/";
155 // #i108078# workaround non-hierarchical vnd.sun.star.expand URIs
156 // this really should be done somewhere else, not here.
157 if (pkgURI.matchIgnoreAsciiCase("vnd.sun.star.expand:"))
159 // expand it here (makeAbsolute requires hierarchical URI)
160 pkgURI = pkgURI.copy( RTL_CONSTASCII_LENGTH("vnd.sun.star.expand:") );
161 if (!pkgURI.isEmpty()) {
162 pkgURI = ::rtl::Uri::decode(
163 pkgURI, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8);
164 if (pkgURI.isEmpty()) {
165 throw uno::RuntimeException();
167 ::rtl::Bootstrap::expandMacros(pkgURI);
171 const uno::Reference<uri::XUriReferenceFactory> xUriFactory =
172 uri::UriReferenceFactory::create( i_xContext);
173 uno::Reference< uri::XUriReference > xBaseURI;
175 const uno::Reference< uri::XUriReference > xPkgURI(
176 xUriFactory->parse(pkgURI), uno::UNO_SET_THROW );
177 xPkgURI->clearFragment();
179 // need to know whether the storage is a FileSystemStorage
180 // XServiceInfo would be better, but it is not implemented
181 // if ( pkgURI.getLength() && ::utl::UCBContentHelper::IsFolder(pkgURI) )
182 if (true) {
183 xBaseURI.set( xPkgURI, uno::UNO_SET_THROW );
185 OUStringBuffer buf(64);
186 if (!xBaseURI->getUriReference().endsWith("/"))
188 const sal_Int32 count( xBaseURI->getPathSegmentCount() );
189 if (count > 0)
191 buf.append(xBaseURI->getPathSegment(count - 1));
193 buf.append('/');
195 if (!i_rSubDocument.empty())
197 buf.append(i_rSubDocument);
198 buf.append('/');
200 if (!buf.isEmpty())
202 const uno::Reference< uri::XUriReference > xPathURI(
203 xUriFactory->parse(buf.makeStringAndClear()), uno::UNO_SET_THROW );
204 xBaseURI.set(
205 xUriFactory->makeAbsolute(xBaseURI, xPathURI,
206 true, uri::RelativeUriExcessParentSegments_ERROR),
207 uno::UNO_SET_THROW);
210 return rdf::URI::create(i_xContext, xBaseURI->getUriReference());
214 struct DocumentMetadataAccess_Impl
216 // note: these are all initialized in constructor, and loadFromStorage
217 const uno::Reference<uno::XComponentContext> m_xContext;
218 const SfxObjectShell & m_rXmlIdRegistrySupplier;
219 uno::Reference<rdf::XURI> m_xBaseURI;
220 uno::Reference<rdf::XRepository> m_xRepository;
221 uno::Reference<rdf::XNamedGraph> m_xManifest;
222 DocumentMetadataAccess_Impl(
223 uno::Reference<uno::XComponentContext> const& i_xContext,
224 SfxObjectShell const & i_rRegistrySupplier)
225 : m_xContext(i_xContext)
226 , m_rXmlIdRegistrySupplier(i_rRegistrySupplier)
228 OSL_ENSURE(m_xContext.is(), "context null");
232 // this is... a hack.
233 template<sal_Int16 Constant>
234 static uno::Reference<rdf::XURI> const &
235 getURI(uno::Reference< uno::XComponentContext > const & i_xContext)
237 static uno::Reference< rdf::XURI > xURI(
238 rdf::URI::createKnown(i_xContext, Constant), uno::UNO_SET_THROW);
239 return xURI;
243 /** would storing the file to a XStorage succeed? */
244 static bool isFileNameValid(const OUString & i_rFileName)
246 if (i_rFileName.isEmpty()) return false;
247 if (i_rFileName[0] == '/') return false; // no absolute paths!
248 sal_Int32 idx(0);
249 do {
250 const OUString segment(
251 i_rFileName.getToken(0, u'/', idx) );
252 if (segment.isEmpty() || // no empty segments
253 segment == "." || // no . segments
254 segment == ".." || // no .. segments
255 !::comphelper::OStorageHelper::IsValidZipEntryFileName(
256 segment, false)) // no invalid characters
257 return false;
258 } while (idx >= 0);
259 return true;
262 /** split a uri hierarchy into first segment and rest */
263 static bool
264 splitPath(OUString const & i_rPath,
265 OUString & o_rDir, OUString& o_rRest)
267 const sal_Int32 idx(i_rPath.indexOf(u'/'));
268 if (idx < 0 || idx >= i_rPath.getLength()) {
269 o_rDir.clear();
270 o_rRest = i_rPath;
271 return true;
272 } else if (idx == 0 || idx == i_rPath.getLength() - 1) {
273 // input must not start or end with '/'
274 return false;
275 } else {
276 o_rDir = i_rPath.copy(0, idx);
277 o_rRest = i_rPath.copy(idx+1);
278 return true;
282 static bool
283 splitXmlId(OUString const & i_XmlId,
284 OUString & o_StreamName, OUString& o_Idref )
286 const sal_Int32 idx(i_XmlId.indexOf(u'#'));
287 if ((idx <= 0) || (idx >= i_XmlId.getLength() - 1)) {
288 return false;
289 } else {
290 o_StreamName = i_XmlId.copy(0, idx);
291 o_Idref = i_XmlId.copy(idx+1);
292 return isValidXmlId(o_StreamName, o_Idref);
297 static uno::Reference<rdf::XURI>
298 getURIForStream(struct DocumentMetadataAccess_Impl const & i_rImpl,
299 OUString const& i_rPath)
301 const uno::Reference<rdf::XURI> xURI(
302 rdf::URI::createNS( i_rImpl.m_xContext,
303 i_rImpl.m_xBaseURI->getStringValue(), i_rPath),
304 uno::UNO_SET_THROW);
305 return xURI;
308 /** add statements declaring i_xResource to be a file of type i_xType with
309 path i_rPath to manifest, with optional additional types i_pTypes */
310 static void
311 addFile(struct DocumentMetadataAccess_Impl const & i_rImpl,
312 uno::Reference<rdf::XURI> const& i_xType,
313 OUString const & i_rPath,
314 const uno::Sequence < uno::Reference< rdf::XURI > > * i_pTypes)
316 try {
317 const uno::Reference<rdf::XURI> xURI( getURIForStream(
318 i_rImpl, i_rPath) );
320 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
321 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
322 xURI);
323 i_rImpl.m_xManifest->addStatement(xURI,
324 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
325 i_xType);
326 if (i_pTypes) {
327 for (const auto& rType : *i_pTypes) {
328 i_rImpl.m_xManifest->addStatement(xURI,
329 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
330 rType);
333 } catch (const uno::RuntimeException &) {
334 throw;
335 } catch (const uno::Exception &) {
336 css::uno::Any anyEx = cppu::getCaughtException();
337 throw lang::WrappedTargetRuntimeException(
338 "addFile: exception", /*this*/nullptr, anyEx);
342 /** add content.xml or styles.xml to manifest */
343 static bool
344 addContentOrStylesFileImpl(struct DocumentMetadataAccess_Impl const & i_rImpl,
345 const OUString & i_rPath)
347 uno::Reference<rdf::XURI> xType;
348 if (isContentFile(i_rPath)) {
349 xType.set(getURI<rdf::URIs::ODF_CONTENTFILE>(i_rImpl.m_xContext));
350 } else if (isStylesFile(i_rPath)) {
351 xType.set(getURI<rdf::URIs::ODF_STYLESFILE>(i_rImpl.m_xContext));
352 } else {
353 return false;
355 addFile(i_rImpl, xType, i_rPath, nullptr);
356 return true;
359 /** add metadata file to manifest */
360 static void
361 addMetadataFileImpl(struct DocumentMetadataAccess_Impl const & i_rImpl,
362 const OUString & i_rPath,
363 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
365 addFile(i_rImpl,
366 getURI<rdf::URIs::PKG_METADATAFILE>(i_rImpl.m_xContext),
367 i_rPath, &i_rTypes);
370 /** remove a file from the manifest */
371 static void
372 removeFile(struct DocumentMetadataAccess_Impl const & i_rImpl,
373 uno::Reference<rdf::XURI> const& i_xPart)
375 if (!i_xPart.is()) throw uno::RuntimeException();
376 try {
377 i_rImpl.m_xManifest->removeStatements(i_rImpl.m_xBaseURI,
378 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
379 i_xPart);
380 i_rImpl.m_xManifest->removeStatements(i_xPart,
381 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), nullptr);
382 } catch (const uno::RuntimeException &) {
383 throw;
384 } catch (const uno::Exception &) {
385 css::uno::Any anyEx = cppu::getCaughtException();
386 throw lang::WrappedTargetRuntimeException(
387 "removeFile: exception",
388 nullptr, anyEx);
392 static ::std::vector< uno::Reference< rdf::XURI > >
393 getAllParts(struct DocumentMetadataAccess_Impl const & i_rImpl)
395 ::std::vector< uno::Reference< rdf::XURI > > ret;
396 try {
397 const uno::Reference<container::XEnumeration> xEnum(
398 i_rImpl.m_xManifest->getStatements( i_rImpl.m_xBaseURI,
399 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), nullptr),
400 uno::UNO_SET_THROW);
401 while (xEnum->hasMoreElements()) {
402 rdf::Statement stmt;
403 if (!(xEnum->nextElement() >>= stmt)) {
404 throw uno::RuntimeException();
406 const uno::Reference<rdf::XURI> xPart(stmt.Object,
407 uno::UNO_QUERY);
408 if (!xPart.is()) continue;
409 ret.push_back(xPart);
411 return ret;
412 } catch (const uno::RuntimeException &) {
413 throw;
414 } catch (const uno::Exception &) {
415 css::uno::Any anyEx = cppu::getCaughtException();
416 throw lang::WrappedTargetRuntimeException(
417 "getAllParts: exception",
418 nullptr, anyEx);
422 static bool
423 isPartOfType(struct DocumentMetadataAccess_Impl const & i_rImpl,
424 uno::Reference<rdf::XURI> const & i_xPart,
425 uno::Reference<rdf::XURI> const & i_xType)
427 if (!i_xPart.is() || !i_xType.is()) throw uno::RuntimeException();
428 try {
429 const uno::Reference<container::XEnumeration> xEnum(
430 i_rImpl.m_xManifest->getStatements(i_xPart,
431 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
432 i_xType),
433 uno::UNO_SET_THROW);
434 return xEnum->hasMoreElements();
435 } catch (const uno::RuntimeException &) {
436 throw;
437 } catch (const uno::Exception &) {
438 css::uno::Any anyEx = cppu::getCaughtException();
439 throw lang::WrappedTargetRuntimeException(
440 "isPartOfType: exception",
441 nullptr, anyEx);
445 static ::std::vector<uno::Reference<rdf::XURI>>
446 getAllParts(struct DocumentMetadataAccess_Impl const& i_rImpl,
447 const uno::Reference<rdf::XURI>& i_xType)
449 ::std::vector<uno::Reference<rdf::XURI>> ret;
452 const uno::Reference<container::XEnumeration> xEnum(
453 i_rImpl.m_xManifest->getStatements(i_rImpl.m_xBaseURI,
454 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
455 nullptr),
456 uno::UNO_SET_THROW);
457 while (xEnum->hasMoreElements())
459 rdf::Statement stmt;
460 if (!(xEnum->nextElement() >>= stmt))
462 throw uno::RuntimeException();
464 const uno::Reference<rdf::XURI> xPart(stmt.Object, uno::UNO_QUERY);
465 if (!xPart.is())
466 continue;
468 const uno::Reference<container::XEnumeration> xEnum2(
469 i_rImpl.m_xManifest->getStatements(
470 xPart, getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), i_xType),
471 uno::UNO_SET_THROW);
472 if (xEnum2->hasMoreElements())
473 ret.emplace_back(xPart);
475 return ret;
477 catch (const uno::RuntimeException&)
479 throw;
481 catch (const uno::Exception& e)
483 throw lang::WrappedTargetRuntimeException("getAllParts: exception", nullptr,
484 uno::makeAny(e));
488 static ucb::InteractiveAugmentedIOException
489 mkException( OUString const & i_rMessage,
490 ucb::IOErrorCode const i_ErrorCode,
491 OUString const & i_rUri, OUString const & i_rResource)
493 ucb::InteractiveAugmentedIOException iaioe;
494 iaioe.Message = i_rMessage;
495 iaioe.Classification = task::InteractionClassification_ERROR;
496 iaioe.Code = i_ErrorCode;
498 const beans::PropertyValue uriProp("Uri",
499 -1, uno::makeAny(i_rUri), static_cast<beans::PropertyState>(0));
500 const beans::PropertyValue rnProp(
501 "ResourceName",
502 -1, uno::makeAny(i_rResource), static_cast<beans::PropertyState>(0));
503 iaioe.Arguments = { uno::makeAny(uriProp), uno::makeAny(rnProp) };
504 return iaioe;
507 /** error handling policy.
508 <p>If a handler is given, ask it how to proceed:
509 <ul><li>(default:) cancel import, raise exception</li>
510 <li>ignore the error and continue</li>
511 <li>retry the action that led to the error</li></ul></p>
512 N.B.: must not be called before DMA is fully initialized!
513 @returns true iff caller should retry
515 static bool
516 handleError( ucb::InteractiveAugmentedIOException const & i_rException,
517 const uno::Reference<task::XInteractionHandler> & i_xHandler)
519 if (!i_xHandler.is()) {
520 throw lang::WrappedTargetException(
521 "DocumentMetadataAccess::loadMetadataFromStorage: exception",
522 /* *this*/ nullptr, uno::makeAny(i_rException));
525 ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest(
526 new ::comphelper::OInteractionRequest(uno::makeAny(i_rException)) );
527 ::rtl::Reference< ::comphelper::OInteractionRetry > pRetry(
528 new ::comphelper::OInteractionRetry );
529 ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove(
530 new ::comphelper::OInteractionApprove );
531 ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort(
532 new ::comphelper::OInteractionAbort );
534 pRequest->addContinuation( pApprove );
535 pRequest->addContinuation( pAbort );
536 // actually call the handler
537 i_xHandler->handle( pRequest );
538 if (pRetry->wasSelected()) {
539 return true;
540 } else if (pApprove->wasSelected()) {
541 return false;
542 } else {
543 OSL_ENSURE(pAbort->wasSelected(), "no continuation selected?");
544 throw lang::WrappedTargetException(
545 "DocumentMetadataAccess::loadMetadataFromStorage: exception",
546 /* *this*/ nullptr, uno::makeAny(i_rException));
550 /** check if storage has content.xml/styles.xml;
551 e.g. ODB files seem to only have content.xml */
552 static void
553 collectFilesFromStorage(uno::Reference<embed::XStorage> const& i_xStorage,
554 std::set< OUString > & o_rFiles)
556 try {
557 if (i_xStorage->hasByName(s_content) &&
558 i_xStorage->isStreamElement(s_content))
560 o_rFiles.insert(s_content);
562 if (i_xStorage->hasByName(s_styles) &&
563 i_xStorage->isStreamElement(s_styles))
565 o_rFiles.insert(s_styles);
567 } catch (const uno::Exception &) {
568 TOOLS_WARN_EXCEPTION("sfx", "collectFilesFromStorage");
572 /** import a metadata file into repository */
573 static void
574 readStream(struct DocumentMetadataAccess_Impl & i_rImpl,
575 uno::Reference< embed::XStorage > const & i_xStorage,
576 OUString const & i_rPath,
577 OUString const & i_rBaseURI)
579 try {
580 OUString dir;
581 OUString rest;
582 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
583 if (dir.isEmpty()) {
584 if (!i_xStorage->isStreamElement(i_rPath)) {
585 throw mkException(
586 "readStream: is not a stream",
587 ucb::IOErrorCode_NO_FILE, i_rBaseURI + i_rPath, i_rPath);
589 const uno::Reference<io::XStream> xStream(
590 i_xStorage->openStreamElement(i_rPath,
591 embed::ElementModes::READ), uno::UNO_SET_THROW);
592 const uno::Reference<io::XInputStream> xInStream(
593 xStream->getInputStream(), uno::UNO_SET_THROW );
594 const uno::Reference<rdf::XURI> xBaseURI(
595 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
596 const uno::Reference<rdf::XURI> xURI(
597 rdf::URI::createNS(i_rImpl.m_xContext,
598 i_rBaseURI, i_rPath));
599 i_rImpl.m_xRepository->importGraph(rdf::FileFormat::RDF_XML,
600 xInStream, xURI, xBaseURI);
601 } else {
602 if (!i_xStorage->isStorageElement(dir)) {
603 throw mkException(
604 "readStream: is not a directory",
605 ucb::IOErrorCode_NO_DIRECTORY, i_rBaseURI + dir, dir);
607 const uno::Reference<embed::XStorage> xDir(
608 i_xStorage->openStorageElement(dir,
609 embed::ElementModes::READ));
610 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
611 uno::UNO_QUERY_THROW);
612 try {
613 OUString mimeType;
614 xDirProps->getPropertyValue(
615 utl::MediaDescriptor::PROP_MEDIATYPE )
616 >>= mimeType;
617 if (mimeType.startsWith(s_odfmime)) {
618 SAL_WARN("sfx", "readStream: refusing to recurse into embedded document");
619 return;
621 } catch (const uno::Exception &) { }
622 readStream(i_rImpl, xDir, rest, i_rBaseURI+dir+"/" );
624 } catch (const container::NoSuchElementException & e) {
625 throw mkException(e.Message, ucb::IOErrorCode_NOT_EXISTING_PATH,
626 i_rBaseURI + i_rPath, i_rPath);
627 } catch (const io::IOException & e) {
628 throw mkException(e.Message, ucb::IOErrorCode_CANT_READ,
629 i_rBaseURI + i_rPath, i_rPath);
630 } catch (const rdf::ParseException & e) {
631 throw mkException(e.Message, ucb::IOErrorCode_WRONG_FORMAT,
632 i_rBaseURI + i_rPath, i_rPath);
636 /** import a metadata file into repository */
637 static void
638 importFile(struct DocumentMetadataAccess_Impl & i_rImpl,
639 uno::Reference<embed::XStorage> const & i_xStorage,
640 OUString const & i_rBaseURI,
641 uno::Reference<task::XInteractionHandler> const & i_xHandler,
642 const OUString& i_rPath)
644 retry:
645 try {
646 readStream(i_rImpl, i_xStorage, i_rPath, i_rBaseURI);
647 } catch (const ucb::InteractiveAugmentedIOException & e) {
648 if (handleError(e, i_xHandler)) goto retry;
649 } catch (const uno::RuntimeException &) {
650 throw;
651 } catch (const uno::Exception &) {
652 css::uno::Any anyEx = cppu::getCaughtException();
653 throw lang::WrappedTargetRuntimeException(
654 "importFile: exception",
655 nullptr, anyEx);
659 /** actually write a metadata file to the storage */
660 static void
661 exportStream(struct DocumentMetadataAccess_Impl const & i_rImpl,
662 uno::Reference< embed::XStorage > const & i_xStorage,
663 uno::Reference<rdf::XURI> const & i_xGraphName,
664 OUString const & i_rFileName,
665 OUString const & i_rBaseURI)
667 const uno::Reference<io::XStream> xStream(
668 i_xStorage->openStreamElement(i_rFileName,
669 embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE),
670 uno::UNO_SET_THROW);
671 const uno::Reference< beans::XPropertySet > xStreamProps(xStream,
672 uno::UNO_QUERY);
673 if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage
674 xStreamProps->setPropertyValue(
675 "MediaType",
676 uno::makeAny(OUString("application/rdf+xml")));
678 const uno::Reference<io::XOutputStream> xOutStream(
679 xStream->getOutputStream(), uno::UNO_SET_THROW );
680 const uno::Reference<rdf::XURI> xBaseURI(
681 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
682 i_rImpl.m_xRepository->exportGraph(rdf::FileFormat::RDF_XML,
683 xOutStream, i_xGraphName, xBaseURI);
686 /** write a metadata file to the storage */
687 static void
688 writeStream(struct DocumentMetadataAccess_Impl & i_rImpl,
689 uno::Reference< embed::XStorage > const & i_xStorage,
690 uno::Reference<rdf::XURI> const & i_xGraphName,
691 OUString const & i_rPath,
692 OUString const & i_rBaseURI)
694 OUString dir;
695 OUString rest;
696 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
697 try {
698 if (dir.isEmpty()) {
699 exportStream(i_rImpl, i_xStorage, i_xGraphName, i_rPath,
700 i_rBaseURI);
701 } else {
702 const uno::Reference<embed::XStorage> xDir(
703 i_xStorage->openStorageElement(dir,
704 embed::ElementModes::WRITE));
705 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
706 uno::UNO_QUERY_THROW);
707 try {
708 OUString mimeType;
709 xDirProps->getPropertyValue(
710 utl::MediaDescriptor::PROP_MEDIATYPE )
711 >>= mimeType;
712 if (mimeType.startsWith(s_odfmime)) {
713 SAL_WARN("sfx", "writeStream: refusing to recurse into embedded document");
714 return;
716 } catch (const uno::Exception &) { }
717 writeStream(i_rImpl, xDir, i_xGraphName, rest, i_rBaseURI+dir+"/");
718 uno::Reference<embed::XTransactedObject> const xTransaction(
719 xDir, uno::UNO_QUERY);
720 if (xTransaction.is()) {
721 xTransaction->commit();
724 } catch (const uno::RuntimeException &) {
725 throw;
726 } catch (const io::IOException &) {
727 throw;
731 static void
732 initLoading(struct DocumentMetadataAccess_Impl & i_rImpl,
733 const uno::Reference< embed::XStorage > & i_xStorage,
734 const uno::Reference<rdf::XURI> & i_xBaseURI,
735 const uno::Reference<task::XInteractionHandler> & i_xHandler)
737 retry:
738 // clear old data
739 i_rImpl.m_xManifest.clear();
740 // init BaseURI
741 i_rImpl.m_xBaseURI = i_xBaseURI;
743 // create repository
744 i_rImpl.m_xRepository.clear();
745 i_rImpl.m_xRepository.set(rdf::Repository::create(i_rImpl.m_xContext),
746 uno::UNO_SET_THROW);
748 // try to delay raising errors until after initialization is done
749 uno::Any rterr;
750 ucb::InteractiveAugmentedIOException iaioe;
751 bool err(false);
753 const uno::Reference <rdf::XURI> xManifest(
754 getURIForStream(i_rImpl, s_manifest));
755 try {
756 readStream(i_rImpl, i_xStorage, s_manifest, i_xBaseURI->getStringValue());
757 } catch (const ucb::InteractiveAugmentedIOException & e) {
758 // no manifest.rdf: this is not an error in ODF < 1.2
759 if (ucb::IOErrorCode_NOT_EXISTING_PATH != e.Code) {
760 iaioe = e;
761 err = true;
763 } catch (const uno::Exception & e) {
764 rterr <<= e;
767 // init manifest graph
768 const uno::Reference<rdf::XNamedGraph> xManifestGraph(
769 i_rImpl.m_xRepository->getGraph(xManifest));
770 i_rImpl.m_xManifest.set(xManifestGraph.is() ? xManifestGraph :
771 i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_SET_THROW);
773 // document statement
774 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
775 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
776 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext));
778 OSL_ENSURE(i_rImpl.m_xBaseURI.is(), "base URI is null");
779 OSL_ENSURE(i_rImpl.m_xRepository.is(), "repository is null");
780 OSL_ENSURE(i_rImpl.m_xManifest.is(), "manifest is null");
782 if (rterr.hasValue()) {
783 throw lang::WrappedTargetRuntimeException(
784 "DocumentMetadataAccess::loadMetadataFromStorage: "
785 "exception", nullptr, rterr);
788 if (err && handleError(iaioe, i_xHandler))
789 goto retry;
792 /** init Impl struct */
793 static void init(struct DocumentMetadataAccess_Impl & i_rImpl)
795 try {
797 i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph(
798 getURIForStream(i_rImpl, s_manifest)),
799 uno::UNO_SET_THROW);
801 // insert the document statement
802 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
803 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
804 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext));
805 } catch (const uno::Exception &) {
806 css::uno::Any anyEx = cppu::getCaughtException();
807 throw lang::WrappedTargetRuntimeException(
808 "init: unexpected exception", nullptr,
809 anyEx);
812 // add top-level content files
813 if (!addContentOrStylesFileImpl(i_rImpl, s_content)) {
814 throw uno::RuntimeException();
816 if (!addContentOrStylesFileImpl(i_rImpl, s_styles)) {
817 throw uno::RuntimeException();
822 DocumentMetadataAccess::DocumentMetadataAccess(
823 uno::Reference< uno::XComponentContext > const & i_xContext,
824 const SfxObjectShell & i_rRegistrySupplier)
825 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
827 // no initialization: must call loadFrom...
830 DocumentMetadataAccess::DocumentMetadataAccess(
831 uno::Reference< uno::XComponentContext > const & i_xContext,
832 const SfxObjectShell & i_rRegistrySupplier,
833 OUString const & i_rURI)
834 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
836 OSL_ENSURE(!i_rURI.isEmpty(), "DMA::DMA: no URI given!");
837 OSL_ENSURE(i_rURI.endsWith("/"), "DMA::DMA: URI without / given!");
838 if (!i_rURI.endsWith("/")) throw uno::RuntimeException();
839 m_pImpl->m_xBaseURI.set(rdf::URI::create(m_pImpl->m_xContext, i_rURI));
840 m_pImpl->m_xRepository.set(rdf::Repository::create(m_pImpl->m_xContext),
841 uno::UNO_SET_THROW);
843 // init repository
844 init(*m_pImpl);
846 OSL_ENSURE(m_pImpl->m_xBaseURI.is(), "base URI is null");
847 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository is null");
848 OSL_ENSURE(m_pImpl->m_xManifest.is(), "manifest is null");
851 DocumentMetadataAccess::~DocumentMetadataAccess()
855 // css::rdf::XRepositorySupplier:
856 uno::Reference< rdf::XRepository > SAL_CALL
857 DocumentMetadataAccess::getRDFRepository()
859 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository not initialized");
860 return m_pImpl->m_xRepository;
863 // css::rdf::XNode:
864 OUString SAL_CALL
865 DocumentMetadataAccess::getStringValue()
867 return m_pImpl->m_xBaseURI->getStringValue();
870 // css::rdf::XURI:
871 OUString SAL_CALL
872 DocumentMetadataAccess::getNamespace()
874 return m_pImpl->m_xBaseURI->getNamespace();
877 OUString SAL_CALL
878 DocumentMetadataAccess::getLocalName()
880 return m_pImpl->m_xBaseURI->getLocalName();
883 // css::rdf::XDocumentMetadataAccess:
884 uno::Reference< rdf::XMetadatable > SAL_CALL
885 DocumentMetadataAccess::getElementByMetadataReference(
886 const css::beans::StringPair & i_rReference)
888 const IXmlIdRegistry * pReg(
889 m_pImpl->m_rXmlIdRegistrySupplier.GetXmlIdRegistry() );
890 if (!pReg) {
891 throw uno::RuntimeException(
892 "DocumentMetadataAccess::getElementByXmlId: no registry", *this);
894 return pReg->GetElementByMetadataReference(i_rReference);
897 uno::Reference< rdf::XMetadatable > SAL_CALL
898 DocumentMetadataAccess::getElementByURI(
899 const uno::Reference< rdf::XURI > & i_xURI )
901 if (!i_xURI.is()) {
902 throw lang::IllegalArgumentException(
903 "DocumentMetadataAccess::getElementByURI: URI is null", *this, 0);
906 const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
907 const OUString name( i_xURI->getStringValue() );
908 if (!name.match(baseURI)) {
909 return nullptr;
911 OUString path;
912 OUString idref;
913 if (!splitXmlId(name.copy(baseURI.getLength()), path, idref)) {
914 return nullptr;
917 return getElementByMetadataReference( beans::StringPair(path, idref) );
920 uno::Sequence<uno::Reference<rdf::XURI>> SAL_CALL
921 DocumentMetadataAccess::getMetadataGraphsWithType(const uno::Reference<rdf::XURI>& i_xType)
923 if (!i_xType.is())
925 throw lang::IllegalArgumentException("DocumentMetadataAccess::getMetadataGraphsWithType: "
926 "type is null",
927 *this, 0);
930 return ::comphelper::containerToSequence(getAllParts(*m_pImpl, i_xType));
933 uno::Reference<rdf::XURI> SAL_CALL
934 DocumentMetadataAccess::addMetadataFile(const OUString & i_rFileName,
935 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
937 if (!isFileNameValid(i_rFileName)) {
938 throw lang::IllegalArgumentException(
939 "DocumentMetadataAccess::addMetadataFile: invalid FileName",
940 *this, 0);
942 if (isReservedFile(i_rFileName)) {
943 throw lang::IllegalArgumentException(
944 "DocumentMetadataAccess::addMetadataFile:"
945 "invalid FileName: reserved", *this, 0);
947 if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
948 [](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
949 throw lang::IllegalArgumentException(
950 "DocumentMetadataAccess::addMetadataFile: "
951 "null type", *this, 2);
954 const uno::Reference<rdf::XURI> xGraphName(
955 getURIForStream(*m_pImpl, i_rFileName) );
957 try {
958 m_pImpl->m_xRepository->createGraph(xGraphName);
959 } catch (const rdf::RepositoryException &) {
960 css::uno::Any anyEx = cppu::getCaughtException();
961 throw lang::WrappedTargetRuntimeException(
962 "DocumentMetadataAccess::addMetadataFile: exception",
963 *this, anyEx);
964 // note: all other exceptions are propagated
967 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
968 return xGraphName;
971 uno::Reference<rdf::XURI> SAL_CALL
972 DocumentMetadataAccess::importMetadataFile(::sal_Int16 i_Format,
973 const uno::Reference< io::XInputStream > & i_xInStream,
974 const OUString & i_rFileName,
975 const uno::Reference< rdf::XURI > & i_xBaseURI,
976 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
978 if (!isFileNameValid(i_rFileName)) {
979 throw lang::IllegalArgumentException(
980 "DocumentMetadataAccess::importMetadataFile: invalid FileName",
981 *this, 0);
983 if (isReservedFile(i_rFileName)) {
984 throw lang::IllegalArgumentException(
985 "DocumentMetadataAccess::importMetadataFile:"
986 "invalid FileName: reserved", *this, 0);
988 if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
989 [](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
990 throw lang::IllegalArgumentException(
991 "DocumentMetadataAccess::importMetadataFile: null type",
992 *this, 5);
995 const uno::Reference<rdf::XURI> xGraphName(
996 getURIForStream(*m_pImpl, i_rFileName) );
998 try {
999 m_pImpl->m_xRepository->importGraph(
1000 i_Format, i_xInStream, xGraphName, i_xBaseURI);
1001 } catch (const rdf::RepositoryException &) {
1002 css::uno::Any anyEx = cppu::getCaughtException();
1003 throw lang::WrappedTargetRuntimeException(
1004 "DocumentMetadataAccess::importMetadataFile: "
1005 "RepositoryException", *this, anyEx);
1006 // note: all other exceptions are propagated
1009 // add to manifest
1010 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
1011 return xGraphName;
1014 void SAL_CALL
1015 DocumentMetadataAccess::removeMetadataFile(
1016 const uno::Reference< rdf::XURI > & i_xGraphName)
1018 try {
1019 m_pImpl->m_xRepository->destroyGraph(i_xGraphName);
1020 } catch (const rdf::RepositoryException &) {
1021 css::uno::Any anyEx = cppu::getCaughtException();
1022 throw lang::WrappedTargetRuntimeException(
1023 "DocumentMetadataAccess::removeMetadataFile: "
1024 "RepositoryException", *this, anyEx);
1025 // note: all other exceptions are propagated
1028 // remove file from manifest
1029 removeFile(*m_pImpl, i_xGraphName);
1032 void SAL_CALL
1033 DocumentMetadataAccess::addContentOrStylesFile(
1034 const OUString & i_rFileName)
1036 if (!isFileNameValid(i_rFileName)) {
1037 throw lang::IllegalArgumentException(
1038 "DocumentMetadataAccess::addContentOrStylesFile: "
1039 "invalid FileName", *this, 0);
1042 if (!addContentOrStylesFileImpl(*m_pImpl, i_rFileName)) {
1043 throw lang::IllegalArgumentException(
1044 "DocumentMetadataAccess::addContentOrStylesFile: "
1045 "invalid FileName: must end with content.xml or styles.xml",
1046 *this, 0);
1050 void SAL_CALL
1051 DocumentMetadataAccess::removeContentOrStylesFile(
1052 const OUString & i_rFileName)
1054 if (!isFileNameValid(i_rFileName)) {
1055 throw lang::IllegalArgumentException(
1056 "DocumentMetadataAccess::removeContentOrStylesFile: "
1057 "invalid FileName", *this, 0);
1060 try {
1061 const uno::Reference<rdf::XURI> xPart(
1062 getURIForStream(*m_pImpl, i_rFileName) );
1063 const uno::Reference<container::XEnumeration> xEnum(
1064 m_pImpl->m_xManifest->getStatements( m_pImpl->m_xBaseURI,
1065 getURI<rdf::URIs::PKG_HASPART>(m_pImpl->m_xContext),
1066 xPart),
1067 uno::UNO_SET_THROW);
1068 if (!xEnum->hasMoreElements()) {
1069 throw container::NoSuchElementException(
1070 "DocumentMetadataAccess::removeContentOrStylesFile: "
1071 "cannot find stream in manifest graph: " + i_rFileName,
1072 *this);
1075 // remove file from manifest
1076 removeFile(*m_pImpl, xPart);
1078 } catch (const uno::RuntimeException &) {
1079 throw;
1080 } catch (const uno::Exception &) {
1081 css::uno::Any anyEx = cppu::getCaughtException();
1082 throw lang::WrappedTargetRuntimeException(
1083 "DocumentMetadataAccess::removeContentOrStylesFile: exception",
1084 *this, anyEx);
1088 void SAL_CALL DocumentMetadataAccess::loadMetadataFromStorage(
1089 const uno::Reference< embed::XStorage > & i_xStorage,
1090 const uno::Reference<rdf::XURI> & i_xBaseURI,
1091 const uno::Reference<task::XInteractionHandler> & i_xHandler)
1093 if (!i_xStorage.is()) {
1094 throw lang::IllegalArgumentException(
1095 "DocumentMetadataAccess::loadMetadataFromStorage: "
1096 "storage is null", *this, 0);
1098 if (!i_xBaseURI.is()) {
1099 throw lang::IllegalArgumentException(
1100 "DocumentMetadataAccess::loadMetadataFromStorage: "
1101 "base URI is null", *this, 1);
1103 const OUString baseURI( i_xBaseURI->getStringValue());
1104 if (baseURI.indexOf('#') >= 0) {
1105 throw lang::IllegalArgumentException(
1106 "DocumentMetadataAccess::loadMetadataFromStorage: "
1107 "base URI not absolute", *this, 1);
1109 if (!baseURI.endsWith("/")) {
1110 throw lang::IllegalArgumentException(
1111 "DocumentMetadataAccess::loadMetadataFromStorage: "
1112 "base URI does not end with slash", *this, 1);
1115 initLoading(*m_pImpl, i_xStorage, i_xBaseURI, i_xHandler);
1117 std::set< OUString > StgFiles;
1118 collectFilesFromStorage(i_xStorage, StgFiles);
1120 std::vector< OUString > MfstMetadataFiles;
1122 try {
1123 const ::std::vector< uno::Reference< rdf::XURI > > parts(
1124 getAllParts(*m_pImpl) );
1125 const uno::Reference<rdf::XURI>& xContentFile(
1126 getURI<rdf::URIs::ODF_CONTENTFILE>(m_pImpl->m_xContext));
1127 const uno::Reference<rdf::XURI>& xStylesFile(
1128 getURI<rdf::URIs::ODF_STYLESFILE>(m_pImpl->m_xContext));
1129 const uno::Reference<rdf::XURI>& xMetadataFile(
1130 getURI<rdf::URIs::PKG_METADATAFILE>(m_pImpl->m_xContext));
1131 const sal_Int32 len( baseURI.getLength() );
1132 for (const auto& rxPart : parts) {
1133 const OUString name(rxPart->getStringValue());
1134 if (!name.match(baseURI)) {
1135 SAL_WARN("sfx", "loadMetadataFromStorage: graph not in document: " << name);
1136 continue;
1138 const OUString relName( name.copy(len) );
1139 if (relName == s_manifest) {
1140 SAL_WARN("sfx", "loadMetadataFromStorage: found ourselves a recursive manifest!");
1141 continue;
1143 // remove found items from StgFiles
1144 StgFiles.erase(relName);
1145 if (isContentFile(relName)) {
1146 if (!isPartOfType(*m_pImpl, rxPart, xContentFile)) {
1147 const uno::Reference <rdf::XURI> xName(
1148 getURIForStream(*m_pImpl, relName) );
1149 // add missing type statement
1150 m_pImpl->m_xManifest->addStatement(xName,
1151 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1152 xContentFile);
1154 } else if (isStylesFile(relName)) {
1155 if (!isPartOfType(*m_pImpl, rxPart, xStylesFile)) {
1156 const uno::Reference <rdf::XURI> xName(
1157 getURIForStream(*m_pImpl, relName) );
1158 // add missing type statement
1159 m_pImpl->m_xManifest->addStatement(xName,
1160 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1161 xStylesFile);
1163 } else if (isReservedFile(relName)) {
1164 SAL_WARN("sfx", "loadMetadataFromStorage: reserved file name in manifest");
1165 } else {
1166 if (isPartOfType(*m_pImpl, rxPart, xMetadataFile)) {
1167 MfstMetadataFiles.push_back(relName);
1169 // do not add statement for MetadataFile; it could be
1170 // something else! just ignore it...
1173 } catch (const uno::RuntimeException &) {
1174 throw;
1175 } catch (const uno::Exception &) {
1176 css::uno::Any anyEx = cppu::getCaughtException();
1177 throw lang::WrappedTargetRuntimeException(
1178 "DocumentMetadataAccess::loadMetadataFromStorage: "
1179 "exception", *this, anyEx);
1182 for (const auto& aStgFile : StgFiles)
1183 addContentOrStylesFileImpl(*m_pImpl, aStgFile);
1185 for (const auto& aMfstMetadataFile : MfstMetadataFiles)
1186 importFile(*m_pImpl, i_xStorage, baseURI, i_xHandler, aMfstMetadataFile);
1189 void SAL_CALL DocumentMetadataAccess::storeMetadataToStorage(
1190 const uno::Reference< embed::XStorage > & i_xStorage)
1192 if (!i_xStorage.is()) {
1193 throw lang::IllegalArgumentException(
1194 "DocumentMetadataAccess::storeMetadataToStorage: "
1195 "storage is null", *this, 0);
1198 // export manifest
1199 const uno::Reference <rdf::XURI> xManifest(
1200 getURIForStream(*m_pImpl, s_manifest) );
1201 const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
1202 try {
1203 writeStream(*m_pImpl, i_xStorage, xManifest, s_manifest, baseURI);
1204 } catch (const uno::RuntimeException &) {
1205 throw;
1206 } catch (const io::IOException &) {
1207 css::uno::Any anyEx = cppu::getCaughtException();
1208 throw lang::WrappedTargetException(
1209 "storeMetadataToStorage: IO exception", *this, anyEx);
1210 } catch (const uno::Exception &) {
1211 css::uno::Any anyEx = cppu::getCaughtException();
1212 throw lang::WrappedTargetRuntimeException(
1213 "storeMetadataToStorage: exception", *this, anyEx);
1216 // export metadata streams
1217 try {
1218 const uno::Sequence<uno::Reference<rdf::XURI> > graphs(
1219 m_pImpl->m_xRepository->getGraphNames());
1220 const sal_Int32 len( baseURI.getLength() );
1221 for (const uno::Reference<rdf::XURI>& xName : graphs) {
1222 const OUString name(xName->getStringValue());
1223 if (!name.match(baseURI)) {
1224 SAL_WARN("sfx", "storeMetadataToStorage: graph not in document: " << name);
1225 continue;
1227 const OUString relName( name.copy(len) );
1228 if (relName == s_manifest) {
1229 continue;
1231 if (!isFileNameValid(relName) || isReservedFile(relName)) {
1232 SAL_WARN("sfx", "storeMetadataToStorage: invalid file name: " << relName);
1233 continue;
1235 try {
1236 writeStream(*m_pImpl, i_xStorage, xName, relName, baseURI);
1237 } catch (const uno::RuntimeException &) {
1238 throw;
1239 } catch (const io::IOException &) {
1240 css::uno::Any anyEx = cppu::getCaughtException();
1241 throw lang::WrappedTargetException(
1242 "storeMetadataToStorage: IO exception",
1243 *this, anyEx);
1244 } catch (const uno::Exception &) {
1245 css::uno::Any anyEx = cppu::getCaughtException();
1246 throw lang::WrappedTargetRuntimeException(
1247 "storeMetadataToStorage: exception",
1248 *this, anyEx);
1251 } catch (const rdf::RepositoryException &) {
1252 css::uno::Any anyEx = cppu::getCaughtException();
1253 throw lang::WrappedTargetRuntimeException(
1254 "storeMetadataToStorage: exception", *this, anyEx);
1258 void SAL_CALL
1259 DocumentMetadataAccess::loadMetadataFromMedium(
1260 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1262 uno::Reference<io::XInputStream> xIn;
1263 utl::MediaDescriptor md(i_rMedium);
1264 OUString URL;
1265 md[ utl::MediaDescriptor::PROP_URL ] >>= URL;
1266 OUString BaseURL;
1267 md[ utl::MediaDescriptor::PROP_DOCUMENTBASEURL ] >>= BaseURL;
1268 if (md.addInputStream()) {
1269 md[ utl::MediaDescriptor::PROP_INPUTSTREAM ] >>= xIn;
1271 if (!xIn.is() && URL.isEmpty()) {
1272 throw lang::IllegalArgumentException(
1273 "DocumentMetadataAccess::loadMetadataFromMedium: "
1274 "invalid medium: no URL, no input stream", *this, 0);
1276 uno::Reference<embed::XStorage> xStorage;
1277 try {
1278 if (xIn.is()) {
1279 xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
1280 xIn, m_pImpl->m_xContext);
1281 } else { // fallback to url
1282 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1283 URL, embed::ElementModes::READ, m_pImpl->m_xContext);
1285 } catch (const uno::RuntimeException &) {
1286 throw;
1287 } catch (const io::IOException &) {
1288 throw;
1289 } catch (const uno::Exception &) {
1290 css::uno::Any anyEx = cppu::getCaughtException();
1291 throw lang::WrappedTargetException(
1292 "DocumentMetadataAccess::loadMetadataFromMedium: "
1293 "exception", *this, anyEx);
1295 if (!xStorage.is()) {
1296 throw uno::RuntimeException(
1297 "DocumentMetadataAccess::loadMetadataFromMedium: "
1298 "cannot get Storage", *this);
1300 uno::Reference<rdf::XURI> xBaseURI;
1301 try {
1302 xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, BaseURL);
1303 } catch (const uno::Exception &) {
1304 // fall back to URL
1305 try {
1306 xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, URL);
1307 } catch (const uno::Exception &) {
1308 OSL_FAIL("cannot create base URI");
1311 uno::Reference<task::XInteractionHandler> xIH;
1312 md[ utl::MediaDescriptor::PROP_INTERACTIONHANDLER ] >>= xIH;
1313 loadMetadataFromStorage(xStorage, xBaseURI, xIH);
1316 void SAL_CALL
1317 DocumentMetadataAccess::storeMetadataToMedium(
1318 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1320 utl::MediaDescriptor md(i_rMedium);
1321 OUString URL;
1322 md[ utl::MediaDescriptor::PROP_URL ] >>= URL;
1323 if (URL.isEmpty()) {
1324 throw lang::IllegalArgumentException(
1325 "DocumentMetadataAccess::storeMetadataToMedium: "
1326 "invalid medium: no URL", *this, 0);
1329 SfxMedium aMedium(i_rMedium);
1330 uno::Reference<embed::XStorage> xStorage(aMedium.GetOutputStorage());
1332 bool sfx(false);
1333 if (xStorage.is()) {
1334 sfx = true;
1335 } else {
1336 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1337 URL, embed::ElementModes::WRITE, m_pImpl->m_xContext);
1340 if (!xStorage.is()) {
1341 throw uno::RuntimeException(
1342 "DocumentMetadataAccess::storeMetadataToMedium: "
1343 "cannot get Storage", *this);
1345 // set MIME type of the storage
1346 utl::MediaDescriptor::const_iterator iter
1347 = md.find(utl::MediaDescriptor::PROP_MEDIATYPE);
1348 if (iter != md.end()) {
1349 uno::Reference< beans::XPropertySet > xProps(xStorage,
1350 uno::UNO_QUERY_THROW);
1351 try {
1352 // this is NOT supported in FileSystemStorage
1353 xProps->setPropertyValue(
1354 utl::MediaDescriptor::PROP_MEDIATYPE,
1355 iter->second);
1356 } catch (const uno::Exception &) { }
1358 storeMetadataToStorage(xStorage);
1360 if (!sfx)
1361 return;
1363 const bool bOk = aMedium.Commit();
1364 aMedium.Close();
1365 if ( !bOk ) {
1366 ErrCode nError = aMedium.GetError();
1367 if ( nError == ERRCODE_NONE ) {
1368 nError = ERRCODE_IO_GENERAL;
1370 task::ErrorCodeIOException ex(
1371 "DocumentMetadataAccess::storeMetadataToMedium Commit failed: " + nError.toHexString(),
1372 uno::Reference< uno::XInterface >(), sal_uInt32(nError));
1373 throw lang::WrappedTargetException(OUString(), *this,
1374 uno::makeAny(ex));
1378 } // namespace sfx2
1380 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */