xref: /core/sfx2/source/doc/SfxDocumentMetaData.cxx (revision 338d7b981c7fe69f579e3ac7ef31b7365a0ef160)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 
23 #include <comphelper/compbase.hxx>
24 #include <cppuhelper/exc_hlp.hxx>
25 #include <com/sun/star/lang/XServiceInfo.hpp>
26 #include <com/sun/star/document/XDocumentProperties.hpp>
27 #include <com/sun/star/document/XDocumentProperties2.hpp>
28 #include <com/sun/star/lang/XInitialization.hpp>
29 #include <com/sun/star/util/XCloneable.hpp>
30 #include <com/sun/star/util/XModifiable.hpp>
31 #include <com/sun/star/xml/sax/SAXException.hpp>
32 #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
33 
34 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
35 #include <com/sun/star/lang/EventObject.hpp>
36 #include <com/sun/star/beans/IllegalTypeException.hpp>
37 #include <com/sun/star/beans/PropertyExistException.hpp>
38 #include <com/sun/star/beans/XPropertySet.hpp>
39 #include <com/sun/star/beans/XPropertySetInfo.hpp>
40 #include <com/sun/star/beans/PropertyAttribute.hpp>
41 #include <com/sun/star/task/ErrorCodeIOException.hpp>
42 #include <com/sun/star/embed/XStorage.hpp>
43 #include <com/sun/star/embed/XTransactedObject.hpp>
44 #include <com/sun/star/embed/ElementModes.hpp>
45 #include <com/sun/star/io/WrongFormatException.hpp>
46 #include <com/sun/star/io/XStream.hpp>
47 #include <com/sun/star/document/XImporter.hpp>
48 #include <com/sun/star/document/XExporter.hpp>
49 #include <com/sun/star/document/XFilter.hpp>
50 #include <com/sun/star/xml/sax/Writer.hpp>
51 #include <com/sun/star/xml/sax/Parser.hpp>
52 #include <com/sun/star/xml/sax/XFastParser.hpp>
53 #include <com/sun/star/xml/dom/DOMException.hpp>
54 #include <com/sun/star/xml/dom/XDocument.hpp>
55 #include <com/sun/star/xml/dom/XElement.hpp>
56 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
57 #include <com/sun/star/xml/dom/NodeType.hpp>
58 #include <com/sun/star/xml/xpath/XPathAPI.hpp>
59 #include <com/sun/star/util/Date.hpp>
60 #include <com/sun/star/util/Time.hpp>
61 #include <com/sun/star/util/DateWithTimezone.hpp>
62 #include <com/sun/star/util/DateTimeWithTimezone.hpp>
63 #include <com/sun/star/util/Duration.hpp>
64 
65 #include <rtl/ustrbuf.hxx>
66 #include <tools/datetime.hxx>
67 #include <comphelper/diagnose_ex.hxx>
68 #include <osl/mutex.hxx>
69 #include <comphelper/fileformat.h>
70 #include <comphelper/interfacecontainer3.hxx>
71 #include <comphelper/storagehelper.hxx>
72 #include <unotools/mediadescriptor.hxx>
73 #include <comphelper/sequence.hxx>
74 #include <sot/storage.hxx>
75 #include <sfx2/docfile.hxx>
76 #include <sax/tools/converter.hxx>
77 #include <i18nlangtag/languagetag.hxx>
78 #include <optional>
79 
80 #include <algorithm>
81 #include <utility>
82 #include <vector>
83 #include <map>
84 #include <cstring>
85 #include <limits>
86 
87 
88 #include <cppuhelper/implbase.hxx>
89 #include <cppuhelper/supportsservice.hxx>
90 #include <com/sun/star/document/XCompatWriterDocProperties.hpp>
91 #include <com/sun/star/beans/PropertyBag.hpp>
92 
93 /**
94  * This file contains the implementation of the service
95  * com.sun.star.document.DocumentProperties.
96  * This service enables access to the meta-data stored in documents.
97  * Currently, this service only handles documents in ODF format.
98  *
99  * The implementation uses an XML DOM to store the properties.
100  * This approach was taken because it allows for preserving arbitrary XML data
101  * in loaded documents, which will be stored unmodified when saving the
102  * document again.
103  *
104  * Upon access, some properties are directly read from and updated in the DOM.
105  * Exception: it seems impossible to get notified upon addition of a property
106  * to a com.sun.star.beans.PropertyBag, which is used for storing user-defined
107  * properties; because of this, user-defined properties are updated in the
108  * XML DOM only when storing the document.
109  * Exception 2: when setting certain properties which correspond to attributes
110  * in the XML DOM, we want to remove the corresponding XML element. Detecting
111  * this condition can get messy, so we store all such properties as members,
112  * and update the DOM tree only when storing the document (in
113  * <method>updateUserDefinedAndAttributes</method>).
114  *
115  */
116 
117 /// anonymous implementation namespace
118 namespace {
119 
120 /// a list of attribute-lists, where attribute means name and content
121 typedef std::vector<std::vector<std::pair<OUString, OUString> > >
122         AttrVector;
123 
124 typedef ::comphelper::WeakComponentImplHelper<
125             css::lang::XServiceInfo,
126             css::document::XDocumentProperties2,
127             css::lang::XInitialization,
128             css::util::XCloneable,
129             css::util::XModifiable,
130             css::xml::sax::XSAXSerializable>
131     SfxDocumentMetaData_Base;
132 
133 class SfxDocumentMetaData:
134     public SfxDocumentMetaData_Base
135 {
136 public:
137     explicit SfxDocumentMetaData(
138         css::uno::Reference< css::uno::XComponentContext > const & context);
139     SfxDocumentMetaData(const SfxDocumentMetaData&) = delete;
140     SfxDocumentMetaData& operator=(const SfxDocumentMetaData&) = delete;
141 
142     // css::lang::XServiceInfo:
143     virtual OUString SAL_CALL getImplementationName() override;
144     virtual sal_Bool SAL_CALL supportsService(
145         const OUString & ServiceName) override;
146     virtual css::uno::Sequence< OUString > SAL_CALL
147         getSupportedServiceNames() override;
148 
149     // css::lang::XComponent:
150     virtual void disposing(std::unique_lock<std::mutex>& rGuard) override;
151 
152     // css::document::XDocumentProperties:
153     virtual OUString SAL_CALL getAuthor() override;
154     virtual void SAL_CALL setAuthor(const OUString & the_value) override;
155     virtual OUString SAL_CALL getGenerator() override;
156     virtual void SAL_CALL setGenerator(const OUString & the_value) override;
157     virtual css::util::DateTime SAL_CALL getCreationDate() override;
158     virtual void SAL_CALL setCreationDate(const css::util::DateTime & the_value) override;
159     virtual OUString SAL_CALL getTitle() override;
160     virtual void SAL_CALL setTitle(const OUString & the_value) override;
161     virtual OUString SAL_CALL getSubject() override;
162     virtual void SAL_CALL setSubject(const OUString & the_value) override;
163     virtual OUString SAL_CALL getDescription() override;
164     virtual void SAL_CALL setDescription(const OUString & the_value) override;
165     virtual css::uno::Sequence< OUString > SAL_CALL getKeywords() override;
166     virtual void SAL_CALL setKeywords(
167         const css::uno::Sequence< OUString > & the_value) override;
168     virtual css::lang::Locale SAL_CALL getLanguage() override;
169     virtual void SAL_CALL setLanguage(const css::lang::Locale & the_value) override;
170     virtual OUString SAL_CALL getModifiedBy() override;
171     virtual void SAL_CALL setModifiedBy(const OUString & the_value) override;
172     virtual css::util::DateTime SAL_CALL getModificationDate() override;
173     virtual void SAL_CALL setModificationDate(
174             const css::util::DateTime & the_value) override;
175     virtual OUString SAL_CALL getPrintedBy() override;
176     virtual void SAL_CALL setPrintedBy(const OUString & the_value) override;
177     virtual css::util::DateTime SAL_CALL getPrintDate() override;
178     virtual void SAL_CALL setPrintDate(const css::util::DateTime & the_value) override;
179     virtual OUString SAL_CALL getTemplateName() override;
180     virtual void SAL_CALL setTemplateName(const OUString & the_value) override;
181     virtual OUString SAL_CALL getTemplateURL() override;
182     virtual void SAL_CALL setTemplateURL(const OUString & the_value) override;
183     virtual css::util::DateTime SAL_CALL getTemplateDate() override;
184     virtual void SAL_CALL setTemplateDate(const css::util::DateTime & the_value) override;
185     virtual OUString SAL_CALL getAutoloadURL() override;
186     virtual void SAL_CALL setAutoloadURL(const OUString & the_value) override;
187     virtual ::sal_Int32 SAL_CALL getAutoloadSecs() override;
188     virtual void SAL_CALL setAutoloadSecs(::sal_Int32 the_value) override;
189     virtual OUString SAL_CALL getDefaultTarget() override;
190     virtual void SAL_CALL setDefaultTarget(const OUString & the_value) override;
191     virtual css::uno::Sequence< css::beans::NamedValue > SAL_CALL
192         getDocumentStatistics() override;
193     virtual void SAL_CALL setDocumentStatistics(
194         const css::uno::Sequence< css::beans::NamedValue > & the_value) override;
195     virtual ::sal_Int16 SAL_CALL getEditingCycles() override;
196     virtual void SAL_CALL setEditingCycles(::sal_Int16 the_value) override;
197     virtual ::sal_Int32 SAL_CALL getEditingDuration() override;
198     virtual void SAL_CALL setEditingDuration(::sal_Int32 the_value) override;
199     virtual void SAL_CALL resetUserData(const OUString & the_value) override;
200     virtual css::uno::Reference< css::beans::XPropertyContainer > SAL_CALL
201         getUserDefinedProperties() override;
202     virtual void SAL_CALL loadFromStorage(
203         const css::uno::Reference< css::embed::XStorage > & Storage,
204         const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
205     virtual void SAL_CALL loadFromMedium(const OUString & URL,
206         const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
207     virtual void SAL_CALL storeToStorage(
208         const css::uno::Reference< css::embed::XStorage > & Storage,
209         const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
210     virtual void SAL_CALL storeToMedium(const OUString & URL,
211         const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
212     virtual css::uno::Sequence< OUString > SAL_CALL getContributor() override;
213     virtual void SAL_CALL setContributor(const css::uno::Sequence< OUString >& the_value) override;
214     virtual OUString SAL_CALL getCoverage() override;
215     virtual void SAL_CALL setCoverage(const OUString & the_value) override;
216     virtual OUString SAL_CALL getIdentifier() override;
217     virtual void SAL_CALL setIdentifier(const OUString & the_value) override;
218     virtual css::uno::Sequence< OUString > SAL_CALL getPublisher() override;
219     virtual void SAL_CALL setPublisher(const css::uno::Sequence< OUString > & the_value) override;
220     virtual css::uno::Sequence< OUString > SAL_CALL getRelation() override;
221     virtual void SAL_CALL setRelation(const css::uno::Sequence< OUString > & the_value) override;
222     virtual OUString SAL_CALL getRights() override;
223     virtual void SAL_CALL setRights(const OUString & the_value) override;
224     virtual OUString SAL_CALL getSource() override;
225     virtual void SAL_CALL setSource(const OUString& the_value) override;
226     virtual OUString SAL_CALL getType() override;
227     virtual void SAL_CALL setType(const OUString& the_value) override;
228 
229 
230     // css::lang::XInitialization:
231     virtual void SAL_CALL initialize(
232         const css::uno::Sequence< css::uno::Any > & aArguments) override;
233 
234     // css::util::XCloneable:
235     virtual css::uno::Reference<css::util::XCloneable> SAL_CALL createClone() override;
236 
237     // css::util::XModifiable:
238     virtual sal_Bool SAL_CALL isModified(  ) override;
239     virtual void SAL_CALL setModified( sal_Bool bModified ) override;
240 
241     // css::util::XModifyBroadcaster:
242     virtual void SAL_CALL addModifyListener(
243         const css::uno::Reference< css::util::XModifyListener > & xListener) override;
244     virtual void SAL_CALL removeModifyListener(
245         const css::uno::Reference< css::util::XModifyListener > & xListener) override;
246 
247     // css::xml::sax::XSAXSerializable
248     virtual void SAL_CALL serialize(
249         const css::uno::Reference<css::xml::sax::XDocumentHandler>& i_xHandler,
250         const css::uno::Sequence< css::beans::StringPair >& i_rNamespaces) override;
251 
252 protected:
~SfxDocumentMetaData()253     virtual ~SfxDocumentMetaData() override {}
createMe(css::uno::Reference<css::uno::XComponentContext> const & context)254     virtual rtl::Reference<SfxDocumentMetaData> createMe( css::uno::Reference< css::uno::XComponentContext > const & context ) { return new SfxDocumentMetaData( context ); };
255     const css::uno::Reference< css::uno::XComponentContext > m_xContext;
256 
257     /// for notification
258     ::comphelper::OInterfaceContainerHelper4<css::util::XModifyListener> m_NotifyListeners;
259     /// flag: false means not initialized yet, or disposed
260     bool m_isInitialized;
261     /// flag
262     bool m_isModified;
263     /// meta-data DOM tree
264     css::uno::Reference< css::xml::dom::XDocument > m_xDoc;
265     /// meta-data super node in the meta-data DOM tree
266     css::uno::Reference< css::xml::dom::XNode> m_xParent;
267     /// standard meta data (single occurrence)
268     std::map< OUString, css::uno::Reference<css::xml::dom::XNode> >
269         m_meta;
270     /// standard meta data (multiple occurrences)
271     std::map< OUString,
272         std::vector<css::uno::Reference<css::xml::dom::XNode> > > m_metaList;
273     /// user-defined meta data (meta:user-defined) @ATTENTION may be null!
274     css::uno::Reference<css::beans::XPropertyContainer> m_xUserDefined;
275     // now for some meta-data attributes; these are not updated directly in the
276     // DOM because updates (detecting "empty" elements) would be quite messy
277     OUString m_TemplateName;
278     OUString m_TemplateURL;
279     css::util::DateTime m_TemplateDate;
280     OUString m_AutoloadURL;
281     sal_Int32 m_AutoloadSecs;
282     OUString m_DefaultTarget;
283 
284     /// check if we are initialized properly
285     void checkInit(std::unique_lock<std::mutex>& rGuard) const;
286     /// initialize state from given DOM tree
287     void init(std::unique_lock<std::mutex>& rGuard, const css::uno::Reference<css::xml::dom::XDocument>& i_xDom);
288     /// update element in DOM tree
289     void updateElement(std::unique_lock<std::mutex>& rGuard,
290         const OUString & i_name,
291         std::vector<std::pair<OUString, OUString> >* i_pAttrs = nullptr);
292     /// update user-defined meta data and attributes in DOM tree
293     void updateUserDefinedAndAttributes(std::unique_lock<std::mutex>& rGuard);
294     /// create empty DOM tree (XDocument)
295     css::uno::Reference<css::xml::dom::XDocument> createDOM() const;
296     /// extract base URL (necessary for converting relative links)
297     css::uno::Reference<css::beans::XPropertySet> getURLProperties(
298         std::unique_lock<std::mutex>& rGuard,
299         const css::uno::Sequence<css::beans::PropertyValue> & i_rMedium) const;
300     /// get text of standard meta data element
301     OUString getMetaText(std::unique_lock<std::mutex>& rGuard, const char* i_name) const;
302     /// set text of standard meta data element iff not equal to existing text
303     bool setMetaText(std::unique_lock<std::mutex>& g, const OUString& i_name,
304         const OUString & i_rValue);
305     /// set text of standard meta data element iff not equal to existing text
306     void setMetaTextAndNotify(const OUString& i_name,
307         const OUString & i_rValue);
308     /// get text of standard meta data element's attribute
309     OUString getMetaAttr(std::unique_lock<std::mutex>& rGuard,
310         const OUString& i_name,
311         const OUString& i_attr) const;
312     /// get text of a list of standard meta data elements (multiple occ.)
313     css::uno::Sequence< OUString > getMetaList(
314         std::unique_lock<std::mutex>& rGuard,
315         const char* i_name) const;
316     /// set text of a list of standard meta data elements (multiple occ.)
317     bool setMetaList(std::unique_lock<std::mutex>& rGuard, const OUString& i_name,
318         const css::uno::Sequence< OUString > & i_rValue,
319         AttrVector const*);
320     void createUserDefined(std::unique_lock<std::mutex>& rGuard);
321 };
322 
323 typedef ::cppu::ImplInheritanceHelper< SfxDocumentMetaData, css::document::XCompatWriterDocProperties > CompatWriterDocPropsImpl_BASE;
324 
325 class CompatWriterDocPropsImpl : public CompatWriterDocPropsImpl_BASE
326 {
327     OUString msManager;
328     OUString msCategory;
329     OUString msCompany;
330 protected:
createMe(css::uno::Reference<css::uno::XComponentContext> const & context)331     virtual rtl::Reference<SfxDocumentMetaData> createMe( css::uno::Reference< css::uno::XComponentContext > const & context ) override { return new CompatWriterDocPropsImpl( context ); };
332 public:
CompatWriterDocPropsImpl(css::uno::Reference<css::uno::XComponentContext> const & context)333     explicit CompatWriterDocPropsImpl( css::uno::Reference< css::uno::XComponentContext > const & context) : CompatWriterDocPropsImpl_BASE( context ) {}
334 
335 // XCompatWriterDocPropsImpl
getManager()336     virtual OUString SAL_CALL getManager() override { return msManager; }
setManager(const OUString & _manager)337     virtual void SAL_CALL setManager( const OUString& _manager ) override { msManager = _manager; }
getCategory()338     virtual OUString SAL_CALL getCategory() override { return msCategory; }
setCategory(const OUString & _category)339     virtual void SAL_CALL setCategory( const OUString& _category ) override { msCategory = _category; }
getCompany()340     virtual OUString SAL_CALL getCompany() override { return msCompany; }
setCompany(const OUString & _company)341     virtual void SAL_CALL setCompany( const OUString& _company ) override { msCompany = _company; }
342 
343 // XServiceInfo
getImplementationName()344     virtual OUString SAL_CALL getImplementationName(  ) override
345     {
346         return u"CompatWriterDocPropsImpl"_ustr;
347     }
348 
supportsService(const OUString & ServiceName)349     virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override
350     {
351         return cppu::supportsService(this, ServiceName);
352     }
353 
getSupportedServiceNames()354     virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames(  ) override
355     {
356         css::uno::Sequence<OUString> aServiceNames { u"com.sun.star.writer.DocumentProperties"_ustr };
357         return aServiceNames;
358     }
359 };
360 
361 constexpr OUString sMetaPageCount = u"meta:page-count"_ustr;
362 constexpr OUString sMetaTableCount = u"meta:table-count"_ustr;
363 constexpr OUString sMetaDrawCount = u"meta:draw-count"_ustr;
364 constexpr OUString sMetaImageCount = u"meta:image-count"_ustr;
365 constexpr OUString sMetaObjectCount = u"meta:object-count"_ustr;
366 constexpr OUString sMetaOleObjectCount = u"meta:ole-object-count"_ustr;
367 constexpr OUString sMetaParagraphCount = u"meta:paragraph-count"_ustr;
368 constexpr OUString sMetaWordCount = u"meta:word-count"_ustr;
369 constexpr OUString sMetaCharacterCount = u"meta:character-count"_ustr;
370 constexpr OUString sMetaRowCount = u"meta:row-count"_ustr;
371 constexpr OUString sMetaFrameCount = u"meta:frame-count"_ustr;
372 constexpr OUString sMetaSentenceCount = u"meta:sentence-count"_ustr;
373 constexpr OUString sMetaSyllableCount = u"meta:syllable-count"_ustr;
374 constexpr OUString sMetaNonWhitespaceCharacterCount = u"meta:non-whitespace-character-count"_ustr;
375 constexpr OUString sMetaCellCount = u"meta:cell-count"_ustr;
376 
377 // NB: keep these two arrays in sync!
378 constexpr OUString s_stdStatAttrs[] = {
379     sMetaPageCount,
380     sMetaTableCount,
381     sMetaDrawCount,
382     sMetaImageCount,
383     sMetaObjectCount,
384     sMetaOleObjectCount,
385     sMetaParagraphCount,
386     sMetaWordCount,
387     sMetaCharacterCount,
388     sMetaRowCount,
389     sMetaFrameCount,
390     sMetaSentenceCount,
391     sMetaSyllableCount,
392     sMetaNonWhitespaceCharacterCount,
393     sMetaCellCount
394 };
395 
396 // NB: keep these two arrays in sync!
397 const char* const s_stdStats[] = {
398     "PageCount",
399     "TableCount",
400     "DrawCount",
401     "ImageCount",
402     "ObjectCount",
403     "OLEObjectCount",
404     "ParagraphCount",
405     "WordCount",
406     "CharacterCount",
407     "RowCount",
408     "FrameCount",
409     "SentenceCount",
410     "SyllableCount",
411     "NonWhitespaceCharacterCount",
412     "CellCount",
413     nullptr
414 };
415 
416 const char* const s_stdMeta[] = {
417     "meta:generator",           // string
418     "dc:title",                 // string
419     "dc:description",           // string
420     "dc:subject",               // string
421     "meta:initial-creator",     // string
422     "dc:creator",               // string
423     "meta:printed-by",          // string
424     "meta:creation-date",       // dateTime
425     "dc:date",                  // dateTime
426     "meta:print-date",          // dateTime
427     "meta:template",            // XLink
428     "meta:auto-reload",
429     "meta:hyperlink-behaviour",
430     "dc:language",              // language
431     "meta:editing-cycles",      // nonNegativeInteger
432     "meta:editing-duration",    // duration
433     "meta:document-statistic",  // ... // note: statistic is singular, no s!
434     "dc:coverage",
435     "dc:identifier",
436     "dc:rights",
437     "dc:source",
438     "dc:type",
439     nullptr
440 };
441 
442 constexpr OUString sMetaKeyword = u"meta:keyword"_ustr;
443 constexpr OUString sMetaUserDefined = u"meta:user-defined"_ustr;
444 constexpr OUString sDCContributor = u"dc:contributor"_ustr;
445 constexpr OUString sDCPublisher = u"dc:publisher"_ustr;
446 constexpr OUString sDCRelation = u"dc:relation"_ustr;
447 constexpr OUString s_stdMetaList[] {
448     sMetaKeyword,             // string*
449     sMetaUserDefined,        // ...*
450     sDCContributor, // string*
451     sDCPublisher, // string*
452     sDCRelation, // string*
453 };
454 
455 constexpr OUStringLiteral s_nsXLink = u"http://www.w3.org/1999/xlink";
456 constexpr OUString s_nsDC = u"http://purl.org/dc/elements/1.1/"_ustr;
457 constexpr OUString s_nsODF = u"urn:oasis:names:tc:opendocument:xmlns:office:1.0"_ustr;
458 constexpr OUString s_nsODFMeta = u"urn:oasis:names:tc:opendocument:xmlns:meta:1.0"_ustr;
459 // constexpr OUStringLiteral s_nsOOo = "http://openoffice.org/2004/office"; // not used (yet?)
460 
461 constexpr OUString s_meta = u"meta.xml"_ustr;
462 
isValidDate(const css::util::Date & i_rDate)463 bool isValidDate(const css::util::Date & i_rDate)
464 {
465     return i_rDate.Month > 0;
466 }
467 
isValidDateTime(const css::util::DateTime & i_rDateTime)468 bool isValidDateTime(const css::util::DateTime & i_rDateTime)
469 {
470     return i_rDateTime.Month > 0;
471 }
472 
473 std::pair< OUString, OUString >
getQualifier(const OUString & nm)474 getQualifier(const OUString& nm) {
475     sal_Int32 ix = nm.indexOf(u':');
476     if (ix == -1) {
477         return std::make_pair(OUString(), nm);
478     } else {
479         return std::make_pair(nm.copy(0,ix), nm.copy(ix+1));
480     }
481 }
482 
483 // get namespace for standard qualified names
484 // NB: only call this with statically known strings!
getNameSpace(const OUString & i_qname)485 OUString getNameSpace(const OUString& i_qname) noexcept
486 {
487     OUString ns;
488     OUString n = getQualifier(i_qname).first;
489     if ( n == "xlink" ) ns = s_nsXLink;
490     if ( n == "dc" ) ns = s_nsDC;
491     if ( n == "office" ) ns = s_nsODF;
492     if ( n == "meta" ) ns = s_nsODFMeta;
493     assert(!ns.isEmpty());
494     return ns;
495 }
496 
497 bool
textToDateOrDateTime(css::util::Date & io_rd,css::util::DateTime & io_rdt,bool & o_rIsDateTime,std::optional<sal_Int16> & o_rTimeZone,const OUString & i_text)498 textToDateOrDateTime(css::util::Date & io_rd, css::util::DateTime & io_rdt,
499         bool & o_rIsDateTime, std::optional<sal_Int16> & o_rTimeZone,
500         const OUString& i_text) noexcept
501 {
502     if (::sax::Converter::parseDateOrDateTime(
503                 &io_rd, io_rdt, o_rIsDateTime, &o_rTimeZone, i_text)) {
504         return true;
505     } else {
506         SAL_WARN_IF(!i_text.isEmpty(), "sfx.doc", "Invalid date: " << i_text);
507         return false;
508     }
509 }
510 
511 // convert string to date/time
512 bool
textToDateTime(css::util::DateTime & io_rdt,const OUString & i_text)513 textToDateTime(css::util::DateTime & io_rdt, const OUString& i_text) noexcept
514 {
515     if (::sax::Converter::parseDateTime(io_rdt, i_text)) {
516         return true;
517     } else {
518         SAL_WARN_IF(!i_text.isEmpty(), "sfx.doc", "Invalid date: " << i_text);
519         return false;
520     }
521 }
522 
523 // convert string to date/time with default return value
524 css::util::DateTime
textToDateTimeDefault(const OUString & i_text)525 textToDateTimeDefault(const OUString& i_text) noexcept
526 {
527     css::util::DateTime dt;
528     static_cast<void> (textToDateTime(dt, i_text));
529     // on conversion error: return default value (unchanged)
530     return dt;
531 }
532 
533 // convert date to string
534 OUString
dateToText(css::util::Date const & i_rd,sal_Int16 const * const pTimeZone)535 dateToText(css::util::Date const& i_rd,
536            sal_Int16 const*const pTimeZone) noexcept
537 {
538     if (isValidDate(i_rd)) {
539         OUStringBuffer buf;
540         ::sax::Converter::convertDate(buf, i_rd, pTimeZone);
541         return buf.makeStringAndClear();
542     } else {
543         return OUString();
544     }
545 }
546 
547 
548 // convert date/time to string
549 OUString
dateTimeToText(css::util::DateTime const & i_rdt,sal_Int16 const * const pTimeZone=nullptr)550 dateTimeToText(css::util::DateTime const& i_rdt,
551                sal_Int16 const*const pTimeZone = nullptr) noexcept
552 {
553     if (isValidDateTime(i_rdt)) {
554         OUStringBuffer buf(32);
555         ::sax::Converter::convertDateTime(buf, i_rdt, pTimeZone, true);
556         return buf.makeStringAndClear();
557     } else {
558         return OUString();
559     }
560 }
561 
562 // convert string to duration
563 bool
textToDuration(css::util::Duration & io_rDur,OUString const & i_rText)564 textToDuration(css::util::Duration& io_rDur, OUString const& i_rText)
565 noexcept
566 {
567     if (::sax::Converter::convertDuration(io_rDur, i_rText)) {
568         return true;
569     } else {
570         SAL_WARN_IF(!i_rText.isEmpty(), "sfx.doc", "Invalid duration: " << i_rText);
571         return false;
572     }
573 }
574 
textToDuration(OUString const & i_rText)575 sal_Int32 textToDuration(OUString const& i_rText) noexcept
576 {
577     css::util::Duration d;
578     if (textToDuration(d, i_rText)) {
579         // #i107372#: approximate years/months
580         const sal_Int32 days( (d.Years * 365) + (d.Months * 30) + d.Days );
581         return  (days * (24*3600))
582                 + (d.Hours * 3600) + (d.Minutes * 60) + d.Seconds;
583     } else {
584         return 0; // default
585     }
586 }
587 
588 // convert duration to string
durationToText(css::util::Duration const & i_rDur)589 OUString durationToText(css::util::Duration const& i_rDur) noexcept
590 {
591     OUStringBuffer buf;
592     ::sax::Converter::convertDuration(buf, i_rDur);
593     return buf.makeStringAndClear();
594 }
595 
596 // convert duration to string
durationToText(sal_Int32 i_value)597 OUString durationToText(sal_Int32 i_value) noexcept
598 {
599     css::util::Duration ud;
600     ud.Days    = static_cast<sal_Int16>(i_value / (24 * 3600));
601     ud.Hours   = static_cast<sal_Int16>((i_value % (24 * 3600)) / 3600);
602     ud.Minutes = static_cast<sal_Int16>((i_value % 3600) / 60);
603     ud.Seconds = static_cast<sal_Int16>(i_value % 60);
604     ud.NanoSeconds = 0;
605     return durationToText(ud);
606 }
607 
608 // extract base URL (necessary for converting relative links)
609 css::uno::Reference< css::beans::XPropertySet >
getURLProperties(std::unique_lock<std::mutex> &,const css::uno::Sequence<css::beans::PropertyValue> & i_rMedium) const610 SfxDocumentMetaData::getURLProperties(
611     std::unique_lock<std::mutex>& /*rGuard*/,
612     const css::uno::Sequence< css::beans::PropertyValue > & i_rMedium) const
613 {
614     css::uno::Reference< css::beans::XPropertyBag> xPropArg = css::beans::PropertyBag::createDefault( m_xContext );
615     try {
616         css::uno::Any baseUri;
617         for (const auto& rProp : i_rMedium) {
618             if (rProp.Name == "DocumentBaseURL") {
619                 baseUri = rProp.Value;
620             } else if (rProp.Name == "URL") {
621                 if (!baseUri.hasValue()) {
622                     baseUri = rProp.Value;
623                 }
624             } else if (rProp.Name == "HierarchicalDocumentName") {
625                 xPropArg->addProperty(
626                     u"StreamRelPath"_ustr,
627                     css::beans::PropertyAttribute::MAYBEVOID,
628                     rProp.Value);
629             }
630         }
631         if (baseUri.hasValue()) {
632             xPropArg->addProperty(
633                 u"BaseURI"_ustr, css::beans::PropertyAttribute::MAYBEVOID,
634                 baseUri);
635         }
636         xPropArg->addProperty(u"StreamName"_ustr,
637                 css::beans::PropertyAttribute::MAYBEVOID,
638                 css::uno::Any(s_meta));
639     } catch (const css::uno::Exception &) {
640         // ignore
641     }
642     return css::uno::Reference< css::beans::XPropertySet>(xPropArg,
643                 css::uno::UNO_QUERY_THROW);
644 }
645 
646 // return the text of the (hopefully unique, i.e., normalize first!) text
647 // node _below_ the given node
648 /// @throws css::uno::RuntimeException
649 OUString
getNodeText(const css::uno::Reference<css::xml::dom::XNode> & i_xNode)650 getNodeText(const css::uno::Reference<css::xml::dom::XNode>& i_xNode)
651 {
652     if (!i_xNode.is())
653         throw css::uno::RuntimeException(u"SfxDocumentMetaData::getNodeText: argument is null"_ustr, i_xNode);
654     for (css::uno::Reference<css::xml::dom::XNode> c = i_xNode->getFirstChild();
655             c.is();
656             c = c->getNextSibling()) {
657         if (c->getNodeType() == css::xml::dom::NodeType_TEXT_NODE) {
658             try {
659                 return c->getNodeValue();
660             } catch (const css::xml::dom::DOMException &) { // too big?
661                 return OUString();
662             }
663         }
664     }
665     return OUString();
666 }
667 
668 OUString
getMetaText(std::unique_lock<std::mutex> & rGuard,const char * i_name) const669 SfxDocumentMetaData::getMetaText(std::unique_lock<std::mutex>& rGuard, const char* i_name) const
670 //        throw (css::uno::RuntimeException)
671 {
672     checkInit(rGuard);
673 
674     const OUString name( OUString::createFromAscii(i_name) );
675     assert(m_meta.find(name) != m_meta.end());
676     css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
677     return (xNode.is()) ? getNodeText(xNode) : OUString();
678 }
679 
680 bool
setMetaText(std::unique_lock<std::mutex> & rGuard,const OUString & name,const OUString & i_rValue)681 SfxDocumentMetaData::setMetaText(std::unique_lock<std::mutex>& rGuard, const OUString& name,
682         const OUString & i_rValue)
683     // throw (css::uno::RuntimeException)
684 {
685     checkInit(rGuard);
686 
687     assert(m_meta.find(name) != m_meta.end());
688     css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
689 
690     try {
691         if (i_rValue.isEmpty()) {
692             if (xNode.is()) { // delete
693                 m_xParent->removeChild(xNode);
694                 xNode.clear();
695                 m_meta[name] = std::move(xNode);
696                 return true;
697             } else {
698                 return false;
699             }
700         } else {
701             if (xNode.is()) { // update
702                 for (css::uno::Reference<css::xml::dom::XNode> c =
703                             xNode->getFirstChild();
704                         c.is();
705                         c = c->getNextSibling()) {
706                     if (c->getNodeType() == css::xml::dom::NodeType_TEXT_NODE) {
707                         if (c->getNodeValue() != i_rValue) {
708                             c->setNodeValue(i_rValue);
709                             return true;
710                         } else {
711                             return false;
712                         }
713                     }
714                 }
715             } else { // insert
716                 xNode.set(m_xDoc->createElementNS(getNameSpace(name), name),
717                             css::uno::UNO_QUERY_THROW);
718                 m_xParent->appendChild(xNode);
719                 m_meta[name] = xNode;
720             }
721             css::uno::Reference<css::xml::dom::XNode> xTextNode(
722                 m_xDoc->createTextNode(i_rValue), css::uno::UNO_QUERY_THROW);
723             xNode->appendChild(xTextNode);
724             return true;
725         }
726     } catch (const css::xml::dom::DOMException &) {
727         css::uno::Any anyEx = cppu::getCaughtException();
728         throw css::lang::WrappedTargetRuntimeException(
729                 u"SfxDocumentMetaData::setMetaText: DOM exception"_ustr,
730                 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
731     }
732 }
733 
734 void
setMetaTextAndNotify(const OUString & i_name,const OUString & i_rValue)735 SfxDocumentMetaData::setMetaTextAndNotify(const OUString & i_name,
736         const OUString & i_rValue)
737     // throw (css::uno::RuntimeException)
738 {
739     std::unique_lock g(m_aMutex);
740     if (setMetaText(g, i_name, i_rValue)) {
741         g.unlock();
742         setModified(true);
743     }
744 }
745 
746 OUString
getMetaAttr(std::unique_lock<std::mutex> &,const OUString & name,const OUString & i_attr) const747 SfxDocumentMetaData::getMetaAttr(std::unique_lock<std::mutex>& /*rGuard*/, const OUString& name, const OUString& i_attr) const
748 //        throw (css::uno::RuntimeException)
749 {
750     assert(m_meta.find(name) != m_meta.end());
751     css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
752     if (xNode.is()) {
753         css::uno::Reference<css::xml::dom::XElement> xElem(xNode,
754             css::uno::UNO_QUERY_THROW);
755         return xElem->getAttributeNS(getNameSpace(i_attr),
756                     getQualifier(i_attr).second);
757     } else {
758         return OUString();
759     }
760 }
761 
762 css::uno::Sequence< OUString>
getMetaList(std::unique_lock<std::mutex> & rGuard,const char * i_name) const763 SfxDocumentMetaData::getMetaList(std::unique_lock<std::mutex>& rGuard, const char* i_name) const
764 //        throw (css::uno::RuntimeException)
765 {
766     checkInit(rGuard);
767     OUString name = OUString::createFromAscii(i_name);
768     assert(m_metaList.find(name) != m_metaList.end());
769     std::vector<css::uno::Reference<css::xml::dom::XNode> > const & vec =
770         m_metaList.find(name)->second;
771     css::uno::Sequence< OUString> ret(vec.size());
772     std::transform(vec.begin(), vec.end(), ret.getArray(),
773                    [](const auto& node) { return getNodeText(node); });
774     return ret;
775 }
776 
777 bool
setMetaList(std::unique_lock<std::mutex> & rGuard,const OUString & name,const css::uno::Sequence<OUString> & i_rValue,AttrVector const * i_pAttrs)778 SfxDocumentMetaData::setMetaList(std::unique_lock<std::mutex>& rGuard, const OUString& name,
779         const css::uno::Sequence<OUString> & i_rValue,
780         AttrVector const* i_pAttrs)
781     // throw (css::uno::RuntimeException)
782 {
783     checkInit(rGuard);
784     assert((i_pAttrs == nullptr) ||
785            (static_cast<size_t>(i_rValue.getLength()) == i_pAttrs->size()));
786 
787     try {
788         assert(m_metaList.find(name) != m_metaList.end());
789         std::vector<css::uno::Reference<css::xml::dom::XNode> > & vec =
790             m_metaList[name];
791 
792         // if nothing changed, do nothing
793         // alas, this does not check for permutations, or attributes...
794         if (nullptr == i_pAttrs) {
795             if (static_cast<size_t>(i_rValue.getLength()) == vec.size()) {
796                 bool isEqual(true);
797                 for (sal_Int32 i = 0; i < i_rValue.getLength(); ++i) {
798                     css::uno::Reference<css::xml::dom::XNode> xNode(vec.at(i));
799                     if (xNode.is()) {
800                         OUString val = getNodeText(xNode);
801                         if (val != i_rValue[i]) {
802                             isEqual = false;
803                             break;
804                         }
805                     }
806                 }
807                 if (isEqual) return false;
808             }
809         }
810 
811         // remove old meta data nodes
812         {
813             std::vector<css::uno::Reference<css::xml::dom::XNode> >
814                 ::reverse_iterator it(vec.rbegin());
815             try {
816                 for ( ;it != vec.rend(); ++it)
817                 {
818                     m_xParent->removeChild(*it);
819                 }
820             }
821             catch (...)
822             {
823                 // Clean up already removed nodes
824                 vec.erase(it.base(), vec.end());
825                 throw;
826             }
827             vec.clear();
828         }
829 
830         // insert new meta data nodes into DOM tree
831         for (sal_Int32 i = 0; i < i_rValue.getLength(); ++i) {
832             css::uno::Reference<css::xml::dom::XElement> xElem(
833                 m_xDoc->createElementNS(getNameSpace(name), name),
834                 css::uno::UNO_SET_THROW);
835             css::uno::Reference<css::xml::dom::XNode> xNode(xElem,
836                 css::uno::UNO_QUERY_THROW);
837             css::uno::Reference<css::xml::dom::XNode> xTextNode(
838                 m_xDoc->createTextNode(i_rValue[i]), css::uno::UNO_QUERY_THROW);
839             // set attributes
840             if (i_pAttrs != nullptr) {
841                 for (auto const& elem : (*i_pAttrs)[i])
842                 {
843                     xElem->setAttributeNS(getNameSpace(elem.first),
844                         elem.first, elem.second);
845                 }
846             }
847             xNode->appendChild(xTextNode);
848             m_xParent->appendChild(xNode);
849             vec.push_back(xNode);
850         }
851 
852         return true;
853     } catch (const css::xml::dom::DOMException &) {
854         css::uno::Any anyEx = cppu::getCaughtException();
855         throw css::lang::WrappedTargetRuntimeException(
856                 u"SfxDocumentMetaData::setMetaList: DOM exception"_ustr,
857                 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
858     }
859 }
860 
861 // convert property list to string list and attribute list
862 std::pair<css::uno::Sequence< OUString>, AttrVector>
propsToStrings(css::uno::Reference<css::beans::XPropertySet> const & i_xPropSet)863 propsToStrings(css::uno::Reference<css::beans::XPropertySet> const & i_xPropSet)
864 {
865     ::std::vector< OUString > values;
866     AttrVector attrs;
867 
868     css::uno::Reference<css::beans::XPropertySetInfo> xSetInfo
869         = i_xPropSet->getPropertySetInfo();
870     css::uno::Sequence<css::beans::Property> props = xSetInfo->getProperties();
871 
872     for (sal_Int32 i = 0; i < props.getLength(); ++i) {
873         if (props[i].Attributes & css::beans::PropertyAttribute::TRANSIENT) {
874             continue;
875         }
876         const OUString name = props[i].Name;
877         css::uno::Any any;
878         try {
879             any = i_xPropSet->getPropertyValue(name);
880         } catch (const css::uno::Exception &) {
881             // ignore
882         }
883         const css::uno::Type & type = any.getValueType();
884         std::vector<std::pair<OUString, OUString> > as;
885         as.emplace_back("meta:name", name);
886         static constexpr OUString vt = u"meta:value-type"_ustr;
887 
888         // convert according to type
889         if (type == ::cppu::UnoType<bool>::get()) {
890             bool b = false;
891             any >>= b;
892             OUStringBuffer buf;
893             ::sax::Converter::convertBool(buf, b);
894             values.push_back(buf.makeStringAndClear());
895             as.emplace_back(vt, u"boolean"_ustr);
896         } else if (type == ::cppu::UnoType< OUString>::get()) {
897             OUString s;
898             any >>= s;
899             values.push_back(s);
900 // #i90847# OOo 2.x does stupid things if value-type="string";
901 // fortunately string is default anyway, so we can just omit it
902 // #i107502#: however, OOo 2.x only reads 4 user-defined without @value-type
903 // => best backward compatibility: first 4 without @value-type, rest with
904             if (4 <= i)
905             {
906                 as.emplace_back(vt, u"string"_ustr);
907             }
908         } else if (type == ::cppu::UnoType<css::util::DateTime>::get()) {
909             css::util::DateTime dt;
910             any >>= dt;
911             values.push_back(dateTimeToText(dt));
912             as.emplace_back(vt, u"date"_ustr);
913         } else if (type == ::cppu::UnoType<css::util::Date>::get()) {
914             css::util::Date d;
915             any >>= d;
916             values.push_back(dateToText(d, nullptr));
917             as.emplace_back(vt,u"date"_ustr);
918         } else if (type == ::cppu::UnoType<css::util::DateTimeWithTimezone>::get()) {
919             css::util::DateTimeWithTimezone dttz;
920             any >>= dttz;
921             values.push_back(dateTimeToText(dttz.DateTimeInTZ, &dttz.Timezone));
922             as.emplace_back(vt, u"date"_ustr);
923         } else if (type == ::cppu::UnoType<css::util::DateWithTimezone>::get()) {
924             css::util::DateWithTimezone dtz;
925             any >>= dtz;
926             values.push_back(dateToText(dtz.DateInTZ, &dtz.Timezone));
927             as.emplace_back(vt, u"date"_ustr);
928         } else if (type == ::cppu::UnoType<css::util::Time>::get()) {
929             // #i97029#: replaced by Duration
930             // Time is supported for backward compatibility with OOo 3.x, x<=2
931             css::util::Time ut;
932             any >>= ut;
933             css::util::Duration ud;
934             ud.Hours   = ut.Hours;
935             ud.Minutes = ut.Minutes;
936             ud.Seconds = ut.Seconds;
937             ud.NanoSeconds = ut.NanoSeconds;
938             values.push_back(durationToText(ud));
939             as.emplace_back(vt, u"time"_ustr);
940         } else if (type == ::cppu::UnoType<css::util::Duration>::get()) {
941             css::util::Duration ud;
942             any >>= ud;
943             values.push_back(durationToText(ud));
944             as.emplace_back(vt, u"time"_ustr);
945         } else if (::cppu::UnoType<double>::get().isAssignableFrom(type)) {
946             // support not just double, but anything that can be converted
947             double d = 0;
948             any >>= d;
949             OUStringBuffer buf;
950             ::sax::Converter::convertDouble(buf, d);
951             values.push_back(buf.makeStringAndClear());
952             as.emplace_back(vt, u"float"_ustr);
953         } else {
954             SAL_WARN("sfx.doc", "Unsupported property type: " << any.getValueTypeName() );
955             continue;
956         }
957         attrs.push_back(std::move(as));
958     }
959 
960     return std::make_pair(comphelper::containerToSequence(values), attrs);
961 }
962 
963 // remove the given element from the DOM, and iff i_pAttrs != 0 insert new one
964 void
updateElement(std::unique_lock<std::mutex> &,const OUString & name,std::vector<std::pair<OUString,OUString>> * i_pAttrs)965 SfxDocumentMetaData::updateElement(std::unique_lock<std::mutex>& /*rGuard*/, const OUString& name,
966         std::vector<std::pair<OUString, OUString> >* i_pAttrs)
967 {
968     try {
969         // remove old element
970         css::uno::Reference<css::xml::dom::XNode> xNode =
971             m_meta.find(name)->second;
972         if (xNode.is()) {
973             m_xParent->removeChild(xNode);
974             xNode.clear();
975         }
976         // add new element
977         if (nullptr != i_pAttrs) {
978             css::uno::Reference<css::xml::dom::XElement> xElem(
979                 m_xDoc->createElementNS(getNameSpace(name), name),
980                     css::uno::UNO_SET_THROW);
981             xNode.set(xElem, css::uno::UNO_QUERY_THROW);
982             // set attributes
983             for (auto const& elem : *i_pAttrs)
984             {
985                 xElem->setAttributeNS(getNameSpace(elem.first),
986                     elem.first, elem.second);
987             }
988             m_xParent->appendChild(xNode);
989         }
990         m_meta[name] = std::move(xNode);
991     } catch (const css::xml::dom::DOMException &) {
992         css::uno::Any anyEx = cppu::getCaughtException();
993         throw css::lang::WrappedTargetRuntimeException(
994                 u"SfxDocumentMetaData::updateElement: DOM exception"_ustr,
995                 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
996     }
997 }
998 
999 // update user-defined meta data in DOM tree
updateUserDefinedAndAttributes(std::unique_lock<std::mutex> & g)1000 void SfxDocumentMetaData::updateUserDefinedAndAttributes(std::unique_lock<std::mutex>& g)
1001 {
1002     createUserDefined(g);
1003     const css::uno::Reference<css::beans::XPropertySet> xPSet(m_xUserDefined,
1004             css::uno::UNO_QUERY_THROW);
1005     const std::pair<css::uno::Sequence< OUString>, AttrVector>
1006         udStringsAttrs( propsToStrings(xPSet) );
1007     (void) setMetaList(g, u"meta:user-defined"_ustr, udStringsAttrs.first,
1008             &udStringsAttrs.second);
1009 
1010     // update elements with attributes
1011     std::vector<std::pair<OUString, OUString> > attributes;
1012     if (!m_TemplateName.isEmpty() || !m_TemplateURL.isEmpty()
1013             || isValidDateTime(m_TemplateDate)) {
1014         attributes.emplace_back("xlink:type", u"simple"_ustr);
1015         attributes.emplace_back("xlink:actuate", u"onRequest"_ustr);
1016         attributes.emplace_back("xlink:title", m_TemplateName);
1017         attributes.emplace_back("xlink:href", m_TemplateURL );
1018         if (isValidDateTime(m_TemplateDate)) {
1019             attributes.emplace_back(
1020                 "meta:date", dateTimeToText(m_TemplateDate));
1021         }
1022         updateElement(g, u"meta:template"_ustr, &attributes);
1023     } else {
1024         updateElement(g, u"meta:template"_ustr);
1025     }
1026     attributes.clear();
1027 
1028     if (!m_AutoloadURL.isEmpty() || (0 != m_AutoloadSecs)) {
1029         attributes.emplace_back("xlink:href", m_AutoloadURL );
1030         attributes.emplace_back("meta:delay",
1031                 durationToText(m_AutoloadSecs));
1032         updateElement(g, u"meta:auto-reload"_ustr, &attributes);
1033     } else {
1034         updateElement(g, u"meta:auto-reload"_ustr);
1035     }
1036     attributes.clear();
1037 
1038     if (!m_DefaultTarget.isEmpty()) {
1039         attributes.emplace_back(
1040                 "office:target-frame-name",
1041                 m_DefaultTarget);
1042         // xlink:show: _blank -> new, any other value -> replace
1043         const char* show = m_DefaultTarget == "_blank" ? "new" : "replace";
1044         attributes.emplace_back(
1045                 "xlink:show",
1046                 OUString::createFromAscii(show));
1047         updateElement(g, u"meta:hyperlink-behaviour"_ustr, &attributes);
1048     } else {
1049         updateElement(g, u"meta:hyperlink-behaviour"_ustr);
1050     }
1051     attributes.clear();
1052 }
1053 
1054 // create empty DOM tree (XDocument)
1055 css::uno::Reference<css::xml::dom::XDocument>
createDOM() const1056 SfxDocumentMetaData::createDOM() const // throw (css::uno::RuntimeException)
1057 {
1058     css::uno::Reference<css::xml::dom::XDocumentBuilder> xBuilder( css::xml::dom::DocumentBuilder::create(m_xContext) );
1059     css::uno::Reference<css::xml::dom::XDocument> xDoc = xBuilder->newDocument();
1060     if (!xDoc.is())
1061         throw css::uno::RuntimeException(
1062                 u"SfxDocumentMetaData::createDOM: cannot create new document"_ustr,
1063                 *const_cast<SfxDocumentMetaData*>(this));
1064     return xDoc;
1065 }
1066 
1067 void
checkInit(std::unique_lock<std::mutex> &) const1068 SfxDocumentMetaData::checkInit(std::unique_lock<std::mutex>& /*rGuard*/) const // throw (css::uno::RuntimeException)
1069 {
1070     if (!m_isInitialized) {
1071         throw css::uno::RuntimeException(
1072                 u"SfxDocumentMetaData::checkInit: not initialized"_ustr,
1073                 *const_cast<SfxDocumentMetaData*>(this));
1074     }
1075     assert(m_xDoc.is() && m_xParent.is());
1076 }
1077 
extractTagAndNamespaceUri(std::u16string_view aChildNodeName,std::u16string_view & rTagName,std::u16string_view & rNamespaceURI)1078 void extractTagAndNamespaceUri(std::u16string_view aChildNodeName,
1079                 std::u16string_view& rTagName, std::u16string_view& rNamespaceURI)
1080 {
1081     size_t idx = aChildNodeName.find(':');
1082     assert(idx != std::u16string_view::npos);
1083     std::u16string_view aPrefix = aChildNodeName.substr(0, idx);
1084     rTagName = aChildNodeName.substr(idx + 1);
1085     if (aPrefix == u"dc")
1086         rNamespaceURI = s_nsDC;
1087     else if (aPrefix == u"meta")
1088         rNamespaceURI = s_nsODFMeta;
1089     else if (aPrefix == u"office")
1090         rNamespaceURI = s_nsODF;
1091     else
1092         assert(false);
1093 }
1094 
1095 
getChildNodeByName(const css::uno::Reference<css::xml::dom::XNode> & xNode,std::u16string_view aChildNodeName)1096 css::uno::Reference<css::xml::dom::XElement> getChildNodeByName(
1097                 const css::uno::Reference<css::xml::dom::XNode>& xNode,
1098                 std::u16string_view aChildNodeName)
1099 {
1100     css::uno::Reference< css::xml::dom::XNodeList > xList = xNode->getChildNodes();
1101     if (!xList)
1102         return nullptr;
1103     std::u16string_view aTagName, aNamespaceURI;
1104     extractTagAndNamespaceUri(aChildNodeName, aTagName, aNamespaceURI);
1105 
1106     const sal_Int32 nLength(xList->getLength());
1107     for (sal_Int32 a(0); a < nLength; a++)
1108     {
1109         const css::uno::Reference< css::xml::dom::XElement > xChild(xList->item(a), css::uno::UNO_QUERY);
1110         if (xChild && xChild->getNodeName() == aTagName && aNamespaceURI == xChild->getNamespaceURI())
1111             return xChild;
1112     }
1113     return nullptr;
1114 }
1115 
1116 
getChildNodeListByName(const css::uno::Reference<css::xml::dom::XNode> & xNode,std::u16string_view aChildNodeName)1117 std::vector<css::uno::Reference<css::xml::dom::XNode> > getChildNodeListByName(
1118                 const css::uno::Reference<css::xml::dom::XNode>& xNode,
1119                 std::u16string_view aChildNodeName)
1120 {
1121     css::uno::Reference< css::xml::dom::XNodeList > xList = xNode->getChildNodes();
1122     if (!xList)
1123         return {};
1124     std::u16string_view aTagName, aNamespaceURI;
1125     extractTagAndNamespaceUri(aChildNodeName, aTagName, aNamespaceURI);
1126     std::vector<css::uno::Reference<css::xml::dom::XNode>> aList;
1127     const sal_Int32 nLength(xList->getLength());
1128     for (sal_Int32 a(0); a < nLength; a++)
1129     {
1130         const css::uno::Reference< css::xml::dom::XElement > xChild(xList->item(a), css::uno::UNO_QUERY);
1131         if (xChild && xChild->getNodeName() == aTagName && aNamespaceURI == xChild->getNamespaceURI())
1132             aList.push_back(xChild);
1133     }
1134     return aList;
1135 }
1136 
1137 // initialize state from DOM tree
init(std::unique_lock<std::mutex> & g,const css::uno::Reference<css::xml::dom::XDocument> & i_xDoc)1138 void SfxDocumentMetaData::init(
1139         std::unique_lock<std::mutex>& g,
1140         const css::uno::Reference<css::xml::dom::XDocument>& i_xDoc)
1141 {
1142     if (!i_xDoc.is())
1143         throw css::uno::RuntimeException(u"SfxDocumentMetaData::init: no DOM tree given"_ustr, *this);
1144 
1145     m_isInitialized = false;
1146     m_xDoc = i_xDoc;
1147 
1148     // select nodes for standard meta data stuff
1149     // NB: we do not handle the single-XML-file ODF variant, which would
1150     //     have the root element office:document.
1151     //     The root of such documents must be converted in the importer!
1152     css::uno::Reference<css::xml::dom::XNode> xDocNode(
1153         m_xDoc, css::uno::UNO_QUERY_THROW);
1154     m_xParent.clear();
1155     try {
1156         css::uno::Reference<css::xml::dom::XNode> xChild = getChildNodeByName(xDocNode, u"office:document-meta");
1157         if (xChild)
1158             m_xParent = getChildNodeByName(xChild, u"office:meta");
1159     } catch (const css::uno::Exception &) {
1160     }
1161 
1162     if (!m_xParent.is()) {
1163         // all this create/append stuff may throw DOMException
1164         try {
1165             css::uno::Reference<css::xml::dom::XElement> xRElem;
1166             css::uno::Reference<css::xml::dom::XNode> xNode(
1167                 i_xDoc->getFirstChild());
1168             while (xNode.is()) {
1169                 if (css::xml::dom::NodeType_ELEMENT_NODE ==xNode->getNodeType())
1170                 {
1171                     if ( xNode->getNamespaceURI() == s_nsODF && xNode->getLocalName() == "document-meta" )
1172                     {
1173                         xRElem.set(xNode, css::uno::UNO_QUERY_THROW);
1174                         break;
1175                     }
1176                     else
1177                     {
1178                         SAL_INFO("sfx.doc", "SfxDocumentMetaData::init(): "
1179                                 "deleting unexpected root element: "
1180                                 << xNode->getLocalName());
1181                         i_xDoc->removeChild(xNode);
1182                         xNode = i_xDoc->getFirstChild(); // start over
1183                     }
1184                 } else {
1185                     xNode = xNode->getNextSibling();
1186                 }
1187             }
1188             if (!xRElem.is()) {
1189                 static constexpr OUStringLiteral sOfficeDocumentMeta = u"office:document-meta";
1190                 xRElem = i_xDoc->createElementNS(
1191                     s_nsODF, sOfficeDocumentMeta);
1192                 css::uno::Reference<css::xml::dom::XNode> xRNode(xRElem,
1193                     css::uno::UNO_QUERY_THROW);
1194                 i_xDoc->appendChild(xRNode);
1195             }
1196             static constexpr OUStringLiteral sOfficeVersion = u"office:version";
1197             xRElem->setAttributeNS(s_nsODF, sOfficeVersion, u"1.0"_ustr);
1198             // does not exist, otherwise m_xParent would not be null
1199             static constexpr OUStringLiteral sOfficeMeta = u"office:meta";
1200             css::uno::Reference<css::xml::dom::XNode> xParent (
1201                 i_xDoc->createElementNS(s_nsODF, sOfficeMeta),
1202                 css::uno::UNO_QUERY_THROW);
1203             xRElem->appendChild(xParent);
1204             m_xParent = std::move(xParent);
1205         } catch (const css::xml::dom::DOMException &) {
1206             css::uno::Any anyEx = cppu::getCaughtException();
1207             throw css::lang::WrappedTargetRuntimeException(
1208                     u"SfxDocumentMetaData::init: DOM exception"_ustr,
1209                     css::uno::Reference<css::uno::XInterface>(*this), anyEx);
1210         }
1211     }
1212 
1213 
1214     // select nodes for elements of which we only handle one occurrence
1215     for (const char* const* pName = s_stdMeta; *pName != nullptr; ++pName) {
1216         OUString name = OUString::createFromAscii(*pName);
1217         // NB: If a document contains more than one occurrence of a
1218         // meta-data element, we arbitrarily pick one of them here.
1219         // We do not remove the others, i.e., when we write the
1220         // document, it will contain the duplicates unchanged.
1221         // The ODF spec says that handling multiple occurrences is
1222         // application-specific.
1223 
1224         // Do not create an empty element if it is missing;
1225         // for certain elements, such as dateTime, this would be invalid
1226         m_meta[name] = getChildNodeByName(m_xParent, name);
1227     }
1228 
1229     // select nodes for elements of which we handle all occurrences
1230     for (const auto & name : s_stdMetaList) {
1231         m_metaList[name] = getChildNodeListByName(m_xParent, name);
1232     }
1233 
1234     // initialize members corresponding to attributes from DOM nodes
1235     static constexpr OUString sMetaTemplate = u"meta:template"_ustr;
1236     static constexpr OUString sMetaAutoReload = u"meta:auto-reload"_ustr;
1237     static constexpr OUStringLiteral sMetaHyperlinkBehaviour = u"meta:hyperlink-behaviour";
1238     m_TemplateName  = getMetaAttr(g, sMetaTemplate, u"xlink:title"_ustr);
1239     m_TemplateURL   = getMetaAttr(g, sMetaTemplate, u"xlink:href"_ustr);
1240     m_TemplateDate  =
1241         textToDateTimeDefault(getMetaAttr(g, sMetaTemplate, u"meta:date"_ustr));
1242     m_AutoloadURL   = getMetaAttr(g, sMetaAutoReload, u"xlink:href"_ustr);
1243     m_AutoloadSecs  =
1244         textToDuration(getMetaAttr(g, sMetaAutoReload, u"meta:delay"_ustr));
1245     m_DefaultTarget =
1246         getMetaAttr(g, sMetaHyperlinkBehaviour, u"office:target-frame-name"_ustr);
1247 
1248 
1249     std::vector<css::uno::Reference<css::xml::dom::XNode> > & vec =
1250         m_metaList[u"meta:user-defined"_ustr];
1251     m_xUserDefined.clear(); // #i105826#: reset (may be re-initialization)
1252     if ( !vec.empty() )
1253     {
1254         createUserDefined(g);
1255     }
1256 
1257     // user-defined meta data: initialize PropertySet from DOM nodes
1258     for (auto const& elem : vec)
1259     {
1260         css::uno::Reference<css::xml::dom::XElement> xElem(elem,
1261             css::uno::UNO_QUERY_THROW);
1262         css::uno::Any any;
1263         OUString name = xElem->getAttributeNS(s_nsODFMeta, u"name"_ustr);
1264         OUString type = xElem->getAttributeNS(s_nsODFMeta, u"value-type"_ustr);
1265         OUString text = getNodeText(elem);
1266         if ( type == "float" ) {
1267             double d;
1268             if (::sax::Converter::convertDouble(d, text)) {
1269                 any <<= d;
1270             } else {
1271                 SAL_WARN("sfx.doc", "Invalid float: " << text);
1272                 continue;
1273             }
1274         } else if ( type == "date" ) {
1275             bool isDateTime;
1276             css::util::Date d;
1277             css::util::DateTime dt;
1278             std::optional<sal_Int16> nTimeZone;
1279             if (textToDateOrDateTime(d, dt, isDateTime, nTimeZone, text)) {
1280                 if (isDateTime) {
1281                     if (nTimeZone) {
1282                         any <<= css::util::DateTimeWithTimezone(dt,
1283                                     *nTimeZone);
1284                     } else {
1285                         any <<= dt;
1286                     }
1287                 } else {
1288                     if (nTimeZone) {
1289                         any <<= css::util::DateWithTimezone(d, *nTimeZone);
1290                     } else {
1291                         any <<= d;
1292                     }
1293                 }
1294             } else {
1295                 SAL_WARN("sfx.doc", "Invalid date: " << text);
1296                 continue;
1297             }
1298         } else if ( type == "time" ) {
1299             css::util::Duration ud;
1300             if (textToDuration(ud, text)) {
1301                 any <<= ud;
1302             } else {
1303                 SAL_WARN("sfx.doc", "Invalid time: " << text);
1304                 continue;
1305             }
1306         } else if ( type == "boolean" ) {
1307             bool b;
1308             if (::sax::Converter::convertBool(b, text)) {
1309                 any <<= b;
1310             } else {
1311                 SAL_WARN("sfx.doc", "Invalid boolean: " << text);
1312                 continue;
1313             }
1314         } else { // default
1315             any <<= text;
1316         }
1317         try {
1318             m_xUserDefined->addProperty(name,
1319                 css::beans::PropertyAttribute::REMOVABLE, any);
1320         } catch (const css::beans::PropertyExistException &) {
1321             SAL_WARN("sfx.doc", "Duplicate: " << name);
1322             // ignore; duplicate
1323         } catch (const css::beans::IllegalTypeException &) {
1324             SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal type: " << name);
1325         } catch (const css::lang::IllegalArgumentException &) {
1326             SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal arg: " << name);
1327         }
1328     }
1329 
1330     m_isModified = false;
1331     m_isInitialized = true;
1332 }
1333 
1334 
SfxDocumentMetaData(css::uno::Reference<css::uno::XComponentContext> const & context)1335 SfxDocumentMetaData::SfxDocumentMetaData(
1336         css::uno::Reference< css::uno::XComponentContext > const & context)
1337     : m_xContext(context)
1338     , m_isInitialized(false)
1339     , m_isModified(false)
1340     , m_AutoloadSecs(0)
1341 {
1342     assert(context.is());
1343     assert(context->getServiceManager().is());
1344     std::unique_lock g(m_aMutex);
1345     init(g, createDOM());
1346 }
1347 
1348 // com.sun.star.uno.XServiceInfo:
1349 OUString SAL_CALL
getImplementationName()1350 SfxDocumentMetaData::getImplementationName()
1351 {
1352     return u"SfxDocumentMetaData"_ustr;
1353 }
1354 
1355 sal_Bool SAL_CALL
supportsService(OUString const & serviceName)1356 SfxDocumentMetaData::supportsService(OUString const & serviceName)
1357 {
1358     return cppu::supportsService(this, serviceName);
1359 }
1360 
1361 css::uno::Sequence< OUString > SAL_CALL
getSupportedServiceNames()1362 SfxDocumentMetaData::getSupportedServiceNames()
1363 {
1364     css::uno::Sequence< OUString > s { u"com.sun.star.document.DocumentProperties"_ustr };
1365     return s;
1366 }
1367 
1368 
1369 // css::lang::XComponent:
disposing(std::unique_lock<std::mutex> & rGuard)1370 void SfxDocumentMetaData::disposing(std::unique_lock<std::mutex>& rGuard)
1371 {
1372     m_NotifyListeners.disposeAndClear(rGuard, css::lang::EventObject(
1373             getXWeak()));
1374     m_isInitialized = false;
1375     m_meta.clear();
1376     m_metaList.clear();
1377     m_xParent.clear();
1378     m_xDoc.clear();
1379     m_xUserDefined.clear();
1380 }
1381 
1382 
1383 // css::document::XDocumentProperties:
1384 OUString SAL_CALL
getAuthor()1385 SfxDocumentMetaData::getAuthor()
1386 {
1387     std::unique_lock g(m_aMutex);
1388     return getMetaText(g, "meta:initial-creator");
1389 }
1390 
setAuthor(const OUString & the_value)1391 void SAL_CALL SfxDocumentMetaData::setAuthor(const OUString & the_value)
1392 {
1393     setMetaTextAndNotify(u"meta:initial-creator"_ustr, the_value);
1394 }
1395 
1396 
1397 OUString SAL_CALL
getGenerator()1398 SfxDocumentMetaData::getGenerator()
1399 {
1400     std::unique_lock g(m_aMutex);
1401     return getMetaText(g, "meta:generator");
1402 }
1403 
1404 void SAL_CALL
setGenerator(const OUString & the_value)1405 SfxDocumentMetaData::setGenerator(const OUString & the_value)
1406 {
1407     setMetaTextAndNotify(u"meta:generator"_ustr, the_value);
1408 }
1409 
1410 css::util::DateTime SAL_CALL
getCreationDate()1411 SfxDocumentMetaData::getCreationDate()
1412 {
1413     std::unique_lock g(m_aMutex);
1414     return textToDateTimeDefault(getMetaText(g, "meta:creation-date"));
1415 }
1416 
1417 void SAL_CALL
setCreationDate(const css::util::DateTime & the_value)1418 SfxDocumentMetaData::setCreationDate(const css::util::DateTime & the_value)
1419 {
1420     setMetaTextAndNotify(u"meta:creation-date"_ustr, dateTimeToText(the_value));
1421 }
1422 
1423 OUString SAL_CALL
getTitle()1424 SfxDocumentMetaData::getTitle()
1425 {
1426     std::unique_lock g(m_aMutex);
1427     return getMetaText(g, "dc:title");
1428 }
1429 
setTitle(const OUString & the_value)1430 void SAL_CALL SfxDocumentMetaData::setTitle(const OUString & the_value)
1431 {
1432     setMetaTextAndNotify(u"dc:title"_ustr, the_value);
1433 }
1434 
1435 OUString SAL_CALL
getSubject()1436 SfxDocumentMetaData::getSubject()
1437 {
1438     std::unique_lock g(m_aMutex);
1439     return getMetaText(g, "dc:subject");
1440 }
1441 
1442 void SAL_CALL
setSubject(const OUString & the_value)1443 SfxDocumentMetaData::setSubject(const OUString & the_value)
1444 {
1445     setMetaTextAndNotify(u"dc:subject"_ustr, the_value);
1446 }
1447 
1448 OUString SAL_CALL
getDescription()1449 SfxDocumentMetaData::getDescription()
1450 {
1451     std::unique_lock g(m_aMutex);
1452     return getMetaText(g, "dc:description");
1453 }
1454 
1455 void SAL_CALL
setDescription(const OUString & the_value)1456 SfxDocumentMetaData::setDescription(const OUString & the_value)
1457 {
1458     setMetaTextAndNotify(u"dc:description"_ustr, the_value);
1459 }
1460 
1461 css::uno::Sequence< OUString >
getKeywords()1462 SAL_CALL SfxDocumentMetaData::getKeywords()
1463 {
1464     std::unique_lock g(m_aMutex);
1465     return getMetaList(g, "meta:keyword");
1466 }
1467 
1468 void SAL_CALL
setKeywords(const css::uno::Sequence<OUString> & the_value)1469 SfxDocumentMetaData::setKeywords(
1470         const css::uno::Sequence< OUString > & the_value)
1471 {
1472     std::unique_lock g(m_aMutex);
1473     if (setMetaList(g, u"meta:keyword"_ustr, the_value, nullptr)) {
1474         g.unlock();
1475         setModified(true);
1476     }
1477 }
1478 
1479 // css::document::XDocumentProperties2
getContributor()1480 css::uno::Sequence<OUString> SAL_CALL SfxDocumentMetaData::getContributor()
1481 {
1482     std::unique_lock g(m_aMutex);
1483     return getMetaList(g, "dc:contributor");
1484 }
1485 
setContributor(const css::uno::Sequence<OUString> & the_value)1486 void SAL_CALL SfxDocumentMetaData::setContributor(const css::uno::Sequence<OUString>& the_value)
1487 {
1488     std::unique_lock g(m_aMutex);
1489     if (setMetaList(g, u"dc:contributor"_ustr, the_value, nullptr))
1490     {
1491         g.unlock();
1492         setModified(true);
1493     }
1494 }
1495 
getCoverage()1496 OUString SAL_CALL SfxDocumentMetaData::getCoverage()
1497 {
1498     std::unique_lock g(m_aMutex);
1499     return getMetaText(g, "dc:coverage");
1500 }
1501 
setCoverage(const OUString & the_value)1502 void SAL_CALL SfxDocumentMetaData::setCoverage(const OUString& the_value)
1503 {
1504     setMetaTextAndNotify(u"dc:coverage"_ustr, the_value);
1505 }
1506 
getIdentifier()1507 OUString SAL_CALL SfxDocumentMetaData::getIdentifier()
1508 {
1509     std::unique_lock g(m_aMutex);
1510     return getMetaText(g, "dc:identifier");
1511 }
1512 
setIdentifier(const OUString & the_value)1513 void SAL_CALL SfxDocumentMetaData::setIdentifier(const OUString& the_value)
1514 {
1515     setMetaTextAndNotify(u"dc:identifier"_ustr, the_value);
1516 }
1517 
getPublisher()1518 css::uno::Sequence<OUString> SAL_CALL SfxDocumentMetaData::getPublisher()
1519 {
1520     std::unique_lock g(m_aMutex);
1521     return getMetaList(g, "dc:publisher");
1522 }
1523 
setPublisher(const css::uno::Sequence<OUString> & the_value)1524 void SAL_CALL SfxDocumentMetaData::setPublisher(const css::uno::Sequence<OUString>& the_value)
1525 {
1526     std::unique_lock g(m_aMutex);
1527     if (setMetaList(g, u"dc:publisher"_ustr, the_value, nullptr))
1528     {
1529         g.unlock();
1530         setModified(true);
1531     }
1532 }
1533 
getRelation()1534 css::uno::Sequence<OUString> SAL_CALL SfxDocumentMetaData::getRelation()
1535 {
1536     std::unique_lock g(m_aMutex);
1537     return getMetaList(g, "dc:relation");
1538 }
1539 
setRelation(const css::uno::Sequence<OUString> & the_value)1540 void SAL_CALL SfxDocumentMetaData::setRelation(const css::uno::Sequence<OUString>& the_value)
1541 {
1542     std::unique_lock g(m_aMutex);
1543     if (setMetaList(g, u"dc:relation"_ustr, the_value, nullptr))
1544     {
1545         g.unlock();
1546         setModified(true);
1547     }
1548 }
1549 
getRights()1550 OUString SAL_CALL SfxDocumentMetaData::getRights()
1551 {
1552     std::unique_lock g(m_aMutex);
1553     return getMetaText(g, "dc:rights");
1554 }
1555 
setRights(const OUString & the_value)1556 void SAL_CALL SfxDocumentMetaData::setRights(const OUString& the_value)
1557 {
1558     setMetaTextAndNotify(u"dc:rights"_ustr, the_value);
1559 }
1560 
getSource()1561 OUString SAL_CALL SfxDocumentMetaData::getSource()
1562 {
1563     std::unique_lock g(m_aMutex);
1564     return getMetaText(g, "dc:source");
1565 }
1566 
setSource(const OUString & the_value)1567 void SAL_CALL SfxDocumentMetaData::setSource(const OUString& the_value)
1568 {
1569     setMetaTextAndNotify(u"dc:source"_ustr, the_value);
1570 }
1571 
getType()1572 OUString SAL_CALL SfxDocumentMetaData::getType()
1573 {
1574     std::unique_lock g(m_aMutex);
1575     return getMetaText(g, "dc:type");
1576 }
1577 
setType(const OUString & the_value)1578 void SAL_CALL SfxDocumentMetaData::setType(const OUString& the_value)
1579 {
1580     setMetaTextAndNotify(u"dc:type"_ustr, the_value);
1581 }
1582 
1583 css::lang::Locale SAL_CALL
getLanguage()1584         SfxDocumentMetaData::getLanguage()
1585 {
1586     std::unique_lock g(m_aMutex);
1587     css::lang::Locale loc( LanguageTag::convertToLocale( getMetaText(g, "dc:language"), false));
1588     return loc;
1589 }
1590 
1591 void SAL_CALL
setLanguage(const css::lang::Locale & the_value)1592 SfxDocumentMetaData::setLanguage(const css::lang::Locale & the_value)
1593 {
1594     OUString text( LanguageTag::convertToBcp47( the_value, false));
1595     setMetaTextAndNotify(u"dc:language"_ustr, text);
1596 }
1597 
1598 OUString SAL_CALL
getModifiedBy()1599 SfxDocumentMetaData::getModifiedBy()
1600 {
1601     std::unique_lock g(m_aMutex);
1602     return getMetaText(g, "dc:creator");
1603 }
1604 
1605 void SAL_CALL
setModifiedBy(const OUString & the_value)1606 SfxDocumentMetaData::setModifiedBy(const OUString & the_value)
1607 {
1608     setMetaTextAndNotify(u"dc:creator"_ustr, the_value);
1609 }
1610 
1611 css::util::DateTime SAL_CALL
getModificationDate()1612 SfxDocumentMetaData::getModificationDate()
1613 {
1614     std::unique_lock g(m_aMutex);
1615     return textToDateTimeDefault(getMetaText(g, "dc:date"));
1616 }
1617 
1618 void SAL_CALL
setModificationDate(const css::util::DateTime & the_value)1619 SfxDocumentMetaData::setModificationDate(const css::util::DateTime & the_value)
1620 {
1621     setMetaTextAndNotify(u"dc:date"_ustr, dateTimeToText(the_value));
1622 }
1623 
1624 OUString SAL_CALL
getPrintedBy()1625 SfxDocumentMetaData::getPrintedBy()
1626 {
1627     std::unique_lock g(m_aMutex);
1628     return getMetaText(g, "meta:printed-by");
1629 }
1630 
1631 void SAL_CALL
setPrintedBy(const OUString & the_value)1632 SfxDocumentMetaData::setPrintedBy(const OUString & the_value)
1633 {
1634     setMetaTextAndNotify(u"meta:printed-by"_ustr, the_value);
1635 }
1636 
1637 css::util::DateTime SAL_CALL
getPrintDate()1638 SfxDocumentMetaData::getPrintDate()
1639 {
1640     std::unique_lock g(m_aMutex);
1641     return textToDateTimeDefault(getMetaText(g, "meta:print-date"));
1642 }
1643 
1644 void SAL_CALL
setPrintDate(const css::util::DateTime & the_value)1645 SfxDocumentMetaData::setPrintDate(const css::util::DateTime & the_value)
1646 {
1647     setMetaTextAndNotify(u"meta:print-date"_ustr, dateTimeToText(the_value));
1648 }
1649 
1650 OUString SAL_CALL
getTemplateName()1651 SfxDocumentMetaData::getTemplateName()
1652 {
1653     std::unique_lock g(m_aMutex);
1654     checkInit(g);
1655     return m_TemplateName;
1656 }
1657 
1658 void SAL_CALL
setTemplateName(const OUString & the_value)1659 SfxDocumentMetaData::setTemplateName(const OUString & the_value)
1660 {
1661     std::unique_lock g(m_aMutex);
1662     checkInit(g);
1663     if (m_TemplateName != the_value) {
1664         m_TemplateName = the_value;
1665         g.unlock();
1666         setModified(true);
1667     }
1668 }
1669 
1670 OUString SAL_CALL
getTemplateURL()1671 SfxDocumentMetaData::getTemplateURL()
1672 {
1673     std::unique_lock g(m_aMutex);
1674     checkInit(g);
1675     return m_TemplateURL;
1676 }
1677 
1678 void SAL_CALL
setTemplateURL(const OUString & the_value)1679 SfxDocumentMetaData::setTemplateURL(const OUString & the_value)
1680 {
1681     std::unique_lock g(m_aMutex);
1682     checkInit(g);
1683     if (m_TemplateURL != the_value) {
1684         m_TemplateURL = the_value;
1685         g.unlock();
1686         setModified(true);
1687     }
1688 }
1689 
1690 css::util::DateTime SAL_CALL
getTemplateDate()1691 SfxDocumentMetaData::getTemplateDate()
1692 {
1693     std::unique_lock g(m_aMutex);
1694     checkInit(g);
1695     return m_TemplateDate;
1696 }
1697 
1698 void SAL_CALL
setTemplateDate(const css::util::DateTime & the_value)1699 SfxDocumentMetaData::setTemplateDate(const css::util::DateTime & the_value)
1700 {
1701     std::unique_lock g(m_aMutex);
1702     checkInit(g);
1703     if (m_TemplateDate != the_value) {
1704         m_TemplateDate = the_value;
1705         g.unlock();
1706         setModified(true);
1707     }
1708 }
1709 
1710 OUString SAL_CALL
getAutoloadURL()1711 SfxDocumentMetaData::getAutoloadURL()
1712 {
1713     std::unique_lock g(m_aMutex);
1714     checkInit(g);
1715     return m_AutoloadURL;
1716 }
1717 
1718 void SAL_CALL
setAutoloadURL(const OUString & the_value)1719 SfxDocumentMetaData::setAutoloadURL(const OUString & the_value)
1720 {
1721     std::unique_lock g(m_aMutex);
1722     checkInit(g);
1723     if (m_AutoloadURL != the_value) {
1724         m_AutoloadURL = the_value;
1725         g.unlock();
1726         setModified(true);
1727     }
1728 }
1729 
1730 ::sal_Int32 SAL_CALL
getAutoloadSecs()1731 SfxDocumentMetaData::getAutoloadSecs()
1732 {
1733     std::unique_lock g(m_aMutex);
1734     checkInit(g);
1735     return m_AutoloadSecs;
1736 }
1737 
1738 void SAL_CALL
setAutoloadSecs(::sal_Int32 the_value)1739 SfxDocumentMetaData::setAutoloadSecs(::sal_Int32 the_value)
1740 {
1741     if (the_value < 0)
1742         throw css::lang::IllegalArgumentException(
1743             u"SfxDocumentMetaData::setAutoloadSecs: argument is negative"_ustr,
1744             *this, 0);
1745     std::unique_lock g(m_aMutex);
1746     checkInit(g);
1747     if (m_AutoloadSecs != the_value) {
1748         m_AutoloadSecs = the_value;
1749         g.unlock();
1750         setModified(true);
1751     }
1752 }
1753 
1754 OUString SAL_CALL
getDefaultTarget()1755 SfxDocumentMetaData::getDefaultTarget()
1756 {
1757     std::unique_lock g(m_aMutex);
1758     checkInit(g);
1759     return m_DefaultTarget;
1760 }
1761 
1762 void SAL_CALL
setDefaultTarget(const OUString & the_value)1763 SfxDocumentMetaData::setDefaultTarget(const OUString & the_value)
1764 {
1765     std::unique_lock g(m_aMutex);
1766     checkInit(g);
1767     if (m_DefaultTarget != the_value) {
1768         m_DefaultTarget = the_value;
1769         g.unlock();
1770         setModified(true);
1771     }
1772 }
1773 
1774 css::uno::Sequence< css::beans::NamedValue > SAL_CALL
getDocumentStatistics()1775 SfxDocumentMetaData::getDocumentStatistics()
1776 {
1777     std::unique_lock g(m_aMutex);
1778     checkInit(g);
1779     ::std::vector<css::beans::NamedValue> stats;
1780     for (size_t i = 0; s_stdStats[i] != nullptr; ++i) {
1781         OUString text = getMetaAttr(g, u"meta:document-statistic"_ustr, s_stdStatAttrs[i]);
1782         if (text.isEmpty()) continue;
1783         css::beans::NamedValue stat;
1784         stat.Name = OUString::createFromAscii(s_stdStats[i]);
1785         sal_Int32 val;
1786         if (!::sax::Converter::convertNumber(val, text, 0) || (val < 0)) {
1787             val = 0;
1788             SAL_WARN("sfx.doc", "Invalid number: " << text);
1789         }
1790         stat.Value <<= val;
1791         stats.push_back(stat);
1792     }
1793 
1794     return ::comphelper::containerToSequence(stats);
1795 }
1796 
1797 void SAL_CALL
setDocumentStatistics(const css::uno::Sequence<css::beans::NamedValue> & the_value)1798 SfxDocumentMetaData::setDocumentStatistics(
1799         const css::uno::Sequence< css::beans::NamedValue > & the_value)
1800 {
1801     {
1802         std::unique_lock g(m_aMutex);
1803         checkInit(g);
1804         std::vector<std::pair<OUString, OUString> > attributes;
1805         for (const auto& rValue : the_value) {
1806             const OUString name = rValue.Name;
1807             // inefficiently search for matching attribute
1808             for (size_t j = 0; s_stdStats[j] != nullptr; ++j) {
1809                 if (name.equalsAscii(s_stdStats[j])) {
1810                     const css::uno::Any any = rValue.Value;
1811                     sal_Int32 val = 0;
1812                     if (any >>= val) {
1813                         attributes.emplace_back(s_stdStatAttrs[j],
1814                             OUString::number(val));
1815                     }
1816                     else {
1817                         SAL_WARN("sfx.doc", "Invalid statistic: " << name);
1818                     }
1819                     break;
1820                 }
1821             }
1822         }
1823         updateElement(g, u"meta:document-statistic"_ustr, &attributes);
1824     }
1825     setModified(true);
1826 }
1827 
1828 ::sal_Int16 SAL_CALL
getEditingCycles()1829 SfxDocumentMetaData::getEditingCycles()
1830 {
1831     std::unique_lock g(m_aMutex);
1832     OUString text = getMetaText(g, "meta:editing-cycles");
1833     sal_Int32 ret;
1834     if (::sax::Converter::convertNumber(ret, text,
1835             0, std::numeric_limits<sal_Int16>::max())) {
1836         return static_cast<sal_Int16>(ret);
1837     } else {
1838         return 0;
1839     }
1840 }
1841 
1842 void SAL_CALL
setEditingCycles(::sal_Int16 the_value)1843 SfxDocumentMetaData::setEditingCycles(::sal_Int16 the_value)
1844 {
1845     if (the_value < 0)
1846         throw css::lang::IllegalArgumentException(
1847             u"SfxDocumentMetaData::setEditingCycles: argument is negative"_ustr,
1848             *this, 0);
1849     setMetaTextAndNotify(u"meta:editing-cycles"_ustr, OUString::number(the_value));
1850 }
1851 
1852 ::sal_Int32 SAL_CALL
getEditingDuration()1853 SfxDocumentMetaData::getEditingDuration()
1854 {
1855     std::unique_lock g(m_aMutex);
1856     return textToDuration(getMetaText(g, "meta:editing-duration"));
1857 }
1858 
1859 void SAL_CALL
setEditingDuration(::sal_Int32 the_value)1860 SfxDocumentMetaData::setEditingDuration(::sal_Int32 the_value)
1861 {
1862     if (the_value < 0)
1863         throw css::lang::IllegalArgumentException(
1864             u"SfxDocumentMetaData::setEditingDuration: argument is negative"_ustr,
1865             *this, 0);
1866     setMetaTextAndNotify(u"meta:editing-duration"_ustr, durationToText(the_value));
1867 }
1868 
1869 void SAL_CALL
resetUserData(const OUString & the_value)1870 SfxDocumentMetaData::resetUserData(const OUString & the_value)
1871 {
1872     std::unique_lock g(m_aMutex);
1873 
1874     bool bModified( false );
1875     bModified |= setMetaText(g, u"meta:initial-creator"_ustr, the_value);
1876     ::DateTime now( ::DateTime::SYSTEM );
1877     css::util::DateTime uDT(now.GetUNODateTime());
1878     bModified |= setMetaText(g, u"meta:creation-date"_ustr, dateTimeToText(uDT));
1879     bModified |= setMetaText(g, u"dc:creator"_ustr, OUString());
1880     bModified |= setMetaText(g, u"meta:printed-by"_ustr, OUString());
1881     bModified |= setMetaText(g, u"dc:date"_ustr, dateTimeToText(css::util::DateTime()));
1882     bModified |= setMetaText(g, u"meta:print-date"_ustr,
1883         dateTimeToText(css::util::DateTime()));
1884     bModified |= setMetaText(g, u"meta:editing-duration"_ustr, durationToText(0));
1885     bModified |= setMetaText(g, u"meta:editing-cycles"_ustr,
1886         u"1"_ustr);
1887 
1888     if (bModified) {
1889         g.unlock();
1890         setModified(true);
1891     }
1892 }
1893 
1894 
1895 css::uno::Reference< css::beans::XPropertyContainer > SAL_CALL
getUserDefinedProperties()1896 SfxDocumentMetaData::getUserDefinedProperties()
1897 {
1898     std::unique_lock g(m_aMutex);
1899     checkInit(g);
1900     createUserDefined(g);
1901     return m_xUserDefined;
1902 }
1903 
1904 
1905 void SAL_CALL
loadFromStorage(const css::uno::Reference<css::embed::XStorage> & xStorage,const css::uno::Sequence<css::beans::PropertyValue> & Medium)1906 SfxDocumentMetaData::loadFromStorage(
1907         const css::uno::Reference< css::embed::XStorage > & xStorage,
1908         const css::uno::Sequence< css::beans::PropertyValue > & Medium)
1909 {
1910     if (!xStorage.is())
1911         throw css::lang::IllegalArgumentException(u"SfxDocumentMetaData::loadFromStorage: argument is null"_ustr, *this, 0);
1912     std::unique_lock g(m_aMutex);
1913 
1914     // open meta data file
1915     css::uno::Reference<css::io::XStream> xStream(
1916         xStorage->openStreamElement(
1917             s_meta,
1918             css::embed::ElementModes::READ) );
1919     if (!xStream.is()) throw css::uno::RuntimeException();
1920     css::uno::Reference<css::io::XInputStream> xInStream =
1921         xStream->getInputStream();
1922     if (!xInStream.is()) throw css::uno::RuntimeException();
1923 
1924     // create DOM parser service
1925     css::uno::Reference<css::lang::XMultiComponentFactory> xMsf (
1926         m_xContext->getServiceManager());
1927     css::xml::sax::InputSource input;
1928     input.aInputStream = std::move(xInStream);
1929 
1930     sal_uInt64 version = SotStorage::GetVersion( xStorage );
1931     // Oasis is also the default (0)
1932     bool bOasis = ( version > SOFFICE_FILEFORMAT_60 || version == 0 );
1933     const char *pServiceName = bOasis
1934         ? "com.sun.star.document.XMLOasisMetaImporter"
1935         : "com.sun.star.document.XMLMetaImporter";
1936 
1937     // set base URL
1938     css::uno::Reference<css::beans::XPropertySet> xPropArg =
1939         getURLProperties(g, Medium);
1940     try {
1941         xPropArg->getPropertyValue(u"BaseURI"_ustr)
1942             >>= input.sSystemId;
1943         input.sSystemId += OUString::Concat("/") + s_meta;
1944     } catch (const css::uno::Exception &) {
1945         input.sSystemId = s_meta;
1946     }
1947     css::uno::Sequence< css::uno::Any > args{ css::uno::Any(xPropArg) };
1948 
1949     // the underlying SvXMLImport implements XFastParser, XImporter, XFastDocumentHandler
1950     css::uno::Reference<XInterface> xFilter =
1951         xMsf->createInstanceWithArgumentsAndContext(
1952             OUString::createFromAscii(pServiceName), args, m_xContext);
1953     assert(xFilter);
1954     css::uno::Reference<css::xml::sax::XFastParser> xFastParser(xFilter, css::uno::UNO_QUERY);
1955     css::uno::Reference<css::document::XImporter> xImp(xFilter, css::uno::UNO_QUERY_THROW);
1956     xImp->setTargetDocument(css::uno::Reference<css::lang::XComponent>(this));
1957     g.unlock(); // NB: the implementation of XMLOasisMetaImporter calls initialize
1958     try {
1959         if (xFastParser)
1960             xFastParser->parseStream(input);
1961         else
1962         {
1963             css::uno::Reference<css::xml::sax::XDocumentHandler> xDocHandler(xFilter, css::uno::UNO_QUERY_THROW);
1964             css::uno::Reference<css::xml::sax::XParser> xParser = css::xml::sax::Parser::create(m_xContext);
1965             xParser->setDocumentHandler(xDocHandler);
1966             xParser->parseStream(input);
1967         }
1968     } catch (const css::xml::sax::SAXException &) {
1969         throw css::io::WrongFormatException(
1970                 u"SfxDocumentMetaData::loadFromStorage:"
1971                 " XML parsing exception"_ustr, *this);
1972     }
1973     g.lock();
1974     // NB: the implementation of XMLOasisMetaImporter calls initialize
1975     checkInit(g);
1976 }
1977 
1978 void SAL_CALL
storeToStorage(const css::uno::Reference<css::embed::XStorage> & xStorage,const css::uno::Sequence<css::beans::PropertyValue> & Medium)1979 SfxDocumentMetaData::storeToStorage(
1980         const css::uno::Reference< css::embed::XStorage > & xStorage,
1981         const css::uno::Sequence< css::beans::PropertyValue > & Medium)
1982 {
1983     if (!xStorage.is())
1984         throw css::lang::IllegalArgumentException(
1985             u"SfxDocumentMetaData::storeToStorage: argument is null"_ustr, *this, 0);
1986     std::unique_lock g(m_aMutex);
1987     checkInit(g);
1988 
1989     // update user-defined meta data in DOM tree
1990 //    updateUserDefinedAndAttributes(); // this will be done in serialize!
1991 
1992     // write into storage
1993     css::uno::Reference<css::io::XStream> xStream =
1994         xStorage->openStreamElement(s_meta,
1995             css::embed::ElementModes::WRITE
1996             | css::embed::ElementModes::TRUNCATE);
1997     if (!xStream.is()) throw css::uno::RuntimeException();
1998     css::uno::Reference< css::beans::XPropertySet > xStreamProps(xStream,
1999         css::uno::UNO_QUERY_THROW);
2000     xStreamProps->setPropertyValue(
2001         u"MediaType"_ustr,
2002         css::uno::Any(u"text/xml"_ustr));
2003     xStreamProps->setPropertyValue(
2004         u"Compressed"_ustr,
2005         css::uno::Any(false));
2006     xStreamProps->setPropertyValue(
2007         u"UseCommonStoragePasswordEncryption"_ustr,
2008         css::uno::Any(false));
2009     css::uno::Reference<css::io::XOutputStream> xOutStream =
2010         xStream->getOutputStream();
2011     if (!xOutStream.is()) throw css::uno::RuntimeException();
2012     css::uno::Reference<css::lang::XMultiComponentFactory> xMsf (
2013         m_xContext->getServiceManager());
2014     css::uno::Reference<css::xml::sax::XWriter> xSaxWriter(
2015         css::xml::sax::Writer::create(m_xContext));
2016     xSaxWriter->setOutputStream(xOutStream);
2017 
2018     const sal_uInt64 version = SotStorage::GetVersion( xStorage );
2019     // Oasis is also the default (0)
2020     const bool bOasis = ( version > SOFFICE_FILEFORMAT_60 || version == 0 );
2021     const char *pServiceName = bOasis
2022         ? "com.sun.star.document.XMLOasisMetaExporter"
2023         : "com.sun.star.document.XMLMetaExporter";
2024 
2025     // set base URL
2026     css::uno::Reference<css::beans::XPropertySet> xPropArg =
2027         getURLProperties(g, Medium);
2028     css::uno::Sequence< css::uno::Any > args{ css::uno::Any(xSaxWriter), css::uno::Any(xPropArg) };
2029 
2030     css::uno::Reference<css::document::XExporter> xExp(
2031         xMsf->createInstanceWithArgumentsAndContext(
2032             OUString::createFromAscii(pServiceName), args, m_xContext),
2033         css::uno::UNO_QUERY_THROW);
2034     xExp->setSourceDocument(css::uno::Reference<css::lang::XComponent>(this));
2035     css::uno::Reference<css::document::XFilter> xFilter(xExp,
2036         css::uno::UNO_QUERY_THROW);
2037     g.unlock(); // filter calls back into this
2038     if (!xFilter->filter(css::uno::Sequence< css::beans::PropertyValue >())) {
2039         throw css::io::IOException(
2040                 u"SfxDocumentMetaData::storeToStorage: cannot filter"_ustr, *this);
2041     }
2042     css::uno::Reference<css::embed::XTransactedObject> xTransaction(
2043         xStorage, css::uno::UNO_QUERY);
2044     if (xTransaction.is()) {
2045         xTransaction->commit();
2046     }
2047 }
2048 
2049 void SAL_CALL
loadFromMedium(const OUString & URL,const css::uno::Sequence<css::beans::PropertyValue> & Medium)2050 SfxDocumentMetaData::loadFromMedium(const OUString & URL,
2051         const css::uno::Sequence< css::beans::PropertyValue > & Medium)
2052 {
2053     css::uno::Reference<css::io::XInputStream> xIn;
2054     utl::MediaDescriptor md(Medium);
2055     // if we have a URL parameter, it replaces the one in the media descriptor
2056     if (!URL.isEmpty()) {
2057         md[ utl::MediaDescriptor::PROP_URL ] <<= URL;
2058         md[ utl::MediaDescriptor::PROP_READONLY ] <<= true;
2059     }
2060     if (md.addInputStream()) {
2061         md[ utl::MediaDescriptor::PROP_INPUTSTREAM ] >>= xIn;
2062     }
2063     css::uno::Reference<css::embed::XStorage> xStorage;
2064     try {
2065         if (xIn.is()) {
2066             xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
2067                             xIn, m_xContext);
2068         } else { // fallback to url parameter
2069             xStorage = ::comphelper::OStorageHelper::GetStorageFromURL(
2070                             URL, css::embed::ElementModes::READ, m_xContext);
2071         }
2072     } catch (const css::uno::RuntimeException &) {
2073         throw;
2074     } catch (const css::io::IOException &) {
2075         throw;
2076     } catch (const css::uno::Exception &) {
2077         css::uno::Any anyEx = cppu::getCaughtException();
2078         throw css::lang::WrappedTargetException(
2079                 u"SfxDocumentMetaData::loadFromMedium: exception"_ustr,
2080                 css::uno::Reference<css::uno::XInterface>(*this),
2081                 anyEx);
2082     }
2083     if (!xStorage.is()) {
2084         throw css::uno::RuntimeException(
2085                 u"SfxDocumentMetaData::loadFromMedium: cannot get Storage"_ustr,
2086                 *this);
2087     }
2088     loadFromStorage(xStorage, md.getAsConstPropertyValueList());
2089 }
2090 
2091 void SAL_CALL
storeToMedium(const OUString & URL,const css::uno::Sequence<css::beans::PropertyValue> & Medium)2092 SfxDocumentMetaData::storeToMedium(const OUString & URL,
2093         const css::uno::Sequence< css::beans::PropertyValue > & Medium)
2094 {
2095     utl::MediaDescriptor md(Medium);
2096     if (!URL.isEmpty()) {
2097         md[ utl::MediaDescriptor::PROP_URL ] <<= URL;
2098     }
2099     SfxMedium aMedium(md.getAsConstPropertyValueList());
2100     css::uno::Reference<css::embed::XStorage> xStorage
2101         = aMedium.GetOutputStorage();
2102 
2103 
2104     if (!xStorage.is()) {
2105         throw css::uno::RuntimeException(
2106                 u"SfxDocumentMetaData::storeToMedium: cannot get Storage"_ustr,
2107                 *this);
2108     }
2109     // set MIME type of the storage
2110     utl::MediaDescriptor::const_iterator iter
2111         = md.find(utl::MediaDescriptor::PROP_MEDIATYPE);
2112     if (iter != md.end()) {
2113         css::uno::Reference< css::beans::XPropertySet > xProps(xStorage,
2114             css::uno::UNO_QUERY_THROW);
2115         xProps->setPropertyValue(
2116             utl::MediaDescriptor::PROP_MEDIATYPE,
2117             iter->second);
2118     }
2119     storeToStorage(xStorage, md.getAsConstPropertyValueList());
2120 
2121 
2122     const bool bOk = aMedium.Commit();
2123     aMedium.Close();
2124     if ( !bOk ) {
2125         ErrCodeMsg nError = aMedium.GetErrorIgnoreWarning();
2126         if ( nError == ERRCODE_NONE ) {
2127             nError = ERRCODE_IO_GENERAL;
2128         }
2129 
2130         throw css::task::ErrorCodeIOException(
2131             "SfxDocumentMetaData::storeToMedium <" + URL + "> Commit failed: " + nError.toString(),
2132             css::uno::Reference< css::uno::XInterface >(), sal_uInt32(nError.GetCode()));
2133 
2134     }
2135 }
2136 
2137 // css::lang::XInitialization:
initialize(const css::uno::Sequence<css::uno::Any> & aArguments)2138 void SAL_CALL SfxDocumentMetaData::initialize( const css::uno::Sequence< css::uno::Any > & aArguments)
2139 {
2140     // possible arguments:
2141     // - no argument: default initialization (empty DOM)
2142     // - 1 argument, XDocument: initialize with given DOM and empty base URL
2143     // NB: links in document must be absolute
2144 
2145     std::unique_lock g(m_aMutex);
2146     css::uno::Reference<css::xml::dom::XDocument> xDoc;
2147 
2148     for (sal_Int32 i = 0; i < aArguments.getLength(); ++i) {
2149         const css::uno::Any& any = aArguments[i];
2150         if (!(any >>= xDoc)) {
2151             throw css::lang::IllegalArgumentException(
2152                 u"SfxDocumentMetaData::initialize: argument must be XDocument"_ustr,
2153                 *this, static_cast<sal_Int16>(i));
2154         }
2155         if (!xDoc.is()) {
2156             throw css::lang::IllegalArgumentException(
2157                 u"SfxDocumentMetaData::initialize: argument is null"_ustr,
2158                 *this, static_cast<sal_Int16>(i));
2159         }
2160     }
2161 
2162     if (!xDoc.is()) {
2163         // For a new document, we create a new DOM tree here.
2164         xDoc = createDOM();
2165     }
2166 
2167     init(g, xDoc);
2168 }
2169 
2170 // css::util::XCloneable:
2171 css::uno::Reference<css::util::XCloneable> SAL_CALL
createClone()2172 SfxDocumentMetaData::createClone()
2173 {
2174     std::unique_lock g(m_aMutex);
2175     checkInit(g);
2176 
2177     rtl::Reference<SfxDocumentMetaData> pNew = createMe(m_xContext);
2178 
2179     // NB: do not copy the modification listeners, only DOM
2180     css::uno::Reference<css::xml::dom::XDocument> xDoc = createDOM();
2181     try {
2182         updateUserDefinedAndAttributes(g);
2183         // deep copy of root node
2184         css::uno::Reference<css::xml::dom::XNode> xRoot(
2185             m_xDoc->getDocumentElement(), css::uno::UNO_QUERY_THROW);
2186         css::uno::Reference<css::xml::dom::XNode> xRootNew(
2187             xDoc->importNode(xRoot, true));
2188         xDoc->appendChild(xRootNew);
2189         g.unlock();
2190         std::unique_lock g2(pNew->m_aMutex);
2191         pNew->init(g2, xDoc);
2192     } catch (const css::uno::RuntimeException &) {
2193         throw;
2194     } catch (const css::uno::Exception &) {
2195         css::uno::Any anyEx = cppu::getCaughtException();
2196         throw css::lang::WrappedTargetRuntimeException(
2197                 u"SfxDocumentMetaData::createClone: exception"_ustr,
2198                 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
2199     }
2200     return css::uno::Reference<css::util::XCloneable> (pNew);
2201 }
2202 
2203 // css::util::XModifiable:
isModified()2204 sal_Bool SAL_CALL SfxDocumentMetaData::isModified(  )
2205 {
2206     std::unique_lock g(m_aMutex);
2207     checkInit(g);
2208     css::uno::Reference<css::util::XModifiable> xMB(m_xUserDefined,
2209         css::uno::UNO_QUERY);
2210     return m_isModified || (xMB.is() && xMB->isModified());
2211 }
2212 
setModified(sal_Bool bModified)2213 void SAL_CALL SfxDocumentMetaData::setModified( sal_Bool bModified )
2214 {
2215     css::uno::Reference<css::util::XModifiable> xMB;
2216     { // do not lock mutex while notifying (#i93514#) to prevent deadlock
2217         std::unique_lock g(m_aMutex);
2218         checkInit(g);
2219         m_isModified = bModified;
2220         if ( !bModified && m_xUserDefined.is() )
2221         {
2222             xMB.set(m_xUserDefined, css::uno::UNO_QUERY);
2223             assert(xMB.is() &&
2224                 "SfxDocumentMetaData::setModified: PropertyBag not Modifiable?");
2225         }
2226     }
2227     if (bModified) {
2228         try {
2229             css::uno::Reference<css::uno::XInterface> xThis(*this);
2230             css::lang::EventObject event(xThis);
2231             std::unique_lock g(m_aMutex);
2232             m_NotifyListeners.notifyEach(g, &css::util::XModifyListener::modified,
2233                 event);
2234         } catch (const css::uno::RuntimeException &) {
2235             throw;
2236         } catch (const css::uno::Exception &) {
2237             // ignore
2238             TOOLS_WARN_EXCEPTION("sfx.doc", "setModified");
2239         }
2240     } else {
2241         if (xMB.is()) {
2242             xMB->setModified(false);
2243         }
2244     }
2245 }
2246 
2247 // css::util::XModifyBroadcaster:
addModifyListener(const css::uno::Reference<css::util::XModifyListener> & xListener)2248 void SAL_CALL SfxDocumentMetaData::addModifyListener(
2249         const css::uno::Reference< css::util::XModifyListener > & xListener)
2250 {
2251     std::unique_lock g(m_aMutex);
2252     checkInit(g);
2253     m_NotifyListeners.addInterface(g, xListener);
2254     css::uno::Reference<css::util::XModifyBroadcaster> xMB(m_xUserDefined,
2255         css::uno::UNO_QUERY);
2256     if (xMB.is()) {
2257         xMB->addModifyListener(xListener);
2258     }
2259 }
2260 
removeModifyListener(const css::uno::Reference<css::util::XModifyListener> & xListener)2261 void SAL_CALL SfxDocumentMetaData::removeModifyListener(
2262         const css::uno::Reference< css::util::XModifyListener > & xListener)
2263 {
2264     std::unique_lock g(m_aMutex);
2265     checkInit(g);
2266     m_NotifyListeners.removeInterface(g, xListener);
2267     css::uno::Reference<css::util::XModifyBroadcaster> xMB(m_xUserDefined,
2268         css::uno::UNO_QUERY);
2269     if (xMB.is()) {
2270         xMB->removeModifyListener(xListener);
2271     }
2272 }
2273 
2274 // css::xml::sax::XSAXSerializable
serialize(const css::uno::Reference<css::xml::sax::XDocumentHandler> & i_xHandler,const css::uno::Sequence<css::beans::StringPair> & i_rNamespaces)2275 void SAL_CALL SfxDocumentMetaData::serialize(
2276     const css::uno::Reference<css::xml::sax::XDocumentHandler>& i_xHandler,
2277     const css::uno::Sequence< css::beans::StringPair >& i_rNamespaces)
2278 {
2279     std::unique_lock g(m_aMutex);
2280     checkInit(g);
2281     updateUserDefinedAndAttributes(g);
2282     css::uno::Reference<css::xml::sax::XSAXSerializable> xSAXable(m_xDoc,
2283         css::uno::UNO_QUERY_THROW);
2284     xSAXable->serialize(i_xHandler, i_rNamespaces);
2285 }
2286 
createUserDefined(std::unique_lock<std::mutex> & g)2287 void SfxDocumentMetaData::createUserDefined(std::unique_lock<std::mutex>& g)
2288 {
2289     // user-defined meta data: create PropertyBag which only accepts property
2290     // values of allowed types
2291     if ( m_xUserDefined.is() )
2292         return;
2293 
2294     css::uno::Sequence<css::uno::Type> types{
2295         ::cppu::UnoType<bool>::get(),
2296         ::cppu::UnoType< OUString>::get(),
2297         ::cppu::UnoType<css::util::DateTime>::get(),
2298         ::cppu::UnoType<css::util::Date>::get(),
2299         ::cppu::UnoType<css::util::DateTimeWithTimezone>::get(),
2300         ::cppu::UnoType<css::util::DateWithTimezone>::get(),
2301         ::cppu::UnoType<css::util::Duration>::get(),
2302         ::cppu::UnoType<float>::get(),
2303         ::cppu::UnoType<double>::get(),
2304         ::cppu::UnoType<sal_Int16>::get(),
2305         ::cppu::UnoType<sal_Int32>::get(),
2306         ::cppu::UnoType<sal_Int64>::get(),
2307         // Time is supported for backward compatibility with OOo 3.x, x<=2
2308         ::cppu::UnoType<css::util::Time>::get()
2309     };
2310     // #i94175#:  ODF allows empty user-defined property names!
2311     m_xUserDefined.set(
2312         css::beans::PropertyBag::createWithTypes( m_xContext, types, true/*AllowEmptyPropertyName*/, false/*AutomaticAddition*/ ),
2313         css::uno::UNO_QUERY_THROW);
2314 
2315     const css::uno::Reference<css::util::XModifyBroadcaster> xMB(
2316         m_xUserDefined, css::uno::UNO_QUERY);
2317     if (xMB.is())
2318     {
2319         m_NotifyListeners.forEach(g,
2320             [xMB] (const css::uno::Reference<css::util::XModifyListener>& l)
2321             {
2322                 xMB->addModifyListener(l);
2323             });
2324     }
2325 }
2326 
2327 } // closing anonymous implementation namespace
2328 
2329 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
CompatWriterDocPropsImpl_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)2330 CompatWriterDocPropsImpl_get_implementation(
2331     css::uno::XComponentContext *context,
2332     css::uno::Sequence<css::uno::Any> const &)
2333 {
2334     return cppu::acquire(new CompatWriterDocPropsImpl(context));
2335 }
2336 
2337 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
SfxDocumentMetaData_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)2338 SfxDocumentMetaData_get_implementation(
2339     css::uno::XComponentContext *context,
2340     css::uno::Sequence<css::uno::Any> const &)
2341 {
2342     return cppu::acquire(new SfxDocumentMetaData(context));
2343 }
2344 
2345 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2346