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 <oox/core/xmlfilterbase.hxx>
21
22 #include <cstdio>
23 #include <string_view>
24
25 #include <com/sun/star/beans/XPropertyAccess.hpp>
26 #include <com/sun/star/beans/XPropertySet.hpp>
27 #include <com/sun/star/beans/Pair.hpp>
28 #include <com/sun/star/embed/XRelationshipAccess.hpp>
29 #include <com/sun/star/frame/XModel.hpp>
30 #include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
31 #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
32 #include <com/sun/star/xml/sax/Writer.hpp>
33 #include <o3tl/any.hxx>
34 #include <unotools/mediadescriptor.hxx>
35 #include <unotools/docinfohelper.hxx>
36 #include <unotools/securityoptions.hxx>
37 #include <sax/fshelper.hxx>
38 #include <rtl/strbuf.hxx>
39 #include <rtl/ustrbuf.hxx>
40 #include <osl/diagnose.h>
41 #include <sal/log.hxx>
42 #include <i18nlangtag/languagetag.hxx>
43 #include <oox/core/fastparser.hxx>
44 #include <oox/core/fragmenthandler.hxx>
45 #include <oox/core/recordparser.hxx>
46 #include <oox/core/relationshandler.hxx>
47 #include <oox/helper/propertyset.hxx>
48 #include <oox/helper/zipstorage.hxx>
49 #include <oox/ole/olestorage.hxx>
50 #include <oox/token/namespaces.hxx>
51 #include <oox/token/relationship.hxx>
52 #include <oox/token/properties.hxx>
53 #include <oox/token/tokens.hxx>
54 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
55 #include <com/sun/star/document/XOOXMLDocumentPropertiesImporter.hpp>
56 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
57 #include <comphelper/processfactory.hxx>
58 #include <oox/core/filterdetect.hxx>
59 #include <comphelper/stl_types.hxx>
60 #include <comphelper/storagehelper.hxx>
61 #include <comphelper/sequence.hxx>
62 #include <comphelper/ofopxmlhelper.hxx>
63
64 #include <oox/crypto/DocumentEncryption.hxx>
65 #include <tools/urlobj.hxx>
66 #include <com/sun/star/util/Date.hpp>
67 #include <com/sun/star/util/Duration.hpp>
68 #include <sax/tools/converter.hxx>
69 #include <oox/token/namespacemap.hxx>
70 #include <editeng/unoprnms.hxx>
71 #include <o3tl/sorted_vector.hxx>
72
73 using ::com::sun::star::xml::dom::DocumentBuilder;
74 using ::com::sun::star::xml::dom::XDocument;
75 using ::com::sun::star::xml::dom::XDocumentBuilder;
76
77 namespace oox::core {
78
79 using namespace ::com::sun::star;
80 using namespace ::com::sun::star::beans;
81 using namespace ::com::sun::star::document;
82 using namespace ::com::sun::star::embed;
83 using namespace ::com::sun::star::io;
84 using namespace ::com::sun::star::lang;
85 using namespace ::com::sun::star::uno;
86 using namespace ::com::sun::star::xml::sax;
87
88 using utl::MediaDescriptor;
89 using ::sax_fastparser::FSHelperPtr;
90 using ::sax_fastparser::FastSerializerHelper;
91
92 namespace {
93
NamespaceIds()94 const Sequence< beans::Pair< OUString, sal_Int32 > >& NamespaceIds()
95 {
96 static const Sequence< beans::Pair< OUString, sal_Int32 > > SINGLETON
97 {
98 {u"http://www.w3.org/XML/1998/namespace"_ustr, NMSP_xml},
99 {u"http://schemas.openxmlformats.org/package/2006/relationships"_ustr,
100 NMSP_packageRel},
101 {u"http://schemas.openxmlformats.org/officeDocument/2006/relationships"_ustr,
102 NMSP_officeRel},
103 {u"http://purl.oclc.org/ooxml/officeDocument/relationships"_ustr,
104 NMSP_officeRel},
105 {u"http://schemas.openxmlformats.org/drawingml/2006/main"_ustr, NMSP_dml},
106 {u"http://purl.oclc.org/ooxml/drawingml/main"_ustr, NMSP_dml},
107 {u"http://schemas.openxmlformats.org/drawingml/2006/diagram"_ustr,
108 NMSP_dmlDiagram},
109 {u"http://purl.oclc.org/ooxml/drawingml/diagram"_ustr, NMSP_dmlDiagram},
110 {u"http://schemas.openxmlformats.org/drawingml/2006/chart"_ustr,
111 NMSP_dmlChart},
112 {u"http://schemas.openxmlformats.org/drawingml/2006/chartDrawing"_ustr,
113 NMSP_dmlChartDr},
114 {u"urn:schemas-microsoft-com:vml"_ustr, NMSP_vml},
115 {u"urn:schemas-microsoft-com:office:office"_ustr, NMSP_vmlOffice},
116 {u"urn:schemas-microsoft-com:office:word"_ustr, NMSP_vmlWord},
117 {u"urn:schemas-microsoft-com:office:excel"_ustr, NMSP_vmlExcel},
118 {u"urn:schemas-microsoft-com:office:powerpoint"_ustr, NMSP_vmlPowerpoint},
119 {u"http://schemas.microsoft.com/office/2006/activeX"_ustr, NMSP_ax},
120 {u"http://schemas.openxmlformats.org/spreadsheetml/2006/main"_ustr,
121 NMSP_xls},
122 {u"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"_ustr,
123 NMSP_xm},
124 {u"http://schemas.microsoft.com/office/excel/2006/main"_ustr,
125 NMSP_dmlSpreadDr},
126 {u"http://schemas.openxmlformats.org/presentationml/2006/main"_ustr,
127 NMSP_ppt},
128 {u"http://schemas.openxmlformats.org/markup-compatibility/2006"_ustr,
129 NMSP_mce},
130 {u"http://schemas.openxmlformats.org/spreadsheetml/2006/main/v2"_ustr,
131 NMSP_mceTest},
132 {u"http://schemas.openxmlformats.org/officeDocument/2006/math"_ustr,
133 NMSP_officeMath},
134 {u"http://schemas.microsoft.com/office/drawing/2008/diagram"_ustr,
135 NMSP_dsp},
136 {u"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"_ustr,
137 NMSP_xls14Lst},
138 {u"http://schemas.libreoffice.org/"_ustr, NMSP_loext},
139 {u"http://schemas.microsoft.com/office/drawing/2010/main"_ustr,
140 NMSP_a14},
141 {u"http://schemas.microsoft.com/office/powerpoint/2010/main"_ustr,
142 NMSP_p14},
143 {u"http://schemas.microsoft.com/office/powerpoint/2012/main"_ustr,
144 NMSP_p15},
145 {u"http://schemas.microsoft.com/office/spreadsheetml/2011/1/ac"_ustr,
146 NMSP_x12ac},
147 {u"http://schemas.microsoft.com/office/drawing/2012/chart"_ustr,
148 NMSP_c15},
149 {u"http://schemas.microsoft.com/office/spreadsheetml/2015/revision2"_ustr,
150 NMSP_xr2},
151 {u"http://schemas.microsoft.com/office/drawing/2017/decorative"_ustr, NMSP_adec},
152 {u"http://schemas.microsoft.com/office/drawing/2016/SVG/main"_ustr, NMSP_asvg},
153 };
154 return SINGLETON;
155 };
156
registerNamespaces(FastParser & rParser)157 void registerNamespaces( FastParser& rParser )
158 {
159 const Sequence< beans::Pair<OUString, sal_Int32> >& ids = NamespaceIds();
160
161 // Filter out duplicates: a namespace can have multiple URLs, think of
162 // strict vs transitional.
163 o3tl::sorted_vector<sal_Int32> aSet;
164 aSet.reserve(ids.getLength());
165 for (const auto& rId : ids)
166 aSet.insert(rId.Second);
167
168 for (auto const& elem : aSet)
169 rParser.registerNamespace(elem);
170 }
171
172 } // namespace
173
174 struct XmlFilterBaseImpl
175 {
176 typedef RefMap< OUString, Relations > RelationsMap;
177
178 FastParser maFastParser;
179 RelationsMap maRelationsMap;
180 const NamespaceMap& mrNamespaceMap;
181 NamedShapePairs* mpDiagramFontHeights = nullptr;
182
183 /// @throws RuntimeException
184 explicit XmlFilterBaseImpl();
185 };
186
187 constexpr OUString gaBinSuffix( u".bin"_ustr );
188
XmlFilterBaseImpl()189 XmlFilterBaseImpl::XmlFilterBaseImpl() :
190 mrNamespaceMap(StaticNamespaceMap())
191 {
192 // register XML namespaces
193 registerNamespaces(maFastParser);
194 }
195
XmlFilterBase(const Reference<XComponentContext> & rxContext)196 XmlFilterBase::XmlFilterBase( const Reference< XComponentContext >& rxContext ) :
197 FilterBase( rxContext ),
198 mxImpl( new XmlFilterBaseImpl ),
199 mnRelId( 1 ),
200 mnMaxDocId( 0 ),
201 mbMSO2007(false),
202 mbMSO(false),
203 mbMissingExtDrawing(false)
204 {
205 }
206
~XmlFilterBase()207 XmlFilterBase::~XmlFilterBase()
208 {
209 // #i118640# Reset the DocumentHandler at the FastSaxParser manually; this is
210 // needed since the mechanism is that instances of FragmentHandler execute
211 // their stuff (creating objects, setting attributes, ...) on being destroyed.
212 // They get destroyed by setting a new DocumentHandler. This also happens in
213 // the following implicit destruction chain of ~XmlFilterBaseImpl, but in that
214 // case it's member RelationsMap maRelationsMap will be destroyed, but maybe
215 // still be used by ~FragmentHandler -> crash.
216 mxImpl->maFastParser.clearDocumentHandler();
217 }
218
getCurrentThemePtr() const219 std::shared_ptr<::oox::drawingml::Theme> XmlFilterBase::getCurrentThemePtr() const
220 {
221 // default returns empty ptr
222 return std::shared_ptr<::oox::drawingml::Theme>();
223 }
224
checkDocumentProperties(const Reference<XDocumentProperties> & xDocProps)225 void XmlFilterBase::checkDocumentProperties(const Reference<XDocumentProperties>& xDocProps)
226 {
227 mbMSO2007 = mbMSO = false;
228 if (!xDocProps->getGenerator().startsWithIgnoreAsciiCase("Microsoft"))
229 return;
230 mbMSO = true;
231
232 uno::Reference<beans::XPropertyAccess> xUserDefProps(xDocProps->getUserDefinedProperties(), uno::UNO_QUERY);
233 if (!xUserDefProps.is())
234 return;
235
236 comphelper::SequenceAsHashMap aUserDefinedProperties(xUserDefProps->getPropertyValues());
237 comphelper::SequenceAsHashMap::iterator it = aUserDefinedProperties.find(u"AppVersion"_ustr);
238 if (it == aUserDefinedProperties.end())
239 return;
240
241 OUString aValue;
242 if (!(it->second >>= aValue))
243 return;
244
245 if (!aValue.startsWithIgnoreAsciiCase("12."))
246 return;
247
248 SAL_INFO("oox", "a MSO 2007 document");
249 mbMSO2007 = true;
250 }
251
putPropertiesToDocumentGrabBag(const css::uno::Reference<css::lang::XComponent> & xDstDoc,const comphelper::SequenceAsHashMap & rProperties)252 void XmlFilterBase::putPropertiesToDocumentGrabBag(const css::uno::Reference<css::lang::XComponent>& xDstDoc,
253 const comphelper::SequenceAsHashMap& rProperties)
254 {
255 try
256 {
257 uno::Reference<beans::XPropertySet> xDocProps(xDstDoc, uno::UNO_QUERY);
258 if (xDocProps.is())
259 {
260 uno::Reference<beans::XPropertySetInfo> xPropsInfo = xDocProps->getPropertySetInfo();
261
262 static constexpr OUString aGrabBagPropName = u"InteropGrabBag"_ustr;
263 if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(aGrabBagPropName))
264 {
265 // get existing grab bag
266 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue(aGrabBagPropName));
267
268 // put the new items
269 aGrabBag.update(rProperties);
270
271 // put it back to the document
272 xDocProps->setPropertyValue(aGrabBagPropName, uno::Any(aGrabBag.getAsConstPropertyValueList()));
273 }
274 }
275 }
276 catch (const uno::Exception&)
277 {
278 SAL_WARN("oox","Failed to save documents grab bag");
279 }
280 }
281
importDocumentProperties()282 void XmlFilterBase::importDocumentProperties()
283 {
284 MediaDescriptor aMediaDesc( getMediaDescriptor() );
285 Reference< XInputStream > xInputStream;
286 Reference< XComponentContext > xContext = getComponentContext();
287 rtl::Reference< ::oox::core::FilterDetect > xDetector( new ::oox::core::FilterDetect( xContext ) );
288 xInputStream = xDetector->extractUnencryptedPackage( aMediaDesc );
289 Reference< XComponent > xModel = getModel();
290 const bool repairPackage = aMediaDesc.getUnpackedValueOrDefault(u"RepairPackage"_ustr, false);
291 Reference< XStorage > xDocumentStorage (
292 ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream(
293 OFOPXML_STORAGE_FORMAT_STRING, xInputStream, {}, repairPackage));
294 Reference< XInterface > xTemp = xContext->getServiceManager()->createInstanceWithContext(
295 u"com.sun.star.document.OOXMLDocumentPropertiesImporter"_ustr,
296 xContext);
297 Reference< XOOXMLDocumentPropertiesImporter > xImporter( xTemp, UNO_QUERY );
298 Reference< XDocumentPropertiesSupplier > xPropSupplier( xModel, UNO_QUERY);
299 Reference< XDocumentProperties > xDocProps = xPropSupplier->getDocumentProperties();
300 xImporter->importProperties( xDocumentStorage, xDocProps );
301 checkDocumentProperties(xDocProps);
302
303 importCustomFragments(xDocumentStorage);
304 }
305
createParser()306 FastParser* XmlFilterBase::createParser()
307 {
308 FastParser* pParser = new FastParser;
309 registerNamespaces(*pParser);
310 return pParser;
311 }
312
313 namespace {
314
getTransitionalRelationshipOfficeDocType(std::u16string_view rPart)315 OUString getTransitionalRelationshipOfficeDocType(std::u16string_view rPart)
316 {
317 return OUString::Concat("http://schemas.openxmlformats.org/officeDocument/2006/relationships/")
318 + rPart;
319 }
320
getStrictRelationshipOfficeDocType(std::u16string_view rPart)321 OUString getStrictRelationshipOfficeDocType(std::u16string_view rPart)
322 {
323 return OUString::Concat("http://purl.oclc.org/ooxml/officeDocument/relationships/") + rPart;
324 }
325
326 }
327
getFragmentPathFromFirstTypeFromOfficeDoc(std::u16string_view rPart)328 OUString XmlFilterBase::getFragmentPathFromFirstTypeFromOfficeDoc( std::u16string_view rPart )
329 {
330 // importRelations() caches the relations map for subsequence calls
331 const OUString aTransitionalRelationshipType = getTransitionalRelationshipOfficeDocType(rPart);
332 OUString aFragment = importRelations( OUString() )->getFragmentPathFromFirstType( aTransitionalRelationshipType );
333 if(aFragment.isEmpty())
334 {
335 const OUString aStrictRelationshipType = getStrictRelationshipOfficeDocType(rPart);
336 aFragment = importRelations( OUString() )->getFragmentPathFromFirstType( aStrictRelationshipType );
337 }
338
339 return aFragment;
340 }
341
importFragment(const rtl::Reference<FragmentHandler> & rxHandler)342 bool XmlFilterBase::importFragment( const rtl::Reference<FragmentHandler>& rxHandler )
343 {
344 FastParser aParser;
345 registerNamespaces(aParser);
346 return importFragment(rxHandler, aParser);
347 }
348
importFragment(const rtl::Reference<FragmentHandler> & rxHandler,FastParser & rParser)349 bool XmlFilterBase::importFragment( const rtl::Reference<FragmentHandler>& rxHandler, FastParser& rParser )
350 {
351 OSL_ENSURE( rxHandler.is(), "XmlFilterBase::importFragment - missing fragment handler" );
352 if( !rxHandler.is() )
353 return false;
354
355 // fragment handler must contain path to fragment stream
356 OUString aFragmentPath = rxHandler->getFragmentPath();
357 OSL_ENSURE( !aFragmentPath.isEmpty(), "XmlFilterBase::importFragment - missing fragment path" );
358 if( aFragmentPath.isEmpty() )
359 return false;
360
361 // try to import binary streams (fragment extension must be '.bin')
362 if (aFragmentPath.endsWith(gaBinSuffix))
363 {
364 try
365 {
366 // try to open the fragment stream (this may fail - do not assert)
367 Reference< XInputStream > xInStrm( openInputStream( aFragmentPath ), UNO_SET_THROW );
368
369 // create the record parser
370 RecordParser aParser;
371 aParser.setFragmentHandler( rxHandler );
372
373 // create the input source and parse the stream
374 RecordInputSource aSource;
375 aSource.mxInStream = std::make_shared<BinaryXInputStream>( xInStrm, true );
376 aSource.maSystemId = aFragmentPath;
377 aParser.parseStream( aSource );
378 return true;
379 }
380 catch( Exception& )
381 {
382 }
383 return false;
384 }
385
386 // get the XFastDocumentHandler interface from the fragment handler
387 if( !rxHandler.is() )
388 return false;
389
390 // try to import XML stream
391 try
392 {
393 /* Try to open the fragment stream (may fail, do not throw/assert).
394 Using the virtual function openFragmentStream() allows a document
395 handler to create specialized input streams, e.g. VML streams that
396 have to preprocess the raw input data. */
397 Reference< XInputStream > xInStrm = rxHandler->openFragmentStream();
398 /* tdf#100084 Check again the aFragmentPath route with lowercase file name
399 TODO: complete handling of case-insensitive file paths */
400 if ( !xInStrm.is() )
401 {
402 sal_Int32 nPathLen = aFragmentPath.lastIndexOf('/') + 1;
403 OUString fileName = aFragmentPath.copy(nPathLen);
404 OUString sLowerCaseFileName = fileName.toAsciiLowerCase();
405 if ( fileName != sLowerCaseFileName )
406 {
407 aFragmentPath = aFragmentPath.subView(0, nPathLen) + sLowerCaseFileName;
408 xInStrm = openInputStream(aFragmentPath);
409 }
410 }
411
412 // own try/catch block for showing parser failure assertion with fragment path
413 if( xInStrm.is() ) try
414 {
415 rParser.setDocumentHandler(rxHandler);
416 rParser.parseStream(xInStrm, aFragmentPath);
417 return true;
418 }
419 catch( Exception& )
420 {
421 OSL_FAIL( OStringBuffer( "XmlFilterBase::importFragment - XML parser failed in fragment '" +
422 OUStringToOString( aFragmentPath, RTL_TEXTENCODING_ASCII_US ) + "'" ).getStr() );
423 }
424 }
425 catch( Exception& )
426 {
427 }
428 return false;
429 }
430
importFragment(const OUString & aFragmentPath)431 Reference<XDocument> XmlFilterBase::importFragment( const OUString& aFragmentPath )
432 {
433 Reference<XDocument> xRet;
434
435 // path to fragment stream valid?
436 OSL_ENSURE( !aFragmentPath.isEmpty(), "XmlFilterBase::importFragment - empty fragment path" );
437 if( aFragmentPath.isEmpty() )
438 return xRet;
439
440 // try to open the fragment stream (this may fail - do not assert)
441 Reference< XInputStream > xInStrm = openInputStream( aFragmentPath );
442 if( !xInStrm.is() )
443 return xRet;
444
445 // binary streams (fragment extension is '.bin') currently not supported
446 if (aFragmentPath.endsWith(gaBinSuffix))
447 return xRet;
448
449 // try to import XML stream
450 try
451 {
452 // create the dom parser
453 Reference<XDocumentBuilder> xDomBuilder( DocumentBuilder::create( getComponentContext() ) );
454
455 // create DOM from fragment
456 xRet = xDomBuilder->parse(xInStrm);
457 }
458 catch( Exception& )
459 {
460 }
461
462 return xRet;
463 }
464
importFragment(const::rtl::Reference<FragmentHandler> & rxHandler,const Reference<XFastSAXSerializable> & rxSerializer)465 bool XmlFilterBase::importFragment( const ::rtl::Reference< FragmentHandler >& rxHandler,
466 const Reference< XFastSAXSerializable >& rxSerializer )
467 {
468 if( !rxHandler.is() )
469 return false;
470
471 // try to import XML stream
472 try
473 {
474 rxSerializer->fastSerialize( rxHandler,
475 mxImpl->maFastParser.getTokenHandler(),
476 Sequence< StringPair >(),
477 NamespaceIds() );
478 return true;
479 }
480 catch( Exception& )
481 {}
482
483 return false;
484 }
485
importRelations(const OUString & rFragmentPath)486 RelationsRef XmlFilterBase::importRelations( const OUString& rFragmentPath )
487 {
488 // try to find cached relations
489 RelationsRef& rxRelations = mxImpl->maRelationsMap[ rFragmentPath ];
490 if( !rxRelations )
491 {
492 // import and cache relations
493 rxRelations = std::make_shared<Relations>( rFragmentPath );
494 importFragment( new RelationsFragment( *this, rxRelations ) );
495 }
496 return rxRelations;
497 }
498
openFragmentStream(const OUString & rStreamName,const OUString & rMediaType)499 Reference< XOutputStream > XmlFilterBase::openFragmentStream( const OUString& rStreamName, const OUString& rMediaType )
500 {
501 Reference< XOutputStream > xOutputStream = openOutputStream( rStreamName );
502 PropertySet aPropSet( xOutputStream );
503 aPropSet.setProperty( PROP_MediaType, rMediaType );
504 return xOutputStream;
505 }
506
openFragmentStreamWithSerializer(const OUString & rStreamName,const OUString & rMediaType)507 FSHelperPtr XmlFilterBase::openFragmentStreamWithSerializer( const OUString& rStreamName, const OUString& rMediaType )
508 {
509 const bool bWriteHeader = rMediaType.indexOf( "vml" ) < 0 || rMediaType.indexOf( "+xml" ) >= 0;
510 return std::make_shared<FastSerializerHelper>( openFragmentStream( rStreamName, rMediaType ), bWriteHeader );
511 }
512
513 namespace {
514
lclAddRelation(const Reference<XRelationshipAccess> & rRelations,sal_Int32 nId,const OUString & rType,std::u16string_view rTarget,bool bExternal)515 OUString lclAddRelation( const Reference< XRelationshipAccess >& rRelations, sal_Int32 nId, const OUString& rType, std::u16string_view rTarget, bool bExternal )
516 {
517 OUString sId = "rId" + OUString::number( nId );
518
519 Sequence< StringPair > aEntry( bExternal ? 3 : 2 );
520 auto pEntry = aEntry.getArray();
521 pEntry[0].First = "Type";
522 pEntry[0].Second = rType;
523 pEntry[1].First = "Target";
524 pEntry[1].Second = INetURLObject::decode(rTarget, INetURLObject::DecodeMechanism::ToIUri, RTL_TEXTENCODING_UTF8);
525 if( bExternal )
526 {
527 pEntry[2].First = "TargetMode";
528 pEntry[2].Second = "External";
529 }
530 rRelations->insertRelationshipByID( sId, aEntry, true );
531
532 return sId;
533 }
534
535 } // namespace
536
addRelation(const OUString & rType,std::u16string_view rTarget)537 OUString XmlFilterBase::addRelation( const OUString& rType, std::u16string_view rTarget )
538 {
539 Reference< XRelationshipAccess > xRelations( getStorage()->getXStorage(), UNO_QUERY );
540 if( xRelations.is() )
541 return lclAddRelation( xRelations, mnRelId ++, rType, rTarget, false/*bExternal*/ );
542
543 return OUString();
544 }
545
addRelation(const Reference<XOutputStream> & rOutputStream,const OUString & rType,std::u16string_view rTarget,bool bExternal)546 OUString XmlFilterBase::addRelation( const Reference< XOutputStream >& rOutputStream, const OUString& rType, std::u16string_view rTarget, bool bExternal )
547 {
548 sal_Int32 nId = 0;
549
550 PropertySet aPropSet( rOutputStream );
551 if( aPropSet.is() )
552 aPropSet.getProperty( nId, PROP_RelId );
553 else
554 nId = mnRelId++;
555
556 Reference< XRelationshipAccess > xRelations( rOutputStream, UNO_QUERY );
557 if( xRelations.is() )
558 return lclAddRelation( xRelations, nId, rType, rTarget, bExternal );
559
560 return OUString();
561 }
562
563 static void
writeElement(const FSHelperPtr & pDoc,sal_Int32 nXmlElement,std::u16string_view sValue)564 writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, std::u16string_view sValue )
565 {
566 pDoc->startElement(nXmlElement);
567 pDoc->writeEscaped( sValue );
568 pDoc->endElement( nXmlElement );
569 }
570
571 static void
writeElement(const FSHelperPtr & pDoc,sal_Int32 nXmlElement,const sal_Int32 nValue)572 writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const sal_Int32 nValue )
573 {
574 pDoc->startElement(nXmlElement);
575 pDoc->write( nValue );
576 pDoc->endElement( nXmlElement );
577 }
578
579 static void
writeElement(const FSHelperPtr & pDoc,sal_Int32 nXmlElement,const util::DateTime & rTime)580 writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const util::DateTime& rTime )
581 {
582 if( rTime.Year == 0 )
583 return;
584
585 if ( ( nXmlElement >> 16 ) != XML_dcterms )
586 pDoc->startElement(nXmlElement);
587 else
588 pDoc->startElement(nXmlElement, FSNS(XML_xsi, XML_type), "dcterms:W3CDTF");
589
590 char pStr[200];
591 snprintf( pStr, sizeof( pStr ), "%d-%02d-%02dT%02d:%02d:%02dZ",
592 rTime.Year, rTime.Month, rTime.Day,
593 rTime.Hours, rTime.Minutes, rTime.Seconds );
594
595 pDoc->write( pStr );
596
597 pDoc->endElement( nXmlElement );
598 }
599
600 static void
writeElement(const FSHelperPtr & pDoc,sal_Int32 nXmlElement,const Sequence<OUString> & aItems)601 writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const Sequence< OUString >& aItems )
602 {
603 if( !aItems.hasElements() )
604 return;
605
606 OUStringBuffer sRep;
607 // tdf#143175 - join elements including a delimiter using a standard iterator
608 ::comphelper::intersperse(aItems.begin(), aItems.end(),
609 ::comphelper::OUStringBufferAppender(sRep), u" "_ustr);
610
611 writeElement( pDoc, nXmlElement, sRep );
612 }
613
614 static void
writeElement(const FSHelperPtr & pDoc,sal_Int32 nXmlElement,const LanguageTag & rLanguageTag)615 writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const LanguageTag& rLanguageTag )
616 {
617 // dc:language, Dublin Core recommends "such as RFC 4646", which is BCP 47
618 // and obsoleted by RFC 5646, see
619 // http://dublincore.org/documents/dcmi-terms/#terms-language
620 // http://dublincore.org/documents/dcmi-terms/#elements-language
621 writeElement( pDoc, nXmlElement, rLanguageTag.getBcp47MS() );
622 }
623
624 static void
writeCoreProperties(XmlFilterBase & rSelf,const Reference<XDocumentProperties> & xProperties)625 writeCoreProperties( XmlFilterBase& rSelf, const Reference< XDocumentProperties >& xProperties )
626 {
627 bool bRemovePersonalInfo
628 = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo);
629 bool bRemoveUserInfo
630 = bRemovePersonalInfo
631 && !SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnKeepDocUserInfo);
632
633 OUString sValue;
634 if( rSelf.getVersion() == oox::core::ISOIEC_29500_2008 )
635 {
636 // The lowercase "officedocument" is intentional and according to the spec
637 // (although most other places are written "officeDocument")
638 sValue = "http://schemas.openxmlformats.org/officedocument/2006/relationships/metadata/core-properties";
639 }
640 else
641 sValue = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties";
642
643 rSelf.addRelation( sValue, u"docProps/core.xml" );
644 FSHelperPtr pCoreProps = rSelf.openFragmentStreamWithSerializer(
645 u"docProps/core.xml"_ustr,
646 u"application/vnd.openxmlformats-package.core-properties+xml"_ustr );
647 pCoreProps->startElementNS( XML_cp, XML_coreProperties,
648 FSNS(XML_xmlns, XML_cp), rSelf.getNamespaceURL(OOX_NS(packageMetaCorePr)),
649 FSNS(XML_xmlns, XML_dc), rSelf.getNamespaceURL(OOX_NS(dc)),
650 FSNS(XML_xmlns, XML_dcterms), rSelf.getNamespaceURL(OOX_NS(dcTerms)),
651 FSNS(XML_xmlns, XML_dcmitype), rSelf.getNamespaceURL(OOX_NS(dcmiType)),
652 FSNS(XML_xmlns, XML_xsi), rSelf.getNamespaceURL(OOX_NS(xsi)));
653
654 uno::Reference<beans::XPropertyAccess> xUserDefinedProperties(xProperties->getUserDefinedProperties(), uno::UNO_QUERY);
655 comphelper::SequenceAsHashMap aUserDefinedProperties(xUserDefinedProperties->getPropertyValues());
656 comphelper::SequenceAsHashMap::iterator it;
657
658 it = aUserDefinedProperties.find(u"OOXMLCorePropertyCategory"_ustr);
659 if (it != aUserDefinedProperties.end())
660 {
661 OUString aValue;
662 if (it->second >>= aValue)
663 writeElement( pCoreProps, FSNS( XML_cp, XML_category ), aValue );
664 }
665
666 it = aUserDefinedProperties.find(u"OOXMLCorePropertyContentStatus"_ustr);
667 if (it != aUserDefinedProperties.end())
668 {
669 OUString aValue;
670 if (it->second >>= aValue)
671 writeElement( pCoreProps, FSNS( XML_cp, XML_contentStatus ), aValue );
672 }
673
674 it = aUserDefinedProperties.find(u"OOXMLCorePropertyContentType"_ustr);
675 if (it != aUserDefinedProperties.end())
676 {
677 OUString aValue;
678 if (it->second >>= aValue)
679 writeElement( pCoreProps, FSNS( XML_cp, XML_contentType ), aValue );
680 }
681 if (!bRemoveUserInfo)
682 {
683 writeElement(pCoreProps, FSNS(XML_dcterms, XML_created), xProperties->getCreationDate());
684 writeElement(pCoreProps, FSNS(XML_dc, XML_creator), xProperties->getAuthor());
685 }
686 writeElement( pCoreProps, FSNS( XML_dc, XML_description ), xProperties->getDescription() );
687
688 it = aUserDefinedProperties.find(u"OOXMLCorePropertyIdentifier"_ustr);
689 if (it != aUserDefinedProperties.end())
690 {
691 OUString aValue;
692 if (it->second >>= aValue)
693 writeElement( pCoreProps, FSNS( XML_dc, XML_identifier ), aValue );
694 }
695 writeElement( pCoreProps, FSNS( XML_cp, XML_keywords ), xProperties->getKeywords() );
696 writeElement( pCoreProps, FSNS( XML_dc, XML_language ), LanguageTag( xProperties->getLanguage()) );
697
698 if (!bRemoveUserInfo)
699 {
700 writeElement(pCoreProps, FSNS(XML_cp, XML_lastModifiedBy), xProperties->getModifiedBy());
701 writeElement(pCoreProps, FSNS(XML_cp, XML_lastPrinted), xProperties->getPrintDate());
702 writeElement(pCoreProps, FSNS(XML_dcterms, XML_modified),
703 xProperties->getModificationDate());
704 }
705 if (!bRemovePersonalInfo)
706 {
707 writeElement(pCoreProps, FSNS(XML_cp, XML_revision), xProperties->getEditingCycles());
708 }
709 writeElement( pCoreProps, FSNS( XML_dc, XML_subject ), xProperties->getSubject() );
710 writeElement( pCoreProps, FSNS( XML_dc, XML_title ), xProperties->getTitle() );
711
712 it = aUserDefinedProperties.find(u"OOXMLCorePropertyVersion"_ustr);
713 if (it != aUserDefinedProperties.end())
714 {
715 OUString aValue;
716 if (it->second >>= aValue)
717 writeElement( pCoreProps, FSNS( XML_cp, XML_version ), aValue );
718 }
719
720 pCoreProps->endElementNS( XML_cp, XML_coreProperties );
721
722 pCoreProps->endDocument();
723 }
724
725 static void
writeAppProperties(XmlFilterBase & rSelf,const Reference<XDocumentProperties> & xProperties)726 writeAppProperties( XmlFilterBase& rSelf, const Reference< XDocumentProperties >& xProperties )
727 {
728 bool bRemovePersonalInfo
729 = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo);
730 bool bRemoveUserInfo
731 = bRemovePersonalInfo
732 && !SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnKeepDocUserInfo);
733 rSelf.addRelation(
734 u"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"_ustr,
735 u"docProps/app.xml" );
736 FSHelperPtr pAppProps = rSelf.openFragmentStreamWithSerializer(
737 u"docProps/app.xml"_ustr,
738 u"application/vnd.openxmlformats-officedocument.extended-properties+xml"_ustr );
739 pAppProps->startElement( XML_Properties,
740 XML_xmlns, rSelf.getNamespaceURL(OOX_NS(officeExtPr)),
741 FSNS(XML_xmlns, XML_vt), rSelf.getNamespaceURL(OOX_NS(officeDocPropsVT)));
742
743 uno::Reference<beans::XPropertyAccess> xUserDefinedProperties(xProperties->getUserDefinedProperties(), uno::UNO_QUERY);
744 comphelper::SequenceAsHashMap aUserDefinedProperties(xUserDefinedProperties->getPropertyValues());
745 comphelper::SequenceAsHashMap::iterator it;
746
747 if (!bRemovePersonalInfo)
748 writeElement(pAppProps, XML_Template, xProperties->getTemplateName());
749
750 it = aUserDefinedProperties.find(u"Manager"_ustr);
751 if (it != aUserDefinedProperties.end())
752 {
753 OUString aValue;
754 if (it->second >>= aValue)
755 writeElement( pAppProps, XML_Manager, aValue );
756 }
757
758 #ifdef OOXTODO
759 writeElement( pAppProps, XML_PresentationFormat, "presentation format" );
760 writeElement( pAppProps, XML_Lines, "lines" );
761 writeElement( pAppProps, XML_Slides, "slides" );
762 writeElement( pAppProps, XML_Notes, "notes" );
763 #endif /* def OOXTODO */
764 // EditingDuration is in seconds, TotalTime is in minutes.
765 if (!bRemovePersonalInfo)
766 writeElement(pAppProps, XML_TotalTime, xProperties->getEditingDuration() / 60);
767 #ifdef OOXTODO
768 writeElement( pAppProps, XML_HiddenSlides, "hidden slides" );
769 writeElement( pAppProps, XML_MMClips, "mm clips" );
770 writeElement( pAppProps, XML_ScaleCrop, "scale crop" );
771 writeElement( pAppProps, XML_HeadingPairs, "heading pairs" );
772 writeElement( pAppProps, XML_TitlesOfParts, "titles of parts" );
773 writeElement( pAppProps, XML_LinksUpToDate, "links up-to-date" );
774 writeElement( pAppProps, XML_SharedDoc, "shared doc" );
775 writeElement( pAppProps, XML_HLinks, "hlinks" );
776 writeElement( pAppProps, XML_HyperlinksChanged, "hyperlinks changed" );
777 writeElement( pAppProps, XML_DigSig, "digital signature" );
778 #endif /* def OOXTODO */
779 writeElement( pAppProps, XML_Application, utl::DocInfoHelper::GetGeneratorString() );
780
781 it = aUserDefinedProperties.find(u"HyperlinkBase"_ustr);
782 if (it != aUserDefinedProperties.end())
783 {
784 OUString aValue;
785 if (it->second >>= aValue)
786 writeElement( pAppProps, XML_HyperlinkBase, aValue );
787 }
788 // AppVersion specifies the version of the application which produced document
789 // It is strictly connected with MS Office versions:
790 // * 12: [Office 2007] [LO < 7.0]
791 // * 14: [Office 2010]
792 // * 15: [Office 2013/2016/2019] [LO >= 7.0]
793 // The LibreOffice is application on 2013/2016/2019 level
794 writeElement( pAppProps, XML_AppVersion, u"15.0000" );
795
796 // OOXTODO Calculate DocSecurity value based on security (password, read-only etc.)
797 it = aUserDefinedProperties.find(u"DocSecurity"_ustr);
798 if (it != aUserDefinedProperties.end())
799 {
800 sal_Int32 nValue;
801 if (it->second >>= nValue)
802 writeElement( pAppProps, XML_DocSecurity, nValue );
803 }
804
805 comphelper::SequenceAsHashMap aStats = xProperties->getDocumentStatistics();
806 sal_Int32 nValue = 0;
807
808 it = aStats.find(u"PageCount"_ustr);
809 if (it != aStats.end())
810 {
811 if (it->second >>= nValue)
812 writeElement(pAppProps, XML_Pages, nValue);
813 }
814
815 it = aStats.find(u"WordCount"_ustr);
816 if (it != aStats.end())
817 {
818 if (it->second >>= nValue)
819 writeElement(pAppProps, XML_Words, nValue);
820 }
821
822 it = aStats.find(u"NonWhitespaceCharacterCount"_ustr);
823 if (it != aStats.end())
824 {
825 if (it->second >>= nValue)
826 writeElement(pAppProps, XML_Characters, nValue);
827 }
828
829 it = aStats.find(u"CharacterCount"_ustr);
830 if (it != aStats.end())
831 {
832 if (it->second >>= nValue)
833 writeElement(pAppProps, XML_CharactersWithSpaces, nValue);
834 }
835
836 it = aStats.find(u"ParagraphCount"_ustr);
837 if (it != aStats.end())
838 {
839 if (it->second >>= nValue)
840 writeElement(pAppProps, XML_Paragraphs, nValue);
841 }
842
843 it = aUserDefinedProperties.find(u"Company"_ustr);
844 if (it != aUserDefinedProperties.end() && !bRemoveUserInfo)
845 {
846 OUString aValue;
847 if (it->second >>= aValue)
848 writeElement(pAppProps, XML_Company, aValue);
849 }
850
851 pAppProps->endElement( XML_Properties );
852
853 pAppProps->endDocument();
854 }
855
856 static void
writeCustomProperties(XmlFilterBase & rSelf,const Reference<XDocumentProperties> & xProperties,bool bSecurityOptOpenReadOnly)857 writeCustomProperties( XmlFilterBase& rSelf, const Reference< XDocumentProperties >& xProperties, bool bSecurityOptOpenReadOnly )
858 {
859 uno::Reference<beans::XPropertyAccess> xUserDefinedProperties( xProperties->getUserDefinedProperties(), uno::UNO_QUERY );
860 auto aprop = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(xUserDefinedProperties->getPropertyValues());
861 sal_Int32 nbCustomProperties = aprop.size();
862 // tdf#89791 : if no custom properties, no need to add docProps/custom.x
863 // tdf#107690: except the case of read-only documents, because that
864 // is handled by the _MarkAsFinal custom property in MSO.
865 if (!nbCustomProperties && !bSecurityOptOpenReadOnly)
866 return;
867
868 if (bSecurityOptOpenReadOnly)
869 {
870 PropertyValue aPropertyValue;
871 // MSO custom property for read-only documents
872 aPropertyValue.Name = "_MarkAsFinal";
873 aPropertyValue.Value <<= true;
874 aprop.push_back(aPropertyValue);
875 }
876
877 rSelf.addRelation(
878 u"http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties"_ustr,
879 u"docProps/custom.xml" );
880 FSHelperPtr pAppProps = rSelf.openFragmentStreamWithSerializer(
881 u"docProps/custom.xml"_ustr,
882 u"application/vnd.openxmlformats-officedocument.custom-properties+xml"_ustr );
883 pAppProps->startElement( XML_Properties,
884 XML_xmlns, rSelf.getNamespaceURL(OOX_NS(officeCustomPr)),
885 FSNS(XML_xmlns, XML_vt), rSelf.getNamespaceURL(OOX_NS(officeDocPropsVT)));
886
887 size_t nIndex = 0;
888 for (const auto& rProp : aprop)
889 {
890 if ( !rProp.Name.isEmpty() )
891 {
892 // Skip storing these values in Custom Properties as it will be stored in Core/Extended Properties
893 if (( rProp.Name == "OOXMLCorePropertyCategory" ) || // stored in cp:category
894 ( rProp.Name == "OOXMLCorePropertyContentStatus" ) || // stored in cp:contentStatus
895 ( rProp.Name == "OOXMLCorePropertyContentType" ) || // stored in cp:contentType
896 ( rProp.Name == "OOXMLCorePropertyIdentifier" ) || // stored in dc:identifier
897 ( rProp.Name == "OOXMLCorePropertyVersion" ) || // stored in cp:version
898 ( rProp.Name == "HyperlinkBase" ) || // stored in Extended File Properties
899 ( rProp.Name == "AppVersion" ) || // stored in Extended File Properties
900 ( rProp.Name == "DocSecurity" ) || // stored in Extended File Properties
901 ( rProp.Name == "Manager" ) || // stored in Extended File Properties
902 ( rProp.Name == "Company" )) // stored in Extended File Properties
903 continue;
904
905 // pid starts from 2 not from 1 as MS supports pid from 2
906 pAppProps->startElement( XML_property ,
907 XML_fmtid, "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}",
908 XML_pid, OString::number(nIndex + 2),
909 XML_name, rProp.Name);
910
911 switch ( rProp.Value.getValueTypeClass() )
912 {
913 case TypeClass_STRING:
914 {
915 OUString aValue;
916 rProp.Value >>= aValue;
917 writeElement( pAppProps, FSNS( XML_vt, XML_lpwstr ), aValue );
918 }
919 break;
920 case TypeClass_BOOLEAN:
921 {
922 bool val = *o3tl::forceAccess<bool>(rProp.Value);
923 writeElement( pAppProps, FSNS( XML_vt, XML_bool ), val ? 1 : 0);
924 }
925 break;
926 case TypeClass_DOUBLE:
927 {
928 double num = {}; // spurious -Werror=maybe-uninitialized
929 if ( rProp.Value >>= num )
930 {
931 // r8 - 8-byte real number
932 writeElement( pAppProps, FSNS( XML_vt, XML_r8 ), OUString::number(num) );
933 }
934 }
935 break;
936 default:
937 {
938 double num = {}; // spurious -Werror=maybe-uninitialized
939 util::Date aDate;
940 util::Duration aDuration;
941 util::DateTime aDateTime;
942 if ( rProp.Value >>= num )
943 {
944 // i4 - 4-byte signed integer
945 writeElement( pAppProps, FSNS( XML_vt, XML_i4 ), num );
946 }
947 else if ( rProp.Value >>= aDate )
948 {
949 aDateTime = util::DateTime( 0, 0 , 0, 0, aDate.Day, aDate.Month, aDate.Year, true );
950 writeElement( pAppProps, FSNS( XML_vt, XML_filetime ), aDateTime);
951 }
952 else if ( rProp.Value >>= aDuration )
953 {
954 OUStringBuffer buf;
955 ::sax::Converter::convertDuration( buf, aDuration );
956 OUString aDurationStr = buf.makeStringAndClear();
957 writeElement( pAppProps, FSNS( XML_vt, XML_lpwstr ), aDurationStr );
958 }
959 else if ( rProp.Value >>= aDateTime )
960 writeElement( pAppProps, FSNS( XML_vt, XML_filetime ), aDateTime );
961 else
962 //no other options
963 OSL_FAIL( "XMLFilterBase::writeCustomProperties unsupported value type!" );
964 }
965 break;
966 }
967 pAppProps->endElement( XML_property );
968 }
969 ++nIndex;
970 }
971 pAppProps->endElement( XML_Properties );
972
973 pAppProps->endDocument();
974 }
975
exportDocumentProperties(const Reference<XDocumentProperties> & xProperties,bool bSecurityOptOpenReadOnly)976 void XmlFilterBase::exportDocumentProperties( const Reference< XDocumentProperties >& xProperties, bool bSecurityOptOpenReadOnly )
977 {
978 if( xProperties.is() )
979 {
980 writeCoreProperties( *this, xProperties );
981 writeAppProperties( *this, xProperties );
982 writeCustomProperties( *this, xProperties, bSecurityOptOpenReadOnly );
983 }
984 }
985
986 // protected ------------------------------------------------------------------
987
implGetInputStream(MediaDescriptor & rMediaDesc) const988 Reference< XInputStream > XmlFilterBase::implGetInputStream( MediaDescriptor& rMediaDesc ) const
989 {
990 /* Get the input stream directly from the media descriptor, or decrypt the
991 package again. The latter is needed e.g. when the document is reloaded.
992 All this is implemented in the detector service. */
993 rtl::Reference< FilterDetect > xDetector( new FilterDetect( getComponentContext() ) );
994 return xDetector->extractUnencryptedPackage( rMediaDesc );
995 }
996
implGetOutputStream(MediaDescriptor & rMediaDescriptor) const997 Reference<XStream> XmlFilterBase::implGetOutputStream( MediaDescriptor& rMediaDescriptor ) const
998 {
999 const Sequence< NamedValue > aMediaEncData = rMediaDescriptor.getUnpackedValueOrDefault(
1000 MediaDescriptor::PROP_ENCRYPTIONDATA,
1001 Sequence< NamedValue >() );
1002
1003 if (aMediaEncData.getLength() == 0)
1004 {
1005 return FilterBase::implGetOutputStream( rMediaDescriptor );
1006 }
1007 else // We need to encrypt the stream so create a memory stream
1008 {
1009 Reference< XComponentContext > xContext = getComponentContext();
1010 return Reference< XStream > (
1011 xContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.comp.MemoryStream"_ustr, xContext),
1012 uno::UNO_QUERY_THROW );
1013 }
1014 }
1015
implFinalizeExport(MediaDescriptor & rMediaDescriptor)1016 bool XmlFilterBase::implFinalizeExport( MediaDescriptor& rMediaDescriptor )
1017 {
1018 bool bRet = true;
1019
1020 const Sequence< NamedValue > aMediaEncData = rMediaDescriptor.getUnpackedValueOrDefault(
1021 MediaDescriptor::PROP_ENCRYPTIONDATA,
1022 Sequence< NamedValue >() );
1023
1024 if (aMediaEncData.getLength())
1025 {
1026 commitStorage();
1027
1028 Reference< XStream> xDocumentStream (FilterBase::implGetOutputStream(rMediaDescriptor));
1029 oox::ole::OleStorage aOleStorage( getComponentContext(), xDocumentStream, true );
1030 crypto::DocumentEncryption encryptor( getComponentContext(), getMainDocumentStream(), aOleStorage, aMediaEncData );
1031 bRet = encryptor.encrypt();
1032 if (bRet)
1033 aOleStorage.commit();
1034 }
1035
1036 return bRet;
1037 }
1038
1039 // private --------------------------------------------------------------------
1040
implCreateStorage(const Reference<XInputStream> & rxInStream) const1041 StorageRef XmlFilterBase::implCreateStorage( const Reference< XInputStream >& rxInStream ) const
1042 {
1043 return std::make_shared<ZipStorage>(
1044 getComponentContext(), rxInStream,
1045 getMediaDescriptor().getUnpackedValueOrDefault(u"RepairPackage"_ustr, false));
1046 }
1047
implCreateStorage(const Reference<XStream> & rxOutStream) const1048 StorageRef XmlFilterBase::implCreateStorage( const Reference< XStream >& rxOutStream ) const
1049 {
1050 return std::make_shared<ZipStorage>( getComponentContext(), rxOutStream );
1051 }
1052
isMSO2007Document() const1053 bool XmlFilterBase::isMSO2007Document() const
1054 {
1055 return mbMSO2007;
1056 }
1057
isMSODocument() const1058 bool XmlFilterBase::isMSODocument() const
1059 {
1060 return mbMSO;
1061 }
1062
setMissingExtDrawing()1063 void XmlFilterBase::setMissingExtDrawing()
1064 {
1065 mbMissingExtDrawing = true;
1066 }
1067
setDiagramFontHeights(NamedShapePairs * pDiagramFontHeights)1068 void XmlFilterBase::setDiagramFontHeights(NamedShapePairs* pDiagramFontHeights)
1069 {
1070 mxImpl->mpDiagramFontHeights = pDiagramFontHeights;
1071 }
1072
getDiagramFontHeights()1073 NamedShapePairs* XmlFilterBase::getDiagramFontHeights() { return mxImpl->mpDiagramFontHeights; }
1074
getNamespaceURL(sal_Int32 nNSID) const1075 OUString XmlFilterBase::getNamespaceURL(sal_Int32 nNSID) const
1076 {
1077 auto itr = mxImpl->mrNamespaceMap.maTransitionalNamespaceMap.find(nNSID);
1078 if (itr == mxImpl->mrNamespaceMap.maTransitionalNamespaceMap.end())
1079 {
1080 SAL_WARN("oox", "missing namespace in the namespace map for : " << nNSID);
1081 return OUString();
1082 }
1083
1084 return itr->second;
1085 }
1086
importCustomFragments(css::uno::Reference<css::embed::XStorage> const & xDocumentStorage)1087 void XmlFilterBase::importCustomFragments(css::uno::Reference<css::embed::XStorage> const & xDocumentStorage)
1088 {
1089 Reference<XRelationshipAccess> xRelations(xDocumentStorage, UNO_QUERY);
1090 if (!xRelations.is())
1091 return;
1092
1093 const uno::Sequence<uno::Sequence<beans::StringPair>> aSeqs = xRelations->getAllRelationships();
1094
1095 std::vector<StreamDataSequence> aCustomFragments;
1096 std::vector<OUString> aCustomFragmentTypes;
1097 std::vector<OUString> aCustomFragmentTargets;
1098 for (const uno::Sequence<beans::StringPair>& aSeq : aSeqs)
1099 {
1100 OUString sType;
1101 OUString sTarget;
1102 for (const beans::StringPair& aPair : aSeq)
1103 {
1104 if (aPair.First == "Target")
1105 sTarget = aPair.Second;
1106 else if (aPair.First == "Type")
1107 sType = aPair.Second;
1108 }
1109
1110 // Preserve non-standard (i.e. custom) entries.
1111 if (!sType.match("http://schemas.openxmlformats.org") // OOXML/ECMA Transitional
1112 && !sType.match("http://purl.oclc.org")) // OOXML Strict
1113 {
1114 StreamDataSequence aDataSeq;
1115 if (importBinaryData(aDataSeq, sTarget))
1116 {
1117 aCustomFragments.emplace_back(aDataSeq);
1118 aCustomFragmentTypes.emplace_back(sType);
1119 aCustomFragmentTargets.emplace_back(sTarget);
1120 }
1121 }
1122 }
1123
1124 // Adding the saved custom xml DOM
1125 comphelper::SequenceAsHashMap aGrabBagProperties;
1126 aGrabBagProperties[u"OOXCustomFragments"_ustr] <<= comphelper::containerToSequence(aCustomFragments);
1127 aGrabBagProperties[u"OOXCustomFragmentTypes"_ustr] <<= comphelper::containerToSequence(aCustomFragmentTypes);
1128 aGrabBagProperties[u"OOXCustomFragmentTargets"_ustr] <<= comphelper::containerToSequence(aCustomFragmentTargets);
1129
1130 std::vector<uno::Reference<xml::dom::XDocument>> aCustomXmlDomList;
1131 std::vector<uno::Reference<xml::dom::XDocument>> aCustomXmlDomPropsList;
1132 //FIXME: Ideally, we should get these the relations, but it seems that is not consistently set.
1133 // In some cases it's stored in the workbook relationships, which is unexpected. So we discover them directly.
1134 for (int i = 1; ; ++i)
1135 {
1136 Reference<XDocument> xCustDoc = importFragment("customXml/item" + OUString::number(i) + ".xml");
1137 Reference<XDocument> xCustDocProps = importFragment("customXml/itemProps" + OUString::number(i) + ".xml");
1138 if (xCustDoc && xCustDocProps)
1139 {
1140 aCustomXmlDomList.emplace_back(xCustDoc);
1141 aCustomXmlDomPropsList.emplace_back(xCustDocProps);
1142 }
1143 else
1144 break;
1145 }
1146
1147 // Adding the saved custom xml DOM
1148 aGrabBagProperties[u"OOXCustomXml"_ustr] <<= comphelper::containerToSequence(aCustomXmlDomList);
1149 aGrabBagProperties[u"OOXCustomXmlProps"_ustr] <<= comphelper::containerToSequence(aCustomXmlDomPropsList);
1150
1151 // Save the [Content_Types].xml after parsing.
1152 uno::Sequence<uno::Sequence<beans::StringPair>> aContentTypeInfo;
1153 uno::Reference<io::XInputStream> xInputStream = openInputStream(u"[Content_Types].xml"_ustr);
1154 if (xInputStream.is())
1155 aContentTypeInfo = comphelper::OFOPXMLHelper::ReadContentTypeSequence(xInputStream, getComponentContext());
1156
1157 aGrabBagProperties[u"OOXContentTypes"_ustr] <<= aContentTypeInfo;
1158
1159 Reference<XComponent> xModel = getModel();
1160 oox::core::XmlFilterBase::putPropertiesToDocumentGrabBag(xModel, aGrabBagProperties);
1161 }
1162
exportCustomFragments()1163 void XmlFilterBase::exportCustomFragments()
1164 {
1165 Reference<XComponent> xModel = getModel();
1166 uno::Reference<beans::XPropertySet> xPropSet(xModel, uno::UNO_QUERY_THROW);
1167
1168 uno::Reference<beans::XPropertySetInfo> xPropSetInfo = xPropSet->getPropertySetInfo();
1169 if (!xPropSetInfo->hasPropertyByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG))
1170 return;
1171
1172 uno::Sequence<uno::Reference<xml::dom::XDocument>> customXmlDomlist;
1173 uno::Sequence<uno::Reference<xml::dom::XDocument>> customXmlDomPropslist;
1174 uno::Sequence<StreamDataSequence> customFragments;
1175 uno::Sequence<OUString> customFragmentTypes;
1176 uno::Sequence<OUString> customFragmentTargets;
1177 uno::Sequence<uno::Sequence<beans::StringPair>> aContentTypes;
1178
1179 uno::Sequence<beans::PropertyValue> propList;
1180 xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= propList;
1181 for (const auto& rProp : propList)
1182 {
1183 const OUString propName = rProp.Name;
1184 if (propName == "OOXCustomXml")
1185 {
1186 rProp.Value >>= customXmlDomlist;
1187 }
1188 else if (propName == "OOXCustomXmlProps")
1189 {
1190 rProp.Value >>= customXmlDomPropslist;
1191 }
1192 else if (propName == "OOXCustomFragments")
1193 {
1194 rProp.Value >>= customFragments;
1195 }
1196 else if (propName == "OOXCustomFragmentTypes")
1197 {
1198 rProp.Value >>= customFragmentTypes;
1199 }
1200 else if (propName == "OOXCustomFragmentTargets")
1201 {
1202 rProp.Value >>= customFragmentTargets;
1203 }
1204 else if (propName == "OOXContentTypes")
1205 {
1206 rProp.Value >>= aContentTypes;
1207 }
1208 }
1209
1210 // Expect customXmlDomPropslist.getLength() == customXmlDomlist.getLength().
1211 for (sal_Int32 j = 0; j < customXmlDomlist.getLength(); j++)
1212 {
1213 uno::Reference<xml::dom::XDocument> customXmlDom = customXmlDomlist[j];
1214 uno::Reference<xml::dom::XDocument> customXmlDomProps = customXmlDomPropslist[j];
1215 const OUString fragmentPath = "customXml/item" + OUString::number(j+1) + ".xml";
1216 if (customXmlDom.is())
1217 {
1218 addRelation(oox::getRelationship(Relationship::CUSTOMXML), Concat2View("../" + fragmentPath));
1219
1220 uno::Reference<xml::sax::XSAXSerializable> serializer(customXmlDom, uno::UNO_QUERY);
1221 uno::Reference<xml::sax::XWriter> writer = xml::sax::Writer::create(comphelper::getProcessComponentContext());
1222 writer->setOutputStream(openFragmentStream(fragmentPath, u"application/xml"_ustr));
1223 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
1224 uno::Sequence<beans::StringPair>());
1225 }
1226
1227 if (customXmlDomProps.is())
1228 {
1229 uno::Reference<xml::sax::XSAXSerializable> serializer(customXmlDomProps, uno::UNO_QUERY);
1230 uno::Reference<xml::sax::XWriter> writer = xml::sax::Writer::create(comphelper::getProcessComponentContext());
1231 writer->setOutputStream(openFragmentStream("customXml/itemProps"+OUString::number(j+1)+".xml",
1232 u"application/vnd.openxmlformats-officedocument.customXmlProperties+xml"_ustr));
1233 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
1234 uno::Sequence<beans::StringPair>());
1235
1236 // Adding itemprops's relationship entry to item.xml.rels file
1237 addRelation(openFragmentStream(fragmentPath, u"application/xml"_ustr),
1238 oox::getRelationship(Relationship::CUSTOMXMLPROPS),
1239 Concat2View("itemProps"+OUString::number(j+1)+".xml"));
1240 }
1241 }
1242
1243 // Expect customFragments.getLength() == customFragmentTypes.getLength() == customFragmentTargets.getLength().
1244 for (sal_Int32 j = 0; j < customFragments.getLength(); j++)
1245 {
1246 addRelation(customFragmentTypes[j], customFragmentTargets[j]);
1247 const OUString aFilename = customFragmentTargets[j];
1248 Reference<XOutputStream> xOutStream = openOutputStream(aFilename);
1249 if (xOutStream.is())
1250 {
1251 xOutStream->writeBytes(customFragments[j]);
1252 uno::Reference<XPropertySet> xProps(xOutStream, uno::UNO_QUERY);
1253 if (xProps.is())
1254 {
1255 const OUString aType = comphelper::OFOPXMLHelper::GetContentTypeByName(aContentTypes, aFilename);
1256 const OUString aContentType = (aType.getLength() ? aType : u"application/octet-stream"_ustr);
1257 xProps->setPropertyValue(u"MediaType"_ustr, uno::Any(aContentType));
1258 }
1259 }
1260 }
1261 }
1262
1263 } // namespace oox::core
1264
1265 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1266