1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
4 * This file is part of the LibreOffice project.
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
12 #include <osl/diagnose.h>
13 #include <rtl/ustrbuf.hxx>
15 #include <package/Inflater.hxx>
16 #include <package/Deflater.hxx>
18 #include <cppuhelper/factory.hxx>
19 #include <comphelper/base64.hxx>
20 #include <com/sun/star/uno/Any.hxx>
21 #include <com/sun/star/container/XNameContainer.hpp>
22 #include <com/sun/star/io/TempFile.hpp>
23 #include <com/sun/star/io/XInputStream.hpp>
24 #include <com/sun/star/io/XOutputStream.hpp>
25 #include <com/sun/star/io/XSeekable.hpp>
26 #include <com/sun/star/embed/XTransactedObject.hpp>
27 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
29 #include "OleHandler.hxx"
32 using namespace ::com::sun::star::uno
;
33 using namespace ::com::sun::star::lang
;
34 using namespace ::com::sun::star::io
;
35 using namespace ::com::sun::star::embed
;
40 Reference
<XStream
> OleHandler::createTempFile() {
41 Reference
<XStream
> tempFile
= TempFile::create(m_xContext
);
42 OSL_ASSERT(tempFile
.is());
46 void OleHandler::ensureCreateRootStorage()
48 if (m_storage
== nullptr || m_rootStream
== nullptr)
50 m_rootStream
= createTempFile();
51 Sequence
<Any
> args
{ Any(m_rootStream
->getInputStream()) };
54 Reference
<XMultiServiceFactory
>(m_xContext
->getServiceManager(), UNO_QUERY_THROW
)
55 ->createInstanceWithArguments(u
"com.sun.star.embed.OLESimpleStorage"_ustr
, args
), UNO_QUERY
);
59 void OleHandler::initRootStorageFromBase64(std::string_view content
)
61 Sequence
<sal_Int8
> oleData
;
62 ::comphelper::Base64::decode(oleData
, OStringToOUString(
63 content
, RTL_TEXTENCODING_UTF8
));
64 m_rootStream
= createTempFile();
65 Reference
<XOutputStream
> xOutput
= m_rootStream
->getOutputStream();
66 xOutput
->writeBytes(oleData
);
68 //Get the input stream and seek to begin
69 Reference
<XSeekable
> xSeek(m_rootStream
->getInputStream(), UNO_QUERY
);
72 //create a com.sun.star.embed.OLESimpleStorage from the temp stream
73 Sequence
<Any
> args
{ Any(xSeek
) };
75 Reference
<XMultiServiceFactory
>(m_xContext
->getServiceManager(), UNO_QUERY_THROW
)
76 ->createInstanceWithArguments(u
"com.sun.star.embed.OLESimpleStorage"_ustr
, args
), UNO_QUERY
);
80 OleHandler::encodeSubStorage(const OUString
& streamName
)
82 if (!m_storage
|| !m_storage
->hasByName(streamName
))
84 return "Not Found:"_ostr
;// + streamName;
87 Reference
<XInputStream
> subStream(m_storage
->getByName(streamName
), UNO_QUERY
);
90 return "Not Found:"_ostr
;// + streamName;
92 //The first four byte are the length of the uncompressed data
93 Sequence
<sal_Int8
> aLength(4);
94 Reference
<XSeekable
> xSeek(subStream
, UNO_QUERY
);
96 //Get the uncompressed length
97 int readbytes
= subStream
->readBytes(aLength
, 4);
100 return "Can not read the length."_ostr
;
102 sal_Int32
const oleLength
= (static_cast<sal_uInt8
>(aLength
[0]) << 0U)
103 | (static_cast<sal_uInt8
>(aLength
[1]) << 8U)
104 | (static_cast<sal_uInt8
>(aLength
[2]) << 16U)
105 | (static_cast<sal_uInt8
>(aLength
[3]) << 24U);
108 return "invalid oleLength"_ostr
;
110 Sequence
<sal_Int8
> content(oleLength
);
111 //Read all bytes. The compressed length should be less than the uncompressed length
112 readbytes
= subStream
->readBytes(content
, oleLength
);
113 if (oleLength
< readbytes
)
115 return "oleLength"_ostr
;// +oleLength + readBytes;
118 // Decompress the bytes
119 std::optional
< ::ZipUtils::Inflater
> decompresser(std::in_place
, false);
120 decompresser
->setInput(content
);
121 Sequence
<sal_Int8
> result(oleLength
);
122 decompresser
->doInflateSegment(result
, 0, oleLength
);
124 decompresser
.reset();
125 //return the base64 string of the uncompressed data
126 OUStringBuffer
buf(oleLength
);
127 ::comphelper::Base64::encode(buf
, result
);
128 return OUStringToOString(buf
, RTL_TEXTENCODING_ASCII_US
);
132 OleHandler::insertByName(const OUString
& streamName
, std::string_view content
)
134 if ( streamName
== "oledata.mso" )
136 initRootStorageFromBase64(content
);
140 ensureCreateRootStorage();
141 insertSubStorage(streamName
, content
);
146 OleHandler::getByName(const OUString
& streamName
)
148 if ( streamName
== "oledata.mso" )
150 //get the length and seek to 0
151 Reference
<XSeekable
> xSeek (m_rootStream
, UNO_QUERY
);
152 int oleLength
= static_cast<int>(xSeek
->getLength());
155 Reference
<XInputStream
> xInput
= m_rootStream
->getInputStream();
156 Sequence
<sal_Int8
> oledata(oleLength
);
157 xInput
->readBytes(oledata
, oleLength
);
158 //return the base64 encoded string
159 OUStringBuffer
buf(oleLength
);
160 ::comphelper::Base64::encode(buf
, oledata
);
161 return OUStringToOString(buf
, RTL_TEXTENCODING_ASCII_US
);
163 return encodeSubStorage(streamName
);
167 OleHandler::insertSubStorage(const OUString
& streamName
, std::string_view content
)
169 //decode the base64 string
170 Sequence
<sal_Int8
> oledata
;
171 ::comphelper::Base64::decode(oledata
,
172 OStringToOUString(content
, RTL_TEXTENCODING_ASCII_US
));
173 //create a temp stream to write data to
174 Reference
<XStream
> subStream
= createTempFile();
175 Reference
<XInputStream
> xInput
= subStream
->getInputStream();
176 Reference
<XOutputStream
> xOutput
= subStream
->getOutputStream();
177 //write the length to the temp stream
178 Sequence
<sal_Int8
> header
{
179 static_cast<sal_Int8
>((oledata
.getLength() >> 0) & 0xFF),
180 static_cast<sal_Int8
>((oledata
.getLength() >> 8) & 0xFF),
181 static_cast<sal_Int8
>((oledata
.getLength() >> 16) & 0xFF),
182 static_cast<sal_Int8
>((oledata
.getLength() >> 24) & 0xFF)
184 xOutput
->writeBytes(header
);
186 // Compress the bytes
187 Sequence
<sal_Int8
> output(oledata
.getLength());
188 std::optional
< ::ZipUtils::Deflater
> compresser(std::in_place
, sal_Int32(3), false);
189 compresser
->setInputSegment(oledata
);
190 compresser
->finish();
191 int compressedDataLength
= compresser
->doDeflateSegment(output
, oledata
.getLength());
193 //realloc the data length
194 output
.realloc(compressedDataLength
);
196 //write the compressed data to the temp stream
197 xOutput
->writeBytes(output
);
199 Reference
<XSeekable
> xSeek(xInput
, UNO_QUERY
);
202 //insert the temp stream as a sub stream and use an XTransactedObject to commit it immediately
203 Reference
<XTransactedObject
> xTransact(m_storage
, UNO_QUERY
);
206 m_storage
->insertByName(streamName
, entry
);
212 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */