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