summaryrefslogtreecommitdiff
path: root/filter/source/msfilter/mscodec.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'filter/source/msfilter/mscodec.cxx')
-rw-r--r--filter/source/msfilter/mscodec.cxx576
1 files changed, 576 insertions, 0 deletions
diff --git a/filter/source/msfilter/mscodec.cxx b/filter/source/msfilter/mscodec.cxx
new file mode 100644
index 000000000000..f530dde3f1a3
--- /dev/null
+++ b/filter/source/msfilter/mscodec.cxx
@@ -0,0 +1,576 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_filter.hxx"
+#include "filter/msfilter/mscodec.hxx"
+
+#include <osl/diagnose.h>
+#include <algorithm>
+#include <string.h>
+#include <tools/solar.h>
+
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/docpasswordhelper.hxx>
+
+#define DEBUG_MSO_ENCRYPTION_STD97 0
+
+#if DEBUG_MSO_ENCRYPTION_STD97
+#include <stdio.h>
+#endif
+
+using namespace ::com::sun::star;
+
+namespace msfilter {
+
+// ============================================================================
+
+namespace {
+
+/** Rotates rnValue left by nBits bits. */
+template< typename Type >
+inline void lclRotateLeft( Type& rnValue, int nBits )
+{
+ OSL_ASSERT(
+ nBits >= 0 &&
+ sal::static_int_cast< unsigned int >(nBits) < sizeof( Type ) * 8 );
+ rnValue = static_cast< Type >( (rnValue << nBits) | (rnValue >> (sizeof( Type ) * 8 - nBits)) );
+}
+
+/** Rotates the lower nWidth bits of rnValue left by nBits bits. */
+template< typename Type >
+inline void lclRotateLeft( Type& rnValue, sal_uInt8 nBits, sal_uInt8 nWidth )
+{
+ OSL_ASSERT( (nBits < nWidth) && (nWidth < sizeof( Type ) * 8) );
+ Type nMask = static_cast< Type >( (1UL << nWidth) - 1 );
+ rnValue = static_cast< Type >(
+ ((rnValue << nBits) | ((rnValue & nMask) >> (nWidth - nBits))) & nMask );
+}
+
+sal_Size lclGetLen( const sal_uInt8* pnPassData, sal_Size nBufferSize )
+{
+ sal_Size nLen = 0;
+ while( (nLen < nBufferSize) && pnPassData[ nLen ] ) ++nLen;
+ return nLen;
+}
+
+sal_uInt16 lclGetKey( const sal_uInt8* pnPassData, sal_Size nBufferSize )
+{
+ sal_Size nLen = lclGetLen( pnPassData, nBufferSize );
+ if( !nLen ) return 0;
+
+ sal_uInt16 nKey = 0;
+ sal_uInt16 nKeyBase = 0x8000;
+ sal_uInt16 nKeyEnd = 0xFFFF;
+ const sal_uInt8* pnChar = pnPassData + nLen - 1;
+ for( sal_Size nIndex = 0; nIndex < nLen; ++nIndex, --pnChar )
+ {
+ sal_uInt8 cChar = *pnChar & 0x7F;
+ for( sal_uInt8 nBit = 0; nBit < 8; ++nBit )
+ {
+ lclRotateLeft( nKeyBase, 1 );
+ if( nKeyBase & 1 ) nKeyBase ^= 0x1020;
+ if( cChar & 1 ) nKey ^= nKeyBase;
+ cChar >>= 1;
+ lclRotateLeft( nKeyEnd, 1 );
+ if( nKeyEnd & 1 ) nKeyEnd ^= 0x1020;
+ }
+ }
+ return nKey ^ nKeyEnd;
+}
+
+sal_uInt16 lclGetHash( const sal_uInt8* pnPassData, sal_Size nBufferSize )
+{
+ sal_Size nLen = lclGetLen( pnPassData, nBufferSize );
+
+ sal_uInt16 nHash = static_cast< sal_uInt16 >( nLen );
+ if( nLen )
+ nHash ^= 0xCE4B;
+
+ const sal_uInt8* pnChar = pnPassData;
+ for( sal_Size nIndex = 0; nIndex < nLen; ++nIndex, ++pnChar )
+ {
+ sal_uInt16 cChar = *pnChar;
+ sal_uInt8 nRot = static_cast< sal_uInt8 >( (nIndex + 1) % 15 );
+ lclRotateLeft( cChar, nRot, 15 );
+ nHash ^= cChar;
+ }
+ return nHash;
+}
+
+
+} // namespace
+
+// ============================================================================
+
+MSCodec_Xor95::MSCodec_Xor95(int nRotateDistance) :
+ mnOffset( 0 ),
+ mnKey( 0 ),
+ mnHash( 0 ),
+ mnRotateDistance( nRotateDistance )
+{
+ (void)memset( mpnKey, 0, sizeof( mpnKey ) );
+}
+
+MSCodec_Xor95::~MSCodec_Xor95()
+{
+ (void)memset( mpnKey, 0, sizeof( mpnKey ) );
+ mnKey = mnHash = 0;
+}
+
+void MSCodec_Xor95::InitKey( const sal_uInt8 pnPassData[ 16 ] )
+{
+ mnKey = lclGetKey( pnPassData, 16 );
+ mnHash = lclGetHash( pnPassData, 16 );
+
+ (void)memcpy( mpnKey, pnPassData, 16 );
+
+ static const sal_uInt8 spnFillChars[] =
+ {
+ 0xBB, 0xFF, 0xFF, 0xBA,
+ 0xFF, 0xFF, 0xB9, 0x80,
+ 0x00, 0xBE, 0x0F, 0x00,
+ 0xBF, 0x0F, 0x00
+ };
+
+ sal_Size nIndex;
+ sal_Size nLen = lclGetLen( pnPassData, 16 );
+ const sal_uInt8* pnFillChar = spnFillChars;
+ for( nIndex = nLen; nIndex < sizeof( mpnKey ); ++nIndex, ++pnFillChar )
+ mpnKey[ nIndex ] = *pnFillChar;
+
+ SVBT16 pnOrigKey;
+ ShortToSVBT16( mnKey, pnOrigKey );
+ sal_uInt8* pnKeyChar = mpnKey;
+ for( nIndex = 0; nIndex < sizeof( mpnKey ); ++nIndex, ++pnKeyChar )
+ {
+ *pnKeyChar ^= pnOrigKey[ nIndex & 0x01 ];
+ lclRotateLeft( *pnKeyChar, mnRotateDistance );
+ }
+}
+
+sal_Bool MSCodec_Xor95::InitCodec( const uno::Sequence< beans::NamedValue >& aData )
+{
+ sal_Bool bResult = sal_False;
+
+ ::comphelper::SequenceAsHashMap aHashData( aData );
+ uno::Sequence< sal_Int8 > aKey = aHashData.getUnpackedValueOrDefault( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "XOR95EncryptionKey" ) ), uno::Sequence< sal_Int8 >() );
+
+ if ( aKey.getLength() == 16 )
+ {
+ (void)memcpy( mpnKey, aKey.getConstArray(), 16 );
+ bResult = sal_True;
+
+ mnKey = (sal_uInt16)aHashData.getUnpackedValueOrDefault( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "XOR95BaseKey" ) ), (sal_Int16)0 );
+ mnHash = (sal_uInt16)aHashData.getUnpackedValueOrDefault( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "XOR95PasswordHash" ) ), (sal_Int16)0 );
+ }
+ else
+ OSL_FAIL( "Unexpected key size!\n" );
+
+ return bResult;
+}
+
+uno::Sequence< beans::NamedValue > MSCodec_Xor95::GetEncryptionData()
+{
+ ::comphelper::SequenceAsHashMap aHashData;
+ aHashData[ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "XOR95EncryptionKey" ) ) ] <<= uno::Sequence<sal_Int8>( (sal_Int8*)mpnKey, 16 );
+ aHashData[ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "XOR95BaseKey" ) ) ] <<= (sal_Int16)mnKey;
+ aHashData[ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "XOR95PasswordHash" ) ) ] <<= (sal_Int16)mnHash;
+
+ return aHashData.getAsConstNamedValueList();
+}
+
+bool MSCodec_Xor95::VerifyKey( sal_uInt16 nKey, sal_uInt16 nHash ) const
+{
+ return (nKey == mnKey) && (nHash == mnHash);
+}
+
+void MSCodec_Xor95::InitCipher()
+{
+ mnOffset = 0;
+}
+
+void MSCodec_XorXLS95::Decode( sal_uInt8* pnData, sal_Size nBytes )
+{
+ const sal_uInt8* pnCurrKey = mpnKey + mnOffset;
+ const sal_uInt8* pnKeyLast = mpnKey + 0x0F;
+
+ for( const sal_uInt8* pnDataEnd = pnData + nBytes; pnData < pnDataEnd; ++pnData )
+ {
+ lclRotateLeft( *pnData, 3 );
+ *pnData ^= *pnCurrKey;
+ if( pnCurrKey < pnKeyLast ) ++pnCurrKey; else pnCurrKey = mpnKey;
+ }
+
+ // update mnOffset
+ Skip( nBytes );
+}
+
+void MSCodec_XorWord95::Decode( sal_uInt8* pnData, sal_Size nBytes )
+{
+ const sal_uInt8* pnCurrKey = mpnKey + mnOffset;
+ const sal_uInt8* pnKeyLast = mpnKey + 0x0F;
+
+ for( const sal_uInt8* pnDataEnd = pnData + nBytes; pnData < pnDataEnd; ++pnData )
+ {
+ const sal_uInt8 cChar = *pnData ^ *pnCurrKey;
+ if (*pnData && cChar)
+ *pnData = cChar;
+ if( pnCurrKey < pnKeyLast ) ++pnCurrKey; else pnCurrKey = mpnKey;
+ }
+
+ // update mnOffset
+ Skip( nBytes );
+}
+
+
+void MSCodec_Xor95::Skip( sal_Size nBytes )
+{
+ mnOffset = (mnOffset + nBytes) & 0x0F;
+}
+
+// ============================================================================
+
+MSCodec_Std97::MSCodec_Std97 ()
+{
+ m_hCipher = rtl_cipher_create (
+ rtl_Cipher_AlgorithmARCFOUR, rtl_Cipher_ModeStream);
+ OSL_ASSERT(m_hCipher != 0);
+
+ m_hDigest = rtl_digest_create (
+ rtl_Digest_AlgorithmMD5);
+ OSL_ASSERT(m_hDigest != 0);
+
+ (void)memset (m_pDigestValue, 0, sizeof(m_pDigestValue));
+ (void)memset (m_pDocId, 0, sizeof(m_pDocId));
+}
+
+MSCodec_Std97::~MSCodec_Std97 ()
+{
+ (void)memset (m_pDigestValue, 0, sizeof(m_pDigestValue));
+ (void)memset (m_pDocId, 0, sizeof(m_pDocId));
+ rtl_digest_destroy (m_hDigest);
+ rtl_cipher_destroy (m_hCipher);
+}
+
+#if 0
+#if DEBUG_MSO_ENCRYPTION_STD97
+static void lcl_PrintKeyData(const sal_uInt8* pKeyData, const char* msg)
+{
+ printf("pKeyData: (%s)\n", msg);
+ for (int j = 0; j < 4; ++j)
+ {
+ for (int i = 0; i < 16; ++i)
+ printf("%2.2x ", pKeyData[j*16+i]);
+ printf("\n");
+ }
+}
+#else
+static void lcl_PrintKeyData(const sal_uInt8* /*pKeyData*/, const char* /*msg*/)
+{
+}
+#endif
+#endif
+
+#if DEBUG_MSO_ENCRYPTION_STD97
+static void lcl_PrintDigest(const sal_uInt8* pDigest, const char* msg)
+{
+ printf("digest: (%s)\n", msg);
+ for (int i = 0; i < 16; ++i)
+ printf("%2.2x ", pDigest[i]);
+ printf("\n");
+}
+#else
+static void lcl_PrintDigest(const sal_uInt8* /*pDigest*/, const char* /*msg*/)
+{
+}
+#endif
+
+sal_Bool MSCodec_Std97::InitCodec( const uno::Sequence< beans::NamedValue >& aData )
+{
+#if DEBUG_MSO_ENCRYPTION_STD97
+ fprintf(stdout, "MSCodec_Std97::InitCodec: --begin\n");fflush(stdout);
+#endif
+ sal_Bool bResult = sal_False;
+
+ ::comphelper::SequenceAsHashMap aHashData( aData );
+ uno::Sequence< sal_Int8 > aKey = aHashData.getUnpackedValueOrDefault( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "STD97EncryptionKey" ) ), uno::Sequence< sal_Int8 >() );
+
+ if ( aKey.getLength() == RTL_DIGEST_LENGTH_MD5 )
+ {
+ (void)memcpy( m_pDigestValue, aKey.getConstArray(), RTL_DIGEST_LENGTH_MD5 );
+ uno::Sequence< sal_Int8 > aUniqueID = aHashData.getUnpackedValueOrDefault( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "STD97UniqueID" ) ), uno::Sequence< sal_Int8 >() );
+ if ( aUniqueID.getLength() == 16 )
+ {
+ (void)memcpy( m_pDocId, aUniqueID.getConstArray(), 16 );
+ bResult = sal_True;
+ lcl_PrintDigest(m_pDigestValue, "digest value");
+ lcl_PrintDigest(m_pDocId, "DocId value");
+ }
+ else
+ OSL_FAIL( "Unexpected document ID!\n" );
+ }
+ else
+ OSL_FAIL( "Unexpected key size!\n" );
+
+ return bResult;
+}
+
+uno::Sequence< beans::NamedValue > MSCodec_Std97::GetEncryptionData()
+{
+ ::comphelper::SequenceAsHashMap aHashData;
+ aHashData[ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "STD97EncryptionKey" ) ) ] <<= uno::Sequence< sal_Int8 >( (sal_Int8*)m_pDigestValue, RTL_DIGEST_LENGTH_MD5 );
+ aHashData[ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "STD97UniqueID" ) ) ] <<= uno::Sequence< sal_Int8 >( (sal_Int8*)m_pDocId, 16 );
+
+ return aHashData.getAsConstNamedValueList();
+}
+
+void MSCodec_Std97::InitKey (
+ const sal_uInt16 pPassData[16],
+ const sal_uInt8 pDocId[16])
+{
+#if DEBUG_MSO_ENCRYPTION_STD97
+ fprintf(stdout, "MSCodec_Std97::InitKey: --begin\n");fflush(stdout);
+#endif
+ uno::Sequence< sal_Int8 > aKey = ::comphelper::DocPasswordHelper::GenerateStd97Key( pPassData, uno::Sequence< sal_Int8 >( (sal_Int8*)pDocId, 16 ) );
+ // Fill raw digest of above updates into DigestValue.
+
+ if ( aKey.getLength() == sizeof(m_pDigestValue) )
+ (void)memcpy ( m_pDigestValue, aKey.getConstArray(), sizeof(m_pDigestValue) );
+ else
+ memset( m_pDigestValue, 0, sizeof(m_pDigestValue) );
+
+ lcl_PrintDigest(m_pDigestValue, "digest value");
+
+ (void)memcpy (m_pDocId, pDocId, 16);
+
+ lcl_PrintDigest(m_pDocId, "DocId value");
+}
+
+bool MSCodec_Std97::VerifyKey (
+ const sal_uInt8 pSaltData[16],
+ const sal_uInt8 pSaltDigest[16])
+{
+ // both the salt data and salt digest (hash) come from the document being imported.
+
+#if DEBUG_MSO_ENCRYPTION_STD97
+ fprintf(stdout, "MSCodec_Std97::VerifyKey: \n");
+ lcl_PrintDigest(pSaltData, "salt data");
+ lcl_PrintDigest(pSaltDigest, "salt hash");
+#endif
+ bool result = false;
+
+ if (InitCipher(0))
+ {
+ sal_uInt8 pDigest[RTL_DIGEST_LENGTH_MD5];
+ GetDigestFromSalt(pSaltData, pDigest);
+
+ sal_uInt8 pBuffer[16];
+ // Decode original SaltDigest into Buffer.
+ rtl_cipher_decode (
+ m_hCipher, pSaltDigest, 16, pBuffer, sizeof(pBuffer));
+
+ // Compare Buffer with computed Digest.
+ result = (memcmp (pBuffer, pDigest, sizeof(pDigest)) == 0);
+
+ // Erase Buffer and Digest arrays.
+ (void)memset (pBuffer, 0, sizeof(pBuffer));
+ (void)memset (pDigest, 0, sizeof(pDigest));
+ }
+
+ return (result);
+}
+
+bool MSCodec_Std97::InitCipher (sal_uInt32 nCounter)
+{
+ rtlCipherError result;
+ sal_uInt8 pKeyData[64]; // 512-bit message block
+
+ // Initialize KeyData array.
+ (void)memset (pKeyData, 0, sizeof(pKeyData));
+
+ // Fill 40 bit of DigestValue into [0..4].
+ (void)memcpy (pKeyData, m_pDigestValue, 5);
+
+ // Fill counter into [5..8].
+ pKeyData[ 5] = sal_uInt8((nCounter >> 0) & 0xff);
+ pKeyData[ 6] = sal_uInt8((nCounter >> 8) & 0xff);
+ pKeyData[ 7] = sal_uInt8((nCounter >> 16) & 0xff);
+ pKeyData[ 8] = sal_uInt8((nCounter >> 24) & 0xff);
+
+ pKeyData[ 9] = 0x80;
+ pKeyData[56] = 0x48;
+
+ // Fill raw digest of KeyData into KeyData.
+ (void)rtl_digest_updateMD5 (
+ m_hDigest, pKeyData, sizeof(pKeyData));
+ (void)rtl_digest_rawMD5 (
+ m_hDigest, pKeyData, RTL_DIGEST_LENGTH_MD5);
+
+ // Initialize Cipher with KeyData (for decoding).
+ result = rtl_cipher_init (
+ m_hCipher, rtl_Cipher_DirectionBoth,
+ pKeyData, RTL_DIGEST_LENGTH_MD5, 0, 0);
+
+ // Erase KeyData array and leave.
+ (void)memset (pKeyData, 0, sizeof(pKeyData));
+
+ return (result == rtl_Cipher_E_None);
+}
+
+bool MSCodec_Std97::CreateSaltDigest( const sal_uInt8 nSaltData[16], sal_uInt8 nSaltDigest[16] )
+{
+#if DEBUG_MSO_ENCRYPTION_STD97
+ lcl_PrintDigest(nSaltData, "salt data");
+#endif
+ bool result = false;
+
+ if (InitCipher(0))
+ {
+ sal_uInt8 pDigest[RTL_DIGEST_LENGTH_MD5];
+ GetDigestFromSalt(nSaltData, pDigest);
+
+ rtl_cipher_decode (
+ m_hCipher, pDigest, 16, pDigest, sizeof(pDigest));
+
+ (void)memcpy(nSaltDigest, pDigest, 16);
+ }
+
+ return (result);
+}
+
+bool MSCodec_Std97::Encode (
+ const void *pData, sal_Size nDatLen,
+ sal_uInt8 *pBuffer, sal_Size nBufLen)
+{
+ rtlCipherError result;
+
+ result = rtl_cipher_encode (
+ m_hCipher, pData, nDatLen, pBuffer, nBufLen);
+
+ return (result == rtl_Cipher_E_None);
+}
+
+bool MSCodec_Std97::Decode (
+ const void *pData, sal_Size nDatLen,
+ sal_uInt8 *pBuffer, sal_Size nBufLen)
+{
+ rtlCipherError result;
+
+ result = rtl_cipher_decode (
+ m_hCipher, pData, nDatLen, pBuffer, nBufLen);
+
+ return (result == rtl_Cipher_E_None);
+}
+
+bool MSCodec_Std97::Skip( sal_Size nDatLen )
+{
+ sal_uInt8 pnDummy[ 1024 ];
+ sal_Size nDatLeft = nDatLen;
+ bool bResult = true;
+
+ while (bResult && nDatLeft)
+ {
+ sal_Size nBlockLen = ::std::min< sal_Size >( nDatLeft, sizeof(pnDummy) );
+ bResult = Decode( pnDummy, nBlockLen, pnDummy, nBlockLen );
+ nDatLeft -= nBlockLen;
+ }
+
+ return bResult;
+}
+
+void MSCodec_Std97::GetDigestFromSalt( const sal_uInt8 pSaltData[16], sal_uInt8 pDigest[16] )
+{
+ sal_uInt8 pBuffer[64];
+ sal_uInt8 pDigestLocal[16];
+
+ // Decode SaltData into Buffer.
+ rtl_cipher_decode (
+ m_hCipher, pSaltData, 16, pBuffer, sizeof(pBuffer));
+
+ // set the 129th bit to make the buffer 128-bit in length.
+ pBuffer[16] = 0x80;
+
+ // erase the rest of the buffer with zeros.
+ (void)memset (pBuffer + 17, 0, sizeof(pBuffer) - 17);
+
+ // set the 441st bit.
+ pBuffer[56] = 0x80;
+
+ // Fill raw digest of Buffer into Digest.
+ rtl_digest_updateMD5 (
+ m_hDigest, pBuffer, sizeof(pBuffer));
+ rtl_digest_rawMD5 (
+ m_hDigest, pDigestLocal, sizeof(pDigestLocal));
+
+ memcpy(pDigest, pDigestLocal, 16);
+}
+
+void MSCodec_Std97::GetEncryptKey (
+ const sal_uInt8 pSalt[16],
+ sal_uInt8 pSaltData[16],
+ sal_uInt8 pSaltDigest[16])
+{
+ if (InitCipher(0))
+ {
+ sal_uInt8 pDigest[RTL_DIGEST_LENGTH_MD5];
+ sal_uInt8 pBuffer[64];
+
+ rtl_cipher_encode (
+ m_hCipher, pSalt, 16, pSaltData, sizeof(pBuffer));
+
+ (void)memcpy( pBuffer, pSalt, 16 );
+
+ pBuffer[16] = 0x80;
+ (void)memset (pBuffer + 17, 0, sizeof(pBuffer) - 17);
+ pBuffer[56] = 0x80;
+
+ rtl_digest_updateMD5 (
+ m_hDigest, pBuffer, sizeof(pBuffer));
+ rtl_digest_rawMD5 (
+ m_hDigest, pDigest, sizeof(pDigest));
+
+ rtl_cipher_encode (
+ m_hCipher, pDigest, 16, pSaltDigest, 16);
+
+ (void)memset (pBuffer, 0, sizeof(pBuffer));
+ (void)memset (pDigest, 0, sizeof(pDigest));
+ }
+}
+
+void MSCodec_Std97::GetDocId( sal_uInt8 pDocId[16] )
+{
+ if ( sizeof( m_pDocId ) == 16 )
+ (void)memcpy( pDocId, m_pDocId, 16 );
+}
+
+// ============================================================================
+
+} // namespace svx
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */