1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <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>
35 #include <ucbhelper/content.hxx>
36 #include <rtl/ref.hxx>
37 #include <sal/log.hxx>
38 #include <osl/diagnose.h>
40 using namespace ::com::sun::star
;
42 #if OSL_DEBUG_LEVEL > 0
43 #define THROW_WHERE SAL_WHERE
45 #define THROW_WHERE ""
48 OZipFileAccess::OZipFileAccess( const uno::Reference
< uno::XComponentContext
>& rxContext
)
49 : m_aMutexHolder( new comphelper::RefCountedMutex
)
50 , m_xContext( rxContext
)
51 , m_bDisposed( false )
52 , m_bOwnContent( false )
54 if ( !rxContext
.is() )
55 throw uno::RuntimeException(THROW_WHERE
);
58 OZipFileAccess::~OZipFileAccess()
60 ::osl::MutexGuard
aGuard( m_aMutexHolder
->GetMutex() );
64 // dispose will use refcounting so the further destruction must be avoided
65 osl_atomic_increment(&m_refCount
);
67 } catch( uno::Exception
& )
72 uno::Sequence
< OUString
> OZipFileAccess::GetPatternsFromString_Impl( const OUString
& aString
)
74 if ( aString
.isEmpty() )
75 return uno::Sequence
< OUString
>();
77 uno::Sequence
< OUString
> aPattern( 1 );
78 auto pPattern
= aPattern
.getArray();
81 const sal_Unicode
* pString
= aString
.getStr();
84 if ( *pString
== '\\' )
88 if ( *pString
== '\\' )
90 pPattern
[nInd
] += "\\";
93 else if ( *pString
== '*' )
95 pPattern
[nInd
] += "*";
100 OSL_FAIL( "The backslash is not guarded!" );
101 pPattern
[nInd
] += "\\";
104 else if ( *pString
== '*' )
106 aPattern
.realloc( ( ++nInd
) + 1 );
107 pPattern
= aPattern
.getArray();
112 pPattern
[nInd
] += OUStringChar( *pString
);
120 bool OZipFileAccess::StringGoodForPattern_Impl( std::u16string_view aString
,
121 const uno::Sequence
< OUString
>& aPattern
)
123 sal_Int32 nInd
= aPattern
.getLength() - 1;
129 if ( aPattern
[0].isEmpty() )
132 return aString
== aPattern
[0];
135 sal_Int32 nBeginInd
= aPattern
[0].getLength();
136 sal_Int32 nEndInd
= aString
.size() - aPattern
[nInd
].getLength();
137 if ( nEndInd
< nBeginInd
138 || ( nEndInd
!= sal_Int32(aString
.size()) && aString
.substr( nEndInd
) != aPattern
[nInd
] )
139 || ( nBeginInd
!= 0 && aString
.substr( 0, nBeginInd
) != aPattern
[0] ) )
142 for ( sal_Int32 nCurInd
= aPattern
.getLength() - 2; nCurInd
> 0; nCurInd
-- )
144 if ( aPattern
[nCurInd
].isEmpty() )
147 if ( nEndInd
== nBeginInd
)
150 // check that search does not use nEndInd position
151 size_t nLastInd
= aString
.substr(0, nEndInd
- 1).rfind( aPattern
[nCurInd
] );
153 if ( nLastInd
== std::u16string_view::npos
)
156 if ( sal_Int32(nLastInd
) < nBeginInd
)
166 void SAL_CALL
OZipFileAccess::initialize( const uno::Sequence
< uno::Any
>& aArguments
)
168 ::osl::MutexGuard
aGuard( m_aMutexHolder
->GetMutex() );
171 throw lang::DisposedException(THROW_WHERE
);
174 throw uno::RuntimeException(THROW_WHERE
); // initialization is allowed only one time
176 if ( !aArguments
.hasElements() )
177 throw lang::IllegalArgumentException(THROW_WHERE
, uno::Reference
< uno::XInterface
>(), 1 );
179 OSL_ENSURE( aArguments
.getLength() == 1, "Too many arguments are provided, only the first one will be used!" );
182 uno::Reference
< io::XStream
> xStream
;
183 uno::Reference
< io::XSeekable
> xSeekable
;
184 uno::Sequence
<beans::NamedValue
> aArgs
;
186 auto openInputStream
= [&]()
188 ::ucbhelper::Content
aContent(
190 uno::Reference
< css::ucb::XCommandEnvironment
>(),
192 uno::Reference
< io::XActiveDataSink
> xSink
= new ZipPackageSink
;
193 if ( aContent
.openStream ( xSink
) )
195 m_xContentStream
= xSink
->getInputStream();
196 m_bOwnContent
= true;
197 xSeekable
.set( m_xContentStream
, uno::UNO_QUERY
);
201 if ( aArguments
[0] >>= aParamURL
)
205 else if ( aArguments
[0] >>= xStream
)
207 // a writable stream can implement both XStream & XInputStream
208 m_xContentStream
= xStream
->getInputStream();
209 xSeekable
.set( xStream
, uno::UNO_QUERY
);
211 else if ( aArguments
[0] >>= m_xContentStream
)
213 xSeekable
.set( m_xContentStream
, uno::UNO_QUERY
);
215 else if (aArguments
[0] >>= aArgs
)
217 for (const beans::NamedValue
& rArg
: aArgs
)
219 if (rArg
.Name
== "URL")
220 rArg
.Value
>>= aParamURL
;
223 if (aParamURL
.isEmpty())
224 throw lang::IllegalArgumentException(
225 THROW_WHERE
"required argument 'URL' is not given or invalid.",
226 uno::Reference
<uno::XInterface
>(), 1);
231 throw lang::IllegalArgumentException(THROW_WHERE
, uno::Reference
< uno::XInterface
>(), 1 );
233 if ( !m_xContentStream
.is() )
234 throw io::IOException(THROW_WHERE
);
236 if ( !xSeekable
.is() )
238 // TODO: after fwkbugfix02 is integrated a helper class can be used to make the stream seekable
239 throw io::IOException(THROW_WHERE
);
242 // TODO: in case xSeekable is implemented on separated XStream implementation a wrapper is required
247 true, false, ZipFile::Checks::Default
);
251 uno::Any SAL_CALL
OZipFileAccess::getByName( const OUString
& aName
)
253 ::osl::MutexGuard
aGuard( m_aMutexHolder
->GetMutex() );
256 throw lang::DisposedException(THROW_WHERE
);
259 throw uno::RuntimeException(THROW_WHERE
);
261 EntryHash::iterator aIter
= m_pZipFile
->GetEntryHash().find( aName
);
262 if ( aIter
== m_pZipFile
->GetEntryHash().end() )
263 throw container::NoSuchElementException(THROW_WHERE
);
265 uno::Reference
< io::XInputStream
> xEntryStream
;
268 xEntryStream
= m_pZipFile
->getDataStream((*aIter
).second
,
269 ::rtl::Reference
< EncryptionData
>(),
273 catch (const container::NoSuchElementException
&)
277 catch (const lang::WrappedTargetException
&)
281 catch (const uno::RuntimeException
&)
285 catch (const uno::Exception
&)
287 css::uno::Any anyEx
= cppu::getCaughtException();
288 throw lang::WrappedTargetException( u
"This package is unusable!"_ustr
,
292 if ( !xEntryStream
.is() )
293 throw uno::RuntimeException(THROW_WHERE
);
295 return uno::Any ( xEntryStream
);
298 uno::Sequence
< OUString
> SAL_CALL
OZipFileAccess::getElementNames()
300 ::osl::MutexGuard
aGuard( m_aMutexHolder
->GetMutex() );
303 throw lang::DisposedException(THROW_WHERE
);
306 throw uno::RuntimeException(THROW_WHERE
);
308 uno::Sequence
< OUString
> aNames( m_pZipFile
->GetEntryHash().size() );
309 auto pNames
= aNames
.getArray();
312 for ( const auto& rEntry
: m_pZipFile
->GetEntryHash() )
314 if ( aNames
.getLength() < ++nLen
)
316 OSL_FAIL( "The size must be the same!" );
317 aNames
.realloc( nLen
);
318 pNames
= aNames
.getArray();
321 pNames
[nLen
-1] = rEntry
.second
.sPath
;
324 if ( aNames
.getLength() != nLen
)
326 OSL_FAIL( "The size must be the same!" );
327 aNames
.realloc( nLen
);
333 sal_Bool SAL_CALL
OZipFileAccess::hasByName( const OUString
& aName
)
335 ::osl::MutexGuard
aGuard( m_aMutexHolder
->GetMutex() );
338 throw lang::DisposedException(THROW_WHERE
);
341 throw uno::RuntimeException(THROW_WHERE
);
343 EntryHash::iterator aIter
= m_pZipFile
->GetEntryHash().find( aName
);
345 return ( aIter
!= m_pZipFile
->GetEntryHash().end() );
348 uno::Type SAL_CALL
OZipFileAccess::getElementType()
350 ::osl::MutexGuard
aGuard( m_aMutexHolder
->GetMutex() );
353 throw lang::DisposedException(THROW_WHERE
);
356 throw uno::RuntimeException(THROW_WHERE
);
358 return cppu::UnoType
<io::XInputStream
>::get();
361 sal_Bool SAL_CALL
OZipFileAccess::hasElements()
363 ::osl::MutexGuard
aGuard( m_aMutexHolder
->GetMutex() );
366 throw lang::DisposedException(THROW_WHERE
);
369 throw uno::RuntimeException(THROW_WHERE
);
371 return ( !m_pZipFile
->GetEntryHash().empty() );
375 uno::Reference
< io::XInputStream
> SAL_CALL
OZipFileAccess::getStreamByPattern( const OUString
& aPatternString
)
377 ::osl::MutexGuard
aGuard( m_aMutexHolder
->GetMutex() );
380 throw lang::DisposedException(THROW_WHERE
);
383 throw io::NotConnectedException(THROW_WHERE
);
385 // Code to compare strings by patterns
386 uno::Sequence
< OUString
> aPattern
= GetPatternsFromString_Impl( aPatternString
);
388 auto aIter
= std::find_if(m_pZipFile
->GetEntryHash().begin(), m_pZipFile
->GetEntryHash().end(),
389 [&aPattern
](const EntryHash::value_type
& rEntry
) { return StringGoodForPattern_Impl(rEntry
.second
.sPath
, aPattern
); });
390 if (aIter
!= m_pZipFile
->GetEntryHash().end())
392 uno::Reference
< io::XInputStream
> xEntryStream( m_pZipFile
->getDataStream( (*aIter
).second
,
393 ::rtl::Reference
< EncryptionData
>(),
397 if ( !xEntryStream
.is() )
398 throw uno::RuntimeException(THROW_WHERE
);
402 throw container::NoSuchElementException(THROW_WHERE
);
406 void SAL_CALL
OZipFileAccess::dispose()
408 ::osl::MutexGuard
aGuard( m_aMutexHolder
->GetMutex() );
411 throw lang::DisposedException(THROW_WHERE
);
413 if ( m_pListenersContainer
)
415 lang::EventObject
aSource( getXWeak() );
416 m_pListenersContainer
->disposeAndClear( aSource
);
417 m_pListenersContainer
.reset();
422 if ( m_xContentStream
.is() && m_bOwnContent
)
424 m_xContentStream
->closeInput();
425 } catch( uno::Exception
& )
431 void SAL_CALL
OZipFileAccess::addEventListener( const uno::Reference
< lang::XEventListener
>& xListener
)
433 ::osl::MutexGuard
aGuard( m_aMutexHolder
->GetMutex() );
436 throw lang::DisposedException(THROW_WHERE
);
438 if ( !m_pListenersContainer
)
439 m_pListenersContainer
.reset( new ::comphelper::OInterfaceContainerHelper3
<css::lang::XEventListener
>( m_aMutexHolder
->GetMutex() ) );
440 m_pListenersContainer
->addInterface( xListener
);
443 void SAL_CALL
OZipFileAccess::removeEventListener( const uno::Reference
< lang::XEventListener
>& xListener
)
445 ::osl::MutexGuard
aGuard( m_aMutexHolder
->GetMutex() );
448 throw lang::DisposedException(THROW_WHERE
);
450 if ( m_pListenersContainer
)
451 m_pListenersContainer
->removeInterface( xListener
);
454 OUString SAL_CALL
OZipFileAccess::getImplementationName()
456 return u
"com.sun.star.comp.package.zip.ZipFileAccess"_ustr
;
459 sal_Bool SAL_CALL
OZipFileAccess::supportsService( const OUString
& ServiceName
)
461 return cppu::supportsService(this, ServiceName
);
464 uno::Sequence
< OUString
> SAL_CALL
OZipFileAccess::getSupportedServiceNames()
466 return { u
"com.sun.star.packages.zip.ZipFileAccess"_ustr
,
467 u
"com.sun.star.comp.packages.zip.ZipFileAccess"_ustr
};
471 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
472 package_OZipFileAccess_get_implementation(
473 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const&)
475 return cppu::acquire(new OZipFileAccess(context
));
479 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */