LibreOffice Module oox (master) 1
filterdetect.cxx
Go to the documentation of this file.
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
21
22#include <com/sun/star/io/XStream.hpp>
26
31#include <oox/token/namespaces.hxx>
32#include <oox/token/tokens.hxx>
33
35
36#include <com/sun/star/uri/UriReferenceFactory.hpp>
37#include <com/sun/star/beans/NamedValue.hpp>
38#include <o3tl/string_view.hxx>
39#include <utility>
40
41using namespace ::com::sun::star;
42
43namespace oox::core {
44
45using namespace ::com::sun::star::beans;
46using namespace ::com::sun::star::io;
47using namespace ::com::sun::star::lang;
48using namespace ::com::sun::star::uno;
49using namespace ::com::sun::star::xml::sax;
50using namespace ::com::sun::star::uri;
51
55
56FilterDetectDocHandler::FilterDetectDocHandler( const Reference< XComponentContext >& rxContext, OUString& rFilterName, OUString aFileName ) :
57 mrFilterName( rFilterName ),
58 maFileName(std::move(aFileName)),
59 maOOXMLVariant( OOXMLVariant::ECMA_Transitional ),
60 mxContext( rxContext )
61{
62 maContextStack.reserve( 2 );
63}
64
66{
67}
68
70{
71}
72
74{
75}
76
77void SAL_CALL FilterDetectDocHandler::processingInstruction( const OUString& /*rTarget*/, const OUString& /*rData*/ )
78{
79}
80
81void SAL_CALL FilterDetectDocHandler::setDocumentLocator( const Reference<XLocator>& /*xLocator*/ )
82{
83}
84
86 sal_Int32 nElement, const Reference< XFastAttributeList >& rAttribs )
87{
88 AttributeList aAttribs( rAttribs );
89 switch ( nElement )
90 {
91 // cases for _rels/.rels
92 case PR_TOKEN( Relationships ):
93 break;
94 case PR_TOKEN( Relationship ):
95 if( !maContextStack.empty() && (maContextStack.back() == PR_TOKEN( Relationships )) )
96 parseRelationship( aAttribs );
97 break;
98
99 // cases for [Content_Types].xml
100 case PC_TOKEN( Types ):
101 break;
102 case PC_TOKEN( Default ):
103 if( !maContextStack.empty() && (maContextStack.back() == PC_TOKEN( Types )) )
104 parseContentTypesDefault( aAttribs );
105 break;
106 case PC_TOKEN( Override ):
107 if( !maContextStack.empty() && (maContextStack.back() == PC_TOKEN( Types )) )
108 parseContentTypesOverride( aAttribs );
109 break;
110 }
111 maContextStack.push_back( nElement );
112}
113
115 const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/ )
116{
117}
118
119void SAL_CALL FilterDetectDocHandler::endFastElement( sal_Int32 /*nElement*/ )
120{
121 maContextStack.pop_back();
122}
123
125 const OUString& /*Namespace*/, const OUString& /*Name*/ )
126{
127}
128
129Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createFastChildContext(
130 sal_Int32 /*Element*/, const Reference<XFastAttributeList>& /*Attribs*/ )
131{
132 return this;
133}
134
135Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createUnknownChildContext(
136 const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/)
137{
138 return this;
139}
140
141void SAL_CALL FilterDetectDocHandler::characters( const OUString& /*aChars*/ )
142{
143}
144
146{
147 OUString aType = rAttribs.getStringDefaulted( XML_Type);
148
149 // tdf#131936 Remember filter when opening file as 'Office Open XML Text'
150 if (aType.startsWithIgnoreAsciiCase("http://schemas.openxmlformats.org/officedocument/2006/relationships/metadata/core-properties"))
152 else if (aType.startsWithIgnoreAsciiCase("http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"))
154 else if (aType.startsWithIgnoreAsciiCase("http://purl.oclc.org/ooxml/officeDocument"))
156
157 if ( aType != "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" // OOXML Transitional
158 && aType != "http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument" ) //OOXML strict
159 return;
160
161 Reference<XUriReferenceFactory> xFactory = UriReferenceFactory::create( mxContext );
162 try
163 {
164 // use '/' to representent the root of the zip package ( and provide a 'file' scheme to
165 // keep the XUriReference implementation happy )
166 Reference< XUriReference > xBase = xFactory->parse( "file:///" );
167
168 Reference< XUriReference > xPart = xFactory->parse( rAttribs.getStringDefaulted( XML_Target) );
169 Reference< XUriReference > xAbs = xFactory->makeAbsolute( xBase, xPart, true, RelativeUriExcessParentSegments_RETAIN );
170
171 if ( xAbs.is() )
172 maTargetPath = xAbs->getPath();
173 }
174 catch( const Exception& )
175 {
176 }
177}
178
179OUString FilterDetectDocHandler::getFilterNameFromContentType( std::u16string_view rContentType, std::u16string_view rFileName )
180{
181 bool bDocm = o3tl::endsWithIgnoreAsciiCase(rFileName, ".docm");
182
183 if( rContentType == u"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" && !bDocm )
184 {
185 switch (maOOXMLVariant)
186 {
188 case OOXMLVariant::ISO_Strict: // Not supported, map to ISO transitional
189 return "writer_OOXML";
191 return "writer_MS_Word_2007";
192 }
193 }
194
195 if( rContentType == u"application/vnd.ms-word.document.macroEnabled.main+xml" || bDocm )
196 return "writer_MS_Word_2007_VBA";
197
198 if( rContentType == u"application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml" ||
199 rContentType == u"application/vnd.ms-word.template.macroEnabledTemplate.main+xml" )
200 {
201 switch (maOOXMLVariant)
202 {
204 case OOXMLVariant::ISO_Strict: // Not supported, map to ISO transitional
205 return "writer_OOXML_Text_Template";
207 return "writer_MS_Word_2007_Template";
208 }
209 }
210
211 if( rContentType == u"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml")
212 return "MS Excel 2007 XML";
213
214 if (rContentType == u"application/vnd.ms-excel.sheet.macroEnabled.main+xml")
215 return "MS Excel 2007 VBA XML";
216
217 if( rContentType == u"application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml" ||
218 rContentType == u"application/vnd.ms-excel.template.macroEnabled.main+xml" )
219 return "MS Excel 2007 XML Template";
220
221 if ( rContentType == u"application/vnd.ms-excel.sheet.binary.macroEnabled.main" )
222 return "MS Excel 2007 Binary";
223
224 if (rContentType == u"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml")
225 return "MS PowerPoint 2007 XML";
226
227 if (rContentType == u"application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml")
228 return "MS PowerPoint 2007 XML VBA";
229
230 if( rContentType == u"application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml" ||
231 rContentType == u"application/vnd.ms-powerpoint.slideshow.macroEnabled.main+xml" )
232 return "MS PowerPoint 2007 XML AutoPlay";
233
234 if( rContentType == u"application/vnd.openxmlformats-officedocument.presentationml.template.main+xml" ||
235 rContentType == u"application/vnd.ms-powerpoint.template.macroEnabled.main+xml" )
236 return "MS PowerPoint 2007 XML Template";
237
238 return OUString();
239}
240
242{
243 // only if no overridden part name found
244 if( mrFilterName.isEmpty() )
245 {
246 // check if target path ends with extension
247 OUString aExtension = rAttribs.getStringDefaulted( XML_Extension);
248 sal_Int32 nExtPos = maTargetPath.getLength() - aExtension.getLength();
249 if( (nExtPos > 0) && (maTargetPath[ nExtPos - 1 ] == '.') && maTargetPath.match( aExtension, nExtPos ) )
251 }
252}
253
255{
256 if( rAttribs.getStringDefaulted( XML_PartName) == maTargetPath )
258}
259
260FilterDetect::FilterDetect( const Reference< XComponentContext >& rxContext ) :
261 mxContext( rxContext, UNO_SET_THROW )
262{
263}
264
266{
267}
268
269namespace
270{
271
272bool lclIsZipPackage( const Reference< XComponentContext >& rxContext, const Reference< XInputStream >& rxInStrm )
273{
274 ZipStorage aZipStorage( rxContext, rxInStrm );
275 return aZipStorage.isStorage();
276}
277
278class PasswordVerifier : public IDocPasswordVerifier
279{
280public:
281 explicit PasswordVerifier( crypto::DocumentDecryption& aDecryptor );
282
283 virtual DocPasswordVerifierResult verifyPassword( const OUString& rPassword, Sequence<NamedValue>& rEncryptionData ) override;
284
285 virtual DocPasswordVerifierResult verifyEncryptionData( const Sequence<NamedValue>& rEncryptionData ) override;
286private:
287 crypto::DocumentDecryption& mDecryptor;
288};
289
290PasswordVerifier::PasswordVerifier( crypto::DocumentDecryption& aDecryptor ) :
291 mDecryptor(aDecryptor)
292{}
293
294comphelper::DocPasswordVerifierResult PasswordVerifier::verifyPassword( const OUString& rPassword, Sequence<NamedValue>& rEncryptionData )
295{
296 try
297 {
298 if (mDecryptor.generateEncryptionKey(rPassword))
299 rEncryptionData = mDecryptor.createEncryptionData(rPassword);
300 }
301 catch (...)
302 {
303 // Any exception is a reason to abort
305 }
306
308}
309
310comphelper::DocPasswordVerifierResult PasswordVerifier::verifyEncryptionData( const Sequence<NamedValue>& )
311{
313}
314
315} // namespace
316
317Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescriptor& rMediaDescriptor ) const
318{
319 // try the plain input stream
320 Reference<XInputStream> xInputStream( rMediaDescriptor[ MediaDescriptor::PROP_INPUTSTREAM ], UNO_QUERY );
321 if( !xInputStream.is() || lclIsZipPackage( mxContext, xInputStream ) )
322 return xInputStream;
323
324 // check if a temporary file is passed in the 'ComponentData' property
325 Reference<XStream> xDecrypted( rMediaDescriptor.getComponentDataEntry( "DecryptedPackage" ), UNO_QUERY );
326 if( xDecrypted.is() )
327 {
328 Reference<XInputStream> xDecryptedInputStream = xDecrypted->getInputStream();
329 if( lclIsZipPackage( mxContext, xDecryptedInputStream ) )
330 return xDecryptedInputStream;
331 }
332
333 // try to decrypt an encrypted OLE package
334 oox::ole::OleStorage aOleStorage( mxContext, xInputStream, false );
335 if( aOleStorage.isStorage() )
336 {
337 try
338 {
339 crypto::DocumentDecryption aDecryptor(mxContext, aOleStorage);
340
341 if( aDecryptor.readEncryptionInfo() )
342 {
343 /* "VelvetSweatshop" is the built-in default encryption
344 password used by MS Excel for the "workbook protection"
345 feature with password. Try this first before prompting the
346 user for a password. */
347 std::vector<OUString> aDefaultPasswords;
348 aDefaultPasswords.emplace_back("VelvetSweatshop");
349
350 /* Use the comphelper password helper to request a password.
351 This helper returns either with the correct password
352 (according to the verifier), or with an empty string if
353 user has cancelled the password input dialog. */
354 PasswordVerifier aVerifier( aDecryptor );
355 Sequence<NamedValue> aEncryptionData = rMediaDescriptor.requestAndVerifyDocPassword(
356 aVerifier,
358 &aDefaultPasswords );
359
360 if( !aEncryptionData.hasElements() )
361 {
362 rMediaDescriptor[ MediaDescriptor::PROP_ABORTED ] <<= true;
363 }
364 else
365 {
366 // create MemoryStream for unencrypted package - rather not put this in a tempfile
367 Reference<XStream> const xTempStream(
368 mxContext->getServiceManager()->createInstanceWithContext(
369 "com.sun.star.comp.MemoryStream", mxContext),
370 UNO_QUERY_THROW);
371
372 // if decryption was unsuccessful (corrupted file or any other reason)
373 if (!aDecryptor.decrypt(xTempStream))
374 {
375 rMediaDescriptor[ MediaDescriptor::PROP_ABORTED ] <<= true;
376 }
377 else
378 {
379 // store temp file in media descriptor to keep it alive
380 rMediaDescriptor.setComponentDataEntry( "DecryptedPackage", Any( xTempStream ) );
381
382 Reference<XInputStream> xDecryptedInputStream = xTempStream->getInputStream();
383 if( lclIsZipPackage( mxContext, xDecryptedInputStream ) )
384 return xDecryptedInputStream;
385 }
386 }
387 }
388 }
389 catch( const Exception& )
390 {
391 }
392 }
393 return Reference<XInputStream>();
394}
395
396// com.sun.star.lang.XServiceInfo interface -----------------------------------
397
399{
400 return "com.sun.star.comp.oox.FormatDetector";
401}
402
403sal_Bool SAL_CALL FilterDetect::supportsService( const OUString& rServiceName )
404{
405 return cppu::supportsService(this, rServiceName);
406}
407
408Sequence< OUString > SAL_CALL FilterDetect::getSupportedServiceNames()
409{
410 return { "com.sun.star.frame.ExtendedTypeDetection" };
411}
412
413// com.sun.star.document.XExtendedFilterDetection interface -------------------
414
415OUString SAL_CALL FilterDetect::detect( Sequence< PropertyValue >& rMediaDescSeq )
416{
417 OUString aFilterName;
418 MediaDescriptor aMediaDescriptor( rMediaDescSeq );
419
420 try
421 {
422 aMediaDescriptor.addInputStream();
423
424 /* Get the unencrypted input stream. This may include creation of a
425 temporary file that contains the decrypted package. This temporary
426 file will be stored in the 'ComponentData' property of the media
427 descriptor. */
428 Reference< XInputStream > xInputStream( extractUnencryptedPackage( aMediaDescriptor ), UNO_SET_THROW );
429
430 // stream must be a ZIP package
431 ZipStorage aZipStorage( mxContext, xInputStream );
432 if( aZipStorage.isStorage() )
433 {
434 // create the fast parser, register the XML namespaces, set document handler
435 FastParser aParser;
436 aParser.registerNamespace( NMSP_packageRel );
437 aParser.registerNamespace( NMSP_officeRel );
438 aParser.registerNamespace( NMSP_packageContentTypes );
439
440 OUString aFileName;
441 aMediaDescriptor[utl::MediaDescriptor::PROP_URL] >>= aFileName;
442
443 aParser.setDocumentHandler( new FilterDetectDocHandler( mxContext, aFilterName, aFileName ) );
444
445 /* Parse '_rels/.rels' to get the target path and '[Content_Types].xml'
446 to determine the content type of the part at the target path. */
447 aParser.parseStream( aZipStorage, "_rels/.rels" );
448 aParser.parseStream( aZipStorage, "[Content_Types].xml" );
449 }
450 }
451 catch( const Exception& )
452 {
453 if ( aMediaDescriptor.getUnpackedValueOrDefault( MediaDescriptor::PROP_ABORTED, false ) )
454 /* The user chose to abort detection, e.g. by hitting 'Cancel' in the password input dialog,
455 so we have to return non-empty type name to abort the detection loop. The loading code is
456 supposed to check whether the "Aborted" flag is present in the descriptor, and to not attempt
457 to actually load the file then.
458
459 The returned type name is the one we got as an input, which typically was detected by the flat
460 detection (i.e. by file extension), so normally that's the correct one. Also at this point we
461 already know that the file is OLE encrypted package, so trying with other type detectors doesn't
462 make much sense anyway.
463 */
464 aFilterName = aMediaDescriptor.getUnpackedValueOrDefault( MediaDescriptor::PROP_TYPENAME, OUString() );
465 }
466
467 // write back changed media descriptor members
468 aMediaDescriptor >> rMediaDescSeq;
469 return aFilterName;
470}
471
472} // namespace oox::core
473
474extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
476 uno::Sequence<uno::Any> const& /*rSeq*/)
477{
478 return cppu::acquire(new oox::core::FilterDetect(pCtx));
479}
480
481/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
virtual OUString SAL_CALL getImplementationName() override
virtual OUString SAL_CALL detect(css::uno::Sequence< css::beans::PropertyValue > &lDescriptor) override
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
Provides access to attribute values of an element.
OUString getStringDefaulted(sal_Int32 nAttrToken) const
Returns the string value of the specified attribute, returns an empty string if attribute not present...
bool isStorage() const
Returns true, if the object represents a valid storage.
Definition: storagebase.cxx:90
Implements stream access for ZIP storages containing XML streams.
Definition: zipstorage.hxx:42
Wrapper for a fast SAX parser that works on automatically generated OOXML token and namespace identif...
Definition: fastparser.hxx:54
void parseStream(const css::xml::sax::InputSource &rInputSource, bool bCloseStream=false)
Parses the passed SAX input source.
void registerNamespace(sal_Int32 nNamespaceId)
Registers an OOXML namespace at the parser.
Definition: fastparser.cxx:81
void setDocumentHandler(const css::uno::Reference< css::xml::sax::XFastDocumentHandler > &rxDocHandler)
Sets the passed document handler that will receive the SAX parser events.
Definition: fastparser.cxx:101
Document handler specifically designed for detecting OOXML file formats.
OUString getFilterNameFromContentType(std::u16string_view rContentType, std::u16string_view rFileName)
virtual css::uno::Reference< XFastContextHandler > SAL_CALL createUnknownChildContext(const OUString &Namespace, const OUString &Name, const css::uno::Reference< css::xml::sax::XFastAttributeList > &Attribs) override
virtual void SAL_CALL endUnknownElement(const OUString &Namespace, const OUString &Name) override
void parseRelationship(const AttributeList &rAttribs)
void parseContentTypesOverride(const AttributeList &rAttribs)
virtual void SAL_CALL startUnknownElement(const OUString &Namespace, const OUString &Name, const css::uno::Reference< css::xml::sax::XFastAttributeList > &Attribs) override
virtual css::uno::Reference< XFastContextHandler > SAL_CALL createFastChildContext(sal_Int32 Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > &Attribs) override
virtual void SAL_CALL endDocument() override
virtual void SAL_CALL setDocumentLocator(const css::uno::Reference< css::xml::sax::XLocator > &xLocator) override
virtual void SAL_CALL processingInstruction(const OUString &rTarget, const OUString &rData) override
css::uno::Reference< css::uno::XComponentContext > mxContext
virtual void SAL_CALL startFastElement(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &Attribs) override
void parseContentTypesDefault(const AttributeList &rAttribs)
FilterDetectDocHandler(const css::uno::Reference< css::uno::XComponentContext > &rxContext, OUString &rFilter, OUString aFileName)
virtual void SAL_CALL characters(const OUString &aChars) override
virtual ~FilterDetectDocHandler() override
virtual void SAL_CALL startDocument() override
virtual void SAL_CALL endFastElement(sal_Int32 Element) override
virtual ~FilterDetect() override
FilterDetect(const css::uno::Reference< css::uno::XComponentContext > &rxContext)
bool decrypt(const css::uno::Reference< css::io::XStream > &xDocumentStream)
Implements stream access for binary OLE storages.
Definition: olestorage.hxx:44
void setComponentDataEntry(const OUString &rName, const css::uno::Any &rValue)
css::uno::Sequence< css::beans::NamedValue > requestAndVerifyDocPassword(comphelper::IDocPasswordVerifier &rVerifier, comphelper::DocPasswordRequestType eRequestType, const ::std::vector< OUString > *pDefaultPasswords)
static constexpr OUStringLiteral PROP_URL
css::uno::Any getComponentDataEntry(const OUString &rName) const
uno::Reference< uno::XComponentContext > mxContext
float u
Reference< XSingleServiceFactory > xFactory
crypto::DocumentDecryption & mDecryptor
SAL_DLLPUBLIC_EXPORT uno::XInterface * com_sun_star_comp_oox_FormatDetector_get_implementation(uno::XComponentContext *pCtx, uno::Sequence< uno::Any > const &)
@ Exception
DocPasswordVerifierResult
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
bool endsWithIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2, std::u16string_view *rest=nullptr)
Relationship
unsigned char sal_Bool