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