1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <com/sun/star/embed/XStorage.hpp>
24 #include <com/sun/star/embed/ElementModes.hpp>
25 #include <com/sun/star/beans/XPropertySet.hpp>
27 #include <osl/file.hxx>
28 #include <sot/stg.hxx>
29 #include <sot/storinfo.hxx>
30 #include <sot/storage.hxx>
31 #include <sot/formats.hxx>
32 #include <sot/exchange.hxx>
33 #include <unotools/ucbstreamhelper.hxx>
34 #include <comphelper/diagnose_ex.hxx>
35 #include <tools/debug.hxx>
36 #include <tools/urlobj.hxx>
37 #include <unotools/ucbhelper.hxx>
38 #include <comphelper/fileformat.h>
39 #include <com/sun/star/uno/Reference.h>
43 using namespace ::com::sun::star
;
45 static SvLockBytesRef
MakeLockBytes_Impl( const OUString
& rName
, StreamMode nMode
)
48 if( !rName
.isEmpty() )
50 SvStream
* pFileStm
= new SvFileStream( rName
, nMode
);
51 xLB
= new SvLockBytes( pFileStm
, true );
55 SvStream
* pCacheStm
= new SvMemoryStream();
56 xLB
= new SvLockBytes( pCacheStm
, true );
61 SotTempStream::SotTempStream( const OUString
& rName
, StreamMode nMode
)
62 : SvStream( MakeLockBytes_Impl( rName
, nMode
).get() )
64 if( nMode
& StreamMode::WRITE
)
70 SotTempStream::~SotTempStream()
75 void SotTempStream::CopyTo( SotTempStream
* pDestStm
)
77 FlushBuffer(); // write all data
79 sal_uInt64 nPos
= Tell(); // save position
81 pDestStm
->SetSize( 0 ); // empty target stream
83 constexpr int BUFSIZE
= 64 * 1024;
84 std::unique_ptr
<sal_uInt8
[]> pMem(new sal_uInt8
[ BUFSIZE
]);
86 while (0 != (nRead
= ReadBytes(pMem
.get(), BUFSIZE
)))
88 if (nRead
!= static_cast<sal_Int32
>(pDestStm
->WriteBytes(pMem
.get(), nRead
)))
90 SetError( SVSTREAM_GENERALERROR
);
97 pDestStm
->Seek( nPos
);
101 SotStorageStream::SotStorageStream( BaseStorageStream
* pStm
)
105 if( StreamMode::WRITE
& pStm
->GetMode() )
108 m_isWritable
= false;
110 SetError( pStm
->GetError() );
114 SotStorageStream::~SotStorageStream()
120 void SotStorageStream::ResetError()
122 SvStream::ResetError();
123 pOwnStm
->ResetError();
126 std::size_t SotStorageStream::GetData(void* pData
, std::size_t const nSize
)
128 std::size_t nRet
= pOwnStm
->Read( pData
, nSize
);
129 SetError( pOwnStm
->GetError() );
133 std::size_t SotStorageStream::PutData(const void* pData
, std::size_t const nSize
)
135 std::size_t nRet
= pOwnStm
->Write( pData
, nSize
);
136 SetError( pOwnStm
->GetError() );
140 sal_uInt64
SotStorageStream::SeekPos(sal_uInt64 nPos
)
142 sal_uInt64 nRet
= pOwnStm
->Seek( nPos
);
143 SetError( pOwnStm
->GetError() );
147 void SotStorageStream::FlushData()
150 SetError( pOwnStm
->GetError() );
153 void SotStorageStream::SetSize(sal_uInt64
const nNewSize
)
155 sal_uInt64
const nPos
= Tell();
156 pOwnStm
->SetSize( nNewSize
);
157 SetError( pOwnStm
->GetError() );
159 if( nNewSize
< nPos
)
164 sal_uInt32
SotStorageStream::GetSize() const
166 sal_uInt64 nSize
= const_cast<SotStorageStream
*>(this)->TellEnd();
170 sal_uInt64
SotStorageStream::TellEnd()
172 // Need to flush the buffer so we materialise the stream and return the correct answer
173 // otherwise we return a 0 value from StgEntry::GetSize
176 return pOwnStm
->GetSize();
179 void SotStorageStream::Commit()
182 if( pOwnStm
->GetError() == ERRCODE_NONE
)
184 SetError( pOwnStm
->GetError() );
187 bool SotStorageStream::SetProperty( const OUString
& rName
, const css::uno::Any
& rValue
)
189 UCBStorageStream
* pStg
= dynamic_cast<UCBStorageStream
*>( pOwnStm
);
192 return pStg
->SetProperty( rName
, rValue
);
196 OSL_FAIL("Not implemented!");
202 * SotStorage::SotStorage()
204 * A I... object must be passed to SvObject, because otherwise itself will
205 * create and define an IUnknown, so that all other I... objects would be
206 * destroyed with delete (Owner() == true).
207 * But IStorage objects are only used and not implemented by ourselves,
208 * therefore we pretend the IStorage object was passed from the outside
209 * and it will be freed with Release().
210 * The CreateStorage methods are needed to create an IStorage object before the
211 * call of SvObject (Own, !Own automatic).
212 * If CreateStorage has created an object, then the RefCounter was already
214 * The transfer is done in pStorageCTor and the variable is NULL, if it didn't
217 #define INIT_SotStorage() \
218 : m_pOwnStg( nullptr ) \
219 , m_pStorStm( nullptr ) \
220 , m_nError( ERRCODE_NONE ) \
221 , m_bIsRoot( false ) \
222 , m_bDelStm( false ) \
223 , m_nVersion( SOFFICE_FILEFORMAT_CURRENT )
225 #define ERASEMASK ( StreamMode::TRUNC | StreamMode::WRITE | StreamMode::SHARE_DENYALL )
227 SotStorage::SotStorage( const OUString
& rName
, StreamMode nMode
)
230 m_aName
= rName
; // save name
231 CreateStorage( true, nMode
);
232 if ( IsOLEStorage() )
233 m_nVersion
= SOFFICE_FILEFORMAT_50
;
236 void SotStorage::CreateStorage( bool bForceUCBStorage
, StreamMode nMode
)
238 DBG_ASSERT( !m_pStorStm
&& !m_pOwnStg
, "Use only in ctor!" );
239 if( !m_aName
.isEmpty() )
242 if( ( nMode
& ERASEMASK
) == ERASEMASK
)
243 ::utl::UCBContentHelper::Kill( m_aName
);
245 INetURLObject
aObj( m_aName
);
246 if ( aObj
.GetProtocol() == INetProtocol::NotValid
)
249 osl::FileBase::getFileURLFromSystemPath( m_aName
, aURL
);
251 m_aName
= aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
255 m_pStorStm
= ::utl::UcbStreamHelper::CreateStream( m_aName
, nMode
).release();
256 if ( m_pStorStm
&& m_pStorStm
->GetError() )
259 m_pStorStm
= nullptr;
264 // try as UCBStorage, next try as OLEStorage
265 bool bIsUCBStorage
= UCBStorage::IsStorageFile( m_pStorStm
);
266 if ( !bIsUCBStorage
&& bForceUCBStorage
)
267 // if UCBStorage has priority, it should not be used only if it is really an OLEStorage
268 bIsUCBStorage
= !Storage::IsStorageFile( m_pStorStm
);
272 // UCBStorage always works directly on the UCB content, so discard the stream first
274 m_pStorStm
= nullptr;
275 m_pOwnStg
= new UCBStorage( m_aName
, nMode
, true, true/*bIsRoot*/ );
279 // OLEStorage can be opened with a stream
280 m_pOwnStg
= new Storage( *m_pStorStm
, true );
284 else if ( bForceUCBStorage
)
286 m_pOwnStg
= new UCBStorage( m_aName
, nMode
, true, true/*bIsRoot*/ );
287 SetError( ERRCODE_IO_NOTSUPPORTED
);
291 m_pOwnStg
= new Storage( m_aName
, nMode
, true );
292 SetError( ERRCODE_IO_NOTSUPPORTED
);
298 if ( bForceUCBStorage
)
299 m_pOwnStg
= new UCBStorage( m_aName
, nMode
, true, true/*bIsRoot*/ );
301 m_pOwnStg
= new Storage( m_aName
, nMode
, true );
302 m_aName
= m_pOwnStg
->GetName();
305 SetError( m_pOwnStg
->GetError() );
307 SignAsRoot( m_pOwnStg
->IsRoot() );
310 SotStorage::SotStorage( bool bUCBStorage
, const OUString
& rName
, StreamMode nMode
)
314 CreateStorage( bUCBStorage
, nMode
);
315 if ( IsOLEStorage() )
316 m_nVersion
= SOFFICE_FILEFORMAT_50
;
319 SotStorage::SotStorage( BaseStorage
* pStor
)
324 m_aName
= pStor
->GetName(); // save name
325 SignAsRoot( pStor
->IsRoot() );
326 SetError( pStor
->GetError() );
330 const ErrCode nErr
= m_pOwnStg
? m_pOwnStg
->GetError() : SVSTREAM_CANNOT_MAKE
;
332 if ( IsOLEStorage() )
333 m_nVersion
= SOFFICE_FILEFORMAT_50
;
336 SotStorage::SotStorage( bool bUCBStorage
, SvStream
& rStm
)
339 SetError( rStm
.GetError() );
341 // try as UCBStorage, next try as OLEStorage
342 if ( UCBStorage::IsStorageFile( &rStm
) || bUCBStorage
)
343 m_pOwnStg
= new UCBStorage( rStm
, false );
345 m_pOwnStg
= new Storage( rStm
, false );
347 SetError( m_pOwnStg
->GetError() );
349 if ( IsOLEStorage() )
350 m_nVersion
= SOFFICE_FILEFORMAT_50
;
352 SignAsRoot( m_pOwnStg
->IsRoot() );
355 SotStorage::SotStorage( SvStream
& rStm
)
358 SetError( rStm
.GetError() );
360 // try as UCBStorage, next try as OLEStorage
361 if ( UCBStorage::IsStorageFile( &rStm
) )
362 m_pOwnStg
= new UCBStorage( rStm
, false );
364 m_pOwnStg
= new Storage( rStm
, false );
366 SetError( m_pOwnStg
->GetError() );
368 if ( IsOLEStorage() )
369 m_nVersion
= SOFFICE_FILEFORMAT_50
;
371 SignAsRoot( m_pOwnStg
->IsRoot() );
374 SotStorage::SotStorage( SvStream
* pStm
, bool bDelete
)
377 SetError( pStm
->GetError() );
379 // try as UCBStorage, next try as OLEStorage
380 if ( UCBStorage::IsStorageFile( pStm
) )
381 m_pOwnStg
= new UCBStorage( *pStm
, false );
383 m_pOwnStg
= new Storage( *pStm
, false );
385 SetError( m_pOwnStg
->GetError() );
389 if ( IsOLEStorage() )
390 m_nVersion
= SOFFICE_FILEFORMAT_50
;
392 SignAsRoot( m_pOwnStg
->IsRoot() );
395 SotStorage::~SotStorage()
402 std::unique_ptr
<SvMemoryStream
> SotStorage::CreateMemoryStream()
404 std::unique_ptr
<SvMemoryStream
> pStm(new SvMemoryStream( 0x8000, 0x8000 ));
405 tools::SvRef
<SotStorage
> aStg
= new SotStorage( *pStm
);
406 if( CopyTo( aStg
.get() ) )
412 aStg
.clear(); // release storage beforehand
418 bool SotStorage::IsStorageFile( const OUString
& rFileName
)
420 OUString
aName( rFileName
);
421 INetURLObject
aObj( aName
);
422 if ( aObj
.GetProtocol() == INetProtocol::NotValid
)
425 osl::FileBase::getFileURLFromSystemPath( aName
, aURL
);
427 aName
= aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
430 std::unique_ptr
<SvStream
> pStm(::utl::UcbStreamHelper::CreateStream( aName
, StreamMode::STD_READ
));
431 bool bRet
= SotStorage::IsStorageFile( pStm
.get() );
435 bool SotStorage::IsStorageFile( SvStream
* pStream
)
437 /** code for new storages must come first! **/
440 sal_uInt64 nPos
= pStream
->Tell();
441 bool bRet
= UCBStorage::IsStorageFile( pStream
);
443 bRet
= Storage::IsStorageFile( pStream
);
444 pStream
->Seek( nPos
);
451 const OUString
& SotStorage::GetName() const
453 if( m_aName
.isEmpty() && m_pOwnStg
)
454 const_cast<SotStorage
*>(this)->m_aName
= m_pOwnStg
->GetName();
458 void SotStorage::SetClass( const SvGlobalName
& rName
,
459 SotClipboardFormatId nOriginalClipFormat
,
460 const OUString
& rUserTypeName
)
463 m_pOwnStg
->SetClass( rName
, nOriginalClipFormat
, rUserTypeName
);
465 SetError( SVSTREAM_GENERALERROR
);
468 SvGlobalName
SotStorage::GetClassName()
472 aGN
= m_pOwnStg
->GetClassName();
474 SetError( SVSTREAM_GENERALERROR
);
478 SotClipboardFormatId
SotStorage::GetFormat()
480 SotClipboardFormatId nFormat
= SotClipboardFormatId::NONE
;
482 nFormat
= m_pOwnStg
->GetFormat();
484 SetError( SVSTREAM_GENERALERROR
);
488 OUString
SotStorage::GetUserName()
492 aName
= m_pOwnStg
->GetUserName();
494 SetError( SVSTREAM_GENERALERROR
);
498 void SotStorage::FillInfoList( SvStorageInfoList
* pFillList
) const
501 m_pOwnStg
->FillInfoList( pFillList
);
504 bool SotStorage::CopyTo( SotStorage
* pDestStg
)
506 if( m_pOwnStg
&& pDestStg
->m_pOwnStg
)
508 m_pOwnStg
->CopyTo( pDestStg
->m_pOwnStg
);
509 SetError( m_pOwnStg
->GetError() );
510 pDestStg
->m_aKey
= m_aKey
;
511 pDestStg
->m_nVersion
= m_nVersion
;
514 SetError( SVSTREAM_GENERALERROR
);
516 return ERRCODE_NONE
== GetError();
519 bool SotStorage::Commit()
523 if( !m_pOwnStg
->Commit() )
524 SetError( m_pOwnStg
->GetError() );
527 SetError( SVSTREAM_GENERALERROR
);
529 return ERRCODE_NONE
== GetError();
532 tools::SvRef
<SotStorageStream
> SotStorage::OpenSotStream( const OUString
& rEleName
,
535 tools::SvRef
<SotStorageStream
> pStm
;
538 // enable full Ole patches,
539 // regardless what is coming, only exclusively allowed
540 nMode
|= StreamMode::SHARE_DENYALL
;
541 ErrCode nE
= m_pOwnStg
->GetError();
542 BaseStorageStream
* p
= m_pOwnStg
->OpenStream( rEleName
, nMode
);
543 pStm
= new SotStorageStream( p
);
546 m_pOwnStg
->ResetError(); // don't set error
547 if( nMode
& StreamMode::TRUNC
)
551 SetError( SVSTREAM_GENERALERROR
);
556 SotStorage
* SotStorage::OpenSotStorage( const OUString
& rEleName
,
562 nMode
|= StreamMode::SHARE_DENYALL
;
563 ErrCode nE
= m_pOwnStg
->GetError();
564 BaseStorage
* p
= m_pOwnStg
->OpenStorage(rEleName
, nMode
, !transacted
);
567 SotStorage
* pStor
= new SotStorage( p
);
569 m_pOwnStg
->ResetError(); // don't set error
575 SetError( SVSTREAM_GENERALERROR
);
580 bool SotStorage::IsStorage( const OUString
& rEleName
) const
582 // a little bit faster
584 return m_pOwnStg
->IsStorage( rEleName
);
589 bool SotStorage::IsStream( const OUString
& rEleName
) const
591 // a little bit faster
593 return m_pOwnStg
->IsStream( rEleName
);
598 bool SotStorage::IsContained( const OUString
& rEleName
) const
600 // a little bit faster
602 return m_pOwnStg
->IsContained( rEleName
);
607 bool SotStorage::Remove( const OUString
& rEleName
)
611 m_pOwnStg
->Remove( rEleName
);
612 SetError( m_pOwnStg
->GetError() );
615 SetError( SVSTREAM_GENERALERROR
);
617 return ERRCODE_NONE
== GetError();
620 bool SotStorage::CopyTo( const OUString
& rEleName
,
621 SotStorage
* pNewSt
, const OUString
& rNewName
)
625 m_pOwnStg
->CopyTo( rEleName
, pNewSt
->m_pOwnStg
, rNewName
);
626 SetError( m_pOwnStg
->GetError() );
627 SetError( pNewSt
->GetError() );
630 SetError( SVSTREAM_GENERALERROR
);
632 return ERRCODE_NONE
== GetError();
635 bool SotStorage::Validate()
637 DBG_ASSERT( m_bIsRoot
, "Validate only if root storage" );
639 return m_pOwnStg
->ValidateFAT();
644 bool SotStorage::IsOLEStorage() const
646 UCBStorage
* pStg
= dynamic_cast<UCBStorage
*>( m_pOwnStg
);
650 bool SotStorage::IsOLEStorage( const OUString
& rFileName
)
652 return Storage::IsStorageFile( rFileName
);
655 bool SotStorage::IsOLEStorage( SvStream
* pStream
)
657 return Storage::IsStorageFile( pStream
);
660 SotStorage
* SotStorage::OpenOLEStorage( const css::uno::Reference
< css::embed::XStorage
>& xStorage
,
661 const OUString
& rEleName
, StreamMode nMode
)
663 sal_Int32 nEleMode
= embed::ElementModes::SEEKABLEREAD
;
664 if ( nMode
& StreamMode::WRITE
)
665 nEleMode
|= embed::ElementModes::WRITE
;
666 if ( nMode
& StreamMode::TRUNC
)
667 nEleMode
|= embed::ElementModes::TRUNCATE
;
668 if ( nMode
& StreamMode::NOCREATE
)
669 nEleMode
|= embed::ElementModes::NOCREATE
;
671 std::unique_ptr
<SvStream
> pStream
;
674 uno::Reference
< io::XStream
> xStream
= xStorage
->openStreamElement( rEleName
, nEleMode
);
676 // TODO/LATER: should it be done this way?
677 if ( nMode
& StreamMode::WRITE
)
679 uno::Reference
< beans::XPropertySet
> xStreamProps( xStream
, uno::UNO_QUERY_THROW
);
680 xStreamProps
->setPropertyValue( "MediaType",
681 uno::Any( OUString( "application/vnd.sun.star.oleobject" ) ) );
684 pStream
= utl::UcbStreamHelper::CreateStream( xStream
);
686 catch ( uno::Exception
& )
688 //TODO/LATER: ErrorHandling
689 pStream
.reset( new SvMemoryStream
);
690 pStream
->SetError( ERRCODE_IO_GENERAL
);
693 return new SotStorage( pStream
.release(), true );
696 SotClipboardFormatId
SotStorage::GetFormatID( const css::uno::Reference
< css::embed::XStorage
>& xStorage
)
698 uno::Reference
< beans::XPropertySet
> xProps( xStorage
, uno::UNO_QUERY
);
700 return SotClipboardFormatId::NONE
;
705 xProps
->getPropertyValue("MediaType") >>= aMediaType
;
707 catch (uno::Exception
const&)
709 TOOLS_INFO_EXCEPTION("sot", "SotStorage::GetFormatID");
712 if ( !aMediaType
.isEmpty() )
714 css::datatransfer::DataFlavor aDataFlavor
;
715 aDataFlavor
.MimeType
= aMediaType
;
716 return SotExchange::GetFormat( aDataFlavor
);
719 return SotClipboardFormatId::NONE
;
722 sal_Int32
SotStorage::GetVersion( const css::uno::Reference
< css::embed::XStorage
>& xStorage
)
724 SotClipboardFormatId nSotFormatID
= SotStorage::GetFormatID( xStorage
);
725 switch( nSotFormatID
)
727 case SotClipboardFormatId::STARWRITER_8
:
728 case SotClipboardFormatId::STARWRITER_8_TEMPLATE
:
729 case SotClipboardFormatId::STARWRITERWEB_8
:
730 case SotClipboardFormatId::STARWRITERGLOB_8
:
731 case SotClipboardFormatId::STARWRITERGLOB_8_TEMPLATE
:
732 case SotClipboardFormatId::STARDRAW_8
:
733 case SotClipboardFormatId::STARDRAW_8_TEMPLATE
:
734 case SotClipboardFormatId::STARIMPRESS_8
:
735 case SotClipboardFormatId::STARIMPRESS_8_TEMPLATE
:
736 case SotClipboardFormatId::STARCALC_8
:
737 case SotClipboardFormatId::STARCALC_8_TEMPLATE
:
738 case SotClipboardFormatId::STARCHART_8
:
739 case SotClipboardFormatId::STARCHART_8_TEMPLATE
:
740 case SotClipboardFormatId::STARMATH_8
:
741 case SotClipboardFormatId::STARMATH_8_TEMPLATE
:
742 return SOFFICE_FILEFORMAT_8
;
743 case SotClipboardFormatId::STARWRITER_60
:
744 case SotClipboardFormatId::STARWRITERWEB_60
:
745 case SotClipboardFormatId::STARWRITERGLOB_60
:
746 case SotClipboardFormatId::STARDRAW_60
:
747 case SotClipboardFormatId::STARIMPRESS_60
:
748 case SotClipboardFormatId::STARCALC_60
:
749 case SotClipboardFormatId::STARCHART_60
:
750 case SotClipboardFormatId::STARMATH_60
:
751 return SOFFICE_FILEFORMAT_60
;
760 void traverse(const tools::SvRef
<SotStorage
>& rStorage
, std::vector
<unsigned char>& rBuf
)
762 SvStorageInfoList infos
;
764 rStorage
->FillInfoList(&infos
);
766 for (const auto& info
: infos
)
770 // try to open and read all content
771 tools::SvRef
<SotStorageStream
> xStream(rStorage
->OpenSotStream(info
.GetName(), StreamMode::STD_READ
));
772 const size_t nSize
= xStream
->GetSize();
773 const size_t nRead
= xStream
->ReadBytes(rBuf
.data(), nSize
);
774 SAL_INFO("sot", "Read " << nRead
<< "bytes");
776 else if (info
.IsStorage())
778 tools::SvRef
<SotStorage
> xStorage(rStorage
->OpenSotStorage(info
.GetName(), StreamMode::STD_READ
));
780 // continue with children
781 traverse(xStorage
, rBuf
);
787 extern "C" SAL_DLLPUBLIC_EXPORT
bool TestImportOLE2(SvStream
&rStream
)
791 size_t nSize
= rStream
.remainingSize();
792 tools::SvRef
<SotStorage
> xRootStorage(new SotStorage(&rStream
, false));
793 std::vector
<unsigned char> aTmpBuf(nSize
);
794 traverse(xRootStorage
, aTmpBuf
);
803 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */