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