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