Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / sfx2 / source / doc / DocumentMetadataAccess.cxx
blob730c64e28cb0ed724c2480940238438d786a4ad7
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>
60 #include <com/sun/star/uri/XUriReference.hpp>
61 #include <com/sun/star/uri/UriReferenceFactory.hpp>
65 Note: in the context of this implementation, all rdf.QueryExceptions and
66 rdf.RepositoryExceptions are RuntimeExceptions, and will be reported as such.
68 This implementation assumes that it is only used with ODF documents, not mere
69 ODF packages. In other words, we enforce that metadata files must not be
70 called reserved names.
73 using namespace ::com::sun::star;
75 namespace sfx2 {
78 bool isValidNCName(OUString const & i_rIdref)
80 const OString id(
81 OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8) );
82 return !(xmlValidateNCName(
83 reinterpret_cast<const unsigned char*>(id.getStr()), 0));
87 const char s_content [] = "content.xml";
88 const char s_styles [] = "styles.xml";
89 const char s_manifest[] = "manifest.rdf";
90 const char s_odfmime [] = "application/vnd.oasis.opendocument.";
93 static bool isContentFile(OUString const & i_rPath)
95 return i_rPath == s_content;
98 static bool isStylesFile (OUString const & i_rPath)
100 return i_rPath == s_styles;
103 bool isValidXmlId(OUString const & i_rStreamName,
104 OUString const & i_rIdref)
106 return isValidNCName(i_rIdref)
107 && (isContentFile(i_rStreamName) || isStylesFile(i_rStreamName));
110 static bool isReservedFile(OUString const & i_rPath)
112 return isContentFile(i_rPath) || isStylesFile(i_rPath) || i_rPath == "meta.xml" || i_rPath == "settings.xml";
116 uno::Reference<rdf::XURI> createBaseURI(
117 uno::Reference<uno::XComponentContext> const & i_xContext,
118 uno::Reference<frame::XModel> const & i_xModel,
119 OUString const & i_rPkgURI, OUString const & i_rSubDocument)
121 if (!i_xContext.is() || (!i_xModel.is() && i_rPkgURI.isEmpty())) {
122 throw uno::RuntimeException();
125 OUString pkgURI(i_rPkgURI);
127 // tdf#123293 chicken/egg problem when loading from stream: there is no URI,
128 // and also the model doesn't have a storage yet, so we need to get the
129 // tdoc URI without a storage...
130 if (pkgURI.isEmpty())
132 assert(i_xModel.is());
133 uno::Reference<frame::XTransientDocumentsDocumentContentIdentifierFactory>
134 const xTDDCIF(
135 i_xContext->getServiceManager()->createInstanceWithContext(
136 "com.sun.star.ucb.TransientDocumentsContentProvider",
137 i_xContext),
138 uno::UNO_QUERY_THROW);
139 uno::Reference<ucb::XContentIdentifier> const xContentId(
140 xTDDCIF->createDocumentContentIdentifier(i_xModel));
141 SAL_WARN_IF(!xContentId.is(), "sfx", "createBaseURI: cannot create ContentIdentifier");
142 if (!xContentId.is())
144 throw uno::RuntimeException("createBaseURI: cannot create ContentIdentifier");
146 pkgURI = xContentId->getContentIdentifier();
147 assert(!pkgURI.isEmpty());
148 if (!pkgURI.isEmpty() && !pkgURI.endsWith("/"))
150 pkgURI += "/";
154 // #i108078# workaround non-hierarchical vnd.sun.star.expand URIs
155 // this really should be done somewhere else, not here.
156 if (pkgURI.matchIgnoreAsciiCase("vnd.sun.star.expand:"))
158 // expand it here (makeAbsolute requires hierarchical URI)
159 pkgURI = pkgURI.copy( RTL_CONSTASCII_LENGTH("vnd.sun.star.expand:") );
160 if (!pkgURI.isEmpty()) {
161 pkgURI = ::rtl::Uri::decode(
162 pkgURI, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8);
163 if (pkgURI.isEmpty()) {
164 throw uno::RuntimeException();
166 ::rtl::Bootstrap::expandMacros(pkgURI);
170 const uno::Reference<uri::XUriReferenceFactory> xUriFactory =
171 uri::UriReferenceFactory::create( i_xContext);
172 uno::Reference< uri::XUriReference > xBaseURI;
174 const uno::Reference< uri::XUriReference > xPkgURI(
175 xUriFactory->parse(pkgURI), uno::UNO_SET_THROW );
176 xPkgURI->clearFragment();
178 // need to know whether the storage is a FileSystemStorage
179 // XServiceInfo would be better, but it is not implemented
180 // if ( pkgURI.getLength() && ::utl::UCBContentHelper::IsFolder(pkgURI) )
181 if (true) {
182 xBaseURI.set( xPkgURI, uno::UNO_SET_THROW );
184 OUStringBuffer buf(64);
185 if (!xBaseURI->getUriReference().endsWith("/"))
187 const sal_Int32 count( xBaseURI->getPathSegmentCount() );
188 if (count > 0)
190 buf.append(xBaseURI->getPathSegment(count - 1));
192 buf.append('/');
194 if (!i_rSubDocument.isEmpty())
196 buf.append(i_rSubDocument);
197 buf.append('/');
199 if (!buf.isEmpty())
201 const uno::Reference< uri::XUriReference > xPathURI(
202 xUriFactory->parse(buf.makeStringAndClear()), uno::UNO_SET_THROW );
203 xBaseURI.set(
204 xUriFactory->makeAbsolute(xBaseURI, xPathURI,
205 true, uri::RelativeUriExcessParentSegments_ERROR),
206 uno::UNO_SET_THROW);
209 return rdf::URI::create(i_xContext, xBaseURI->getUriReference());
213 struct DocumentMetadataAccess_Impl
215 // note: these are all initialized in constructor, and loadFromStorage
216 const uno::Reference<uno::XComponentContext> m_xContext;
217 const SfxObjectShell & m_rXmlIdRegistrySupplier;
218 uno::Reference<rdf::XURI> m_xBaseURI;
219 uno::Reference<rdf::XRepository> m_xRepository;
220 uno::Reference<rdf::XNamedGraph> m_xManifest;
221 DocumentMetadataAccess_Impl(
222 uno::Reference<uno::XComponentContext> const& i_xContext,
223 SfxObjectShell const & i_rRegistrySupplier)
224 : m_xContext(i_xContext)
225 , m_rXmlIdRegistrySupplier(i_rRegistrySupplier)
226 , m_xBaseURI()
227 , m_xRepository()
228 , m_xManifest()
230 OSL_ENSURE(m_xContext.is(), "context null");
234 // this is... a hack.
235 template<sal_Int16 Constant>
236 static uno::Reference<rdf::XURI> const &
237 getURI(uno::Reference< uno::XComponentContext > const & i_xContext)
239 static uno::Reference< rdf::XURI > xURI(
240 rdf::URI::createKnown(i_xContext, Constant), uno::UNO_SET_THROW);
241 return xURI;
245 /** would storing the file to a XStorage succeed? */
246 static bool isFileNameValid(const OUString & i_rFileName)
248 if (i_rFileName.isEmpty()) return false;
249 if (i_rFileName[0] == '/') return false; // no absolute paths!
250 sal_Int32 idx(0);
251 do {
252 const OUString segment(
253 i_rFileName.getToken(0, u'/', idx) );
254 if (segment.isEmpty() || // no empty segments
255 segment == "." || // no . segments
256 segment == ".." || // no .. segments
257 !::comphelper::OStorageHelper::IsValidZipEntryFileName(
258 segment, false)) // no invalid characters
259 return false;
260 } while (idx >= 0);
261 return true;
264 /** split a uri hierarchy into first segment and rest */
265 static bool
266 splitPath(OUString const & i_rPath,
267 OUString & o_rDir, OUString& o_rRest)
269 const sal_Int32 idx(i_rPath.indexOf(u'/'));
270 if (idx < 0 || idx >= i_rPath.getLength()) {
271 o_rDir.clear();
272 o_rRest = i_rPath;
273 return true;
274 } else if (idx == 0 || idx == i_rPath.getLength() - 1) {
275 // input must not start or end with '/'
276 return false;
277 } else {
278 o_rDir = i_rPath.copy(0, idx);
279 o_rRest = i_rPath.copy(idx+1);
280 return true;
284 static bool
285 splitXmlId(OUString const & i_XmlId,
286 OUString & o_StreamName, OUString& o_Idref )
288 const sal_Int32 idx(i_XmlId.indexOf(u'#'));
289 if ((idx <= 0) || (idx >= i_XmlId.getLength() - 1)) {
290 return false;
291 } else {
292 o_StreamName = i_XmlId.copy(0, idx);
293 o_Idref = i_XmlId.copy(idx+1);
294 return isValidXmlId(o_StreamName, o_Idref);
299 static uno::Reference<rdf::XURI>
300 getURIForStream(struct DocumentMetadataAccess_Impl const & i_rImpl,
301 OUString const& i_rPath)
303 const uno::Reference<rdf::XURI> xURI(
304 rdf::URI::createNS( i_rImpl.m_xContext,
305 i_rImpl.m_xBaseURI->getStringValue(), i_rPath),
306 uno::UNO_SET_THROW);
307 return xURI;
310 /** add statements declaring i_xResource to be a file of type i_xType with
311 path i_rPath to manifest, with optional additional types i_pTypes */
312 static void
313 addFile(struct DocumentMetadataAccess_Impl const & i_rImpl,
314 uno::Reference<rdf::XURI> const& i_xType,
315 OUString const & i_rPath,
316 const uno::Sequence < uno::Reference< rdf::XURI > > * i_pTypes)
318 try {
319 const uno::Reference<rdf::XURI> xURI( getURIForStream(
320 i_rImpl, i_rPath) );
322 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(),
323 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
324 xURI.get());
325 i_rImpl.m_xManifest->addStatement(xURI.get(),
326 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
327 i_xType.get());
328 if (i_pTypes) {
329 for (const auto& rType : *i_pTypes) {
330 i_rImpl.m_xManifest->addStatement(xURI.get(),
331 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
332 rType.get());
335 } catch (const uno::RuntimeException &) {
336 throw;
337 } catch (const uno::Exception &) {
338 css::uno::Any anyEx = cppu::getCaughtException();
339 throw lang::WrappedTargetRuntimeException(
340 "addFile: exception", /*this*/nullptr, anyEx);
344 /** add content.xml or styles.xml to manifest */
345 static bool
346 addContentOrStylesFileImpl(struct DocumentMetadataAccess_Impl const & i_rImpl,
347 const OUString & i_rPath)
349 uno::Reference<rdf::XURI> xType;
350 if (isContentFile(i_rPath)) {
351 xType.set(getURI<rdf::URIs::ODF_CONTENTFILE>(i_rImpl.m_xContext));
352 } else if (isStylesFile(i_rPath)) {
353 xType.set(getURI<rdf::URIs::ODF_STYLESFILE>(i_rImpl.m_xContext));
354 } else {
355 return false;
357 addFile(i_rImpl, xType.get(), i_rPath, nullptr);
358 return true;
361 /** add metadata file to manifest */
362 static void
363 addMetadataFileImpl(struct DocumentMetadataAccess_Impl const & i_rImpl,
364 const OUString & i_rPath,
365 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
367 addFile(i_rImpl,
368 getURI<rdf::URIs::PKG_METADATAFILE>(i_rImpl.m_xContext),
369 i_rPath, &i_rTypes);
372 /** remove a file from the manifest */
373 static void
374 removeFile(struct DocumentMetadataAccess_Impl const & i_rImpl,
375 uno::Reference<rdf::XURI> const& i_xPart)
377 if (!i_xPart.is()) throw uno::RuntimeException();
378 try {
379 i_rImpl.m_xManifest->removeStatements(i_rImpl.m_xBaseURI.get(),
380 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
381 i_xPart.get());
382 i_rImpl.m_xManifest->removeStatements(i_xPart.get(),
383 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), nullptr);
384 } catch (const uno::RuntimeException &) {
385 throw;
386 } catch (const uno::Exception &) {
387 css::uno::Any anyEx = cppu::getCaughtException();
388 throw lang::WrappedTargetRuntimeException(
389 "removeFile: exception",
390 nullptr, anyEx);
394 static ::std::vector< uno::Reference< rdf::XURI > >
395 getAllParts(struct DocumentMetadataAccess_Impl const & i_rImpl)
397 ::std::vector< uno::Reference< rdf::XURI > > ret;
398 try {
399 const uno::Reference<container::XEnumeration> xEnum(
400 i_rImpl.m_xManifest->getStatements( i_rImpl.m_xBaseURI.get(),
401 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), nullptr),
402 uno::UNO_SET_THROW);
403 while (xEnum->hasMoreElements()) {
404 rdf::Statement stmt;
405 if (!(xEnum->nextElement() >>= stmt)) {
406 throw uno::RuntimeException();
408 const uno::Reference<rdf::XURI> xPart(stmt.Object,
409 uno::UNO_QUERY);
410 if (!xPart.is()) continue;
411 ret.push_back(xPart);
413 return ret;
414 } catch (const uno::RuntimeException &) {
415 throw;
416 } catch (const uno::Exception &) {
417 css::uno::Any anyEx = cppu::getCaughtException();
418 throw lang::WrappedTargetRuntimeException(
419 "getAllParts: exception",
420 nullptr, anyEx);
424 static bool
425 isPartOfType(struct DocumentMetadataAccess_Impl const & i_rImpl,
426 uno::Reference<rdf::XURI> const & i_xPart,
427 uno::Reference<rdf::XURI> const & i_xType)
429 if (!i_xPart.is() || !i_xType.is()) throw uno::RuntimeException();
430 try {
431 const uno::Reference<container::XEnumeration> xEnum(
432 i_rImpl.m_xManifest->getStatements(i_xPart.get(),
433 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
434 i_xType.get()),
435 uno::UNO_SET_THROW);
436 return xEnum->hasMoreElements();
437 } catch (const uno::RuntimeException &) {
438 throw;
439 } catch (const uno::Exception &) {
440 css::uno::Any anyEx = cppu::getCaughtException();
441 throw lang::WrappedTargetRuntimeException(
442 "isPartOfType: exception",
443 nullptr, anyEx);
447 static ::std::vector<uno::Reference<rdf::XURI>>
448 getAllParts(struct DocumentMetadataAccess_Impl const& i_rImpl,
449 const uno::Reference<rdf::XURI>& i_xType)
451 ::std::vector<uno::Reference<rdf::XURI>> ret;
454 const uno::Reference<container::XEnumeration> xEnum(
455 i_rImpl.m_xManifest->getStatements(i_rImpl.m_xBaseURI.get(),
456 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
457 nullptr),
458 uno::UNO_SET_THROW);
459 while (xEnum->hasMoreElements())
461 rdf::Statement stmt;
462 if (!(xEnum->nextElement() >>= stmt))
464 throw uno::RuntimeException();
466 const uno::Reference<rdf::XURI> xPart(stmt.Object, uno::UNO_QUERY);
467 if (!xPart.is())
468 continue;
470 const uno::Reference<container::XEnumeration> xEnum2(
471 i_rImpl.m_xManifest->getStatements(
472 xPart.get(), getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), i_xType.get()),
473 uno::UNO_SET_THROW);
474 if (xEnum2->hasMoreElements())
475 ret.emplace_back(xPart);
477 return ret;
479 catch (const uno::RuntimeException&)
481 throw;
483 catch (const uno::Exception& e)
485 throw lang::WrappedTargetRuntimeException("getAllParts: exception", nullptr,
486 uno::makeAny(e));
490 static ucb::InteractiveAugmentedIOException
491 mkException( OUString const & i_rMessage,
492 ucb::IOErrorCode const i_ErrorCode,
493 OUString const & i_rUri, OUString const & i_rResource)
495 ucb::InteractiveAugmentedIOException iaioe;
496 iaioe.Message = i_rMessage;
497 iaioe.Classification = task::InteractionClassification_ERROR;
498 iaioe.Code = i_ErrorCode;
500 const beans::PropertyValue uriProp("Uri",
501 -1, uno::makeAny(i_rUri), static_cast<beans::PropertyState>(0));
502 const beans::PropertyValue rnProp(
503 "ResourceName",
504 -1, uno::makeAny(i_rResource), static_cast<beans::PropertyState>(0));
505 iaioe.Arguments = { uno::makeAny(uriProp), uno::makeAny(rnProp) };
506 return iaioe;
509 /** error handling policy.
510 <p>If a handler is given, ask it how to proceed:
511 <ul><li>(default:) cancel import, raise exception</li>
512 <li>ignore the error and continue</li>
513 <li>retry the action that led to the error</li></ul></p>
514 N.B.: must not be called before DMA is fully initialized!
515 @returns true iff caller should retry
517 static bool
518 handleError( ucb::InteractiveAugmentedIOException const & i_rException,
519 const uno::Reference<task::XInteractionHandler> & i_xHandler)
521 if (!i_xHandler.is()) {
522 throw lang::WrappedTargetException(
523 "DocumentMetadataAccess::loadMetadataFromStorage: exception",
524 /* *this*/ nullptr, uno::makeAny(i_rException));
527 ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest(
528 new ::comphelper::OInteractionRequest(uno::makeAny(i_rException)) );
529 ::rtl::Reference< ::comphelper::OInteractionRetry > pRetry(
530 new ::comphelper::OInteractionRetry );
531 ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove(
532 new ::comphelper::OInteractionApprove );
533 ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort(
534 new ::comphelper::OInteractionAbort );
536 pRequest->addContinuation( pApprove.get() );
537 pRequest->addContinuation( pAbort.get() );
538 // actually call the handler
539 i_xHandler->handle( pRequest.get() );
540 if (pRetry->wasSelected()) {
541 return true;
542 } else if (pApprove->wasSelected()) {
543 return false;
544 } else {
545 OSL_ENSURE(pAbort->wasSelected(), "no continuation selected?");
546 throw lang::WrappedTargetException(
547 "DocumentMetadataAccess::loadMetadataFromStorage: exception",
548 /* *this*/ nullptr, uno::makeAny(i_rException));
552 /** check if storage has content.xml/styles.xml;
553 e.g. ODB files seem to only have content.xml */
554 static void
555 collectFilesFromStorage(uno::Reference<embed::XStorage> const& i_xStorage,
556 std::set< OUString > & o_rFiles)
558 static OUString content(s_content);
559 static OUString styles(s_styles );
560 try {
561 if (i_xStorage->hasByName(content) &&
562 i_xStorage->isStreamElement(content))
564 o_rFiles.insert(content);
566 if (i_xStorage->hasByName(styles) &&
567 i_xStorage->isStreamElement(styles))
569 o_rFiles.insert(styles);
571 } catch (const uno::Exception &) {
572 TOOLS_WARN_EXCEPTION("sfx", "collectFilesFromStorage");
576 /** import a metadata file into repository */
577 static void
578 readStream(struct DocumentMetadataAccess_Impl & i_rImpl,
579 uno::Reference< embed::XStorage > const & i_xStorage,
580 OUString const & i_rPath,
581 OUString const & i_rBaseURI)
583 try {
584 OUString dir;
585 OUString rest;
586 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
587 if (dir.isEmpty()) {
588 if (!i_xStorage->isStreamElement(i_rPath)) {
589 throw mkException(
590 "readStream: is not a stream",
591 ucb::IOErrorCode_NO_FILE, i_rBaseURI + i_rPath, i_rPath);
593 const uno::Reference<io::XStream> xStream(
594 i_xStorage->openStreamElement(i_rPath,
595 embed::ElementModes::READ), uno::UNO_SET_THROW);
596 const uno::Reference<io::XInputStream> xInStream(
597 xStream->getInputStream(), uno::UNO_SET_THROW );
598 const uno::Reference<rdf::XURI> xBaseURI(
599 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
600 const uno::Reference<rdf::XURI> xURI(
601 rdf::URI::createNS(i_rImpl.m_xContext,
602 i_rBaseURI, i_rPath));
603 i_rImpl.m_xRepository->importGraph(rdf::FileFormat::RDF_XML,
604 xInStream, xURI, xBaseURI);
605 } else {
606 if (!i_xStorage->isStorageElement(dir)) {
607 throw mkException(
608 "readStream: is not a directory",
609 ucb::IOErrorCode_NO_DIRECTORY, i_rBaseURI + dir, dir);
611 const uno::Reference<embed::XStorage> xDir(
612 i_xStorage->openStorageElement(dir,
613 embed::ElementModes::READ));
614 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
615 uno::UNO_QUERY_THROW);
616 try {
617 OUString mimeType;
618 xDirProps->getPropertyValue(
619 utl::MediaDescriptor::PROP_MEDIATYPE() )
620 >>= mimeType;
621 if (mimeType.startsWith(s_odfmime)) {
622 SAL_WARN("sfx", "readStream: refusing to recurse into embedded document");
623 return;
625 } catch (const uno::Exception &) { }
626 readStream(i_rImpl, xDir, rest, i_rBaseURI+dir+"/" );
628 } catch (const container::NoSuchElementException & e) {
629 throw mkException(e.Message, ucb::IOErrorCode_NOT_EXISTING_PATH,
630 i_rBaseURI + i_rPath, i_rPath);
631 } catch (const io::IOException & e) {
632 throw mkException(e.Message, ucb::IOErrorCode_CANT_READ,
633 i_rBaseURI + i_rPath, i_rPath);
634 } catch (const rdf::ParseException & e) {
635 throw mkException(e.Message, ucb::IOErrorCode_WRONG_FORMAT,
636 i_rBaseURI + i_rPath, i_rPath);
640 /** import a metadata file into repository */
641 static void
642 importFile(struct DocumentMetadataAccess_Impl & i_rImpl,
643 uno::Reference<embed::XStorage> const & i_xStorage,
644 OUString const & i_rBaseURI,
645 uno::Reference<task::XInteractionHandler> const & i_xHandler,
646 const OUString& i_rPath)
648 retry:
649 try {
650 readStream(i_rImpl, i_xStorage, i_rPath, i_rBaseURI);
651 } catch (const ucb::InteractiveAugmentedIOException & e) {
652 if (handleError(e, i_xHandler)) goto retry;
653 } catch (const uno::RuntimeException &) {
654 throw;
655 } catch (const uno::Exception &) {
656 css::uno::Any anyEx = cppu::getCaughtException();
657 throw lang::WrappedTargetRuntimeException(
658 "importFile: exception",
659 nullptr, anyEx);
663 /** actually write a metadata file to the storage */
664 static void
665 exportStream(struct DocumentMetadataAccess_Impl const & i_rImpl,
666 uno::Reference< embed::XStorage > const & i_xStorage,
667 uno::Reference<rdf::XURI> const & i_xGraphName,
668 OUString const & i_rFileName,
669 OUString const & i_rBaseURI)
671 const uno::Reference<io::XStream> xStream(
672 i_xStorage->openStreamElement(i_rFileName,
673 embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE),
674 uno::UNO_SET_THROW);
675 const uno::Reference< beans::XPropertySet > xStreamProps(xStream,
676 uno::UNO_QUERY);
677 if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage
678 xStreamProps->setPropertyValue(
679 "MediaType",
680 uno::makeAny(OUString("application/rdf+xml")));
682 const uno::Reference<io::XOutputStream> xOutStream(
683 xStream->getOutputStream(), uno::UNO_SET_THROW );
684 const uno::Reference<rdf::XURI> xBaseURI(
685 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
686 i_rImpl.m_xRepository->exportGraph(rdf::FileFormat::RDF_XML,
687 xOutStream, i_xGraphName, xBaseURI);
690 /** write a metadata file to the storage */
691 static void
692 writeStream(struct DocumentMetadataAccess_Impl & i_rImpl,
693 uno::Reference< embed::XStorage > const & i_xStorage,
694 uno::Reference<rdf::XURI> const & i_xGraphName,
695 OUString const & i_rPath,
696 OUString const & i_rBaseURI)
698 OUString dir;
699 OUString rest;
700 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
701 try {
702 if (dir.isEmpty()) {
703 exportStream(i_rImpl, i_xStorage, i_xGraphName, i_rPath,
704 i_rBaseURI);
705 } else {
706 const uno::Reference<embed::XStorage> xDir(
707 i_xStorage->openStorageElement(dir,
708 embed::ElementModes::WRITE));
709 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
710 uno::UNO_QUERY_THROW);
711 try {
712 OUString mimeType;
713 xDirProps->getPropertyValue(
714 utl::MediaDescriptor::PROP_MEDIATYPE() )
715 >>= mimeType;
716 if (mimeType.startsWith(s_odfmime)) {
717 SAL_WARN("sfx", "writeStream: refusing to recurse into embedded document");
718 return;
720 } catch (const uno::Exception &) { }
721 writeStream(i_rImpl, xDir, i_xGraphName, rest, i_rBaseURI+dir+"/");
722 uno::Reference<embed::XTransactedObject> const xTransaction(
723 xDir, uno::UNO_QUERY);
724 if (xTransaction.is()) {
725 xTransaction->commit();
728 } catch (const uno::RuntimeException &) {
729 throw;
730 } catch (const io::IOException &) {
731 throw;
735 static void
736 initLoading(struct DocumentMetadataAccess_Impl & i_rImpl,
737 const uno::Reference< embed::XStorage > & i_xStorage,
738 const uno::Reference<rdf::XURI> & i_xBaseURI,
739 const uno::Reference<task::XInteractionHandler> & i_xHandler)
741 retry:
742 // clear old data
743 i_rImpl.m_xManifest.clear();
744 // init BaseURI
745 i_rImpl.m_xBaseURI = i_xBaseURI;
747 // create repository
748 i_rImpl.m_xRepository.clear();
749 i_rImpl.m_xRepository.set(rdf::Repository::create(i_rImpl.m_xContext),
750 uno::UNO_SET_THROW);
752 // try to delay raising errors until after initialization is done
753 uno::Any rterr;
754 ucb::InteractiveAugmentedIOException iaioe;
755 bool err(false);
757 const uno::Reference <rdf::XURI> xManifest(
758 getURIForStream(i_rImpl, s_manifest));
759 try {
760 readStream(i_rImpl, i_xStorage, s_manifest, i_xBaseURI->getStringValue());
761 } catch (const ucb::InteractiveAugmentedIOException & e) {
762 // no manifest.rdf: this is not an error in ODF < 1.2
763 if (ucb::IOErrorCode_NOT_EXISTING_PATH != e.Code) {
764 iaioe = e;
765 err = true;
767 } catch (const uno::Exception & e) {
768 rterr <<= e;
771 // init manifest graph
772 const uno::Reference<rdf::XNamedGraph> xManifestGraph(
773 i_rImpl.m_xRepository->getGraph(xManifest));
774 i_rImpl.m_xManifest.set(xManifestGraph.is() ? xManifestGraph :
775 i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_SET_THROW);
777 // document statement
778 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(),
779 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
780 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get());
782 OSL_ENSURE(i_rImpl.m_xBaseURI.is(), "base URI is null");
783 OSL_ENSURE(i_rImpl.m_xRepository.is(), "repository is null");
784 OSL_ENSURE(i_rImpl.m_xManifest.is(), "manifest is null");
786 if (rterr.hasValue()) {
787 throw lang::WrappedTargetRuntimeException(
788 "DocumentMetadataAccess::loadMetadataFromStorage: "
789 "exception", nullptr, rterr);
792 if (err && handleError(iaioe, i_xHandler))
793 goto retry;
796 /** init Impl struct */
797 static void init(struct DocumentMetadataAccess_Impl & i_rImpl)
799 try {
801 i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph(
802 getURIForStream(i_rImpl, s_manifest)),
803 uno::UNO_SET_THROW);
805 // insert the document statement
806 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI.get(),
807 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
808 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext).get());
809 } catch (const uno::Exception &) {
810 css::uno::Any anyEx = cppu::getCaughtException();
811 throw lang::WrappedTargetRuntimeException(
812 "init: unexpected exception", nullptr,
813 anyEx);
816 // add top-level content files
817 if (!addContentOrStylesFileImpl(i_rImpl, s_content)) {
818 throw uno::RuntimeException();
820 if (!addContentOrStylesFileImpl(i_rImpl, s_styles)) {
821 throw uno::RuntimeException();
826 DocumentMetadataAccess::DocumentMetadataAccess(
827 uno::Reference< uno::XComponentContext > const & i_xContext,
828 const SfxObjectShell & i_rRegistrySupplier)
829 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
831 // no initialization: must call loadFrom...
834 DocumentMetadataAccess::DocumentMetadataAccess(
835 uno::Reference< uno::XComponentContext > const & i_xContext,
836 const SfxObjectShell & i_rRegistrySupplier,
837 OUString const & i_rURI)
838 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
840 OSL_ENSURE(!i_rURI.isEmpty(), "DMA::DMA: no URI given!");
841 OSL_ENSURE(i_rURI.endsWith("/"), "DMA::DMA: URI without / given!");
842 if (!i_rURI.endsWith("/")) throw uno::RuntimeException();
843 m_pImpl->m_xBaseURI.set(rdf::URI::create(m_pImpl->m_xContext, i_rURI));
844 m_pImpl->m_xRepository.set(rdf::Repository::create(m_pImpl->m_xContext),
845 uno::UNO_SET_THROW);
847 // init repository
848 init(*m_pImpl);
850 OSL_ENSURE(m_pImpl->m_xBaseURI.is(), "base URI is null");
851 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository is null");
852 OSL_ENSURE(m_pImpl->m_xManifest.is(), "manifest is null");
855 DocumentMetadataAccess::~DocumentMetadataAccess()
859 // css::rdf::XRepositorySupplier:
860 uno::Reference< rdf::XRepository > SAL_CALL
861 DocumentMetadataAccess::getRDFRepository()
863 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository not initialized");
864 return m_pImpl->m_xRepository;
867 // css::rdf::XNode:
868 OUString SAL_CALL
869 DocumentMetadataAccess::getStringValue()
871 return m_pImpl->m_xBaseURI->getStringValue();
874 // css::rdf::XURI:
875 OUString SAL_CALL
876 DocumentMetadataAccess::getNamespace()
878 return m_pImpl->m_xBaseURI->getNamespace();
881 OUString SAL_CALL
882 DocumentMetadataAccess::getLocalName()
884 return m_pImpl->m_xBaseURI->getLocalName();
887 // css::rdf::XDocumentMetadataAccess:
888 uno::Reference< rdf::XMetadatable > SAL_CALL
889 DocumentMetadataAccess::getElementByMetadataReference(
890 const css::beans::StringPair & i_rReference)
892 const IXmlIdRegistry * pReg(
893 m_pImpl->m_rXmlIdRegistrySupplier.GetXmlIdRegistry() );
894 if (!pReg) {
895 throw uno::RuntimeException(
896 "DocumentMetadataAccess::getElementByXmlId: no registry", *this);
898 return pReg->GetElementByMetadataReference(i_rReference);
901 uno::Reference< rdf::XMetadatable > SAL_CALL
902 DocumentMetadataAccess::getElementByURI(
903 const uno::Reference< rdf::XURI > & i_xURI )
905 if (!i_xURI.is()) {
906 throw lang::IllegalArgumentException(
907 "DocumentMetadataAccess::getElementByURI: URI is null", *this, 0);
910 const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
911 const OUString name( i_xURI->getStringValue() );
912 if (!name.match(baseURI)) {
913 return nullptr;
915 OUString path;
916 OUString idref;
917 if (!splitXmlId(name.copy(baseURI.getLength()), path, idref)) {
918 return nullptr;
921 return getElementByMetadataReference( beans::StringPair(path, idref) );
924 uno::Sequence<uno::Reference<rdf::XURI>> SAL_CALL
925 DocumentMetadataAccess::getMetadataGraphsWithType(const uno::Reference<rdf::XURI>& i_xType)
927 if (!i_xType.is())
929 throw lang::IllegalArgumentException("DocumentMetadataAccess::getMetadataGraphsWithType: "
930 "type is null",
931 *this, 0);
934 return ::comphelper::containerToSequence(getAllParts(*m_pImpl, i_xType));
937 uno::Reference<rdf::XURI> SAL_CALL
938 DocumentMetadataAccess::addMetadataFile(const OUString & i_rFileName,
939 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
941 if (!isFileNameValid(i_rFileName)) {
942 throw lang::IllegalArgumentException(
943 "DocumentMetadataAccess::addMetadataFile: invalid FileName",
944 *this, 0);
946 if (isReservedFile(i_rFileName)) {
947 throw lang::IllegalArgumentException(
948 "DocumentMetadataAccess::addMetadataFile:"
949 "invalid FileName: reserved", *this, 0);
951 if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
952 [](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
953 throw lang::IllegalArgumentException(
954 "DocumentMetadataAccess::addMetadataFile: "
955 "null type", *this, 2);
958 const uno::Reference<rdf::XURI> xGraphName(
959 getURIForStream(*m_pImpl, i_rFileName) );
961 try {
962 m_pImpl->m_xRepository->createGraph(xGraphName);
963 } catch (const rdf::RepositoryException &) {
964 css::uno::Any anyEx = cppu::getCaughtException();
965 throw lang::WrappedTargetRuntimeException(
966 "DocumentMetadataAccess::addMetadataFile: exception",
967 *this, anyEx);
968 // note: all other exceptions are propagated
971 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
972 return xGraphName;
975 uno::Reference<rdf::XURI> SAL_CALL
976 DocumentMetadataAccess::importMetadataFile(::sal_Int16 i_Format,
977 const uno::Reference< io::XInputStream > & i_xInStream,
978 const OUString & i_rFileName,
979 const uno::Reference< rdf::XURI > & i_xBaseURI,
980 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
982 if (!isFileNameValid(i_rFileName)) {
983 throw lang::IllegalArgumentException(
984 "DocumentMetadataAccess::importMetadataFile: invalid FileName",
985 *this, 0);
987 if (isReservedFile(i_rFileName)) {
988 throw lang::IllegalArgumentException(
989 "DocumentMetadataAccess::importMetadataFile:"
990 "invalid FileName: reserved", *this, 0);
992 if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
993 [](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
994 throw lang::IllegalArgumentException(
995 "DocumentMetadataAccess::importMetadataFile: null type",
996 *this, 5);
999 const uno::Reference<rdf::XURI> xGraphName(
1000 getURIForStream(*m_pImpl, i_rFileName) );
1002 try {
1003 m_pImpl->m_xRepository->importGraph(
1004 i_Format, i_xInStream, xGraphName, i_xBaseURI);
1005 } catch (const rdf::RepositoryException &) {
1006 css::uno::Any anyEx = cppu::getCaughtException();
1007 throw lang::WrappedTargetRuntimeException(
1008 "DocumentMetadataAccess::importMetadataFile: "
1009 "RepositoryException", *this, anyEx);
1010 // note: all other exceptions are propagated
1013 // add to manifest
1014 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
1015 return xGraphName;
1018 void SAL_CALL
1019 DocumentMetadataAccess::removeMetadataFile(
1020 const uno::Reference< rdf::XURI > & i_xGraphName)
1022 try {
1023 m_pImpl->m_xRepository->destroyGraph(i_xGraphName);
1024 } catch (const rdf::RepositoryException &) {
1025 css::uno::Any anyEx = cppu::getCaughtException();
1026 throw lang::WrappedTargetRuntimeException(
1027 "DocumentMetadataAccess::removeMetadataFile: "
1028 "RepositoryException", *this, anyEx);
1029 // note: all other exceptions are propagated
1032 // remove file from manifest
1033 removeFile(*m_pImpl, i_xGraphName.get());
1036 void SAL_CALL
1037 DocumentMetadataAccess::addContentOrStylesFile(
1038 const OUString & i_rFileName)
1040 if (!isFileNameValid(i_rFileName)) {
1041 throw lang::IllegalArgumentException(
1042 "DocumentMetadataAccess::addContentOrStylesFile: "
1043 "invalid FileName", *this, 0);
1046 if (!addContentOrStylesFileImpl(*m_pImpl, i_rFileName)) {
1047 throw lang::IllegalArgumentException(
1048 "DocumentMetadataAccess::addContentOrStylesFile: "
1049 "invalid FileName: must end with content.xml or styles.xml",
1050 *this, 0);
1054 void SAL_CALL
1055 DocumentMetadataAccess::removeContentOrStylesFile(
1056 const OUString & i_rFileName)
1058 if (!isFileNameValid(i_rFileName)) {
1059 throw lang::IllegalArgumentException(
1060 "DocumentMetadataAccess::removeContentOrStylesFile: "
1061 "invalid FileName", *this, 0);
1064 try {
1065 const uno::Reference<rdf::XURI> xPart(
1066 getURIForStream(*m_pImpl, i_rFileName) );
1067 const uno::Reference<container::XEnumeration> xEnum(
1068 m_pImpl->m_xManifest->getStatements( m_pImpl->m_xBaseURI.get(),
1069 getURI<rdf::URIs::PKG_HASPART>(m_pImpl->m_xContext),
1070 xPart.get()),
1071 uno::UNO_SET_THROW);
1072 if (!xEnum->hasMoreElements()) {
1073 throw container::NoSuchElementException(
1074 "DocumentMetadataAccess::removeContentOrStylesFile: "
1075 "cannot find stream in manifest graph: " + i_rFileName,
1076 *this);
1079 // remove file from manifest
1080 removeFile(*m_pImpl, xPart);
1082 } catch (const uno::RuntimeException &) {
1083 throw;
1084 } catch (const uno::Exception &) {
1085 css::uno::Any anyEx = cppu::getCaughtException();
1086 throw lang::WrappedTargetRuntimeException(
1087 "DocumentMetadataAccess::removeContentOrStylesFile: exception",
1088 *this, anyEx);
1092 void SAL_CALL DocumentMetadataAccess::loadMetadataFromStorage(
1093 const uno::Reference< embed::XStorage > & i_xStorage,
1094 const uno::Reference<rdf::XURI> & i_xBaseURI,
1095 const uno::Reference<task::XInteractionHandler> & i_xHandler)
1097 if (!i_xStorage.is()) {
1098 throw lang::IllegalArgumentException(
1099 "DocumentMetadataAccess::loadMetadataFromStorage: "
1100 "storage is null", *this, 0);
1102 if (!i_xBaseURI.is()) {
1103 throw lang::IllegalArgumentException(
1104 "DocumentMetadataAccess::loadMetadataFromStorage: "
1105 "base URI is null", *this, 1);
1107 const OUString baseURI( i_xBaseURI->getStringValue());
1108 if (baseURI.indexOf('#') >= 0) {
1109 throw lang::IllegalArgumentException(
1110 "DocumentMetadataAccess::loadMetadataFromStorage: "
1111 "base URI not absolute", *this, 1);
1113 if (!baseURI.endsWith("/")) {
1114 throw lang::IllegalArgumentException(
1115 "DocumentMetadataAccess::loadMetadataFromStorage: "
1116 "base URI does not end with slash", *this, 1);
1119 initLoading(*m_pImpl, i_xStorage, i_xBaseURI, i_xHandler);
1121 std::set< OUString > StgFiles;
1122 collectFilesFromStorage(i_xStorage, StgFiles);
1124 std::vector< OUString > MfstMetadataFiles;
1126 try {
1127 const ::std::vector< uno::Reference< rdf::XURI > > parts(
1128 getAllParts(*m_pImpl) );
1129 const uno::Reference<rdf::XURI>& xContentFile(
1130 getURI<rdf::URIs::ODF_CONTENTFILE>(m_pImpl->m_xContext));
1131 const uno::Reference<rdf::XURI>& xStylesFile(
1132 getURI<rdf::URIs::ODF_STYLESFILE>(m_pImpl->m_xContext));
1133 const uno::Reference<rdf::XURI>& xMetadataFile(
1134 getURI<rdf::URIs::PKG_METADATAFILE>(m_pImpl->m_xContext));
1135 const sal_Int32 len( baseURI.getLength() );
1136 for (const auto& rxPart : parts) {
1137 const OUString name(rxPart->getStringValue());
1138 if (!name.match(baseURI)) {
1139 SAL_WARN("sfx", "loadMetadataFromStorage: graph not in document: " << name);
1140 continue;
1142 const OUString relName( name.copy(len) );
1143 if (relName == s_manifest) {
1144 SAL_WARN("sfx", "loadMetadataFromStorage: found ourselves a recursive manifest!");
1145 continue;
1147 // remove found items from StgFiles
1148 StgFiles.erase(relName);
1149 if (isContentFile(relName)) {
1150 if (!isPartOfType(*m_pImpl, rxPart, xContentFile)) {
1151 const uno::Reference <rdf::XURI> xName(
1152 getURIForStream(*m_pImpl, relName) );
1153 // add missing type statement
1154 m_pImpl->m_xManifest->addStatement(xName.get(),
1155 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1156 xContentFile.get());
1158 } else if (isStylesFile(relName)) {
1159 if (!isPartOfType(*m_pImpl, rxPart, xStylesFile)) {
1160 const uno::Reference <rdf::XURI> xName(
1161 getURIForStream(*m_pImpl, relName) );
1162 // add missing type statement
1163 m_pImpl->m_xManifest->addStatement(xName.get(),
1164 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1165 xStylesFile.get());
1167 } else if (isReservedFile(relName)) {
1168 SAL_WARN("sfx", "loadMetadataFromStorage: reserved file name in manifest");
1169 } else {
1170 if (isPartOfType(*m_pImpl, rxPart, xMetadataFile)) {
1171 MfstMetadataFiles.push_back(relName);
1173 // do not add statement for MetadataFile; it could be
1174 // something else! just ignore it...
1177 } catch (const uno::RuntimeException &) {
1178 throw;
1179 } catch (const uno::Exception &) {
1180 css::uno::Any anyEx = cppu::getCaughtException();
1181 throw lang::WrappedTargetRuntimeException(
1182 "DocumentMetadataAccess::loadMetadataFromStorage: "
1183 "exception", *this, anyEx);
1186 for (const auto& aStgFile : StgFiles)
1187 addContentOrStylesFileImpl(*m_pImpl, aStgFile);
1189 for (const auto& aMfstMetadataFile : MfstMetadataFiles)
1190 importFile(*m_pImpl, i_xStorage, baseURI, i_xHandler, aMfstMetadataFile);
1193 void SAL_CALL DocumentMetadataAccess::storeMetadataToStorage(
1194 const uno::Reference< embed::XStorage > & i_xStorage)
1196 if (!i_xStorage.is()) {
1197 throw lang::IllegalArgumentException(
1198 "DocumentMetadataAccess::storeMetadataToStorage: "
1199 "storage is null", *this, 0);
1202 // export manifest
1203 const uno::Reference <rdf::XURI> xManifest(
1204 getURIForStream(*m_pImpl, s_manifest) );
1205 const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
1206 try {
1207 writeStream(*m_pImpl, i_xStorage, xManifest, s_manifest, baseURI);
1208 } catch (const uno::RuntimeException &) {
1209 throw;
1210 } catch (const io::IOException &) {
1211 css::uno::Any anyEx = cppu::getCaughtException();
1212 throw lang::WrappedTargetException(
1213 "storeMetadataToStorage: IO exception", *this, anyEx);
1214 } catch (const uno::Exception &) {
1215 css::uno::Any anyEx = cppu::getCaughtException();
1216 throw lang::WrappedTargetRuntimeException(
1217 "storeMetadataToStorage: exception", *this, anyEx);
1220 // export metadata streams
1221 try {
1222 const uno::Sequence<uno::Reference<rdf::XURI> > graphs(
1223 m_pImpl->m_xRepository->getGraphNames());
1224 const sal_Int32 len( baseURI.getLength() );
1225 for (const uno::Reference<rdf::XURI>& xName : graphs) {
1226 const OUString name(xName->getStringValue());
1227 if (!name.match(baseURI)) {
1228 SAL_WARN("sfx", "storeMetadataToStorage: graph not in document: " << name);
1229 continue;
1231 const OUString relName( name.copy(len) );
1232 if (relName == s_manifest) {
1233 continue;
1235 if (!isFileNameValid(relName) || isReservedFile(relName)) {
1236 SAL_WARN("sfx", "storeMetadataToStorage: invalid file name: " << relName);
1237 continue;
1239 try {
1240 writeStream(*m_pImpl, i_xStorage, xName, relName, baseURI);
1241 } catch (const uno::RuntimeException &) {
1242 throw;
1243 } catch (const io::IOException &) {
1244 css::uno::Any anyEx = cppu::getCaughtException();
1245 throw lang::WrappedTargetException(
1246 "storeMetadataToStorage: IO exception",
1247 *this, anyEx);
1248 } catch (const uno::Exception &) {
1249 css::uno::Any anyEx = cppu::getCaughtException();
1250 throw lang::WrappedTargetRuntimeException(
1251 "storeMetadataToStorage: exception",
1252 *this, anyEx);
1255 } catch (const rdf::RepositoryException &) {
1256 css::uno::Any anyEx = cppu::getCaughtException();
1257 throw lang::WrappedTargetRuntimeException(
1258 "storeMetadataToStorage: exception", *this, anyEx);
1262 void SAL_CALL
1263 DocumentMetadataAccess::loadMetadataFromMedium(
1264 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1266 uno::Reference<io::XInputStream> xIn;
1267 utl::MediaDescriptor md(i_rMedium);
1268 OUString URL;
1269 md[ utl::MediaDescriptor::PROP_URL() ] >>= URL;
1270 OUString BaseURL;
1271 md[ utl::MediaDescriptor::PROP_DOCUMENTBASEURL() ] >>= BaseURL;
1272 if (md.addInputStream()) {
1273 md[ utl::MediaDescriptor::PROP_INPUTSTREAM() ] >>= xIn;
1275 if (!xIn.is() && URL.isEmpty()) {
1276 throw lang::IllegalArgumentException(
1277 "DocumentMetadataAccess::loadMetadataFromMedium: "
1278 "invalid medium: no URL, no input stream", *this, 0);
1280 uno::Reference<embed::XStorage> xStorage;
1281 try {
1282 if (xIn.is()) {
1283 xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
1284 xIn, m_pImpl->m_xContext);
1285 } else { // fallback to url
1286 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1287 URL, embed::ElementModes::READ, m_pImpl->m_xContext);
1289 } catch (const uno::RuntimeException &) {
1290 throw;
1291 } catch (const io::IOException &) {
1292 throw;
1293 } catch (const uno::Exception &) {
1294 css::uno::Any anyEx = cppu::getCaughtException();
1295 throw lang::WrappedTargetException(
1296 "DocumentMetadataAccess::loadMetadataFromMedium: "
1297 "exception", *this, anyEx);
1299 if (!xStorage.is()) {
1300 throw uno::RuntimeException(
1301 "DocumentMetadataAccess::loadMetadataFromMedium: "
1302 "cannot get Storage", *this);
1304 uno::Reference<rdf::XURI> xBaseURI;
1305 try {
1306 xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, BaseURL);
1307 } catch (const uno::Exception &) {
1308 // fall back to URL
1309 try {
1310 xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, URL);
1311 } catch (const uno::Exception &) {
1312 OSL_FAIL("cannot create base URI");
1315 uno::Reference<task::XInteractionHandler> xIH;
1316 md[ utl::MediaDescriptor::PROP_INTERACTIONHANDLER() ] >>= xIH;
1317 loadMetadataFromStorage(xStorage, xBaseURI, xIH);
1320 void SAL_CALL
1321 DocumentMetadataAccess::storeMetadataToMedium(
1322 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1324 utl::MediaDescriptor md(i_rMedium);
1325 OUString URL;
1326 md[ utl::MediaDescriptor::PROP_URL() ] >>= URL;
1327 if (URL.isEmpty()) {
1328 throw lang::IllegalArgumentException(
1329 "DocumentMetadataAccess::storeMetadataToMedium: "
1330 "invalid medium: no URL", *this, 0);
1333 SfxMedium aMedium(i_rMedium);
1334 uno::Reference<embed::XStorage> xStorage(aMedium.GetOutputStorage());
1336 bool sfx(false);
1337 if (xStorage.is()) {
1338 sfx = true;
1339 } else {
1340 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1341 URL, embed::ElementModes::WRITE, m_pImpl->m_xContext);
1344 if (!xStorage.is()) {
1345 throw uno::RuntimeException(
1346 "DocumentMetadataAccess::storeMetadataToMedium: "
1347 "cannot get Storage", *this);
1349 // set MIME type of the storage
1350 utl::MediaDescriptor::const_iterator iter
1351 = md.find(utl::MediaDescriptor::PROP_MEDIATYPE());
1352 if (iter != md.end()) {
1353 uno::Reference< beans::XPropertySet > xProps(xStorage,
1354 uno::UNO_QUERY_THROW);
1355 try {
1356 // this is NOT supported in FileSystemStorage
1357 xProps->setPropertyValue(
1358 utl::MediaDescriptor::PROP_MEDIATYPE(),
1359 iter->second);
1360 } catch (const uno::Exception &) { }
1362 storeMetadataToStorage(xStorage);
1364 if (!sfx)
1365 return;
1367 const bool bOk = aMedium.Commit();
1368 aMedium.Close();
1369 if ( !bOk ) {
1370 ErrCode nError = aMedium.GetError();
1371 if ( nError == ERRCODE_NONE ) {
1372 nError = ERRCODE_IO_GENERAL;
1374 task::ErrorCodeIOException ex(
1375 "DocumentMetadataAccess::storeMetadataToMedium Commit failed: " + nError.toHexString(),
1376 uno::Reference< uno::XInterface >(), sal_uInt32(nError));
1377 throw lang::WrappedTargetException(OUString(), *this,
1378 uno::makeAny(ex));
1382 } // namespace sfx2
1384 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */