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