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