Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/package/source/zippackage/zipfileaccess.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 <com/sun/star/lang/DisposedException.hpp>
21
#include <com/sun/star/lang/IllegalArgumentException.hpp>
22
#include <com/sun/star/ucb/XCommandEnvironment.hpp>
23
#include <com/sun/star/io/NotConnectedException.hpp>
24
#include <com/sun/star/io/XActiveDataSink.hpp>
25
#include <com/sun/star/io/XStream.hpp>
26
#include <com/sun/star/io/XSeekable.hpp>
27
#include <com/sun/star/beans/NamedValue.hpp>
28
#include <comphelper/processfactory.hxx>
29
#include <cppuhelper/exc_hlp.hxx>
30
#include <cppuhelper/supportsservice.hxx>
31
#include <zipfileaccess.hxx>
32
#include "ZipPackageSink.hxx"
33
#include <EncryptionData.hxx>
34
35
#include <ucbhelper/content.hxx>
36
#include <rtl/ref.hxx>
37
#include <sal/log.hxx>
38
#include <osl/diagnose.h>
39
40
using namespace ::com::sun::star;
41
42
OZipFileAccess::OZipFileAccess( const uno::Reference< uno::XComponentContext >& rxContext )
43
250
: m_aMutexHolder( new comphelper::RefCountedMutex )
44
250
, m_xContext( rxContext )
45
250
, m_bDisposed( false )
46
250
, m_bOwnContent( false )
47
250
{
48
250
    if ( !rxContext.is() )
49
0
        throw uno::RuntimeException();
50
250
}
51
52
OZipFileAccess::~OZipFileAccess()
53
250
{
54
250
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
55
250
    if ( !m_bDisposed )
56
250
    {
57
250
        try {
58
             // dispose will use refcounting so the further destruction must be avoided
59
250
            osl_atomic_increment(&m_refCount);
60
250
            dispose();
61
250
        } catch( uno::Exception& )
62
250
        {}
63
250
    }
64
250
}
65
66
uno::Sequence< OUString > OZipFileAccess::GetPatternsFromString_Impl( const OUString& aString )
67
0
{
68
0
    if ( aString.isEmpty() )
69
0
        return uno::Sequence< OUString >();
70
71
0
    uno::Sequence< OUString > aPattern( 1 );
72
0
    auto pPattern = aPattern.getArray();
73
0
    sal_Int32 nInd = 0;
74
75
0
    const sal_Unicode* pString = aString.getStr();
76
0
    while( *pString )
77
0
    {
78
0
        if ( *pString == '\\' )
79
0
        {
80
0
            pString++;
81
82
0
            if ( *pString == '\\' )
83
0
            {
84
0
                pPattern[nInd] += "\\";
85
0
                pString++;
86
0
            }
87
0
            else if ( *pString == '*' )
88
0
            {
89
0
                pPattern[nInd] += "*";
90
0
                pString++;
91
0
            }
92
0
            else
93
0
            {
94
0
                OSL_FAIL( "The backslash is not guarded!" );
95
0
                pPattern[nInd] += "\\";
96
0
            }
97
0
        }
98
0
        else if ( *pString == '*' )
99
0
        {
100
0
            aPattern.realloc( ( ++nInd ) + 1 );
101
0
            pPattern = aPattern.getArray();
102
0
            pString++;
103
0
        }
104
0
        else
105
0
        {
106
0
            pPattern[nInd] += OUStringChar( *pString );
107
0
            pString++;
108
0
        }
109
0
    }
110
111
0
    return aPattern;
112
0
}
113
114
bool OZipFileAccess::StringGoodForPattern_Impl( std::u16string_view aString,
115
                                                    const uno::Sequence< OUString >& aPattern )
116
0
{
117
0
    sal_Int32 nInd = aPattern.getLength() - 1;
118
0
    if ( nInd < 0 )
119
0
        return false;
120
121
0
    if ( nInd == 0 )
122
0
    {
123
0
        if ( aPattern[0].isEmpty() )
124
0
            return true;
125
126
0
        return aString == aPattern[0];
127
0
    }
128
129
0
    sal_Int32 nBeginInd = aPattern[0].getLength();
130
0
    sal_Int32 nEndInd = aString.size() - aPattern[nInd].getLength();
131
0
    if ( nEndInd < nBeginInd
132
0
      || ( nEndInd != sal_Int32(aString.size()) && aString.substr( nEndInd ) != aPattern[nInd] )
133
0
      || ( nBeginInd != 0 && aString.substr( 0, nBeginInd ) != aPattern[0] ) )
134
0
          return false;
135
136
0
    for ( sal_Int32 nCurInd = aPattern.getLength() - 2; nCurInd > 0; nCurInd-- )
137
0
    {
138
0
        if ( aPattern[nCurInd].isEmpty() )
139
0
            continue;
140
141
0
        if ( nEndInd == nBeginInd )
142
0
            return false;
143
144
        // check that search does not use nEndInd position
145
0
        size_t nLastInd = aString.substr(0, nEndInd - 1).rfind( aPattern[nCurInd] );
146
147
0
        if ( nLastInd == std::u16string_view::npos )
148
0
            return false;
149
150
0
        if ( sal_Int32(nLastInd) < nBeginInd )
151
0
            return false;
152
153
0
        nEndInd = nLastInd;
154
0
    }
155
156
0
    return true;
157
0
}
158
159
// XInitialization
160
void SAL_CALL OZipFileAccess::initialize( const uno::Sequence< uno::Any >& aArguments )
161
250
{
162
250
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
163
164
250
    if ( m_bDisposed )
165
0
        throw lang::DisposedException();
166
167
250
    if ( m_pZipFile )
168
0
        throw uno::RuntimeException(); // initialization is allowed only one time
169
170
250
    if ( !aArguments.hasElements() )
171
0
        throw lang::IllegalArgumentException(u""_ustr, uno::Reference< uno::XInterface >(), 1 );
172
173
250
    OSL_ENSURE( aArguments.getLength() == 1, "Too many arguments are provided, only the first one will be used!" );
174
175
250
    OUString aParamURL;
176
250
    uno::Reference< io::XStream > xStream;
177
250
    uno::Reference< io::XSeekable > xSeekable;
178
250
    uno::Sequence<beans::NamedValue> aArgs;
179
180
250
    auto openInputStream = [&]()
181
250
    {
182
250
        ::ucbhelper::Content aContent(
183
250
            aParamURL,
184
250
            uno::Reference< css::ucb::XCommandEnvironment >(),
185
250
            m_xContext );
186
250
        uno::Reference < io::XActiveDataSink > xSink = new ZipPackageSink;
187
250
        if ( aContent.openStream ( xSink ) )
188
0
        {
189
0
            m_xContentStream = xSink->getInputStream();
190
0
            m_bOwnContent = true;
191
0
            xSeekable.set( m_xContentStream, uno::UNO_QUERY );
192
0
        }
193
250
    };
194
195
250
    if ( aArguments[0] >>= aParamURL )
196
250
    {
197
250
        openInputStream();
198
250
    }
199
0
    else if ( aArguments[0] >>= xStream )
200
0
    {
201
        // a writable stream can implement both XStream & XInputStream
202
0
        m_xContentStream = xStream->getInputStream();
203
0
        xSeekable.set( xStream, uno::UNO_QUERY );
204
0
    }
205
0
    else if ( aArguments[0] >>= m_xContentStream )
206
0
    {
207
0
        xSeekable.set( m_xContentStream, uno::UNO_QUERY );
208
0
    }
209
0
    else if (aArguments[0] >>= aArgs)
210
0
    {
211
0
        for (const beans::NamedValue& rArg : aArgs)
212
0
        {
213
0
            if (rArg.Name == "URL")
214
0
                rArg.Value >>= aParamURL;
215
0
        }
216
217
0
        if (aParamURL.isEmpty())
218
0
            throw lang::IllegalArgumentException(
219
0
                u"required argument 'URL' is not given or invalid."_ustr,
220
0
                uno::Reference<uno::XInterface>(), 1);
221
222
0
        openInputStream();
223
0
    }
224
0
    else
225
0
        throw lang::IllegalArgumentException(u""_ustr, uno::Reference< uno::XInterface >(), 1 );
226
227
250
    if ( !m_xContentStream.is() )
228
0
        throw io::IOException();
229
230
250
    if ( !xSeekable.is() )
231
0
    {
232
        // TODO: after fwkbugfix02 is integrated a helper class can be used to make the stream seekable
233
0
        throw io::IOException();
234
0
    }
235
236
    // TODO: in case xSeekable is implemented on separated XStream implementation a wrapper is required
237
250
    m_pZipFile.emplace(
238
250
                m_aMutexHolder,
239
250
                m_xContentStream,
240
250
                m_xContext,
241
250
                true, false, ZipFile::Checks::Default);
242
250
}
243
244
// XNameAccess
245
uno::Any SAL_CALL OZipFileAccess::getByName( const OUString& aName )
246
0
{
247
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
248
249
0
    if ( m_bDisposed )
250
0
        throw lang::DisposedException();
251
252
0
    if ( !m_pZipFile )
253
0
        throw uno::RuntimeException();
254
255
0
    EntryHash::iterator aIter = m_pZipFile->GetEntryHash().find( aName );
256
0
    if ( aIter == m_pZipFile->GetEntryHash().end() )
257
0
        throw container::NoSuchElementException();
258
259
0
    uno::Reference< io::XInputStream > xEntryStream;
260
0
    try
261
0
    {
262
0
        xEntryStream  = m_pZipFile->getDataStream((*aIter).second,
263
0
                                                  ::rtl::Reference< EncryptionData >(),
264
0
                                                  {},
265
0
                                                  m_aMutexHolder);
266
0
    }
267
0
    catch (const container::NoSuchElementException&)
268
0
    {
269
0
        throw;
270
0
    }
271
0
    catch (const lang::WrappedTargetException&)
272
0
    {
273
0
        throw;
274
0
    }
275
0
    catch (const uno::RuntimeException&)
276
0
    {
277
0
        throw;
278
0
    }
279
0
    catch (const uno::Exception&)
280
0
    {
281
0
        css::uno::Any anyEx = cppu::getCaughtException();
282
0
        throw lang::WrappedTargetException( u"This package is unusable!"_ustr,
283
0
                  getXWeak(), anyEx);
284
0
    }
285
286
0
    if ( !xEntryStream.is() )
287
0
        throw uno::RuntimeException();
288
289
0
    return uno::Any ( xEntryStream );
290
0
}
291
292
uno::Sequence< OUString > SAL_CALL OZipFileAccess::getElementNames()
293
0
{
294
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
295
296
0
    if ( m_bDisposed )
297
0
        throw lang::DisposedException();
298
299
0
    if ( !m_pZipFile )
300
0
        throw uno::RuntimeException();
301
302
0
    uno::Sequence< OUString > aNames( m_pZipFile->GetEntryHash().size() );
303
0
    auto pNames = aNames.getArray();
304
0
    sal_Int32 nLen = 0;
305
306
0
    for ( const auto& rEntry : m_pZipFile->GetEntryHash() )
307
0
    {
308
0
        if ( aNames.getLength() < ++nLen )
309
0
        {
310
0
            OSL_FAIL( "The size must be the same!" );
311
0
            aNames.realloc( nLen );
312
0
            pNames = aNames.getArray();
313
0
        }
314
315
0
        pNames[nLen-1] = rEntry.second.sPath;
316
0
    }
317
318
0
    if ( aNames.getLength() != nLen )
319
0
    {
320
0
        OSL_FAIL( "The size must be the same!" );
321
0
        aNames.realloc( nLen );
322
0
    }
323
324
0
    return aNames;
325
0
}
326
327
sal_Bool SAL_CALL OZipFileAccess::hasByName( const OUString& aName )
328
0
{
329
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
330
331
0
    if ( m_bDisposed )
332
0
        throw lang::DisposedException();
333
334
0
    if ( !m_pZipFile )
335
0
        throw uno::RuntimeException();
336
337
0
    EntryHash::iterator aIter = m_pZipFile->GetEntryHash().find( aName );
338
339
0
    return ( aIter != m_pZipFile->GetEntryHash().end() );
340
0
}
341
342
uno::Type SAL_CALL OZipFileAccess::getElementType()
343
0
{
344
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
345
346
0
    if ( m_bDisposed )
347
0
        throw lang::DisposedException();
348
349
0
    if ( !m_pZipFile )
350
0
        throw uno::RuntimeException();
351
352
0
    return cppu::UnoType<io::XInputStream>::get();
353
0
}
354
355
sal_Bool SAL_CALL OZipFileAccess::hasElements()
356
0
{
357
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
358
359
0
    if ( m_bDisposed )
360
0
        throw lang::DisposedException();
361
362
0
    if ( !m_pZipFile )
363
0
        throw uno::RuntimeException();
364
365
0
    return ( !m_pZipFile->GetEntryHash().empty() );
366
0
}
367
368
// XZipFileAccess
369
uno::Reference< io::XInputStream > SAL_CALL OZipFileAccess::getStreamByPattern( const OUString& aPatternString )
370
0
{
371
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
372
373
0
    if ( m_bDisposed )
374
0
        throw lang::DisposedException();
375
376
0
    if ( !m_pZipFile )
377
0
        throw io::NotConnectedException();
378
379
    // Code to compare strings by patterns
380
0
    uno::Sequence< OUString > aPattern = GetPatternsFromString_Impl( aPatternString );
381
382
0
    auto aIter = std::find_if(m_pZipFile->GetEntryHash().begin(), m_pZipFile->GetEntryHash().end(),
383
0
        [&aPattern](const EntryHash::value_type& rEntry) { return StringGoodForPattern_Impl(rEntry.second.sPath, aPattern); });
384
0
    if (aIter != m_pZipFile->GetEntryHash().end())
385
0
    {
386
0
        uno::Reference< io::XInputStream > xEntryStream( m_pZipFile->getDataStream( (*aIter).second,
387
0
                                                                                    ::rtl::Reference< EncryptionData >(),
388
0
                                                                                    {},
389
0
                                                                                    m_aMutexHolder ) );
390
391
0
        if ( !xEntryStream.is() )
392
0
            throw uno::RuntimeException();
393
0
        return xEntryStream;
394
0
    }
395
396
0
    throw container::NoSuchElementException();
397
0
}
398
399
// XComponent
400
void SAL_CALL OZipFileAccess::dispose()
401
250
{
402
250
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
403
404
250
    if ( m_bDisposed )
405
0
        throw lang::DisposedException();
406
407
250
    if ( m_pListenersContainer )
408
0
    {
409
0
        lang::EventObject aSource( getXWeak() );
410
0
        m_pListenersContainer->disposeAndClear( aSource );
411
0
        m_pListenersContainer.reset();
412
0
    }
413
414
250
    m_pZipFile.reset();
415
416
250
    if ( m_xContentStream.is() && m_bOwnContent )
417
0
        try {
418
0
            m_xContentStream->closeInput();
419
0
        } catch( uno::Exception& )
420
0
        {}
421
422
250
    m_bDisposed = true;
423
250
}
424
425
void SAL_CALL OZipFileAccess::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
426
0
{
427
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
428
429
0
    if ( m_bDisposed )
430
0
        throw lang::DisposedException();
431
432
0
    if ( !m_pListenersContainer )
433
0
        m_pListenersContainer.reset( new ::comphelper::OInterfaceContainerHelper3<css::lang::XEventListener>( m_aMutexHolder->GetMutex() ) );
434
0
    m_pListenersContainer->addInterface( xListener );
435
0
}
436
437
void SAL_CALL OZipFileAccess::removeEventListener( const uno::Reference< lang::XEventListener >& xListener )
438
0
{
439
0
    ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
440
441
0
    if ( m_bDisposed )
442
0
        throw lang::DisposedException();
443
444
0
    if ( m_pListenersContainer )
445
0
        m_pListenersContainer->removeInterface( xListener );
446
0
}
447
448
OUString SAL_CALL OZipFileAccess::getImplementationName()
449
0
{
450
0
    return u"com.sun.star.comp.package.zip.ZipFileAccess"_ustr;
451
0
}
452
453
sal_Bool SAL_CALL OZipFileAccess::supportsService( const OUString& ServiceName )
454
0
{
455
0
    return cppu::supportsService(this, ServiceName);
456
0
}
457
458
uno::Sequence< OUString > SAL_CALL OZipFileAccess::getSupportedServiceNames()
459
0
{
460
0
    return { u"com.sun.star.packages.zip.ZipFileAccess"_ustr,
461
0
    u"com.sun.star.comp.packages.zip.ZipFileAccess"_ustr };
462
0
}
463
464
465
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
466
package_OZipFileAccess_get_implementation(
467
    css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
468
250
{
469
250
    return cppu::acquire(new OZipFileAccess(context));
470
250
}
471
472
473
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */