xref: /core/oox/source/core/xmlfilterbase.cxx (revision 239ceb31)
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