summaryrefslogtreecommitdiff
path: root/oox/source/core/filterdetect.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'oox/source/core/filterdetect.cxx')
-rw-r--r--oox/source/core/filterdetect.cxx657
1 files changed, 657 insertions, 0 deletions
diff --git a/oox/source/core/filterdetect.cxx b/oox/source/core/filterdetect.cxx
new file mode 100644
index 000000000000..00244c224778
--- /dev/null
+++ b/oox/source/core/filterdetect.cxx
@@ -0,0 +1,657 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include "oox/core/filterdetect.hxx"
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/xml/sax/XFastParser.hpp>
+#include <rtl/digest.h>
+#include <openssl/evp.h>
+#include <comphelper/docpasswordhelper.hxx>
+#include <comphelper/mediadescriptor.hxx>
+#include "oox/helper/attributelist.hxx"
+#include "oox/helper/binaryinputstream.hxx"
+#include "oox/helper/binaryoutputstream.hxx"
+#include "oox/helper/zipstorage.hxx"
+#include "oox/core/fasttokenhandler.hxx"
+#include "oox/core/namespaces.hxx"
+#include "oox/ole/olestorage.hxx"
+
+using ::rtl::OUString;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::RuntimeException;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::uno::XInterface;
+using ::com::sun::star::lang::XMultiServiceFactory;
+using ::com::sun::star::beans::NamedValue;
+using ::com::sun::star::beans::PropertyValue;
+using ::com::sun::star::io::XInputStream;
+using ::com::sun::star::io::XOutputStream;
+using ::com::sun::star::io::XStream;
+using ::com::sun::star::xml::sax::InputSource;
+using ::com::sun::star::xml::sax::SAXException;
+using ::com::sun::star::xml::sax::XFastAttributeList;
+using ::com::sun::star::xml::sax::XFastContextHandler;
+using ::com::sun::star::xml::sax::XFastParser;
+using ::com::sun::star::xml::sax::XLocator;
+using ::comphelper::MediaDescriptor;
+using ::comphelper::SequenceAsHashMap;
+
+namespace oox {
+namespace core {
+
+// ============================================================================
+
+FilterDetectDocHandler::FilterDetectDocHandler( OUString& rFilterName ) :
+ mrFilterName( rFilterName )
+{
+ maContextStack.reserve( 2 );
+}
+
+FilterDetectDocHandler::~FilterDetectDocHandler()
+{
+}
+
+void SAL_CALL FilterDetectDocHandler::startDocument()
+ throw (SAXException, RuntimeException)
+{
+}
+
+void SAL_CALL FilterDetectDocHandler::endDocument()
+ throw (SAXException, RuntimeException)
+{
+}
+
+void SAL_CALL FilterDetectDocHandler::setDocumentLocator( const Reference<XLocator>& /*xLocator*/ )
+ throw (SAXException, RuntimeException)
+{
+}
+
+void SAL_CALL FilterDetectDocHandler::startFastElement(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& rAttribs )
+ throw (SAXException,RuntimeException)
+{
+ AttributeList aAttribs( rAttribs );
+ switch ( nElement )
+ {
+ // cases for _rels/.rels
+ case NMSP_PACKAGE_RELATIONSHIPS|XML_Relationships:
+ break;
+ case NMSP_PACKAGE_RELATIONSHIPS|XML_Relationship:
+ if( !maContextStack.empty() && (maContextStack.back() == (NMSP_PACKAGE_RELATIONSHIPS|XML_Relationships)) )
+ parseRelationship( aAttribs );
+ break;
+
+ // cases for [Content_Types].xml
+ case NMSP_CONTENT_TYPES|XML_Types:
+ break;
+ case NMSP_CONTENT_TYPES|XML_Default:
+ if( !maContextStack.empty() && (maContextStack.back() == (NMSP_CONTENT_TYPES|XML_Types)) )
+ parseContentTypesDefault( aAttribs );
+ break;
+ case NMSP_CONTENT_TYPES|XML_Override:
+ if( !maContextStack.empty() && (maContextStack.back() == (NMSP_CONTENT_TYPES|XML_Types)) )
+ parseContentTypesOverride( aAttribs );
+ break;
+ }
+ maContextStack.push_back( nElement );
+}
+
+void SAL_CALL FilterDetectDocHandler::startUnknownElement(
+ const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/ )
+ throw (SAXException, RuntimeException)
+{
+}
+
+void SAL_CALL FilterDetectDocHandler::endFastElement( sal_Int32 /*nElement*/ )
+ throw (SAXException, RuntimeException)
+{
+ maContextStack.pop_back();
+}
+
+void SAL_CALL FilterDetectDocHandler::endUnknownElement(
+ const OUString& /*Namespace*/, const OUString& /*Name*/ ) throw (SAXException, RuntimeException)
+{
+}
+
+Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createFastChildContext(
+ sal_Int32 /*Element*/, const Reference<XFastAttributeList>& /*Attribs*/ )
+ throw (SAXException, RuntimeException)
+{
+ return this;
+}
+
+Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createUnknownChildContext(
+ const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/)
+ throw (SAXException, RuntimeException)
+{
+ return this;
+}
+
+void SAL_CALL FilterDetectDocHandler::characters( const OUString& /*aChars*/ )
+ throw (SAXException, RuntimeException)
+{
+}
+
+void SAL_CALL FilterDetectDocHandler::ignorableWhitespace( const OUString& /*aWhitespaces*/ )
+ throw (SAXException, RuntimeException)
+{
+}
+
+void SAL_CALL FilterDetectDocHandler::processingInstruction(
+ const OUString& /*aTarget*/, const OUString& /*aData*/ )
+ throw (SAXException, RuntimeException)
+{
+}
+
+void FilterDetectDocHandler::parseRelationship( const AttributeList& rAttribs )
+{
+ OUString aType = rAttribs.getString( XML_Type, OUString() );
+ if( aType.equalsAscii( "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" ) )
+ maTargetPath = OUString( sal_Unicode( '/' ) ) + rAttribs.getString( XML_Target, OUString() );
+}
+
+OUString FilterDetectDocHandler::getFilterNameFromContentType( const OUString& rContentType ) const
+{
+ if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" ) ||
+ rContentType.equalsAscii( "application/vnd.ms-word.document.macroEnabled.main+xml" ) )
+ return CREATE_OUSTRING( "writer_MS_Word_2007" );
+
+ if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml" ) ||
+ rContentType.equalsAscii( "application/vnd.ms-word.template.macroEnabledTemplate.main+xml" ) )
+ return CREATE_OUSTRING( "writer_MS_Word_2007_Template" );
+
+ if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" ) ||
+ rContentType.equalsAscii( "application/vnd.ms-excel.sheet.macroEnabled.main+xml" ) )
+ return CREATE_OUSTRING( "MS Excel 2007 XML" );
+
+ if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml" ) ||
+ rContentType.equalsAscii( "application/vnd.ms-excel.template.macroEnabled.main+xml" ) )
+ return CREATE_OUSTRING( "MS Excel 2007 XML Template" );
+
+ if( rContentType.equalsAscii( "application/vnd.ms-excel.sheet.binary.macroEnabled.main" ) )
+ return CREATE_OUSTRING( "MS Excel 2007 Binary" );
+
+ if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml" ) ||
+ rContentType.equalsAscii( "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml" ) )
+ return CREATE_OUSTRING( "MS PowerPoint 2007 XML" );
+
+ if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml" ) ||
+ rContentType.equalsAscii( "application/vnd.ms-powerpoint.template.macroEnabled.main+xml" ) )
+ return CREATE_OUSTRING( "MS PowerPoint 2007 XML Template" );
+
+ return OUString();
+}
+
+void FilterDetectDocHandler::parseContentTypesDefault( const AttributeList& rAttribs )
+{
+ // only if no overridden part name found
+ if( mrFilterName.getLength() == 0 )
+ {
+ // check if target path ends with extension
+ OUString aExtension = rAttribs.getString( XML_Extension, OUString() );
+ sal_Int32 nExtPos = maTargetPath.getLength() - aExtension.getLength();
+ if( (nExtPos > 0) && (maTargetPath[ nExtPos - 1 ] == '.') && maTargetPath.match( aExtension, nExtPos ) )
+ mrFilterName = getFilterNameFromContentType( rAttribs.getString( XML_ContentType, OUString() ) );
+ }
+}
+
+void FilterDetectDocHandler::parseContentTypesOverride( const AttributeList& rAttribs )
+{
+ if( rAttribs.getString( XML_PartName, OUString() ).equals( maTargetPath ) )
+ mrFilterName = getFilterNameFromContentType( rAttribs.getString( XML_ContentType, OUString() ) );
+}
+
+// ============================================================================
+
+/* Helper for XServiceInfo */
+Sequence< OUString > FilterDetect_getSupportedServiceNames()
+{
+ Sequence< OUString > aServiceNames( 1 );
+ aServiceNames[ 0 ] = CREATE_OUSTRING( "com.sun.star.frame.ExtendedTypeDetection" );
+ return aServiceNames;
+}
+
+/* Helper for XServiceInfo */
+OUString FilterDetect_getImplementationName()
+{
+ return CREATE_OUSTRING( "com.sun.star.comp.oox.FormatDetector" );
+}
+
+/* Helper for registry */
+Reference< XInterface > SAL_CALL FilterDetect_createInstance( const Reference< XMultiServiceFactory >& xServiceManager ) throw( Exception )
+{
+ return Reference< XInterface >( *new FilterDetect( xServiceManager ) );
+}
+
+// ----------------------------------------------------------------------------
+
+FilterDetect::FilterDetect( const Reference< XMultiServiceFactory >& rxFactory ) :
+ mxFactory( rxFactory )
+{
+ OSL_ENSURE( mxFactory.is(), "FilterDetect::FilterDetect - no service factory" );
+}
+
+FilterDetect::~FilterDetect()
+{
+}
+
+/* =========================================================================== */
+/* Kudos to Caolan McNamara who provided the core decryption implementations. */
+/* =========================================================================== */
+
+namespace {
+
+const sal_uInt32 ENCRYPTINFO_CRYPTOAPI = 0x00000004;
+const sal_uInt32 ENCRYPTINFO_DOCPROPS = 0x00000008;
+const sal_uInt32 ENCRYPTINFO_EXTERNAL = 0x00000010;
+const sal_uInt32 ENCRYPTINFO_AES = 0x00000020;
+
+const sal_uInt32 ENCRYPT_ALGO_AES128 = 0x0000660E;
+const sal_uInt32 ENCRYPT_ALGO_AES192 = 0x0000660F;
+const sal_uInt32 ENCRYPT_ALGO_AES256 = 0x00006610;
+const sal_uInt32 ENCRYPT_ALGO_RC4 = 0x00006801;
+
+const sal_uInt32 ENCRYPT_HASH_SHA1 = 0x00008004;
+
+// ----------------------------------------------------------------------------
+
+bool lclIsZipPackage( const Reference< XMultiServiceFactory >& rxFactory, const Reference< XInputStream >& rxInStrm )
+{
+ ZipStorage aZipStorage( rxFactory, rxInStrm );
+ return aZipStorage.isStorage();
+}
+
+// ----------------------------------------------------------------------------
+
+struct PackageEncryptionInfo
+{
+ sal_uInt8 mpnSalt[ 16 ];
+ sal_uInt8 mpnEncrVerifier[ 16 ];
+ sal_uInt8 mpnEncrVerifierHash[ 32 ];
+ sal_uInt32 mnFlags;
+ sal_uInt32 mnAlgorithmId;
+ sal_uInt32 mnAlgorithmIdHash;
+ sal_uInt32 mnKeySize;
+ sal_uInt32 mnSaltSize;
+ sal_uInt32 mnVerifierHashSize;
+};
+
+bool lclReadEncryptionInfo( PackageEncryptionInfo& rEncrInfo, BinaryInputStream& rStrm )
+{
+ rStrm.skip( 4 );
+ rStrm >> rEncrInfo.mnFlags;
+ if( getFlag( rEncrInfo.mnFlags, ENCRYPTINFO_EXTERNAL ) )
+ return false;
+
+ sal_uInt32 nHeaderSize, nRepeatedFlags;
+ rStrm >> nHeaderSize >> nRepeatedFlags;
+ if( (nHeaderSize < 20) || (nRepeatedFlags != rEncrInfo.mnFlags) )
+ return false;
+
+ rStrm.skip( 4 );
+ rStrm >> rEncrInfo.mnAlgorithmId >> rEncrInfo.mnAlgorithmIdHash >> rEncrInfo.mnKeySize;
+ rStrm.skip( nHeaderSize - 20 );
+ rStrm >> rEncrInfo.mnSaltSize;
+ if( rEncrInfo.mnSaltSize != 16 )
+ return false;
+
+ rStrm.readMemory( rEncrInfo.mpnSalt, 16 );
+ rStrm.readMemory( rEncrInfo.mpnEncrVerifier, 16 );
+ rStrm >> rEncrInfo.mnVerifierHashSize;
+ rStrm.readMemory( rEncrInfo.mpnEncrVerifierHash, 32 );
+ return !rStrm.isEof();
+}
+
+// ----------------------------------------------------------------------------
+
+void lclDeriveKey( const sal_uInt8* pnHash, sal_uInt32 nHashLen, sal_uInt8* pnKeyDerived, sal_uInt32 nRequiredKeyLen )
+{
+ sal_uInt8 pnBuffer[ 64 ];
+ memset( pnBuffer, 0x36, sizeof( pnBuffer ) );
+ for( sal_uInt32 i = 0; i < nHashLen; ++i )
+ pnBuffer[ i ] ^= pnHash[ i ];
+
+ rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
+ rtlDigestError aError = rtl_digest_update( aDigest, pnBuffer, sizeof( pnBuffer ) );
+ sal_uInt8 pnX1[ RTL_DIGEST_LENGTH_SHA1 ];
+ aError = rtl_digest_get( aDigest, pnX1, RTL_DIGEST_LENGTH_SHA1 );
+ rtl_digest_destroy( aDigest );
+
+ memset( pnBuffer, 0x5C, sizeof( pnBuffer ) );
+ for( sal_uInt32 i = 0; i < nHashLen; ++i )
+ pnBuffer[ i ] ^= pnHash[ i ];
+
+ aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
+ aError = rtl_digest_update( aDigest, pnBuffer, sizeof( pnBuffer ) );
+ sal_uInt8 pnX2[ RTL_DIGEST_LENGTH_SHA1 ];
+ aError = rtl_digest_get( aDigest, pnX2, RTL_DIGEST_LENGTH_SHA1 );
+ rtl_digest_destroy( aDigest );
+
+ if( nRequiredKeyLen > RTL_DIGEST_LENGTH_SHA1 )
+ {
+ memcpy( pnKeyDerived + RTL_DIGEST_LENGTH_SHA1, pnX2, nRequiredKeyLen - RTL_DIGEST_LENGTH_SHA1 );
+ nRequiredKeyLen = RTL_DIGEST_LENGTH_SHA1;
+ }
+ memcpy( pnKeyDerived, pnX1, nRequiredKeyLen );
+}
+
+// ----------------------------------------------------------------------------
+
+bool lclGenerateEncryptionKey( const PackageEncryptionInfo& rEncrInfo, const OUString& rPassword, sal_uInt8* pnKey, sal_uInt32 nRequiredKeyLen )
+{
+ size_t nBufferSize = rEncrInfo.mnSaltSize + 2 * rPassword.getLength();
+ sal_uInt8* pnBuffer = new sal_uInt8[ nBufferSize ];
+ memcpy( pnBuffer, rEncrInfo.mpnSalt, rEncrInfo.mnSaltSize );
+
+ sal_uInt8* pnPasswordLoc = pnBuffer + rEncrInfo.mnSaltSize;
+ const sal_Unicode* pStr = rPassword.getStr();
+ for( sal_Int32 i = 0, nLen = rPassword.getLength(); i < nLen; ++i, ++pStr, pnPasswordLoc += 2 )
+ ByteOrderConverter::writeLittleEndian( pnPasswordLoc, static_cast< sal_uInt16 >( *pStr ) );
+
+ rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
+ rtlDigestError aError = rtl_digest_update( aDigest, pnBuffer, nBufferSize );
+ delete[] pnBuffer;
+
+ size_t nHashSize = RTL_DIGEST_LENGTH_SHA1 + 4;
+ sal_uInt8* pnHash = new sal_uInt8[ nHashSize ];
+ aError = rtl_digest_get( aDigest, pnHash + 4, RTL_DIGEST_LENGTH_SHA1 );
+ rtl_digest_destroy( aDigest );
+
+ for( sal_uInt32 i = 0; i < 50000; ++i )
+ {
+ ByteOrderConverter::writeLittleEndian( pnHash, i );
+ aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
+ aError = rtl_digest_update( aDigest, pnHash, nHashSize );
+ aError = rtl_digest_get( aDigest, pnHash + 4, RTL_DIGEST_LENGTH_SHA1 );
+ rtl_digest_destroy( aDigest );
+ }
+
+ memmove( pnHash, pnHash + 4, RTL_DIGEST_LENGTH_SHA1 );
+ memset( pnHash + RTL_DIGEST_LENGTH_SHA1, 0, 4 );
+ aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
+ aError = rtl_digest_update( aDigest, pnHash, nHashSize );
+ aError = rtl_digest_get( aDigest, pnHash, RTL_DIGEST_LENGTH_SHA1 );
+ rtl_digest_destroy( aDigest );
+
+ lclDeriveKey( pnHash, RTL_DIGEST_LENGTH_SHA1, pnKey, nRequiredKeyLen );
+ delete[] pnHash;
+
+ // check password
+ EVP_CIPHER_CTX aes_ctx;
+ EVP_CIPHER_CTX_init( &aes_ctx );
+ EVP_DecryptInit_ex( &aes_ctx, EVP_aes_128_ecb(), 0, pnKey, 0 );
+ EVP_CIPHER_CTX_set_padding( &aes_ctx, 0 );
+ int nOutLen = 0;
+ sal_uInt8 pnVerifier[ 16 ] = { 0 };
+ /*int*/ EVP_DecryptUpdate( &aes_ctx, pnVerifier, &nOutLen, rEncrInfo.mpnEncrVerifier, sizeof( rEncrInfo.mpnEncrVerifier ) );
+ EVP_CIPHER_CTX_cleanup( &aes_ctx );
+
+ EVP_CIPHER_CTX_init( &aes_ctx );
+ EVP_DecryptInit_ex( &aes_ctx, EVP_aes_128_ecb(), 0, pnKey, 0 );
+ EVP_CIPHER_CTX_set_padding( &aes_ctx, 0 );
+ sal_uInt8 pnVerifierHash[ 32 ] = { 0 };
+ /*int*/ EVP_DecryptUpdate( &aes_ctx, pnVerifierHash, &nOutLen, rEncrInfo.mpnEncrVerifierHash, sizeof( rEncrInfo.mpnEncrVerifierHash ) );
+ EVP_CIPHER_CTX_cleanup( &aes_ctx );
+
+ aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
+ aError = rtl_digest_update( aDigest, pnVerifier, sizeof( pnVerifier ) );
+ sal_uInt8 pnSha1Hash[ RTL_DIGEST_LENGTH_SHA1 ];
+ aError = rtl_digest_get( aDigest, pnSha1Hash, RTL_DIGEST_LENGTH_SHA1 );
+ rtl_digest_destroy( aDigest );
+
+ return memcmp( pnSha1Hash, pnVerifierHash, RTL_DIGEST_LENGTH_SHA1 ) == 0;
+}
+
+// the password verifier ------------------------------------------------------
+
+class PasswordVerifier : public ::comphelper::IDocPasswordVerifier
+{
+public:
+ explicit PasswordVerifier( const PackageEncryptionInfo& rEncryptInfo );
+
+ virtual ::comphelper::DocPasswordVerifierResult
+ verifyPassword( const OUString& rPassword );
+
+ inline const sal_uInt8* getKey() const { return &maKey.front(); }
+
+private:
+ const PackageEncryptionInfo& mrEncryptInfo;
+ ::std::vector< sal_uInt8 > maKey;
+};
+
+PasswordVerifier::PasswordVerifier( const PackageEncryptionInfo& rEncryptInfo ) :
+ mrEncryptInfo( rEncryptInfo ),
+ maKey( static_cast< size_t >( rEncryptInfo.mnKeySize / 8 ), 0 )
+{
+}
+
+::comphelper::DocPasswordVerifierResult PasswordVerifier::verifyPassword( const OUString& rPassword )
+{
+ // verifies the password and writes the related decryption key into maKey
+ return lclGenerateEncryptionKey( mrEncryptInfo, rPassword, &maKey.front(), maKey.size() ) ?
+ ::comphelper::DocPasswordVerifierResult_OK : ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD;
+}
+
+} // namespace
+
+// ----------------------------------------------------------------------------
+
+Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescriptor& rMediaDesc ) const
+{
+ if( mxFactory.is() )
+ {
+ // try the plain input stream
+ Reference< XInputStream > xInStrm( rMediaDesc[ MediaDescriptor::PROP_INPUTSTREAM() ], UNO_QUERY );
+ if( !xInStrm.is() || lclIsZipPackage( mxFactory, xInStrm ) )
+ return xInStrm;
+
+ // check if a temporary file is passed in the 'ComponentData' property
+ Reference< XStream > xDecrypted( rMediaDesc.getComponentDataEntry( CREATE_OUSTRING( "DecryptedPackage" ) ), UNO_QUERY );
+ if( xDecrypted.is() )
+ {
+ Reference< XInputStream > xDecrInStrm = xDecrypted->getInputStream();
+ if( lclIsZipPackage( mxFactory, xDecrInStrm ) )
+ return xDecrInStrm;
+ }
+
+ // try to decrypt an encrypted OLE package
+ ::oox::ole::OleStorage aOleStorage( mxFactory, xInStrm, false );
+ if( aOleStorage.isStorage() ) try
+ {
+ // open the required input streams in the encrypted package
+ Reference< XInputStream > xEncryptionInfo( aOleStorage.openInputStream( CREATE_OUSTRING( "EncryptionInfo" ) ), UNO_SET_THROW );
+ Reference< XInputStream > xEncryptedPackage( aOleStorage.openInputStream( CREATE_OUSTRING( "EncryptedPackage" ) ), UNO_SET_THROW );
+
+ // read the encryption info stream
+ PackageEncryptionInfo aEncryptInfo;
+ BinaryXInputStream aInfoStrm( xEncryptionInfo, true );
+ bool bValidInfo = lclReadEncryptionInfo( aEncryptInfo, aInfoStrm );
+
+ // check flags and agorithm IDs, requiered are AES128 and SHA-1
+ bool bImplemented = bValidInfo &&
+ getFlag( aEncryptInfo.mnFlags, ENCRYPTINFO_CRYPTOAPI ) &&
+ getFlag( aEncryptInfo.mnFlags, ENCRYPTINFO_AES ) &&
+ // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set
+ ((aEncryptInfo.mnAlgorithmId == 0) || (aEncryptInfo.mnAlgorithmId == ENCRYPT_ALGO_AES128)) &&
+ // hash algorithm ID 0 defaults to SHA-1 too
+ ((aEncryptInfo.mnAlgorithmIdHash == 0) || (aEncryptInfo.mnAlgorithmIdHash == ENCRYPT_HASH_SHA1)) &&
+ (aEncryptInfo.mnVerifierHashSize == 20);
+
+ if( bImplemented )
+ {
+ /* "VelvetSweatshop" is the built-in default encryption
+ password used by MS Excel for the "workbook protection"
+ feature with password. Try this first before prompting the
+ user for a password. */
+ ::std::vector< OUString > aDefaultPasswords;
+ aDefaultPasswords.push_back( CREATE_OUSTRING( "VelvetSweatshop" ) );
+
+ /* Use the comphelper password helper to request a password.
+ This helper returns either with the correct password
+ (according to the verifier), or with an empty string if
+ user has cancelled the password input dialog. */
+ PasswordVerifier aVerifier( aEncryptInfo );
+ OUString aPassword = ::comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
+ aVerifier, rMediaDesc, ::comphelper::DocPasswordRequestType_MS, &aDefaultPasswords );
+
+ if( aPassword.getLength() == 0 )
+ {
+ rMediaDesc[ MediaDescriptor::PROP_ABORTED() ] <<= true;
+ }
+ else
+ {
+ // create temporary file for unencrypted package
+ Reference< XStream > xTempFile( mxFactory->createInstance( CREATE_OUSTRING( "com.sun.star.io.TempFile" ) ), UNO_QUERY_THROW );
+ Reference< XOutputStream > xDecryptedPackage( xTempFile->getOutputStream(), UNO_SET_THROW );
+ BinaryXOutputStream aDecryptedPackage( xDecryptedPackage, true );
+ BinaryXInputStream aEncryptedPackage( xEncryptedPackage, true );
+
+ EVP_CIPHER_CTX aes_ctx;
+ EVP_CIPHER_CTX_init( &aes_ctx );
+ EVP_DecryptInit_ex( &aes_ctx, EVP_aes_128_ecb(), 0, aVerifier.getKey(), 0 );
+ EVP_CIPHER_CTX_set_padding( &aes_ctx, 0 );
+
+ sal_uInt8 pnInBuffer[ 1024 ];
+ sal_uInt8 pnOutBuffer[ 1024 ];
+ sal_Int32 nInLen;
+ int nOutLen;
+ aEncryptedPackage.skip( 8 ); // decrypted size
+ while( (nInLen = aEncryptedPackage.readMemory( pnInBuffer, sizeof( pnInBuffer ) )) > 0 )
+ {
+ EVP_DecryptUpdate( &aes_ctx, pnOutBuffer, &nOutLen, pnInBuffer, nInLen );
+ aDecryptedPackage.writeMemory( pnOutBuffer, nOutLen );
+ }
+ EVP_DecryptFinal_ex( &aes_ctx, pnOutBuffer, &nOutLen );
+ aDecryptedPackage.writeMemory( pnOutBuffer, nOutLen );
+
+ EVP_CIPHER_CTX_cleanup( &aes_ctx );
+ xDecryptedPackage->flush();
+ aDecryptedPackage.seekToStart();
+
+ // store temp file in media descriptor to keep it alive
+ rMediaDesc.setComponentDataEntry( CREATE_OUSTRING( "DecryptedPackage" ), Any( xTempFile ) );
+
+ Reference< XInputStream > xDecrInStrm = xTempFile->getInputStream();
+ if( lclIsZipPackage( mxFactory, xDecrInStrm ) )
+ return xDecrInStrm;
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ }
+ }
+
+ return Reference< XInputStream >();
+}
+
+// com.sun.star.lang.XServiceInfo interface -----------------------------------
+
+OUString SAL_CALL FilterDetect::getImplementationName() throw( RuntimeException )
+{
+ return FilterDetect_getImplementationName();
+}
+
+sal_Bool SAL_CALL FilterDetect::supportsService( const OUString& rServiceName ) throw( RuntimeException )
+{
+ const Sequence< OUString > aServices = FilterDetect_getSupportedServiceNames();
+ const OUString* pArray = aServices.getConstArray();
+ const OUString* pArrayEnd = pArray + aServices.getLength();
+ return ::std::find( pArray, pArrayEnd, rServiceName ) != pArrayEnd;
+}
+
+Sequence< OUString > SAL_CALL FilterDetect::getSupportedServiceNames() throw( RuntimeException )
+{
+ return FilterDetect_getSupportedServiceNames();
+}
+
+// com.sun.star.document.XExtendedFilterDetection interface -------------------
+
+OUString SAL_CALL FilterDetect::detect( Sequence< PropertyValue >& rMediaDescSeq ) throw( RuntimeException )
+{
+ OUString aFilterName;
+ MediaDescriptor aMediaDesc( rMediaDescSeq );
+
+ /* Check that the user has not choosen to abort detection, e.g. by hitting
+ 'Cancel' in the password input dialog. This may happen because this
+ filter detection is used by different filters. */
+ bool bAborted = aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_ABORTED(), false );
+ if( !bAborted && mxFactory.is() ) try
+ {
+ aMediaDesc.addInputStream();
+
+ /* Get the unencrypted input stream. This may include creation of a
+ temporary file that contains the decrypted package. This temporary
+ file will be stored in the 'ComponentData' property of the media
+ descriptor. */
+ Reference< XInputStream > xInStrm( extractUnencryptedPackage( aMediaDesc ), UNO_SET_THROW );
+
+ // try to detect the file type, must be a ZIP package
+ ZipStorage aZipStorage( mxFactory, xInStrm );
+ if( aZipStorage.isStorage() )
+ {
+ Reference< XFastParser > xParser( mxFactory->createInstance(
+ CREATE_OUSTRING( "com.sun.star.xml.sax.FastParser" ) ), UNO_QUERY_THROW );
+
+ xParser->setFastDocumentHandler( new FilterDetectDocHandler( aFilterName ) );
+ xParser->setTokenHandler( new FastTokenHandler );
+
+ xParser->registerNamespace( CREATE_OUSTRING( "http://schemas.openxmlformats.org/package/2006/relationships" ), NMSP_PACKAGE_RELATIONSHIPS );
+ xParser->registerNamespace( CREATE_OUSTRING( "http://schemas.openxmlformats.org/officeDocument/2006/relationships" ), NMSP_RELATIONSHIPS );
+ xParser->registerNamespace( CREATE_OUSTRING( "http://schemas.openxmlformats.org/package/2006/content-types" ), NMSP_CONTENT_TYPES );
+
+ // Parse _rels/.rels to get the target path.
+ InputSource aParserInput;
+ aParserInput.sSystemId = CREATE_OUSTRING( "_rels/.rels" );
+ aParserInput.aInputStream = aZipStorage.openInputStream( aParserInput.sSystemId );
+ xParser->parseStream( aParserInput );
+
+ // Parse [Content_Types].xml to determine the content type of the part at the target path.
+ aParserInput.sSystemId = CREATE_OUSTRING( "[Content_Types].xml" );
+ aParserInput.aInputStream = aZipStorage.openInputStream( aParserInput.sSystemId );
+ xParser->parseStream( aParserInput );
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ // write back changed media descriptor members
+ aMediaDesc >> rMediaDescSeq;
+ return aFilterName;
+}
+
+// ============================================================================
+
+} // namespace core
+} // namespace oox
+