xref: /trunk/main/sfx2/source/doc/DocumentMetadataAccess.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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