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