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