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