LibreOffice Module sfx2 (master) 1
DocumentMetadataAccess.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
22
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>
38
39#include <rtl/ustrbuf.hxx>
40#include <rtl/uri.hxx>
41#include <rtl/bootstrap.hxx>
42#include <sal/log.hxx>
43
49#include <o3tl/string_view.hxx>
50
51#include <sfx2/docfile.hxx>
53#include <sfx2/objsh.hxx>
55
56#include <libxml/tree.h>
57
58#include <utility>
59#include <vector>
60#include <set>
61#include <string_view>
62
63#include <com/sun/star/uri/XUriReference.hpp>
64#include <com/sun/star/uri/UriReferenceFactory.hpp>
65
66
67/*
68 Note: in the context of this implementation, all rdf.QueryExceptions and
69 rdf.RepositoryExceptions are RuntimeExceptions, and will be reported as such.
70
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.
74 */
75
76using namespace ::com::sun::star;
77
78namespace sfx2 {
79
80
81bool isValidNCName(std::u16string_view i_rIdref)
82{
83 const OString id(
84 OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8) );
85 return !(xmlValidateNCName(
86 reinterpret_cast<const unsigned char*>(id.getStr()), 0));
87}
88
89
90constexpr OUStringLiteral s_content = u"content.xml";
91constexpr OUStringLiteral s_styles = u"styles.xml";
92constexpr OUStringLiteral s_manifest = u"manifest.rdf";
93const char s_odfmime [] = "application/vnd.oasis.opendocument.";
94
95
96static bool isContentFile(std::u16string_view i_rPath)
97{
98 return i_rPath == s_content;
99}
100
101static bool isStylesFile (std::u16string_view i_rPath)
102{
103 return i_rPath == s_styles;
104}
105
106bool isValidXmlId(std::u16string_view i_rStreamName,
107 std::u16string_view i_rIdref)
108{
109 return isValidNCName(i_rIdref)
110 && (isContentFile(i_rStreamName) || isStylesFile(i_rStreamName));
111}
112
113static bool isReservedFile(std::u16string_view i_rPath)
114{
115 return isContentFile(i_rPath) || isStylesFile(i_rPath) || i_rPath == u"meta.xml" || i_rPath == u"settings.xml";
116}
117
118
119uno::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)
123{
124 if (!i_xContext.is() || (!i_xModel.is() && i_rPkgURI.isEmpty())) {
125 throw uno::RuntimeException();
126 }
127
128 OUString pkgURI(i_rPkgURI);
129
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())
134 {
135 assert(i_xModel.is());
136 uno::Reference<frame::XTransientDocumentsDocumentContentIdentifierFactory>
137 const xTDDCIF(
138 i_xContext->getServiceManager()->createInstanceWithContext(
139 "com.sun.star.ucb.TransientDocumentsContentProvider",
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())
146 {
147 throw uno::RuntimeException("createBaseURI: cannot create ContentIdentifier");
148 }
149 pkgURI = xContentId->getContentIdentifier();
150 assert(!pkgURI.isEmpty());
151 if (!pkgURI.isEmpty() && !pkgURI.endsWith("/"))
152 {
153 pkgURI += "/";
154 }
155 }
156
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))
160 {
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();
167 }
168 ::rtl::Bootstrap::expandMacros(pkgURI);
169 }
170 }
171
172 const uno::Reference<uri::XUriReferenceFactory> xUriFactory =
173 uri::UriReferenceFactory::create( i_xContext);
174 uno::Reference< uri::XUriReference > xBaseURI;
175
176 const uno::Reference< uri::XUriReference > xPkgURI(
177 xUriFactory->parse(pkgURI), uno::UNO_SET_THROW );
178 xPkgURI->clearFragment();
179
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 );
185 }
186 OUStringBuffer buf(64);
187 if (!xBaseURI->getUriReference().endsWith("/"))
188 {
189 const sal_Int32 count( xBaseURI->getPathSegmentCount() );
190 if (count > 0)
191 {
192 buf.append(xBaseURI->getPathSegment(count - 1));
193 }
194 buf.append('/');
195 }
196 if (!i_rSubDocument.empty())
197 {
198 buf.append(OUString::Concat(i_rSubDocument) + "/");
199 }
200 if (!buf.isEmpty())
201 {
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);
208 }
209
210 return rdf::URI::create(i_xContext, xBaseURI->getUriReference());
211}
212
213
215{
216 // note: these are all initialized in constructor, and loadFromStorage
217 const uno::Reference<uno::XComponentContext> m_xContext;
219 uno::Reference<rdf::XURI> m_xBaseURI;
220 uno::Reference<rdf::XRepository> m_xRepository;
221 uno::Reference<rdf::XNamedGraph> m_xManifest;
223 uno::Reference<uno::XComponentContext> i_xContext,
224 SfxObjectShell const & i_rRegistrySupplier)
225 : m_xContext(std::move(i_xContext))
226 , m_rXmlIdRegistrySupplier(i_rRegistrySupplier)
227 {
228 OSL_ENSURE(m_xContext.is(), "context null");
229 }
230};
231
232// this is... a hack.
233template<sal_Int16 Constant>
234static uno::Reference<rdf::XURI> const &
235getURI(uno::Reference< uno::XComponentContext > const & i_xContext)
236{
237 static uno::Reference< rdf::XURI > xURI(
238 rdf::URI::createKnown(i_xContext, Constant), uno::UNO_SET_THROW);
239 return xURI;
240}
241
242
244static bool isFileNameValid(std::u16string_view i_rFileName)
245{
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
256 segment, false)) // no invalid characters
257 return false;
258 } while (idx >= 0);
259 return true;
260}
261
263static bool
264splitPath(OUString const & i_rPath,
265 OUString & o_rDir, OUString& o_rRest)
266{
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;
279 }
280}
281
282static bool
283splitXmlId(std::u16string_view i_XmlId,
284 OUString & o_StreamName, OUString& o_Idref )
285{
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);
292}
293
294
295static uno::Reference<rdf::XURI>
297 OUString const& i_rPath)
298{
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;
304}
305
308static void
310 uno::Reference<rdf::XURI> const& i_xType,
311 OUString const & i_rPath,
312 const uno::Sequence < uno::Reference< rdf::XURI > > * i_pTypes)
313{
314 try {
315 const uno::Reference<rdf::XURI> xURI( getURIForStream(
316 i_rImpl, i_rPath) );
317
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);
329 }
330 }
331 } catch (const uno::RuntimeException &) {
332 throw;
333 } catch (const uno::Exception &) {
334 css::uno::Any anyEx = cppu::getCaughtException();
335 throw lang::WrappedTargetRuntimeException(
336 "addFile: exception", /*this*/nullptr, anyEx);
337 }
338}
339
341static bool
343 const OUString & i_rPath)
344{
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;
352 }
353 addFile(i_rImpl, xType, i_rPath, nullptr);
354 return true;
355}
356
358static void
360 const OUString & i_rPath,
361 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
362{
363 addFile(i_rImpl,
364 getURI<rdf::URIs::PKG_METADATAFILE>(i_rImpl.m_xContext),
365 i_rPath, &i_rTypes);
366}
367
369static void
371 uno::Reference<rdf::XURI> const& i_xPart)
372{
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 "removeFile: exception",
386 nullptr, anyEx);
387 }
388}
389
390static ::std::vector< uno::Reference< rdf::XURI > >
392{
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();
403 }
404 const uno::Reference<rdf::XURI> xPart(stmt.Object,
405 uno::UNO_QUERY);
406 if (!xPart.is()) continue;
407 ret.push_back(xPart);
408 }
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 "getAllParts: exception",
416 nullptr, anyEx);
417 }
418}
419
420static bool
422 uno::Reference<rdf::XURI> const & i_xPart,
423 uno::Reference<rdf::XURI> const & i_xType)
424{
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 "isPartOfType: exception",
439 nullptr, anyEx);
440 }
441}
442
443static ::std::vector<uno::Reference<rdf::XURI>>
445 const uno::Reference<rdf::XURI>& i_xType)
446{
447 ::std::vector<uno::Reference<rdf::XURI>> ret;
448 try
449 {
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())
456 {
457 rdf::Statement stmt;
458 if (!(xEnum->nextElement() >>= stmt))
459 {
460 throw uno::RuntimeException();
461 }
462 const uno::Reference<rdf::XURI> xPart(stmt.Object, uno::UNO_QUERY);
463 if (!xPart.is())
464 continue;
465
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);
472 }
473 return ret;
474 }
475 catch (const uno::RuntimeException&)
476 {
477 throw;
478 }
479 catch (const uno::Exception& e)
480 {
481 throw lang::WrappedTargetRuntimeException("getAllParts: exception", nullptr,
482 uno::Any(e));
483 }
484}
485
486static ucb::InteractiveAugmentedIOException
487mkException( OUString const & i_rMessage,
488 ucb::IOErrorCode const i_ErrorCode,
489 OUString const & i_rUri, OUString const & i_rResource)
490{
491 ucb::InteractiveAugmentedIOException iaioe;
492 iaioe.Message = i_rMessage;
493 iaioe.Classification = task::InteractionClassification_ERROR;
494 iaioe.Code = i_ErrorCode;
495
496 const beans::PropertyValue uriProp("Uri",
497 -1, uno::Any(i_rUri), static_cast<beans::PropertyState>(0));
498 const beans::PropertyValue rnProp(
499 "ResourceName",
500 -1, uno::Any(i_rResource), static_cast<beans::PropertyState>(0));
501 iaioe.Arguments = { uno::Any(uriProp), uno::Any(rnProp) };
502 return iaioe;
503}
504
513static bool
514handleError( ucb::InteractiveAugmentedIOException const & i_rException,
515 const uno::Reference<task::XInteractionHandler> & i_xHandler)
516{
517 if (!i_xHandler.is()) {
518 throw lang::WrappedTargetException(
519 "DocumentMetadataAccess::loadMetadataFromStorage: exception",
520 /* *this*/ nullptr, uno::Any(i_rException));
521 }
522
524 new ::comphelper::OInteractionRequest(uno::Any(i_rException)) );
526 new ::comphelper::OInteractionRetry );
528 new ::comphelper::OInteractionApprove );
530 new ::comphelper::OInteractionAbort );
531
532 pRequest->addContinuation( pApprove );
533 pRequest->addContinuation( pAbort );
534 // actually call the handler
535 i_xHandler->handle( pRequest );
536 if (pRetry->wasSelected()) {
537 return true;
538 } else if (pApprove->wasSelected()) {
539 return false;
540 } else {
541 OSL_ENSURE(pAbort->wasSelected(), "no continuation selected?");
542 throw lang::WrappedTargetException(
543 "DocumentMetadataAccess::loadMetadataFromStorage: exception",
544 /* *this*/ nullptr, uno::Any(i_rException));
545 }
546}
547
550static void
551collectFilesFromStorage(uno::Reference<embed::XStorage> const& i_xStorage,
552 std::set< OUString > & o_rFiles)
553{
554 try {
555 if (i_xStorage->hasByName(s_content) &&
556 i_xStorage->isStreamElement(s_content))
557 {
558 o_rFiles.insert(s_content);
559 }
560 if (i_xStorage->hasByName(s_styles) &&
561 i_xStorage->isStreamElement(s_styles))
562 {
563 o_rFiles.insert(s_styles);
564 }
565 } catch (const uno::Exception &) {
566 TOOLS_WARN_EXCEPTION("sfx", "collectFilesFromStorage");
567 }
568}
569
571static void
573 uno::Reference< embed::XStorage > const & i_xStorage,
574 OUString const & i_rPath,
575 OUString const & i_rBaseURI)
576{
577 try {
578 OUString dir;
579 OUString rest;
580 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
581 if (dir.isEmpty()) {
582 if (!i_xStorage->isStreamElement(i_rPath)) {
583 throw mkException(
584 "readStream: is not a stream",
585 ucb::IOErrorCode_NO_FILE, i_rBaseURI + i_rPath, i_rPath);
586 }
587 const uno::Reference<io::XStream> xStream(
588 i_xStorage->openStreamElement(i_rPath,
589 embed::ElementModes::READ), uno::UNO_SET_THROW);
590 const uno::Reference<io::XInputStream> xInStream(
591 xStream->getInputStream(), uno::UNO_SET_THROW );
592 const uno::Reference<rdf::XURI> xBaseURI(
593 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
594 const uno::Reference<rdf::XURI> xURI(
595 rdf::URI::createNS(i_rImpl.m_xContext,
596 i_rBaseURI, i_rPath));
597 i_rImpl.m_xRepository->importGraph(rdf::FileFormat::RDF_XML,
598 xInStream, xURI, xBaseURI);
599 } else {
600 if (!i_xStorage->isStorageElement(dir)) {
601 throw mkException(
602 "readStream: is not a directory",
603 ucb::IOErrorCode_NO_DIRECTORY, i_rBaseURI + dir, dir);
604 }
605 const uno::Reference<embed::XStorage> xDir(
606 i_xStorage->openStorageElement(dir,
607 embed::ElementModes::READ));
608 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
609 uno::UNO_QUERY_THROW);
610 try {
611 OUString mimeType;
612 xDirProps->getPropertyValue(
614 >>= mimeType;
615 if (mimeType.startsWith(s_odfmime)) {
616 SAL_WARN("sfx", "readStream: refusing to recurse into embedded document");
617 return;
618 }
619 } catch (const uno::Exception &) { }
620 readStream(i_rImpl, xDir, rest, i_rBaseURI+dir+"/" );
621 }
622 } catch (const container::NoSuchElementException & e) {
623 throw mkException(e.Message, ucb::IOErrorCode_NOT_EXISTING_PATH,
624 i_rBaseURI + i_rPath, i_rPath);
625 } catch (const io::IOException & e) {
626 throw mkException(e.Message, ucb::IOErrorCode_CANT_READ,
627 i_rBaseURI + i_rPath, i_rPath);
628 } catch (const rdf::ParseException & e) {
629 throw mkException(e.Message, ucb::IOErrorCode_WRONG_FORMAT,
630 i_rBaseURI + i_rPath, i_rPath);
631 }
632}
633
635static void
637 uno::Reference<embed::XStorage> const & i_xStorage,
638 OUString const & i_rBaseURI,
639 uno::Reference<task::XInteractionHandler> const & i_xHandler,
640 const OUString& i_rPath)
641{
642retry:
643 try {
644 readStream(i_rImpl, i_xStorage, i_rPath, i_rBaseURI);
645 } catch (const ucb::InteractiveAugmentedIOException & e) {
646 if (handleError(e, i_xHandler)) goto retry;
647 } catch (const uno::RuntimeException &) {
648 throw;
649 } catch (const uno::Exception &) {
650 css::uno::Any anyEx = cppu::getCaughtException();
651 throw lang::WrappedTargetRuntimeException(
652 "importFile: exception",
653 nullptr, anyEx);
654 }
655}
656
658static void
660 uno::Reference< embed::XStorage > const & i_xStorage,
661 uno::Reference<rdf::XURI> const & i_xGraphName,
662 OUString const & i_rFileName,
663 OUString const & i_rBaseURI)
664{
665 const uno::Reference<io::XStream> xStream(
666 i_xStorage->openStreamElement(i_rFileName,
667 embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE),
668 uno::UNO_SET_THROW);
669 const uno::Reference< beans::XPropertySet > xStreamProps(xStream,
670 uno::UNO_QUERY);
671 if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage
672 xStreamProps->setPropertyValue(
673 "MediaType",
674 uno::Any(OUString("application/rdf+xml")));
675 }
676 const uno::Reference<io::XOutputStream> xOutStream(
677 xStream->getOutputStream(), uno::UNO_SET_THROW );
678 const uno::Reference<rdf::XURI> xBaseURI(
679 rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
680 i_rImpl.m_xRepository->exportGraph(rdf::FileFormat::RDF_XML,
681 xOutStream, i_xGraphName, xBaseURI);
682}
683
685static void
687 uno::Reference< embed::XStorage > const & i_xStorage,
688 uno::Reference<rdf::XURI> const & i_xGraphName,
689 OUString const & i_rPath,
690 OUString const & i_rBaseURI)
691{
692 OUString dir;
693 OUString rest;
694 if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
695 try {
696 if (dir.isEmpty()) {
697 exportStream(i_rImpl, i_xStorage, i_xGraphName, i_rPath,
698 i_rBaseURI);
699 } else {
700 const uno::Reference<embed::XStorage> xDir(
701 i_xStorage->openStorageElement(dir,
702 embed::ElementModes::WRITE));
703 const uno::Reference< beans::XPropertySet > xDirProps(xDir,
704 uno::UNO_QUERY_THROW);
705 try {
706 OUString mimeType;
707 xDirProps->getPropertyValue(
709 >>= mimeType;
710 if (mimeType.startsWith(s_odfmime)) {
711 SAL_WARN("sfx", "writeStream: refusing to recurse into embedded document");
712 return;
713 }
714 } catch (const uno::Exception &) { }
715 writeStream(i_rImpl, xDir, i_xGraphName, rest, i_rBaseURI+dir+"/");
716 uno::Reference<embed::XTransactedObject> const xTransaction(
717 xDir, uno::UNO_QUERY);
718 if (xTransaction.is()) {
719 xTransaction->commit();
720 }
721 }
722 } catch (const uno::RuntimeException &) {
723 throw;
724 } catch (const io::IOException &) {
725 throw;
726 }
727}
728
729static void
731 const uno::Reference< embed::XStorage > & i_xStorage,
732 const uno::Reference<rdf::XURI> & i_xBaseURI,
733 const uno::Reference<task::XInteractionHandler> & i_xHandler)
734{
735retry:
736 // clear old data
737 i_rImpl.m_xManifest.clear();
738 // init BaseURI
739 i_rImpl.m_xBaseURI = i_xBaseURI;
740
741 // create repository
742 i_rImpl.m_xRepository.clear();
743 i_rImpl.m_xRepository.set(rdf::Repository::create(i_rImpl.m_xContext),
744 uno::UNO_SET_THROW);
745
746 // try to delay raising errors until after initialization is done
747 uno::Any rterr;
748 ucb::InteractiveAugmentedIOException iaioe;
749 bool err(false);
750
751 const uno::Reference <rdf::XURI> xManifest(
752 getURIForStream(i_rImpl, s_manifest));
753 try {
754 readStream(i_rImpl, i_xStorage, s_manifest, i_xBaseURI->getStringValue());
755 } catch (const ucb::InteractiveAugmentedIOException & e) {
756 // no manifest.rdf: this is not an error in ODF < 1.2
757 if (ucb::IOErrorCode_NOT_EXISTING_PATH != e.Code) {
758 iaioe = e;
759 err = true;
760 }
761 } catch (const uno::Exception & e) {
762 rterr <<= e;
763 }
764
765 // init manifest graph
766 const uno::Reference<rdf::XNamedGraph> xManifestGraph(
767 i_rImpl.m_xRepository->getGraph(xManifest));
768 i_rImpl.m_xManifest.set(xManifestGraph.is() ? xManifestGraph :
769 i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_SET_THROW);
770
771 // document statement
772 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
773 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
774 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext));
775
776 OSL_ENSURE(i_rImpl.m_xBaseURI.is(), "base URI is null");
777 OSL_ENSURE(i_rImpl.m_xRepository.is(), "repository is null");
778 OSL_ENSURE(i_rImpl.m_xManifest.is(), "manifest is null");
779
780 if (rterr.hasValue()) {
781 throw lang::WrappedTargetRuntimeException(
782 "DocumentMetadataAccess::loadMetadataFromStorage: "
783 "exception", nullptr, rterr);
784 }
785
786 if (err && handleError(iaioe, i_xHandler))
787 goto retry;
788}
789
791static void init(struct DocumentMetadataAccess_Impl & i_rImpl)
792{
793 try {
794
795 i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph(
796 getURIForStream(i_rImpl, s_manifest)),
797 uno::UNO_SET_THROW);
798
799 // insert the document statement
800 i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
801 getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
802 getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext));
803 } catch (const uno::Exception &) {
804 css::uno::Any anyEx = cppu::getCaughtException();
805 throw lang::WrappedTargetRuntimeException(
806 "init: unexpected exception", nullptr,
807 anyEx);
808 }
809
810 // add top-level content files
811 if (!addContentOrStylesFileImpl(i_rImpl, s_content)) {
812 throw uno::RuntimeException();
813 }
814 if (!addContentOrStylesFileImpl(i_rImpl, s_styles)) {
815 throw uno::RuntimeException();
816 }
817}
818
819
821 uno::Reference< uno::XComponentContext > const & i_xContext,
822 const SfxObjectShell & i_rRegistrySupplier)
823 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
824{
825 // no initialization: must call loadFrom...
826}
827
828DocumentMetadataAccess::DocumentMetadataAccess(
829 uno::Reference< uno::XComponentContext > const & i_xContext,
830 const SfxObjectShell & i_rRegistrySupplier,
831 OUString const & i_rURI)
832 : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
833{
834 OSL_ENSURE(!i_rURI.isEmpty(), "DMA::DMA: no URI given!");
835 OSL_ENSURE(i_rURI.endsWith("/"), "DMA::DMA: URI without / given!");
836 if (!i_rURI.endsWith("/")) throw uno::RuntimeException();
837 m_pImpl->m_xBaseURI.set(rdf::URI::create(m_pImpl->m_xContext, i_rURI));
838 m_pImpl->m_xRepository.set(rdf::Repository::create(m_pImpl->m_xContext),
839 uno::UNO_SET_THROW);
840
841 // init repository
842 init(*m_pImpl);
843
844 OSL_ENSURE(m_pImpl->m_xBaseURI.is(), "base URI is null");
845 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository is null");
846 OSL_ENSURE(m_pImpl->m_xManifest.is(), "manifest is null");
847}
848
849DocumentMetadataAccess::~DocumentMetadataAccess()
850{
851}
852
853// css::rdf::XRepositorySupplier:
854uno::Reference< rdf::XRepository > SAL_CALL
855DocumentMetadataAccess::getRDFRepository()
856{
857 OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository not initialized");
858 return m_pImpl->m_xRepository;
859}
860
861// css::rdf::XNode:
862OUString SAL_CALL
863DocumentMetadataAccess::getStringValue()
864{
865 return m_pImpl->m_xBaseURI->getStringValue();
866}
867
868// css::rdf::XURI:
869OUString SAL_CALL
870DocumentMetadataAccess::getNamespace()
871{
872 return m_pImpl->m_xBaseURI->getNamespace();
873}
874
875OUString SAL_CALL
876DocumentMetadataAccess::getLocalName()
877{
878 return m_pImpl->m_xBaseURI->getLocalName();
879}
880
881// css::rdf::XDocumentMetadataAccess:
882uno::Reference< rdf::XMetadatable > SAL_CALL
883DocumentMetadataAccess::getElementByMetadataReference(
884 const css::beans::StringPair & i_rReference)
885{
886 const IXmlIdRegistry * pReg(
887 m_pImpl->m_rXmlIdRegistrySupplier.GetXmlIdRegistry() );
888 if (!pReg) {
889 throw uno::RuntimeException(
890 "DocumentMetadataAccess::getElementByXmlId: no registry", *this);
891 }
892 return pReg->GetElementByMetadataReference(i_rReference);
893}
894
895uno::Reference< rdf::XMetadatable > SAL_CALL
896DocumentMetadataAccess::getElementByURI(
897 const uno::Reference< rdf::XURI > & i_xURI )
898{
899 if (!i_xURI.is()) {
900 throw lang::IllegalArgumentException(
901 "DocumentMetadataAccess::getElementByURI: URI is null", *this, 0);
902 }
903
904 const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
905 const OUString name( i_xURI->getStringValue() );
906 if (!name.match(baseURI)) {
907 return nullptr;
908 }
909 OUString path;
910 OUString idref;
911 if (!splitXmlId(name.subView(baseURI.getLength()), path, idref)) {
912 return nullptr;
913 }
914
915 return getElementByMetadataReference( beans::StringPair(path, idref) );
916}
917
918uno::Sequence<uno::Reference<rdf::XURI>> SAL_CALL
919DocumentMetadataAccess::getMetadataGraphsWithType(const uno::Reference<rdf::XURI>& i_xType)
920{
921 if (!i_xType.is())
922 {
923 throw lang::IllegalArgumentException("DocumentMetadataAccess::getMetadataGraphsWithType: "
924 "type is null",
925 *this, 0);
926 }
927
928 return ::comphelper::containerToSequence(getAllParts(*m_pImpl, i_xType));
929}
930
931uno::Reference<rdf::XURI> SAL_CALL
932DocumentMetadataAccess::addMetadataFile(const OUString & i_rFileName,
933 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
934{
935 if (!isFileNameValid(i_rFileName)) {
936 throw lang::IllegalArgumentException(
937 "DocumentMetadataAccess::addMetadataFile: invalid FileName",
938 *this, 0);
939 }
940 if (isReservedFile(i_rFileName)) {
941 throw lang::IllegalArgumentException(
942 "DocumentMetadataAccess::addMetadataFile:"
943 "invalid FileName: reserved", *this, 0);
944 }
945 if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
946 [](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
947 throw lang::IllegalArgumentException(
948 "DocumentMetadataAccess::addMetadataFile: "
949 "null type", *this, 2);
950 }
951
952 const uno::Reference<rdf::XURI> xGraphName(
953 getURIForStream(*m_pImpl, i_rFileName) );
954
955 try {
956 m_pImpl->m_xRepository->createGraph(xGraphName);
957 } catch (const rdf::RepositoryException &) {
958 css::uno::Any anyEx = cppu::getCaughtException();
959 throw lang::WrappedTargetRuntimeException(
960 "DocumentMetadataAccess::addMetadataFile: exception",
961 *this, anyEx);
962 // note: all other exceptions are propagated
963 }
964
965 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
966 return xGraphName;
967}
968
969uno::Reference<rdf::XURI> SAL_CALL
970DocumentMetadataAccess::importMetadataFile(::sal_Int16 i_Format,
971 const uno::Reference< io::XInputStream > & i_xInStream,
972 const OUString & i_rFileName,
973 const uno::Reference< rdf::XURI > & i_xBaseURI,
974 const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
975{
976 if (!isFileNameValid(i_rFileName)) {
977 throw lang::IllegalArgumentException(
978 "DocumentMetadataAccess::importMetadataFile: invalid FileName",
979 *this, 0);
980 }
981 if (isReservedFile(i_rFileName)) {
982 throw lang::IllegalArgumentException(
983 "DocumentMetadataAccess::importMetadataFile:"
984 "invalid FileName: reserved", *this, 0);
985 }
986 if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
987 [](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
988 throw lang::IllegalArgumentException(
989 "DocumentMetadataAccess::importMetadataFile: null type",
990 *this, 5);
991 }
992
993 const uno::Reference<rdf::XURI> xGraphName(
994 getURIForStream(*m_pImpl, i_rFileName) );
995
996 try {
997 m_pImpl->m_xRepository->importGraph(
998 i_Format, i_xInStream, xGraphName, i_xBaseURI);
999 } catch (const rdf::RepositoryException &) {
1000 css::uno::Any anyEx = cppu::getCaughtException();
1001 throw lang::WrappedTargetRuntimeException(
1002 "DocumentMetadataAccess::importMetadataFile: "
1003 "RepositoryException", *this, anyEx);
1004 // note: all other exceptions are propagated
1005 }
1006
1007 // add to manifest
1008 addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
1009 return xGraphName;
1010}
1011
1012void SAL_CALL
1013DocumentMetadataAccess::removeMetadataFile(
1014 const uno::Reference< rdf::XURI > & i_xGraphName)
1015{
1016 try {
1017 m_pImpl->m_xRepository->destroyGraph(i_xGraphName);
1018 } catch (const rdf::RepositoryException &) {
1019 css::uno::Any anyEx = cppu::getCaughtException();
1020 throw lang::WrappedTargetRuntimeException(
1021 "DocumentMetadataAccess::removeMetadataFile: "
1022 "RepositoryException", *this, anyEx);
1023 // note: all other exceptions are propagated
1024 }
1025
1026 // remove file from manifest
1027 removeFile(*m_pImpl, i_xGraphName);
1028}
1029
1030void SAL_CALL
1031DocumentMetadataAccess::addContentOrStylesFile(
1032 const OUString & i_rFileName)
1033{
1034 if (!isFileNameValid(i_rFileName)) {
1035 throw lang::IllegalArgumentException(
1036 "DocumentMetadataAccess::addContentOrStylesFile: "
1037 "invalid FileName", *this, 0);
1038 }
1039
1040 if (!addContentOrStylesFileImpl(*m_pImpl, i_rFileName)) {
1041 throw lang::IllegalArgumentException(
1042 "DocumentMetadataAccess::addContentOrStylesFile: "
1043 "invalid FileName: must end with content.xml or styles.xml",
1044 *this, 0);
1045 }
1046}
1047
1048void SAL_CALL
1049DocumentMetadataAccess::removeContentOrStylesFile(
1050 const OUString & i_rFileName)
1051{
1052 if (!isFileNameValid(i_rFileName)) {
1053 throw lang::IllegalArgumentException(
1054 "DocumentMetadataAccess::removeContentOrStylesFile: "
1055 "invalid FileName", *this, 0);
1056 }
1057
1058 try {
1059 const uno::Reference<rdf::XURI> xPart(
1060 getURIForStream(*m_pImpl, i_rFileName) );
1061 const uno::Reference<container::XEnumeration> xEnum(
1062 m_pImpl->m_xManifest->getStatements( m_pImpl->m_xBaseURI,
1063 getURI<rdf::URIs::PKG_HASPART>(m_pImpl->m_xContext),
1064 xPart),
1065 uno::UNO_SET_THROW);
1066 if (!xEnum->hasMoreElements()) {
1067 throw container::NoSuchElementException(
1068 "DocumentMetadataAccess::removeContentOrStylesFile: "
1069 "cannot find stream in manifest graph: " + i_rFileName,
1070 *this);
1071 }
1072
1073 // remove file from manifest
1074 removeFile(*m_pImpl, xPart);
1075
1076 } catch (const uno::RuntimeException &) {
1077 throw;
1078 } catch (const uno::Exception &) {
1079 css::uno::Any anyEx = cppu::getCaughtException();
1080 throw lang::WrappedTargetRuntimeException(
1081 "DocumentMetadataAccess::removeContentOrStylesFile: exception",
1082 *this, anyEx);
1083 }
1084}
1085
1086void SAL_CALL DocumentMetadataAccess::loadMetadataFromStorage(
1087 const uno::Reference< embed::XStorage > & i_xStorage,
1088 const uno::Reference<rdf::XURI> & i_xBaseURI,
1089 const uno::Reference<task::XInteractionHandler> & i_xHandler)
1090{
1091 if (!i_xStorage.is()) {
1092 throw lang::IllegalArgumentException(
1093 "DocumentMetadataAccess::loadMetadataFromStorage: "
1094 "storage is null", *this, 0);
1095 }
1096 if (!i_xBaseURI.is()) {
1097 throw lang::IllegalArgumentException(
1098 "DocumentMetadataAccess::loadMetadataFromStorage: "
1099 "base URI is null", *this, 1);
1100 }
1101 const OUString baseURI( i_xBaseURI->getStringValue());
1102 if (baseURI.indexOf('#') >= 0) {
1103 throw lang::IllegalArgumentException(
1104 "DocumentMetadataAccess::loadMetadataFromStorage: "
1105 "base URI not absolute", *this, 1);
1106 }
1107 if (!baseURI.endsWith("/")) {
1108 throw lang::IllegalArgumentException(
1109 "DocumentMetadataAccess::loadMetadataFromStorage: "
1110 "base URI does not end with slash", *this, 1);
1111 }
1112
1113 initLoading(*m_pImpl, i_xStorage, i_xBaseURI, i_xHandler);
1114
1115 std::set< OUString > StgFiles;
1116 collectFilesFromStorage(i_xStorage, StgFiles);
1117
1118 std::vector< OUString > MfstMetadataFiles;
1119
1120 try {
1121 const ::std::vector< uno::Reference< rdf::XURI > > parts(
1122 getAllParts(*m_pImpl) );
1123 const uno::Reference<rdf::XURI>& xContentFile(
1124 getURI<rdf::URIs::ODF_CONTENTFILE>(m_pImpl->m_xContext));
1125 const uno::Reference<rdf::XURI>& xStylesFile(
1126 getURI<rdf::URIs::ODF_STYLESFILE>(m_pImpl->m_xContext));
1127 const uno::Reference<rdf::XURI>& xMetadataFile(
1128 getURI<rdf::URIs::PKG_METADATAFILE>(m_pImpl->m_xContext));
1129 const sal_Int32 len( baseURI.getLength() );
1130 for (const auto& rxPart : parts) {
1131 const OUString name(rxPart->getStringValue());
1132 if (!name.match(baseURI)) {
1133 SAL_WARN("sfx", "loadMetadataFromStorage: graph not in document: " << name);
1134 continue;
1135 }
1136 const OUString relName( name.copy(len) );
1137 if (relName == s_manifest) {
1138 SAL_WARN("sfx", "loadMetadataFromStorage: found ourselves a recursive manifest!");
1139 continue;
1140 }
1141 // remove found items from StgFiles
1142 StgFiles.erase(relName);
1143 if (isContentFile(relName)) {
1144 if (!isPartOfType(*m_pImpl, rxPart, xContentFile)) {
1145 const uno::Reference <rdf::XURI> xName(
1146 getURIForStream(*m_pImpl, relName) );
1147 // add missing type statement
1148 m_pImpl->m_xManifest->addStatement(xName,
1149 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1150 xContentFile);
1151 }
1152 } else if (isStylesFile(relName)) {
1153 if (!isPartOfType(*m_pImpl, rxPart, xStylesFile)) {
1154 const uno::Reference <rdf::XURI> xName(
1155 getURIForStream(*m_pImpl, relName) );
1156 // add missing type statement
1157 m_pImpl->m_xManifest->addStatement(xName,
1158 getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
1159 xStylesFile);
1160 }
1161 } else if (isReservedFile(relName)) {
1162 SAL_WARN("sfx", "loadMetadataFromStorage: reserved file name in manifest");
1163 } else {
1164 if (isPartOfType(*m_pImpl, rxPart, xMetadataFile)) {
1165 MfstMetadataFiles.push_back(relName);
1166 }
1167 // do not add statement for MetadataFile; it could be
1168 // something else! just ignore it...
1169 }
1170 }
1171 } catch (const uno::RuntimeException &) {
1172 throw;
1173 } catch (const uno::Exception &) {
1174 css::uno::Any anyEx = cppu::getCaughtException();
1175 throw lang::WrappedTargetRuntimeException(
1176 "DocumentMetadataAccess::loadMetadataFromStorage: "
1177 "exception", *this, anyEx);
1178 }
1179
1180 for (const auto& aStgFile : StgFiles)
1182
1183 for (const auto& aMfstMetadataFile : MfstMetadataFiles)
1184 importFile(*m_pImpl, i_xStorage, baseURI, i_xHandler, aMfstMetadataFile);
1185}
1186
1187void SAL_CALL DocumentMetadataAccess::storeMetadataToStorage(
1188 const uno::Reference< embed::XStorage > & i_xStorage)
1189{
1190 if (!i_xStorage.is()) {
1191 throw lang::IllegalArgumentException(
1192 "DocumentMetadataAccess::storeMetadataToStorage: "
1193 "storage is null", *this, 0);
1194 }
1195
1196 // export manifest
1197 const uno::Reference <rdf::XURI> xManifest(
1199 const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
1200 try {
1201 writeStream(*m_pImpl, i_xStorage, xManifest, s_manifest, baseURI);
1202 } catch (const uno::RuntimeException &) {
1203 throw;
1204 } catch (const io::IOException &) {
1205 css::uno::Any anyEx = cppu::getCaughtException();
1206 throw lang::WrappedTargetException(
1207 "storeMetadataToStorage: IO exception", *this, anyEx);
1208 } catch (const uno::Exception &) {
1209 css::uno::Any anyEx = cppu::getCaughtException();
1210 throw lang::WrappedTargetRuntimeException(
1211 "storeMetadataToStorage: exception", *this, anyEx);
1212 }
1213
1214 // export metadata streams
1215 try {
1216 const uno::Sequence<uno::Reference<rdf::XURI> > graphs(
1217 m_pImpl->m_xRepository->getGraphNames());
1218 const sal_Int32 len( baseURI.getLength() );
1219 for (const uno::Reference<rdf::XURI>& xName : graphs) {
1220 const OUString name(xName->getStringValue());
1221 if (!name.match(baseURI)) {
1222 SAL_WARN("sfx", "storeMetadataToStorage: graph not in document: " << name);
1223 continue;
1224 }
1225 const OUString relName( name.copy(len) );
1226 if (relName == s_manifest) {
1227 continue;
1228 }
1229 if (!isFileNameValid(relName) || isReservedFile(relName)) {
1230 SAL_WARN("sfx", "storeMetadataToStorage: invalid file name: " << relName);
1231 continue;
1232 }
1233 try {
1234 writeStream(*m_pImpl, i_xStorage, xName, relName, baseURI);
1235 } catch (const uno::RuntimeException &) {
1236 throw;
1237 } catch (const io::IOException &) {
1238 css::uno::Any anyEx = cppu::getCaughtException();
1239 throw lang::WrappedTargetException(
1240 "storeMetadataToStorage: IO exception",
1241 *this, anyEx);
1242 } catch (const uno::Exception &) {
1243 css::uno::Any anyEx = cppu::getCaughtException();
1244 throw lang::WrappedTargetRuntimeException(
1245 "storeMetadataToStorage: exception",
1246 *this, anyEx);
1247 }
1248 }
1249 } catch (const rdf::RepositoryException &) {
1250 css::uno::Any anyEx = cppu::getCaughtException();
1251 throw lang::WrappedTargetRuntimeException(
1252 "storeMetadataToStorage: exception", *this, anyEx);
1253 }
1254}
1255
1256void SAL_CALL
1257DocumentMetadataAccess::loadMetadataFromMedium(
1258 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1259{
1260 uno::Reference<io::XInputStream> xIn;
1261 utl::MediaDescriptor md(i_rMedium);
1262 OUString URL;
1264 OUString BaseURL;
1266 if (md.addInputStream()) {
1268 }
1269 if (!xIn.is() && URL.isEmpty()) {
1270 throw lang::IllegalArgumentException(
1271 "DocumentMetadataAccess::loadMetadataFromMedium: "
1272 "invalid medium: no URL, no input stream", *this, 0);
1273 }
1274 uno::Reference<embed::XStorage> xStorage;
1275 try {
1276 if (xIn.is()) {
1278 xIn, m_pImpl->m_xContext);
1279 } else { // fallback to url
1281 URL, embed::ElementModes::READ, m_pImpl->m_xContext);
1282 }
1283 } catch (const uno::RuntimeException &) {
1284 throw;
1285 } catch (const io::IOException &) {
1286 throw;
1287 } catch (const uno::Exception &) {
1288 css::uno::Any anyEx = cppu::getCaughtException();
1289 throw lang::WrappedTargetException(
1290 "DocumentMetadataAccess::loadMetadataFromMedium: "
1291 "exception", *this, anyEx);
1292 }
1293 if (!xStorage.is()) {
1294 throw uno::RuntimeException(
1295 "DocumentMetadataAccess::loadMetadataFromMedium: "
1296 "cannot get Storage", *this);
1297 }
1298 uno::Reference<rdf::XURI> xBaseURI;
1299 try {
1300 xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, BaseURL);
1301 } catch (const uno::Exception &) {
1302 // fall back to URL
1303 try {
1304 xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, URL);
1305 } catch (const uno::Exception &) {
1306 OSL_FAIL("cannot create base URI");
1307 }
1308 }
1309 uno::Reference<task::XInteractionHandler> xIH;
1311 loadMetadataFromStorage(xStorage, xBaseURI, xIH);
1312}
1313
1314void SAL_CALL
1315DocumentMetadataAccess::storeMetadataToMedium(
1316 const uno::Sequence< beans::PropertyValue > & i_rMedium)
1317{
1318 utl::MediaDescriptor md(i_rMedium);
1319 OUString URL;
1321 if (URL.isEmpty()) {
1322 throw lang::IllegalArgumentException(
1323 "DocumentMetadataAccess::storeMetadataToMedium: "
1324 "invalid medium: no URL", *this, 0);
1325 }
1326
1327 SfxMedium aMedium(i_rMedium);
1328 uno::Reference<embed::XStorage> xStorage(aMedium.GetOutputStorage());
1329
1330 bool sfx(false);
1331 if (xStorage.is()) {
1332 sfx = true;
1333 } else {
1335 URL, embed::ElementModes::WRITE, m_pImpl->m_xContext);
1336 }
1337
1338 if (!xStorage.is()) {
1339 throw uno::RuntimeException(
1340 "DocumentMetadataAccess::storeMetadataToMedium: "
1341 "cannot get Storage", *this);
1342 }
1343 // set MIME type of the storage
1344 utl::MediaDescriptor::const_iterator iter
1346 if (iter != md.end()) {
1347 uno::Reference< beans::XPropertySet > xProps(xStorage,
1348 uno::UNO_QUERY_THROW);
1349 try {
1350 // this is NOT supported in FileSystemStorage
1351 xProps->setPropertyValue(
1353 iter->second);
1354 } catch (const uno::Exception &) { }
1355 }
1356 storeMetadataToStorage(xStorage);
1357
1358 if (!sfx)
1359 return;
1360
1361 const bool bOk = aMedium.Commit();
1362 aMedium.Close();
1363 if ( !bOk ) {
1364 ErrCode nError = aMedium.GetError();
1365 if ( nError == ERRCODE_NONE ) {
1366 nError = ERRCODE_IO_GENERAL;
1367 }
1368 task::ErrorCodeIOException ex(
1369 "DocumentMetadataAccess::storeMetadataToMedium Commit failed: " + nError.toString(),
1370 uno::Reference< uno::XInterface >(), sal_uInt32(nError));
1371 throw lang::WrappedTargetException(OUString(), *this,
1372 uno::Any(ex));
1373 }
1374}
1375
1376} // namespace sfx2
1377
1378/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
::std::unique_ptr< XmlIdRegistry_Impl > m_pImpl
Reference< XInputStream > xStream
COMPHELPER_DLLPUBLIC OUString toString() const
void Close(bool bInDestruction=false)
Definition: docfile.cxx:3137
ErrCode GetError() const
Definition: docfile.hxx:147
bool Commit()
Definition: docfile.cxx:839
css::uno::Reference< css::embed::XStorage > GetOutputStorage()
Definition: docfile.cxx:949
static css::uno::Reference< css::embed::XStorage > GetStorageFromInputStream(const css::uno::Reference< css::io::XInputStream > &xStream, const css::uno::Reference< css::uno::XComponentContext > &rxContext=css::uno::Reference< css::uno::XComponentContext >())
static css::uno::Reference< css::embed::XStorage > GetStorageFromURL2(const OUString &aURL, sal_Int32 nStorageMode, const css::uno::Reference< css::uno::XComponentContext > &rxContext=css::uno::Reference< css::uno::XComponentContext >())
static bool IsValidZipEntryFileName(std::u16string_view aName, bool bSlashAllowed)
DocumentMetadataAccess(const DocumentMetadataAccess &)=delete
interface for getElementByMetadataReference; for use by sfx2::DocumentMetadataAccess
virtual css::uno::Reference< css::rdf::XMetadatable > GetElementByMetadataReference(const css::beans::StringPair &i_rXmlId) const =0
static constexpr OUStringLiteral PROP_INPUTSTREAM
static constexpr OUStringLiteral PROP_MEDIATYPE
static constexpr OUStringLiteral PROP_URL
static constexpr OUStringLiteral PROP_DOCUMENTBASEURL
static constexpr OUStringLiteral PROP_INTERACTIONHANDLER
#define TOOLS_WARN_EXCEPTION(area, stream)
float u
#define ERRCODE_IO_GENERAL
#define ERRCODE_NONE
const char * name
const sal_uInt16 idx[]
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
err
Any SAL_CALL getCaughtException()
constexpr sal_Int64 md(U i, U)
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
static uno::Reference< rdf::XURI > const & getURI(uno::Reference< uno::XComponentContext > const &i_xContext)
static bool isPartOfType(struct DocumentMetadataAccess_Impl const &i_rImpl, uno::Reference< rdf::XURI > const &i_xPart, uno::Reference< rdf::XURI > const &i_xType)
bool isValidNCName(std::u16string_view i_rIdref)
is i_rIdref a valid NCName ?
static ::std::vector< uno::Reference< rdf::XURI > > getAllParts(struct DocumentMetadataAccess_Impl const &i_rImpl)
static void init(struct DocumentMetadataAccess_Impl &i_rImpl)
init Impl struct
constexpr OUStringLiteral s_styles
static void writeStream(struct DocumentMetadataAccess_Impl &i_rImpl, uno::Reference< embed::XStorage > const &i_xStorage, uno::Reference< rdf::XURI > const &i_xGraphName, OUString const &i_rPath, OUString const &i_rBaseURI)
write a metadata file to the storage
constexpr OUStringLiteral s_content
static void initLoading(struct DocumentMetadataAccess_Impl &i_rImpl, const uno::Reference< embed::XStorage > &i_xStorage, const uno::Reference< rdf::XURI > &i_xBaseURI, const uno::Reference< task::XInteractionHandler > &i_xHandler)
static bool splitPath(OUString const &i_rPath, OUString &o_rDir, OUString &o_rRest)
split a uri hierarchy into first segment and rest
static bool addContentOrStylesFileImpl(struct DocumentMetadataAccess_Impl const &i_rImpl, const OUString &i_rPath)
add content.xml or styles.xml to manifest
static uno::Reference< rdf::XURI > getURIForStream(struct DocumentMetadataAccess_Impl const &i_rImpl, OUString const &i_rPath)
static void readStream(struct DocumentMetadataAccess_Impl &i_rImpl, uno::Reference< embed::XStorage > const &i_xStorage, OUString const &i_rPath, OUString const &i_rBaseURI)
import a metadata file into repository
static bool splitXmlId(std::u16string_view i_XmlId, OUString &o_StreamName, OUString &o_Idref)
bool isValidXmlId(std::u16string_view i_rStreamName, std::u16string_view i_rIdref)
static void removeFile(struct DocumentMetadataAccess_Impl const &i_rImpl, uno::Reference< rdf::XURI > const &i_xPart)
remove a file from the manifest
static void addMetadataFileImpl(struct DocumentMetadataAccess_Impl const &i_rImpl, const OUString &i_rPath, const uno::Sequence< uno::Reference< rdf::XURI > > &i_rTypes)
add metadata file to manifest
static ::std::vector< uno::Reference< rdf::XURI > > getAllParts(struct DocumentMetadataAccess_Impl const &i_rImpl, const uno::Reference< rdf::XURI > &i_xType)
static void addFile(struct DocumentMetadataAccess_Impl const &i_rImpl, uno::Reference< rdf::XURI > const &i_xType, OUString const &i_rPath, const uno::Sequence< uno::Reference< rdf::XURI > > *i_pTypes)
add statements declaring i_xResource to be a file of type i_xType with path i_rPath to manifest,...
const char s_odfmime[]
static void collectFilesFromStorage(uno::Reference< embed::XStorage > const &i_xStorage, std::set< OUString > &o_rFiles)
check if storage has content.xml/styles.xml; e.g.
static void importFile(struct DocumentMetadataAccess_Impl &i_rImpl, uno::Reference< embed::XStorage > const &i_xStorage, OUString const &i_rBaseURI, uno::Reference< task::XInteractionHandler > const &i_xHandler, const OUString &i_rPath)
import a metadata file into repository
static bool isStylesFile(std::u16string_view i_rPath)
uno::Reference< rdf::XURI > createBaseURI(uno::Reference< uno::XComponentContext > const &i_xContext, uno::Reference< frame::XModel > const &i_xModel, OUString const &i_rPkgURI, std::u16string_view i_rSubDocument)
static bool handleError(ucb::InteractiveAugmentedIOException const &i_rException, const uno::Reference< task::XInteractionHandler > &i_xHandler)
error handling policy.
static ucb::InteractiveAugmentedIOException mkException(OUString const &i_rMessage, ucb::IOErrorCode const i_ErrorCode, OUString const &i_rUri, OUString const &i_rResource)
constexpr OUStringLiteral s_manifest
static bool isFileNameValid(std::u16string_view i_rFileName)
would storing the file to a XStorage succeed?
static bool isReservedFile(std::u16string_view i_rPath)
static void exportStream(struct DocumentMetadataAccess_Impl const &i_rImpl, uno::Reference< embed::XStorage > const &i_xStorage, uno::Reference< rdf::XURI > const &i_xGraphName, OUString const &i_rFileName, OUString const &i_rBaseURI)
actually write a metadata file to the storage
static bool isContentFile(std::u16string_view i_rPath)
DocumentMetadataAccess_Impl(uno::Reference< uno::XComponentContext > i_xContext, SfxObjectShell const &i_rRegistrySupplier)
const uno::Reference< uno::XComponentContext > m_xContext
uno::Reference< rdf::XRepository > m_xRepository
uno::Reference< rdf::XNamedGraph > m_xManifest
uno::Reference< rdf::XURI > m_xBaseURI
bool hasValue()