text: /**************************************************************
   * 
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   * 
   *   http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   * 
   *************************************************************/
  
  
  
  // MARKER(update_precomp.py): autogen include statement, do not remove
  #include <stdarg.h>
  #include <stdio.h>
  #include <string.h>
  #include <utility>
  
  #include <rtl/ustring.hxx>
  #include <rtl/ustrbuf.hxx>
  #include <rtl/random.h>
  #include <sax/fshelper.hxx>
  #include <unotools/streamwrap.hxx>
  
  #include "precompiled_scfilt.hxx"
  #include "docuno.hxx"
  #include "xestream.hxx"
  #include "xladdress.hxx"
  #include "xlstring.hxx"
  #include "xeroot.hxx"
  #include "xestyle.hxx"
  #include "rangelst.hxx"
  #include "compiler.hxx"
  
  #include <oox/xls/excelvbaproject.hxx>
  #include <formula/grammar.hxx>
  
  #define DEBUG_XL_ENCRYPTION 0
  
  using ::rtl::OString;
  using ::rtl::OUString;
  using ::rtl::OUStringBuffer;
  using ::utl::OStreamWrapper;
  using ::std::vector;
  
  using namespace ::com::sun::star::beans;
  using namespace ::com::sun::star::io;
  using namespace ::com::sun::star::lang;
  using namespace ::com::sun::star::sheet;
  using namespace ::com::sun::star::uno;
  using namespace ::formula;
  using namespace ::oox;
  
  // ============================================================================
  
  XclExpStream::XclExpStream( SvStream& rOutStrm, const XclExpRoot& rRoot, sal_uInt16 nMaxRecSize ) :
      mrStrm( rOutStrm ),
      mrRoot( rRoot ),
      mnMaxRecSize( nMaxRecSize ),
      mnCurrMaxSize( 0 ),
      mnMaxSliceSize( 0 ),
      mnHeaderSize( 0 ),
      mnCurrSize( 0 ),
      mnSliceSize( 0 ),
      mnPredictSize( 0 ),
      mnLastSizePos( 0 ),
      mbInRec( false )
  {
      if( mnMaxRecSize == 0 )
          mnMaxRecSize = (mrRoot.GetBiff() <= EXC_BIFF5) ? EXC_MAXRECSIZE_BIFF5 : EXC_MAXRECSIZE_BIFF8;
      mnMaxContSize = mnMaxRecSize;
  }
  
  XclExpStream::~XclExpStream()
  {
      mrStrm.Flush();
  }
  
  void XclExpStream::StartRecord( sal_uInt16 nRecId, sal_Size nRecSize )
  {
      DBG_ASSERT( !mbInRec, "XclExpStream::StartRecord - another record still open" );
      DisableEncryption();
      mnMaxContSize = mnCurrMaxSize = mnMaxRecSize;
      mnPredictSize = nRecSize;
      mbInRec = true;
      InitRecord( nRecId );
      SetSliceSize( 0 );
      EnableEncryption();
  }
  
  void XclExpStream::EndRecord()
  {
      DBG_ASSERT( mbInRec, "XclExpStream::EndRecord - no record open" );
      DisableEncryption();
      UpdateRecSize();
      mrStrm.Seek( STREAM_SEEK_TO_END );
      mbInRec = false;
  }
  
  void XclExpStream::SetSliceSize( sal_uInt16 nSize )
  {
      mnMaxSliceSize = nSize;
      mnSliceSize = 0;
  }
  
  XclExpStream& XclExpStream::operator<<( sal_Int8 nValue )
  {
      PrepareWrite( 1 );
      if (mbUseEncrypter && HasValidEncrypter())
          mxEncrypter->Encrypt(mrStrm, nValue);
      else
          mrStrm << nValue;
      return *this;
  }
  
  XclExpStream& XclExpStream::operator<<( sal_uInt8 nValue )
  {
      PrepareWrite( 1 );
      if (mbUseEncrypter && HasValidEncrypter())
          mxEncrypter->Encrypt(mrStrm, nValue);
      else
          mrStrm << nValue;
      return *this;
  }
  
  XclExpStream& XclExpStream::operator<<( sal_Int16 nValue )
  {
      PrepareWrite( 2 );
      if (mbUseEncrypter && HasValidEncrypter())
          mxEncrypter->Encrypt(mrStrm, nValue);
      else
          mrStrm << nValue;
      return *this;
  }
  
  XclExpStream& XclExpStream::operator<<( sal_uInt16 nValue )
  {
      PrepareWrite( 2 );
      if (mbUseEncrypter && HasValidEncrypter())
          mxEncrypter->Encrypt(mrStrm, nValue);
      else
          mrStrm << nValue;
      return *this;
  }
  
  XclExpStream& XclExpStream::operator<<( sal_Int32 nValue )
  {
      PrepareWrite( 4 );
      if (mbUseEncrypter && HasValidEncrypter())
          mxEncrypter->Encrypt(mrStrm, nValue);
      else
          mrStrm << nValue;
      return *this;
  }
  
  XclExpStream& XclExpStream::operator<<( sal_uInt32 nValue )
  {
      PrepareWrite( 4 );
      if (mbUseEncrypter && HasValidEncrypter())
          mxEncrypter->Encrypt(mrStrm, nValue);
      else
          mrStrm << nValue;
      return *this;
  }
  
  XclExpStream& XclExpStream::operator<<( float fValue )
  {
      PrepareWrite( 4 );
      if (mbUseEncrypter && HasValidEncrypter())
          mxEncrypter->Encrypt(mrStrm, fValue);
      else
          mrStrm << fValue;
      return *this;
  }
  
  XclExpStream& XclExpStream::operator<<( double fValue )
  {
      PrepareWrite( 8 );
      if (mbUseEncrypter && HasValidEncrypter())
          mxEncrypter->Encrypt(mrStrm, fValue);
      else
          mrStrm << fValue;
      return *this;
  }
  
  sal_Size XclExpStream::Write( const void* pData, sal_Size nBytes )
  {
      sal_Size nRet = 0;
      if( pData && (nBytes > 0) )
      {
          if( mbInRec )
          {
              const sal_uInt8* pBuffer = reinterpret_cast< const sal_uInt8* >( pData );
              sal_Size nBytesLeft = nBytes;
              bool bValid = true;
  
              while( bValid && (nBytesLeft > 0) )
              {
                  sal_Size nWriteLen = ::std::min< sal_Size >( PrepareWrite(), nBytesLeft );
                  sal_Size nWriteRet = nWriteLen;
                  if (mbUseEncrypter && HasValidEncrypter())
                  {
                      DBG_ASSERT(nWriteLen > 0, "XclExpStream::Write: write length is 0!");
                      vector<sal_uInt8> aBytes(nWriteLen);
                      memcpy(&aBytes[0], pBuffer, nWriteLen);
                      mxEncrypter->EncryptBytes(mrStrm, aBytes);
                      // TODO: How do I check if all the bytes have been successfully written ?
                  }
                  else
                  {
                      nWriteRet = mrStrm.Write( pBuffer, nWriteLen );
                  bValid = (nWriteLen == nWriteRet);
                  DBG_ASSERT( bValid, "XclExpStream::Write - stream write error" );
                  }
                  pBuffer += nWriteRet;
                  nRet += nWriteRet;
                  nBytesLeft -= nWriteRet;
                  UpdateSizeVars( nWriteRet );
              }
          }
          else
              nRet = mrStrm.Write( pData, nBytes );
      }
      return nRet;
  }
  
  void XclExpStream::WriteZeroBytes( sal_Size nBytes )
  {
      if( mbInRec )
      {
          sal_Size nBytesLeft = nBytes;
          while( nBytesLeft > 0 )
          {
              sal_Size nWriteLen = ::std::min< sal_Size >( PrepareWrite(), nBytesLeft );
              WriteRawZeroBytes( nWriteLen );
              nBytesLeft -= nWriteLen;
              UpdateSizeVars( nWriteLen );
          }
      }
      else
          WriteRawZeroBytes( nBytes );
  }
  
  void XclExpStream::WriteZeroBytesToRecord( sal_Size nBytes )
  {
      if (!mbInRec)
          // not in record.
          return;
  
      sal_uInt8 nZero = 0;
      for (sal_Size i = 0; i < nBytes; ++i)
          *this << nZero;
  }
  
  sal_Size XclExpStream::CopyFromStream( SvStream& rInStrm, sal_Size nBytes )
  {
      sal_Size nStrmPos = rInStrm.Tell();
      rInStrm.Seek( STREAM_SEEK_TO_END );
      sal_Size nStrmSize = rInStrm.Tell();
      rInStrm.Seek( nStrmPos );
  
      sal_Size nBytesLeft = ::std::min( nBytes, nStrmSize - nStrmPos );
      sal_Size nRet = 0;
      if( nBytesLeft > 0 )
      {
          const sal_Size nMaxBuffer = 4096;
          sal_uInt8* pBuffer = new sal_uInt8[ ::std::min( nBytesLeft, nMaxBuffer ) ];
          bool bValid = true;
  
          while( bValid && (nBytesLeft > 0) )
          {
              sal_Size nWriteLen = ::std::min( nBytesLeft, nMaxBuffer );
              rInStrm.Read( pBuffer, nWriteLen );
              sal_Size nWriteRet = Write( pBuffer, nWriteLen );
              bValid = (nWriteLen == nWriteRet);
              nRet += nWriteRet;
              nBytesLeft -= nWriteRet;
          }
          delete[] pBuffer;
      }
      return nRet;
  }
  
  //UNUSED2008-05  void XclExpStream::WriteUnicodeBuffer( const sal_uInt16* pBuffer, sal_Size nChars, sal_uInt8 nFlags )
  //UNUSED2008-05  {
  //UNUSED2008-05      SetSliceSize( 0 );
  //UNUSED2008-05      if( pBuffer && (nChars > 0) )
  //UNUSED2008-05      {
  //UNUSED2008-05          sal_uInt16 nCharLen = (nFlags & EXC_STRF_16BIT) ? 2 : 1;
  //UNUSED2008-05          for( sal_Size nIndex = 0; nIndex < nChars; ++nIndex )
  //UNUSED2008-05          {
  //UNUSED2008-05              if( mbInRec && (mnCurrSize + nCharLen > mnCurrMaxSize) )
  //UNUSED2008-05              {
  //UNUSED2008-05                  StartContinue();
  //UNUSED2008-05                  // repeat only 16bit flag
  //UNUSED2008-05                  operator<<( static_cast< sal_uInt8 >( nFlags & EXC_STRF_16BIT ) );
  //UNUSED2008-05              }
  //UNUSED2008-05              if( nCharLen == 2 )
  //UNUSED2008-05                  operator<<( pBuffer[ nIndex ] );
  //UNUSED2008-05              else
  //UNUSED2008-05                  operator<<( static_cast< sal_uInt8 >( pBuffer[ nIndex ] ) );
  //UNUSED2008-05          }
  //UNUSED2008-05      }
  //UNUSED2008-05  }
  
  void XclExpStream::WriteUnicodeBuffer( const ScfUInt16Vec& rBuffer, sal_uInt8 nFlags )
  {
      SetSliceSize( 0 );
      nFlags &= EXC_STRF_16BIT;   // repeat only 16bit flag
      sal_uInt16 nCharLen = nFlags ? 2 : 1;
  
      ScfUInt16Vec::const_iterator aEnd = rBuffer.end();
      for( ScfUInt16Vec::const_iterator aIter = rBuffer.begin(); aIter != aEnd; ++aIter )
      {
          if( mbInRec && (mnCurrSize + nCharLen > mnCurrMaxSize) )
          {
              StartContinue();
              operator<<( nFlags );
          }
          if( nCharLen == 2 )
              operator<<( *aIter );
          else
              operator<<( static_cast< sal_uInt8 >( *aIter ) );
      }
  }
  
  //UNUSED2008-05  void XclExpStream::WriteByteStringBuffer( const ByteString& rString, sal_uInt16 nMaxLen )
  //UNUSED2008-05  {
  //UNUSED2008-05      SetSliceSize( 0 );
  //UNUSED2008-05      Write( rString.GetBuffer(), ::std::min< sal_Size >( rString.Len(), nMaxLen ) );
  //UNUSED2008-05  }
  
  // ER: #71367# Xcl has an obscure sense of whether starting a new record or not,
  // and crashes if it encounters the string header at the very end of a record.
  // Thus we add 1 to give some room, seems like they do it that way but with another count (10?)
  void XclExpStream::WriteByteString( const ByteString& rString, sal_uInt16 nMaxLen, bool b16BitCount )
  {
      SetSliceSize( 0 );
      sal_Size nLen = ::std::min< sal_Size >( rString.Len(), nMaxLen );
      if( !b16BitCount )
          nLen = ::std::min< sal_Size >( nLen, 0xFF );
  
      sal_uInt16 nLeft = PrepareWrite();
      sal_uInt16 nLenFieldSize = b16BitCount ? 2 : 1;
      if( mbInRec && (nLeft <= nLenFieldSize) )
          StartContinue();
  
      if( b16BitCount )
          operator<<( static_cast< sal_uInt16 >( nLen ) );
      else
          operator<<( static_cast< sal_uInt8 >( nLen ) );
      Write( rString.GetBuffer(), nLen );
  }
  
  void XclExpStream::WriteCharBuffer( const ScfUInt8Vec& rBuffer )
  {
      SetSliceSize( 0 );
      Write( &rBuffer[ 0 ], rBuffer.size() );
  }
  
  void XclExpStream::SetEncrypter( XclExpEncrypterRef xEncrypter )
  {
      mxEncrypter = xEncrypter;
  }
  
  bool XclExpStream::HasValidEncrypter() const
  {
      return mxEncrypter.is() && mxEncrypter->IsValid();
  }
  
  void XclExpStream::EnableEncryption( bool bEnable )
  {
      mbUseEncrypter = bEnable && HasValidEncrypter();
  }
  
  void XclExpStream::DisableEncryption()
  {
      EnableEncryption(false);
  }
  
  sal_Size XclExpStream::SetSvStreamPos( sal_Size nPos )
  {
      DBG_ASSERT( !mbInRec, "XclExpStream::SetSvStreamPos - not allowed inside of a record" );
      return mbInRec ? 0 : mrStrm.Seek( nPos );
  }
  
  // private --------------------------------------------------------------------
  
  void XclExpStream::InitRecord( sal_uInt16 nRecId )
  {
      mrStrm.Seek( STREAM_SEEK_TO_END );
      mrStrm << nRecId;
  
      mnLastSizePos = mrStrm.Tell();
      mnHeaderSize = static_cast< sal_uInt16 >( ::std::min< sal_Size >( mnPredictSize, mnCurrMaxSize ) );
      mrStrm << mnHeaderSize;
      mnCurrSize = mnSliceSize = 0;
  }
  
  void XclExpStream::UpdateRecSize()
  {
      if( mnCurrSize != mnHeaderSize )
      {
          mrStrm.Seek( mnLastSizePos );
          mrStrm << mnCurrSize;
      }
  }
  
  void XclExpStream::UpdateSizeVars( sal_Size nSize )
  {
      DBG_ASSERT( mnCurrSize + nSize <= mnCurrMaxSize, "XclExpStream::UpdateSizeVars - record overwritten" );
      mnCurrSize = mnCurrSize + static_cast< sal_uInt16 >( nSize );
  
      if( mnMaxSliceSize > 0 )
      {
          DBG_ASSERT( mnSliceSize + nSize <= mnMaxSliceSize, "XclExpStream::UpdateSizeVars - slice overwritten" );
          mnSliceSize = mnSliceSize + static_cast< sal_uInt16 >( nSize );
          if( mnSliceSize >= mnMaxSliceSize )
              mnSliceSize = 0;
      }
  }
  
  void XclExpStream::StartContinue()
  {
      UpdateRecSize();
      mnCurrMaxSize = mnMaxContSize;
      mnPredictSize -= mnCurrSize;
      InitRecord( EXC_ID_CONT );
  }
  
  void XclExpStream::PrepareWrite( sal_uInt16 nSize )
  {
      if( mbInRec )
      {
          if( (mnCurrSize + nSize > mnCurrMaxSize) ||
              ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
              StartContinue();
          UpdateSizeVars( nSize );
      }
  }
  
  sal_uInt16 XclExpStream::PrepareWrite()
  {
      sal_uInt16 nRet = 0;
      if( mbInRec )
      {
          if( (mnCurrSize >= mnCurrMaxSize) ||
              ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
              StartContinue();
          UpdateSizeVars( 0 );
  
          nRet = (mnMaxSliceSize > 0) ? (mnMaxSliceSize - mnSliceSize) : (mnCurrMaxSize - mnCurrSize);
      }
      return nRet;
  }
  
  void XclExpStream::WriteRawZeroBytes( sal_Size nBytes )
  {
      const sal_uInt32 nData = 0;
      sal_Size nBytesLeft = nBytes;
      while( nBytesLeft >= sizeof( nData ) )
      {
          mrStrm << nData;
          nBytesLeft -= sizeof( nData );
      }
      if( nBytesLeft )
          mrStrm.Write( &nData, nBytesLeft );
  }
  
  // ============================================================================
  
  XclExpBiff8Encrypter::XclExpBiff8Encrypter( const XclExpRoot& rRoot ) :
      mrRoot(rRoot),
      mnOldPos(STREAM_SEEK_TO_END),
      mbValid(false)
  {
      Sequence< NamedValue > aEncryptionData = rRoot.GetEncryptionData();
      if( !aEncryptionData.hasElements() )
          // Empty password.  Get the default biff8 password.
          aEncryptionData = rRoot.GenerateDefaultEncryptionData();
      Init( aEncryptionData );
  }
  
  XclExpBiff8Encrypter::~XclExpBiff8Encrypter()
  {
  }
  
  bool XclExpBiff8Encrypter::IsValid() const
  {
      return mbValid;
  }
  
  void XclExpBiff8Encrypter::GetSaltDigest( sal_uInt8 pnSaltDigest[16] ) const
  {
      if ( sizeof( mpnSaltDigest ) == 16 )
          memcpy( pnSaltDigest, mpnSaltDigest, 16 );
  }
  
  void XclExpBiff8Encrypter::GetSalt( sal_uInt8 pnSalt[16] ) const
  {
      if ( sizeof( mpnSalt ) == 16 )
          memcpy( pnSalt, mpnSalt, 16 );
  }
  
  void XclExpBiff8Encrypter::GetDocId( sal_uInt8 pnDocId[16] ) const
  {
      if ( sizeof( mpnDocId ) == 16 )
      memcpy( pnDocId, mpnDocId, 16 );
  }
  
  void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt8 nData )
  {
      vector<sal_uInt8> aByte(1);
      aByte[0] = nData;
      EncryptBytes(rStrm, aByte);
  }
  
  void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt16 nData )
  {
      ::std::vector<sal_uInt8> pnBytes(2);
      pnBytes[0] = nData & 0xFF;
      pnBytes[1] = (nData >> 8) & 0xFF;
      EncryptBytes(rStrm, pnBytes);
  }
  
  void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt32 nData )
  {
      ::std::vector<sal_uInt8> pnBytes(4);
      pnBytes[0] = nData & 0xFF;
      pnBytes[1] = (nData >>  8) & 0xFF;
      pnBytes[2] = (nData >> 16) & 0xFF;
      pnBytes[3] = (nData >> 24) & 0xFF;
      EncryptBytes(rStrm, pnBytes);
  }
  
  void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, float fValue )
  {
      ::std::vector<sal_uInt8> pnBytes(4);
      memcpy(&pnBytes[0], &fValue, 4);
      EncryptBytes(rStrm, pnBytes);
  }
  
  void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, double fValue )
  {
      ::std::vector<sal_uInt8> pnBytes(8);
      memcpy(&pnBytes[0], &fValue, 8);
      EncryptBytes(rStrm, pnBytes);
  }
  
  void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int8 nData )
  {
      Encrypt(rStrm, static_cast<sal_uInt8>(nData));
  }
  
  void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int16 nData )
  {
      Encrypt(rStrm, static_cast<sal_uInt16>(nData));
  }
  
  void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int32 nData )
  {
      Encrypt(rStrm, static_cast<sal_uInt32>(nData));
  }
  
  void XclExpBiff8Encrypter::Init( const Sequence< NamedValue >& rEncryptionData )
  {
      mbValid = false;
  
      if( maCodec.InitCodec( rEncryptionData ) )
      {
          maCodec.GetDocId( mpnDocId );
  
          // generate the salt here
          TimeValue aTime;
          osl_getSystemTime( &aTime );
          rtlRandomPool aRandomPool = rtl_random_createPool ();
          rtl_random_addBytes( aRandomPool, &aTime, 8 ); 
          rtl_random_getBytes( aRandomPool, mpnSalt, 16 );
          rtl_random_destroyPool( aRandomPool );
  
          memset( mpnSaltDigest, 0, sizeof( mpnSaltDigest ) );
  
          // generate salt hash.
          ::msfilter::MSCodec_Std97 aCodec;
          aCodec.InitCodec( rEncryptionData );
          aCodec.CreateSaltDigest( mpnSalt, mpnSaltDigest );
  
          // verify to make sure it's in good shape.
          mbValid = maCodec.VerifyKey( mpnSalt, mpnSaltDigest );
      }
  }
  
  sal_uInt32 XclExpBiff8Encrypter::GetBlockPos( sal_Size nStrmPos ) const
  {
      return static_cast< sal_uInt32 >( nStrmPos / EXC_ENCR_BLOCKSIZE );
  }
  
  sal_uInt16 XclExpBiff8Encrypter::GetOffsetInBlock( sal_Size nStrmPos ) const
  {
      return static_cast< sal_uInt16 >( nStrmPos % EXC_ENCR_BLOCKSIZE );
  }
  
  void XclExpBiff8Encrypter::EncryptBytes( SvStream& rStrm, vector<sal_uInt8>& aBytes )
  {
      sal_Size nStrmPos = rStrm.Tell();
      sal_uInt16 nBlockOffset = GetOffsetInBlock(nStrmPos);
      sal_uInt32 nBlockPos = GetBlockPos(nStrmPos);
  
  #if DEBUG_XL_ENCRYPTION
      fprintf(stdout, "XclExpBiff8Encrypter::EncryptBytes: stream pos = %ld  offset in block = %d  block pos = %ld\n",
              nStrmPos, nBlockOffset, nBlockPos);
  #endif
  
      sal_uInt16 nSize = static_cast< sal_uInt16 >( aBytes.size() );
      if (nSize == 0)
          return;
  
  #if DEBUG_XL_ENCRYPTION
      fprintf(stdout, "RAW: ");
      for (sal_uInt16 i = 0; i < nSize; ++i)
          fprintf(stdout, "%2.2X ", aBytes[i]);
      fprintf(stdout, "\n");
  #endif
  
      if (mnOldPos != nStrmPos)
      {
          sal_uInt16 nOldOffset = GetOffsetInBlock(mnOldPos);
          sal_uInt32 nOldBlockPos = GetBlockPos(mnOldPos);
  
          if ( (nBlockPos != nOldBlockPos) || (nBlockOffset < nOldOffset) )
          {
              maCodec.InitCipher(nBlockPos);
              nOldOffset = 0;
          }
  
          if (nBlockOffset > nOldOffset)
              maCodec.Skip(nBlockOffset - nOldOffset);
      }
  
      sal_uInt16 nBytesLeft = nSize;
      sal_uInt16 nPos = 0;
      while (nBytesLeft > 0)
      {
          sal_uInt16 nBlockLeft = EXC_ENCR_BLOCKSIZE - nBlockOffset;
          sal_uInt16 nEncBytes = ::std::min(nBlockLeft, nBytesLeft);
  
          bool bRet = maCodec.Encode(&aBytes[nPos], nEncBytes, &aBytes[nPos], nEncBytes);
          DBG_ASSERT(bRet, "XclExpBiff8Encrypter::EncryptBytes: encryption failed!!");
          bRet = bRet; // to remove a silly compiler warning.
  
          sal_Size nRet = rStrm.Write(&aBytes[nPos], nEncBytes);
          DBG_ASSERT(nRet == nEncBytes, "XclExpBiff8Encrypter::EncryptBytes: fail to write to stream!!");
          nRet = nRet; // to remove a silly compiler warning.
  
          nStrmPos = rStrm.Tell();
          nBlockOffset = GetOffsetInBlock(nStrmPos);
          nBlockPos = GetBlockPos(nStrmPos);
          if (nBlockOffset == 0)
              maCodec.InitCipher(nBlockPos);
  
          nBytesLeft -= nEncBytes;
          nPos += nEncBytes;
      }
      mnOldPos = nStrmPos;
  }
  
  OUString XclXmlUtils::GetStreamName( const char* sStreamDir, const char* sStream, sal_Int32 nId )
  {
      OUStringBuffer sBuf;
      if( sStreamDir )
          sBuf.appendAscii( sStreamDir );
      sBuf.appendAscii( sStream );
      if( nId )
          sBuf.append( nId );
      sBuf.appendAscii( ".xml" );
      return sBuf.makeStringAndClear();
  }
  
  OString XclXmlUtils::ToOString( const Color& rColor )
  {
      char buf[9];
      sprintf( buf, "%.2X%.2X%.2X%.2X", rColor.GetTransparency(), rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
      buf[8] = '\0';
      return OString( buf );
  }
  
  OString XclXmlUtils::ToOString( const OUString& s )
  {
      return OUStringToOString( s, RTL_TEXTENCODING_UTF8  );
  }
  
  OString XclXmlUtils::ToOString( const String& s )
  {
      return OString( s.GetBuffer(), s.Len(), RTL_TEXTENCODING_UTF8 );
  }
  
  OString XclXmlUtils::ToOString( const ScAddress& rAddress )
  {
      String sAddress;
      rAddress.Format( sAddress, SCA_VALID, NULL, ScAddress::Details( FormulaGrammar::CONV_XL_A1 ) );
      return ToOString( sAddress );
  }
  
  OString XclXmlUtils::ToOString( const ScfUInt16Vec& rBuffer )
  {
      const sal_uInt16* pBuffer = &rBuffer [0];
      return OString( pBuffer, rBuffer.size(), RTL_TEXTENCODING_UTF8 );
  }
  
  OString XclXmlUtils::ToOString( const ScRange& rRange )
  {
      String sRange;
      rRange.Format( sRange, SCA_VALID, NULL, ScAddress::Details( FormulaGrammar::CONV_XL_A1 ) );
      return ToOString( sRange );
  }
  
  OString XclXmlUtils::ToOString( const ScRangeList& rRangeList )
  {
      String s;
      rRangeList.Format( s, SCA_VALID, NULL, FormulaGrammar::CONV_XL_A1, ' ' );
      return ToOString( s );
  }
  
  static ScAddress lcl_ToAddress( const XclAddress& rAddress )
  {
      ScAddress aAddress;
  
      // For some reason, ScRange::Format() returns omits row numbers if
      // the row is >= MAXROW or the column is >= MAXCOL, and Excel doesn't
      // like "A:IV" (i.e. no row numbers).  Prevent this.
      aAddress.SetRow( std::min<sal_Int32>( rAddress.mnRow, MAXROW-1 ) );
      aAddress.SetCol( static_cast<sal_Int16>(std::min<sal_Int32>( rAddress.mnCol, MAXCOL-1 )) );
  
      return aAddress;
  }
  
  OString XclXmlUtils::ToOString( const XclAddress& rAddress )
  {
      return ToOString( lcl_ToAddress( rAddress ) );
  }
  
  OString XclXmlUtils::ToOString( const XclExpString& s )
  {
      DBG_ASSERT( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
      return ToOString( s.GetUnicodeBuffer() );
  }
  
  static ScRange lcl_ToRange( const XclRange& rRange )
  {
      ScRange aRange;
  
      aRange.aStart = lcl_ToAddress( rRange.maFirst );
      aRange.aEnd   = lcl_ToAddress( rRange.maLast );
  
      return aRange;
  }
  
  OString XclXmlUtils::ToOString( const XclRangeList& rRanges )
  {
      ScRangeList aRanges;
      for( XclRangeList::const_iterator i = rRanges.begin(), end = rRanges.end();
              i != end; ++i )
      {
          aRanges.Append( lcl_ToRange( *i ) );
      }
      return ToOString( aRanges );
  }
  
  OUString XclXmlUtils::ToOUString( const char* s )
  {
      return OUString( s, (sal_Int32) strlen( s ), RTL_TEXTENCODING_ASCII_US );
  }
  
  OUString XclXmlUtils::ToOUString( const ScfUInt16Vec& rBuf, sal_Int32 nStart, sal_Int32 nLength )
  {
      if( nLength == -1 )
          nLength = rBuf.size();
  
      return OUString( &rBuf[nStart], nLength );
  }
  
  OUString XclXmlUtils::ToOUString( const String& s )
  {
      return OUString( s.GetBuffer(), s.Len() );
  }
  
  OUString XclXmlUtils::ToOUString( ScDocument& rDocument, const ScAddress& rAddress, ScTokenArray* pTokenArray )
  {
      ScCompiler aCompiler( &rDocument, rAddress, *pTokenArray);
      aCompiler.SetGrammar(FormulaGrammar::GRAM_NATIVE_XL_A1);
      String s;
      aCompiler.CreateStringFromTokenArray( s );
      return ToOUString( s );
  }
  
  OUString XclXmlUtils::ToOUString( const XclExpString& s )
  {
      DBG_ASSERT( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
      return ToOUString( s.GetUnicodeBuffer() );
  }
  
  const char* XclXmlUtils::ToPsz( bool b )
  {
      return b ? "true" : "false";
  }
  
  // ============================================================================
  
  XclExpXmlStream::XclExpXmlStream( const Reference< XComponentContext >& rxContext, SvStream& rStrm, const XclExpRoot& rRoot )
      : XmlFilterBase( rxContext )
      , mrRoot( rRoot )
  {
      Sequence< PropertyValue > aArgs( 1 );
      const OUString sStream( RTL_CONSTASCII_USTRINGPARAM( "StreamForOutput" ) );
      aArgs[0].Name  = sStream;
      aArgs[0].Value <<= Reference< XStream > ( new OStreamWrapper( rStrm ) );
  
      XServiceInfo* pInfo = rRoot.GetDocModelObj();
      Reference< XComponent > aComponent( pInfo, UNO_QUERY );
      setSourceDocument( aComponent );
      filter( aArgs );
  
      PushStream( CreateOutputStream(
                  OUString::createFromAscii( "xl/workbook.xml" ),
                  OUString::createFromAscii( "xl/workbook.xml" ),
                  Reference< XOutputStream >(),
                  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
                  "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" ) );
  }
  
  XclExpXmlStream::~XclExpXmlStream()
  {
  }
  
  sax_fastparser::FSHelperPtr& XclExpXmlStream::GetCurrentStream()
  {
      DBG_ASSERT( !maStreams.empty(), "XclExpXmlStream::GetCurrentStream - no current stream" );
      return maStreams.top();
  }
  
  void XclExpXmlStream::PushStream( sax_fastparser::FSHelperPtr aStream )
  {
      maStreams.push( aStream );
  }
  
  void XclExpXmlStream::PopStream()
  {
      DBG_ASSERT( !maStreams.empty(), "XclExpXmlStream::PopStream - stack is empty!" );
      maStreams.pop();
  }
  
  OUString XclExpXmlStream::GetIdForPath( const OUString& sPath )
  {
      if( maOpenedStreamMap.find( sPath ) == maOpenedStreamMap.end() )
          return OUString();
      return maOpenedStreamMap[ sPath ].first;
  }
  
  sax_fastparser::FSHelperPtr XclExpXmlStream::GetStreamForPath( const OUString& sPath )
  {
      if( maOpenedStreamMap.find( sPath ) == maOpenedStreamMap.end() )
          return sax_fastparser::FSHelperPtr();
      return maOpenedStreamMap[ sPath ].second;
  }
  
  sax_fastparser::FSHelperPtr& XclExpXmlStream::WriteAttributes( sal_Int32 nAttribute, ... )
  {
      sax_fastparser::FSHelperPtr& rStream = GetCurrentStream();
  
      va_list args;
      va_start( args, nAttribute );
      do {
          const char* pValue = va_arg( args, const char* );
          if( pValue )
          {
              rStream->write( " " )
                  ->writeId( nAttribute )
                  ->write( "=\"" )
                  ->writeEscaped( pValue )
                  ->write( "\"" );
          }
  
          nAttribute = va_arg( args, sal_Int32 );
          if( nAttribute == FSEND )
              break;
      } while( true );
      va_end( args );
  
      return rStream;
  }
  
  static void lcl_WriteValue( sax_fastparser::FSHelperPtr& rStream, sal_Int32 nElement, const char* pValue )
  {
      if( !pValue )
          return;
      rStream->singleElement( nElement,
              XML_val, pValue,
              FSEND );
  }
  
  static const char* lcl_GetUnderlineStyle( FontUnderline eUnderline, bool& bHaveUnderline )
  {
      bHaveUnderline = true;
      switch( eUnderline )
      {
          // OOXTODO: doubleAccounting, singleAccounting
          // OOXTODO: what should be done with the other FontUnderline values?
          case UNDERLINE_SINGLE:  return "single";
          case UNDERLINE_DOUBLE:  return "double";
          case UNDERLINE_NONE:
          default:                bHaveUnderline = false; return "none";
      }
  }
  
  static const char* lcl_ToVerticalAlignmentRun( SvxEscapement eEscapement, bool& bHaveAlignment )
  {
      bHaveAlignment = true;
      switch( eEscapement )
      {
          case SVX_ESCAPEMENT_SUPERSCRIPT:    return "superscript";
          case SVX_ESCAPEMENT_SUBSCRIPT:      return "subscript";
          case SVX_ESCAPEMENT_OFF:
          default:                            bHaveAlignment = false; return "baseline";
      }
  }
  
  sax_fastparser::FSHelperPtr& XclExpXmlStream::WriteFontData( const XclFontData& rFontData, sal_Int32 nFontId )
  {
      bool bHaveUnderline, bHaveVertAlign;
      const char* pUnderline = lcl_GetUnderlineStyle( rFontData.GetScUnderline(), bHaveUnderline );
      const char* pVertAlign = lcl_ToVerticalAlignmentRun( rFontData.GetScEscapement(), bHaveVertAlign );
  
      sax_fastparser::FSHelperPtr& rStream = GetCurrentStream();
  
      lcl_WriteValue( rStream, nFontId,        XclXmlUtils::ToOString( rFontData.maName ).getStr() );
      lcl_WriteValue( rStream, XML_charset,    rFontData.mnCharSet != 0 ? OString::valueOf( (sal_Int32) rFontData.mnCharSet ).getStr() : NULL );
      lcl_WriteValue( rStream, XML_family,     OString::valueOf( (sal_Int32) rFontData.mnFamily ).getStr() );
      lcl_WriteValue( rStream, XML_b,          rFontData.mnWeight > 400 ? XclXmlUtils::ToPsz( rFontData.mnWeight > 400 ) : NULL );
      lcl_WriteValue( rStream, XML_i,          rFontData.mbItalic ? XclXmlUtils::ToPsz( rFontData.mbItalic ) : NULL );
      lcl_WriteValue( rStream, XML_strike,     rFontData.mbStrikeout ? XclXmlUtils::ToPsz( rFontData.mbStrikeout ) : NULL );
      lcl_WriteValue( rStream, XML_outline,    rFontData.mbOutline ? XclXmlUtils::ToPsz( rFontData.mbOutline ) : NULL );
      lcl_WriteValue( rStream, XML_shadow,     rFontData.mbShadow ? XclXmlUtils::ToPsz( rFontData.mbShadow ) : NULL );
      // OOXTODO: lcl_WriteValue( rStream, XML_condense, );    // mac compatibility setting
      // OOXTODO: lcl_WriteValue( rStream, XML_extend, );      // compatibility setting
      if( rFontData.maColor != Color( 0xFF, 0xFF, 0xFF, 0xFF ) )
          rStream->singleElement( XML_color,
                  // OOXTODO: XML_auto,       bool
                  // OOXTODO: XML_indexed,    uint
                  XML_rgb,    XclXmlUtils::ToOString( rFontData.maColor ).getStr(),
                  // OOXTODO: XML_theme,      index into <clrScheme/>
                  // OOXTODO: XML_tint,       double
                  FSEND );
      lcl_WriteValue( rStream, XML_sz,         OString::valueOf( (double) (rFontData.mnHeight / 20.0) ).getStr() );  // Twips->Points
      lcl_WriteValue( rStream, XML_u,          bHaveUnderline ? pUnderline : NULL );
      lcl_WriteValue( rStream, XML_vertAlign,  bHaveVertAlign ? pVertAlign : NULL );
  
      return rStream;
  }
  
  sax_fastparser::FSHelperPtr XclExpXmlStream::CreateOutputStream (
      const OUString& sFullStream,
      const OUString& sRelativeStream,
      const Reference< XOutputStream >& xParentRelation,
      const char* sContentType,
      const char* sRelationshipType,
      OUString* pRelationshipId )
  {
      OUString sRelationshipId;
      if (xParentRelation.is())
          sRelationshipId = addRelation( xParentRelation, OUString::createFromAscii( sRelationshipType), sRelativeStream );
      else
          sRelationshipId = addRelation( OUString::createFromAscii( sRelationshipType ), sRelativeStream );
  
      if( pRelationshipId )
          *pRelationshipId = sRelationshipId;
  
      sax_fastparser::FSHelperPtr p = openFragmentStreamWithSerializer( sFullStream, OUString::createFromAscii( sContentType ) );
  
      maOpenedStreamMap[ sFullStream ] = std::make_pair( sRelationshipId, p );
  
      return p;
  }
  
  bool XclExpXmlStream::importDocument() throw()
  {
      return false;
  }
  
  oox::vml::Drawing* XclExpXmlStream::getVmlDrawing()
  {
      return 0;
  }
  
  const oox::drawingml::Theme* XclExpXmlStream::getCurrentTheme() const
  {
      return 0;
  }
  
  const oox::drawingml::table::TableStyleListPtr XclExpXmlStream::getTableStyles()
  {
      return oox::drawingml::table::TableStyleListPtr();
  }
  
  oox::drawingml::chart::ChartConverter& XclExpXmlStream::getChartConverter()
  {
      // DO NOT CALL
      return * (oox::drawingml::chart::ChartConverter*) NULL;
  }
  
  bool XclExpXmlStream::exportDocument() throw()
  {
      return false;
  }
  
  ::oox::ole::VbaProject* XclExpXmlStream::implCreateVbaProject() const
  {
      return new ::oox::xls::ExcelVbaProject( getComponentContext(), Reference< XSpreadsheetDocument >( getModel(), UNO_QUERY ) );
  }
  
  OUString XclExpXmlStream::implGetImplementationName() const
  {
      return CREATE_OUSTRING( "TODO" );
  }
  
  void XclExpXmlStream::Trace( const char* format, ...)
  {
      va_list ap;
      va_start( ap, format );
      vfprintf( stderr, format, ap );
      va_end( ap );
  }
  
  