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