xref: /trunk/main/oox/source/core/filterdetect.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "oox/core/filterdetect.hxx"
25 #include "oox/core/encryption.hxx"
26 
27 #include <com/sun/star/io/XStream.hpp>
28 #include <com/sun/star/logging/LogLevel.hpp>
29 #include <comphelper/docpasswordhelper.hxx>
30 #include <comphelper/mediadescriptor.hxx>
31 #include "oox/core/fastparser.hxx"
32 #include "oox/core/relationshandler.hxx"
33 #include "oox/helper/attributelist.hxx"
34 #include "oox/helper/binaryinputstream.hxx"
35 #include "oox/helper/binaryoutputstream.hxx"
36 #include "oox/helper/zipstorage.hxx"
37 #include "oox/ole/olestorage.hxx"
38 
39 namespace oox {
40 namespace core {
41 
42 // ============================================================================
43 
44 using namespace ::com::sun::star::beans;
45 using namespace ::com::sun::star::io;
46 using namespace ::com::sun::star::lang;
47 using namespace ::com::sun::star::logging;
48 using namespace ::com::sun::star::uno;
49 using namespace ::com::sun::star::xml::sax;
50 
51 using ::comphelper::MediaDescriptor;
52 using ::comphelper::SequenceAsHashMap;
53 using ::rtl::OUString;
54 
55 // ============================================================================
56 
FilterDetectDocHandler(OUString & rFilterName)57 FilterDetectDocHandler::FilterDetectDocHandler( OUString& rFilterName ) :
58     mrFilterName( rFilterName )
59 {
60     maContextStack.reserve( 2 );
61 }
62 
~FilterDetectDocHandler()63 FilterDetectDocHandler::~FilterDetectDocHandler()
64 {
65 }
66 
startDocument()67 void SAL_CALL FilterDetectDocHandler::startDocument()
68 {
69 }
70 
endDocument()71 void SAL_CALL FilterDetectDocHandler::endDocument()
72 {
73 }
74 
setDocumentLocator(const Reference<XLocator> &)75 void SAL_CALL FilterDetectDocHandler::setDocumentLocator( const Reference<XLocator>& /*xLocator*/ )
76 {
77 }
78 
startFastElement(sal_Int32 nElement,const Reference<XFastAttributeList> & rAttribs)79 void SAL_CALL FilterDetectDocHandler::startFastElement(
80         sal_Int32 nElement, const Reference< XFastAttributeList >& rAttribs )
81 {
82     AttributeList aAttribs( rAttribs );
83     switch ( nElement )
84     {
85         // cases for _rels/.rels
86         case PR_TOKEN( Relationships ):
87         break;
88         case PR_TOKEN( Relationship ):
89             if( !maContextStack.empty() && (maContextStack.back() == PR_TOKEN( Relationships )) )
90                 parseRelationship( aAttribs );
91         break;
92 
93         // cases for [Content_Types].xml
94         case PC_TOKEN( Types ):
95         break;
96         case PC_TOKEN( Default ):
97             if( !maContextStack.empty() && (maContextStack.back() == PC_TOKEN( Types )) )
98                 parseContentTypesDefault( aAttribs );
99         break;
100         case PC_TOKEN( Override ):
101             if( !maContextStack.empty() && (maContextStack.back() == PC_TOKEN( Types )) )
102                 parseContentTypesOverride( aAttribs );
103         break;
104     }
105     maContextStack.push_back( nElement );
106 }
107 
startUnknownElement(const OUString &,const OUString &,const Reference<XFastAttributeList> &)108 void SAL_CALL FilterDetectDocHandler::startUnknownElement(
109     const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/ )
110 {
111 }
112 
endFastElement(sal_Int32)113 void SAL_CALL FilterDetectDocHandler::endFastElement( sal_Int32 /*nElement*/ )
114 {
115     maContextStack.pop_back();
116 }
117 
endUnknownElement(const OUString &,const OUString &)118 void SAL_CALL FilterDetectDocHandler::endUnknownElement(
119     const OUString& /*Namespace*/, const OUString& /*Name*/ )
120 {
121 }
122 
createFastChildContext(sal_Int32,const Reference<XFastAttributeList> &)123 Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createFastChildContext(
124     sal_Int32 /*Element*/, const Reference<XFastAttributeList>& /*Attribs*/ )
125 {
126     return this;
127 }
128 
createUnknownChildContext(const OUString &,const OUString &,const Reference<XFastAttributeList> &)129 Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createUnknownChildContext(
130     const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/)
131 {
132     return this;
133 }
134 
characters(const OUString &)135 void SAL_CALL FilterDetectDocHandler::characters( const OUString& /*aChars*/ )
136 {
137 }
138 
ignorableWhitespace(const OUString &)139 void SAL_CALL FilterDetectDocHandler::ignorableWhitespace( const OUString& /*aWhitespaces*/ )
140 {
141 }
142 
processingInstruction(const OUString &,const OUString &)143 void SAL_CALL FilterDetectDocHandler::processingInstruction(
144     const OUString& /*aTarget*/, const OUString& /*aData*/ )
145 {
146 }
147 
parseRelationship(const AttributeList & rAttribs)148 void FilterDetectDocHandler::parseRelationship( const AttributeList& rAttribs )
149 {
150     OUString aType = rAttribs.getString( XML_Type, OUString() );
151     if( aType.equalsAscii( "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" ) )
152         maTargetPath = RelationsFragment::removeDuplicateSlashes( OUString( sal_Unicode( '/' ) ) + rAttribs.getString( XML_Target, OUString() ) );
153 }
154 
getFilterNameFromContentType(const OUString & rContentType) const155 OUString FilterDetectDocHandler::getFilterNameFromContentType( const OUString& rContentType ) const
156 {
157     if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" ) ||
158         rContentType.equalsAscii( "application/vnd.ms-word.document.macroEnabled.main+xml" ) )
159         return CREATE_OUSTRING( "writer_MS_Word_2007" );
160 
161     if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml" ) ||
162         rContentType.equalsAscii( "application/vnd.ms-word.template.macroEnabledTemplate.main+xml" ) )
163         return CREATE_OUSTRING( "writer_MS_Word_2007_Template" );
164 
165     if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" ) ||
166         rContentType.equalsAscii( "application/vnd.ms-excel.sheet.macroEnabled.main+xml" ) )
167         return CREATE_OUSTRING( "MS Excel 2007 XML" );
168 
169     if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml" ) ||
170         rContentType.equalsAscii( "application/vnd.ms-excel.template.macroEnabled.main+xml" ) )
171         return CREATE_OUSTRING( "MS Excel 2007 XML Template" );
172 
173     if( rContentType.equalsAscii( "application/vnd.ms-excel.sheet.binary.macroEnabled.main" ) )
174         return CREATE_OUSTRING( "MS Excel 2007 Binary" );
175 
176     if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml" ) ||
177         rContentType.equalsAscii( "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml" ) )
178         return CREATE_OUSTRING( "MS PowerPoint 2007 XML" );
179 
180     if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml" ) ||
181         rContentType.equalsAscii( "application/vnd.ms-powerpoint.template.macroEnabled.main+xml" ) )
182         return CREATE_OUSTRING( "MS PowerPoint 2007 XML Template" );
183 
184     return OUString();
185 }
186 
parseContentTypesDefault(const AttributeList & rAttribs)187 void FilterDetectDocHandler::parseContentTypesDefault( const AttributeList& rAttribs )
188 {
189     // only if no overridden part name found
190     if( mrFilterName.getLength() == 0 )
191     {
192         // check if target path ends with extension
193         OUString aExtension = rAttribs.getString( XML_Extension, OUString() );
194         sal_Int32 nExtPos = maTargetPath.getLength() - aExtension.getLength();
195         if( (nExtPos > 0) && (maTargetPath[ nExtPos - 1 ] == '.') && maTargetPath.match( aExtension, nExtPos ) )
196             mrFilterName = getFilterNameFromContentType( rAttribs.getString( XML_ContentType, OUString() ) );
197     }
198 }
199 
parseContentTypesOverride(const AttributeList & rAttribs)200 void FilterDetectDocHandler::parseContentTypesOverride( const AttributeList& rAttribs )
201 {
202     if( rAttribs.getString( XML_PartName, OUString() ).equals( maTargetPath ) )
203         mrFilterName = getFilterNameFromContentType( rAttribs.getString( XML_ContentType, OUString() ) );
204 }
205 
206 // ============================================================================
207 
208 /* Helper for XServiceInfo */
FilterDetect_getSupportedServiceNames()209 Sequence< OUString > FilterDetect_getSupportedServiceNames()
210 {
211     Sequence< OUString > aServiceNames( 1 );
212     aServiceNames[ 0 ] = CREATE_OUSTRING( "com.sun.star.frame.ExtendedTypeDetection" );
213     return aServiceNames;
214 }
215 
216 /* Helper for XServiceInfo */
FilterDetect_getImplementationName()217 OUString FilterDetect_getImplementationName()
218 {
219     return CREATE_OUSTRING( "com.sun.star.comp.oox.FormatDetector" );
220 }
221 
222 /* Helper for registry */
FilterDetect_createInstance(const Reference<XComponentContext> & rxContext)223 Reference< XInterface > SAL_CALL FilterDetect_createInstance( const Reference< XComponentContext >& rxContext )
224 {
225     return static_cast< ::cppu::OWeakObject* >( new FilterDetect( rxContext ) );
226 }
227 
228 // ----------------------------------------------------------------------------
229 
FilterDetect(const Reference<XComponentContext> & rxContext)230 FilterDetect::FilterDetect( const Reference< XComponentContext >& rxContext ) :
231     mxContext( rxContext, UNO_SET_THROW ),
232     logger( rxContext )
233 {
234 }
235 
~FilterDetect()236 FilterDetect::~FilterDetect()
237 {
238 }
239 
240 namespace {
241 
242 // ----------------------------------------------------------------------------
243 
lclIsZipPackage(const Reference<XComponentContext> & rxContext,const Reference<XInputStream> & rxInStrm)244 bool lclIsZipPackage( const Reference< XComponentContext >& rxContext, const Reference< XInputStream >& rxInStrm )
245 {
246     ZipStorage aZipStorage( rxContext, rxInStrm );
247     return aZipStorage.isStorage();
248 }
249 
250 // the password verifier ------------------------------------------------------
251 
252 class PasswordVerifier : public ::comphelper::IDocPasswordVerifier
253 {
254 public:
255     explicit            PasswordVerifier( const ::boost::shared_ptr< EncryptionInfo >& rEncryptInfo, const ::comphelper::EventLogger& rLogger );
256 
257     virtual ::comphelper::DocPasswordVerifierResult
258                         verifyPassword( const OUString& rPassword, Sequence< NamedValue >& o_rEncryptionData );
259     virtual ::comphelper::DocPasswordVerifierResult
260                         verifyEncryptionData( const Sequence< NamedValue >& rEncryptionData );
261 
262 private:
263     const ::boost::shared_ptr< EncryptionInfo> encryptionInfo;
264     const ::comphelper::EventLogger            logger;
265 };
266 
PasswordVerifier(const::boost::shared_ptr<EncryptionInfo> & rEncryptInfo,const::comphelper::EventLogger & rLogger)267 PasswordVerifier::PasswordVerifier( const ::boost::shared_ptr< EncryptionInfo>& rEncryptInfo, const ::comphelper::EventLogger& rLogger ) :
268     encryptionInfo( rEncryptInfo ),
269     logger( rLogger )
270 {
271 }
272 
verifyPassword(const OUString & rPassword,Sequence<NamedValue> & o_rEncryptionData)273 ::comphelper::DocPasswordVerifierResult PasswordVerifier::verifyPassword( const OUString& rPassword, Sequence< NamedValue >& o_rEncryptionData )
274 {
275     try
276     {
277         o_rEncryptionData = encryptionInfo->verifyPassword( rPassword );
278         if( o_rEncryptionData.hasElements() )
279         {
280             logger.log( LogLevel::FINE, OUString::createFromAscii( "Password is correct" ) );
281             return ::comphelper::DocPasswordVerifierResult_OK;
282         }
283         else
284         {
285             logger.log( LogLevel::WARNING, OUString::createFromAscii( "Password is incorrect" ) );
286             return ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD;
287         }
288     }
289     catch ( const Exception &e )
290     {
291         logger.log( LogLevel::WARNING, "Error verifying password: $1$", e.Message );
292         return ::comphelper::DocPasswordVerifierResult_ABORT;
293     }
294 }
295 
verifyEncryptionData(const Sequence<NamedValue> & rEncryptionData)296 ::comphelper::DocPasswordVerifierResult PasswordVerifier::verifyEncryptionData( const Sequence< NamedValue >& rEncryptionData )
297 {
298     try
299     {
300         bool bResult = encryptionInfo->verifyEncryptionData( rEncryptionData );
301         if( bResult )
302         {
303             logger.log( LogLevel::FINE, OUString::createFromAscii( "EncryptionData is correct" ) );
304             return ::comphelper::DocPasswordVerifierResult_OK;
305         }
306         else
307         {
308             logger.log( LogLevel::WARNING, OUString::createFromAscii( "EncryptionData is incorrect" ) );
309             return ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD;
310         }
311     }
312     catch ( const Exception& e )
313     {
314         logger.log( LogLevel::WARNING, "Error verifying EncryptionData: $1$", e.Message );
315         return ::comphelper::DocPasswordVerifierResult_ABORT;
316     }
317 }
318 
319 } // namespace
320 
321 // ----------------------------------------------------------------------------
322 
extractUnencryptedPackage(MediaDescriptor & rMediaDesc) const323 Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescriptor& rMediaDesc ) const
324 {
325     // try the plain input stream
326     Reference< XInputStream > xInStrm( rMediaDesc[ MediaDescriptor::PROP_INPUTSTREAM() ], UNO_QUERY );
327     if( !xInStrm.is() || lclIsZipPackage( mxContext, xInStrm ) )
328         return xInStrm;
329 
330     // check if a temporary file is passed in the 'ComponentData' property
331     Reference< XStream > xDecrypted( rMediaDesc.getComponentDataEntry( CREATE_OUSTRING( "DecryptedPackage" ) ), UNO_QUERY );
332     if( xDecrypted.is() )
333     {
334         Reference< XInputStream > xDecrInStrm = xDecrypted->getInputStream();
335         if( lclIsZipPackage( mxContext, xDecrInStrm ) )
336             return xDecrInStrm;
337     }
338 
339     // try to decrypt an encrypted OLE package
340     ::oox::ole::OleStorage aOleStorage( mxContext, xInStrm, false );
341     if( aOleStorage.isStorage() ) try
342     {
343         // open the required input streams in the encrypted package
344         Reference< XInputStream > xEncryptionInfo( aOleStorage.openInputStream( CREATE_OUSTRING( "EncryptionInfo" ) ), UNO_SET_THROW );
345         Reference< XInputStream > xEncryptedPackage( aOleStorage.openInputStream( CREATE_OUSTRING( "EncryptedPackage" ) ), UNO_SET_THROW );
346 
347         // read the encryption info stream
348         ::boost::shared_ptr< EncryptionInfo > encryptionInfo( EncryptionInfo::readEncryptionInfo( mxContext, xEncryptionInfo ) );
349 
350         // check flags and algorithm IDs, required are AES128 and SHA-1
351         bool bImplemented = encryptionInfo->isImplemented();
352         if( bImplemented )
353         {
354             /*  "VelvetSweatshop" is the built-in default encryption
355                 password used by MS Excel for the "workbook protection"
356                 feature with password. Try this first before prompting the
357                 user for a password. */
358             ::std::vector< OUString > aDefaultPasswords;
359             aDefaultPasswords.push_back( CREATE_OUSTRING( "VelvetSweatshop" ) );
360 
361             /*  Use the comphelper password helper to request a password.
362                 This helper returns either with the correct password
363                 (according to the verifier), or with an empty string if
364                 user has cancelled the password input dialog. */
365             PasswordVerifier aVerifier( encryptionInfo, logger );
366             Sequence< NamedValue > aEncryptionData = ::comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
367                 aVerifier, rMediaDesc, ::comphelper::DocPasswordRequestType_MS, &aDefaultPasswords );
368 
369             if( aEncryptionData.getLength() == 0 )
370             {
371                 rMediaDesc[ MediaDescriptor::PROP_ABORTED() ] <<= true;
372             }
373             else
374             {
375                 // create temporary file for unencrypted package
376                 Reference< XMultiComponentFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
377                 Reference< XStream > xTempFile( xFactory->createInstanceWithContext( CREATE_OUSTRING( "com.sun.star.io.TempFile" ), mxContext ), UNO_QUERY_THROW );
378                 Reference< XOutputStream > xDecryptedPackage( xTempFile->getOutputStream(), UNO_SET_THROW );
379                 BinaryXOutputStream aDecryptedPackage( xDecryptedPackage, true );
380                 BinaryXInputStream aEncryptedPackage( xEncryptedPackage, true );
381 
382                 encryptionInfo->decryptStream( aEncryptedPackage, aDecryptedPackage );
383                 aDecryptedPackage.seekToStart();
384 
385                 // store temp file in media descriptor to keep it alive
386                 rMediaDesc.setComponentDataEntry( CREATE_OUSTRING( "DecryptedPackage" ), Any( xTempFile ) );
387 
388                 Reference< XInputStream > xDecrInStrm = xTempFile->getInputStream();
389                 if( lclIsZipPackage( mxContext, xDecrInStrm ) )
390                     return xDecrInStrm;
391             }
392         }
393         else
394             logger.log( LogLevel::WARNING, "Encryption type not implemented" );
395     }
396     catch( Exception& e )
397     {
398         logger.log( LogLevel::WARNING, "Error in ::oox::core::FilterDetect::extractUnencryptedPackage(): $1$", e.Message );
399     }
400 
401     return Reference< XInputStream >();
402 }
403 
404 // com.sun.star.lang.XServiceInfo interface -----------------------------------
405 
getImplementationName()406 OUString SAL_CALL FilterDetect::getImplementationName()
407 {
408     return FilterDetect_getImplementationName();
409 }
410 
supportsService(const OUString & rServiceName)411 sal_Bool SAL_CALL FilterDetect::supportsService( const OUString& rServiceName )
412 {
413     const Sequence< OUString > aServices = FilterDetect_getSupportedServiceNames();
414     const OUString* pArray = aServices.getConstArray();
415     const OUString* pArrayEnd = pArray + aServices.getLength();
416     return ::std::find( pArray, pArrayEnd, rServiceName ) != pArrayEnd;
417 }
418 
getSupportedServiceNames()419 Sequence< OUString > SAL_CALL FilterDetect::getSupportedServiceNames()
420 {
421     return FilterDetect_getSupportedServiceNames();
422 }
423 
424 // com.sun.star.document.XExtendedFilterDetection interface -------------------
425 
detect(Sequence<PropertyValue> & rMediaDescSeq)426 OUString SAL_CALL FilterDetect::detect( Sequence< PropertyValue >& rMediaDescSeq )
427 {
428     OUString aFilterName;
429     MediaDescriptor aMediaDesc( rMediaDescSeq );
430 
431     /*  Check that the user has not chosen to abort detection, e.g. by hitting
432         'Cancel' in the password input dialog. This may happen because this
433         filter detection is used by different filters. */
434     bool bAborted = aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_ABORTED(), false );
435     if( !bAborted ) try
436     {
437         aMediaDesc.addInputStream();
438 
439         /*  Get the unencrypted input stream. This may include creation of a
440             temporary file that contains the decrypted package. This temporary
441             file will be stored in the 'ComponentData' property of the media
442             descriptor. */
443         Reference< XInputStream > xInStrm( extractUnencryptedPackage( aMediaDesc ), UNO_SET_THROW );
444 
445         // stream must be a ZIP package
446         ZipStorage aZipStorage( mxContext, xInStrm );
447         if( aZipStorage.isStorage() )
448         {
449             // create the fast parser, register the XML namespaces, set document handler
450             FastParser aParser( mxContext );
451             aParser.registerNamespace( NMSP_packageRel );
452             aParser.registerNamespace( NMSP_officeRel );
453             aParser.registerNamespace( NMSP_packageContentTypes );
454             aParser.setDocumentHandler( new FilterDetectDocHandler( aFilterName ) );
455 
456             /*  Parse '_rels/.rels' to get the target path and '[Content_Types].xml'
457                 to determine the content type of the part at the target path. */
458             aParser.parseStream( aZipStorage, CREATE_OUSTRING( "_rels/.rels" ) );
459             aParser.parseStream( aZipStorage, CREATE_OUSTRING( "[Content_Types].xml" ) );
460         }
461     }
462     catch( Exception& e )
463     {
464         logger.log( LogLevel::WARNING, "Error in ::oox::core::FilterDetect::detect(): $1$", e.Message );
465     }
466 
467     // write back changed media descriptor members
468     aMediaDesc >> rMediaDescSeq;
469     return aFilterName;
470 }
471 
472 // ============================================================================
473 
474 } // namespace core
475 } // namespace oox
476