tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / sfx2 / source / doc / DocumentMetadataAccess.cxx
blobc5de76afe89c1443dd6f141e2a985e8d748c892a
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>
49 #include <o3tl/string_view.hxx>
51 #include <sfx2/docfile.hxx>
52 #include <sfx2/XmlIdRegistry.hxx>
53 #include <sfx2/objsh.hxx>
54 #include <comphelper/diagnose_ex.hxx>
56 #include <libxml/tree.h>
58 #include <utility>
59 #include <vector>
60 #include <set>
61 #include <string_view>
63 #include <com/sun/star/uri/XUriReference.hpp>
64 #include <com/sun/star/uri/UriReferenceFactory.hpp>
68 Note: in the context of this implementation, all rdf.QueryExceptions and
69 rdf.RepositoryExceptions are RuntimeExceptions, and will be reported as such.
71 This implementation assumes that it is only used with ODF documents, not mere
72 ODF packages. In other words, we enforce that metadata files must not be
73 called reserved names.
76 using namespace ::com::sun::star;
78 namespace sfx2 {
81 bool isValidNCName(std::u16string_view i_rIdref)
83 const OString id(
84 OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8) );
85 return !(xmlValidateNCName(
86 reinterpret_cast<const unsigned char*>(id.getStr()), 0));
90 constexpr OUString s_content = u"content.xml"_ustr;
91 constexpr OUString s_styles = u"styles.xml"_ustr;
92 constexpr OUString s_manifest = u"manifest.rdf"_ustr;
93 const char s_odfmime [] = "application/vnd.oasis.opendocument.";
96 static bool isContentFile(std::u16string_view i_rPath)
98 return i_rPath == s_content;
101 static bool isStylesFile (std::u16string_view i_rPath)
103 return i_rPath == s_styles;
106 bool isValidXmlId(std::u16string_view i_rStreamName,
107 std::u16string_view i_rIdref)
109 return isValidNCName(i_rIdref)
110 && (isContentFile(i_rStreamName) || isStylesFile(i_rStreamName));
113 static bool isReservedFile(std::u16string_view i_rPath)
115 return isContentFile(i_rPath) || isStylesFile(i_rPath) || i_rPath == u"meta.xml" || i_rPath == u"settings.xml";
119 uno::Reference<rdf::XURI> createBaseURI(
120 uno::Reference<uno::XComponentContext> const & i_xContext,
121 uno::Reference<frame::XModel> const & i_xModel,
122 OUString const & i_rPkgURI, std::u16string_view i_rSubDocument)
124 if (!i_xContext.is() || (!i_xModel.is() && i_rPkgURI.isEmpty())) {
125 throw uno::RuntimeException();
128 OUString pkgURI(i_rPkgURI);
130 // tdf#123293 chicken/egg problem when loading from stream: there is no URI,
131 // and also the model doesn't have a storage yet, so we need to get the
132 // tdoc URI without a storage...
133 if (pkgURI.isEmpty())
135 assert(i_xModel.is());
136 uno::Reference<frame::XTransientDocumentsDocumentContentIdentifierFactory>
137 const xTDDCIF(
138 i_xContext->getServiceManager()->createInstanceWithContext(
139 u"com.sun.star.ucb.TransientDocumentsContentProvider"_ustr,
140 i_xContext),
141 uno::UNO_QUERY_THROW);
142 uno::Reference<ucb::XContentIdentifier> const xContentId(
143 xTDDCIF->createDocumentContentIdentifier(i_xModel));
144 SAL_WARN_IF(!xContentId.is(), "sfx", "createBaseURI: cannot create ContentIdentifier");
145 if (!xContentId.is())
147 throw uno::RuntimeException(u"createBaseURI: cannot create ContentIdentifier"_ustr);
149 pkgURI = xContentId->getContentIdentifier();
150 assert(!pkgURI.isEmpty());
151 if (!pkgURI.isEmpty() && !pkgURI.endsWith("/"))
153 pkgURI += "/";
157 // #i108078# workaround non-hierarchical vnd.sun.star.expand URIs
158 // this really should be done somewhere else, not here.
159 if (pkgURI.startsWithIgnoreAsciiCase("vnd.sun.star.expand:", &pkgURI))
161 // expand it here (makeAbsolute requires hierarchical URI)
162 if (!pkgURI.isEmpty()) {
163 pkgURI = ::rtl::Uri::decode(
164 pkgURI, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8);
165 if (pkgURI.isEmpty()) {
166 throw uno::RuntimeException();
168 ::rtl::Bootstrap::expandMacros(pkgURI);
172 const uno::Reference<uri::XUriReferenceFactory> xUriFactory =
173 uri::UriReferenceFactory::create( i_xContext);
174 uno::Reference< uri::XUriReference > xBaseURI;
176 const uno::Reference< uri::XUriReference > xPkgURI(
177 xUriFactory->parse(pkgURI), uno::UNO_SET_THROW );
178 xPkgURI->clearFragment();
180 // need to know whether the storage is a FileSystemStorage
181 // XServiceInfo would be better, but it is not implemented
182 // if ( pkgURI.getLength() && ::utl::UCBContentHelper::IsFolder(pkgURI) )
183 if (true) {
184 xBaseURI.set( xPkgURI, uno::UNO_SET_THROW );
186 OUStringBuffer buf(64);
187 if (!xBaseURI->getUriReference().endsWith("/"))
189 const sal_Int32 count( xBaseURI->getPathSegmentCount() );
190 if (count > 0)
192 buf.append(xBaseURI->getPathSegment(count - 1));
194 buf.append('/');
196 if (!i_rSubDocument.empty())
198 buf.append(OUString::Concat(i_rSubDocument) + "/");
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> i_xContext,
224 SfxObjectShell const & i_rRegistrySupplier)
225 : m_xContext(std::move(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(std::u16string_view i_rFileName)
246 if (i_rFileName.empty()) return false;
247 if (i_rFileName[0] == '/') return false; // no absolute paths!
248 sal_Int32 idx(0);
249 do {
250 const OUString segment(
251 o3tl::getToken(i_rFileName, 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(std::u16string_view i_XmlId,
284 OUString & o_StreamName, OUString& o_Idref )
286 const size_t idx(i_XmlId.find(u'#'));
287 if (idx == std::u16string_view::npos)
288 return false;
289 o_StreamName = i_XmlId.substr(0, idx);
290 o_Idref = i_XmlId.substr(idx+1);
291 return isValidXmlId(o_StreamName, o_Idref);
295 static uno::Reference<rdf::XURI>
296 getURIForStream(struct DocumentMetadataAccess_Impl const & i_rImpl,
297 OUString const& i_rPath)
299 const uno::Reference<rdf::XURI> xURI(
300 rdf::URI::createNS( i_rImpl.m_xContext,
301 i_rImpl.m_xBaseURI->getStringValue(), i_rPath),
302 uno::UNO_SET_THROW);
303 return xURI;
306 /** add statements declaring i_xResource to be a file of type i_xType with
307 path i_rPath to manifest, with optional additional types i_pTypes */
308 static void
309 addFile(struct DocumentMetadataAccess_Impl const & i_rImpl,
310 uno::Reference<rdf::XURI> const& i_xType,
311 OUString const & i_rPath,
312 const uno::Sequence < uno::Reference< rdf::XURI > > * i_pTypes)
314 try {
315 const uno::Reference<rdf::XURI> xURI( getURIForStream(
316 i_rImpl, i_rPath) );
318 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
319 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
320 xURI);
321 i_rImpl.m_xManifest->addStatement(xURI,
322 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
323 i_xType);
324 if (i_pTypes) {
325 for (const auto& rType : *i_pTypes) {
326 i_rImpl.m_xManifest->addStatement(xURI,
327 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
328 rType);
331 } catch (const uno::RuntimeException &) {
332 throw;
333 } catch (const uno::Exception &) {
334 css::uno::Any anyEx = cppu::getCaughtException();
335 throw lang::WrappedTargetRuntimeException(
336 u"addFile: exception"_ustr, /*this*/nullptr, anyEx);
340 /** add content.xml or styles.xml to manifest */
341 static bool
342 addContentOrStylesFileImpl(struct DocumentMetadataAccess_Impl const & i_rImpl,
343 const OUString & i_rPath)
345 uno::Reference<rdf::XURI> xType;
346 if (isContentFile(i_rPath)) {
347 xType.set(getURI<rdf::URIs::ODF_CONTENTFILE>(i_rImpl.m_xContext));
348 } else if (isStylesFile(i_rPath)) {
349 xType.set(getURI<rdf::URIs::ODF_STYLESFILE>(i_rImpl.m_xContext));
350 } else {
351 return false;
353 addFile(i_rImpl, xType, i_rPath, nullptr);
354 return true;
357 /** add metadata file to manifest */
358 static void
359 addMetadataFileImpl(struct DocumentMetadataAccess_Impl const & i_rImpl,
360 const OUString & i_rPath,
361 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
363 addFile(i_rImpl,
364 getURI<rdf::URIs::PKG_METADATAFILE>(i_rImpl.m_xContext),
365 i_rPath, &i_rTypes);
368 /** remove a file from the manifest */
369 static void
370 removeFile(struct DocumentMetadataAccess_Impl const & i_rImpl,
371 uno::Reference<rdf::XURI> const& i_xPart)
373 if (!i_xPart.is()) throw uno::RuntimeException();
374 try {
375 i_rImpl.m_xManifest->removeStatements(i_rImpl.m_xBaseURI,
376 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
377 i_xPart);
378 i_rImpl.m_xManifest->removeStatements(i_xPart,
379 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), nullptr);
380 } catch (const uno::RuntimeException &) {
381 throw;
382 } catch (const uno::Exception &) {
383 css::uno::Any anyEx = cppu::getCaughtException();
384 throw lang::WrappedTargetRuntimeException(
385 u"removeFile: exception"_ustr,
386 nullptr, anyEx);
390 static ::std::vector< uno::Reference< rdf::XURI > >
391 getAllParts(struct DocumentMetadataAccess_Impl const & i_rImpl)
393 ::std::vector< uno::Reference< rdf::XURI > > ret;
394 try {
395 const uno::Reference<container::XEnumeration> xEnum(
396 i_rImpl.m_xManifest->getStatements( i_rImpl.m_xBaseURI,
397 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), nullptr),
398 uno::UNO_SET_THROW);
399 while (xEnum->hasMoreElements()) {
400 rdf::Statement stmt;
401 if (!(xEnum->nextElement() >>= stmt)) {
402 throw uno::RuntimeException();
404 const uno::Reference<rdf::XURI> xPart(stmt.Object,
405 uno::UNO_QUERY);
406 if (!xPart.is()) continue;
407 ret.push_back(xPart);
409 return ret;
410 } catch (const uno::RuntimeException &) {
411 throw;
412 } catch (const uno::Exception &) {
413 css::uno::Any anyEx = cppu::getCaughtException();
414 throw lang::WrappedTargetRuntimeException(
415 u"getAllParts: exception"_ustr,
416 nullptr, anyEx);
420 static bool
421 isPartOfType(struct DocumentMetadataAccess_Impl const & i_rImpl,
422 uno::Reference<rdf::XURI> const & i_xPart,
423 uno::Reference<rdf::XURI> const & i_xType)
425 if (!i_xPart.is() || !i_xType.is()) throw uno::RuntimeException();
426 try {
427 const uno::Reference<container::XEnumeration> xEnum(
428 i_rImpl.m_xManifest->getStatements(i_xPart,
429 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
430 i_xType),
431 uno::UNO_SET_THROW);
432 return xEnum->hasMoreElements();
433 } catch (const uno::RuntimeException &) {
434 throw;
435 } catch (const uno::Exception &) {
436 css::uno::Any anyEx = cppu::getCaughtException();
437 throw lang::WrappedTargetRuntimeException(
438 u"isPartOfType: exception"_ustr,
439 nullptr, anyEx);
443 static ::std::vector<uno::Reference<rdf::XURI>>
444 getAllParts(struct DocumentMetadataAccess_Impl const& i_rImpl,
445 const uno::Reference<rdf::XURI>& i_xType)
447 ::std::vector<uno::Reference<rdf::XURI>> ret;
450 const uno::Reference<container::XEnumeration> xEnum(
451 i_rImpl.m_xManifest->getStatements(i_rImpl.m_xBaseURI,
452 getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
453 nullptr),
454 uno::UNO_SET_THROW);
455 while (xEnum->hasMoreElements())
457 rdf::Statement stmt;
458 if (!(xEnum->nextElement() >>= stmt))
460 throw uno::RuntimeException();
462 const uno::Reference<rdf::XURI> xPart(stmt.Object, uno::UNO_QUERY);
463 if (!xPart.is())
464 continue;
466 const uno::Reference<container::XEnumeration> xEnum2(
467 i_rImpl.m_xManifest->getStatements(
468 xPart, getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), i_xType),
469 uno::UNO_SET_THROW);
470 if (xEnum2->hasMoreElements())
471 ret.emplace_back(xPart);
473 return ret;
475 catch (const uno::RuntimeException&)
477 throw;
479 catch (const uno::Exception& e)
481 throw lang::WrappedTargetRuntimeException(u"getAllParts: exception"_ustr, nullptr,
482 uno::Any(e));
486 static ucb::InteractiveAugmentedIOException
487 mkException( OUString const & i_rMessage,
488 ucb::IOErrorCode const i_ErrorCode,
489 OUString const & i_rUri, OUString const & i_rResource)
491 const beans::PropertyValue uriProp(u"Uri"_ustr,
492 -1, uno::Any(i_rUri), static_cast<beans::PropertyState>(0));
493 const beans::PropertyValue rnProp(
494 u"ResourceName"_ustr,
495 -1, uno::Any(i_rResource), static_cast<beans::PropertyState>(0));
496 return ucb::InteractiveAugmentedIOException(i_rMessage, {},
497 task::InteractionClassification_ERROR, i_ErrorCode,
498 { uno::Any(uriProp), uno::Any(rnProp) });
501 /** error handling policy.
502 <p>If a handler is given, ask it how to proceed:
503 <ul><li>(default:) cancel import, raise exception</li>
504 <li>ignore the error and continue</li>
505 <li>retry the action that led to the error</li></ul></p>
506 N.B.: must not be called before DMA is fully initialized!
507 @returns true iff caller should retry
509 static bool
510 handleError( ucb::InteractiveAugmentedIOException const & i_rException,
511 const uno::Reference<task::XInteractionHandler> & i_xHandler)
513 if (!i_xHandler.is()) {
514 throw lang::WrappedTargetException(
515 u"DocumentMetadataAccess::loadMetadataFromStorage: exception"_ustr,
516 /* *this*/ nullptr, uno::Any(i_rException));
519 ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest(
520 new ::comphelper::OInteractionRequest(uno::Any(i_rException)) );
521 ::rtl::Reference< ::comphelper::OInteractionRetry > pRetry(
522 new ::comphelper::OInteractionRetry );
523 ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove(
524 new ::comphelper::OInteractionApprove );
525 ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort(
526 new ::comphelper::OInteractionAbort );
528 pRequest->addContinuation( pApprove );
529 pRequest->addContinuation( pAbort );
530 // actually call the handler
531 i_xHandler->handle( pRequest );
532 if (pRetry->wasSelected()) {
533 return true;
534 } else if (pApprove->wasSelected()) {
535 return false;
536 } else {
537 OSL_ENSURE(pAbort->wasSelected(), "no continuation selected?");
538 throw lang::WrappedTargetException(
539 u"DocumentMetadataAccess::loadMetadataFromStorage: exception"_ustr,
540 /* *this*/ nullptr, uno::Any(i_rException));
544 /** check if storage has content.xml/styles.xml;
545 e.g. ODB files seem to only have content.xml */
546 static void
547 collectFilesFromStorage(uno::Reference<embed::XStorage> const& i_xStorage,
548 std::set< OUString > & o_rFiles)
550 try {
551 if (i_xStorage->hasByName(s_content) &&
552 i_xStorage->isStreamElement(s_content))
554 o_rFiles.insert(s_content);
556 if (i_xStorage->hasByName(s_styles) &&
557 i_xStorage->isStreamElement(s_styles))
559 o_rFiles.insert(s_styles);
561 } catch (const uno::Exception &) {
562 TOOLS_WARN_EXCEPTION("sfx", "collectFilesFromStorage");
566 /** import a metadata file into repository */
567 static void
568 readStream(struct DocumentMetadataAccess_Impl & i_rImpl,
569 uno::Reference< embed::XStorage > const & i_xStorage,
570 OUString const & i_rPath,
571 OUString const & i_rBaseURI)
573 try {
574 OUString dir;
575 OUString rest;
576 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
577 if (dir.isEmpty()) {
578 if (!i_xStorage->isStreamElement(i_rPath)) {
579 throw mkException(
580 u"readStream: is not a stream"_ustr,
581 ucb::IOErrorCode_NO_FILE, i_rBaseURI + i_rPath, i_rPath);
583 const uno::Reference<io::XStream> xStream(
584 i_xStorage->openStreamElement(i_rPath,
585 embed::ElementModes::READ), uno::UNO_SET_THROW);
586 const uno::Reference<io::XInputStream> xInStream(
587 xStream->getInputStream(), uno::UNO_SET_THROW );
588 const uno::Reference<rdf::XURI> xBaseURI(
589 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
590 const uno::Reference<rdf::XURI> xURI(
591 rdf::URI::createNS(i_rImpl.m_xContext,
592 i_rBaseURI, i_rPath));
593 i_rImpl.m_xRepository->importGraph(rdf::FileFormat::RDF_XML,
594 xInStream, xURI, xBaseURI);
595 } else {
596 if (!i_xStorage->isStorageElement(dir)) {
597 throw mkException(
598 u"readStream: is not a directory"_ustr,
599 ucb::IOErrorCode_NO_DIRECTORY, i_rBaseURI + dir, dir);
601 const uno::Reference<embed::XStorage> xDir(
602 i_xStorage->openStorageElement(dir,
603 embed::ElementModes::READ));
604 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
605 uno::UNO_QUERY_THROW);
606 try {
607 OUString mimeType;
608 xDirProps->getPropertyValue(
609 utl::MediaDescriptor::PROP_MEDIATYPE )
610 >>= mimeType;
611 if (mimeType.startsWith(s_odfmime)) {
612 SAL_WARN("sfx", "readStream: refusing to recurse into embedded document");
613 return;
615 } catch (const uno::Exception &) { }
616 readStream(i_rImpl, xDir, rest, i_rBaseURI+dir+"/" );
618 } catch (const container::NoSuchElementException & e) {
619 throw mkException(e.Message, ucb::IOErrorCode_NOT_EXISTING_PATH,
620 i_rBaseURI + i_rPath, i_rPath);
621 } catch (const io::IOException & e) {
622 throw mkException(e.Message, ucb::IOErrorCode_CANT_READ,
623 i_rBaseURI + i_rPath, i_rPath);
624 } catch (const rdf::ParseException & e) {
625 throw mkException(e.Message, ucb::IOErrorCode_WRONG_FORMAT,
626 i_rBaseURI + i_rPath, i_rPath);
630 /** import a metadata file into repository */
631 static void
632 importFile(struct DocumentMetadataAccess_Impl & i_rImpl,
633 uno::Reference<embed::XStorage> const & i_xStorage,
634 OUString const & i_rBaseURI,
635 uno::Reference<task::XInteractionHandler> const & i_xHandler,
636 const OUString& i_rPath)
638 retry:
639 try {
640 readStream(i_rImpl, i_xStorage, i_rPath, i_rBaseURI);
641 } catch (const ucb::InteractiveAugmentedIOException & e) {
642 if (handleError(e, i_xHandler)) goto retry;
643 } catch (const uno::RuntimeException &) {
644 throw;
645 } catch (const uno::Exception &) {
646 css::uno::Any anyEx = cppu::getCaughtException();
647 throw lang::WrappedTargetRuntimeException(
648 u"importFile: exception"_ustr,
649 nullptr, anyEx);
653 /** actually write a metadata file to the storage */
654 static void
655 exportStream(struct DocumentMetadataAccess_Impl const & i_rImpl,
656 uno::Reference< embed::XStorage > const & i_xStorage,
657 uno::Reference<rdf::XURI> const & i_xGraphName,
658 OUString const & i_rFileName,
659 OUString const & i_rBaseURI)
661 const uno::Reference<io::XStream> xStream(
662 i_xStorage->openStreamElement(i_rFileName,
663 embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE),
664 uno::UNO_SET_THROW);
665 const uno::Reference< beans::XPropertySet > xStreamProps(xStream,
666 uno::UNO_QUERY);
667 if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage
668 xStreamProps->setPropertyValue(
669 u"MediaType"_ustr,
670 uno::Any(u"application/rdf+xml"_ustr));
672 const uno::Reference<io::XOutputStream> xOutStream(
673 xStream->getOutputStream(), uno::UNO_SET_THROW );
674 const uno::Reference<rdf::XURI> xBaseURI(
675 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
676 i_rImpl.m_xRepository->exportGraph(rdf::FileFormat::RDF_XML,
677 xOutStream, i_xGraphName, xBaseURI);
680 /** write a metadata file to the storage */
681 static void
682 writeStream(struct DocumentMetadataAccess_Impl & i_rImpl,
683 uno::Reference< embed::XStorage > const & i_xStorage,
684 uno::Reference<rdf::XURI> const & i_xGraphName,
685 OUString const & i_rPath,
686 OUString const & i_rBaseURI)
688 OUString dir;
689 OUString rest;
690 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
691 try {
692 if (dir.isEmpty()) {
693 exportStream(i_rImpl, i_xStorage, i_xGraphName, i_rPath,
694 i_rBaseURI);
695 } else {
696 const uno::Reference<embed::XStorage> xDir(
697 i_xStorage->openStorageElement(dir,
698 embed::ElementModes::WRITE));
699 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
700 uno::UNO_QUERY_THROW);
701 try {
702 OUString mimeType;
703 xDirProps->getPropertyValue(
704 utl::MediaDescriptor::PROP_MEDIATYPE )
705 >>= mimeType;
706 if (mimeType.startsWith(s_odfmime)) {
707 SAL_WARN("sfx", "writeStream: refusing to recurse into embedded document");
708 return;
710 } catch (const uno::Exception &) { }
711 writeStream(i_rImpl, xDir, i_xGraphName, rest, i_rBaseURI+dir+"/");
712 uno::Reference<embed::XTransactedObject> const xTransaction(
713 xDir, uno::UNO_QUERY);
714 if (xTransaction.is()) {
715 xTransaction->commit();
718 } catch (const uno::RuntimeException &) {
719 throw;
720 } catch (const io::IOException &) {
721 throw;
725 static void
726 initLoading(struct DocumentMetadataAccess_Impl & i_rImpl,
727 const uno::Reference< embed::XStorage > & i_xStorage,
728 const uno::Reference<rdf::XURI> & i_xBaseURI,
729 const uno::Reference<task::XInteractionHandler> & i_xHandler)
731 retry:
732 // clear old data
733 i_rImpl.m_xManifest.clear();
734 // init BaseURI
735 i_rImpl.m_xBaseURI = i_xBaseURI;
737 // create repository
738 i_rImpl.m_xRepository.clear();
739 i_rImpl.m_xRepository.set(rdf::Repository::create(i_rImpl.m_xContext),
740 uno::UNO_SET_THROW);
742 // try to delay raising errors until after initialization is done
743 uno::Any rterr;
744 ucb::InteractiveAugmentedIOException iaioe;
745 bool err(false);
747 const uno::Reference <rdf::XURI> xManifest(
748 getURIForStream(i_rImpl, s_manifest));
749 try {
750 readStream(i_rImpl, i_xStorage, s_manifest, i_xBaseURI->getStringValue());
751 } catch (const ucb::InteractiveAugmentedIOException & e) {
752 // no manifest.rdf: this is not an error in ODF < 1.2
753 if (ucb::IOErrorCode_NOT_EXISTING_PATH != e.Code) {
754 iaioe = e;
755 err = true;
757 } catch (const uno::Exception & e) {
758 rterr <<= e;
761 // init manifest graph
762 const uno::Reference<rdf::XNamedGraph> xManifestGraph(
763 i_rImpl.m_xRepository->getGraph(xManifest));
764 i_rImpl.m_xManifest.set(xManifestGraph.is() ? xManifestGraph :
765 i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_SET_THROW);
767 // document statement
768 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
769 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
770 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext));
772 OSL_ENSURE(i_rImpl.m_xBaseURI.is(), "base URI is null");
773 OSL_ENSURE(i_rImpl.m_xRepository.is(), "repository is null");
774 OSL_ENSURE(i_rImpl.m_xManifest.is(), "manifest is null");
776 if (rterr.hasValue()) {
777 throw lang::WrappedTargetRuntimeException(
778 u"DocumentMetadataAccess::loadMetadataFromStorage: "
779 "exception"_ustr, nullptr, rterr);
782 if (err && handleError(iaioe, i_xHandler))
783 goto retry;
786 /** init Impl struct */
787 static void init(struct DocumentMetadataAccess_Impl & i_rImpl)
789 try {
791 i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph(
792 getURIForStream(i_rImpl, s_manifest)),
793 uno::UNO_SET_THROW);
795 // insert the document statement
796 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
797 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
798 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext));
799 } catch (const uno::Exception &) {
800 css::uno::Any anyEx = cppu::getCaughtException();
801 throw lang::WrappedTargetRuntimeException(
802 u"init: unexpected exception"_ustr, nullptr,
803 anyEx);
806 // add top-level content files
807 if (!addContentOrStylesFileImpl(i_rImpl, s_content)) {
808 throw uno::RuntimeException();
810 if (!addContentOrStylesFileImpl(i_rImpl, s_styles)) {
811 throw uno::RuntimeException();
816 DocumentMetadataAccess::DocumentMetadataAccess(
817 uno::Reference< uno::XComponentContext > const & i_xContext,
818 const SfxObjectShell & i_rRegistrySupplier)
819 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
821 // no initialization: must call loadFrom...
824 DocumentMetadataAccess::DocumentMetadataAccess(
825 uno::Reference< uno::XComponentContext > const & i_xContext,
826 const SfxObjectShell & i_rRegistrySupplier,
827 OUString const & i_rURI)
828 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
830 OSL_ENSURE(!i_rURI.isEmpty(), "DMA::DMA: no URI given!");
831 OSL_ENSURE(i_rURI.endsWith("/"), "DMA::DMA: URI without / given!");
832 if (!i_rURI.endsWith("/")) throw uno::RuntimeException();
833 m_pImpl->m_xBaseURI.set(rdf::URI::create(m_pImpl->m_xContext, i_rURI));
834 m_pImpl->m_xRepository.set(rdf::Repository::create(m_pImpl->m_xContext),
835 uno::UNO_SET_THROW);
837 // init repository
838 init(*m_pImpl);
840 OSL_ENSURE(m_pImpl->m_xBaseURI.is(), "base URI is null");
841 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository is null");
842 OSL_ENSURE(m_pImpl->m_xManifest.is(), "manifest is null");
845 DocumentMetadataAccess::~DocumentMetadataAccess()
849 // css::rdf::XRepositorySupplier:
850 uno::Reference< rdf::XRepository > SAL_CALL
851 DocumentMetadataAccess::getRDFRepository()
853 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository not initialized");
854 return m_pImpl->m_xRepository;
857 // css::rdf::XNode:
858 OUString SAL_CALL
859 DocumentMetadataAccess::getStringValue()
861 return m_pImpl->m_xBaseURI->getStringValue();
864 // css::rdf::XURI:
865 OUString SAL_CALL
866 DocumentMetadataAccess::getNamespace()
868 return m_pImpl->m_xBaseURI->getNamespace();
871 OUString SAL_CALL
872 DocumentMetadataAccess::getLocalName()
874 return m_pImpl->m_xBaseURI->getLocalName();
877 // css::rdf::XDocumentMetadataAccess:
878 uno::Reference< rdf::XMetadatable > SAL_CALL
879 DocumentMetadataAccess::getElementByMetadataReference(
880 const css::beans::StringPair & i_rReference)
882 const IXmlIdRegistry * pReg(
883 m_pImpl->m_rXmlIdRegistrySupplier.GetXmlIdRegistry() );
884 if (!pReg) {
885 throw uno::RuntimeException(
886 u"DocumentMetadataAccess::getElementByXmlId: no registry"_ustr, *this);
888 return pReg->GetElementByMetadataReference(i_rReference);
891 uno::Reference< rdf::XMetadatable > SAL_CALL
892 DocumentMetadataAccess::getElementByURI(
893 const uno::Reference< rdf::XURI > & i_xURI )
895 if (!i_xURI.is()) {
896 throw lang::IllegalArgumentException(
897 u"DocumentMetadataAccess::getElementByURI: URI is null"_ustr, *this, 0);
900 const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
901 const OUString name( i_xURI->getStringValue() );
902 if (!name.match(baseURI)) {
903 return nullptr;
905 OUString path;
906 OUString idref;
907 if (!splitXmlId(name.subView(baseURI.getLength()), path, idref)) {
908 return nullptr;
911 return getElementByMetadataReference( beans::StringPair(path, idref) );
914 uno::Sequence<uno::Reference<rdf::XURI>> SAL_CALL
915 DocumentMetadataAccess::getMetadataGraphsWithType(const uno::Reference<rdf::XURI>& i_xType)
917 if (!i_xType.is())
919 throw lang::IllegalArgumentException(u"DocumentMetadataAccess::getMetadataGraphsWithType: "
920 "type is null"_ustr,
921 *this, 0);
924 return ::comphelper::containerToSequence(getAllParts(*m_pImpl, i_xType));
927 uno::Reference<rdf::XURI> SAL_CALL
928 DocumentMetadataAccess::addMetadataFile(const OUString & i_rFileName,
929 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
931 if (!isFileNameValid(i_rFileName)) {
932 throw lang::IllegalArgumentException(
933 u"DocumentMetadataAccess::addMetadataFile: invalid FileName"_ustr,
934 *this, 0);
936 if (isReservedFile(i_rFileName)) {
937 throw lang::IllegalArgumentException(
938 u"DocumentMetadataAccess::addMetadataFile:"
939 "invalid FileName: reserved"_ustr, *this, 0);
941 if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
942 [](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
943 throw lang::IllegalArgumentException(
944 u"DocumentMetadataAccess::addMetadataFile: "
945 "null type"_ustr, *this, 2);
948 const uno::Reference<rdf::XURI> xGraphName(
949 getURIForStream(*m_pImpl, i_rFileName) );
951 try {
952 m_pImpl->m_xRepository->createGraph(xGraphName);
953 } catch (const rdf::RepositoryException &) {
954 css::uno::Any anyEx = cppu::getCaughtException();
955 throw lang::WrappedTargetRuntimeException(
956 u"DocumentMetadataAccess::addMetadataFile: exception"_ustr,
957 *this, anyEx);
958 // note: all other exceptions are propagated
961 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
962 return xGraphName;
965 uno::Reference<rdf::XURI> SAL_CALL
966 DocumentMetadataAccess::importMetadataFile(::sal_Int16 i_Format,
967 const uno::Reference< io::XInputStream > & i_xInStream,
968 const OUString & i_rFileName,
969 const uno::Reference< rdf::XURI > & i_xBaseURI,
970 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
972 if (!isFileNameValid(i_rFileName)) {
973 throw lang::IllegalArgumentException(
974 u"DocumentMetadataAccess::importMetadataFile: invalid FileName"_ustr,
975 *this, 0);
977 if (isReservedFile(i_rFileName)) {
978 throw lang::IllegalArgumentException(
979 u"DocumentMetadataAccess::importMetadataFile:"
980 "invalid FileName: reserved"_ustr, *this, 0);
982 if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
983 [](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
984 throw lang::IllegalArgumentException(
985 u"DocumentMetadataAccess::importMetadataFile: null type"_ustr,
986 *this, 5);
989 const uno::Reference<rdf::XURI> xGraphName(
990 getURIForStream(*m_pImpl, i_rFileName) );
992 try {
993 m_pImpl->m_xRepository->importGraph(
994 i_Format, i_xInStream, xGraphName, i_xBaseURI);
995 } catch (const rdf::RepositoryException &) {
996 css::uno::Any anyEx = cppu::getCaughtException();
997 throw lang::WrappedTargetRuntimeException(
998 u"DocumentMetadataAccess::importMetadataFile: "
999 "RepositoryException"_ustr, *this, anyEx);
1000 // note: all other exceptions are propagated
1003 // add to manifest
1004 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
1005 return xGraphName;
1008 void SAL_CALL
1009 DocumentMetadataAccess::removeMetadataFile(
1010 const uno::Reference< rdf::XURI > & i_xGraphName)
1012 try {
1013 m_pImpl->m_xRepository->destroyGraph(i_xGraphName);
1014 } catch (const rdf::RepositoryException &) {
1015 css::uno::Any anyEx = cppu::getCaughtException();
1016 throw lang::WrappedTargetRuntimeException(
1017 u"DocumentMetadataAccess::removeMetadataFile: "
1018 "RepositoryException"_ustr, *this, anyEx);
1019 // note: all other exceptions are propagated
1022 // remove file from manifest
1023 removeFile(*m_pImpl, i_xGraphName);
1026 void SAL_CALL
1027 DocumentMetadataAccess::addContentOrStylesFile(
1028 const OUString & i_rFileName)
1030 if (!isFileNameValid(i_rFileName)) {
1031 throw lang::IllegalArgumentException(
1032 u"DocumentMetadataAccess::addContentOrStylesFile: "
1033 "invalid FileName"_ustr, *this, 0);
1036 if (!addContentOrStylesFileImpl(*m_pImpl, i_rFileName)) {
1037 throw lang::IllegalArgumentException(
1038 u"DocumentMetadataAccess::addContentOrStylesFile: "
1039 "invalid FileName: must end with content.xml or styles.xml"_ustr,
1040 *this, 0);
1044 void SAL_CALL
1045 DocumentMetadataAccess::removeContentOrStylesFile(
1046 const OUString & i_rFileName)
1048 if (!isFileNameValid(i_rFileName)) {
1049 throw lang::IllegalArgumentException(
1050 u"DocumentMetadataAccess::removeContentOrStylesFile: "
1051 "invalid FileName"_ustr, *this, 0);
1054 try {
1055 const uno::Reference<rdf::XURI> xPart(
1056 getURIForStream(*m_pImpl, i_rFileName) );
1057 const uno::Reference<container::XEnumeration> xEnum(
1058 m_pImpl->m_xManifest->getStatements( m_pImpl->m_xBaseURI,
1059 getURI<rdf::URIs::PKG_HASPART>(m_pImpl->m_xContext),
1060 xPart),
1061 uno::UNO_SET_THROW);
1062 if (!xEnum->hasMoreElements()) {
1063 throw container::NoSuchElementException(
1064 "DocumentMetadataAccess::removeContentOrStylesFile: "
1065 "cannot find stream in manifest graph: " + i_rFileName,
1066 *this);
1069 // remove file from manifest
1070 removeFile(*m_pImpl, xPart);
1072 } catch (const uno::RuntimeException &) {
1073 throw;
1074 } catch (const uno::Exception &) {
1075 css::uno::Any anyEx = cppu::getCaughtException();
1076 throw lang::WrappedTargetRuntimeException(
1077 u"DocumentMetadataAccess::removeContentOrStylesFile: exception"_ustr,
1078 *this, anyEx);
1082 void SAL_CALL DocumentMetadataAccess::loadMetadataFromStorage(
1083 const uno::Reference< embed::XStorage > & i_xStorage,
1084 const uno::Reference<rdf::XURI> & i_xBaseURI,
1085 const uno::Reference<task::XInteractionHandler> & i_xHandler)
1087 if (!i_xStorage.is()) {
1088 throw lang::IllegalArgumentException(
1089 u"DocumentMetadataAccess::loadMetadataFromStorage: "
1090 "storage is null"_ustr, *this, 0);
1092 if (!i_xBaseURI.is()) {
1093 throw lang::IllegalArgumentException(
1094 u"DocumentMetadataAccess::loadMetadataFromStorage: "
1095 "base URI is null"_ustr, *this, 1);
1097 const OUString baseURI( i_xBaseURI->getStringValue());
1098 if (baseURI.indexOf('#') >= 0) {
1099 throw lang::IllegalArgumentException(
1100 u"DocumentMetadataAccess::loadMetadataFromStorage: "
1101 "base URI not absolute"_ustr, *this, 1);
1103 if (!baseURI.endsWith("/")) {
1104 throw lang::IllegalArgumentException(
1105 u"DocumentMetadataAccess::loadMetadataFromStorage: "
1106 "base URI does not end with slash"_ustr, *this, 1);
1109 initLoading(*m_pImpl, i_xStorage, i_xBaseURI, i_xHandler);
1111 std::set< OUString > StgFiles;
1112 collectFilesFromStorage(i_xStorage, StgFiles);
1114 std::vector< OUString > MfstMetadataFiles;
1116 try {
1117 const ::std::vector< uno::Reference< rdf::XURI > > parts(
1118 getAllParts(*m_pImpl) );
1119 const uno::Reference<rdf::XURI>& xContentFile(
1120 getURI<rdf::URIs::ODF_CONTENTFILE>(m_pImpl->m_xContext));
1121 const uno::Reference<rdf::XURI>& xStylesFile(
1122 getURI<rdf::URIs::ODF_STYLESFILE>(m_pImpl->m_xContext));
1123 const uno::Reference<rdf::XURI>& xMetadataFile(
1124 getURI<rdf::URIs::PKG_METADATAFILE>(m_pImpl->m_xContext));
1125 const sal_Int32 len( baseURI.getLength() );
1126 for (const auto& rxPart : parts) {
1127 const OUString name(rxPart->getStringValue());
1128 if (!name.match(baseURI)) {
1129 SAL_WARN("sfx", "loadMetadataFromStorage: graph not in document: " << name);
1130 continue;
1132 const OUString relName( name.copy(len) );
1133 if (relName == s_manifest) {
1134 SAL_WARN("sfx", "loadMetadataFromStorage: found ourselves a recursive manifest!");
1135 continue;
1137 // remove found items from StgFiles
1138 StgFiles.erase(relName);
1139 if (isContentFile(relName)) {
1140 if (!isPartOfType(*m_pImpl, rxPart, xContentFile)) {
1141 const uno::Reference <rdf::XURI> xName(
1142 getURIForStream(*m_pImpl, relName) );
1143 // add missing type statement
1144 m_pImpl->m_xManifest->addStatement(xName,
1145 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1146 xContentFile);
1148 } else if (isStylesFile(relName)) {
1149 if (!isPartOfType(*m_pImpl, rxPart, xStylesFile)) {
1150 const uno::Reference <rdf::XURI> xName(
1151 getURIForStream(*m_pImpl, relName) );
1152 // add missing type statement
1153 m_pImpl->m_xManifest->addStatement(xName,
1154 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1155 xStylesFile);
1157 } else if (isReservedFile(relName)) {
1158 SAL_WARN("sfx", "loadMetadataFromStorage: reserved file name in manifest");
1159 } else {
1160 if (isPartOfType(*m_pImpl, rxPart, xMetadataFile)) {
1161 MfstMetadataFiles.push_back(relName);
1163 // do not add statement for MetadataFile; it could be
1164 // something else! just ignore it...
1167 } catch (const uno::RuntimeException &) {
1168 throw;
1169 } catch (const uno::Exception &) {
1170 css::uno::Any anyEx = cppu::getCaughtException();
1171 throw lang::WrappedTargetRuntimeException(
1172 u"DocumentMetadataAccess::loadMetadataFromStorage: "
1173 "exception"_ustr, *this, anyEx);
1176 for (const auto& aStgFile : StgFiles)
1177 addContentOrStylesFileImpl(*m_pImpl, aStgFile);
1179 for (const auto& aMfstMetadataFile : MfstMetadataFiles)
1180 importFile(*m_pImpl, i_xStorage, baseURI, i_xHandler, aMfstMetadataFile);
1183 void SAL_CALL DocumentMetadataAccess::storeMetadataToStorage(
1184 const uno::Reference< embed::XStorage > & i_xStorage)
1186 if (!i_xStorage.is()) {
1187 throw lang::IllegalArgumentException(
1188 u"DocumentMetadataAccess::storeMetadataToStorage: "
1189 "storage is null"_ustr, *this, 0);
1192 // export manifest
1193 const uno::Reference <rdf::XURI> xManifest(
1194 getURIForStream(*m_pImpl, s_manifest) );
1195 const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
1196 try {
1197 writeStream(*m_pImpl, i_xStorage, xManifest, s_manifest, baseURI);
1198 } catch (const uno::RuntimeException &) {
1199 throw;
1200 } catch (const io::IOException &) {
1201 css::uno::Any anyEx = cppu::getCaughtException();
1202 throw lang::WrappedTargetException(
1203 u"storeMetadataToStorage: IO exception"_ustr, *this, anyEx);
1204 } catch (const uno::Exception &) {
1205 css::uno::Any anyEx = cppu::getCaughtException();
1206 throw lang::WrappedTargetRuntimeException(
1207 u"storeMetadataToStorage: exception"_ustr, *this, anyEx);
1210 // export metadata streams
1211 try {
1212 const uno::Sequence<uno::Reference<rdf::XURI> > graphs(
1213 m_pImpl->m_xRepository->getGraphNames());
1214 const sal_Int32 len( baseURI.getLength() );
1215 for (const uno::Reference<rdf::XURI>& xName : graphs) {
1216 const OUString name(xName->getStringValue());
1217 if (!name.match(baseURI)) {
1218 SAL_WARN("sfx", "storeMetadataToStorage: graph not in document: " << name);
1219 continue;
1221 const OUString relName( name.copy(len) );
1222 if (relName == s_manifest) {
1223 continue;
1225 if (!isFileNameValid(relName) || isReservedFile(relName)) {
1226 SAL_WARN("sfx", "storeMetadataToStorage: invalid file name: " << relName);
1227 continue;
1229 try {
1230 writeStream(*m_pImpl, i_xStorage, xName, relName, baseURI);
1231 } catch (const uno::RuntimeException &) {
1232 throw;
1233 } catch (const io::IOException &) {
1234 css::uno::Any anyEx = cppu::getCaughtException();
1235 throw lang::WrappedTargetException(
1236 u"storeMetadataToStorage: IO exception"_ustr,
1237 *this, anyEx);
1238 } catch (const uno::Exception &) {
1239 css::uno::Any anyEx = cppu::getCaughtException();
1240 throw lang::WrappedTargetRuntimeException(
1241 u"storeMetadataToStorage: exception"_ustr,
1242 *this, anyEx);
1245 } catch (const rdf::RepositoryException &) {
1246 css::uno::Any anyEx = cppu::getCaughtException();
1247 throw lang::WrappedTargetRuntimeException(
1248 u"storeMetadataToStorage: exception"_ustr, *this, anyEx);
1252 void SAL_CALL
1253 DocumentMetadataAccess::loadMetadataFromMedium(
1254 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1256 uno::Reference<io::XInputStream> xIn;
1257 utl::MediaDescriptor md(i_rMedium);
1258 OUString URL;
1259 md[ utl::MediaDescriptor::PROP_URL ] >>= URL;
1260 OUString BaseURL;
1261 md[ utl::MediaDescriptor::PROP_DOCUMENTBASEURL ] >>= BaseURL;
1262 if (md.addInputStream()) {
1263 md[ utl::MediaDescriptor::PROP_INPUTSTREAM ] >>= xIn;
1265 if (!xIn.is() && URL.isEmpty()) {
1266 throw lang::IllegalArgumentException(
1267 u"DocumentMetadataAccess::loadMetadataFromMedium: "
1268 "invalid medium: no URL, no input stream"_ustr, *this, 0);
1270 uno::Reference<embed::XStorage> xStorage;
1271 try {
1272 if (xIn.is()) {
1273 xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
1274 xIn, m_pImpl->m_xContext);
1275 } else { // fallback to url
1276 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1277 URL, embed::ElementModes::READ, m_pImpl->m_xContext);
1279 } catch (const uno::RuntimeException &) {
1280 throw;
1281 } catch (const io::IOException &) {
1282 throw;
1283 } catch (const uno::Exception &) {
1284 css::uno::Any anyEx = cppu::getCaughtException();
1285 throw lang::WrappedTargetException(
1286 u"DocumentMetadataAccess::loadMetadataFromMedium: "
1287 "exception"_ustr, *this, anyEx);
1289 if (!xStorage.is()) {
1290 throw uno::RuntimeException(
1291 u"DocumentMetadataAccess::loadMetadataFromMedium: "
1292 "cannot get Storage"_ustr, *this);
1294 uno::Reference<rdf::XURI> xBaseURI;
1295 try {
1296 xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, BaseURL);
1297 } catch (const uno::Exception &) {
1298 // fall back to URL
1299 try {
1300 xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, URL);
1301 } catch (const uno::Exception &) {
1302 OSL_FAIL("cannot create base URI");
1305 uno::Reference<task::XInteractionHandler> xIH;
1306 md[ utl::MediaDescriptor::PROP_INTERACTIONHANDLER ] >>= xIH;
1307 loadMetadataFromStorage(xStorage, xBaseURI, xIH);
1310 void SAL_CALL
1311 DocumentMetadataAccess::storeMetadataToMedium(
1312 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1314 utl::MediaDescriptor md(i_rMedium);
1315 OUString URL;
1316 md[ utl::MediaDescriptor::PROP_URL ] >>= URL;
1317 if (URL.isEmpty()) {
1318 throw lang::IllegalArgumentException(
1319 u"DocumentMetadataAccess::storeMetadataToMedium: "
1320 "invalid medium: no URL"_ustr, *this, 0);
1323 SfxMedium aMedium(i_rMedium);
1324 uno::Reference<embed::XStorage> xStorage(aMedium.GetOutputStorage());
1326 bool sfx(false);
1327 if (xStorage.is()) {
1328 sfx = true;
1329 } else {
1330 xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
1331 URL, embed::ElementModes::WRITE, m_pImpl->m_xContext);
1334 if (!xStorage.is()) {
1335 throw uno::RuntimeException(
1336 u"DocumentMetadataAccess::storeMetadataToMedium: "
1337 "cannot get Storage"_ustr, *this);
1339 // set MIME type of the storage
1340 utl::MediaDescriptor::const_iterator iter
1341 = md.find(utl::MediaDescriptor::PROP_MEDIATYPE);
1342 if (iter != md.end()) {
1343 uno::Reference< beans::XPropertySet > xProps(xStorage,
1344 uno::UNO_QUERY_THROW);
1345 try {
1346 // this is NOT supported in FileSystemStorage
1347 xProps->setPropertyValue(
1348 utl::MediaDescriptor::PROP_MEDIATYPE,
1349 iter->second);
1350 } catch (const uno::Exception &) { }
1352 storeMetadataToStorage(xStorage);
1354 if (!sfx)
1355 return;
1357 const bool bOk = aMedium.Commit();
1358 aMedium.Close();
1359 if ( !bOk ) {
1360 ErrCodeMsg nError = aMedium.GetErrorIgnoreWarning();
1361 if ( nError == ERRCODE_NONE ) {
1362 nError = ERRCODE_IO_GENERAL;
1364 task::ErrorCodeIOException ex(
1365 "DocumentMetadataAccess::storeMetadataToMedium Commit failed: " + nError.toString(),
1366 uno::Reference< uno::XInterface >(), sal_uInt32(nError.GetCode()));
1367 throw lang::WrappedTargetException(OUString(), *this,
1368 uno::Any(ex));
1372 } // namespace sfx2
1374 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */