Line data Source code
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/filterdetect.hxx"
21 :
22 : #include <com/sun/star/io/TempFile.hpp>
23 : #include <com/sun/star/io/XStream.hpp>
24 : #include <comphelper/docpasswordhelper.hxx>
25 : #include <unotools/mediadescriptor.hxx>
26 : #include <cppuhelper/supportsservice.hxx>
27 :
28 : #include "oox/core/fastparser.hxx"
29 : #include "oox/helper/attributelist.hxx"
30 : #include "oox/helper/zipstorage.hxx"
31 : #include "oox/ole/olestorage.hxx"
32 :
33 : #include "oox/crypto/DocumentDecryption.hxx"
34 :
35 : #include <com/sun/star/uri/UriReferenceFactory.hpp>
36 :
37 : #include <services.hxx>
38 :
39 : namespace oox {
40 : namespace core {
41 :
42 : using namespace ::com::sun::star::beans;
43 : using namespace ::com::sun::star::io;
44 : using namespace ::com::sun::star::lang;
45 : using namespace ::com::sun::star::uno;
46 : using namespace ::com::sun::star::xml::sax;
47 : using namespace ::com::sun::star::uri;
48 :
49 : using utl::MediaDescriptor;
50 : using comphelper::SequenceAsHashMap;
51 : using comphelper::IDocPasswordVerifier;
52 : using comphelper::DocPasswordVerifierResult;
53 :
54 1791 : FilterDetectDocHandler::FilterDetectDocHandler( const Reference< XComponentContext >& rxContext, OUString& rFilterName ) :
55 : mrFilterName( rFilterName ),
56 1791 : mxContext( rxContext )
57 : {
58 1791 : maContextStack.reserve( 2 );
59 1791 : }
60 :
61 3582 : FilterDetectDocHandler::~FilterDetectDocHandler()
62 : {
63 3582 : }
64 :
65 3150 : void SAL_CALL FilterDetectDocHandler::startDocument()
66 : throw (SAXException, RuntimeException, std::exception)
67 : {
68 3150 : }
69 :
70 3150 : void SAL_CALL FilterDetectDocHandler::endDocument()
71 : throw (SAXException, RuntimeException, std::exception)
72 : {
73 3150 : }
74 :
75 3150 : void SAL_CALL FilterDetectDocHandler::setDocumentLocator( const Reference<XLocator>& /*xLocator*/ )
76 : throw (SAXException, RuntimeException, std::exception)
77 : {
78 3150 : }
79 :
80 31612 : void SAL_CALL FilterDetectDocHandler::startFastElement(
81 : sal_Int32 nElement, const Reference< XFastAttributeList >& rAttribs )
82 : throw (SAXException,RuntimeException, std::exception)
83 : {
84 31612 : AttributeList aAttribs( rAttribs );
85 31612 : switch ( nElement )
86 : {
87 : // cases for _rels/.rels
88 : case PR_TOKEN( Relationships ):
89 1575 : break;
90 : case PR_TOKEN( Relationship ):
91 5257 : if( !maContextStack.empty() && (maContextStack.back() == PR_TOKEN( Relationships )) )
92 5257 : parseRelationship( aAttribs );
93 5257 : break;
94 :
95 : // cases for [Content_Types].xml
96 : case PC_TOKEN( Types ):
97 1575 : break;
98 : case PC_TOKEN( Default ):
99 2298 : if( !maContextStack.empty() && (maContextStack.back() == PC_TOKEN( Types )) )
100 2298 : parseContentTypesDefault( aAttribs );
101 2298 : break;
102 : case PC_TOKEN( Override ):
103 20907 : if( !maContextStack.empty() && (maContextStack.back() == PC_TOKEN( Types )) )
104 20907 : parseContentTypesOverride( aAttribs );
105 20907 : break;
106 : }
107 31612 : maContextStack.push_back( nElement );
108 31612 : }
109 :
110 0 : void SAL_CALL FilterDetectDocHandler::startUnknownElement(
111 : const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/ )
112 : throw (SAXException, RuntimeException, std::exception)
113 : {
114 0 : }
115 :
116 31612 : void SAL_CALL FilterDetectDocHandler::endFastElement( sal_Int32 /*nElement*/ )
117 : throw (SAXException, RuntimeException, std::exception)
118 : {
119 31612 : maContextStack.pop_back();
120 31612 : }
121 :
122 0 : void SAL_CALL FilterDetectDocHandler::endUnknownElement(
123 : const OUString& /*Namespace*/, const OUString& /*Name*/ ) throw (SAXException, RuntimeException, std::exception)
124 : {
125 0 : }
126 :
127 31612 : Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createFastChildContext(
128 : sal_Int32 /*Element*/, const Reference<XFastAttributeList>& /*Attribs*/ )
129 : throw (SAXException, RuntimeException, std::exception)
130 : {
131 31612 : return this;
132 : }
133 :
134 0 : Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createUnknownChildContext(
135 : const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/)
136 : throw (SAXException, RuntimeException, std::exception)
137 : {
138 0 : return this;
139 : }
140 :
141 1296 : void SAL_CALL FilterDetectDocHandler::characters( const OUString& /*aChars*/ )
142 : throw (SAXException, RuntimeException, std::exception)
143 : {
144 1296 : }
145 :
146 5257 : void FilterDetectDocHandler::parseRelationship( const AttributeList& rAttribs )
147 : {
148 5257 : OUString aType = rAttribs.getString( XML_Type, OUString() );
149 10514 : if ( aType == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" // OOXML Transitional
150 5257 : || aType == "http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument" ) //OOXML strict
151 : {
152 1575 : Reference<XUriReferenceFactory> xFactory = UriReferenceFactory::create( mxContext );
153 : try
154 : {
155 : // use '/' to representent the root of the zip package ( and provide a 'file' scheme to
156 : // keep the XUriReference implementation happy )
157 1575 : Reference< XUriReference > xBase = xFactory->parse( OUString("file:///") );
158 :
159 3150 : Reference< XUriReference > xPart = xFactory->parse( rAttribs.getString( XML_Target, OUString() ) );
160 3150 : Reference< XUriReference > xAbs = xFactory->makeAbsolute( xBase, xPart, sal_True, RelativeUriExcessParentSegments_RETAIN );
161 :
162 1575 : if ( xAbs.is() )
163 3150 : maTargetPath = xAbs->getPath();
164 : }
165 0 : catch( const Exception& )
166 : {
167 1575 : }
168 5257 : }
169 5257 : }
170 :
171 2553 : OUString FilterDetectDocHandler::getFilterNameFromContentType( const OUString& rContentType )
172 : {
173 3606 : if( rContentType == "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" ||
174 1053 : rContentType == "application/vnd.ms-word.document.macroEnabled.main+xml" )
175 1500 : return OUString( "writer_MS_Word_2007" );
176 :
177 2105 : if( rContentType == "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml" ||
178 1052 : rContentType == "application/vnd.ms-word.template.macroEnabledTemplate.main+xml" )
179 1 : return OUString( "writer_MS_Word_2007_Template" );
180 :
181 2036 : if( rContentType == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" ||
182 984 : rContentType == "application/vnd.ms-excel.sheet.macroEnabled.main+xml" )
183 68 : return OUString( "MS Excel 2007 XML" );
184 :
185 1968 : if( rContentType == "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml" ||
186 984 : rContentType == "application/vnd.ms-excel.template.macroEnabled.main+xml" )
187 0 : return OUString( "MS Excel 2007 XML Template" );
188 :
189 984 : if ( rContentType == "application/vnd.ms-excel.sheet.binary.macroEnabled.main" )
190 0 : return OUString( "MS Excel 2007 Binary" );
191 :
192 1962 : if( rContentType == "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml" ||
193 978 : rContentType == "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml" )
194 6 : return OUString( "MS PowerPoint 2007 XML" );
195 :
196 1956 : if( rContentType == "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml" ||
197 978 : rContentType == "application/vnd.ms-powerpoint.slideshow.macroEnabled.main+xml" )
198 0 : return OUString( "MS PowerPoint 2007 XML AutoPlay" );
199 :
200 1956 : if( rContentType == "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml" ||
201 978 : rContentType == "application/vnd.ms-powerpoint.template.macroEnabled.main+xml" )
202 0 : return OUString( "MS PowerPoint 2007 XML Template" );
203 :
204 978 : return OUString();
205 : }
206 :
207 2298 : void FilterDetectDocHandler::parseContentTypesDefault( const AttributeList& rAttribs )
208 : {
209 : // only if no overridden part name found
210 2298 : if( mrFilterName.isEmpty() )
211 : {
212 : // check if target path ends with extension
213 2273 : OUString aExtension = rAttribs.getString( XML_Extension, OUString() );
214 2273 : sal_Int32 nExtPos = maTargetPath.getLength() - aExtension.getLength();
215 2273 : if( (nExtPos > 0) && (maTargetPath[ nExtPos - 1 ] == '.') && maTargetPath.match( aExtension, nExtPos ) )
216 978 : mrFilterName = getFilterNameFromContentType( rAttribs.getString( XML_ContentType, OUString() ) );
217 : }
218 2298 : }
219 :
220 20907 : void FilterDetectDocHandler::parseContentTypesOverride( const AttributeList& rAttribs )
221 : {
222 20907 : if( rAttribs.getString( XML_PartName, OUString() ).equals( maTargetPath ) )
223 1575 : mrFilterName = getFilterNameFromContentType( rAttribs.getString( XML_ContentType, OUString() ) );
224 20907 : }
225 :
226 : /* Helper for XServiceInfo */
227 25 : Sequence< OUString > FilterDetect_getSupportedServiceNames()
228 : {
229 25 : Sequence< OUString > aServiceNames( 1 );
230 25 : aServiceNames[ 0 ] = "com.sun.star.frame.ExtendedTypeDetection";
231 25 : return aServiceNames;
232 : }
233 :
234 : /* Helper for XServiceInfo */
235 90 : OUString FilterDetect_getImplementationName()
236 : {
237 90 : return OUString( "com.sun.star.comp.oox.FormatDetector" );
238 : }
239 :
240 : /* Helper for registry */
241 3479 : Reference< XInterface > SAL_CALL FilterDetect_createInstance( const Reference< XComponentContext >& rxContext ) throw( Exception )
242 : {
243 3479 : return static_cast< ::cppu::OWeakObject* >( new FilterDetect( rxContext ) );
244 : }
245 :
246 7162 : FilterDetect::FilterDetect( const Reference< XComponentContext >& rxContext ) throw( RuntimeException ) :
247 7162 : mxContext( rxContext, UNO_SET_THROW )
248 : {
249 7162 : }
250 :
251 10641 : FilterDetect::~FilterDetect()
252 : {
253 10641 : }
254 :
255 : namespace
256 : {
257 :
258 7162 : bool lclIsZipPackage( const Reference< XComponentContext >& rxContext, const Reference< XInputStream >& rxInStrm )
259 : {
260 7162 : ZipStorage aZipStorage( rxContext, rxInStrm );
261 7162 : return aZipStorage.isStorage();
262 : }
263 :
264 0 : class PasswordVerifier : public IDocPasswordVerifier
265 : {
266 : public:
267 : explicit PasswordVerifier( DocumentDecryption& aDecryptor );
268 :
269 : virtual DocPasswordVerifierResult verifyPassword( const OUString& rPassword, Sequence<NamedValue>& rEncryptionData ) SAL_OVERRIDE;
270 :
271 : virtual DocPasswordVerifierResult verifyEncryptionData( const Sequence<NamedValue>& rEncryptionData ) SAL_OVERRIDE;
272 : private:
273 : DocumentDecryption& mDecryptor;
274 : };
275 :
276 0 : PasswordVerifier::PasswordVerifier( DocumentDecryption& aDecryptor ) :
277 0 : mDecryptor(aDecryptor)
278 0 : {}
279 :
280 0 : comphelper::DocPasswordVerifierResult PasswordVerifier::verifyPassword( const OUString& rPassword, Sequence<NamedValue>& rEncryptionData )
281 : {
282 0 : if(mDecryptor.generateEncryptionKey(rPassword))
283 0 : rEncryptionData = mDecryptor.createEncryptionData(rPassword);
284 :
285 0 : return rEncryptionData.hasElements() ? comphelper::DocPasswordVerifierResult_OK : comphelper::DocPasswordVerifierResult_WRONG_PASSWORD;
286 : }
287 :
288 0 : comphelper::DocPasswordVerifierResult PasswordVerifier::verifyEncryptionData( const Sequence<NamedValue>& )
289 : {
290 0 : return comphelper::DocPasswordVerifierResult_WRONG_PASSWORD;
291 : }
292 :
293 : } // namespace
294 :
295 7162 : Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescriptor& rMediaDescriptor ) const
296 : {
297 : // try the plain input stream
298 7162 : Reference<XInputStream> xInputStream( rMediaDescriptor[ MediaDescriptor::PROP_INPUTSTREAM() ], UNO_QUERY );
299 7162 : if( !xInputStream.is() || lclIsZipPackage( mxContext, xInputStream ) )
300 5473 : return xInputStream;
301 :
302 : // check if a temporary file is passed in the 'ComponentData' property
303 3378 : Reference<XStream> xDecrypted( rMediaDescriptor.getComponentDataEntry( "DecryptedPackage" ), UNO_QUERY );
304 1689 : if( xDecrypted.is() )
305 : {
306 0 : Reference<XInputStream> xDecryptedInputStream = xDecrypted->getInputStream();
307 0 : if( lclIsZipPackage( mxContext, xDecryptedInputStream ) )
308 0 : return xDecryptedInputStream;
309 : }
310 :
311 : // try to decrypt an encrypted OLE package
312 3378 : oox::ole::OleStorage aOleStorage( mxContext, xInputStream, false );
313 1689 : if( aOleStorage.isStorage() )
314 : {
315 : try
316 : {
317 15 : DocumentDecryption aDecryptor(aOleStorage, mxContext);
318 :
319 15 : if( aDecryptor.readEncryptionInfo() )
320 : {
321 : /* "VelvetSweatshop" is the built-in default encryption
322 : password used by MS Excel for the "workbook protection"
323 : feature with password. Try this first before prompting the
324 : user for a password. */
325 0 : std::vector<OUString> aDefaultPasswords;
326 0 : aDefaultPasswords.push_back("VelvetSweatshop");
327 :
328 : /* Use the comphelper password helper to request a password.
329 : This helper returns either with the correct password
330 : (according to the verifier), or with an empty string if
331 : user has cancelled the password input dialog. */
332 0 : PasswordVerifier aVerifier( aDecryptor );
333 0 : Sequence<NamedValue> aEncryptionData;
334 0 : aEncryptionData = rMediaDescriptor.requestAndVerifyDocPassword(
335 : aVerifier,
336 : comphelper::DocPasswordRequestType_MS,
337 0 : &aDefaultPasswords );
338 :
339 0 : if( aEncryptionData.getLength() == 0 )
340 : {
341 0 : rMediaDescriptor[ MediaDescriptor::PROP_ABORTED() ] <<= true;
342 : }
343 : else
344 : {
345 : // create temporary file for unencrypted package
346 0 : Reference<XStream> xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW );
347 0 : aDecryptor.decrypt( xTempFile );
348 :
349 : // store temp file in media descriptor to keep it alive
350 0 : rMediaDescriptor.setComponentDataEntry( "DecryptedPackage", Any( xTempFile ) );
351 :
352 0 : Reference<XInputStream> xDecryptedInputStream = xTempFile->getInputStream();
353 0 : if( lclIsZipPackage( mxContext, xDecryptedInputStream ) )
354 0 : return xDecryptedInputStream;
355 0 : }
356 15 : }
357 : }
358 15 : catch( const Exception& )
359 : {
360 : }
361 : }
362 8851 : return Reference<XInputStream>();
363 : }
364 :
365 : // com.sun.star.lang.XServiceInfo interface -----------------------------------
366 :
367 0 : OUString SAL_CALL FilterDetect::getImplementationName() throw( RuntimeException, std::exception )
368 : {
369 0 : return FilterDetect_getImplementationName();
370 : }
371 :
372 0 : sal_Bool SAL_CALL FilterDetect::supportsService( const OUString& rServiceName ) throw( RuntimeException, std::exception )
373 : {
374 0 : return cppu::supportsService(this, rServiceName);
375 : }
376 :
377 0 : Sequence< OUString > SAL_CALL FilterDetect::getSupportedServiceNames() throw( RuntimeException, std::exception )
378 : {
379 0 : return FilterDetect_getSupportedServiceNames();
380 : }
381 :
382 : // com.sun.star.document.XExtendedFilterDetection interface -------------------
383 :
384 3479 : OUString SAL_CALL FilterDetect::detect( Sequence< PropertyValue >& rMediaDescSeq ) throw( RuntimeException, std::exception )
385 : {
386 3479 : OUString aFilterName;
387 6958 : MediaDescriptor aMediaDescriptor( rMediaDescSeq );
388 :
389 : /* Check that the user has not chosen to abort detection, e.g. by hitting
390 : 'Cancel' in the password input dialog. This may happen because this
391 : filter detection is used by different filters. */
392 3479 : bool bAborted = aMediaDescriptor.getUnpackedValueOrDefault( MediaDescriptor::PROP_ABORTED(), false );
393 3479 : if( !bAborted ) try
394 : {
395 3479 : aMediaDescriptor.addInputStream();
396 :
397 : /* Get the unencrypted input stream. This may include creation of a
398 : temporary file that contains the decrypted package. This temporary
399 : file will be stored in the 'ComponentData' property of the media
400 : descriptor. */
401 5167 : Reference< XInputStream > xInputStream( extractUnencryptedPackage( aMediaDescriptor ), UNO_SET_THROW );
402 :
403 : // stream must be a ZIP package
404 3582 : ZipStorage aZipStorage( mxContext, xInputStream );
405 1791 : if( aZipStorage.isStorage() )
406 : {
407 : // create the fast parser, register the XML namespaces, set document handler
408 1791 : FastParser aParser( mxContext );
409 1791 : aParser.registerNamespace( NMSP_packageRel );
410 1791 : aParser.registerNamespace( NMSP_officeRel );
411 1791 : aParser.registerNamespace( NMSP_packageContentTypes );
412 1791 : aParser.setDocumentHandler( new FilterDetectDocHandler( mxContext, aFilterName ) );
413 :
414 : /* Parse '_rels/.rels' to get the target path and '[Content_Types].xml'
415 : to determine the content type of the part at the target path. */
416 2007 : aParser.parseStream( aZipStorage, "_rels/.rels" );
417 1575 : aParser.parseStream( aZipStorage, "[Content_Types].xml" );
418 1791 : }
419 : }
420 1904 : catch( const Exception& )
421 : {
422 : }
423 :
424 : // write back changed media descriptor members
425 3479 : aMediaDescriptor >> rMediaDescSeq;
426 6958 : return aFilterName;
427 : }
428 :
429 : } // namespace core
430 246 : } // namespace oox
431 :
432 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|