tdf#96401: allow to detect a broken ZIP package

In deep detection, first check if it's a broken ZIP package. If it is,
set the RepairPackage media descriptor property to true.

Pass the RepairPackage value to the OOXML filter detection.

Change-Id: Ic958283f3cce92ac29ce93ac330cc9e409e3eb78
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159976
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
This commit is contained in:
Mike Kaganski 2023-11-26 20:18:34 +03:00
parent ef79b9c558
commit 86c682273d
6 changed files with 113 additions and 4 deletions

View file

@ -41,6 +41,7 @@ $(eval $(call gb_Library_use_libraries,filterconfig,\
cppu \
sal \
salhelper \
sfx \
i18nlangtag \
))

View file

@ -25,9 +25,14 @@
#include <com/sun/star/util/URLTransformer.hpp>
#include <com/sun/star/util/XURLTransformer.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/embed/StorageFormats.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/packages/zip/ZipIOException.hpp>
#include <com/sun/star/task/XInteractionHandler.hpp>
#include <sfx2/brokenpackageint.hxx>
#include <o3tl/string_view.hxx>
#include <tools/wldcrd.hxx>
#include <sal/log.hxx>
@ -832,6 +837,50 @@ void TypeDetection::impl_getAllFormatTypes(
}
static bool isBrokenZIP(const css::uno::Reference<css::io::XInputStream>& xStream,
const css::uno::Reference<css::uno::XComponentContext>& xContext)
{
std::vector<css::uno::Any> aArguments{
css::uno::Any(xStream),
css::uno::Any(css::beans::NamedValue("AllowRemoveOnInsert", css::uno::Any(false))),
css::uno::Any(css::beans::NamedValue("StorageFormat",
css::uno::Any(css::embed::StorageFormats::ZIP))),
};
try
{
// If this is a broken ZIP package, or not a ZIP, this would throw ZipIOException
xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
"com.sun.star.packages.comp.ZipPackage", comphelper::containerToSequence(aArguments),
xContext);
}
catch (const css::packages::zip::ZipIOException&)
{
// Now test if repair will succeed
aArguments.emplace_back(css::beans::NamedValue("RepairPackage", css::uno::Any(true)));
try
{
// If this is a broken ZIP package that can be repaired, this would succeed,
// and the result will be not empty
if (css::uno::Reference<css::beans::XPropertySet> xPackage{
xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
"com.sun.star.packages.comp.ZipPackage",
comphelper::containerToSequence(aArguments), xContext),
css::uno::UNO_QUERY })
if (bool bHasElements; xPackage->getPropertyValue("HasElements") >>= bHasElements)
return bHasElements;
}
catch (const css::uno::Exception&)
{
}
}
catch (const css::uno::Exception&)
{
}
// The package is either not broken, or is not a repairable ZIP
return false;
}
OUString TypeDetection::impl_detectTypeFlatAndDeep( utl::MediaDescriptor& rDescriptor ,
const FlatDetection& lFlatTypes ,
bool bAllowDeep ,
@ -841,6 +890,59 @@ OUString TypeDetection::impl_detectTypeFlatAndDeep( utl::MediaDescriptor& r
// a set and a not set value.
rLastChance.clear();
// tdf#96401: First of all, check if this is a broken ZIP package. Not doing this here would
// make some filters silently not recognize their content in broken packages, and some filters
// show a warning and mistakenly claim own content based on user choice.
if (bAllowDeep && !rDescriptor.getUnpackedValueOrDefault("RepairPackage", false)
&& rDescriptor.getUnpackedValueOrDefault("RepairAllowed", true)
&& rDescriptor.contains(utl::MediaDescriptor::PROP_INTERACTIONHANDLER))
{
try
{
impl_openStream(rDescriptor);
if (auto xStream = rDescriptor.getUnpackedValueOrDefault(
utl::MediaDescriptor::PROP_INPUTSTREAM,
css::uno::Reference<css::io::XInputStream>()))
{
css::uno::Reference<css::uno::XComponentContext> xContext;
// SAFE ->
{
std::unique_lock aLock(m_aMutex);
xContext = m_xContext;
}
// <- SAFE
if (isBrokenZIP(xStream, xContext))
{
if (css::uno::Reference<css::task::XInteractionHandler> xInteraction{
rDescriptor.getValue(utl::MediaDescriptor::PROP_INTERACTIONHANDLER),
css::uno::UNO_QUERY })
{
INetURLObject aURL(rDescriptor.getUnpackedValueOrDefault(
utl::MediaDescriptor::PROP_URL, OUString()));
OUString aDocumentTitle
= aURL.getName(INetURLObject::LAST_SEGMENT, true,
INetURLObject::DecodeMechanism::WithCharset);
// Ask the user whether they wants to try to repair
RequestPackageReparation aRequest(aDocumentTitle);
xInteraction->handle(aRequest.GetRequest());
if (aRequest.isApproved())
rDescriptor["RepairPackage"] <<= true;
else
rDescriptor["RepairAllowed"] <<= false; // Do not ask again
}
}
}
}
catch (const css::uno::Exception&)
{
// No problem
}
}
// step over all possible types for this URL.
// solutions:
// a) no types => no detection

View file

@ -43,7 +43,8 @@ class ZipStorage final : public StorageBase
public:
explicit ZipStorage(
const css::uno::Reference< css::uno::XComponentContext >& rxContext,
const css::uno::Reference< css::io::XInputStream >& rxInStream );
const css::uno::Reference< css::io::XInputStream >& rxInStream,
bool bRepairStorage = false );
explicit ZipStorage(
const css::uno::Reference< css::uno::XComponentContext >& rxContext,

View file

@ -428,7 +428,8 @@ OUString SAL_CALL FilterDetect::detect( Sequence< PropertyValue >& rMediaDescSeq
Reference< XInputStream > xInputStream( extractUnencryptedPackage( aMediaDescriptor ), UNO_SET_THROW );
// stream must be a ZIP package
ZipStorage aZipStorage( mxContext, xInputStream );
ZipStorage aZipStorage(mxContext, xInputStream,
aMediaDescriptor.getUnpackedValueOrDefault("RepairPackage", false));
if( aZipStorage.isStorage() )
{
// create the fast parser, register the XML namespaces, set document handler

View file

@ -38,7 +38,7 @@ using namespace ::com::sun::star::io;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::uno;
ZipStorage::ZipStorage( const Reference< XComponentContext >& rxContext, const Reference< XInputStream >& rxInStream ) :
ZipStorage::ZipStorage( const Reference< XComponentContext >& rxContext, const Reference< XInputStream >& rxInStream, bool bRepairStorage ) :
StorageBase( rxInStream, false )
{
OSL_ENSURE( rxContext.is(), "ZipStorage::ZipStorage - missing component context" );
@ -61,7 +61,7 @@ ZipStorage::ZipStorage( const Reference< XComponentContext >& rxContext, const R
implementation of relations handling.
*/
mxStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream(
ZIP_STORAGE_FORMAT_STRING, rxInStream, rxContext, false);
ZIP_STORAGE_FORMAT_STRING, rxInStream, rxContext, bRepairStorage);
}
catch (Exception const&)
{

View file

@ -1801,6 +1801,10 @@ Any SAL_CALL ZipPackage::getPropertyValue( const OUString& PropertyName )
{
return Any(m_bMediaTypeFallbackUsed);
}
else if (PropertyName == "HasElements")
{
return Any(m_pZipFile && m_pZipFile->entries().hasMoreElements());
}
throw UnknownPropertyException(PropertyName);
}
void SAL_CALL ZipPackage::addPropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*xListener*/ )