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