Coverage Report

Created: 2026-09-28 10:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/package/source/zippackage/ZipPackage.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
20
#include <ZipPackage.hxx>
21
#include "ZipPackageSink.hxx"
22
#include <ZipEnumeration.hxx>
23
#include <ZipPackageStream.hxx>
24
#include <ZipPackageFolder.hxx>
25
#include <ZipOutputEntry.hxx>
26
#include <ZipOutputStream.hxx>
27
#include <ZipPackageBuffer.hxx>
28
#include <ZipFile.hxx>
29
#include <PackageConstants.hxx>
30
#include <com/sun/star/beans/PropertyValue.hpp>
31
#include <com/sun/star/packages/zip/ZipConstants.hpp>
32
#include <com/sun/star/packages/zip/ZipException.hpp>
33
#include <com/sun/star/packages/zip/ZipIOException.hpp>
34
#include <com/sun/star/packages/manifest/ManifestReader.hpp>
35
#include <com/sun/star/packages/manifest/ManifestWriter.hpp>
36
#include <com/sun/star/io/TempFile.hpp>
37
#include <com/sun/star/io/XStream.hpp>
38
#include <com/sun/star/io/XInputStream.hpp>
39
#include <com/sun/star/io/XOutputStream.hpp>
40
#include <com/sun/star/io/XTruncate.hpp>
41
#include <com/sun/star/io/XSeekable.hpp>
42
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
43
#include <com/sun/star/container/XNameContainer.hpp>
44
#include <officecfg/Office/Common.hxx>
45
#include <comphelper/DirectoryHelper.hxx>
46
#include <comphelper/fileurl.hxx>
47
#include <comphelper/processfactory.hxx>
48
#include <ucbhelper/content.hxx>
49
#include <cppuhelper/exc_hlp.hxx>
50
#include <com/sun/star/ucb/ContentCreationException.hpp>
51
#include <com/sun/star/ucb/TransferInfo.hpp>
52
#include <com/sun/star/ucb/NameClash.hpp>
53
#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
54
#include <com/sun/star/ucb/OpenMode.hpp>
55
#include <com/sun/star/ucb/SimpleFileAccess.hpp>
56
#include <com/sun/star/io/XActiveDataStreamer.hpp>
57
#include <com/sun/star/embed/UseBackupException.hpp>
58
#include <com/sun/star/embed/StorageFormats.hpp>
59
#include <com/sun/star/beans/NamedValue.hpp>
60
#include <com/sun/star/xml/crypto/DigestID.hpp>
61
#include <com/sun/star/xml/crypto/KDFID.hpp>
62
#include <cppuhelper/implbase.hxx>
63
#include <rtl/uri.hxx>
64
#include <rtl/random.h>
65
#include <o3tl/string_view.hxx>
66
#include <osl/diagnose.h>
67
#include <sal/log.hxx>
68
#include <unotools/streamwrap.hxx>
69
#include <unotools/tempfile.hxx>
70
#include <com/sun/star/io/XAsyncOutputMonitor.hpp>
71
72
#include <string_view>
73
74
#include <comphelper/seekableinput.hxx>
75
#include <comphelper/storagehelper.hxx>
76
#include <comphelper/ofopxmlhelper.hxx>
77
#include <comphelper/documentconstants.hxx>
78
#include <comphelper/sequenceashashmap.hxx>
79
#include <cppuhelper/supportsservice.hxx>
80
#include <comphelper/sequence.hxx>
81
#include <comphelper/servicehelper.hxx>
82
#include <utility>
83
84
using namespace std::literals;
85
86
using namespace osl;
87
using namespace cppu;
88
using namespace ucbhelper;
89
using namespace com::sun::star;
90
using namespace com::sun::star::io;
91
using namespace com::sun::star::uno;
92
using namespace com::sun::star::ucb;
93
using namespace com::sun::star::util;
94
using namespace com::sun::star::lang;
95
using namespace com::sun::star::task;
96
using namespace com::sun::star::beans;
97
using namespace com::sun::star::packages;
98
using namespace com::sun::star::container;
99
using namespace com::sun::star::packages::zip;
100
using namespace com::sun::star::packages::manifest;
101
using namespace com::sun::star::packages::zip::ZipConstants;
102
103
namespace {
104
105
class ActiveDataStreamer : public ::cppu::WeakImplHelper< XActiveDataStreamer >
106
{
107
    uno::Reference< XStream > mStream;
108
public:
109
110
    virtual uno::Reference< XStream > SAL_CALL getStream() override
111
0
            { return mStream; }
112
113
    virtual void SAL_CALL setStream( const uno::Reference< XStream >& stream ) override
114
0
            { mStream = stream; }
115
};
116
117
class DummyInputStream : public ::cppu::WeakImplHelper< XInputStream >
118
{
119
    virtual sal_Int32 SAL_CALL readBytes( uno::Sequence< sal_Int8 >&, sal_Int32 ) override
120
0
        { return 0; }
121
122
    virtual sal_Int32 SAL_CALL readSomeBytes( uno::Sequence< sal_Int8 >&, sal_Int32 ) override
123
0
        { return 0; }
124
125
    virtual void SAL_CALL skipBytes( sal_Int32 ) override
126
0
        {}
127
128
    virtual sal_Int32 SAL_CALL available() override
129
0
        { return 0; }
130
131
    virtual void SAL_CALL closeInput() override
132
0
        {}
133
};
134
135
} // namespace
136
137
sal_Int32 GetDefaultDerivedKeySize(sal_Int32 const nCipherID)
138
0
{
139
0
    switch (nCipherID)
140
0
    {
141
0
        case css::xml::crypto::CipherID::BLOWFISH_CFB_8:
142
0
            return 16;
143
0
        case css::xml::crypto::CipherID::AES_CBC_W3C_PADDING:
144
0
        case css::xml::crypto::CipherID::AES_GCM_W3C:
145
0
            return 32;
146
0
        default:
147
0
            O3TL_UNREACHABLE;
148
0
    }
149
0
}
150
151
ZipPackage::ZipPackage ( uno::Reference < XComponentContext > xContext )
152
102k
: m_aMutexHolder( new comphelper::RefCountedMutex )
153
102k
, m_nStartKeyGenerationID( xml::crypto::DigestID::SHA1 )
154
102k
, m_oChecksumDigestID( xml::crypto::DigestID::SHA1_1K )
155
102k
, m_nKeyDerivationFunctionID(xml::crypto::KDFID::PBKDF2)
156
102k
, m_nCommonEncryptionID( xml::crypto::CipherID::BLOWFISH_CFB_8 )
157
102k
, m_bHasEncryptedEntries ( false )
158
102k
, m_bHasNonEncryptedEntries ( false )
159
102k
, m_bInconsistent ( false )
160
102k
, m_bForceRecovery ( false )
161
102k
, m_bMediaTypeFallbackUsed ( false )
162
102k
, m_nFormat( embed::StorageFormats::PACKAGE ) // package is the default format
163
102k
, m_bAllowRemoveOnInsert( true )
164
102k
, m_eMode ( e_IMode_None )
165
102k
, m_xContext(std::move( xContext ))
166
102k
{
167
102k
    m_xRootFolder = new ZipPackageFolder( m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
168
102k
}
169
170
ZipPackage::~ZipPackage()
171
102k
{
172
102k
}
173
174
bool ZipPackage::isLocalFile() const
175
0
{
176
0
    return comphelper::isFileUrl(m_aURL);
177
0
}
178
179
// note: don't check for StorageFormats::ZIP, it breaks signing!
180
void ZipPackage::checkZipEntriesWithDD()
181
11.7k
{
182
11.7k
    if (!m_bForceRecovery)
183
11.7k
    {
184
11.7k
        ZipEnumeration entries{m_pZipFile->entries()};
185
217k
        while (entries.hasMoreElements())
186
205k
        {
187
205k
            ZipEntry const& rEntry{*entries.nextElement()};
188
205k
            if ((rEntry.nFlag & 0x08) != 0 && rEntry.nMethod == STORED)
189
0
            {
190
0
                uno::Reference<XPropertySet> xStream;
191
0
                getByHierarchicalName(rEntry.sPath) >>= xStream;
192
0
                uno::Reference<XServiceInfo> const xStreamSI{xStream, uno::UNO_QUERY_THROW};
193
0
                if (!xStreamSI->supportsService("com.sun.star.packages.PackageStream"))
194
0
                {
195
0
                    SAL_INFO("package", "entry STORED with data descriptor is folder: \"" << rEntry.sPath << "\"");
196
0
                    throw ZipIOException(u"entry STORED with data descriptor is folder"_ustr);
197
0
                }
198
0
                if (!xStream->getPropertyValue("WasEncrypted").get<bool>())
199
0
                {
200
0
                    SAL_INFO("package", "entry STORED with data descriptor but not encrypted: \"" << rEntry.sPath << "\"");
201
0
                    throw ZipIOException(u"entry STORED with data descriptor but not encrypted"_ustr);
202
0
                }
203
0
            }
204
205k
        }
205
11.7k
    }
206
11.7k
}
207
208
void ZipPackage::parseManifest()
209
0
{
210
0
    if ( m_nFormat != embed::StorageFormats::PACKAGE )
211
0
        return;
212
213
0
    bool bManifestParsed = false;
214
0
    ::std::optional<OUString> oFirstVersion;
215
0
    static constexpr OUString sMeta (u"META-INF"_ustr);
216
0
    if ( m_xRootFolder->hasByName( sMeta ) )
217
0
    {
218
0
        try {
219
0
            static constexpr OUString sManifest (u"manifest.xml"_ustr);
220
0
            Any aAny = m_xRootFolder->getByName( sMeta );
221
0
            uno::Reference< XNameContainer > xMetaInfFolder;
222
0
            aAny >>= xMetaInfFolder;
223
0
            if ( xMetaInfFolder.is() && xMetaInfFolder->hasByName( sManifest ) )
224
0
            {
225
0
                uno::Reference < XActiveDataSink > xSink;
226
0
                aAny = xMetaInfFolder->getByName( sManifest );
227
0
                aAny >>= xSink;
228
0
                if ( xSink.is() )
229
0
                {
230
0
                    uno::Reference < XManifestReader > xReader = ManifestReader::create( m_xContext );
231
232
0
                    const uno::Sequence < uno::Sequence < PropertyValue > > aManifestSequence = xReader->readManifestSequence ( xSink->getInputStream() );
233
0
                    const Any *pKeyInfo = nullptr;
234
235
0
                    for ( const uno::Sequence<PropertyValue>& rSequence : aManifestSequence )
236
0
                    {
237
0
                        OUString sPath, sMediaType, sVersion;
238
0
                        const Any *pSalt = nullptr, *pVector = nullptr, *pCount = nullptr, *pSize = nullptr, *pDigest = nullptr, *pDigestAlg = nullptr, *pEncryptionAlg = nullptr, *pStartKeyAlg = nullptr, *pDerivedKeySize = nullptr;
239
0
                        uno::Any const* pKDF = nullptr;
240
0
                        uno::Any const* pArgon2Args = nullptr;
241
0
                        for ( const PropertyValue& rValue : rSequence )
242
0
                        {
243
0
                            if ( rValue.Name == u"FullPath" )
244
0
                                rValue.Value >>= sPath;
245
0
                            else if ( rValue.Name == u"Version" )
246
0
                            {
247
0
                                rValue.Value >>= sVersion;
248
0
                                if (!oFirstVersion)
249
0
                                {
250
0
                                    oFirstVersion.emplace(sVersion);
251
0
                                }
252
0
                            }
253
0
                            else if ( rValue.Name == u"MediaType" )
254
0
                                rValue.Value >>= sMediaType;
255
0
                            else if ( rValue.Name == u"Salt" )
256
0
                                pSalt = &( rValue.Value );
257
0
                            else if ( rValue.Name == u"InitialisationVector" )
258
0
                                pVector = &( rValue.Value );
259
0
                            else if ( rValue.Name == u"IterationCount" )
260
0
                                pCount = &( rValue.Value );
261
0
                            else if ( rValue.Name == u"Size" )
262
0
                                pSize = &( rValue.Value );
263
0
                            else if ( rValue.Name == u"Digest" )
264
0
                                pDigest = &( rValue.Value );
265
0
                            else if ( rValue.Name == u"DigestAlgorithm" )
266
0
                                pDigestAlg = &( rValue.Value );
267
0
                            else if ( rValue.Name == u"EncryptionAlgorithm" )
268
0
                                pEncryptionAlg = &( rValue.Value );
269
0
                            else if ( rValue.Name == u"StartKeyAlgorithm" )
270
0
                                pStartKeyAlg = &( rValue.Value );
271
0
                            else if ( rValue.Name == u"DerivedKeySize" )
272
0
                                pDerivedKeySize = &( rValue.Value );
273
0
                            else if ( rValue.Name == u"KeyInfo" )
274
0
                                pKeyInfo = &( rValue.Value );
275
0
                            else if (rValue.Name == u"KeyDerivationFunction") {
276
0
                                pKDF = &rValue.Value;
277
0
                            } else if (rValue.Name == u"Argon2Args") {
278
0
                                pArgon2Args = &rValue.Value;
279
0
                            }
280
0
                        }
281
282
0
                        if ( !sPath.isEmpty() && hasByHierarchicalName ( sPath ) )
283
0
                        {
284
0
                            aAny = getByHierarchicalName( sPath );
285
0
                            uno::Reference < XInterface > xTmp;
286
0
                            aAny >>= xTmp;
287
0
                            if (auto pFolder = dynamic_cast<ZipPackageFolder*>(xTmp.get()))
288
0
                            {
289
0
                                pFolder->SetMediaType ( sMediaType );
290
0
                                pFolder->SetVersion ( sVersion );
291
0
                            }
292
0
                            else if (auto pStream = dynamic_cast<ZipPackageStream*>(xTmp.get()))
293
0
                            {
294
0
                                pStream->SetMediaType ( sMediaType );
295
0
                                pStream->SetFromManifest( true );
296
297
0
                                if (pKeyInfo
298
0
                                    && pVector && pSize && pEncryptionAlg
299
0
                                    && pKDF && pKDF->has<sal_Int32>() && pKDF->get<sal_Int32>() == xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P
300
0
                                    && ((pEncryptionAlg->has<sal_Int32>()
301
0
                                            && pEncryptionAlg->get<sal_Int32>() == xml::crypto::CipherID::AES_GCM_W3C)
302
0
                                        || (pDigest && pDigestAlg)))
303
0
                                {
304
0
                                    uno::Sequence < sal_Int8 > aSequence;
305
0
                                    sal_Int64 nSize = 0;
306
0
                                    ::std::optional<sal_Int32> oDigestAlg;
307
0
                                    sal_Int32 nEncryptionAlg = 0;
308
309
0
                                    pStream->SetToBeEncrypted ( true );
310
311
0
                                    *pVector >>= aSequence;
312
0
                                    pStream->setInitialisationVector ( aSequence );
313
314
0
                                    *pSize >>= nSize;
315
0
                                    pStream->setSize ( nSize );
316
317
0
                                    if (pDigest && pDigestAlg)
318
0
                                    {
319
0
                                        *pDigest >>= aSequence;
320
0
                                        pStream->setDigest(aSequence);
321
322
0
                                        assert(pDigestAlg->has<sal_Int32>());
323
0
                                        oDigestAlg.emplace(pDigestAlg->get<sal_Int32>());
324
0
                                    }
325
326
0
                                    *pEncryptionAlg >>= nEncryptionAlg;
327
328
0
                                    *pKeyInfo >>= m_aGpgProps;
329
330
0
                                    pStream->SetToBeCompressed ( true );
331
0
                                    pStream->SetToBeEncrypted ( true );
332
0
                                    pStream->SetIsEncrypted ( true );
333
0
                                    pStream->setIterationCount(::std::optional<sal_Int32>());
334
0
                                    pStream->setArgon2Args(::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>>());
335
336
                                    // clamp to default SHA256 start key magic value,
337
                                    // c.f. ZipPackageStream::GetEncryptionKey()
338
                                    // trying to get key value from properties
339
0
                                    const sal_Int32 nStartKeyAlg = xml::crypto::DigestID::SHA256;
340
341
0
                                    pStream->SetImportedAlgorithms({
342
0
                                        .nImportedStartKeyAlgorithm = nStartKeyAlg,
343
0
                                        .nImportedEncryptionAlgorithm = nEncryptionAlg,
344
0
                                        .oImportedChecksumAlgorithm = oDigestAlg,
345
                                        // note m_nCommonEncryptionID is not inited yet here
346
0
                                        .nImportedDerivedKeySize = ::GetDefaultDerivedKeySize(nEncryptionAlg),
347
0
                                        });
348
349
0
                                    if (!m_bHasEncryptedEntries
350
0
                                        && (pStream->getName() == "content.xml"
351
0
                                            || pStream->getName() == "encrypted-package"))
352
0
                                    {
353
0
                                        m_bHasEncryptedEntries = true;
354
0
                                        m_oChecksumDigestID = oDigestAlg;
355
0
                                        m_nKeyDerivationFunctionID = xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
356
0
                                        m_nCommonEncryptionID = nEncryptionAlg;
357
0
                                        m_nStartKeyGenerationID = nStartKeyAlg;
358
0
                                    }
359
0
                                }
360
0
                                else if (pSalt
361
0
                                    && pVector && pSize && pEncryptionAlg
362
0
                                    && pKDF && pKDF->has<sal_Int32>()
363
0
                                    && ((pKDF->get<sal_Int32>() == xml::crypto::KDFID::PBKDF2 && pCount)
364
0
                                        || (pKDF->get<sal_Int32>() == xml::crypto::KDFID::Argon2id && pArgon2Args))
365
0
                                    && ((pEncryptionAlg->has<sal_Int32>()
366
0
                                            && pEncryptionAlg->get<sal_Int32>() == xml::crypto::CipherID::AES_GCM_W3C)
367
0
                                        || (pDigest && pDigestAlg)))
368
369
0
                                {
370
0
                                    uno::Sequence < sal_Int8 > aSequence;
371
0
                                    sal_Int64 nSize = 0;
372
0
                                    ::std::optional<sal_Int32> oDigestAlg;
373
0
                                    sal_Int32 nKDF = 0;
374
0
                                    sal_Int32 nEncryptionAlg = 0;
375
0
                                    sal_Int32 nCount = 0;
376
0
                                    sal_Int32 nDerivedKeySize = 16, nStartKeyAlg = xml::crypto::DigestID::SHA1;
377
378
0
                                    pStream->SetToBeEncrypted ( true );
379
380
0
                                    *pSalt >>= aSequence;
381
0
                                    pStream->setSalt ( aSequence );
382
383
0
                                    *pVector >>= aSequence;
384
0
                                    pStream->setInitialisationVector ( aSequence );
385
386
0
                                    *pKDF >>= nKDF;
387
388
0
                                    if (pCount)
389
0
                                    {
390
0
                                        *pCount >>= nCount;
391
0
                                        pStream->setIterationCount(::std::optional<sal_Int32>(nCount));
392
0
                                    }
393
394
0
                                    if (pArgon2Args)
395
0
                                    {
396
0
                                        uno::Sequence<sal_Int32> args;
397
0
                                        *pArgon2Args >>= args;
398
0
                                        assert(args.getLength() == 3);
399
0
                                        ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgs;
400
0
                                        oArgs.emplace(args[0], args[1], args[2]);
401
0
                                        pStream->setArgon2Args(oArgs);
402
0
                                    }
403
404
0
                                    *pSize >>= nSize;
405
0
                                    pStream->setSize ( nSize );
406
407
0
                                    if (pDigest && pDigestAlg)
408
0
                                    {
409
0
                                        *pDigest >>= aSequence;
410
0
                                        pStream->setDigest(aSequence);
411
412
0
                                        assert(pDigestAlg->has<sal_Int32>());
413
0
                                        oDigestAlg.emplace(pDigestAlg->get<sal_Int32>());
414
0
                                    }
415
416
0
                                    *pEncryptionAlg >>= nEncryptionAlg;
417
418
0
                                    if ( pDerivedKeySize )
419
0
                                        *pDerivedKeySize >>= nDerivedKeySize;
420
421
0
                                    if ( pStartKeyAlg )
422
0
                                        *pStartKeyAlg >>= nStartKeyAlg;
423
424
0
                                    pStream->SetImportedAlgorithms({
425
0
                                        .nImportedStartKeyAlgorithm = nStartKeyAlg,
426
0
                                        .nImportedEncryptionAlgorithm = nEncryptionAlg,
427
0
                                        .oImportedChecksumAlgorithm = oDigestAlg,
428
0
                                        .nImportedDerivedKeySize = nDerivedKeySize,
429
0
                                        });
430
431
0
                                    pStream->SetToBeCompressed ( true );
432
0
                                    pStream->SetToBeEncrypted ( true );
433
0
                                    pStream->SetIsEncrypted ( true );
434
0
                                    if (!m_bHasEncryptedEntries
435
0
                                        && (pStream->getName() == "content.xml"
436
0
                                            || pStream->getName() == "encrypted-package"))
437
0
                                    {
438
0
                                        m_bHasEncryptedEntries = true;
439
0
                                        m_nStartKeyGenerationID = nStartKeyAlg;
440
0
                                        m_nKeyDerivationFunctionID = nKDF;
441
0
                                        m_oChecksumDigestID = oDigestAlg;
442
0
                                        m_nCommonEncryptionID = nEncryptionAlg;
443
0
                                    }
444
0
                                }
445
0
                                else
446
0
                                    m_bHasNonEncryptedEntries = true;
447
0
                            }
448
0
                            else
449
0
                                throw ZipIOException(u"Wrong content"_ustr);
450
0
                        }
451
0
                    }
452
453
0
                    bManifestParsed = true;
454
0
                }
455
456
0
                checkZipEntriesWithDD(); // check before removing entries!
457
458
                // now hide the manifest.xml file from user
459
0
                xMetaInfFolder->removeByName( sManifest );
460
0
            }
461
0
        }
462
0
        catch( Exception& )
463
0
        {
464
0
            if ( !m_bForceRecovery )
465
0
                throw;
466
0
        }
467
0
    }
468
469
0
    if ( !bManifestParsed && !m_bForceRecovery )
470
0
        throw ZipIOException(
471
0
            u"Could not parse manifest.xml"_ustr );
472
473
0
    static constexpr OUString sMimetype (u"mimetype"_ustr);
474
0
    if ( m_xRootFolder->hasByName( sMimetype ) )
475
0
    {
476
        // get mediatype from the "mimetype" stream
477
0
        OUString aPackageMediatype;
478
0
        uno::Reference < io::XActiveDataSink > xMimeSink;
479
0
        m_xRootFolder->getByName( sMimetype ) >>= xMimeSink;
480
0
        if ( xMimeSink.is() )
481
0
        {
482
0
            uno::Reference< io::XInputStream > xMimeInStream = xMimeSink->getInputStream();
483
0
            if ( xMimeInStream.is() )
484
0
            {
485
                // Mediatypes longer than 1024 symbols should not appear here
486
0
                uno::Sequence< sal_Int8 > aData( 1024 );
487
0
                sal_Int32 nRead = xMimeInStream->readBytes( aData, 1024 );
488
0
                if ( nRead > aData.getLength() )
489
0
                    nRead = aData.getLength();
490
491
0
                if ( nRead )
492
0
                    aPackageMediatype = OUString( reinterpret_cast<char const *>(aData.getConstArray()), nRead, RTL_TEXTENCODING_ASCII_US );
493
0
            }
494
0
        }
495
496
0
        if (!bManifestParsed || m_xRootFolder->GetMediaType().isEmpty())
497
0
        {
498
            // the manifest.xml could not be successfully parsed, this is an inconsistent package
499
0
            if ( aPackageMediatype.startsWith("application/vnd.") )
500
0
            {
501
                // accept only types that look similar to own mediatypes
502
0
                m_xRootFolder->SetMediaType( aPackageMediatype );
503
                // also set version explicitly
504
0
                if (oFirstVersion && m_xRootFolder->GetVersion().isEmpty())
505
0
                {
506
0
                    m_xRootFolder->SetVersion(*oFirstVersion);
507
0
                }
508
                // if there is an encrypted inner package, there is no root
509
                // document, because instead there is a package, and it is not
510
                // an error
511
0
                if (!m_xRootFolder->hasByName(u"encrypted-package"sv))
512
0
                {
513
0
                    m_bMediaTypeFallbackUsed = true;
514
0
                }
515
0
            }
516
0
        }
517
0
        else if ( !m_bForceRecovery )
518
0
        {
519
            // the mimetype stream should contain the same information as manifest.xml
520
0
            OUString const mediaTypeXML(m_xRootFolder->hasByName(u"encrypted-package"sv)
521
0
                ? m_xRootFolder->doGetByName(u"encrypted-package").xPackageEntry->GetMediaType()
522
0
                : m_xRootFolder->GetMediaType());
523
0
            if (mediaTypeXML != aPackageMediatype)
524
0
            {
525
0
                throw ZipIOException(
526
0
                    "mimetype conflicts with manifest.xml, \""
527
0
                    + mediaTypeXML + "\" vs. \""
528
0
                    + aPackageMediatype + "\"" );
529
0
            }
530
0
        }
531
532
0
        m_xRootFolder->removeByName( sMimetype );
533
0
    }
534
535
0
    m_bInconsistent = m_xRootFolder->LookForUnexpectedODF12Streams(
536
0
        std::u16string_view(), m_xRootFolder->hasByName(u"encrypted-package"sv));
537
538
0
    bool bODF12AndNewer = ( m_xRootFolder->GetVersion().compareTo( ODFVER_012_TEXT ) >= 0 );
539
0
    if ( !m_bForceRecovery && bODF12AndNewer )
540
0
    {
541
0
        if ( m_bInconsistent )
542
0
        {
543
            // this is an ODF1.2 document that contains streams not referred in the manifest.xml;
544
            // in case of ODF1.2 documents without version in manifest.xml the property IsInconsistent
545
            // should be checked later
546
0
            throw ZipIOException(
547
0
                u"there are streams not referred in manifest.xml"_ustr );
548
0
        }
549
        // all the streams should be encrypted with the same StartKey in ODF1.2
550
        // TODO/LATER: in future the exception should be thrown
551
        // throw ZipIOException( u"More than one Start Key Generation algorithm is specified!"_ustr );
552
0
    }
553
554
    // in case it is a correct ODF1.2 document, the version must be set
555
    // and the META-INF folder is reserved for package format
556
0
    if ( bODF12AndNewer )
557
0
        m_xRootFolder->removeByName( sMeta );
558
0
}
559
560
void ZipPackage::parseContentType()
561
11.8k
{
562
11.8k
    if ( m_nFormat != embed::StorageFormats::OFOPXML )
563
0
        return;
564
565
11.8k
    try {
566
11.8k
        static constexpr std::u16string_view aContentTypes(u"[Content_Types].xml");
567
        // the content type must exist in OFOPXML format!
568
11.8k
        if ( !m_xRootFolder->hasByName( aContentTypes ) )
569
18
            throw io::IOException(u"Wrong format!"_ustr );
570
571
11.8k
        uno::Reference < io::XActiveDataSink > xSink;
572
11.8k
        uno::Any aAny = m_xRootFolder->getByName( aContentTypes );
573
11.8k
        aAny >>= xSink;
574
11.8k
        if ( xSink.is() )
575
11.8k
        {
576
11.8k
            uno::Reference< io::XInputStream > xInStream = xSink->getInputStream();
577
11.8k
            if ( xInStream.is() )
578
7.84k
            {
579
                // here aContentTypeInfo[0] - Defaults, and aContentTypeInfo[1] - Overrides
580
7.84k
                const uno::Sequence< uno::Sequence< beans::StringPair > > aContentTypeInfo =
581
7.84k
                    ::comphelper::OFOPXMLHelper::ReadContentTypeSequence( xInStream, m_xContext );
582
583
7.84k
                if ( aContentTypeInfo.getLength() != 2 )
584
0
                    throw io::IOException(u""_ustr );
585
586
                // set the implicit types first
587
7.84k
                for ( const auto& rPair : aContentTypeInfo[0] )
588
12.3k
                    m_xRootFolder->setChildStreamsTypeByExtension( rPair );
589
590
                // now set the explicit types
591
7.84k
                for ( const auto& rPair : aContentTypeInfo[1] )
592
108k
                {
593
108k
                    OUString aPath;
594
108k
                    if ( rPair.First.toChar() == '/' )
595
108k
                        aPath = rPair.First.copy( 1 );
596
2
                    else
597
2
                        aPath = rPair.First;
598
599
108k
                    if ( !aPath.isEmpty() && hasByHierarchicalName( aPath ) )
600
107k
                    {
601
107k
                        uno::Any aIterAny = getByHierarchicalName( aPath );
602
107k
                        uno::Reference < XInterface > xIterTmp;
603
107k
                        aIterAny >>= xIterTmp;
604
107k
                        if (auto pStream = dynamic_cast<ZipPackageStream*>(xIterTmp.get()))
605
107k
                        {
606
                            // this is a package stream, in OFOPXML format only streams can have mediatype
607
107k
                            pStream->SetMediaType( rPair.Second );
608
107k
                        }
609
107k
                    }
610
108k
                }
611
7.84k
            }
612
11.8k
        }
613
614
11.8k
        m_xRootFolder->removeByName( aContentTypes );
615
11.8k
    }
616
11.8k
    catch( uno::Exception& )
617
11.8k
    {
618
115
        if ( !m_bForceRecovery )
619
115
            throw;
620
115
    }
621
11.8k
}
622
623
void ZipPackage::getZipFileContents()
624
19.3k
{
625
19.3k
    ZipEnumeration aEnum = m_pZipFile->entries();
626
19.3k
    OUString sDirName;
627
628
372k
    while (aEnum.hasMoreElements())
629
353k
    {
630
353k
        ZipPackageFolder* pCurrent = m_xRootFolder.get();
631
353k
        const ZipEntry & rEntry = *aEnum.nextElement();
632
353k
        OUString rName = rEntry.sPath;
633
634
353k
        if ( m_bForceRecovery )
635
0
        {
636
            // the PKZIP Application note version 6.2 does not allows to use '\' as separator
637
            // unfortunately it is used by some implementations, so we have to support it in recovery mode
638
0
            rName = rName.replace( '\\', '/' );
639
0
        }
640
641
353k
        sal_Int32 nStreamIndex = rName.lastIndexOf ( '/' );
642
353k
        if ( nStreamIndex != -1 )
643
333k
        {
644
333k
            sDirName = rName.copy ( 0, nStreamIndex );
645
333k
            FolderHash::iterator aIter = m_aRecent.find ( sDirName );
646
333k
            if ( aIter != m_aRecent.end() )
647
187k
                pCurrent = ( *aIter ).second;
648
333k
        }
649
650
353k
        if ( pCurrent == m_xRootFolder.get() )
651
165k
        {
652
165k
            sal_Int32 nIndex;
653
165k
            sal_Int32 nOldIndex = 0;
654
425k
            while ( ( nIndex = rName.indexOf( '/', nOldIndex ) ) != -1 )
655
259k
            {
656
259k
                std::u16string_view sTemp = rName.subView( nOldIndex, nIndex - nOldIndex );
657
259k
                if ( nIndex == nOldIndex )
658
0
                    break;
659
259k
                if ( !pCurrent->hasByName( sTemp ) )
660
147k
                {
661
147k
                    rtl::Reference<ZipPackageFolder> pPkgFolder = new ZipPackageFolder(m_xContext, m_nFormat, m_bAllowRemoveOnInsert);
662
147k
                    try {
663
147k
                        pPkgFolder->setName( OUString(sTemp) );
664
147k
                    } catch (uno::RuntimeException const& e) {
665
0
                        throw css::packages::zip::ZipIOException(e.Message);
666
0
                    }
667
147k
                    pPkgFolder->doSetParent( pCurrent );
668
147k
                    pCurrent = pPkgFolder.get();
669
147k
                }
670
112k
                else
671
112k
                {
672
112k
                    ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
673
112k
                    if (!rInfo.bFolder)
674
0
                        throw css::packages::zip::ZipIOException(u"Bad Zip File, stream as folder"_ustr);
675
112k
                    pCurrent = rInfo.pFolder;
676
112k
                }
677
259k
                nOldIndex = nIndex+1;
678
259k
            }
679
165k
            if ( nStreamIndex != -1 && !sDirName.isEmpty() )
680
146k
                m_aRecent [ sDirName ] = pCurrent;
681
165k
        }
682
353k
        if ( rName.getLength() -1 != nStreamIndex )
683
351k
        {
684
351k
            nStreamIndex++;
685
351k
            std::u16string_view sTemp = rName.subView( nStreamIndex );
686
687
351k
            if (!pCurrent->hasByName(sTemp))
688
351k
            {
689
351k
                rtl::Reference<ZipPackageStream> pPkgStream = new ZipPackageStream(*this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert);
690
351k
                pPkgStream->SetPackageMember(true);
691
351k
                pPkgStream->setZipEntryOnLoading(rEntry);
692
351k
                pPkgStream->setName(OUString(sTemp));
693
351k
                pPkgStream->doSetParent(pCurrent);
694
351k
            }
695
351k
        }
696
353k
    }
697
698
19.3k
    if ( m_nFormat == embed::StorageFormats::PACKAGE )
699
0
        parseManifest();
700
19.3k
    else if ( m_nFormat == embed::StorageFormats::OFOPXML )
701
11.8k
    {
702
11.8k
        parseContentType();
703
11.8k
        checkZipEntriesWithDD();
704
11.8k
    }
705
19.3k
}
706
707
void SAL_CALL ZipPackage::initialize( const uno::Sequence< Any >& aArguments )
708
102k
{
709
102k
    beans::NamedValue aNamedValue;
710
711
102k
    if ( !aArguments.hasElements() )
712
0
        return;
713
714
102k
    bool bHaveZipFile = true;
715
716
102k
    for( const auto& rArgument : aArguments )
717
226k
    {
718
226k
        OUString aParamUrl;
719
226k
        if ( rArgument >>= aParamUrl )
720
0
        {
721
0
            m_eMode = e_IMode_URL;
722
0
            try
723
0
            {
724
0
                sal_Int32 nParam = aParamUrl.indexOf( '?' );
725
0
                if ( nParam >= 0 )
726
0
                {
727
0
                    m_aURL = aParamUrl.copy( 0, nParam );
728
0
                    std::u16string_view aParam = aParamUrl.subView( nParam + 1 );
729
730
0
                    sal_Int32 nIndex = 0;
731
0
                    do
732
0
                    {
733
0
                        std::u16string_view aCommand = o3tl::getToken(aParam, 0, '&', nIndex );
734
0
                        if ( aCommand == u"repairpackage" )
735
0
                        {
736
0
                            m_bForceRecovery = true;
737
0
                            break;
738
0
                        }
739
0
                        else if ( aCommand == u"purezip" )
740
0
                        {
741
0
                            m_nFormat = embed::StorageFormats::ZIP;
742
0
                            m_xRootFolder->setPackageFormat_Impl( m_nFormat );
743
0
                            break;
744
0
                        }
745
0
                        else if ( aCommand == u"ofopxml" )
746
0
                        {
747
0
                            m_nFormat = embed::StorageFormats::OFOPXML;
748
0
                            m_xRootFolder->setPackageFormat_Impl( m_nFormat );
749
0
                            break;
750
0
                        }
751
0
                    }
752
0
                    while ( nIndex >= 0 );
753
0
                }
754
0
                else
755
0
                    m_aURL = aParamUrl;
756
757
0
                Content aContent(
758
0
                    m_aURL, uno::Reference< XCommandEnvironment >(),
759
0
                    m_xContext );
760
0
                Any aAny = aContent.getPropertyValue(u"Size"_ustr);
761
0
                sal_uInt64 aSize = 0;
762
                // kind of optimization: treat empty files as nonexistent files
763
                // and write to such files directly. Note that "Size" property is optional.
764
0
                bool bHasSizeProperty = aAny >>= aSize;
765
0
                if( !bHasSizeProperty || aSize )
766
0
                {
767
0
                    uno::Reference < XActiveDataSink > xSink = new ZipPackageSink;
768
0
                    if ( aContent.openStream ( xSink ) )
769
0
                        m_xContentStream = xSink->getInputStream();
770
0
                }
771
0
                else
772
0
                    bHaveZipFile = false;
773
0
            }
774
0
            catch ( css::uno::Exception& )
775
0
            {
776
                // Exception derived from uno::Exception thrown. This probably
777
                // means the file doesn't exist...we'll create it at
778
                // commitChanges time
779
0
                bHaveZipFile = false;
780
0
            }
781
0
        }
782
226k
        else if ( rArgument >>= m_xStream )
783
102k
        {
784
            // a writable stream can implement both XStream & XInputStream
785
102k
            m_eMode = e_IMode_XStream;
786
102k
            m_xContentStream = m_xStream->getInputStream();
787
102k
        }
788
124k
        else if ( rArgument >>= m_xContentStream )
789
2
        {
790
2
            m_eMode = e_IMode_XInputStream;
791
2
        }
792
124k
        else if ( rArgument >>= aNamedValue )
793
124k
        {
794
124k
            if ( aNamedValue.Name == "RepairPackage" )
795
2
                aNamedValue.Value >>= m_bForceRecovery;
796
124k
            else if ( aNamedValue.Name == "PackageFormat" )
797
0
            {
798
                // setting this argument to true means Package format
799
                // setting it to false means plain Zip format
800
801
0
                bool bPackFormat = true;
802
0
                aNamedValue.Value >>= bPackFormat;
803
0
                if ( !bPackFormat )
804
0
                    m_nFormat = embed::StorageFormats::ZIP;
805
806
0
                m_xRootFolder->setPackageFormat_Impl( m_nFormat );
807
0
            }
808
124k
            else if ( aNamedValue.Name == "StorageFormat" )
809
21.5k
            {
810
21.5k
                OUString aFormatName;
811
21.5k
                sal_Int32 nFormatID = 0;
812
21.5k
                if ( aNamedValue.Value >>= aFormatName )
813
21.5k
                {
814
21.5k
                    if ( aFormatName == PACKAGE_STORAGE_FORMAT_STRING )
815
0
                        m_nFormat = embed::StorageFormats::PACKAGE;
816
21.5k
                    else if ( aFormatName == ZIP_STORAGE_FORMAT_STRING )
817
7.77k
                        m_nFormat = embed::StorageFormats::ZIP;
818
13.7k
                    else if ( aFormatName == OFOPXML_STORAGE_FORMAT_STRING )
819
13.7k
                        m_nFormat = embed::StorageFormats::OFOPXML;
820
0
                    else
821
0
                        throw lang::IllegalArgumentException(u""_ustr, uno::Reference< uno::XInterface >(), 1 );
822
21.5k
                }
823
0
                else if ( aNamedValue.Value >>= nFormatID )
824
0
                {
825
0
                    if (nFormatID != embed::StorageFormats::PACKAGE
826
0
                        && nFormatID != embed::StorageFormats::ZIP
827
0
                        && nFormatID != embed::StorageFormats::OFOPXML)
828
0
                        throw lang::IllegalArgumentException(u""_ustr, uno::Reference< uno::XInterface >(), 1 );
829
830
0
                    m_nFormat = nFormatID;
831
0
                }
832
0
                else
833
0
                    throw lang::IllegalArgumentException(u""_ustr, uno::Reference< uno::XInterface >(), 1 );
834
835
21.5k
                m_xRootFolder->setPackageFormat_Impl( m_nFormat );
836
21.5k
            }
837
102k
            else if ( aNamedValue.Name == "AllowRemoveOnInsert" )
838
102k
            {
839
102k
                aNamedValue.Value >>= m_bAllowRemoveOnInsert;
840
102k
                m_xRootFolder->setRemoveOnInsertMode_Impl( m_bAllowRemoveOnInsert );
841
102k
            }
842
0
            else if (aNamedValue.Name == "NoFileSync")
843
0
                aNamedValue.Value >>= m_bDisableFileSync;
844
845
            // for now the progress handler is not used, probably it will never be
846
            // if ( aNamedValue.Name == "ProgressHandler" )
847
124k
        }
848
0
        else
849
0
        {
850
            // The URL is not acceptable
851
0
            throw css::uno::Exception (u"Bad arguments."_ustr,
852
0
                getXWeak() );
853
0
        }
854
226k
    }
855
856
102k
    try
857
102k
    {
858
102k
        if ( m_xContentStream.is() )
859
102k
        {
860
            // the stream must be seekable, if it is not it will be wrapped
861
102k
            m_xContentStream = ::comphelper::OSeekableInputWrapper::CheckSeekableCanWrap( m_xContentStream, m_xContext );
862
102k
            m_xContentSeek.set( m_xContentStream, UNO_QUERY_THROW );
863
102k
            if ( !m_xContentSeek->getLength() )
864
81.1k
                bHaveZipFile = false;
865
102k
        }
866
0
        else
867
0
            bHaveZipFile = false;
868
102k
    }
869
102k
    catch ( css::uno::Exception& )
870
102k
    {
871
        // Exception derived from uno::Exception thrown. This probably
872
        // means the file doesn't exist...we'll create it at
873
        // commitChanges time
874
2
        bHaveZipFile = false;
875
2
    }
876
102k
    if ( !bHaveZipFile )
877
81.1k
        return;
878
879
21.5k
    bool bBadZipFile = false;
880
21.5k
    OUString message;
881
21.5k
    try
882
21.5k
    {
883
21.5k
        m_pZipFile.emplace(m_aMutexHolder, m_xContentStream, m_xContext, true,
884
21.5k
            m_bForceRecovery,
885
21.5k
            m_nFormat == embed::StorageFormats::ZIP
886
21.5k
                ? ZipFile::Checks::Default
887
21.5k
                : m_nFormat == embed::StorageFormats::OFOPXML
888
13.7k
                    ? ZipFile::Checks::CheckInsensitive
889
13.7k
                    : ZipFile::Checks::TryCheckInsensitive);
890
21.5k
        getZipFileContents();
891
21.5k
    }
892
21.5k
    catch ( IOException & e )
893
21.5k
    {
894
801
        bBadZipFile = true;
895
801
        message = "IOException: " + e.Message;
896
801
    }
897
21.5k
    catch ( ZipException & e )
898
21.5k
    {
899
1.31k
        bBadZipFile = true;
900
1.31k
        message = "ZipException: " + e.Message;
901
1.31k
    }
902
21.5k
    catch ( Exception & )
903
21.5k
    {
904
185
        m_pZipFile.reset();
905
185
        throw;
906
185
    }
907
908
21.3k
    if ( bBadZipFile )
909
2.12k
    {
910
        // clean up the memory, and tell the UCB about the error
911
2.12k
        m_pZipFile.reset();
912
913
2.12k
        throw css::packages::zip::ZipIOException (
914
2.12k
            "Bad Zip File, " + message,
915
2.12k
            getXWeak() );
916
2.12k
    }
917
21.3k
}
918
919
Any SAL_CALL ZipPackage::getByHierarchicalName( const OUString& aName )
920
208k
{
921
208k
    OUString sDirName;
922
208k
    sal_Int32 nOldIndex, nStreamIndex;
923
924
208k
    if (aName == "/")
925
        // root directory.
926
100k
        return Any ( uno::Reference( cppu::getXWeak(m_xRootFolder.get()) ) );
927
928
107k
    sal_Int32 nIndex = aName.getLength();
929
930
107k
    nStreamIndex = aName.lastIndexOf ( '/' );
931
107k
    bool bFolder = nStreamIndex == nIndex-1; // last character is '/'.
932
933
107k
    if ( nStreamIndex != -1 )
934
107k
    {
935
        // The name contains '/'.
936
107k
        sDirName = aName.copy ( 0, nStreamIndex );
937
107k
        FolderHash::iterator aIter = m_aRecent.find ( sDirName );
938
107k
        if ( aIter != m_aRecent.end() )
939
107k
        {
940
            // There is a cached entry for this name.
941
942
107k
            ZipPackageFolder* pFolder = aIter->second;
943
944
107k
            if ( bFolder )
945
0
            {
946
                // Determine the directory name.
947
0
                sal_Int32 nDirIndex = aName.lastIndexOf ( '/', nStreamIndex );
948
0
                std::u16string_view sTemp = aName.subView ( nDirIndex == -1 ? 0 : nDirIndex+1, nStreamIndex-nDirIndex-1 );
949
950
0
                if (pFolder && sTemp == pFolder->getName())
951
0
                    return Any(uno::Reference(cppu::getXWeak(pFolder)));
952
0
            }
953
107k
            else
954
107k
            {
955
                // Determine the file name.
956
107k
                std::u16string_view sTemp = aName.subView( nStreamIndex + 1 );
957
958
107k
                if (pFolder && pFolder->hasByName(sTemp))
959
107k
                    return pFolder->getByName(sTemp);
960
107k
            }
961
962
0
            m_aRecent.erase( aIter );
963
0
        }
964
107k
    }
965
0
    else if ( m_xRootFolder->hasByName ( aName ) )
966
        // top-level element.
967
0
        return m_xRootFolder->getByName ( aName );
968
969
    // Not in the cache. Search normally.
970
971
0
    nOldIndex = 0;
972
0
    ZipPackageFolder * pCurrent = m_xRootFolder.get();
973
0
    ZipPackageFolder * pPrevious = nullptr;
974
975
    // Find the right directory for the given path.
976
977
0
    while ( ( nIndex = aName.indexOf( '/', nOldIndex )) != -1 )
978
0
    {
979
0
        std::u16string_view sTemp = aName.subView ( nOldIndex, nIndex - nOldIndex );
980
0
        if ( nIndex == nOldIndex )
981
0
            break;
982
0
        if ( !pCurrent->hasByName( sTemp ) )
983
0
            throw NoSuchElementException(u""_ustr );
984
985
0
        pPrevious = pCurrent;
986
0
        ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
987
0
        if (!rInfo.bFolder)
988
0
            throw css::packages::zip::ZipIOException(u"Bad Zip File, stream as folder"_ustr);
989
0
        pCurrent = rInfo.pFolder;
990
0
        nOldIndex = nIndex+1;
991
0
    }
992
993
0
    if ( bFolder )
994
0
    {
995
0
        if ( nStreamIndex != -1 )
996
0
            m_aRecent[sDirName] = pPrevious; // cache it.
997
0
        return Any ( uno::Reference( cppu::getXWeak(pCurrent) ) );
998
0
    }
999
1000
0
    std::u16string_view sTemp = aName.subView( nOldIndex );
1001
1002
0
    if ( pCurrent->hasByName ( sTemp ) )
1003
0
    {
1004
0
        if ( nStreamIndex != -1 )
1005
0
            m_aRecent[sDirName] = pCurrent; // cache it.
1006
0
        return pCurrent->getByName( sTemp );
1007
0
    }
1008
1009
0
    throw NoSuchElementException(u""_ustr);
1010
0
}
1011
1012
sal_Bool SAL_CALL ZipPackage::hasByHierarchicalName( const OUString& aName )
1013
108k
{
1014
108k
    if (aName == "/")
1015
        // root directory
1016
0
        return true;
1017
1018
108k
    try
1019
108k
    {
1020
108k
        OUString sDirName;
1021
108k
        sal_Int32 nStreamIndex;
1022
108k
        nStreamIndex = aName.lastIndexOf ( '/' );
1023
108k
        bool bFolder = nStreamIndex == aName.getLength()-1;
1024
108k
        if ( nStreamIndex != -1 )
1025
108k
        {
1026
108k
            sDirName = aName.copy ( 0, nStreamIndex );
1027
108k
            FolderHash::iterator aIter = m_aRecent.find ( sDirName );
1028
108k
            if ( aIter != m_aRecent.end() )
1029
108k
            {
1030
108k
                if ( bFolder )
1031
0
                {
1032
0
                    sal_Int32 nDirIndex = aName.lastIndexOf ( '/', nStreamIndex );
1033
0
                    std::u16string_view sTemp = aName.subView ( nDirIndex == -1 ? 0 : nDirIndex+1, nStreamIndex-nDirIndex-1 );
1034
0
                    if ( sTemp == ( *aIter ).second->getName() )
1035
0
                        return true;
1036
0
                    else
1037
0
                        m_aRecent.erase ( aIter );
1038
0
                }
1039
108k
                else
1040
108k
                {
1041
108k
                    std::u16string_view sTemp = aName.subView( nStreamIndex + 1 );
1042
108k
                    if ( ( *aIter ).second->hasByName( sTemp ) )
1043
107k
                        return true;
1044
148
                    else
1045
148
                        m_aRecent.erase( aIter );
1046
108k
                }
1047
108k
            }
1048
108k
        }
1049
1
        else
1050
1
        {
1051
1
            if ( m_xRootFolder->hasByName ( aName ) )
1052
0
                return true;
1053
1
        }
1054
358
        ZipPackageFolder * pCurrent = m_xRootFolder.get();
1055
358
        ZipPackageFolder * pPrevious = nullptr;
1056
358
        sal_Int32 nOldIndex = 0;
1057
358
        sal_Int32 nIndex;
1058
839
        while ( ( nIndex = aName.indexOf( '/', nOldIndex )) != -1 )
1059
532
        {
1060
532
            if ( nIndex == nOldIndex )
1061
0
                break;
1062
1063
532
            std::u16string_view sTemp = aName.subView ( nOldIndex, nIndex - nOldIndex );
1064
1065
532
            if ( pCurrent->hasByName( sTemp ) )
1066
481
            {
1067
481
                pPrevious = pCurrent;
1068
481
                ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
1069
481
                if (!rInfo.bFolder)
1070
0
                    throw css::packages::zip::ZipIOException(u"Bad Zip File, stream as folder"_ustr);
1071
481
                pCurrent = rInfo.pFolder;
1072
481
            }
1073
51
            else
1074
51
                return false;
1075
481
            nOldIndex = nIndex+1;
1076
481
        }
1077
307
        if ( bFolder )
1078
0
        {
1079
0
            m_aRecent[sDirName] = pPrevious;
1080
0
            return true;
1081
0
        }
1082
307
        else
1083
307
        {
1084
307
            std::u16string_view sTemp = aName.subView( nOldIndex );
1085
1086
307
            if ( pCurrent->hasByName( sTemp ) )
1087
29
            {
1088
29
                m_aRecent[sDirName] = pCurrent;
1089
29
                return true;
1090
29
            }
1091
307
        }
1092
307
    }
1093
108k
    catch (const uno::RuntimeException &)
1094
108k
    {
1095
0
        throw;
1096
0
    }
1097
108k
    catch (const uno::Exception&)
1098
108k
    {
1099
0
        uno::Any e(::cppu::getCaughtException());
1100
0
        throw lang::WrappedTargetRuntimeException(u"ZipPackage::hasByHierarchicalName"_ustr, nullptr, e);
1101
0
    }
1102
278
    return false;
1103
108k
}
1104
1105
uno::Reference< XInterface > SAL_CALL ZipPackage::createInstance()
1106
0
{
1107
0
    uno::Reference < XInterface > xRef = *( new ZipPackageStream( *this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert ) );
1108
0
    return xRef;
1109
0
}
1110
1111
uno::Reference< XInterface > SAL_CALL ZipPackage::createInstanceWithArguments( const uno::Sequence< Any >& aArguments )
1112
9.20k
{
1113
9.20k
    bool bArg = false;
1114
9.20k
    uno::Reference < XInterface > xRef;
1115
9.20k
    if ( aArguments.hasElements() )
1116
9.20k
        aArguments[0] >>= bArg;
1117
9.20k
    if ( bArg )
1118
8.61k
        xRef = *new ZipPackageFolder( m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
1119
587
    else
1120
587
        xRef = *new ZipPackageStream( *this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
1121
1122
9.20k
    return xRef;
1123
9.20k
}
1124
1125
void ZipPackage::WriteMimetypeMagicFile( ZipOutputStream& aZipOut )
1126
0
{
1127
0
    static constexpr std::u16string_view sMime (u"mimetype" );
1128
0
    if ( m_xRootFolder->hasByName( sMime ) )
1129
0
        m_xRootFolder->removeByName( sMime );
1130
1131
0
    auto pEntry = std::make_unique<ZipEntry>();
1132
0
    sal_Int32 nBufferLength = m_xRootFolder->GetMediaType().getLength();
1133
0
    OString sMediaType = OUStringToOString( m_xRootFolder->GetMediaType(), RTL_TEXTENCODING_ASCII_US );
1134
0
    const uno::Sequence< sal_Int8 > aType( reinterpret_cast<sal_Int8 const *>(sMediaType.getStr()),
1135
0
                                     nBufferLength );
1136
1137
0
    pEntry->sPath = sMime;
1138
0
    pEntry->nMethod = STORED;
1139
0
    pEntry->nSize = pEntry->nCompressedSize = nBufferLength;
1140
0
    pEntry->nTime = ZipOutputStream::getCurrentDosTime();
1141
1142
0
    CRC32 aCRC32;
1143
0
    aCRC32.update( aType );
1144
0
    pEntry->nCrc = aCRC32.getValue();
1145
1146
0
    try
1147
0
    {
1148
0
        ZipOutputStream::setEntry(*pEntry);
1149
0
        aZipOut.writeLOC(std::move(pEntry));
1150
0
        aZipOut.rawWrite(aType);
1151
0
        aZipOut.rawCloseEntry();
1152
0
    }
1153
0
    catch ( const css::io::IOException & )
1154
0
    {
1155
0
        css::uno::Any anyEx = cppu::getCaughtException();
1156
0
        throw WrappedTargetException(
1157
0
                u"Error adding mimetype to the ZipOutputStream!"_ustr,
1158
0
                getXWeak(),
1159
0
                anyEx );
1160
0
    }
1161
0
}
1162
1163
void ZipPackage::WriteManifest( ZipOutputStream& aZipOut, const std::vector< uno::Sequence < PropertyValue > >& aManList )
1164
0
{
1165
    // Write the manifest
1166
0
    uno::Reference < XManifestWriter > xWriter = ManifestWriter::create( m_xContext );
1167
0
    auto pEntry = std::make_unique<ZipEntry>();
1168
0
    rtl::Reference<ZipPackageBuffer> pBuffer = new ZipPackageBuffer;
1169
1170
0
    pEntry->sPath = "META-INF/manifest.xml";
1171
0
    pEntry->nMethod = DEFLATED;
1172
0
    pEntry->nCrc = -1;
1173
0
    pEntry->nCompressedSize = -1;
1174
0
    pEntry->nTime = ZipOutputStream::getCurrentDosTime();
1175
1176
0
    xWriter->writeManifestSequence ( pBuffer,  comphelper::containerToSequence(aManList) );
1177
1178
0
    sal_Int32 nBufferLength = static_cast < sal_Int32 > ( pBuffer->getPosition() );
1179
0
    pBuffer->realloc( nBufferLength );
1180
0
    pEntry->nSize = nBufferLength;
1181
1182
    // the manifest.xml is never encrypted - so pass an empty reference
1183
0
    ZipOutputStream::setEntry(*pEntry);
1184
0
    auto p = pEntry.get();
1185
0
    aZipOut.writeLOC(std::move(pEntry));
1186
0
    ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, p, nullptr, /*bEncrypt*/false);
1187
0
    aZipEntry.write(pBuffer->getSequence());
1188
0
    aZipEntry.closeEntry();
1189
0
    aZipOut.rawCloseEntry();
1190
0
}
1191
1192
void ZipPackage::WriteContentTypes( ZipOutputStream& aZipOut, const std::vector< uno::Sequence < PropertyValue > >& aManList )
1193
0
{
1194
0
    auto pEntry = std::make_unique<ZipEntry>();
1195
0
    rtl::Reference<ZipPackageBuffer> pBuffer = new ZipPackageBuffer;
1196
1197
0
    pEntry->sPath = "[Content_Types].xml";
1198
0
    pEntry->nMethod = DEFLATED;
1199
0
    pEntry->nCrc = -1;
1200
0
    pEntry->nCompressedSize = -1;
1201
0
    pEntry->nTime = ZipOutputStream::getCurrentDosTime();
1202
1203
    // Add at least the standard default entries.
1204
0
    uno::Sequence< beans::StringPair > aDefaultsSequence
1205
0
    {
1206
0
        { u"fntdata"_ustr, u"application/x-fontdata"_ustr },
1207
0
        { u"jpeg"_ustr, u"image/jpeg"_ustr },
1208
0
        { u"png"_ustr, u"image/png"_ustr },
1209
0
        { u"rels"_ustr, u"application/vnd.openxmlformats-package.relationships+xml"_ustr },
1210
0
        { u"xml"_ustr, u"application/xml"_ustr }
1211
0
    };
1212
1213
0
    uno::Sequence< beans::StringPair > aOverridesSequence(aManList.size());
1214
0
    auto aOverridesSequenceRange = asNonConstRange(aOverridesSequence);
1215
0
    sal_Int32 nOverSeqLength = 0;
1216
0
    for (const auto& rMan : aManList)
1217
0
    {
1218
0
        OSL_ENSURE( rMan[PKG_MNFST_MEDIATYPE].Name == "MediaType" && rMan[PKG_MNFST_FULLPATH].Name == "FullPath",
1219
0
                    "The mediatype sequence format is wrong!" );
1220
0
        OUString aType;
1221
0
        rMan[PKG_MNFST_MEDIATYPE].Value >>= aType;
1222
0
        if (aType.isEmpty())
1223
0
            continue;
1224
1225
0
        OUString aPath, aExtension;
1226
0
        rMan[PKG_MNFST_FULLPATH].Value >>= aPath;
1227
0
        (void)comphelper::DirectoryHelper::splitAtLastToken(aPath, '.', aExtension);
1228
1229
        // don't add override if same extension and content type exists as default
1230
0
        const bool bTypeFound
1231
0
            = !aExtension.isEmpty()
1232
0
              && std::find_if(aDefaultsSequence.begin(), aDefaultsSequence.end(),
1233
0
                              [&aExtension, &aType](const beans::StringPair& defPair)
1234
0
                              { return defPair.First == aExtension && defPair.Second == aType; })
1235
0
                     != aDefaultsSequence.end();
1236
0
        if (bTypeFound)
1237
0
            continue;
1238
1239
0
        aOverridesSequenceRange[nOverSeqLength].First = "/" + aPath;
1240
0
        aOverridesSequenceRange[nOverSeqLength].Second = aType;
1241
0
        ++nOverSeqLength;
1242
0
    }
1243
0
    aOverridesSequence.realloc(nOverSeqLength);
1244
1245
0
    ::comphelper::OFOPXMLHelper::WriteContentSequence(
1246
0
            pBuffer, aDefaultsSequence, aOverridesSequence, m_xContext );
1247
1248
0
    sal_Int32 nBufferLength = static_cast < sal_Int32 > ( pBuffer->getPosition() );
1249
0
    pBuffer->realloc( nBufferLength );
1250
0
    pEntry->nSize = nBufferLength;
1251
1252
    // there is no encryption in this format currently
1253
0
    ZipOutputStream::setEntry(*pEntry);
1254
0
    auto p = pEntry.get();
1255
0
    aZipOut.writeLOC(std::move(pEntry));
1256
0
    ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, p, nullptr, /*bEncrypt*/false);
1257
0
    aZipEntry.write(pBuffer->getSequence());
1258
0
    aZipEntry.closeEntry();
1259
0
    aZipOut.rawCloseEntry();
1260
0
}
1261
1262
void ZipPackage::ConnectTo( const uno::Reference< io::XInputStream >& xInStream )
1263
0
{
1264
0
    assert(dynamic_cast<comphelper::ByteReader*>(xInStream.get()));
1265
0
    m_xContentSeek.set( xInStream, uno::UNO_QUERY_THROW );
1266
0
    m_xContentStream = xInStream;
1267
1268
    // seek back to the beginning of the temp file so we can read segments from it
1269
0
    m_xContentSeek->seek( 0 );
1270
0
    if ( m_pZipFile )
1271
0
        m_pZipFile->setInputStream( m_xContentStream );
1272
0
    else
1273
0
        m_pZipFile.emplace(m_aMutexHolder, m_xContentStream, m_xContext, false,
1274
0
            false,
1275
0
            m_nFormat == embed::StorageFormats::ZIP
1276
0
                ? ZipFile::Checks::Default
1277
0
                : m_nFormat == embed::StorageFormats::OFOPXML
1278
0
                    ? ZipFile::Checks::CheckInsensitive
1279
0
                    : ZipFile::Checks::TryCheckInsensitive);
1280
0
}
1281
1282
uno::Reference< io::XInputStream > ZipPackage::writeTempFile()
1283
0
{
1284
    // In case the target local file does not exist or empty
1285
    // write directly to it otherwise create a temporary file to write to.
1286
    // If a temporary file is created it is returned back by the method.
1287
    // If the data written directly, xComponentStream will be switched here
1288
1289
0
    bool bUseTemp = true;
1290
0
    uno::Reference < io::XInputStream > xResult;
1291
0
    uno::Reference < io::XInputStream > xTempIn;
1292
1293
0
    uno::Reference < io::XOutputStream > xTempOut;
1294
0
    uno::Reference< io::XActiveDataStreamer > xSink;
1295
1296
0
    if ( m_eMode == e_IMode_URL && !m_pZipFile && isLocalFile() )
1297
0
    {
1298
0
        xSink = openOriginalForOutput();
1299
0
        if( xSink.is() )
1300
0
        {
1301
0
            uno::Reference< io::XStream > xStr = xSink->getStream();
1302
0
            if( xStr.is() )
1303
0
            {
1304
0
                xTempOut = xStr->getOutputStream();
1305
0
                if( xTempOut.is() )
1306
0
                    bUseTemp = false;
1307
0
            }
1308
0
        }
1309
0
    }
1310
0
    else if ( m_eMode == e_IMode_XStream && !m_pZipFile )
1311
0
    {
1312
        // write directly to an empty stream
1313
0
        xTempOut = m_xStream->getOutputStream();
1314
0
        if( xTempOut.is() )
1315
0
            bUseTemp = false;
1316
0
    }
1317
1318
0
    if( bUseTemp )
1319
0
    {
1320
        // create temporary file
1321
0
        rtl::Reference < utl::TempFileFastService > xTempFile( new utl::TempFileFastService );
1322
0
        xTempOut.set( xTempFile );
1323
0
        xTempIn.set( xTempFile );
1324
0
    }
1325
1326
    // Hand it to the ZipOutputStream:
1327
0
    ZipOutputStream aZipOut( xTempOut );
1328
0
    try
1329
0
    {
1330
0
        if ( m_nFormat == embed::StorageFormats::PACKAGE )
1331
0
        {
1332
            // Remove the old manifest.xml file as the
1333
            // manifest will be re-generated and the
1334
            // META-INF directory implicitly created if does not exist
1335
0
            static constexpr std::u16string_view sMeta (u"META-INF");
1336
1337
0
            if ( m_xRootFolder->hasByName( sMeta ) )
1338
0
            {
1339
0
                static constexpr OUString sManifest (u"manifest.xml"_ustr);
1340
1341
0
                uno::Reference< XNameContainer > xMetaInfFolder;
1342
0
                Any aAny = m_xRootFolder->getByName( sMeta );
1343
0
                aAny >>= xMetaInfFolder;
1344
0
                if ( xMetaInfFolder.is() && xMetaInfFolder->hasByName( sManifest ) )
1345
0
                    xMetaInfFolder->removeByName( sManifest );
1346
0
            }
1347
1348
            // Write a magic file with mimetype
1349
0
            WriteMimetypeMagicFile( aZipOut );
1350
0
        }
1351
0
        else if ( m_nFormat == embed::StorageFormats::OFOPXML )
1352
0
        {
1353
            // Remove the old [Content_Types].xml file as the
1354
            // file will be re-generated
1355
1356
0
            static constexpr std::u16string_view aContentTypes(u"[Content_Types].xml");
1357
1358
0
            if ( m_xRootFolder->hasByName( aContentTypes ) )
1359
0
                m_xRootFolder->removeByName( aContentTypes );
1360
0
        }
1361
1362
        // Create a vector to store data for the manifest.xml file
1363
0
        std::vector < uno::Sequence < PropertyValue > > aManList;
1364
1365
0
        static constexpr OUStringLiteral sMediaType(u"MediaType");
1366
0
        static constexpr OUStringLiteral sVersion(u"Version");
1367
0
        static constexpr OUStringLiteral sFullPath(u"FullPath");
1368
0
        const bool bIsGpgEncrypt = m_aGpgProps.hasElements();
1369
1370
        // note: this is always created here (needed for GPG), possibly
1371
        // filtered out later in ManifestExport
1372
0
        if ( m_nFormat == embed::StorageFormats::PACKAGE )
1373
0
        {
1374
0
            uno::Sequence < PropertyValue > aPropSeq(
1375
0
                bIsGpgEncrypt ? PKG_SIZE_NOENCR_MNFST+1 : PKG_SIZE_NOENCR_MNFST );
1376
0
            auto pPropSeq = aPropSeq.getArray();
1377
0
            pPropSeq [PKG_MNFST_MEDIATYPE].Name = sMediaType;
1378
0
            pPropSeq [PKG_MNFST_MEDIATYPE].Value <<= m_xRootFolder->GetMediaType();
1379
0
            pPropSeq [PKG_MNFST_VERSION].Name = sVersion;
1380
0
            pPropSeq [PKG_MNFST_VERSION].Value <<= m_xRootFolder->GetVersion();
1381
0
            pPropSeq [PKG_MNFST_FULLPATH].Name = sFullPath;
1382
0
            pPropSeq [PKG_MNFST_FULLPATH].Value <<= u"/"_ustr;
1383
1384
0
            if( bIsGpgEncrypt )
1385
0
            {
1386
0
                pPropSeq[PKG_SIZE_NOENCR_MNFST].Name = "KeyInfo";
1387
0
                pPropSeq[PKG_SIZE_NOENCR_MNFST].Value <<= m_aGpgProps;
1388
0
            }
1389
0
            aManList.push_back( aPropSeq );
1390
0
        }
1391
1392
0
        {
1393
0
            ::std::optional<sal_Int32> oPBKDF2IterationCount;
1394
0
            ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgon2Args;
1395
1396
0
            if (!bIsGpgEncrypt)
1397
0
            {
1398
0
                if (m_nKeyDerivationFunctionID == xml::crypto::KDFID::PBKDF2)
1399
0
                {   // if there is only one KDF invocation, increase the safety margin
1400
0
                    oPBKDF2IterationCount.emplace(m_xRootFolder->hasByName(u"encrypted-package"sv) ? 600000 : 100000);
1401
0
                }
1402
0
                else
1403
0
                {
1404
0
                    assert(m_nKeyDerivationFunctionID == xml::crypto::KDFID::Argon2id);
1405
0
                    oArgon2Args.emplace(3, (1<<16), 4);
1406
0
                }
1407
0
            }
1408
1409
            // call saveContents - it will recursively save sub-directories
1410
0
            m_xRootFolder->saveContents(u""_ustr, aManList, aZipOut, GetEncryptionKey(),
1411
0
                oPBKDF2IterationCount, oArgon2Args);
1412
0
        }
1413
1414
0
        if( m_nFormat == embed::StorageFormats::PACKAGE )
1415
0
        {
1416
0
            WriteManifest( aZipOut, aManList );
1417
0
        }
1418
0
        else if( m_nFormat == embed::StorageFormats::OFOPXML )
1419
0
        {
1420
0
            WriteContentTypes( aZipOut, aManList );
1421
0
        }
1422
1423
0
        aZipOut.finish();
1424
1425
0
        if( bUseTemp )
1426
0
            xResult = std::move(xTempIn);
1427
1428
        // Update our References to point to the new temp file
1429
0
        if( !bUseTemp )
1430
0
        {
1431
            // the case when the original contents were written directly
1432
0
            xTempOut->flush();
1433
1434
            // in case the stream is based on a file it will implement the following interface
1435
            // the call should be used to be sure that the contents are written to the file system
1436
0
            uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor( xTempOut, uno::UNO_QUERY );
1437
0
            if (asyncOutputMonitor.is() && !m_bDisableFileSync)
1438
0
                asyncOutputMonitor->waitForCompletion();
1439
1440
            // no need to postpone switching to the new stream since the target was written directly
1441
0
            uno::Reference< io::XInputStream > xNewStream;
1442
0
            if ( m_eMode == e_IMode_URL )
1443
0
                xNewStream = xSink->getStream()->getInputStream();
1444
0
            else if ( m_eMode == e_IMode_XStream && m_xStream.is() )
1445
0
                xNewStream = m_xStream->getInputStream();
1446
1447
0
            if ( xNewStream.is() )
1448
0
                ConnectTo( xNewStream );
1449
0
        }
1450
0
    }
1451
0
    catch ( uno::Exception& )
1452
0
    {
1453
0
        if( bUseTemp )
1454
0
        {
1455
            // no information loss appears, thus no special handling is required
1456
0
            uno::Any aCaught( ::cppu::getCaughtException() );
1457
1458
            // it is allowed to throw WrappedTargetException
1459
0
            WrappedTargetException aException;
1460
0
            if ( aCaught >>= aException )
1461
0
                throw aException;
1462
1463
0
            throw WrappedTargetException(
1464
0
                    u"Problem writing the original content!"_ustr,
1465
0
                    getXWeak(),
1466
0
                    aCaught );
1467
0
        }
1468
0
        else
1469
0
        {
1470
            // the document is written directly, although it was empty it is important to notify that the writing has failed
1471
            // TODO/LATER: let the package be able to recover in this situation
1472
0
            OUString aErrTxt(u"This package is unusable!"_ustr);
1473
0
            embed::UseBackupException aException( aErrTxt, uno::Reference< uno::XInterface >(), OUString() );
1474
0
            throw WrappedTargetException( aErrTxt,
1475
0
                                            getXWeak(),
1476
0
                                            Any ( aException ) );
1477
0
        }
1478
0
    }
1479
1480
0
    return xResult;
1481
0
}
1482
1483
uno::Reference< XActiveDataStreamer > ZipPackage::openOriginalForOutput()
1484
0
{
1485
    // open and truncate the original file
1486
0
    Content aOriginalContent(
1487
0
        m_aURL, uno::Reference< XCommandEnvironment >(),
1488
0
        m_xContext );
1489
0
    uno::Reference< XActiveDataStreamer > xSink = new ActiveDataStreamer;
1490
1491
0
    if ( m_eMode == e_IMode_URL )
1492
0
    {
1493
0
        try
1494
0
        {
1495
0
            bool bTruncSuccess = false;
1496
1497
0
            try
1498
0
            {
1499
0
                Exception aDetect;
1500
0
                Any aAny = aOriginalContent.setPropertyValue(u"Size"_ustr, Any( sal_Int64(0) ) );
1501
0
                if( !( aAny >>= aDetect ) )
1502
0
                    bTruncSuccess = true;
1503
0
            }
1504
0
            catch( Exception& )
1505
0
            {
1506
0
            }
1507
1508
0
            if( !bTruncSuccess )
1509
0
            {
1510
                // the file is not accessible
1511
                // just try to write an empty stream to it
1512
1513
0
                uno::Reference< XInputStream > xTempIn = new DummyInputStream; //uno::Reference< XInputStream >( xTempOut, UNO_QUERY );
1514
0
                aOriginalContent.writeStream( xTempIn , true );
1515
0
            }
1516
1517
0
            OpenCommandArgument2 aArg;
1518
0
            aArg.Mode        = OpenMode::DOCUMENT;
1519
0
            aArg.Priority    = 0; // unused
1520
0
            aArg.Sink       = xSink;
1521
0
            aArg.Properties = uno::Sequence< Property >( 0 ); // unused
1522
1523
0
            aOriginalContent.executeCommand(u"open"_ustr, Any( aArg ) );
1524
0
        }
1525
0
        catch( Exception& )
1526
0
        {
1527
            // seems to be nonlocal file
1528
            // temporary file mechanics should be used
1529
0
        }
1530
0
    }
1531
1532
0
    return xSink;
1533
0
}
1534
1535
void SAL_CALL ZipPackage::commitChanges()
1536
0
{
1537
    // lock the component for the time of committing
1538
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
1539
1540
0
    if ( m_eMode == e_IMode_XInputStream )
1541
0
    {
1542
0
        IOException aException;
1543
0
        throw WrappedTargetException(u"This package is read only!"_ustr,
1544
0
                getXWeak(), Any ( aException ) );
1545
0
    }
1546
    // first the writeTempFile is called, if it returns a stream the stream should be written to the target
1547
    // if no stream was returned, the file was written directly, nothing should be done
1548
0
    uno::Reference< io::XInputStream > xTempInStream;
1549
0
    try
1550
0
    {
1551
0
        xTempInStream = writeTempFile();
1552
0
    }
1553
0
    catch (const ucb::ContentCreationException&)
1554
0
    {
1555
0
        css::uno::Any anyEx = cppu::getCaughtException();
1556
0
        throw WrappedTargetException(u"Temporary file should be creatable!"_ustr,
1557
0
                    getXWeak(), anyEx );
1558
0
    }
1559
0
    if ( xTempInStream.is() )
1560
0
    {
1561
0
        uno::Reference< io::XSeekable > xTempSeek( xTempInStream, uno::UNO_QUERY_THROW );
1562
1563
0
        try
1564
0
        {
1565
0
            xTempSeek->seek( 0 );
1566
0
        }
1567
0
        catch( const uno::Exception& )
1568
0
        {
1569
0
            css::uno::Any anyEx = cppu::getCaughtException();
1570
0
            throw WrappedTargetException(u"Temporary file should be seekable!"_ustr,
1571
0
                    getXWeak(), anyEx );
1572
0
        }
1573
1574
0
        try
1575
0
        {
1576
            // connect to the temporary stream
1577
0
            ConnectTo( xTempInStream );
1578
0
        }
1579
0
        catch( const io::IOException& )
1580
0
        {
1581
0
            css::uno::Any anyEx = cppu::getCaughtException();
1582
0
            throw WrappedTargetException(u"Temporary file should be connectable!"_ustr,
1583
0
                    getXWeak(), anyEx );
1584
0
        }
1585
1586
0
        if ( m_eMode == e_IMode_XStream )
1587
0
        {
1588
            // First truncate our output stream
1589
0
            uno::Reference < XOutputStream > xOutputStream;
1590
1591
            // preparation for copy step
1592
0
            try
1593
0
            {
1594
0
                xOutputStream = m_xStream->getOutputStream();
1595
1596
                // Make sure we avoid a situation where the current position is
1597
                // not zero, but the underlying file is truncated in the
1598
                // meantime.
1599
0
                uno::Reference<io::XSeekable> xSeekable(xOutputStream, uno::UNO_QUERY);
1600
0
                if (xSeekable.is())
1601
0
                    xSeekable->seek(0);
1602
1603
0
                uno::Reference < XTruncate > xTruncate ( xOutputStream, UNO_QUERY_THROW );
1604
1605
                // after successful truncation the original file contents are already lost
1606
0
                xTruncate->truncate();
1607
0
            }
1608
0
            catch( const uno::Exception& )
1609
0
            {
1610
0
                css::uno::Any anyEx = cppu::getCaughtException();
1611
0
                throw WrappedTargetException(u"This package is read only!"_ustr,
1612
0
                        getXWeak(), anyEx );
1613
0
            }
1614
1615
0
            try
1616
0
            {
1617
                // then copy the contents of the tempfile to our output stream
1618
0
                ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, xOutputStream );
1619
0
                xOutputStream->flush();
1620
0
                uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor(
1621
0
                    xOutputStream, uno::UNO_QUERY );
1622
0
                if ( asyncOutputMonitor.is() ) {
1623
0
                    asyncOutputMonitor->waitForCompletion();
1624
0
                }
1625
0
            }
1626
0
            catch( uno::Exception& )
1627
0
            {
1628
                // if anything goes wrong in this block the target file becomes corrupted
1629
                // so an exception should be thrown as a notification about it
1630
                // and the package must disconnect from the stream
1631
0
                DisconnectFromTargetAndThrowException_Impl( xTempInStream );
1632
0
            }
1633
0
        }
1634
0
        else if ( m_eMode == e_IMode_URL )
1635
0
        {
1636
0
            uno::Reference< XOutputStream > aOrigFileStream;
1637
0
            bool bCanBeCorrupted = false;
1638
1639
0
            if( isLocalFile() )
1640
0
            {
1641
                // write directly in case of local file
1642
0
                uno::Reference< css::ucb::XSimpleFileAccess3 > xSimpleAccess(
1643
0
                    SimpleFileAccess::create( m_xContext ) );
1644
0
                OSL_ENSURE( xSimpleAccess.is(), "Can't instantiate SimpleFileAccess service!" );
1645
0
                uno::Reference< io::XTruncate > xOrigTruncate;
1646
0
                if ( xSimpleAccess.is() )
1647
0
                {
1648
0
                    try
1649
0
                    {
1650
0
                        aOrigFileStream = xSimpleAccess->openFileWrite( m_aURL );
1651
0
                        xOrigTruncate.set( aOrigFileStream, uno::UNO_QUERY_THROW );
1652
                        // after successful truncation the file is already corrupted
1653
0
                        xOrigTruncate->truncate();
1654
0
                    }
1655
0
                    catch( uno::Exception& )
1656
0
                    {}
1657
0
                }
1658
1659
0
                if( xOrigTruncate.is() )
1660
0
                {
1661
0
                    try
1662
0
                    {
1663
0
                        ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, aOrigFileStream );
1664
0
                        aOrigFileStream->closeOutput();
1665
0
                    }
1666
0
                    catch( uno::Exception& )
1667
0
                    {
1668
0
                        try {
1669
0
                            aOrigFileStream->closeOutput();
1670
0
                        } catch ( uno::Exception& ) {}
1671
1672
0
                        aOrigFileStream.clear();
1673
                        // the original file can already be corrupted
1674
0
                        bCanBeCorrupted = true;
1675
0
                    }
1676
0
                }
1677
0
            }
1678
1679
0
            if( !aOrigFileStream.is() )
1680
0
            {
1681
0
                try
1682
0
                {
1683
0
                    uno::Reference < XPropertySet > xPropSet ( xTempInStream, UNO_QUERY_THROW );
1684
1685
0
                    OUString sTargetFolder = m_aURL.copy ( 0, m_aURL.lastIndexOf ( u'/' ) );
1686
0
                    Content aContent(
1687
0
                        sTargetFolder, uno::Reference< XCommandEnvironment >(),
1688
0
                        m_xContext );
1689
1690
0
                    OUString sTempURL;
1691
0
                    Any aAny = xPropSet->getPropertyValue (u"Uri"_ustr);
1692
0
                    aAny >>= sTempURL;
1693
1694
0
                    TransferInfo aInfo;
1695
0
                    aInfo.NameClash = NameClash::OVERWRITE;
1696
0
                    aInfo.MoveData = false;
1697
0
                    aInfo.SourceURL = sTempURL;
1698
0
                    aInfo.NewTitle = rtl::Uri::decode ( m_aURL.copy ( 1 + m_aURL.lastIndexOf ( u'/' ) ),
1699
0
                                                        rtl_UriDecodeWithCharset,
1700
0
                                                        RTL_TEXTENCODING_UTF8 );
1701
                    // if the file is still not corrupted, it can become after the next step
1702
0
                    aContent.executeCommand (u"transfer"_ustr, Any(aInfo) );
1703
0
                }
1704
0
                catch ( const css::uno::Exception& )
1705
0
                {
1706
0
                    if ( bCanBeCorrupted )
1707
0
                        DisconnectFromTargetAndThrowException_Impl( xTempInStream );
1708
1709
0
                    css::uno::Any anyEx = cppu::getCaughtException();
1710
0
                    throw WrappedTargetException(
1711
0
                                                u"This package may be read only!"_ustr,
1712
0
                                                getXWeak(),
1713
0
                                                anyEx );
1714
0
                }
1715
0
            }
1716
0
        }
1717
0
    }
1718
1719
    // after successful storing it can be set to false
1720
0
    m_bMediaTypeFallbackUsed = false;
1721
0
}
1722
1723
void ZipPackage::DisconnectFromTargetAndThrowException_Impl( const uno::Reference< io::XInputStream >& xTempStream )
1724
0
{
1725
0
    m_xStream.set( xTempStream, uno::UNO_QUERY );
1726
0
    if ( m_xStream.is() )
1727
0
        m_eMode = e_IMode_XStream;
1728
0
    else
1729
0
        m_eMode = e_IMode_XInputStream;
1730
1731
0
    OUString aTempURL;
1732
0
    try {
1733
0
        uno::Reference< beans::XPropertySet > xTempFile( xTempStream, uno::UNO_QUERY_THROW );
1734
0
        uno::Any aUrl = xTempFile->getPropertyValue(u"Uri"_ustr);
1735
0
        aUrl >>= aTempURL;
1736
0
        xTempFile->setPropertyValue(u"RemoveFile"_ustr,
1737
0
                                     uno::Any( false ) );
1738
0
    }
1739
0
    catch ( uno::Exception& )
1740
0
    {
1741
0
        OSL_FAIL( "These calls are pretty simple, they should not fail!" );
1742
0
    }
1743
1744
0
    OUString aErrTxt(u"This package is read only!"_ustr);
1745
0
    embed::UseBackupException aException( aErrTxt, uno::Reference< uno::XInterface >(), aTempURL );
1746
0
    throw WrappedTargetException( aErrTxt,
1747
0
                                    getXWeak(),
1748
0
                                    Any ( aException ) );
1749
0
}
1750
1751
uno::Sequence< sal_Int8 > ZipPackage::GetEncryptionKey()
1752
0
{
1753
0
    uno::Sequence< sal_Int8 > aResult;
1754
1755
0
    if ( m_aStorageEncryptionKeys.hasElements() )
1756
0
    {
1757
0
        OUString aNameToFind;
1758
0
        if ( m_nStartKeyGenerationID == xml::crypto::DigestID::SHA256 )
1759
0
            aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
1760
0
        else if ( m_nStartKeyGenerationID == xml::crypto::DigestID::SHA1 )
1761
0
            aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA1CORRECT;
1762
0
        else
1763
0
            throw uno::RuntimeException(u"No expected key is provided!"_ustr );
1764
1765
0
        for (const auto& rKey : m_aStorageEncryptionKeys)
1766
0
            if ( rKey.Name == aNameToFind )
1767
0
                rKey.Value >>= aResult;
1768
1769
0
        if (!aResult.hasElements() && m_aStorageEncryptionKeys.hasElements())
1770
0
        {   // tdf#159519 sanity check
1771
0
            throw uno::RuntimeException(u"Expected key is missing!"_ustr);
1772
0
        }
1773
0
    }
1774
0
    else
1775
0
        aResult = m_aEncryptionKey;
1776
1777
0
    return aResult;
1778
0
}
1779
1780
sal_Bool SAL_CALL ZipPackage::hasPendingChanges()
1781
0
{
1782
0
    return false;
1783
0
}
1784
Sequence< ElementChange > SAL_CALL ZipPackage::getPendingChanges()
1785
0
{
1786
0
    return uno::Sequence < ElementChange > ();
1787
0
}
1788
1789
1790
OUString ZipPackage::getImplementationName()
1791
0
{
1792
0
    return u"com.sun.star.packages.comp.ZipPackage"_ustr;
1793
0
}
1794
1795
Sequence< OUString > ZipPackage::getSupportedServiceNames()
1796
0
{
1797
0
    return { u"com.sun.star.packages.Package"_ustr };
1798
0
}
1799
1800
sal_Bool SAL_CALL ZipPackage::supportsService( OUString const & rServiceName )
1801
0
{
1802
0
    return cppu::supportsService(this, rServiceName);
1803
0
}
1804
1805
uno::Reference< XPropertySetInfo > SAL_CALL ZipPackage::getPropertySetInfo()
1806
0
{
1807
0
    return uno::Reference < XPropertySetInfo > ();
1808
0
}
1809
1810
void SAL_CALL ZipPackage::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
1811
57.5k
{
1812
57.5k
    if ( m_nFormat != embed::StorageFormats::PACKAGE )
1813
0
        throw UnknownPropertyException(aPropertyName);
1814
1815
57.5k
    if (aPropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY
1816
57.5k
      ||aPropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY
1817
57.5k
      ||aPropertyName == IS_INCONSISTENT_PROPERTY
1818
57.5k
      ||aPropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY)
1819
0
        throw PropertyVetoException(u""_ustr );
1820
57.5k
    else if ( aPropertyName == ENCRYPTION_KEY_PROPERTY )
1821
0
    {
1822
0
        if ( !( aValue >>= m_aEncryptionKey ) )
1823
0
            throw IllegalArgumentException(u""_ustr, uno::Reference< uno::XInterface >(), 2 );
1824
1825
0
        m_aStorageEncryptionKeys.realloc( 0 );
1826
0
    }
1827
57.5k
    else if ( aPropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
1828
0
    {
1829
        // this property is only necessary to support raw passwords in storage API;
1830
        // because of this support the storage has to operate with more than one key dependent on storage generation algorithm;
1831
        // when this support is removed, the storage will get only one key from outside
1832
0
        if ( !( aValue >>= m_aStorageEncryptionKeys ) )
1833
0
            throw IllegalArgumentException(u""_ustr, uno::Reference< uno::XInterface >(), 2 );
1834
1835
0
        m_aEncryptionKey.realloc( 0 );
1836
0
    }
1837
57.5k
    else if ( aPropertyName == ENCRYPTION_ALGORITHMS_PROPERTY )
1838
57.5k
    {
1839
57.5k
        uno::Sequence< beans::NamedValue > aAlgorithms;
1840
57.5k
        if ( m_pZipFile || !( aValue >>= aAlgorithms ) || !aAlgorithms.hasElements() )
1841
0
        {
1842
            // the algorithms can not be changed if the file has a persistence based on the algorithms ( m_pZipFile )
1843
0
            throw IllegalArgumentException(u"unexpected algorithms list is provided."_ustr, uno::Reference< uno::XInterface >(), 2 );
1844
0
        }
1845
1846
57.5k
        for (const auto& rAlgorithm : aAlgorithms)
1847
230k
        {
1848
230k
            if ( rAlgorithm.Name == "StartKeyGenerationAlgorithm" )
1849
57.5k
            {
1850
57.5k
                sal_Int32 nID = 0;
1851
57.5k
                if ( !( rAlgorithm.Value >>= nID )
1852
57.5k
                  || ( nID != xml::crypto::DigestID::SHA256 && nID != xml::crypto::DigestID::SHA1 ) )
1853
0
                {
1854
0
                    throw IllegalArgumentException(u"Unexpected start key generation algorithm is provided!"_ustr, uno::Reference<uno::XInterface>(), 2);
1855
0
                }
1856
1857
57.5k
                m_nStartKeyGenerationID = nID;
1858
57.5k
            }
1859
172k
            else if (rAlgorithm.Name == "KeyDerivationFunction")
1860
57.5k
            {
1861
57.5k
                sal_Int32 nID = 0;
1862
57.5k
                if (!(rAlgorithm.Value >>= nID)
1863
57.5k
                  || (nID != xml::crypto::KDFID::PBKDF2
1864
0
                      && nID != xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P
1865
0
                      && nID != xml::crypto::KDFID::Argon2id))
1866
0
                {
1867
0
                    throw IllegalArgumentException(u"Unexpected key derivation function provided!"_ustr, uno::Reference<uno::XInterface>(), 2);
1868
0
                }
1869
57.5k
                m_nKeyDerivationFunctionID = nID;
1870
57.5k
            }
1871
115k
            else if ( rAlgorithm.Name == "EncryptionAlgorithm" )
1872
57.5k
            {
1873
57.5k
                sal_Int32 nID = 0;
1874
57.5k
                if ( !( rAlgorithm.Value >>= nID )
1875
57.5k
                  || (nID != xml::crypto::CipherID::AES_GCM_W3C
1876
57.5k
                      && nID != xml::crypto::CipherID::AES_CBC_W3C_PADDING
1877
0
                      && nID != xml::crypto::CipherID::BLOWFISH_CFB_8))
1878
0
                {
1879
0
                    throw IllegalArgumentException(u"Unexpected encryption algorithm is provided!"_ustr, uno::Reference<uno::XInterface>(), 2);
1880
0
                }
1881
1882
57.5k
                m_nCommonEncryptionID = nID;
1883
57.5k
            }
1884
57.5k
            else if ( rAlgorithm.Name == "ChecksumAlgorithm" )
1885
57.5k
            {
1886
57.5k
                sal_Int32 nID = 0;
1887
57.5k
                if (!rAlgorithm.Value.hasValue())
1888
0
                {
1889
0
                    m_oChecksumDigestID.reset();
1890
0
                    continue;
1891
0
                }
1892
57.5k
                if ( !( rAlgorithm.Value >>= nID )
1893
57.5k
                  || ( nID != xml::crypto::DigestID::SHA1_1K && nID != xml::crypto::DigestID::SHA256_1K ) )
1894
0
                {
1895
0
                    throw IllegalArgumentException(u"Unexpected checksum algorithm is provided!"_ustr, uno::Reference<uno::XInterface>(), 2);
1896
0
                }
1897
1898
57.5k
                m_oChecksumDigestID.emplace(nID);
1899
57.5k
            }
1900
0
            else
1901
0
            {
1902
0
                OSL_ENSURE( false, "Unexpected encryption algorithm is provided!" );
1903
0
                throw IllegalArgumentException(u"unexpected algorithms list is provided."_ustr, uno::Reference< uno::XInterface >(), 2 );
1904
0
            }
1905
230k
        }
1906
57.5k
    }
1907
0
    else if ( aPropertyName == ENCRYPTION_GPG_PROPERTIES )
1908
0
    {
1909
0
        uno::Sequence< uno::Sequence< beans::NamedValue > > aGpgProps;
1910
0
        if ( !( aValue >>= aGpgProps ) || !aGpgProps.hasElements() )
1911
0
        {
1912
0
            throw IllegalArgumentException(u"unexpected Gpg properties are provided."_ustr, uno::Reference< uno::XInterface >(), 2 );
1913
0
        }
1914
1915
0
        m_aGpgProps = std::move(aGpgProps);
1916
1917
        // override algorithm defaults (which are some legacy ODF
1918
        // defaults) with reasonable values
1919
        // note: these should be overridden by SfxObjectShell::SetupStorage()
1920
0
        m_nStartKeyGenerationID = 0; // this is unused for PGP
1921
0
        m_nKeyDerivationFunctionID = xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
1922
0
        m_nCommonEncryptionID = xml::crypto::CipherID::AES_CBC_W3C_PADDING;
1923
0
        m_oChecksumDigestID.emplace(xml::crypto::DigestID::SHA512_1K);
1924
0
    }
1925
0
    else
1926
0
        throw UnknownPropertyException(aPropertyName);
1927
57.5k
}
1928
1929
Any SAL_CALL ZipPackage::getPropertyValue( const OUString& PropertyName )
1930
58.0k
{
1931
    // TODO/LATER: Activate the check when zip-ucp is ready
1932
    // if ( m_nFormat != embed::StorageFormats::PACKAGE )
1933
    //  throw UnknownPropertyException();
1934
1935
58.0k
    if ( PropertyName == ENCRYPTION_KEY_PROPERTY )
1936
0
    {
1937
0
        return Any(m_aEncryptionKey);
1938
0
    }
1939
58.0k
    else if ( PropertyName == ENCRYPTION_ALGORITHMS_PROPERTY )
1940
0
    {
1941
0
        ::comphelper::SequenceAsHashMap aAlgorithms;
1942
0
        aAlgorithms[u"StartKeyGenerationAlgorithm"_ustr] <<= m_nStartKeyGenerationID;
1943
0
        aAlgorithms[u"KeyDerivationFunction"_ustr] <<= m_nKeyDerivationFunctionID;
1944
0
        aAlgorithms[u"EncryptionAlgorithm"_ustr] <<= m_nCommonEncryptionID;
1945
0
        if (m_oChecksumDigestID)
1946
0
        {
1947
0
            aAlgorithms[u"ChecksumAlgorithm"_ustr] <<= *m_oChecksumDigestID;
1948
0
        }
1949
0
        else
1950
0
        {
1951
0
            aAlgorithms[u"ChecksumAlgorithm"_ustr];
1952
0
        }
1953
0
        return Any(aAlgorithms.getAsConstNamedValueList());
1954
0
    }
1955
58.0k
    if ( PropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
1956
0
    {
1957
0
        return Any(m_aStorageEncryptionKeys);
1958
0
    }
1959
58.0k
    else if ( PropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY )
1960
8.61k
    {
1961
8.61k
        return Any(m_bHasEncryptedEntries);
1962
8.61k
    }
1963
49.4k
    else if ( PropertyName == ENCRYPTION_GPG_PROPERTIES )
1964
0
    {
1965
0
        return Any(m_aGpgProps);
1966
0
    }
1967
49.4k
    else if ( PropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY )
1968
8.61k
    {
1969
8.61k
        return Any(m_bHasNonEncryptedEntries);
1970
8.61k
    }
1971
40.8k
    else if ( PropertyName == IS_INCONSISTENT_PROPERTY )
1972
0
    {
1973
0
        return Any(m_bInconsistent);
1974
0
    }
1975
40.8k
    else if ( PropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY )
1976
40.8k
    {
1977
40.8k
        return Any(m_bMediaTypeFallbackUsed);
1978
40.8k
    }
1979
0
    else if (PropertyName == "HasElements")
1980
0
    {
1981
0
        return Any(m_pZipFile && m_pZipFile->entries().hasMoreElements());
1982
0
    }
1983
0
    throw UnknownPropertyException(PropertyName);
1984
58.0k
}
1985
void SAL_CALL ZipPackage::addPropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*xListener*/ )
1986
0
{
1987
0
}
1988
void SAL_CALL ZipPackage::removePropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*aListener*/ )
1989
0
{
1990
0
}
1991
void SAL_CALL ZipPackage::addVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< XVetoableChangeListener >& /*aListener*/ )
1992
0
{
1993
0
}
1994
void SAL_CALL ZipPackage::removeVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< XVetoableChangeListener >& /*aListener*/ )
1995
0
{
1996
0
}
1997
1998
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
1999
package_ZipPackage_get_implementation(
2000
    css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
2001
102k
{
2002
102k
    return cppu::acquire(new ZipPackage(context));
2003
102k
}
2004
2005
extern "C" bool TestImportZip(SvStream& rStream)
2006
2
{
2007
    // explicitly tests the "RepairPackage" recovery mode
2008
2
    rtl::Reference<ZipPackage> xPackage(new ZipPackage(comphelper::getProcessComponentContext()));
2009
2
    css::uno::Reference<css::io::XInputStream> xStream(new utl::OInputStreamWrapper(rStream));
2010
2
    css::uno::Sequence<Any> aArgs{ Any(xStream), Any(NamedValue(u"RepairPackage"_ustr, Any(true))) };
2011
2
    xPackage->initialize(aArgs);
2012
2
    return true;
2013
2
}
2014
2015
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */